-Date: Fri, 18 Oct 2019 22:50:05 -0500
-Subject: [PATCH] Llama API
-
-
-diff --git a/src/main/java/org/bukkit/entity/Llama.java b/src/main/java/org/bukkit/entity/Llama.java
-index bc84b892cae5fe7019a3ad481e9da79956efa1fe..48eb5b00c460cccde29d327cef1d63fc04d6a829 100644
---- a/src/main/java/org/bukkit/entity/Llama.java
-+++ b/src/main/java/org/bukkit/entity/Llama.java
-@@ -119,4 +119,20 @@ public interface Llama extends ChestedHorse, RangedEntity { // Paper
- @org.jetbrains.annotations.Nullable
- Llama getCaravanTail();
- // Paper end
-+
-+ // Purpur start
-+ /**
-+ * Check if this Llama should attempt to join a caravan
-+ *
-+ * @return True if Llama is allowed to join a caravan
-+ */
-+ boolean shouldJoinCaravan();
-+
-+ /**
-+ * Set if this Llama should attempt to join a caravan
-+ *
-+ * @param shouldJoinCaravan True to allow joining a caravan
-+ */
-+ void setShouldJoinCaravan(boolean shouldJoinCaravan);
-+ // Purpur end
- }
-diff --git a/src/main/java/org/purpurmc/purpur/event/entity/LlamaJoinCaravanEvent.java b/src/main/java/org/purpurmc/purpur/event/entity/LlamaJoinCaravanEvent.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..8849bb0becb16db907fa648cca2e98ab9d957c75
---- /dev/null
-+++ b/src/main/java/org/purpurmc/purpur/event/entity/LlamaJoinCaravanEvent.java
-@@ -0,0 +1,61 @@
-+package org.purpurmc.purpur.event.entity;
-+
-+import org.bukkit.entity.Llama;
-+import org.bukkit.event.Cancellable;
-+import org.bukkit.event.HandlerList;
-+import org.bukkit.event.entity.EntityEvent;
-+import org.jetbrains.annotations.NotNull;
-+
-+/**
-+ * Called when a Llama tries to join a caravan.
-+ *
-+ * Cancelling the event will not let the Llama join. To prevent future attempts
-+ * at joining a caravan use {@link Llama#setShouldJoinCaravan(boolean)}.
-+ */
-+public class LlamaJoinCaravanEvent extends EntityEvent implements Cancellable {
-+ private static final HandlerList handlers = new HandlerList();
-+ private boolean canceled;
-+ private final Llama head;
-+
-+ public LlamaJoinCaravanEvent(@NotNull Llama llama, @NotNull Llama head) {
-+ super(llama);
-+ this.head = head;
-+ }
-+
-+ @Override
-+ @NotNull
-+ public Llama getEntity() {
-+ return (Llama) entity;
-+ }
-+
-+ /**
-+ * Get the Llama that this Llama is about to follow
-+ *
-+ * @return Llama about to be followed
-+ */
-+ @NotNull
-+ public Llama getHead() {
-+ return head;
-+ }
-+
-+ @Override
-+ public boolean isCancelled() {
-+ return canceled;
-+ }
-+
-+ @Override
-+ public void setCancelled(boolean cancel) {
-+ canceled = cancel;
-+ }
-+
-+ @Override
-+ @NotNull
-+ public HandlerList getHandlers() {
-+ return handlers;
-+ }
-+
-+ @NotNull
-+ public static HandlerList getHandlerList() {
-+ return handlers;
-+ }
-+}
-diff --git a/src/main/java/org/purpurmc/purpur/event/entity/LlamaLeaveCaravanEvent.java b/src/main/java/org/purpurmc/purpur/event/entity/LlamaLeaveCaravanEvent.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..c268c35b541a222d50875c29770c846a8ffcc4f8
---- /dev/null
-+++ b/src/main/java/org/purpurmc/purpur/event/entity/LlamaLeaveCaravanEvent.java
-@@ -0,0 +1,34 @@
-+package org.purpurmc.purpur.event.entity;
-+
-+import org.bukkit.entity.Llama;
-+import org.bukkit.event.HandlerList;
-+import org.bukkit.event.entity.EntityEvent;
-+import org.jetbrains.annotations.NotNull;
-+
-+/**
-+ * Called when a Llama leaves a caravan
-+ */
-+public class LlamaLeaveCaravanEvent extends EntityEvent {
-+ private static final HandlerList handlers = new HandlerList();
-+
-+ public LlamaLeaveCaravanEvent(@NotNull Llama llama) {
-+ super(llama);
-+ }
-+
-+ @Override
-+ @NotNull
-+ public Llama getEntity() {
-+ return (Llama) entity;
-+ }
-+
-+ @Override
-+ @NotNull
-+ public HandlerList getHandlers() {
-+ return handlers;
-+ }
-+
-+ @NotNull
-+ public static HandlerList getHandlerList() {
-+ return handlers;
-+ }
-+}
diff --git a/patches/api/0008-AFK-API.patch b/patches/api/0008-AFK-API.patch
deleted file mode 100644
index d805660fb..000000000
--- a/patches/api/0008-AFK-API.patch
+++ /dev/null
@@ -1,112 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: William Blake Galbreath
-Date: Sat, 10 Aug 2019 22:19:56 -0500
-Subject: [PATCH] AFK API
-
-
-diff --git a/src/main/java/org/bukkit/entity/Player.java b/src/main/java/org/bukkit/entity/Player.java
-index d972cb242102a3ee7c017299aed64340628c79d8..972cca3e02296f94099f965a4f7662ec63a067ea 100644
---- a/src/main/java/org/bukkit/entity/Player.java
-+++ b/src/main/java/org/bukkit/entity/Player.java
-@@ -3814,5 +3814,25 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
- * @return True if Player uses Purpur Client
- */
- public boolean usesPurpurClient();
-+
-+ /**
-+ * Check if player is AFK
-+ *
-+ * @return True if AFK
-+ */
-+ boolean isAfk();
-+
-+ /**
-+ * Set player as AFK
-+ *
-+ * @param setAfk Whether to set AFK or not
-+ */
-+ void setAfk(boolean setAfk);
-+
-+ /**
-+ * Reset the idle timer back to 0
-+ * @deprecated Use {@link #resetIdleDuration()} instead
-+ */
-+ void resetIdleTimer();
- // Purpur end
- }
-diff --git a/src/main/java/org/purpurmc/purpur/event/PlayerAFKEvent.java b/src/main/java/org/purpurmc/purpur/event/PlayerAFKEvent.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..25e92af7710316ed2afedf846a59dbd672869b51
---- /dev/null
-+++ b/src/main/java/org/purpurmc/purpur/event/PlayerAFKEvent.java
-@@ -0,0 +1,70 @@
-+package org.purpurmc.purpur.event;
-+
-+import org.bukkit.entity.Player;
-+import org.bukkit.event.Cancellable;
-+import org.bukkit.event.HandlerList;
-+import org.bukkit.event.player.PlayerEvent;
-+import org.jetbrains.annotations.NotNull;
-+import org.jetbrains.annotations.Nullable;
-+
-+public class PlayerAFKEvent extends PlayerEvent implements Cancellable {
-+ private static final HandlerList handlers = new HandlerList();
-+ private final boolean setAfk;
-+ private boolean shouldKick;
-+ private String broadcast;
-+ private boolean cancel;
-+
-+ public PlayerAFKEvent(@NotNull Player player, boolean setAfk, boolean shouldKick, @Nullable String broadcast, boolean async) {
-+ super(player, async);
-+ this.setAfk = setAfk;
-+ this.shouldKick = shouldKick;
-+ this.broadcast = broadcast;
-+ }
-+
-+ /**
-+ * Whether player is going afk or coming back
-+ *
-+ * @return True if going afk. False is coming back
-+ */
-+ public boolean isGoingAfk() {
-+ return setAfk;
-+ }
-+
-+ public boolean shouldKick() {
-+ return shouldKick;
-+ }
-+
-+ public void setShouldKick(boolean shouldKick) {
-+ this.shouldKick = shouldKick;
-+ }
-+
-+ @Nullable
-+ public String getBroadcastMsg() {
-+ return broadcast;
-+ }
-+
-+ public void setBroadcastMsg(@Nullable String broadcast) {
-+ this.broadcast = broadcast;
-+ }
-+
-+ @Override
-+ public boolean isCancelled() {
-+ return cancel;
-+ }
-+
-+ @Override
-+ public void setCancelled(boolean cancel) {
-+ this.cancel = cancel;
-+ }
-+
-+ @Override
-+ @NotNull
-+ public HandlerList getHandlers() {
-+ return handlers;
-+ }
-+
-+ @NotNull
-+ public static HandlerList getHandlerList() {
-+ return handlers;
-+ }
-+}
diff --git a/patches/api/0009-Bring-back-server-name.patch b/patches/api/0009-Bring-back-server-name.patch
deleted file mode 100644
index 32c20281f..000000000
--- a/patches/api/0009-Bring-back-server-name.patch
+++ /dev/null
@@ -1,44 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: William Blake Galbreath
-Date: Sun, 26 May 2019 15:18:40 -0500
-Subject: [PATCH] Bring back server name
-
-
-diff --git a/src/main/java/org/bukkit/Bukkit.java b/src/main/java/org/bukkit/Bukkit.java
-index 5d1b55fdbcbe63f6b42b694d05211a3cc691a09d..54f11593f4acfb89623cf1fad58819e001505fd1 100644
---- a/src/main/java/org/bukkit/Bukkit.java
-+++ b/src/main/java/org/bukkit/Bukkit.java
-@@ -2910,4 +2910,15 @@ public final class Bukkit {
- public static Server.Spigot spigot() {
- return server.spigot();
- }
-+
-+ // Purpur start
-+ /**
-+ * Get the name of this server
-+ * @return the name of the server
-+ */
-+ @NotNull
-+ public static String getServerName() {
-+ return server.getServerName();
-+ }
-+ // Purpur end
- }
-diff --git a/src/main/java/org/bukkit/Server.java b/src/main/java/org/bukkit/Server.java
-index f5c3c1a0c11dd3518981ce3b86dba8ced8578d9c..1299cab5a4a0a017e0a1b792539d9b6fb8ef6c54 100644
---- a/src/main/java/org/bukkit/Server.java
-+++ b/src/main/java/org/bukkit/Server.java
-@@ -2552,4 +2552,13 @@ public interface Server extends PluginMessageRecipient, net.kyori.adventure.audi
- */
- boolean isOwnedByCurrentRegion(@NotNull Entity entity);
- // Paper end - Folia region threading API
-+
-+ // Purpur start
-+ /**
-+ * Get the name of this server
-+ * @return the name of the server
-+ */
-+ @NotNull
-+ String getServerName();
-+ // Purpur end
- }
diff --git a/patches/api/0010-ExecuteCommandEvent.patch b/patches/api/0010-ExecuteCommandEvent.patch
deleted file mode 100644
index dd05e0eb9..000000000
--- a/patches/api/0010-ExecuteCommandEvent.patch
+++ /dev/null
@@ -1,175 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: William Blake Galbreath
-Date: Fri, 31 May 2019 00:08:28 -0500
-Subject: [PATCH] ExecuteCommandEvent
-
-
-diff --git a/src/main/java/org/bukkit/command/SimpleCommandMap.java b/src/main/java/org/bukkit/command/SimpleCommandMap.java
-index ac9a28922f8a556944a4c3649d74c32c622f0cb0..e842d13febca67ffa1c89fb2c1324d2609fb81fd 100644
---- a/src/main/java/org/bukkit/command/SimpleCommandMap.java
-+++ b/src/main/java/org/bukkit/command/SimpleCommandMap.java
-@@ -143,6 +143,19 @@ public class SimpleCommandMap implements CommandMap {
- return false;
- }
-
-+ // Purpur start
-+ String[] parsedArgs = Arrays.copyOfRange(args, 1, args.length);
-+ org.purpurmc.purpur.event.ExecuteCommandEvent event = new org.purpurmc.purpur.event.ExecuteCommandEvent(sender, target, sentCommandLabel, parsedArgs);
-+ if (!event.callEvent()) {
-+ return true; // cancelled
-+ }
-+
-+ sender = event.getSender();
-+ target = event.getCommand();
-+ sentCommandLabel = event.getLabel();
-+ parsedArgs = event.getArgs();
-+ // Purpur end
-+
- // Paper start - Plugins do weird things to workaround normal registration
- if (target.timings == null) {
- target.timings = co.aikar.timings.TimingsManager.getCommandTiming(null, target);
-@@ -152,7 +165,7 @@ public class SimpleCommandMap implements CommandMap {
- try {
- try (co.aikar.timings.Timing ignored = target.timings.startTiming()) { // Paper - use try with resources
- // Note: we don't return the result of target.execute as thats success / failure, we return handled (true) or not handled (false)
-- target.execute(sender, sentCommandLabel, Arrays.copyOfRange(args, 1, args.length));
-+ target.execute(sender, sentCommandLabel, parsedArgs); // Purpur
- } // target.timings.stopTiming(); // Spigot // Paper
- } catch (CommandException ex) {
- server.getPluginManager().callEvent(new com.destroystokyo.paper.event.server.ServerExceptionEvent(new com.destroystokyo.paper.exception.ServerCommandException(ex, target, sender, args))); // Paper
-diff --git a/src/main/java/org/purpurmc/purpur/event/ExecuteCommandEvent.java b/src/main/java/org/purpurmc/purpur/event/ExecuteCommandEvent.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..bc590c4d49d32f4365a50ceb5785e798702a8179
---- /dev/null
-+++ b/src/main/java/org/purpurmc/purpur/event/ExecuteCommandEvent.java
-@@ -0,0 +1,130 @@
-+package org.purpurmc.purpur.event;
-+
-+import com.google.common.base.Preconditions;
-+import org.bukkit.command.Command;
-+import org.bukkit.command.CommandSender;
-+import org.bukkit.event.Cancellable;
-+import org.bukkit.event.Event;
-+import org.bukkit.event.HandlerList;
-+import org.jetbrains.annotations.NotNull;
-+import org.jetbrains.annotations.Nullable;
-+
-+/**
-+ * This event is called whenever someone runs a command
-+ */
-+public class ExecuteCommandEvent extends Event implements Cancellable {
-+ private static final HandlerList handlers = new HandlerList();
-+ private boolean cancel = false;
-+ private CommandSender sender;
-+ private Command command;
-+ private String label;
-+ private String[] args;
-+
-+ public ExecuteCommandEvent(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @Nullable String[] args) {
-+ this.sender = sender;
-+ this.command = command;
-+ this.label = label;
-+ this.args = args;
-+ }
-+
-+ /**
-+ * Gets the command that the player is attempting to execute.
-+ *
-+ * @return Command the player is attempting to execute
-+ */
-+ @NotNull
-+ public Command getCommand() {
-+ return command;
-+ }
-+
-+ /**
-+ * Sets the command that the player will execute.
-+ *
-+ * @param command New command that the player will execute
-+ * @throws IllegalArgumentException if command is null or empty
-+ */
-+ public void setCommand(@NotNull Command command) throws IllegalArgumentException {
-+ Preconditions.checkArgument(command != null, "Command cannot be null");
-+ this.command = command;
-+ }
-+
-+ /**
-+ * Gets the sender that this command will be executed as.
-+ *
-+ * @return Sender this command will be executed as
-+ */
-+ @NotNull
-+ public CommandSender getSender() {
-+ return sender;
-+ }
-+
-+ /**
-+ * Sets the sender that this command will be executed as.
-+ *
-+ * @param sender New sender which this event will execute as
-+ * @throws IllegalArgumentException if the sender provided is null
-+ */
-+ public void setSender(@NotNull final CommandSender sender) throws IllegalArgumentException {
-+ Preconditions.checkArgument(sender != null, "Sender cannot be null");
-+ this.sender = sender;
-+ }
-+
-+ /**
-+ * Get the label used to execute this command
-+ *
-+ * @return Label used to execute this command
-+ */
-+ @NotNull
-+ public String getLabel() {
-+ return label;
-+ }
-+
-+ /**
-+ * Set the label used to execute this command
-+ *
-+ * @param label Label used
-+ */
-+ public void setLabel(@NotNull String label) {
-+ this.label = label;
-+ }
-+
-+ /**
-+ * Get the args passed to the command
-+ *
-+ * @return Args passed to the command
-+ */
-+ @NotNull
-+ public String[] getArgs() {
-+ return args;
-+ }
-+
-+ /**
-+ * Set the args passed to the command
-+ *
-+ * @param args Args passed to the command
-+ */
-+ public void setArgs(@NotNull String[] args) {
-+ this.args = args;
-+ }
-+
-+ @Override
-+ public boolean isCancelled() {
-+ return cancel;
-+ }
-+
-+ @Override
-+ public void setCancelled(boolean cancel) {
-+ this.cancel = cancel;
-+ }
-+
-+ @NotNull
-+ @Override
-+ public HandlerList getHandlers() {
-+ return handlers;
-+ }
-+
-+ @NotNull
-+ public static HandlerList getHandlerList() {
-+ return handlers;
-+ }
-+}
diff --git a/patches/api/0011-Lagging-threshold.patch b/patches/api/0011-Lagging-threshold.patch
deleted file mode 100644
index 428bbf5fb..000000000
--- a/patches/api/0011-Lagging-threshold.patch
+++ /dev/null
@@ -1,42 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: William Blake Galbreath
-Date: Tue, 23 Jul 2019 10:07:24 -0500
-Subject: [PATCH] Lagging threshold
-
-
-diff --git a/src/main/java/org/bukkit/Bukkit.java b/src/main/java/org/bukkit/Bukkit.java
-index 54f11593f4acfb89623cf1fad58819e001505fd1..215647d7f5cc8487d6a173bc0160fec6db1971eb 100644
---- a/src/main/java/org/bukkit/Bukkit.java
-+++ b/src/main/java/org/bukkit/Bukkit.java
-@@ -2920,5 +2920,14 @@ public final class Bukkit {
- public static String getServerName() {
- return server.getServerName();
- }
-+
-+ /**
-+ * Check if server is lagging according to laggy threshold setting
-+ *
-+ * @return True if lagging
-+ */
-+ public static boolean isLagging() {
-+ return server.isLagging();
-+ }
- // Purpur end
- }
-diff --git a/src/main/java/org/bukkit/Server.java b/src/main/java/org/bukkit/Server.java
-index 1299cab5a4a0a017e0a1b792539d9b6fb8ef6c54..7f9dc209c88a66bd2ee82cc62b948e827c6b1060 100644
---- a/src/main/java/org/bukkit/Server.java
-+++ b/src/main/java/org/bukkit/Server.java
-@@ -2560,5 +2560,12 @@ public interface Server extends PluginMessageRecipient, net.kyori.adventure.audi
- */
- @NotNull
- String getServerName();
-+
-+ /**
-+ * Check if server is lagging according to laggy threshold setting
-+ *
-+ * @return True if lagging
-+ */
-+ boolean isLagging();
- // Purpur end
- }
diff --git a/patches/api/0012-PlayerSetSpawnerTypeWithEggEvent.patch b/patches/api/0012-PlayerSetSpawnerTypeWithEggEvent.patch
deleted file mode 100644
index fa03a99a9..000000000
--- a/patches/api/0012-PlayerSetSpawnerTypeWithEggEvent.patch
+++ /dev/null
@@ -1,97 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: William Blake Galbreath
-Date: Fri, 5 Jul 2019 18:21:15 -0500
-Subject: [PATCH] PlayerSetSpawnerTypeWithEggEvent
-
-
-diff --git a/src/main/java/org/purpurmc/purpur/event/PlayerSetSpawnerTypeWithEggEvent.java b/src/main/java/org/purpurmc/purpur/event/PlayerSetSpawnerTypeWithEggEvent.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..519809eab5d926dc7b0a7bad5d446d0defc099dc
---- /dev/null
-+++ b/src/main/java/org/purpurmc/purpur/event/PlayerSetSpawnerTypeWithEggEvent.java
-@@ -0,0 +1,85 @@
-+package org.purpurmc.purpur.event;
-+
-+import org.bukkit.block.Block;
-+import org.bukkit.block.CreatureSpawner;
-+import org.bukkit.entity.EntityType;
-+import org.bukkit.entity.Player;
-+import org.bukkit.event.Cancellable;
-+import org.bukkit.event.HandlerList;
-+import org.bukkit.event.player.PlayerEvent;
-+import org.jetbrains.annotations.NotNull;
-+
-+public class PlayerSetSpawnerTypeWithEggEvent extends PlayerEvent implements Cancellable {
-+ private static final HandlerList handlers = new HandlerList();
-+ private final Block block;
-+ private final CreatureSpawner spawner;
-+ private EntityType type;
-+ private boolean cancel;
-+
-+ public PlayerSetSpawnerTypeWithEggEvent(@NotNull Player player, @NotNull Block block, @NotNull CreatureSpawner spawner, @NotNull EntityType type) {
-+ super(player);
-+ this.block = block;
-+ this.spawner = spawner;
-+ this.type = type;
-+ }
-+
-+ /**
-+ * Get the spawner Block in the world
-+ *
-+ * @return Spawner Block
-+ */
-+ @NotNull
-+ public Block getBlock() {
-+ return block;
-+ }
-+
-+ /**
-+ * Get the spawner state
-+ *
-+ * @return Spawner state
-+ */
-+ @NotNull
-+ public CreatureSpawner getSpawner() {
-+ return spawner;
-+ }
-+
-+ /**
-+ * Gets the EntityType being set on the spawner
-+ *
-+ * @return EntityType being set
-+ */
-+ @NotNull
-+ public EntityType getEntityType() {
-+ return type;
-+ }
-+
-+ /**
-+ * Sets the EntityType being set on the spawner
-+ *
-+ * @param type EntityType to set
-+ */
-+ public void setEntityType(@NotNull EntityType type) {
-+ this.type = type;
-+ }
-+
-+ @Override
-+ public boolean isCancelled() {
-+ return cancel;
-+ }
-+
-+ @Override
-+ public void setCancelled(boolean cancel) {
-+ this.cancel = cancel;
-+ }
-+
-+ @Override
-+ @NotNull
-+ public HandlerList getHandlers() {
-+ return handlers;
-+ }
-+
-+ @NotNull
-+ public static HandlerList getHandlerList() {
-+ return handlers;
-+ }
-+}
diff --git a/patches/api/0013-Anvil-API.patch b/patches/api/0013-Anvil-API.patch
deleted file mode 100644
index 5ed2626ed..000000000
--- a/patches/api/0013-Anvil-API.patch
+++ /dev/null
@@ -1,124 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: William Blake Galbreath
-Date: Sun, 19 Apr 2020 00:25:09 -0500
-Subject: [PATCH] Anvil API
-
-
-diff --git a/src/main/java/org/bukkit/inventory/AnvilInventory.java b/src/main/java/org/bukkit/inventory/AnvilInventory.java
-index c60be4fd24c7fdf65251dd6169e5e1ac3b588d95..569deccd2f1cf21da9b5906433ac493c1f2081be 100644
---- a/src/main/java/org/bukkit/inventory/AnvilInventory.java
-+++ b/src/main/java/org/bukkit/inventory/AnvilInventory.java
-@@ -123,4 +123,14 @@ public interface AnvilInventory extends Inventory {
- setItem(2, result);
- }
- // Paper end
-+
-+ // Purpur start
-+ boolean canBypassCost();
-+
-+ void setBypassCost(boolean bypassCost);
-+
-+ boolean canDoUnsafeEnchants();
-+
-+ void setDoUnsafeEnchants(boolean canDoUnsafeEnchants);
-+ // Purpur end
- }
-diff --git a/src/main/java/org/purpurmc/purpur/event/inventory/AnvilTakeResultEvent.java b/src/main/java/org/purpurmc/purpur/event/inventory/AnvilTakeResultEvent.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..b363c91a29f826910db22f2643decf996a067ab5
---- /dev/null
-+++ b/src/main/java/org/purpurmc/purpur/event/inventory/AnvilTakeResultEvent.java
-@@ -0,0 +1,52 @@
-+package org.purpurmc.purpur.event.inventory;
-+
-+import org.bukkit.entity.HumanEntity;
-+import org.bukkit.entity.Player;
-+import org.bukkit.event.HandlerList;
-+import org.bukkit.event.inventory.InventoryEvent;
-+import org.bukkit.inventory.AnvilInventory;
-+import org.bukkit.inventory.InventoryView;
-+import org.bukkit.inventory.ItemStack;
-+import org.jetbrains.annotations.NotNull;
-+
-+/**
-+ * Called when a player takes the result item out of an anvil
-+ */
-+public class AnvilTakeResultEvent extends InventoryEvent {
-+ private static final HandlerList handlers = new HandlerList();
-+ private final Player player;
-+ private final ItemStack result;
-+
-+ public AnvilTakeResultEvent(@NotNull HumanEntity player, @NotNull InventoryView view, @NotNull ItemStack result) {
-+ super(view);
-+ this.player = (Player) player;
-+ this.result = result;
-+ }
-+
-+ @NotNull
-+ public Player getPlayer() {
-+ return player;
-+ }
-+
-+ @NotNull
-+ public ItemStack getResult() {
-+ return result;
-+ }
-+
-+ @NotNull
-+ @Override
-+ public AnvilInventory getInventory() {
-+ return (AnvilInventory) super.getInventory();
-+ }
-+
-+ @NotNull
-+ @Override
-+ public HandlerList getHandlers() {
-+ return handlers;
-+ }
-+
-+ @NotNull
-+ public static HandlerList getHandlerList() {
-+ return handlers;
-+ }
-+}
-diff --git a/src/main/java/org/purpurmc/purpur/event/inventory/AnvilUpdateResultEvent.java b/src/main/java/org/purpurmc/purpur/event/inventory/AnvilUpdateResultEvent.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..fd6a5a3589d436c2aaf988fd305899695799d3bb
---- /dev/null
-+++ b/src/main/java/org/purpurmc/purpur/event/inventory/AnvilUpdateResultEvent.java
-@@ -0,0 +1,35 @@
-+package org.purpurmc.purpur.event.inventory;
-+
-+import org.bukkit.event.HandlerList;
-+import org.bukkit.event.inventory.InventoryEvent;
-+import org.bukkit.inventory.AnvilInventory;
-+import org.bukkit.inventory.InventoryView;
-+import org.jetbrains.annotations.NotNull;
-+
-+/**
-+ * Called when anvil slots change, triggering the result slot to be updated
-+ */
-+public class AnvilUpdateResultEvent extends InventoryEvent {
-+ private static final HandlerList handlers = new HandlerList();
-+
-+ public AnvilUpdateResultEvent(@NotNull InventoryView view) {
-+ super(view);
-+ }
-+
-+ @NotNull
-+ @Override
-+ public AnvilInventory getInventory() {
-+ return (AnvilInventory) super.getInventory();
-+ }
-+
-+ @NotNull
-+ @Override
-+ public HandlerList getHandlers() {
-+ return handlers;
-+ }
-+
-+ @NotNull
-+ public static HandlerList getHandlerList() {
-+ return handlers;
-+ }
-+}
diff --git a/patches/api/0018-Add-predicate-to-recipe-s-ExactChoice-ingredient.patch b/patches/api/0018-Add-predicate-to-recipe-s-ExactChoice-ingredient.patch
deleted file mode 100644
index e6dd23224..000000000
--- a/patches/api/0018-Add-predicate-to-recipe-s-ExactChoice-ingredient.patch
+++ /dev/null
@@ -1,52 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: William Blake Galbreath
-Date: Fri, 2 Oct 2020 17:43:24 -0500
-Subject: [PATCH] Add predicate to recipe's ExactChoice ingredient
-
-
-diff --git a/src/main/java/org/bukkit/inventory/RecipeChoice.java b/src/main/java/org/bukkit/inventory/RecipeChoice.java
-index db8bcc66bdc4bedfffb4705db6338eda4c0ad29a..feda3ddfaaf37b6ee218a0e0b1fbc199899bd364 100644
---- a/src/main/java/org/bukkit/inventory/RecipeChoice.java
-+++ b/src/main/java/org/bukkit/inventory/RecipeChoice.java
-@@ -10,6 +10,7 @@ import java.util.function.Predicate;
- import org.bukkit.Material;
- import org.bukkit.Tag;
- import org.jetbrains.annotations.NotNull;
-+import org.jetbrains.annotations.Nullable; // Purpur
-
- /**
- * Represents a potential item match within a recipe. All choices within a
-@@ -150,6 +151,7 @@ public interface RecipeChoice extends Predicate, Cloneable {
- public static class ExactChoice implements RecipeChoice {
-
- private List choices;
-+ private Predicate predicate; // Purpur
-
- public ExactChoice(@NotNull ItemStack stack) {
- this(Arrays.asList(stack));
-@@ -194,6 +196,7 @@ public interface RecipeChoice extends Predicate, Cloneable {
-
- @Override
- public boolean test(@NotNull ItemStack t) {
-+ if (predicate != null) return predicate.test(t); // Purpur
- for (ItemStack match : choices) {
- if (t.isSimilar(match)) {
- return true;
-@@ -203,6 +206,17 @@ public interface RecipeChoice extends Predicate, Cloneable {
- return false;
- }
-
-+ // Purpur start
-+ @Nullable
-+ public Predicate getPredicate() {
-+ return predicate;
-+ }
-+
-+ public void setPredicate(@Nullable Predicate predicate) {
-+ this.predicate = predicate;
-+ }
-+ // Purpur end
-+
- @Override
- public int hashCode() {
- int hash = 7;
diff --git a/patches/api/0019-Rabid-Wolf-API.patch b/patches/api/0019-Rabid-Wolf-API.patch
deleted file mode 100644
index fa9743084..000000000
--- a/patches/api/0019-Rabid-Wolf-API.patch
+++ /dev/null
@@ -1,31 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Encode42
-Date: Tue, 8 Dec 2020 17:15:15 -0500
-Subject: [PATCH] Rabid Wolf API
-
-
-diff --git a/src/main/java/org/bukkit/entity/Wolf.java b/src/main/java/org/bukkit/entity/Wolf.java
-index 4b84c04675775e2a606630b00de8afe51665cebc..ccbaf40a3131f477b4be2264401ad893725c1162 100644
---- a/src/main/java/org/bukkit/entity/Wolf.java
-+++ b/src/main/java/org/bukkit/entity/Wolf.java
-@@ -112,4 +112,20 @@ public interface Wolf extends Tameable, Sittable, io.papermc.paper.entity.Collar
- return variant;
- }
- }
-+
-+ // Purpur start
-+ /**
-+ * Checks if this wolf is rabid
-+ *
-+ * @return whether the wolf is rabid
-+ */
-+ public boolean isRabid();
-+
-+ /**
-+ * Sets this wolf to be rabid or not
-+ *
-+ * @param rabid whether the wolf should be rabid
-+ */
-+ public void setRabid(boolean rabid);
-+ // Purpur end
- }
diff --git a/patches/api/0020-PlayerBookTooLargeEvent.patch b/patches/api/0020-PlayerBookTooLargeEvent.patch
deleted file mode 100644
index 937bb73c4..000000000
--- a/patches/api/0020-PlayerBookTooLargeEvent.patch
+++ /dev/null
@@ -1,77 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: William Blake Galbreath
-Date: Wed, 23 Dec 2020 00:43:27 -0600
-Subject: [PATCH] PlayerBookTooLargeEvent
-
-
-diff --git a/src/main/java/org/purpurmc/purpur/event/player/PlayerBookTooLargeEvent.java b/src/main/java/org/purpurmc/purpur/event/player/PlayerBookTooLargeEvent.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..c88394336bc9ab0f66a2af24d393f4a176a234d5
---- /dev/null
-+++ b/src/main/java/org/purpurmc/purpur/event/player/PlayerBookTooLargeEvent.java
-@@ -0,0 +1,65 @@
-+package org.purpurmc.purpur.event.player;
-+
-+import org.bukkit.Bukkit;
-+import org.bukkit.entity.Player;
-+import org.bukkit.event.HandlerList;
-+import org.bukkit.event.player.PlayerEvent;
-+import org.bukkit.inventory.ItemStack;
-+import org.jetbrains.annotations.NotNull;
-+
-+/**
-+ * Called when a player tries to bypass book limitations
-+ */
-+public class PlayerBookTooLargeEvent extends PlayerEvent {
-+ private static final HandlerList handlers = new HandlerList();
-+ private final ItemStack book;
-+ private boolean kickPlayer = true;
-+
-+ /**
-+ * @param player The player
-+ * @param book The book
-+ */
-+ public PlayerBookTooLargeEvent(@NotNull Player player, @NotNull ItemStack book) {
-+ super(player, !Bukkit.isPrimaryThread());
-+ this.book = book;
-+ }
-+
-+ /**
-+ * Get the book containing the wanted edits
-+ *
-+ * @return The book
-+ */
-+ @NotNull
-+ public ItemStack getBook() {
-+ return book;
-+ }
-+
-+ /**
-+ * Whether server should kick the player or not
-+ *
-+ * @return True to kick player
-+ */
-+ public boolean shouldKickPlayer() {
-+ return kickPlayer;
-+ }
-+
-+ /**
-+ * Whether server should kick the player or not
-+ *
-+ * @param kickPlayer True to kick player
-+ */
-+ public void setShouldKickPlayer(boolean kickPlayer) {
-+ this.kickPlayer = kickPlayer;
-+ }
-+
-+ @Override
-+ @NotNull
-+ public HandlerList getHandlers() {
-+ return handlers;
-+ }
-+
-+ @NotNull
-+ public static HandlerList getHandlerList() {
-+ return handlers;
-+ }
-+}
diff --git a/patches/api/0021-Full-netherite-armor-grants-fire-resistance.patch b/patches/api/0021-Full-netherite-armor-grants-fire-resistance.patch
deleted file mode 100644
index 5082cfcd3..000000000
--- a/patches/api/0021-Full-netherite-armor-grants-fire-resistance.patch
+++ /dev/null
@@ -1,23 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: William Blake Galbreath
-Date: Thu, 24 Dec 2020 11:00:04 -0600
-Subject: [PATCH] Full netherite armor grants fire resistance
-
-
-diff --git a/src/main/java/org/bukkit/event/entity/EntityPotionEffectEvent.java b/src/main/java/org/bukkit/event/entity/EntityPotionEffectEvent.java
-index c9f395064656dd0126410eb3c6e197baa450c063..13156a12e5df50cdc1e465dc0bd9d94108275629 100644
---- a/src/main/java/org/bukkit/event/entity/EntityPotionEffectEvent.java
-+++ b/src/main/java/org/bukkit/event/entity/EntityPotionEffectEvent.java
-@@ -217,6 +217,12 @@ public class EntityPotionEffectEvent extends EntityEvent implements Cancellable
- * When all effects are removed due to a bucket of milk.
- */
- MILK,
-+ // Purpur start
-+ /**
-+ * When a player wears full netherite armor
-+ */
-+ NETHERITE_ARMOR,
-+ // Purpur end
- /**
- * When a player gets bad omen after killing a patrol captain.
- */
diff --git a/patches/api/0022-Add-EntityTeleportHinderedEvent.patch b/patches/api/0022-Add-EntityTeleportHinderedEvent.patch
deleted file mode 100644
index a172c1de1..000000000
--- a/patches/api/0022-Add-EntityTeleportHinderedEvent.patch
+++ /dev/null
@@ -1,141 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Mariell Hoversholm
-Date: Sat, 9 Jan 2021 15:26:04 +0100
-Subject: [PATCH] Add EntityTeleportHinderedEvent
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program. If not, see .
-
-diff --git a/src/main/java/org/purpurmc/purpur/event/entity/EntityTeleportHinderedEvent.java b/src/main/java/org/purpurmc/purpur/event/entity/EntityTeleportHinderedEvent.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..c66eb163877e872f234d86dc244cab7efeb818cd
---- /dev/null
-+++ b/src/main/java/org/purpurmc/purpur/event/entity/EntityTeleportHinderedEvent.java
-@@ -0,0 +1,117 @@
-+package org.purpurmc.purpur.event.entity;
-+
-+import org.bukkit.entity.Entity;
-+import org.bukkit.event.HandlerList;
-+import org.bukkit.event.entity.EntityEvent;
-+import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause;
-+import org.jetbrains.annotations.NotNull;
-+import org.jetbrains.annotations.Nullable;
-+
-+/**
-+ * Fired when an entity is hindered from teleporting.
-+ */
-+public class EntityTeleportHinderedEvent extends EntityEvent {
-+ private static final HandlerList handlers = new HandlerList();
-+
-+ @NotNull
-+ private final Reason reason;
-+
-+ @Nullable
-+ private final TeleportCause teleportCause;
-+
-+ private boolean retry = false;
-+
-+ public EntityTeleportHinderedEvent(@NotNull Entity what, @NotNull Reason reason,
-+ @Nullable TeleportCause teleportCause) {
-+ super(what);
-+ this.reason = reason;
-+ this.teleportCause = teleportCause;
-+ }
-+
-+ /**
-+ * @return why the teleport was hindered.
-+ */
-+ @NotNull
-+ public Reason getReason() {
-+ return reason;
-+ }
-+
-+ /**
-+ * @return why the teleport occurred if cause was given, otherwise {@code null}.
-+ */
-+ @Nullable
-+ public TeleportCause getTeleportCause() {
-+ return teleportCause;
-+ }
-+
-+ /**
-+ * Whether the teleport should be retried.
-+ *
-+ * Note that this can put the server in a never-ending loop of trying to teleport someone resulting in a stack
-+ * overflow. Do not retry more than necessary.
-+ *
-+ *
-+ * @return whether the teleport should be retried.
-+ */
-+ public boolean shouldRetry() {
-+ return retry;
-+ }
-+
-+ /**
-+ * Sets whether the teleport should be retried.
-+ *
-+ * Note that this can put the server in a never-ending loop of trying to teleport someone resulting in a stack
-+ * overflow. Do not retry more than necessary.
-+ *
-+ *
-+ * @param retry whether the teleport should be retried.
-+ */
-+ public void setShouldRetry(boolean retry) {
-+ this.retry = retry;
-+ }
-+
-+ /**
-+ * Calls the event and tests if should retry.
-+ *
-+ * @return whether the teleport should be retried.
-+ */
-+ @Override
-+ public boolean callEvent() {
-+ super.callEvent();
-+ return shouldRetry();
-+ }
-+
-+ @Override
-+ @NotNull
-+ public HandlerList getHandlers() {
-+ return handlers;
-+ }
-+
-+ @NotNull
-+ public static HandlerList getHandlerList() {
-+ return handlers;
-+ }
-+
-+ /**
-+ * Reason for hindrance in teleports.
-+ */
-+ public enum Reason {
-+ /**
-+ * The teleported entity is a passenger of another entity.
-+ */
-+ IS_PASSENGER,
-+
-+ /**
-+ * The teleported entity has passengers.
-+ */
-+ IS_VEHICLE,
-+
-+ /**
-+ * The teleport event was cancelled.
-+ *
-+ * This is only caused by players teleporting.
-+ *
-+ */
-+ EVENT_CANCELLED,
-+ }
-+}
diff --git a/patches/api/0024-API-for-any-mob-to-burn-daylight.patch b/patches/api/0024-API-for-any-mob-to-burn-daylight.patch
deleted file mode 100644
index eb412045f..000000000
--- a/patches/api/0024-API-for-any-mob-to-burn-daylight.patch
+++ /dev/null
@@ -1,48 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Ben Kerllenevich
-Date: Tue, 25 May 2021 16:30:30 -0400
-Subject: [PATCH] API for any mob to burn daylight
-
-Co-authored by: Encode42
-
-diff --git a/src/main/java/org/bukkit/entity/Entity.java b/src/main/java/org/bukkit/entity/Entity.java
-index 50344412a04f3008439e337ecf9dd09c7f853bc9..f482467ae784b134da97eb38afb7f12585520297 100644
---- a/src/main/java/org/bukkit/entity/Entity.java
-+++ b/src/main/java/org/bukkit/entity/Entity.java
-@@ -1185,5 +1185,12 @@ public interface Entity extends Metadatable, CommandSender, Nameable, Persistent
- * @return True if ridable in water
- */
- boolean isRidableInWater();
-+
-+ /**
-+ * Checks if the entity is in daylight
-+ *
-+ * @return True if in daylight
-+ */
-+ boolean isInDaylight();
- // Purpur end
- }
-diff --git a/src/main/java/org/bukkit/entity/LivingEntity.java b/src/main/java/org/bukkit/entity/LivingEntity.java
-index aba9952b2256b058eb413ce93f3305c861a200db..61a046584acf48693489ff551a0dd4c4b16af9ff 100644
---- a/src/main/java/org/bukkit/entity/LivingEntity.java
-+++ b/src/main/java/org/bukkit/entity/LivingEntity.java
-@@ -1455,5 +1455,19 @@ public interface LivingEntity extends Attributable, Damageable, ProjectileSource
- * @param slot Equipment slot to play break animation for
- */
- void broadcastItemBreak(@NotNull org.bukkit.inventory.EquipmentSlot slot);
-+
-+ /**
-+ * If this mob will burn in the sunlight
-+ *
-+ * @return True if mob will burn in sunlight
-+ */
-+ boolean shouldBurnInDay();
-+
-+ /**
-+ * Set if this mob should burn in the sunlight
-+ *
-+ * @param shouldBurnInDay True to burn in sunlight
-+ */
-+ void setShouldBurnInDay(boolean shouldBurnInDay);
- // Purpur end
- }
diff --git a/patches/api/0026-Fix-default-permission-system.patch b/patches/api/0026-Fix-default-permission-system.patch
deleted file mode 100644
index 754c4e1e3..000000000
--- a/patches/api/0026-Fix-default-permission-system.patch
+++ /dev/null
@@ -1,77 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: William Blake Galbreath
-Date: Wed, 30 Jun 2021 17:44:27 -0500
-Subject: [PATCH] Fix default permission system
-
-
-diff --git a/src/main/java/org/bukkit/permissions/PermissibleBase.java b/src/main/java/org/bukkit/permissions/PermissibleBase.java
-index cd3296fea01648592d2af89b3d80135acb6d0958..45797a6fbae1d8edc4211cb30def24ad4f59bd49 100644
---- a/src/main/java/org/bukkit/permissions/PermissibleBase.java
-+++ b/src/main/java/org/bukkit/permissions/PermissibleBase.java
-@@ -168,7 +168,7 @@ public class PermissibleBase implements Permissible {
-
- for (Permission perm : defaults) {
- String name = perm.getName().toLowerCase(java.util.Locale.ENGLISH);
-- permissions.put(name, new PermissionAttachmentInfo(parent, name, null, true));
-+ permissions.put(name, new PermissionAttachmentInfo(parent, name, null, perm.getDefault().getValue(isOp()))); // Purpur
- Bukkit.getServer().getPluginManager().subscribeToPermission(name, parent);
- calculateChildPermissions(perm.getChildren(), false, null);
- }
-@@ -196,7 +196,7 @@ public class PermissibleBase implements Permissible {
- String name = entry.getKey();
-
- Permission perm = Bukkit.getServer().getPluginManager().getPermission(name);
-- boolean value = entry.getValue() ^ invert;
-+ boolean value = (entry.getValue() == null && perm != null ? perm.getDefault().getValue(isOp()) : entry.getValue()) ^ invert; // Purpur
- String lname = name.toLowerCase(java.util.Locale.ENGLISH);
-
- permissions.put(lname, new PermissionAttachmentInfo(parent, lname, attachment, value));
-diff --git a/src/main/java/org/bukkit/util/permissions/DefaultPermissions.java b/src/main/java/org/bukkit/util/permissions/DefaultPermissions.java
-index 8e481e3815f5645ee92f0d229e5ff25c8fc9a6c2..10627d2a11251a8cb01bbc3f6242d66f3505a16e 100644
---- a/src/main/java/org/bukkit/util/permissions/DefaultPermissions.java
-+++ b/src/main/java/org/bukkit/util/permissions/DefaultPermissions.java
-@@ -31,7 +31,7 @@ public final class DefaultPermissions {
-
- if (withLegacy) {
- Permission legacy = new Permission(LEGACY_PREFIX + result.getName(), result.getDescription(), PermissionDefault.FALSE);
-- legacy.getChildren().put(result.getName(), true);
-+ legacy.getChildren().put(result.getName(), null); // Purpur
- registerPermission(perm, false);
- }
-
-@@ -40,7 +40,7 @@ public final class DefaultPermissions {
-
- @NotNull
- public static Permission registerPermission(@NotNull Permission perm, @NotNull Permission parent) {
-- parent.getChildren().put(perm.getName(), true);
-+ parent.getChildren().put(perm.getName(), null); // Purpur
- return registerPermission(perm);
- }
-
-@@ -53,7 +53,7 @@ public final class DefaultPermissions {
- @NotNull
- public static Permission registerPermission(@NotNull String name, @Nullable String desc, @NotNull Permission parent) {
- Permission perm = registerPermission(name, desc);
-- parent.getChildren().put(perm.getName(), true);
-+ parent.getChildren().put(perm.getName(), null); // Purpur
- return perm;
- }
-
-@@ -66,7 +66,7 @@ public final class DefaultPermissions {
- @NotNull
- public static Permission registerPermission(@NotNull String name, @Nullable String desc, @Nullable PermissionDefault def, @NotNull Permission parent) {
- Permission perm = registerPermission(name, desc, def);
-- parent.getChildren().put(perm.getName(), true);
-+ parent.getChildren().put(perm.getName(), null); // Purpur
- return perm;
- }
-
-@@ -79,7 +79,7 @@ public final class DefaultPermissions {
- @NotNull
- public static Permission registerPermission(@NotNull String name, @Nullable String desc, @Nullable PermissionDefault def, @Nullable Map children, @NotNull Permission parent) {
- Permission perm = registerPermission(name, desc, def, children);
-- parent.getChildren().put(perm.getName(), true);
-+ parent.getChildren().put(perm.getName(), null); // Purpur
- return perm;
- }
-
diff --git a/patches/api/0027-Summoner-API.patch b/patches/api/0027-Summoner-API.patch
deleted file mode 100644
index bc94bd07e..000000000
--- a/patches/api/0027-Summoner-API.patch
+++ /dev/null
@@ -1,81 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: William Blake Galbreath
-Date: Sat, 3 Jul 2021 18:45:01 -0500
-Subject: [PATCH] Summoner API
-
-
-diff --git a/src/main/java/org/bukkit/entity/IronGolem.java b/src/main/java/org/bukkit/entity/IronGolem.java
-index 655e37cb3a09610a3f3df805d6dcad17d722da62..09fd716c8fc9ea34a1cbf87bcbe22df035422a51 100644
---- a/src/main/java/org/bukkit/entity/IronGolem.java
-+++ b/src/main/java/org/bukkit/entity/IronGolem.java
-@@ -19,4 +19,20 @@ public interface IronGolem extends Golem {
- * player created, false if you want it to be a natural village golem.
- */
- public void setPlayerCreated(boolean playerCreated);
-+
-+ // Purpur start
-+ /**
-+ * Get the player that summoned this iron golem
-+ *
-+ * @return UUID of summoner
-+ */
-+ @org.jetbrains.annotations.Nullable java.util.UUID getSummoner();
-+
-+ /**
-+ * Set the player that summoned this iron golem
-+ *
-+ * @param summoner UUID of summoner
-+ */
-+ void setSummoner(@org.jetbrains.annotations.Nullable java.util.UUID summoner);
-+ // Purpur end
- }
-diff --git a/src/main/java/org/bukkit/entity/Snowman.java b/src/main/java/org/bukkit/entity/Snowman.java
-index 7fbfdb07585c7b28acea1f0c1f58ada0cc744441..21fcca092e2e31baa5ece0de9e44e3fade8c7123 100644
---- a/src/main/java/org/bukkit/entity/Snowman.java
-+++ b/src/main/java/org/bukkit/entity/Snowman.java
-@@ -23,4 +23,20 @@ public interface Snowman extends Golem, RangedEntity, io.papermc.paper.entity.Sh
- * @param derpMode True to remove the pumpkin, false to add a pumpkin
- */
- void setDerp(boolean derpMode);
-+
-+ // Purpur start
-+ /**
-+ * Get the player that summoned this snowman
-+ *
-+ * @return UUID of summoner
-+ */
-+ @org.jetbrains.annotations.Nullable java.util.UUID getSummoner();
-+
-+ /**
-+ * Set the player that summoned this snowman
-+ *
-+ * @param summoner UUID of summoner
-+ */
-+ void setSummoner(@org.jetbrains.annotations.Nullable java.util.UUID summoner);
-+ // Purpur end
- }
-diff --git a/src/main/java/org/bukkit/entity/Wither.java b/src/main/java/org/bukkit/entity/Wither.java
-index 14543c2238b45c526dd9aebea2aa5c22f5df54dc..5312daf33405704c74e2c9e109754285ea6cf734 100644
---- a/src/main/java/org/bukkit/entity/Wither.java
-+++ b/src/main/java/org/bukkit/entity/Wither.java
-@@ -107,4 +107,20 @@ public interface Wither extends Monster, Boss, com.destroystokyo.paper.entity.Ra
- */
- void enterInvulnerabilityPhase();
- // Paper end
-+
-+ // Purpur start
-+ /**
-+ * Get the player that summoned this wither
-+ *
-+ * @return UUID of summoner
-+ */
-+ @org.jetbrains.annotations.Nullable java.util.UUID getSummoner();
-+
-+ /**
-+ * Set the player that summoned this wither
-+ *
-+ * @param summoner UUID of summoner
-+ */
-+ void setSummoner(@org.jetbrains.annotations.Nullable java.util.UUID summoner);
-+ // Purpur end
- }
diff --git a/patches/api/0028-Clean-up-version-command-output.patch b/patches/api/0028-Clean-up-version-command-output.patch
deleted file mode 100644
index 1bd0a28dd..000000000
--- a/patches/api/0028-Clean-up-version-command-output.patch
+++ /dev/null
@@ -1,51 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: William Blake Galbreath
-Date: Thu, 15 Jul 2021 23:43:04 -0500
-Subject: [PATCH] Clean up version command output
-
-
-diff --git a/src/main/java/com/destroystokyo/paper/util/VersionFetcher.java b/src/main/java/com/destroystokyo/paper/util/VersionFetcher.java
-index a736d7bcdc5861a01b66ba36158db1c716339346..22fc165fd9c95f0f3ae1be7a0857e48cc50fad5b 100644
---- a/src/main/java/com/destroystokyo/paper/util/VersionFetcher.java
-+++ b/src/main/java/com/destroystokyo/paper/util/VersionFetcher.java
-@@ -26,6 +26,12 @@ public interface VersionFetcher {
- @NotNull
- Component getVersionMessage(@NotNull String serverVersion);
-
-+ // Purpur start
-+ default int distance() {
-+ return 0;
-+ }
-+ // Purpur end
-+
- class DummyVersionFetcher implements VersionFetcher {
-
- @Override
-diff --git a/src/main/java/org/bukkit/command/defaults/VersionCommand.java b/src/main/java/org/bukkit/command/defaults/VersionCommand.java
-index fd5d9881abfd930bb883120f018f76dc78b62b14..d3dadad49df09e85c724c93e8cc88da2c985e9b4 100644
---- a/src/main/java/org/bukkit/command/defaults/VersionCommand.java
-+++ b/src/main/java/org/bukkit/command/defaults/VersionCommand.java
-@@ -214,7 +214,7 @@ public class VersionCommand extends BukkitCommand {
- String version = Bukkit.getVersion();
- // Paper start
- if (version.startsWith("null")) { // running from ide?
-- setVersionMessage(Component.text("Unknown version, custom build?", NamedTextColor.YELLOW));
-+ setVersionMessage(Component.text("* Unknown version, custom build?", NamedTextColor.RED)); // Purpur
- return;
- }
- setVersionMessage(getVersionFetcher().getVersionMessage(version));
-@@ -255,9 +255,11 @@ public class VersionCommand extends BukkitCommand {
- // Paper start
- private void setVersionMessage(final @NotNull Component msg) {
- lastCheck = System.currentTimeMillis();
-- final Component message = Component.textOfChildren(
-- Component.text(Bukkit.getVersionMessage(), NamedTextColor.WHITE),
-- Component.newline(),
-+ // Purpur start
-+ int distance = getVersionFetcher().distance();
-+ final Component message = Component.join(net.kyori.adventure.text.JoinConfiguration.separator(Component.newline()),
-+ ChatColor.parseMM("Current: %s%s*", distance == 0 ? "" : distance > 0 ? "" : "", Bukkit.getVersion()),
-+ // Purpur end
- msg
- );
- this.versionMessage = Component.text()
diff --git a/patches/api/0030-Added-the-ability-to-add-combustible-items.patch b/patches/api/0030-Added-the-ability-to-add-combustible-items.patch
deleted file mode 100644
index 597454cba..000000000
--- a/patches/api/0030-Added-the-ability-to-add-combustible-items.patch
+++ /dev/null
@@ -1,60 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: DoctaEnkoda
-Date: Mon, 9 Aug 2021 13:22:03 +0200
-Subject: [PATCH] Added the ability to add combustible items
-
-
-diff --git a/src/main/java/org/bukkit/Bukkit.java b/src/main/java/org/bukkit/Bukkit.java
-index 215647d7f5cc8487d6a173bc0160fec6db1971eb..7bc4e5e9947ab646707a5c0b39c70c0cc6606bd8 100644
---- a/src/main/java/org/bukkit/Bukkit.java
-+++ b/src/main/java/org/bukkit/Bukkit.java
-@@ -2929,5 +2929,24 @@ public final class Bukkit {
- public static boolean isLagging() {
- return server.isLagging();
- }
-+
-+ /**
-+ * Add an Item as fuel for furnaces
-+ *
-+ * @param material The material that will be the fuel
-+ * @param burnTime The time (in ticks) this item will burn for
-+ */
-+ public static void addFuel(@NotNull Material material, int burnTime) {
-+ server.addFuel(material, burnTime);
-+ }
-+
-+ /**
-+ * Remove an item as fuel for furnaces
-+ *
-+ * @param material The material that will no longer be a fuel
-+ */
-+ public static void removeFuel(@NotNull Material material) {
-+ server.removeFuel(material);
-+ }
- // Purpur end
- }
-diff --git a/src/main/java/org/bukkit/Server.java b/src/main/java/org/bukkit/Server.java
-index 7f9dc209c88a66bd2ee82cc62b948e827c6b1060..fa926319ff033768d78508491e072efec1dcbc9f 100644
---- a/src/main/java/org/bukkit/Server.java
-+++ b/src/main/java/org/bukkit/Server.java
-@@ -2567,5 +2567,20 @@ public interface Server extends PluginMessageRecipient, net.kyori.adventure.audi
- * @return True if lagging
- */
- boolean isLagging();
-+
-+ /**
-+ * Add an Item as fuel for furnaces
-+ *
-+ * @param material The material that will be the fuel
-+ * @param burnTime The time (in ticks) this item will burn for
-+ */
-+ public void addFuel(@NotNull Material material, int burnTime);
-+
-+ /**
-+ * Remove an item as fuel for furnaces
-+ *
-+ * @param material The material that will no longer be a fuel
-+ */
-+ public void removeFuel(@NotNull Material material);
- // Purpur end
- }
diff --git a/patches/api/0031-Grindstone-API.patch b/patches/api/0031-Grindstone-API.patch
deleted file mode 100644
index 5a699b1c1..000000000
--- a/patches/api/0031-Grindstone-API.patch
+++ /dev/null
@@ -1,84 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: BillyGalbreath
-Date: Mon, 27 Dec 2021 08:10:50 -0600
-Subject: [PATCH] Grindstone API
-
-
-diff --git a/src/main/java/org/purpurmc/purpur/event/inventory/GrindstoneTakeResultEvent.java b/src/main/java/org/purpurmc/purpur/event/inventory/GrindstoneTakeResultEvent.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..eebb5d124456b8209d1b8e8cc4cb772dd3714f04
---- /dev/null
-+++ b/src/main/java/org/purpurmc/purpur/event/inventory/GrindstoneTakeResultEvent.java
-@@ -0,0 +1,72 @@
-+package org.purpurmc.purpur.event.inventory;
-+
-+import org.bukkit.entity.HumanEntity;
-+import org.bukkit.entity.Player;
-+import org.bukkit.event.HandlerList;
-+import org.bukkit.event.inventory.InventoryEvent;
-+import org.bukkit.inventory.GrindstoneInventory;
-+import org.bukkit.inventory.InventoryView;
-+import org.bukkit.inventory.ItemStack;
-+import org.jetbrains.annotations.NotNull;
-+
-+/**
-+ * Called when a player takes the result item out of a Grindstone
-+ */
-+public class GrindstoneTakeResultEvent extends InventoryEvent {
-+ private static final HandlerList handlers = new HandlerList();
-+ private final Player player;
-+ private final ItemStack result;
-+ private int experienceAmount;
-+
-+ public GrindstoneTakeResultEvent(@NotNull HumanEntity player, @NotNull InventoryView view, @NotNull ItemStack result, int experienceAmount) {
-+ super(view);
-+ this.player = (Player) player;
-+ this.result = result;
-+ this.experienceAmount = experienceAmount;
-+ }
-+
-+ @NotNull
-+ public Player getPlayer() {
-+ return player;
-+ }
-+
-+ @NotNull
-+ public ItemStack getResult() {
-+ return result;
-+ }
-+
-+ @NotNull
-+ @Override
-+ public GrindstoneInventory getInventory() {
-+ return (GrindstoneInventory) super.getInventory();
-+ }
-+
-+ /**
-+ * Get the amount of experience this transaction will give
-+ *
-+ * @return Amount of experience to give
-+ */
-+ public int getExperienceAmount() {
-+ return this.experienceAmount;
-+ }
-+
-+ /**
-+ * Set the amount of experience this transaction will give
-+ *
-+ * @param experienceAmount Amount of experience to give
-+ */
-+ public void setExperienceAmount(int experienceAmount) {
-+ this.experienceAmount = experienceAmount;
-+ }
-+
-+ @NotNull
-+ @Override
-+ public HandlerList getHandlers() {
-+ return handlers;
-+ }
-+
-+ @NotNull
-+ public static HandlerList getHandlerList() {
-+ return handlers;
-+ }
-+}
diff --git a/patches/api/0032-Shears-can-have-looting-enchantment.patch b/patches/api/0032-Shears-can-have-looting-enchantment.patch
deleted file mode 100644
index 53f2a3f93..000000000
--- a/patches/api/0032-Shears-can-have-looting-enchantment.patch
+++ /dev/null
@@ -1,27 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: BillyGalbreath
-Date: Mon, 3 Jan 2022 02:00:50 -0600
-Subject: [PATCH] Shears can have looting enchantment
-
-
-diff --git a/src/main/java/org/bukkit/enchantments/EnchantmentTarget.java b/src/main/java/org/bukkit/enchantments/EnchantmentTarget.java
-index 5831ffe24eed01311c71989dcb1830dbc395607b..45f5493eebfecf56b7c0ef4659c078dfc62c0612 100644
---- a/src/main/java/org/bukkit/enchantments/EnchantmentTarget.java
-+++ b/src/main/java/org/bukkit/enchantments/EnchantmentTarget.java
-@@ -238,6 +238,16 @@ public enum EnchantmentTarget {
- public boolean includes(@NotNull Material item) {
- return item.equals(Material.BOW) || item.equals(Material.CROSSBOW);
- }
-+ },
-+
-+ /**
-+ * Allow the Enchantment to be placed on shears.
-+ */
-+ WEAPON_AND_SHEARS {
-+ @Override
-+ public boolean includes(@NotNull Material item) {
-+ return WEAPON.includes(item) || item.equals(Material.SHEARS);
-+ }
- // Purpur end
- };
-
diff --git a/patches/api/0033-Lobotomize-stuck-villagers.patch b/patches/api/0033-Lobotomize-stuck-villagers.patch
deleted file mode 100644
index 03b9bf50d..000000000
--- a/patches/api/0033-Lobotomize-stuck-villagers.patch
+++ /dev/null
@@ -1,25 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: BillyGalbreath
-Date: Mon, 24 Jan 2022 20:42:22 -0600
-Subject: [PATCH] Lobotomize stuck villagers
-
-
-diff --git a/src/main/java/org/bukkit/entity/Villager.java b/src/main/java/org/bukkit/entity/Villager.java
-index 3bc24457d143449e6a338d79becf7c39b9f81054..4a5edf4e72e81b22c1abb2ade244f7f4292e993c 100644
---- a/src/main/java/org/bukkit/entity/Villager.java
-+++ b/src/main/java/org/bukkit/entity/Villager.java
-@@ -328,4 +328,14 @@ public interface Villager extends AbstractVillager {
- */
- public void clearReputations();
- // Paper end
-+
-+ // Purpur start
-+
-+ /**
-+ * Check if villager is currently lobotomized
-+ *
-+ * @return True if lobotomized
-+ */
-+ boolean isLobotomized();
-+ // Purpur end
- }
diff --git a/patches/api/0034-Add-local-difficulty-api.patch b/patches/api/0034-Add-local-difficulty-api.patch
deleted file mode 100644
index 46e1e1486..000000000
--- a/patches/api/0034-Add-local-difficulty-api.patch
+++ /dev/null
@@ -1,27 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: BillyGalbreath
-Date: Sat, 9 Jul 2022 00:57:26 -0500
-Subject: [PATCH] Add local difficulty api
-
-
-diff --git a/src/main/java/org/bukkit/World.java b/src/main/java/org/bukkit/World.java
-index 97f97ea5c6aa513c439f86a9c82821e0f7d9cd1e..099516b90c504205b894b387542221e8c0c98b40 100644
---- a/src/main/java/org/bukkit/World.java
-+++ b/src/main/java/org/bukkit/World.java
-@@ -4249,6 +4249,16 @@ public interface World extends RegionAccessor, WorldInfo, PluginMessageRecipient
- @Nullable
- public DragonBattle getEnderDragonBattle();
-
-+ // Purpur start
-+ /**
-+ * Gets the local difficulty (based on inhabited time) at a location
-+ *
-+ * @param location Location to check
-+ * @return The local difficulty
-+ */
-+ public float getLocalDifficultyAt(@NotNull Location location);
-+ // Purpur end
-+
- /**
- * Get all {@link FeatureFlag} enabled in this world.
- *
diff --git a/patches/api/0035-Remove-Timings.patch b/patches/api/0035-Remove-Timings.patch
deleted file mode 100644
index 75f6b7465..000000000
--- a/patches/api/0035-Remove-Timings.patch
+++ /dev/null
@@ -1,168 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: BillyGalbreath
-Date: Fri, 1 Jul 2022 04:03:26 -0500
-Subject: [PATCH] Remove Timings
-
-
-diff --git a/src/main/java/co/aikar/timings/TimedEventExecutor.java b/src/main/java/co/aikar/timings/TimedEventExecutor.java
-index 8f29c1561ba5916cb5634392edd8bd2a5a294a51..6fbc64e0f214d0c8e5afcbe385e414a4e1fe1c72 100644
---- a/src/main/java/co/aikar/timings/TimedEventExecutor.java
-+++ b/src/main/java/co/aikar/timings/TimedEventExecutor.java
-@@ -77,9 +77,9 @@ public class TimedEventExecutor implements EventExecutor {
- executor.execute(listener, event);
- return;
- }
-- try (Timing ignored = timings.startTiming()){
-+ //try (Timing ignored = timings.startTiming()){ // Purpur
- executor.execute(listener, event);
-- }
-+ //} // Purpur
- }
-
- @Override
-diff --git a/src/main/java/co/aikar/timings/Timing.java b/src/main/java/co/aikar/timings/Timing.java
-index 7514fad26f955329f8bf17ff17db75f0c8301ee5..1d866e980abc542bdfee1ce082cd9cdd7761e9f7 100644
---- a/src/main/java/co/aikar/timings/Timing.java
-+++ b/src/main/java/co/aikar/timings/Timing.java
-@@ -39,6 +39,7 @@ public interface Timing extends AutoCloseable {
- * @return Timing
- */
- @NotNull
-+ @io.papermc.paper.annotation.DoNotUse // Purpur
- Timing startTiming();
-
- /**
-@@ -46,6 +47,7 @@ public interface Timing extends AutoCloseable {
- *
- * Will automatically be called when this Timing is used with try-with-resources
- */
-+ @io.papermc.paper.annotation.DoNotUse // Purpur
- void stopTiming();
-
- /**
-@@ -56,6 +58,7 @@ public interface Timing extends AutoCloseable {
- * @return Timing
- */
- @NotNull
-+ @io.papermc.paper.annotation.DoNotUse // Purpur
- Timing startTimingIfSync();
-
- /**
-@@ -65,12 +68,14 @@ public interface Timing extends AutoCloseable {
- *
- * But only if we are on the primary thread.
- */
-+ @io.papermc.paper.annotation.DoNotUse // Purpur
- void stopTimingIfSync();
-
- /**
- * @deprecated Doesn't do anything - Removed
- */
- @Deprecated
-+ @io.papermc.paper.annotation.DoNotUse // Purpur
- void abort();
-
- /**
-@@ -82,5 +87,6 @@ public interface Timing extends AutoCloseable {
- TimingHandler getTimingHandler();
-
- @Override
-+ @io.papermc.paper.annotation.DoNotUse // Purpur
- void close();
- }
-diff --git a/src/main/java/co/aikar/timings/Timings.java b/src/main/java/co/aikar/timings/Timings.java
-index 9812d668ad945aba486fbf6d5bf83c4292cb5d03..752d54830aa8baa1450bf72da03ae55ed30293c2 100644
---- a/src/main/java/co/aikar/timings/Timings.java
-+++ b/src/main/java/co/aikar/timings/Timings.java
-@@ -124,7 +124,7 @@ public final class Timings {
- @NotNull
- public static Timing ofStart(@NotNull Plugin plugin, @NotNull String name, @Nullable Timing groupHandler) {
- Timing timing = of(plugin, name, groupHandler);
-- timing.startTiming();
-+ //timing.startTiming(); // Purpur
- return timing;
- }
-
-@@ -145,9 +145,11 @@ public final class Timings {
- * @param enabled Should timings be reported
- */
- public static void setTimingsEnabled(boolean enabled) {
-- timingsEnabled = enabled;
-- warnAboutDeprecationOnEnable();
-- reset();
-+ // Purpur start - we don't do that here...
-+ timingsEnabled = false;
-+ //warnAboutDeprecationOnEnable();
-+ //reset();
-+ // Purpur end
- }
-
- private static void warnAboutDeprecationOnEnable() {
-diff --git a/src/main/java/co/aikar/timings/TimingsCommand.java b/src/main/java/co/aikar/timings/TimingsCommand.java
-index e801e79fa57c44b2e5d359647c920f88064826f1..1abfcee0f6d632f4cd8d74b4994a90c9ea9d254c 100644
---- a/src/main/java/co/aikar/timings/TimingsCommand.java
-+++ b/src/main/java/co/aikar/timings/TimingsCommand.java
-@@ -45,7 +45,7 @@ public class TimingsCommand extends BukkitCommand {
- public TimingsCommand(@NotNull String name) {
- super(name);
- this.description = "Manages Spigot Timings data to see performance of the server.";
-- this.usageMessage = "/timings ";
-+ this.usageMessage = "/timings";// "; // Purpur
- this.setPermission("bukkit.command.timings");
- }
-
-@@ -54,8 +54,12 @@ public class TimingsCommand extends BukkitCommand {
- if (!testPermission(sender)) {
- return true;
- }
-- if (false) {
-- sender.sendMessage(Timings.deprecationMessage());
-+ if (true) {
-+ net.kyori.adventure.text.minimessage.MiniMessage mm = net.kyori.adventure.text.minimessage.MiniMessage.miniMessage();
-+ sender.sendMessage(mm.deserialize("Purpur has removed timings to save your performance. Please use /spark instead"));
-+ sender.sendMessage(mm.deserialize("For more information, view its documentation at"));
-+ sender.sendMessage(mm.deserialize("https://spark.lucko.me/docs/Command-Usage"));
-+ return true;
- }
- if (args.length < 1) {
- sender.sendMessage(text("Usage: " + this.usageMessage, NamedTextColor.RED));
-@@ -115,7 +119,7 @@ public class TimingsCommand extends BukkitCommand {
- Preconditions.checkNotNull(args, "Arguments cannot be null");
- Preconditions.checkNotNull(alias, "Alias cannot be null");
-
-- if (args.length == 1) {
-+ if (false && args.length == 1) { // Purpur
- return StringUtil.copyPartialMatches(args[0], TIMINGS_SUBCOMMANDS,
- new ArrayList(TIMINGS_SUBCOMMANDS.size()));
- }
-diff --git a/src/main/java/org/bukkit/command/SimpleCommandMap.java b/src/main/java/org/bukkit/command/SimpleCommandMap.java
-index e842d13febca67ffa1c89fb2c1324d2609fb81fd..5349f16136d9348c374a7dfe5b89a71dfcb0e66d 100644
---- a/src/main/java/org/bukkit/command/SimpleCommandMap.java
-+++ b/src/main/java/org/bukkit/command/SimpleCommandMap.java
-@@ -163,10 +163,10 @@ public class SimpleCommandMap implements CommandMap {
- // Paper end
-
- try {
-- try (co.aikar.timings.Timing ignored = target.timings.startTiming()) { // Paper - use try with resources
-+ //try (co.aikar.timings.Timing ignored = target.timings.startTiming()) { // Paper - use try with resources // Purpur
- // Note: we don't return the result of target.execute as thats success / failure, we return handled (true) or not handled (false)
- target.execute(sender, sentCommandLabel, parsedArgs); // Purpur
-- } // target.timings.stopTiming(); // Spigot // Paper
-+ //} // target.timings.stopTiming(); // Spigot // Paper // Purpur
- } catch (CommandException ex) {
- server.getPluginManager().callEvent(new com.destroystokyo.paper.event.server.ServerExceptionEvent(new com.destroystokyo.paper.exception.ServerCommandException(ex, target, sender, args))); // Paper
- //target.timings.stopTiming(); // Spigot // Paper
-diff --git a/src/main/java/org/spigotmc/CustomTimingsHandler.java b/src/main/java/org/spigotmc/CustomTimingsHandler.java
-index 12946bd55fcf7c40d39081779a7fa30049ee6165..9c2d605c50cbf9aefa56ec209df9f6cea1392e89 100644
---- a/src/main/java/org/spigotmc/CustomTimingsHandler.java
-+++ b/src/main/java/org/spigotmc/CustomTimingsHandler.java
-@@ -61,7 +61,7 @@ public final class CustomTimingsHandler {
- handler = timing;
- }
-
-- public void startTiming() { handler.startTiming(); }
-- public void stopTiming() { handler.stopTiming(); }
-+ public void startTiming() { /*handler.startTiming();*/ } // Purpur
-+ public void stopTiming() { /*handler.stopTiming();*/ } // Purpur
-
- }
diff --git a/patches/api/0036-Add-Bee-API.patch b/patches/api/0036-Add-Bee-API.patch
deleted file mode 100644
index 6efae8520..000000000
--- a/patches/api/0036-Add-Bee-API.patch
+++ /dev/null
@@ -1,179 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: SageSphinx63920
-Date: Mon, 25 Jul 2022 19:33:49 +0200
-Subject: [PATCH] Add Bee API
-
-
-diff --git a/src/main/java/org/purpurmc/purpur/event/entity/BeeFoundFlowerEvent.java b/src/main/java/org/purpurmc/purpur/event/entity/BeeFoundFlowerEvent.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..833f46d1941f377765132fc528c45567ee0290d2
---- /dev/null
-+++ b/src/main/java/org/purpurmc/purpur/event/entity/BeeFoundFlowerEvent.java
-@@ -0,0 +1,48 @@
-+package org.purpurmc.purpur.event.entity;
-+
-+import org.bukkit.Location;
-+import org.bukkit.entity.Bee;
-+import org.bukkit.event.HandlerList;
-+import org.bukkit.event.entity.EntityEvent;
-+import org.jetbrains.annotations.NotNull;
-+import org.jetbrains.annotations.Nullable;
-+
-+/**
-+ * Called when a bee targets a flower
-+ */
-+public class BeeFoundFlowerEvent extends EntityEvent {
-+ private static final HandlerList handlers = new HandlerList();
-+ private final Location location;
-+
-+ public BeeFoundFlowerEvent(@NotNull Bee bee, @Nullable Location location) {
-+ super(bee);
-+ this.location = location;
-+ }
-+
-+ @Override
-+ @NotNull
-+ public Bee getEntity() {
-+ return (Bee) super.getEntity();
-+ }
-+
-+ /**
-+ * Returns the location of the flower that the bee targets
-+ *
-+ * @return The location of the flower
-+ */
-+ @Nullable
-+ public Location getLocation() {
-+ return location;
-+ }
-+
-+ @Override
-+ @NotNull
-+ public HandlerList getHandlers() {
-+ return handlers;
-+ }
-+
-+ @NotNull
-+ public static HandlerList getHandlerList() {
-+ return handlers;
-+ }
-+}
-diff --git a/src/main/java/org/purpurmc/purpur/event/entity/BeeStartedPollinatingEvent.java b/src/main/java/org/purpurmc/purpur/event/entity/BeeStartedPollinatingEvent.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..ae0bb654745724889c67fae9072ae90ea3778ba4
---- /dev/null
-+++ b/src/main/java/org/purpurmc/purpur/event/entity/BeeStartedPollinatingEvent.java
-@@ -0,0 +1,47 @@
-+package org.purpurmc.purpur.event.entity;
-+
-+import org.bukkit.Location;
-+import org.bukkit.entity.Bee;
-+import org.bukkit.event.HandlerList;
-+import org.bukkit.event.entity.EntityEvent;
-+import org.jetbrains.annotations.NotNull;
-+
-+/**
-+ * Called when a bee starts pollinating
-+ */
-+public class BeeStartedPollinatingEvent extends EntityEvent {
-+ private static final HandlerList handlers = new HandlerList();
-+ private final Location location;
-+
-+ public BeeStartedPollinatingEvent(@NotNull Bee bee, @NotNull Location location) {
-+ super(bee);
-+ this.location = location;
-+ }
-+
-+ @Override
-+ @NotNull
-+ public Bee getEntity() {
-+ return (Bee) super.getEntity();
-+ }
-+
-+ /**
-+ * Returns the location of the flower that the bee pollinates
-+ *
-+ * @return The location of the flower
-+ */
-+ @NotNull
-+ public Location getLocation() {
-+ return this.location;
-+ }
-+
-+ @Override
-+ @NotNull
-+ public HandlerList getHandlers() {
-+ return handlers;
-+ }
-+
-+ @NotNull
-+ public static HandlerList getHandlerList() {
-+ return handlers;
-+ }
-+}
-diff --git a/src/main/java/org/purpurmc/purpur/event/entity/BeeStopPollinatingEvent.java b/src/main/java/org/purpurmc/purpur/event/entity/BeeStopPollinatingEvent.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..ff3c9f075be2f624af8b0ce5fffc5ea69a41f32e
---- /dev/null
-+++ b/src/main/java/org/purpurmc/purpur/event/entity/BeeStopPollinatingEvent.java
-@@ -0,0 +1,60 @@
-+package org.purpurmc.purpur.event.entity;
-+
-+import org.bukkit.Location;
-+import org.bukkit.entity.Bee;
-+import org.bukkit.event.HandlerList;
-+import org.bukkit.event.entity.EntityEvent;
-+import org.jetbrains.annotations.NotNull;
-+import org.jetbrains.annotations.Nullable;
-+
-+/**
-+ * Called when a bee stops pollinating
-+ */
-+public class BeeStopPollinatingEvent extends EntityEvent {
-+ private static final HandlerList handlers = new HandlerList();
-+ private final Location location;
-+ private final boolean success;
-+
-+ public BeeStopPollinatingEvent(@NotNull Bee bee, @Nullable Location location, boolean success) {
-+ super(bee);
-+ this.location = location;
-+ this.success = success;
-+ }
-+
-+ @Override
-+ @NotNull
-+ public Bee getEntity() {
-+ return (Bee) super.getEntity();
-+ }
-+
-+ /**
-+ * Returns the location of the flower that the bee stopped pollinating
-+ *
-+ * @return The location of the flower
-+ */
-+ @Nullable
-+ public Location getLocation() {
-+ return location;
-+ }
-+
-+ /**
-+ * Returns whether the bee successfully pollinated the flower
-+ *
-+ * @return True if the pollination was successful
-+ */
-+ public boolean wasSuccessful() {
-+ return success;
-+ }
-+
-+
-+ @Override
-+ @NotNull
-+ public HandlerList getHandlers() {
-+ return handlers;
-+ }
-+
-+ @NotNull
-+ public static HandlerList getHandlerList() {
-+ return handlers;
-+ }
-+}
diff --git a/patches/api/0037-Debug-Marker-API.patch b/patches/api/0037-Debug-Marker-API.patch
deleted file mode 100644
index 8541e74ea..000000000
--- a/patches/api/0037-Debug-Marker-API.patch
+++ /dev/null
@@ -1,341 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: YouHaveTrouble
-Date: Sat, 23 Jul 2022 14:40:17 +0200
-Subject: [PATCH] Debug Marker API
-
-
-diff --git a/src/main/java/org/bukkit/Bukkit.java b/src/main/java/org/bukkit/Bukkit.java
-index 7bc4e5e9947ab646707a5c0b39c70c0cc6606bd8..70ef8f63ab79e102cb4326c21cc344488f4fbdd3 100644
---- a/src/main/java/org/bukkit/Bukkit.java
-+++ b/src/main/java/org/bukkit/Bukkit.java
-@@ -2948,5 +2948,89 @@ public final class Bukkit {
- public static void removeFuel(@NotNull Material material) {
- server.removeFuel(material);
- }
-+
-+ /**
-+ * Creates debug block highlight on specified block location and show it to all players on the server.
-+ *
-+ * Clients may be inconsistent in displaying it.
-+ * @param location Location to highlight
-+ * @param duration Duration for highlight to show in milliseconds
-+ */
-+ public static void sendBlockHighlight(@NotNull Location location, int duration) {
-+ server.sendBlockHighlight(location, duration);
-+ }
-+
-+ /**
-+ * Creates debug block highlight on specified block location and show it to all players on the server.
-+ *
-+ * Clients may be inconsistent in displaying it.
-+ * @param location Location to highlight
-+ * @param duration Duration for highlight to show in milliseconds
-+ * @param argb Color of the highlight. ARGB int. Will be ignored on some versions of vanilla client
-+ */
-+ public static void sendBlockHighlight(@NotNull Location location, int duration, int argb) {
-+ server.sendBlockHighlight(location, duration, argb);
-+ }
-+
-+ /**
-+ * Creates debug block highlight on specified block location and show it to all players on the server.
-+ *
-+ * Clients may be inconsistent in displaying it.
-+ * @param location Location to highlight
-+ * @param duration Duration for highlight to show in milliseconds
-+ * @param text Text to show above the highlight
-+ */
-+ public static void sendBlockHighlight(@NotNull Location location, int duration, @NotNull String text) {
-+ server.sendBlockHighlight(location, duration, text);
-+ }
-+
-+ /**
-+ * Creates debug block highlight on specified block location and show it to all players on the server.
-+ *
-+ * Clients may be inconsistent in displaying it.
-+ * @param location Location to highlight
-+ * @param duration Duration for highlight to show in milliseconds
-+ * @param text Text to show above the highlight
-+ * @param argb Color of the highlight. ARGB int. Will be ignored on some versions of vanilla client
-+ */
-+ public static void sendBlockHighlight(@NotNull Location location, int duration, @NotNull String text, int argb) {
-+ server.sendBlockHighlight(location, duration, text, argb);
-+ }
-+
-+ /**
-+ * Creates debug block highlight on specified block location and show it to all players on the server.
-+ *
-+ * Clients may be inconsistent in displaying it.
-+ * @param location Location to highlight
-+ * @param duration Duration for highlight to show in milliseconds
-+ * @param color Color of the highlight. Will be ignored on some versions of vanilla client
-+ * @param transparency Transparency of the highlight
-+ * @throws IllegalArgumentException If transparency is outside 0-255 range
-+ */
-+ public static void sendBlockHighlight(@NotNull Location location, int duration, @NotNull org.bukkit.Color color, int transparency) {
-+ server.sendBlockHighlight(location, duration, color, transparency);
-+ }
-+
-+ /**
-+ * Creates debug block highlight on specified block location and show it to all players on the server.
-+ *
-+ * Clients may be inconsistent in displaying it.
-+ * @param location Location to highlight
-+ * @param duration Duration for highlight to show in milliseconds
-+ * @param text Text to show above the highlight
-+ * @param color Color of the highlight. Will be ignored on some versions of vanilla client
-+ * @param transparency Transparency of the highlight
-+ * @throws IllegalArgumentException If transparency is outside 0-255 range
-+ */
-+ public static void sendBlockHighlight(@NotNull Location location, int duration, @NotNull String text, @NotNull org.bukkit.Color color, int transparency) {
-+ server.sendBlockHighlight(location, duration, text, color, transparency);
-+ }
-+
-+ /**
-+ * Clears all debug block highlights for all players on the server.
-+ */
-+ public static void clearBlockHighlights() {
-+ server.clearBlockHighlights();
-+ }
- // Purpur end
- }
-diff --git a/src/main/java/org/bukkit/Server.java b/src/main/java/org/bukkit/Server.java
-index fa926319ff033768d78508491e072efec1dcbc9f..320422ff95773efa01e8205edcc5c6c9a84d5ecd 100644
---- a/src/main/java/org/bukkit/Server.java
-+++ b/src/main/java/org/bukkit/Server.java
-@@ -2582,5 +2582,75 @@ public interface Server extends PluginMessageRecipient, net.kyori.adventure.audi
- * @param material The material that will no longer be a fuel
- */
- public void removeFuel(@NotNull Material material);
-+
-+ /**
-+ * Creates debug block highlight on specified block location and show it to all players on the server.
-+ *
-+ * Clients may be inconsistent in displaying it.
-+ * @param location Location to highlight
-+ * @param duration Duration for highlight to show in milliseconds
-+ */
-+ void sendBlockHighlight(@NotNull Location location, int duration);
-+
-+ /**
-+ * Creates debug block highlight on specified block location and show it to all players on the server.
-+ *
-+ * Clients may be inconsistent in displaying it.
-+ * @param location Location to highlight
-+ * @param duration Duration for highlight to show in milliseconds
-+ * @param argb Color of the highlight. ARGB int. Will be ignored on some versions of vanilla client
-+ */
-+ void sendBlockHighlight(@NotNull Location location, int duration, int argb);
-+
-+ /**
-+ * Creates debug block highlight on specified block location and show it to all players on the server.
-+ *
-+ * Clients may be inconsistent in displaying it.
-+ * @param location Location to highlight
-+ * @param duration Duration for highlight to show in milliseconds
-+ * @param text Text to show above the highlight
-+ */
-+ void sendBlockHighlight(@NotNull Location location, int duration, @NotNull String text);
-+
-+ /**
-+ * Creates debug block highlight on specified block location and show it to all players on the server.
-+ *
-+ * Clients may be inconsistent in displaying it.
-+ * @param location Location to highlight
-+ * @param duration Duration for highlight to show in milliseconds
-+ * @param text Text to show above the highlight
-+ * @param argb Color of the highlight. ARGB int. Will be ignored on some versions of vanilla client
-+ */
-+ void sendBlockHighlight(@NotNull Location location, int duration, @NotNull String text, int argb);
-+
-+ /**
-+ * Creates debug block highlight on specified block location and show it to all players on the server.
-+ *
-+ * Clients may be inconsistent in displaying it.
-+ * @param location Location to highlight
-+ * @param duration Duration for highlight to show in milliseconds
-+ * @param color Color of the highlight. Will be ignored on some versions of vanilla client
-+ * @param transparency Transparency of the highlight
-+ * @throws IllegalArgumentException If transparency is outside 0-255 range
-+ */
-+ void sendBlockHighlight(@NotNull Location location, int duration, @NotNull org.bukkit.Color color, int transparency);
-+
-+ /**
-+ * Creates debug block highlight on specified block location and show it to all players on the server.
-+ *
-+ * Clients may be inconsistent in displaying it.
-+ * @param location Location to highlight
-+ * @param duration Duration for highlight to show in milliseconds
-+ * @param text Text to show above the highlight
-+ * @param color Color of the highlight. Will be ignored on some versions of vanilla client
-+ * @param transparency Transparency of the highlight
-+ * @throws IllegalArgumentException If transparency is outside 0-255 range
-+ */
-+ void sendBlockHighlight(@NotNull Location location, int duration, @NotNull String text, @NotNull org.bukkit.Color color, int transparency);
-+
-+ /**
-+ * Clears all debug block highlights for all players on the server.
-+ */
-+ void clearBlockHighlights();
- // Purpur end
- }
-diff --git a/src/main/java/org/bukkit/World.java b/src/main/java/org/bukkit/World.java
-index 099516b90c504205b894b387542221e8c0c98b40..83a5b68c785a88594e6e3824ed282844086f7f1a 100644
---- a/src/main/java/org/bukkit/World.java
-+++ b/src/main/java/org/bukkit/World.java
-@@ -4257,6 +4257,76 @@ public interface World extends RegionAccessor, WorldInfo, PluginMessageRecipient
- * @return The local difficulty
- */
- public float getLocalDifficultyAt(@NotNull Location location);
-+
-+ /**
-+ * Creates debug block highlight on specified block location and show it to all players on this world.
-+ *
-+ * Clients may be inconsistent in displaying it.
-+ * @param location Location to highlight
-+ * @param duration Duration for highlight to show in milliseconds
-+ */
-+ void sendBlockHighlight(@NotNull Location location, int duration);
-+
-+ /**
-+ * Creates debug block highlight on specified block location and show it to all players on this world.
-+ *
-+ * Clients may be inconsistent in displaying it.
-+ * @param location Location to highlight
-+ * @param duration Duration for highlight to show in milliseconds
-+ * @param argb Color of the highlight. ARGB int. Will be ignored on some versions of vanilla client
-+ */
-+ void sendBlockHighlight(@NotNull Location location, int duration, int argb);
-+
-+ /**
-+ * Creates debug block highlight on specified block location and show it to all players on this world.
-+ *
-+ * Clients may be inconsistent in displaying it.
-+ * @param location Location to highlight
-+ * @param duration Duration for highlight to show in milliseconds
-+ * @param text Text to show above the highlight
-+ */
-+ void sendBlockHighlight(@NotNull Location location, int duration, @NotNull String text);
-+
-+ /**
-+ * Creates debug block highlight on specified block location and show it to all players on this world.
-+ *
-+ * Clients may be inconsistent in displaying it.
-+ * @param location Location to highlight
-+ * @param duration Duration for highlight to show in milliseconds
-+ * @param text Text to show above the highlight
-+ * @param argb Color of the highlight. ARGB int. Will be ignored on some versions of vanilla client
-+ */
-+ void sendBlockHighlight(@NotNull Location location, int duration, @NotNull String text, int argb);
-+
-+ /**
-+ * Creates debug block highlight on specified block location and show it to all players on this world.
-+ *
-+ * Clients may be inconsistent in displaying it.
-+ * @param location Location to highlight
-+ * @param duration Duration for highlight to show in milliseconds
-+ * @param color Color of the highlight. Will be ignored on some versions of vanilla client
-+ * @param transparency Transparency of the highlight
-+ * @throws IllegalArgumentException If transparency is outside 0-255 range
-+ */
-+ void sendBlockHighlight(@NotNull Location location, int duration, @NotNull org.bukkit.Color color, int transparency);
-+
-+ /**
-+ * Creates debug block highlight on specified block location and show it to all players on this world.
-+ *
-+ * Clients may be inconsistent in displaying it.
-+ * @param location Location to highlight
-+ * @param duration Duration for highlight to show in milliseconds
-+ * @param text Text to show above the highlight
-+ * @param color Color of the highlight. Will be ignored on some versions of vanilla client
-+ * @param transparency Transparency of the highlight
-+ * @throws IllegalArgumentException If transparency is outside 0-255 range
-+ */
-+ void sendBlockHighlight(@NotNull Location location, int duration, @NotNull String text, @NotNull org.bukkit.Color color, int transparency);
-+
-+ /**
-+ * Clears all debug block highlights for all players on this world.
-+ */
-+ void clearBlockHighlights();
- // Purpur end
-
- /**
-diff --git a/src/main/java/org/bukkit/entity/Player.java b/src/main/java/org/bukkit/entity/Player.java
-index 972cca3e02296f94099f965a4f7662ec63a067ea..ec49be86fa9b2612ae2853f06f503bffa3a1271d 100644
---- a/src/main/java/org/bukkit/entity/Player.java
-+++ b/src/main/java/org/bukkit/entity/Player.java
-@@ -3834,5 +3834,75 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
- * @deprecated Use {@link #resetIdleDuration()} instead
- */
- void resetIdleTimer();
-+
-+ /**
-+ * Creates debug block highlight on specified block location and show it to this player.
-+ *
-+ * Clients may be inconsistent in displaying it.
-+ * @param location Location to highlight
-+ * @param duration Duration for highlight to show in milliseconds
-+ */
-+ void sendBlockHighlight(@NotNull Location location, int duration);
-+
-+ /**
-+ * Creates debug block highlight on specified block location and show it to this player.
-+ *
-+ * Clients may be inconsistent in displaying it.
-+ * @param location Location to highlight
-+ * @param duration Duration for highlight to show in milliseconds
-+ * @param argb Color of the highlight. ARGB int. Will be ignored on some versions of vanilla client
-+ */
-+ void sendBlockHighlight(@NotNull Location location, int duration, int argb);
-+
-+ /**
-+ * Creates debug block highlight on specified block location and show it to this player.
-+ *
-+ * Clients may be inconsistent in displaying it.
-+ * @param location Location to highlight
-+ * @param duration Duration for highlight to show in milliseconds
-+ * @param text Text to show above the highlight
-+ */
-+ void sendBlockHighlight(@NotNull Location location, int duration, @NotNull String text);
-+
-+ /**
-+ * Creates debug block highlight on specified block location and show it to this player.
-+ *
-+ * Clients may be inconsistent in displaying it.
-+ * @param location Location to highlight
-+ * @param duration Duration for highlight to show in milliseconds
-+ * @param text Text to show above the highlight
-+ * @param argb Color of the highlight. ARGB int. Will be ignored on some versions of vanilla client
-+ */
-+ void sendBlockHighlight(@NotNull Location location, int duration, @NotNull String text, int argb);
-+
-+ /**
-+ * Creates debug block highlight on specified block location and show it to this player.
-+ *
-+ * Clients may be inconsistent in displaying it.
-+ * @param location Location to highlight
-+ * @param duration Duration for highlight to show in milliseconds
-+ * @param color Color of the highlight. Will be ignored on some versions of vanilla client
-+ * @param transparency Transparency of the highlight
-+ * @throws IllegalArgumentException If transparency is outside 0-255 range
-+ */
-+ void sendBlockHighlight(@NotNull Location location, int duration, @NotNull org.bukkit.Color color, int transparency);
-+
-+ /**
-+ * Creates debug block highlight on specified block location and show it to this player.
-+ *
-+ * Clients may be inconsistent in displaying it.
-+ * @param location Location to highlight
-+ * @param duration Duration for highlight to show in milliseconds
-+ * @param text Text to show above the highlight
-+ * @param color Color of the highlight. Will be ignored on some versions of vanilla client
-+ * @param transparency Transparency of the highlight
-+ * @throws IllegalArgumentException If transparency is outside 0-255 range
-+ */
-+ void sendBlockHighlight(@NotNull Location location, int duration, @NotNull String text, @NotNull org.bukkit.Color color, int transparency);
-+
-+ /**
-+ * Clears all debug block highlights
-+ */
-+ void clearBlockHighlights();
- // Purpur end
- }
diff --git a/patches/api/0038-Add-death-screen-API.patch b/patches/api/0038-Add-death-screen-API.patch
deleted file mode 100644
index 68f89598e..000000000
--- a/patches/api/0038-Add-death-screen-API.patch
+++ /dev/null
@@ -1,36 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: MelnCat
-Date: Fri, 23 Sep 2022 18:35:28 -0700
-Subject: [PATCH] Add death screen API
-
-
-diff --git a/src/main/java/org/bukkit/entity/Player.java b/src/main/java/org/bukkit/entity/Player.java
-index ec49be86fa9b2612ae2853f06f503bffa3a1271d..d057743b8f6a463434c1f76398c7a98614b19d47 100644
---- a/src/main/java/org/bukkit/entity/Player.java
-+++ b/src/main/java/org/bukkit/entity/Player.java
-@@ -3904,5 +3904,25 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
- * Clears all debug block highlights
- */
- void clearBlockHighlights();
-+
-+ /**
-+ * Sends a player the death screen with a specified death message.
-+ *
-+ * @param message The death message to show the player
-+ */
-+ void sendDeathScreen(@NotNull net.kyori.adventure.text.Component message);
-+
-+ /**
-+ * Sends a player the death screen with a specified death message,
-+ * along with the entity that caused the death.
-+ *
-+ * @param message The death message to show the player
-+ * @param killer The entity that killed the player
-+ * @deprecated Use {@link #sendDeathScreen(net.kyori.adventure.text.Component)} instead, as 1.20 removed the killer ID from the packet.
-+ */
-+ @Deprecated(since = "1.20")
-+ default void sendDeathScreen(@NotNull net.kyori.adventure.text.Component message, @Nullable Entity killer) {
-+ sendDeathScreen(message);
-+ }
- // Purpur end
- }
diff --git a/patches/api/0039-Language-API.patch b/patches/api/0039-Language-API.patch
deleted file mode 100644
index 1fbe2ebee..000000000
--- a/patches/api/0039-Language-API.patch
+++ /dev/null
@@ -1,72 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: MelnCat
-Date: Sat, 1 Oct 2022 17:08:23 -0700
-Subject: [PATCH] Language API
-
-
-diff --git a/src/main/java/org/purpurmc/purpur/language/Language.java b/src/main/java/org/purpurmc/purpur/language/Language.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..38483d908ed830e97883733bee2370f87060f4c7
---- /dev/null
-+++ b/src/main/java/org/purpurmc/purpur/language/Language.java
-@@ -0,0 +1,60 @@
-+package org.purpurmc.purpur.language;
-+
-+import net.kyori.adventure.translation.Translatable;
-+import org.jetbrains.annotations.NotNull;
-+
-+/**
-+ * Represents a language that can translate translation keys
-+ */
-+public abstract class Language {
-+ private static Language language;
-+
-+ /**
-+ * Returns the default language of the server
-+ */
-+ @NotNull
-+ public static Language getLanguage() {
-+ return language;
-+ }
-+
-+ public static void setLanguage(@NotNull Language language) {
-+ if (Language.language != null) {
-+ throw new UnsupportedOperationException("Cannot redefine singleton Language");
-+ }
-+ Language.language = language;
-+ }
-+
-+ /**
-+ * Checks if a certain translation key is translatable with this language
-+ * @param key The translation key
-+ * @return Whether this language can translate the key
-+ */
-+ abstract public boolean has(@NotNull String key);
-+
-+ /**
-+ * Checks if a certain translation key is translatable with this language
-+ * @param key The translation key
-+ * @return Whether this language can translate the key
-+ */
-+ public boolean has(@NotNull Translatable key) {
-+ return has(key.translationKey());
-+ }
-+
-+ /**
-+ * Translates a translation key to this language
-+ * @param key The translation key
-+ * @return The translated key, or the translation key if it couldn't be translated
-+ */
-+ @NotNull
-+ abstract public String getOrDefault(@NotNull String key);
-+
-+ /**
-+ * Translates a translation key to this language
-+ * @param key The translation key
-+ * @return The translated key, or the translation key if it couldn't be translated
-+ */
-+ @NotNull
-+ public String getOrDefault(@NotNull Translatable key) {
-+ return getOrDefault(key.translationKey());
-+ }
-+}
diff --git a/patches/api/0040-Add-log-suppression-for-LibraryLoader.patch b/patches/api/0040-Add-log-suppression-for-LibraryLoader.patch
deleted file mode 100644
index d1a85cb9d..000000000
--- a/patches/api/0040-Add-log-suppression-for-LibraryLoader.patch
+++ /dev/null
@@ -1,46 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Krakenied
-Date: Fri, 14 Oct 2022 23:11:27 +0200
-Subject: [PATCH] Add log suppression for LibraryLoader
-
-
-diff --git a/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java b/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java
-index eaefbb00e9993d54906cc8cf35cf753c0d6c7707..f1e58639213be0c43cd2ff090b625e7d0a67e8be 100644
---- a/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java
-+++ b/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java
-@@ -55,6 +55,7 @@ public final class JavaPluginLoader implements PluginLoader {
- private final Pattern[] fileFilters = new Pattern[]{Pattern.compile("\\.jar$")};
- private final List loaders = new CopyOnWriteArrayList();
- private final LibraryLoader libraryLoader;
-+ public static boolean SuppressLibraryLoaderLogger = false; // Purpur
-
- /**
- * This class was not meant to be constructed explicitly
-diff --git a/src/main/java/org/bukkit/plugin/java/LibraryLoader.java b/src/main/java/org/bukkit/plugin/java/LibraryLoader.java
-index 8e1b6be2462aaa692efa1f72986921a6dc357196..b6e18b12fd4d61ce92203582906d24b4d14e6cc5 100644
---- a/src/main/java/org/bukkit/plugin/java/LibraryLoader.java
-+++ b/src/main/java/org/bukkit/plugin/java/LibraryLoader.java
-@@ -68,6 +68,7 @@ public class LibraryLoader
- @Override
- public void transferStarted(@NotNull TransferEvent event) throws TransferCancelledException
- {
-+ if (!JavaPluginLoader.SuppressLibraryLoaderLogger) // Purpur
- logger.log( Level.INFO, "Downloading {0}", event.getResource().getRepositoryUrl() + event.getResource().getResourceName() );
- }
- } );
-@@ -88,6 +89,7 @@ public class LibraryLoader
- {
- return null;
- }
-+ if (!JavaPluginLoader.SuppressLibraryLoaderLogger) // Purpur
- logger.log( Level.INFO, "[{0}] Loading {1} libraries... please wait", new Object[]
- {
- java.util.Objects.requireNonNullElseGet(desc.getPrefix(), desc::getName), desc.getLibraries().size() // Paper - use configured log prefix
-@@ -135,6 +137,7 @@ public class LibraryLoader
- }
-
- jarFiles.add( url );
-+ if (!JavaPluginLoader.SuppressLibraryLoaderLogger) // Purpur
- logger.log( Level.INFO, "[{0}] Loaded library {1}", new Object[]
- {
- java.util.Objects.requireNonNullElseGet(desc.getPrefix(), desc::getName), file // Paper - use configured log prefix
diff --git a/patches/api/0041-Fire-Immunity-API.patch b/patches/api/0041-Fire-Immunity-API.patch
deleted file mode 100644
index d84da08f3..000000000
--- a/patches/api/0041-Fire-Immunity-API.patch
+++ /dev/null
@@ -1,29 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Racci <90304606+DaRacci@users.noreply.github.com>
-Date: Fri, 4 Feb 2022 16:09:47 +1100
-Subject: [PATCH] Fire Immunity API
-
-
-diff --git a/src/main/java/org/bukkit/entity/Entity.java b/src/main/java/org/bukkit/entity/Entity.java
-index f482467ae784b134da97eb38afb7f12585520297..07b8c0dd049ff783fd2e408be634642479bf8b1e 100644
---- a/src/main/java/org/bukkit/entity/Entity.java
-+++ b/src/main/java/org/bukkit/entity/Entity.java
-@@ -1192,5 +1192,18 @@ public interface Entity extends Metadatable, CommandSender, Nameable, Persistent
- * @return True if in daylight
- */
- boolean isInDaylight();
-+
-+ /**
-+ * Checks if the entity is fire immune
-+ *
-+ * @return True if fire immune
-+ */
-+ boolean isImmuneToFire();
-+
-+ /**
-+ * Sets if the entity is fire immune
-+ * Set this to null to restore the entity type default
-+ */
-+ void setImmuneToFire(@Nullable Boolean fireImmune);
- // Purpur end
- }
diff --git a/patches/api/0042-Added-goat-ram-event.patch b/patches/api/0042-Added-goat-ram-event.patch
deleted file mode 100644
index 7665c860d..000000000
--- a/patches/api/0042-Added-goat-ram-event.patch
+++ /dev/null
@@ -1,71 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: SageSphinx63920
-Date: Sat, 29 Oct 2022 00:06:05 +0200
-Subject: [PATCH] Added goat ram event
-
-
-diff --git a/src/main/java/org/purpurmc/purpur/event/entity/GoatRamEntityEvent.java b/src/main/java/org/purpurmc/purpur/event/entity/GoatRamEntityEvent.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..f62c14f3d4999e9112c1c73642aa337d97b94b5a
---- /dev/null
-+++ b/src/main/java/org/purpurmc/purpur/event/entity/GoatRamEntityEvent.java
-@@ -0,0 +1,59 @@
-+package org.purpurmc.purpur.event.entity;
-+
-+import org.bukkit.entity.Goat;
-+import org.bukkit.entity.LivingEntity;
-+import org.bukkit.event.Cancellable;
-+import org.bukkit.event.HandlerList;
-+import org.bukkit.event.entity.EntityEvent;
-+import org.jetbrains.annotations.NotNull;
-+
-+/**
-+ * Called when a goat rams an entity
-+ */
-+public class GoatRamEntityEvent extends EntityEvent implements Cancellable {
-+ private static final HandlerList handlers = new HandlerList();
-+ private final LivingEntity rammedEntity;
-+ private boolean cancelled;
-+
-+ public GoatRamEntityEvent(@NotNull Goat goat, @NotNull LivingEntity rammedEntity) {
-+ super(goat);
-+ this.rammedEntity = rammedEntity;
-+ }
-+
-+ /**
-+ * Returns the entity that was rammed by the goat
-+ *
-+ * @return The rammed entity
-+ */
-+ @NotNull
-+ public LivingEntity getRammedEntity() {
-+ return this.rammedEntity;
-+ }
-+
-+ @Override
-+ @NotNull
-+ public Goat getEntity() {
-+ return (Goat) super.getEntity();
-+ }
-+
-+ @Override
-+ @NotNull
-+ public HandlerList getHandlers() {
-+ return handlers;
-+ }
-+
-+ @NotNull
-+ public static HandlerList getHandlerList() {
-+ return handlers;
-+ }
-+
-+ @Override
-+ public boolean isCancelled() {
-+ return this.cancelled;
-+ }
-+
-+ @Override
-+ public void setCancelled(boolean cancel) {
-+ this.cancelled = cancel;
-+ }
-+}
diff --git a/patches/api/0043-Add-PreExplodeEvents.patch b/patches/api/0043-Add-PreExplodeEvents.patch
deleted file mode 100644
index db26aac79..000000000
--- a/patches/api/0043-Add-PreExplodeEvents.patch
+++ /dev/null
@@ -1,135 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: SageSphinx63920
-Date: Mon, 26 Dec 2022 23:40:13 +0100
-Subject: [PATCH] Add PreExplodeEvents
-
-
-diff --git a/src/main/java/org/purpurmc/purpur/event/PreBlockExplodeEvent.java b/src/main/java/org/purpurmc/purpur/event/PreBlockExplodeEvent.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..8ea97ddceedb7c719e8a50a0dd8f3f0919ca1647
---- /dev/null
-+++ b/src/main/java/org/purpurmc/purpur/event/PreBlockExplodeEvent.java
-@@ -0,0 +1,53 @@
-+package org.purpurmc.purpur.event;
-+
-+import org.bukkit.block.Block;
-+import org.bukkit.block.BlockState;
-+import org.bukkit.event.Cancellable;
-+import org.bukkit.event.HandlerList;
-+import org.bukkit.event.block.BlockExplodeEvent;
-+import org.jetbrains.annotations.NotNull;
-+import java.util.Collections;
-+
-+/**
-+ * Called before a block's explosion is processed
-+ */
-+public class PreBlockExplodeEvent extends BlockExplodeEvent implements Cancellable {
-+ private static final HandlerList handlers = new HandlerList();
-+ private boolean cancelled;
-+ private final float yield;
-+
-+ public PreBlockExplodeEvent(@NotNull final Block what, final float yield, @NotNull BlockState explodedBlockState) {
-+ super(what, explodedBlockState, Collections.emptyList(), yield);
-+ this.yield = yield;
-+ this.cancelled = false;
-+ }
-+
-+ /**
-+ * Returns the percentage of blocks to drop from this explosion
-+ *
-+ * @return The yield.
-+ */
-+ public float getYield() {
-+ return yield;
-+ }
-+
-+ @Override
-+ public boolean isCancelled() {
-+ return this.cancelled;
-+ }
-+
-+ @Override
-+ public void setCancelled(boolean cancel) {
-+ this.cancelled = cancel;
-+ }
-+
-+ @Override
-+ public @NotNull HandlerList getHandlers() {
-+ return handlers;
-+ }
-+
-+ @NotNull
-+ public static HandlerList getHandlerList() {
-+ return handlers;
-+ }
-+}
-diff --git a/src/main/java/org/purpurmc/purpur/event/entity/PreEntityExplodeEvent.java b/src/main/java/org/purpurmc/purpur/event/entity/PreEntityExplodeEvent.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..2d4f68228861492baaea0bcc604dfef623b337ba
---- /dev/null
-+++ b/src/main/java/org/purpurmc/purpur/event/entity/PreEntityExplodeEvent.java
-@@ -0,0 +1,64 @@
-+package org.purpurmc.purpur.event.entity;
-+
-+import org.bukkit.Location;
-+import org.bukkit.event.Cancellable;
-+import org.bukkit.event.HandlerList;
-+import org.bukkit.event.entity.EntityExplodeEvent;
-+import org.jetbrains.annotations.NotNull;
-+import java.util.Collections;
-+
-+/**
-+ * Called before an entity's explosion is processed
-+ */
-+public class PreEntityExplodeEvent extends EntityExplodeEvent implements Cancellable {
-+ private static final HandlerList handlers = new HandlerList();
-+ private boolean cancelled;
-+ private final float yield;
-+ private final Location location;
-+
-+ public PreEntityExplodeEvent(@NotNull org.bukkit.entity.Entity what, @NotNull final Location location, final float yield) {
-+ super(what, location, Collections.emptyList(), yield);
-+ this.cancelled = false;
-+ this.yield = yield;
-+ this.location = location;
-+ }
-+
-+ /**
-+ * Returns the percentage of blocks to drop from this explosion
-+ *
-+ * @return The yield.
-+ */
-+ public float getYield() {
-+ return yield;
-+ }
-+
-+ /**
-+ * Returns the location where the explosion happened.
-+ *
-+ * @return The location of the explosion
-+ */
-+ @NotNull
-+ public Location getLocation() {
-+ return location;
-+ }
-+
-+ @Override
-+ public boolean isCancelled() {
-+ return this.cancelled;
-+ }
-+
-+ @Override
-+ public void setCancelled(boolean cancel) {
-+ this.cancelled = cancel;
-+ }
-+
-+ @Override
-+ public @NotNull HandlerList getHandlers() {
-+ return handlers;
-+ }
-+
-+ @NotNull
-+ public static HandlerList getHandlerList() {
-+ return handlers;
-+ }
-+}
diff --git a/patches/api/0044-Stored-Bee-API.patch b/patches/api/0044-Stored-Bee-API.patch
deleted file mode 100644
index e155a6a0f..000000000
--- a/patches/api/0044-Stored-Bee-API.patch
+++ /dev/null
@@ -1,93 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: EOT3000
-Date: Sat, 10 Jun 2023 20:27:14 -0400
-Subject: [PATCH] Stored Bee API
-
-
-diff --git a/src/main/java/org/bukkit/block/EntityBlockStorage.java b/src/main/java/org/bukkit/block/EntityBlockStorage.java
-index 739911cda33b373f99df627a3a378b37d7d461aa..51e78c22cd021722b963fe31d1d9175d141add1a 100644
---- a/src/main/java/org/bukkit/block/EntityBlockStorage.java
-+++ b/src/main/java/org/bukkit/block/EntityBlockStorage.java
-@@ -47,6 +47,24 @@ public interface EntityBlockStorage extends TileState {
- @NotNull
- List releaseEntities();
-
-+ // Purpur start
-+ /**
-+ * Releases a stored entity, and returns the entity in the world.
-+ *
-+ * @param entity Entity to release
-+ * @return The entity which was released, or null if the stored entity is not in the hive
-+ */
-+ @org.jetbrains.annotations.Nullable
-+ T releaseEntity(@NotNull org.purpurmc.purpur.entity.StoredEntity entity);
-+
-+ /**
-+ * Gets all the entities currently stored in the block.
-+ *
-+ * @return List of all entities which are stored in the block
-+ */
-+ @NotNull
-+ List> getEntities();
-+ //Purpur end
- /**
- * Add an entity to the block.
- *
-diff --git a/src/main/java/org/purpurmc/purpur/entity/StoredEntity.java b/src/main/java/org/purpurmc/purpur/entity/StoredEntity.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..29540d55532197d2381a52ea9222b5785d224ef8
---- /dev/null
-+++ b/src/main/java/org/purpurmc/purpur/entity/StoredEntity.java
-@@ -0,0 +1,52 @@
-+package org.purpurmc.purpur.entity;
-+
-+import org.bukkit.Nameable;
-+import org.bukkit.block.EntityBlockStorage;
-+import org.bukkit.entity.Entity;
-+import org.bukkit.entity.EntityType;
-+import org.bukkit.persistence.PersistentDataHolder;
-+import org.jetbrains.annotations.NotNull;
-+import org.jetbrains.annotations.Nullable;
-+
-+/**
-+ * Represents an entity stored in a block
-+ *
-+ * @see org.bukkit.block.EntityBlockStorage
-+ */
-+public interface StoredEntity extends PersistentDataHolder, Nameable {
-+ /**
-+ * Checks if this entity has been released yet
-+ *
-+ * @return if this entity has been released
-+ */
-+ boolean hasBeenReleased();
-+
-+ /**
-+ * Releases the entity from its stored block
-+ *
-+ * @return the released entity, or null if unsuccessful (including if this entity has already been released)
-+ */
-+ @Nullable
-+ T release();
-+
-+ /**
-+ * Returns the block in which this entity is stored
-+ *
-+ * @return the EntityBlockStorage in which this entity is stored, or null if it has been released
-+ */
-+ @Nullable
-+ EntityBlockStorage getBlockStorage();
-+
-+ /**
-+ * Gets the entity type of this stored entity
-+ *
-+ * @return the type of entity this stored entity represents
-+ */
-+ @NotNull
-+ EntityType getType();
-+
-+ /**
-+ * Writes data to the block entity snapshot. {@link EntityBlockStorage#update()} must be run in order to update the block in game.
-+ */
-+ void update();
-+}
diff --git a/patches/api/0045-Explorer-Map-API.patch b/patches/api/0045-Explorer-Map-API.patch
deleted file mode 100644
index 91241d8f1..000000000
--- a/patches/api/0045-Explorer-Map-API.patch
+++ /dev/null
@@ -1,23 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: William Blake Galbreath
-Date: Wed, 5 Jul 2023 12:48:08 -0500
-Subject: [PATCH] Explorer Map API
-
-
-diff --git a/src/main/java/org/bukkit/map/MapRenderer.java b/src/main/java/org/bukkit/map/MapRenderer.java
-index cb7040876a99a5a7e49b81684ef0f3b79584c376..22d8f31b1b8a5dbb5ab3275068642937c097abfe 100644
---- a/src/main/java/org/bukkit/map/MapRenderer.java
-+++ b/src/main/java/org/bukkit/map/MapRenderer.java
-@@ -54,4 +54,12 @@ public abstract class MapRenderer {
- */
- public abstract void render(@NotNull MapView map, @NotNull MapCanvas canvas, @NotNull Player player);
-
-+ // Purpur - start
-+ /**
-+ * Check if this is an explorer (aka treasure) map.
-+ *
-+ * @return True if explorer map
-+ */
-+ public abstract boolean isExplorerMap();
-+ // Purpur - end
- }
diff --git a/patches/api/0046-Stonecutter-damage.patch b/patches/api/0046-Stonecutter-damage.patch
deleted file mode 100644
index 8d0888381..000000000
--- a/patches/api/0046-Stonecutter-damage.patch
+++ /dev/null
@@ -1,20 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: granny
-Date: Sun, 11 Feb 2024 23:07:47 -0800
-Subject: [PATCH] Stonecutter damage
-
-
-diff --git a/src/main/java/org/bukkit/event/entity/EntityDamageEvent.java b/src/main/java/org/bukkit/event/entity/EntityDamageEvent.java
-index 446b3ffd5caca5344be1c250475679834cd0d4a2..3da8d3d8925cd7a111c0c357bceecfd3a801c8eb 100644
---- a/src/main/java/org/bukkit/event/entity/EntityDamageEvent.java
-+++ b/src/main/java/org/bukkit/event/entity/EntityDamageEvent.java
-@@ -303,7 +303,8 @@ public class EntityDamageEvent extends EntityEvent implements Cancellable {
- WORLD_BORDER,
- /**
- * Damage caused when an entity contacts a block such as a Cactus,
-- * Dripstone (Stalagmite) or Berry Bush.
-+ * Dripstone (Stalagmite) or Berry Bush. (Stonecutters too if you
-+ * have the Stonecutter damage Purpur feature enabled)
- *
- * Damage: variable
- */
diff --git a/patches/generated-api/0001-Ridables.patch b/patches/generated-api/0001-Ridables.patch
deleted file mode 100644
index 4a9d76ce8..000000000
--- a/patches/generated-api/0001-Ridables.patch
+++ /dev/null
@@ -1,23 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: William Blake Galbreath
-Date: Sat, 4 May 2019 00:57:16 -0500
-Subject: [PATCH] Ridables
-
-
-diff --git a/com/destroystokyo/paper/entity/ai/VanillaGoal.java b/com/destroystokyo/paper/entity/ai/VanillaGoal.java
-index 069f2668f5229b0368b796e65eef1648fba0a097..50dc058cd8ecade74be30c75907d07f349c4df87 100644
---- a/com/destroystokyo/paper/entity/ai/VanillaGoal.java
-+++ b/com/destroystokyo/paper/entity/ai/VanillaGoal.java
-@@ -442,6 +442,12 @@ public interface VanillaGoal extends Goal {
-
- GoalKey ZOMBIE_ATTACK_TURTLE_EGG = create("zombie_attack_turtle_egg", Zombie.class);
-
-+ // Purpur start
-+ GoalKey MOB_HAS_RIDER = GoalKey.of(Mob.class, NamespacedKey.minecraft("has_rider"));
-+ GoalKey HORSE_HAS_RIDER = GoalKey.of(AbstractHorse.class, NamespacedKey.minecraft("horse_has_rider"));
-+ GoalKey LLAMA_HAS_RIDER = GoalKey.of(Llama.class, NamespacedKey.minecraft("llama_has_rider"));
-+ // Purpur end
-+
- /**
- * Removed in 1.20.2
- */
diff --git a/patches/generated-api/0002-Phantoms-attracted-to-crystals-and-crystals-shoot-ph.patch b/patches/generated-api/0002-Phantoms-attracted-to-crystals-and-crystals-shoot-ph.patch
deleted file mode 100644
index ddfb8fbce..000000000
--- a/patches/generated-api/0002-Phantoms-attracted-to-crystals-and-crystals-shoot-ph.patch
+++ /dev/null
@@ -1,19 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: William Blake Galbreath
-Date: Sun, 28 Jun 2020 21:50:55 -0500
-Subject: [PATCH] Phantoms attracted to crystals and crystals shoot phantoms
-
-
-diff --git a/com/destroystokyo/paper/entity/ai/VanillaGoal.java b/com/destroystokyo/paper/entity/ai/VanillaGoal.java
-index 50dc058cd8ecade74be30c75907d07f349c4df87..b3a1325786fffb1a585b3911c75c03d184e8a848 100644
---- a/com/destroystokyo/paper/entity/ai/VanillaGoal.java
-+++ b/com/destroystokyo/paper/entity/ai/VanillaGoal.java
-@@ -446,6 +446,8 @@ public interface VanillaGoal extends Goal {
- GoalKey MOB_HAS_RIDER = GoalKey.of(Mob.class, NamespacedKey.minecraft("has_rider"));
- GoalKey HORSE_HAS_RIDER = GoalKey.of(AbstractHorse.class, NamespacedKey.minecraft("horse_has_rider"));
- GoalKey LLAMA_HAS_RIDER = GoalKey.of(Llama.class, NamespacedKey.minecraft("llama_has_rider"));
-+ GoalKey FIND_CRYSTAL = GoalKey.of(Phantom.class, NamespacedKey.minecraft("find_crystal"));
-+ GoalKey ORBIT_CRYSTAL = GoalKey.of(Phantom.class, NamespacedKey.minecraft("orbit_crystal"));
- // Purpur end
-
- /**
diff --git a/patches/generated-api/0003-Add-option-to-disable-zombie-aggressiveness-towards-.patch b/patches/generated-api/0003-Add-option-to-disable-zombie-aggressiveness-towards-.patch
deleted file mode 100644
index fef774bc6..000000000
--- a/patches/generated-api/0003-Add-option-to-disable-zombie-aggressiveness-towards-.patch
+++ /dev/null
@@ -1,20 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: nitricspace
-Date: Wed, 23 Sep 2020 22:14:38 +0100
-Subject: [PATCH] Add option to disable zombie aggressiveness towards villagers
- when lagging
-
-
-diff --git a/com/destroystokyo/paper/entity/ai/VanillaGoal.java b/com/destroystokyo/paper/entity/ai/VanillaGoal.java
-index b3a1325786fffb1a585b3911c75c03d184e8a848..31c4d5da936f88e68c67c6e464b94563a47a0f34 100644
---- a/com/destroystokyo/paper/entity/ai/VanillaGoal.java
-+++ b/com/destroystokyo/paper/entity/ai/VanillaGoal.java
-@@ -448,6 +448,8 @@ public interface VanillaGoal extends Goal {
- GoalKey LLAMA_HAS_RIDER = GoalKey.of(Llama.class, NamespacedKey.minecraft("llama_has_rider"));
- GoalKey FIND_CRYSTAL = GoalKey.of(Phantom.class, NamespacedKey.minecraft("find_crystal"));
- GoalKey ORBIT_CRYSTAL = GoalKey.of(Phantom.class, NamespacedKey.minecraft("orbit_crystal"));
-+ GoalKey DROWNED_ATTACK_VILLAGER = GoalKey.of(Drowned.class, NamespacedKey.minecraft("drowned_attack_villager"));
-+ GoalKey ZOMBIE_ATTACK_VILLAGER = GoalKey.of(Zombie.class, NamespacedKey.minecraft("zombie_attack_villager"));
- // Purpur end
-
- /**
diff --git a/patches/generated-api/0004-Rabid-Wolf-API.patch b/patches/generated-api/0004-Rabid-Wolf-API.patch
deleted file mode 100644
index 82e3b3a72..000000000
--- a/patches/generated-api/0004-Rabid-Wolf-API.patch
+++ /dev/null
@@ -1,18 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Encode42
-Date: Tue, 8 Dec 2020 17:15:15 -0500
-Subject: [PATCH] Rabid Wolf API
-
-
-diff --git a/com/destroystokyo/paper/entity/ai/VanillaGoal.java b/com/destroystokyo/paper/entity/ai/VanillaGoal.java
-index 31c4d5da936f88e68c67c6e464b94563a47a0f34..a41aa6c187d60373630eb803cb69d58b85eacc98 100644
---- a/com/destroystokyo/paper/entity/ai/VanillaGoal.java
-+++ b/com/destroystokyo/paper/entity/ai/VanillaGoal.java
-@@ -450,6 +450,7 @@ public interface VanillaGoal extends Goal {
- GoalKey ORBIT_CRYSTAL = GoalKey.of(Phantom.class, NamespacedKey.minecraft("orbit_crystal"));
- GoalKey DROWNED_ATTACK_VILLAGER = GoalKey.of(Drowned.class, NamespacedKey.minecraft("drowned_attack_villager"));
- GoalKey ZOMBIE_ATTACK_VILLAGER = GoalKey.of(Zombie.class, NamespacedKey.minecraft("zombie_attack_villager"));
-+ GoalKey AVOID_RABID_WOLF = GoalKey.of(Wolf.class, NamespacedKey.minecraft("avoid_rabid_wolf"));
- // Purpur end
-
- /**
diff --git a/patches/generated-api/0005-Iron-golem-poppy-calms-anger.patch b/patches/generated-api/0005-Iron-golem-poppy-calms-anger.patch
deleted file mode 100644
index d85e4e893..000000000
--- a/patches/generated-api/0005-Iron-golem-poppy-calms-anger.patch
+++ /dev/null
@@ -1,18 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: William Blake Galbreath
-Date: Thu, 13 May 2021 21:38:01 -0500
-Subject: [PATCH] Iron golem poppy calms anger
-
-
-diff --git a/com/destroystokyo/paper/entity/ai/VanillaGoal.java b/com/destroystokyo/paper/entity/ai/VanillaGoal.java
-index a41aa6c187d60373630eb803cb69d58b85eacc98..9b991201a2f6cc9feccccf7f4e7bcded64117764 100644
---- a/com/destroystokyo/paper/entity/ai/VanillaGoal.java
-+++ b/com/destroystokyo/paper/entity/ai/VanillaGoal.java
-@@ -451,6 +451,7 @@ public interface VanillaGoal extends Goal {
- GoalKey DROWNED_ATTACK_VILLAGER = GoalKey.of(Drowned.class, NamespacedKey.minecraft("drowned_attack_villager"));
- GoalKey ZOMBIE_ATTACK_VILLAGER = GoalKey.of(Zombie.class, NamespacedKey.minecraft("zombie_attack_villager"));
- GoalKey AVOID_RABID_WOLF = GoalKey.of(Wolf.class, NamespacedKey.minecraft("avoid_rabid_wolf"));
-+ GoalKey RECEIVE_FLOWER = GoalKey.of(IronGolem.class, NamespacedKey.minecraft("receive_flower"));
- // Purpur end
-
- /**
diff --git a/patches/server/0002-Purpur-config-files.patch b/patches/server/0002-Purpur-config-files.patch
deleted file mode 100644
index d0887d441..000000000
--- a/patches/server/0002-Purpur-config-files.patch
+++ /dev/null
@@ -1,529 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: William Blake Galbreath
-Date: Thu, 9 May 2019 18:09:43 -0500
-Subject: [PATCH] Purpur config files
-
-
-diff --git a/src/main/java/com/destroystokyo/paper/Metrics.java b/src/main/java/com/destroystokyo/paper/Metrics.java
-index 4b002e8b75d117b726b0de274a76d3596fce015b..8cde30544e14f8fc2dac32966ae3c21f8cf3a551 100644
---- a/src/main/java/com/destroystokyo/paper/Metrics.java
-+++ b/src/main/java/com/destroystokyo/paper/Metrics.java
-@@ -593,7 +593,7 @@ public class Metrics {
- boolean logFailedRequests = config.getBoolean("logFailedRequests", false);
- // Only start Metrics, if it's enabled in the config
- if (config.getBoolean("enabled", true)) {
-- Metrics metrics = new Metrics("Paper", serverUUID, logFailedRequests, Bukkit.getLogger());
-+ Metrics metrics = new Metrics("Purpur", serverUUID, logFailedRequests, Bukkit.getLogger()); // Pufferfish // Purpur
-
- metrics.addCustomChart(new Metrics.SimplePie("minecraft_version", () -> {
- String minecraftVersion = Bukkit.getVersion();
-@@ -602,16 +602,8 @@ public class Metrics {
- }));
-
- metrics.addCustomChart(new Metrics.SingleLineChart("players", () -> Bukkit.getOnlinePlayers().size()));
-- metrics.addCustomChart(new Metrics.SimplePie("online_mode", () -> Bukkit.getOnlineMode() ? "online" : "offline"));
-- final String paperVersion;
-- final String implVersion = org.bukkit.craftbukkit.Main.class.getPackage().getImplementationVersion();
-- if (implVersion != null) {
-- final String buildOrHash = implVersion.substring(implVersion.lastIndexOf('-') + 1);
-- paperVersion = "git-Paper-%s-%s".formatted(Bukkit.getServer().getMinecraftVersion(), buildOrHash);
-- } else {
-- paperVersion = "unknown";
-- }
-- metrics.addCustomChart(new Metrics.SimplePie("paper_version", () -> paperVersion));
-+ metrics.addCustomChart(new Metrics.SimplePie("online_mode", () -> Bukkit.getOnlineMode() ? "online" : (io.papermc.paper.configuration.GlobalConfiguration.get().proxies.isProxyOnlineMode() ? "bungee" : "offline"))); // Purpur
-+ metrics.addCustomChart(new Metrics.SimplePie("purpur_version", () -> (org.bukkit.craftbukkit.Main.class.getPackage().getImplementationVersion() != null) ? org.bukkit.craftbukkit.Main.class.getPackage().getImplementationVersion() : "unknown")); // Purpur
-
- metrics.addCustomChart(new Metrics.DrilldownPie("java_version", () -> {
- Map> map = new HashMap<>();
-diff --git a/src/main/java/net/minecraft/commands/CommandSourceStack.java b/src/main/java/net/minecraft/commands/CommandSourceStack.java
-index e6c7f62ed379a78645933670299e4fcda8540ed1..edb94e5601acc38994dac20a167b145de778d426 100644
---- a/src/main/java/net/minecraft/commands/CommandSourceStack.java
-+++ b/src/main/java/net/minecraft/commands/CommandSourceStack.java
-@@ -331,6 +331,30 @@ public class CommandSourceStack implements ExecutionCommandSource io.papermc.paper.adventure.PaperAdventure.asVanilla(message), broadcastToOps);
-+ }
-+ // Purpur end
-+
- public void sendSuccess(Supplier feedbackSupplier, boolean broadcastToOps) {
- boolean flag1 = this.source.acceptsSuccess() && !this.silent;
- boolean flag2 = broadcastToOps && this.source.shouldInformAdmins() && !this.silent;
-diff --git a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
-index eb4fc900164d1fb3a78653ae8bc42ea30323f5b7..6d47aa73788eef78aa6714384db879b4e907de9b 100644
---- a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
-+++ b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
-@@ -232,6 +232,15 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface
- thread.start(); // Paper - Enhance console tab completions for brigadier commands; start console thread after MinecraftServer.console & PaperConfig are initialized
- io.papermc.paper.command.PaperCommands.registerCommands(this); // Paper - setup /paper command
- com.destroystokyo.paper.Metrics.PaperMetrics.startMetrics(); // Paper - start metrics
-+ // Purpur start
-+ try {
-+ org.purpurmc.purpur.PurpurConfig.init((java.io.File) options.valueOf("purpur-settings"));
-+ } catch (Exception e) {
-+ DedicatedServer.LOGGER.error("Unable to load server configuration", e);
-+ return false;
-+ }
-+ org.purpurmc.purpur.PurpurConfig.registerCommands();
-+ // Purpur end
- com.destroystokyo.paper.VersionHistoryManager.INSTANCE.getClass(); // Paper - load version history now
- io.papermc.paper.brigadier.PaperBrigadierProviderImpl.INSTANCE.getClass(); // Paper - init PaperBrigadierProvider
-
-diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java
-index b4ef3ad2c17168085372f1fe46809f02d9dfe74a..04f7f6743aabdca54892b2b155386f86032cf807 100644
---- a/src/main/java/net/minecraft/world/level/Level.java
-+++ b/src/main/java/net/minecraft/world/level/Level.java
-@@ -170,6 +170,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
- // Paper end - add paper world config
-
- public final com.destroystokyo.paper.antixray.ChunkPacketBlockController chunkPacketBlockController; // Paper - Anti-Xray
-+ public final org.purpurmc.purpur.PurpurWorldConfig purpurConfig; // Purpur
- public final co.aikar.timings.WorldTimingsHandler timings; // Paper
- public static BlockPos lastPhysicsProblem; // Spigot
- private org.spigotmc.TickLimiter entityLimiter;
-@@ -216,6 +217,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
- protected Level(WritableLevelData worlddatamutable, ResourceKey resourcekey, RegistryAccess iregistrycustom, Holder holder, Supplier supplier, boolean flag, boolean flag1, long i, int j, org.bukkit.generator.ChunkGenerator gen, org.bukkit.generator.BiomeProvider biomeProvider, org.bukkit.World.Environment env, java.util.function.Function paperWorldConfigCreator, java.util.concurrent.Executor executor) { // Paper - create paper world config; Async-Anti-Xray: Pass executor
- this.spigotConfig = new org.spigotmc.SpigotWorldConfig(((net.minecraft.world.level.storage.PrimaryLevelData) worlddatamutable).getLevelName()); // Spigot
- this.paperConfig = paperWorldConfigCreator.apply(this.spigotConfig); // Paper - create paper world config
-+ this.purpurConfig = new org.purpurmc.purpur.PurpurWorldConfig(((net.minecraft.world.level.storage.PrimaryLevelData) worlddatamutable).getLevelName(), env); // Purpur
- this.generator = gen;
- this.world = new CraftWorld((ServerLevel) this, gen, biomeProvider, env);
-
-diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
-index 467ef4ffd6ee13b247ac9e453b006ec3d89362c9..d97f33d654086c892f01e47a59be7e37f6e03ae7 100644
---- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
-+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
-@@ -1054,6 +1054,7 @@ public final class CraftServer implements Server {
-
- org.spigotmc.SpigotConfig.init((File) this.console.options.valueOf("spigot-settings")); // Spigot
- this.console.paperConfigurations.reloadConfigs(this.console);
-+ org.purpurmc.purpur.PurpurConfig.init((File) console.options.valueOf("purpur-settings")); // Purpur
- for (ServerLevel world : this.console.getAllLevels()) {
- // world.serverLevelData.setDifficulty(config.difficulty); // Paper - per level difficulty
- world.setSpawnSettings(world.serverLevelData.getDifficulty() != Difficulty.PEACEFUL && config.spawnMonsters, config.spawnAnimals); // Paper - per level difficulty (from MinecraftServer#setDifficulty(ServerLevel, Difficulty, boolean))
-@@ -1069,6 +1070,7 @@ public final class CraftServer implements Server {
- }
- }
- world.spigotConfig.init(); // Spigot
-+ world.purpurConfig.init(); // Purpur
- }
-
- Plugin[] pluginClone = pluginManager.getPlugins().clone(); // Paper
-@@ -1084,6 +1086,7 @@ public final class CraftServer implements Server {
- this.reloadData();
- org.spigotmc.SpigotConfig.registerCommands(); // Spigot
- io.papermc.paper.command.PaperCommands.registerCommands(this.console); // Paper
-+ org.purpurmc.purpur.PurpurConfig.registerCommands(); // Purpur
- this.overrideAllCommandBlockCommands = this.commandsConfiguration.getStringList("command-block-overrides").contains("*");
- this.ignoreVanillaPermissions = this.commandsConfiguration.getBoolean("ignore-vanilla-permissions");
-
-@@ -3048,6 +3051,18 @@ public final class CraftServer implements Server {
- return CraftServer.this.console.paperConfigurations.createLegacyObject(CraftServer.this.console);
- }
-
-+ // Purpur start
-+ @Override
-+ public YamlConfiguration getPurpurConfig() {
-+ return org.purpurmc.purpur.PurpurConfig.config;
-+ }
-+
-+ @Override
-+ public java.util.Properties getServerProperties() {
-+ return getProperties().properties;
-+ }
-+ // Purpur end
-+
- @Override
- public void restart() {
- org.spigotmc.RestartCommand.restart();
-diff --git a/src/main/java/org/bukkit/craftbukkit/Main.java b/src/main/java/org/bukkit/craftbukkit/Main.java
-index c988afa496d25314451435eedd64079a0d87cef0..45a33086e1bb8a70e0fa01090731d27a0474da41 100644
---- a/src/main/java/org/bukkit/craftbukkit/Main.java
-+++ b/src/main/java/org/bukkit/craftbukkit/Main.java
-@@ -175,6 +175,14 @@ public class Main {
- .describedAs("Jar file");
- // Paper end
-
-+ // Purpur Start
-+ acceptsAll(asList("purpur", "purpur-settings"), "File for purpur settings")
-+ .withRequiredArg()
-+ .ofType(File.class)
-+ .defaultsTo(new File("purpur.yml"))
-+ .describedAs("Yml file");
-+ // Purpur end
-+
- // Paper start
- acceptsAll(asList("server-name"), "Name of the server")
- .withRequiredArg()
-diff --git a/src/main/java/org/purpurmc/purpur/PurpurConfig.java b/src/main/java/org/purpurmc/purpur/PurpurConfig.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..4f015144be0a7a448c6c2b0765232c02ad405d09
---- /dev/null
-+++ b/src/main/java/org/purpurmc/purpur/PurpurConfig.java
-@@ -0,0 +1,175 @@
-+package org.purpurmc.purpur;
-+
-+import com.google.common.base.Throwables;
-+import com.google.common.collect.ImmutableMap;
-+import com.mojang.datafixers.util.Pair;
-+import net.kyori.adventure.bossbar.BossBar;
-+import net.kyori.adventure.text.minimessage.MiniMessage;
-+import net.minecraft.core.registries.BuiltInRegistries;
-+import net.minecraft.resources.ResourceLocation;
-+import net.minecraft.server.MinecraftServer;
-+import net.minecraft.world.effect.MobEffect;
-+import net.minecraft.world.effect.MobEffectInstance;
-+import net.minecraft.world.entity.EntityDimensions;
-+import net.minecraft.world.entity.EntityType;
-+import net.minecraft.world.food.FoodProperties;
-+import net.minecraft.world.food.Foods;
-+import net.minecraft.world.item.enchantment.Enchantment;
-+import net.minecraft.world.level.block.Block;
-+import net.minecraft.world.level.block.Blocks;
-+import org.bukkit.Bukkit;
-+import org.bukkit.command.Command;
-+import org.bukkit.configuration.ConfigurationSection;
-+import org.bukkit.configuration.InvalidConfigurationException;
-+import org.bukkit.configuration.file.YamlConfiguration;
-+import org.purpurmc.purpur.command.PurpurCommand;
-+import org.purpurmc.purpur.task.TPSBarTask;
-+
-+import java.io.File;
-+import java.io.IOException;
-+import java.lang.reflect.InvocationTargetException;
-+import java.lang.reflect.Method;
-+import java.lang.reflect.Modifier;
-+import java.util.ArrayList;
-+import java.util.Collections;
-+import java.util.HashMap;
-+import java.util.HashSet;
-+import java.util.List;
-+import java.util.Map;
-+import java.util.Set;
-+import java.util.logging.Level;
-+
-+@SuppressWarnings("unused")
-+public class PurpurConfig {
-+ private static final String HEADER = "This is the main configuration file for Purpur.\n"
-+ + "As you can see, there's tons to configure. Some options may impact gameplay, so use\n"
-+ + "with caution, and make sure you know what each option does before configuring.\n"
-+ + "\n"
-+ + "If you need help with the configuration or have any questions related to Purpur,\n"
-+ + "join us in our Discord guild.\n"
-+ + "\n"
-+ + "Website: https://purpurmc.org \n"
-+ + "Docs: https://purpurmc.org/docs \n";
-+ private static File CONFIG_FILE;
-+ public static YamlConfiguration config;
-+
-+ private static Map commands;
-+
-+ public static int version;
-+ static boolean verbose;
-+
-+ public static void init(File configFile) {
-+ CONFIG_FILE = configFile;
-+ config = new YamlConfiguration();
-+ try {
-+ config.load(CONFIG_FILE);
-+ } catch (IOException ignore) {
-+ } catch (InvalidConfigurationException ex) {
-+ Bukkit.getLogger().log(Level.SEVERE, "Could not load purpur.yml, please correct your syntax errors", ex);
-+ throw Throwables.propagate(ex);
-+ }
-+ config.options().header(HEADER);
-+ config.options().copyDefaults(true);
-+ verbose = getBoolean("verbose", false);
-+
-+ commands = new HashMap<>();
-+ commands.put("purpur", new PurpurCommand("purpur"));
-+
-+ version = getInt("config-version", 35);
-+ set("config-version", 35);
-+
-+ readConfig(PurpurConfig.class, null);
-+
-+ Blocks.rebuildCache();
-+ }
-+
-+ protected static void log(String s) {
-+ if (verbose) {
-+ log(Level.INFO, s);
-+ }
-+ }
-+
-+ protected static void log(Level level, String s) {
-+ Bukkit.getLogger().log(level, s);
-+ }
-+
-+ public static void registerCommands() {
-+ for (Map.Entry entry : commands.entrySet()) {
-+ MinecraftServer.getServer().server.getCommandMap().register(entry.getKey(), "Purpur", entry.getValue());
-+ }
-+ }
-+
-+ static void readConfig(Class> clazz, Object instance) {
-+ for (Method method : clazz.getDeclaredMethods()) {
-+ if (Modifier.isPrivate(method.getModifiers())) {
-+ if (method.getParameterTypes().length == 0 && method.getReturnType() == Void.TYPE) {
-+ try {
-+ method.setAccessible(true);
-+ method.invoke(instance);
-+ } catch (InvocationTargetException ex) {
-+ throw Throwables.propagate(ex.getCause());
-+ } catch (Exception ex) {
-+ Bukkit.getLogger().log(Level.SEVERE, "Error invoking " + method, ex);
-+ }
-+ }
-+ }
-+ }
-+
-+ try {
-+ config.save(CONFIG_FILE);
-+ } catch (IOException ex) {
-+ Bukkit.getLogger().log(Level.SEVERE, "Could not save " + CONFIG_FILE, ex);
-+ }
-+ }
-+
-+ private static void set(String path, Object val) {
-+ config.addDefault(path, val);
-+ config.set(path, val);
-+ }
-+
-+ private static String getString(String path, String def) {
-+ config.addDefault(path, def);
-+ return config.getString(path, config.getString(path));
-+ }
-+
-+ private static boolean getBoolean(String path, boolean def) {
-+ config.addDefault(path, def);
-+ return config.getBoolean(path, config.getBoolean(path));
-+ }
-+
-+ private static double getDouble(String path, double def) {
-+ config.addDefault(path, def);
-+ return config.getDouble(path, config.getDouble(path));
-+ }
-+
-+ private static int getInt(String path, int def) {
-+ config.addDefault(path, def);
-+ return config.getInt(path, config.getInt(path));
-+ }
-+
-+ private static List> getList(String path, T def) {
-+ config.addDefault(path, def);
-+ return config.getList(path, config.getList(path));
-+ }
-+
-+ static Map getMap(String path, Map def) {
-+ if (def != null && config.getConfigurationSection(path) == null) {
-+ config.addDefault(path, def);
-+ return def;
-+ }
-+ return toMap(config.getConfigurationSection(path));
-+ }
-+
-+ private static Map toMap(ConfigurationSection section) {
-+ ImmutableMap.Builder builder = ImmutableMap.builder();
-+ if (section != null) {
-+ for (String key : section.getKeys(false)) {
-+ Object obj = section.get(key);
-+ if (obj != null) {
-+ builder.put(key, obj instanceof ConfigurationSection val ? toMap(val) : obj);
-+ }
-+ }
-+ }
-+ return builder.build();
-+ }
-+}
-diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..5f0732c2b8f85185b6dfc1db3119c22e8be7f5da
---- /dev/null
-+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java
-@@ -0,0 +1,92 @@
-+package org.purpurmc.purpur;
-+
-+import net.minecraft.core.registries.BuiltInRegistries;
-+import net.minecraft.resources.ResourceLocation;
-+import net.minecraft.util.Mth;
-+import net.minecraft.world.entity.Entity;
-+import net.minecraft.world.entity.EntityType;
-+import net.minecraft.world.item.DyeColor;
-+import net.minecraft.world.item.Item;
-+import net.minecraft.world.item.Items;
-+import net.minecraft.world.level.block.Block;
-+import net.minecraft.world.level.block.Blocks;
-+import net.minecraft.world.level.block.state.properties.Tilt;
-+import org.purpurmc.purpur.tool.Flattenable;
-+import org.purpurmc.purpur.tool.Strippable;
-+import org.purpurmc.purpur.tool.Tillable;
-+import org.purpurmc.purpur.tool.Waxable;
-+import org.purpurmc.purpur.tool.Weatherable;
-+import org.apache.commons.lang.BooleanUtils;
-+import org.bukkit.ChatColor;
-+import org.bukkit.World;
-+import org.bukkit.configuration.ConfigurationSection;
-+import java.util.ArrayList;
-+import java.util.HashMap;
-+import java.util.List;
-+import java.util.Map;
-+import java.util.function.Predicate;
-+import java.util.logging.Level;
-+import static org.purpurmc.purpur.PurpurConfig.log;
-+
-+@SuppressWarnings("unused")
-+public class PurpurWorldConfig {
-+
-+ private final String worldName;
-+ private final World.Environment environment;
-+
-+ public PurpurWorldConfig(String worldName, World.Environment environment) {
-+ this.worldName = worldName;
-+ this.environment = environment;
-+ init();
-+ }
-+
-+ public void init() {
-+ log("-------- World Settings For [" + worldName + "] --------");
-+ PurpurConfig.readConfig(PurpurWorldConfig.class, this);
-+ }
-+
-+ private void set(String path, Object val) {
-+ PurpurConfig.config.addDefault("world-settings.default." + path, val);
-+ PurpurConfig.config.set("world-settings.default." + path, val);
-+ if (PurpurConfig.config.get("world-settings." + worldName + "." + path) != null) {
-+ PurpurConfig.config.addDefault("world-settings." + worldName + "." + path, val);
-+ PurpurConfig.config.set("world-settings." + worldName + "." + path, val);
-+ }
-+ }
-+
-+ private ConfigurationSection getConfigurationSection(String path) {
-+ ConfigurationSection section = PurpurConfig.config.getConfigurationSection("world-settings." + worldName + "." + path);
-+ return section != null ? section : PurpurConfig.config.getConfigurationSection("world-settings.default." + path);
-+ }
-+
-+ private String getString(String path, String def) {
-+ PurpurConfig.config.addDefault("world-settings.default." + path, def);
-+ return PurpurConfig.config.getString("world-settings." + worldName + "." + path, PurpurConfig.config.getString("world-settings.default." + path));
-+ }
-+
-+ private boolean getBoolean(String path, boolean def) {
-+ PurpurConfig.config.addDefault("world-settings.default." + path, def);
-+ return PurpurConfig.config.getBoolean("world-settings." + worldName + "." + path, PurpurConfig.config.getBoolean("world-settings.default." + path));
-+ }
-+
-+ private double getDouble(String path, double def) {
-+ PurpurConfig.config.addDefault("world-settings.default." + path, def);
-+ return PurpurConfig.config.getDouble("world-settings." + worldName + "." + path, PurpurConfig.config.getDouble("world-settings.default." + path));
-+ }
-+
-+ private int getInt(String path, int def) {
-+ PurpurConfig.config.addDefault("world-settings.default." + path, def);
-+ return PurpurConfig.config.getInt("world-settings." + worldName + "." + path, PurpurConfig.config.getInt("world-settings.default." + path));
-+ }
-+
-+ private List> getList(String path, T def) {
-+ PurpurConfig.config.addDefault("world-settings.default." + path, def);
-+ return PurpurConfig.config.getList("world-settings." + worldName + "." + path, PurpurConfig.config.getList("world-settings.default." + path));
-+ }
-+
-+ private Map getMap(String path, Map def) {
-+ final Map fallback = PurpurConfig.getMap("world-settings.default." + path, def);
-+ final Map value = PurpurConfig.getMap("world-settings." + worldName + "." + path, null);
-+ return value.isEmpty() ? fallback : value;
-+ }
-+}
-diff --git a/src/main/java/org/purpurmc/purpur/command/PurpurCommand.java b/src/main/java/org/purpurmc/purpur/command/PurpurCommand.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..afdf04f8b22ad0b7c0b41675e44687b49c2f86d6
---- /dev/null
-+++ b/src/main/java/org/purpurmc/purpur/command/PurpurCommand.java
-@@ -0,0 +1,65 @@
-+package org.purpurmc.purpur.command;
-+
-+import net.minecraft.server.MinecraftServer;
-+import net.minecraft.server.level.ServerLevel;
-+import org.purpurmc.purpur.PurpurConfig;
-+import org.bukkit.ChatColor;
-+import org.bukkit.Location;
-+import org.bukkit.command.Command;
-+import org.bukkit.command.CommandSender;
-+
-+import java.io.File;
-+import java.util.Collections;
-+import java.util.List;
-+import java.util.stream.Collectors;
-+import java.util.stream.Stream;
-+
-+public class PurpurCommand extends Command {
-+ public PurpurCommand(String name) {
-+ super(name);
-+ this.description = "Purpur related commands";
-+ this.usageMessage = "/purpur [reload | version]";
-+ this.setPermission("bukkit.command.purpur");
-+ }
-+
-+ @Override
-+ public List tabComplete(CommandSender sender, String alias, String[] args, Location location) throws IllegalArgumentException {
-+ if (args.length == 1) {
-+ return Stream.of("reload", "version")
-+ .filter(arg -> arg.startsWith(args[0].toLowerCase()))
-+ .collect(Collectors.toList());
-+ }
-+ return Collections.emptyList();
-+ }
-+
-+ @Override
-+ public boolean execute(CommandSender sender, String commandLabel, String[] args) {
-+ if (!testPermission(sender)) return true;
-+
-+ if (args.length != 1) {
-+ sender.sendMessage(ChatColor.RED + "Usage: " + usageMessage);
-+ return false;
-+ }
-+
-+ if (args[0].equalsIgnoreCase("reload")) {
-+ Command.broadcastCommandMessage(sender, ChatColor.RED + "Please note that this command is not supported and may cause issues.");
-+ Command.broadcastCommandMessage(sender, ChatColor.RED + "If you encounter any issues please use the /stop command to restart your server.");
-+
-+ MinecraftServer console = MinecraftServer.getServer();
-+ PurpurConfig.init((File) console.options.valueOf("purpur-settings"));
-+ for (ServerLevel level : console.getAllLevels()) {
-+ level.purpurConfig.init();
-+ }
-+ console.server.reloadCount++;
-+
-+ Command.broadcastCommandMessage(sender, ChatColor.GREEN + "Purpur config reload complete.");
-+ } else if (args[0].equalsIgnoreCase("version")) {
-+ Command verCmd = org.bukkit.Bukkit.getServer().getCommandMap().getCommand("version");
-+ if (verCmd != null) {
-+ return verCmd.execute(sender, commandLabel, new String[0]);
-+ }
-+ }
-+
-+ return true;
-+ }
-+}
diff --git a/patches/server/0003-Purpur-client-support.patch b/patches/server/0003-Purpur-client-support.patch
deleted file mode 100644
index c6be6a65a..000000000
--- a/patches/server/0003-Purpur-client-support.patch
+++ /dev/null
@@ -1,60 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: William Blake Galbreath
-Date: Fri, 30 Jul 2021 14:31:25 -0500
-Subject: [PATCH] Purpur client support
-
-
-diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java
-index 8437316888c6056060a2780652147590b6fe7443..8b07eb1c428bc70b8ee07ea2209f1898d6034809 100644
---- a/src/main/java/net/minecraft/server/level/ServerPlayer.java
-+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java
-@@ -298,6 +298,7 @@ public class ServerPlayer extends Player {
- public com.destroystokyo.paper.event.entity.PlayerNaturallySpawnCreaturesEvent playerNaturallySpawnedEvent; // Paper - PlayerNaturallySpawnCreaturesEvent
- public @Nullable String clientBrandName = null; // Paper - Brand support
- public org.bukkit.event.player.PlayerQuitEvent.QuitReason quitReason = null; // Paper - Add API for quit reason; there are a lot of changes to do if we change all methods leading to the event
-+ public boolean purpurClient = false; // Purpur
-
- // Paper start - replace player chunk loader
- private final java.util.concurrent.atomic.AtomicReference viewDistances = new java.util.concurrent.atomic.AtomicReference<>(new io.papermc.paper.chunk.system.RegionizedPlayerChunkLoader.ViewDistances(-1, -1, -1));
-diff --git a/src/main/java/net/minecraft/server/network/ServerCommonPacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerCommonPacketListenerImpl.java
-index 8ac5d8ccf731100a1be690cb2ed1be82cadba8ed..4228fd441f8350d43bd545e31c920304f07968bc 100644
---- a/src/main/java/net/minecraft/server/network/ServerCommonPacketListenerImpl.java
-+++ b/src/main/java/net/minecraft/server/network/ServerCommonPacketListenerImpl.java
-@@ -78,6 +78,7 @@ public abstract class ServerCommonPacketListenerImpl implements ServerCommonPack
- public final java.util.Map packCallbacks = new java.util.concurrent.ConcurrentHashMap<>(); // Paper - adventure resource pack callbacks
- private static final long KEEPALIVE_LIMIT = Long.getLong("paper.playerconnection.keepalive", 30) * 1000; // Paper - provide property to set keepalive limit
- protected static final ResourceLocation MINECRAFT_BRAND = new ResourceLocation("brand"); // Paper - Brand support
-+ protected static final ResourceLocation PURPUR_CLIENT = new ResourceLocation("purpur", "client"); // Purpur
-
- public ServerCommonPacketListenerImpl(MinecraftServer minecraftserver, Connection networkmanager, CommonListenerCookie commonlistenercookie, ServerPlayer player) { // CraftBukkit
- this.server = minecraftserver;
-@@ -170,6 +171,13 @@ public abstract class ServerCommonPacketListenerImpl implements ServerCommonPack
- ServerGamePacketListenerImpl.LOGGER.error("Couldn\'t register custom payload", ex);
- this.disconnect("Invalid payload REGISTER!", org.bukkit.event.player.PlayerKickEvent.Cause.INVALID_PAYLOAD); // Paper - kick event cause
- }
-+ // Purpur start
-+ } else if (identifier.equals(PURPUR_CLIENT)) {
-+ try {
-+ player.purpurClient = true;
-+ } catch (Exception ignore) {
-+ }
-+ // Purpur end
- } else if (identifier.equals(ServerCommonPacketListenerImpl.CUSTOM_UNREGISTER)) {
- try {
- String channels = payload.toString(com.google.common.base.Charsets.UTF_8);
-diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
-index 5f896948d158651cd9837364759dbfbcce6b7d21..7f78db4e0331d0f4801807ed1242642ee940d0f8 100644
---- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
-+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
-@@ -3522,4 +3522,11 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
- public void setSendViewDistance(final int viewDistance) {
- this.getHandle().setSendViewDistance(viewDistance);
- }
-+
-+ // Purpur start
-+ @Override
-+ public boolean usesPurpurClient() {
-+ return getHandle().purpurClient;
-+ }
-+ // Purpur end
- }
diff --git a/patches/server/0004-Fix-decompile-errors.patch b/patches/server/0004-Fix-decompile-errors.patch
deleted file mode 100644
index f16d294f4..000000000
--- a/patches/server/0004-Fix-decompile-errors.patch
+++ /dev/null
@@ -1,52 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: BillyGalbreath
-Date: Sun, 12 Jun 2022 06:20:21 -0500
-Subject: [PATCH] Fix decompile errors
-
-
-diff --git a/src/main/java/net/minecraft/commands/execution/tasks/BuildContexts.java b/src/main/java/net/minecraft/commands/execution/tasks/BuildContexts.java
-index b0d26b0eadb2a43924629424a6c13198aace8f69..9f5c3ec2eae9b30bdb8dbcb328d7f701cb7aeb9d 100644
---- a/src/main/java/net/minecraft/commands/execution/tasks/BuildContexts.java
-+++ b/src/main/java/net/minecraft/commands/execution/tasks/BuildContexts.java
-@@ -52,7 +52,7 @@ public class BuildContexts> {
- }
-
- RedirectModifier redirectModifier = commandContext.getRedirectModifier();
-- if (redirectModifier instanceof CustomModifierExecutor customModifierExecutor) {
-+ if (redirectModifier instanceof CustomModifierExecutor customModifierExecutor) { // Purpur - decompile error
- customModifierExecutor.apply(baseSource, list, contextChain, chainModifiers, ExecutionControl.create(context, frame));
- return;
- }
-@@ -92,11 +92,11 @@ public class BuildContexts> {
-
- if (list.isEmpty()) {
- if (chainModifiers.isReturn()) {
-- context.queueNext(new CommandQueueEntry<>(frame, FallthroughTask.instance()));
-+ context.queueNext(new CommandQueueEntry<>(frame, (EntryAction) FallthroughTask.instance())); // Purpur - decompile error
- }
- } else {
- CommandContext commandContext2 = contextChain.getTopContext();
-- if (commandContext2.getCommand() instanceof CustomCommandExecutor customCommandExecutor) {
-+ if (commandContext2.getCommand() instanceof CustomCommandExecutor customCommandExecutor) { // Purpur - decompile error
- ExecutionControl executionControl = ExecutionControl.create(context, frame);
-
- for (T executionCommandSource2 : list) {
-diff --git a/src/main/java/net/minecraft/commands/synchronization/ArgumentTypeInfos.java b/src/main/java/net/minecraft/commands/synchronization/ArgumentTypeInfos.java
-index 7b118a92a6eb779f800ae8f5d8f6e3c861fc4f6a..057a038e8dcacd7496a0b2373de2c20255a5c297 100644
---- a/src/main/java/net/minecraft/commands/synchronization/ArgumentTypeInfos.java
-+++ b/src/main/java/net/minecraft/commands/synchronization/ArgumentTypeInfos.java
-@@ -119,10 +119,10 @@ public class ArgumentTypeInfos {
- register(registry, "dimension", DimensionArgument.class, SingletonArgumentInfo.contextFree(DimensionArgument::dimension));
- register(registry, "gamemode", GameModeArgument.class, SingletonArgumentInfo.contextFree(GameModeArgument::gameMode));
- register(registry, "time", TimeArgument.class, new TimeArgument.Info());
-- register(registry, "resource_or_tag", fixClassType(ResourceOrTagArgument.class), new ResourceOrTagArgument.Info());
-- register(registry, "resource_or_tag_key", fixClassType(ResourceOrTagKeyArgument.class), new ResourceOrTagKeyArgument.Info());
-- register(registry, "resource", fixClassType(ResourceArgument.class), new ResourceArgument.Info());
-- register(registry, "resource_key", fixClassType(ResourceKeyArgument.class), new ResourceKeyArgument.Info());
-+ register(registry, "resource_or_tag", fixClassType(ResourceOrTagArgument.class), new ResourceOrTagArgument.Info<>()); // Purpur - decompile error
-+ register(registry, "resource_or_tag_key", fixClassType(ResourceOrTagKeyArgument.class), new ResourceOrTagKeyArgument.Info<>()); // Purpur - decompile error
-+ register(registry, "resource", fixClassType(ResourceArgument.class), new ResourceArgument.Info<>()); // Purpur - decompile error
-+ register(registry, "resource_key", fixClassType(ResourceKeyArgument.class), new ResourceKeyArgument.Info<>()); // Purpur - decompile error
- register(registry, "template_mirror", TemplateMirrorArgument.class, SingletonArgumentInfo.contextFree(TemplateMirrorArgument::templateMirror));
- register(registry, "template_rotation", TemplateRotationArgument.class, SingletonArgumentInfo.contextFree(TemplateRotationArgument::templateRotation));
- register(registry, "heightmap", HeightmapTypeArgument.class, SingletonArgumentInfo.contextFree(HeightmapTypeArgument::heightmap));
diff --git a/patches/server/0005-Component-related-conveniences.patch b/patches/server/0005-Component-related-conveniences.patch
deleted file mode 100644
index 934ea5bc3..000000000
--- a/patches/server/0005-Component-related-conveniences.patch
+++ /dev/null
@@ -1,107 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: William Blake Galbreath
-Date: Tue, 29 Jun 2021 21:37:40 -0500
-Subject: [PATCH] Component related conveniences
-
-
-diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java
-index 8b07eb1c428bc70b8ee07ea2209f1898d6034809..0b9c85adaf5c7b5dcb23ab74a4fd02e6c0798851 100644
---- a/src/main/java/net/minecraft/server/level/ServerPlayer.java
-+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java
-@@ -1978,6 +1978,26 @@ public class ServerPlayer extends Player {
- this.lastSentExp = -1; // CraftBukkit - Added to reset
- }
-
-+ // Purpur start
-+ public void sendActionBarMessage(@Nullable String message) {
-+ if (message != null && !message.isEmpty()) {
-+ sendActionBarMessage(net.kyori.adventure.text.minimessage.MiniMessage.miniMessage().deserialize(message));
-+ }
-+ }
-+
-+ public void sendActionBarMessage(@Nullable net.kyori.adventure.text.Component message) {
-+ if (message != null) {
-+ sendActionBarMessage(io.papermc.paper.adventure.PaperAdventure.asVanilla(message));
-+ }
-+ }
-+
-+ public void sendActionBarMessage(@Nullable Component message) {
-+ if (message != null) {
-+ displayClientMessage(message, true);
-+ }
-+ }
-+ // Purpur end
-+
- @Override
- public void displayClientMessage(Component message, boolean overlay) {
- this.sendSystemMessage(message, overlay);
-diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java
-index a2142930b4d4b05987c90496fb9d733d99040aa0..2d6b6795703431939005aa09d1ed590c3f755163 100644
---- a/src/main/java/net/minecraft/server/players/PlayerList.java
-+++ b/src/main/java/net/minecraft/server/players/PlayerList.java
-@@ -1063,6 +1063,20 @@ public abstract class PlayerList {
- }
- // CraftBukkit end
-
-+ // Purpur Start
-+ public void broadcastMiniMessage(@Nullable String message, boolean overlay) {
-+ if (message != null && !message.isEmpty()) {
-+ this.broadcastMessage(net.kyori.adventure.text.minimessage.MiniMessage.miniMessage().deserialize(message), overlay);
-+ }
-+ }
-+
-+ public void broadcastMessage(@Nullable net.kyori.adventure.text.Component message, boolean overlay) {
-+ if (message != null) {
-+ this.broadcastSystemMessage(io.papermc.paper.adventure.PaperAdventure.asVanilla(message), overlay);
-+ }
-+ }
-+ // Purpur end
-+
- public void broadcastAll(Packet> packet, ResourceKey dimension) {
- Iterator iterator = this.players.iterator();
-
-diff --git a/src/main/java/net/minecraft/world/damagesource/DamageSource.java b/src/main/java/net/minecraft/world/damagesource/DamageSource.java
-index 160dc3216e8f5db5f9b3cce5e2d655f2b35b208a..43b9a7e8ed9043c4d3f8295258a27209ddb4474b 100644
---- a/src/main/java/net/minecraft/world/damagesource/DamageSource.java
-+++ b/src/main/java/net/minecraft/world/damagesource/DamageSource.java
-@@ -177,6 +177,15 @@ public class DamageSource {
- }
- }
-
-+ // Purpur start
-+ public Component getLocalizedDeathMessage(String str, LivingEntity entity) {
-+ net.kyori.adventure.text.Component name = io.papermc.paper.adventure.PaperAdventure.asAdventure(entity.getDisplayName());
-+ net.kyori.adventure.text.minimessage.tag.resolver.TagResolver template = net.kyori.adventure.text.minimessage.tag.resolver.Placeholder.component("player", name);
-+ net.kyori.adventure.text.Component component = net.kyori.adventure.text.minimessage.MiniMessage.miniMessage().deserialize(str, template);
-+ return io.papermc.paper.adventure.PaperAdventure.asVanilla(component);
-+ }
-+ // Purpur end
-+
- public String getMsgId() {
- return this.type().msgId();
- }
-diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
-index 2bc85351e6e52f90da5fdb29d8d042a06132d742..6c7f80723d67baf0ca036e0a12c3007144352edf 100644
---- a/src/main/java/net/minecraft/world/entity/Entity.java
-+++ b/src/main/java/net/minecraft/world/entity/Entity.java
-@@ -4190,6 +4190,20 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
- return SlotAccess.NULL;
- }
-
-+ // Purpur Start
-+ public void sendMiniMessage(@Nullable String message) {
-+ if (message != null && !message.isEmpty()) {
-+ this.sendMessage(net.kyori.adventure.text.minimessage.MiniMessage.miniMessage().deserialize(message));
-+ }
-+ }
-+
-+ public void sendMessage(@Nullable net.kyori.adventure.text.Component message) {
-+ if (message != null) {
-+ this.sendSystemMessage(io.papermc.paper.adventure.PaperAdventure.asVanilla(message));
-+ }
-+ }
-+ // Purpur end
-+
- @Override
- public void sendSystemMessage(Component message) {}
-
diff --git a/patches/server/0006-Ridables.patch b/patches/server/0006-Ridables.patch
deleted file mode 100644
index ab339717c..000000000
--- a/patches/server/0006-Ridables.patch
+++ /dev/null
@@ -1,6546 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: William Blake Galbreath
-Date: Sun, 5 Jul 2020 22:19:49 -0500
-Subject: [PATCH] Ridables
-
-
-diff --git a/src/main/java/net/minecraft/core/BlockPos.java b/src/main/java/net/minecraft/core/BlockPos.java
-index 665e88b2dedf9d5bb50914d5f3d377f2d19f40b0..f70a80b496bd1498778e82fc221c3b1b39308b75 100644
---- a/src/main/java/net/minecraft/core/BlockPos.java
-+++ b/src/main/java/net/minecraft/core/BlockPos.java
-@@ -61,6 +61,12 @@ public class BlockPos extends Vec3i {
- private static final int X_OFFSET = 38;
- // Paper end - Optimize Bit Operations by inlining
-
-+ // Purpur start
-+ public BlockPos(net.minecraft.world.entity.Entity entity) {
-+ super(entity.getBlockX(), entity.getBlockY(), entity.getBlockZ());
-+ }
-+ // Purpur end
-+
- public BlockPos(int x, int y, int z) {
- super(x, y, z);
- }
-diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
-index 57e6aaa63c1308baa5e2863b82b7521d5f4a4f31..73b77367039eb6a4445d1ef2d66fb3410e91f4b8 100644
---- a/src/main/java/net/minecraft/server/MinecraftServer.java
-+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
-@@ -1762,6 +1762,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop 0; // Paper - Add EntityMoveEvent
- net.minecraft.world.level.block.entity.HopperBlockEntity.skipHopperEvents = worldserver.paperConfig().hopper.disableMoveEvent || org.bukkit.event.inventory.InventoryMoveItemEvent.getHandlerList().getRegisteredListeners().length == 0; // Paper - Perf: Optimize Hoppers
- worldserver.updateLagCompensationTick(); // Paper - lag compensation
-+ worldserver.hasRidableMoveEvent = org.purpurmc.purpur.event.entity.RidableMoveEvent.getHandlerList().getRegisteredListeners().length > 0; // Purpur
-
- this.profiler.push(() -> {
- String s = String.valueOf(worldserver);
-diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
-index 0981d440d0dbfe4df668d1f3f1b5706a93bc4434..fc791a66d299905798c2c1ca542467e4c7933caf 100644
---- a/src/main/java/net/minecraft/server/level/ServerLevel.java
-+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
-@@ -229,6 +229,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
- public boolean hasPhysicsEvent = true; // Paper - BlockPhysicsEvent
- public boolean hasEntityMoveEvent; // Paper - Add EntityMoveEvent
- private final alternate.current.wire.WireHandler wireHandler = new alternate.current.wire.WireHandler(this); // Paper - optimize redstone (Alternate Current)
-+ public boolean hasRidableMoveEvent = false; // Purpur
-
- public LevelChunk getChunkIfLoaded(int x, int z) {
- return this.chunkSource.getChunkAtIfLoadedImmediately(x, z); // Paper - Use getChunkIfLoadedImmediately
-diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java
-index 0b9c85adaf5c7b5dcb23ab74a4fd02e6c0798851..2820333cbcd2cb972c4408cb0d9cc1be37a844cf 100644
---- a/src/main/java/net/minecraft/server/level/ServerPlayer.java
-+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java
-@@ -814,6 +814,15 @@ public class ServerPlayer extends Player {
- this.trackEnteredOrExitedLavaOnVehicle();
- this.updatePlayerAttributes();
- this.advancements.flushDirty(this);
-+
-+ // Purpur start
-+ if (this.level().purpurConfig.useNightVisionWhenRiding && this.getVehicle() != null && this.getVehicle().getRider() == this && this.level().getGameTime() % 100 == 0) { // 5 seconds
-+ MobEffectInstance nightVision = this.getEffect(MobEffects.NIGHT_VISION);
-+ if (nightVision == null || nightVision.getDuration() <= 300) { // 15 seconds
-+ this.addEffect(new MobEffectInstance(MobEffects.NIGHT_VISION, 400, 0)); // 20 seconds
-+ }
-+ }
-+ // Purpur end
- }
-
- private void updatePlayerAttributes() {
-diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
-index 5e9202bc7fc649764568b55d66ba0d684118c00c..1377d7abfe024b8ac977aa1a071b401836c3c048 100644
---- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
-+++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
-@@ -2789,6 +2789,8 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl
-
- ServerGamePacketListenerImpl.this.cserver.getPluginManager().callEvent(event);
-
-+ player.processClick(enumhand); // Purpur
-+
- // Entity in bucket - SPIGOT-4048 and SPIGOT-6859a
- if ((entity instanceof Bucketable && entity instanceof LivingEntity && origItem != null && origItem.asItem() == Items.WATER_BUCKET) && (event.isCancelled() || ServerGamePacketListenerImpl.this.player.getInventory().getSelected() == null || ServerGamePacketListenerImpl.this.player.getInventory().getSelected().getItem() != origItem)) {
- entity.resendPossiblyDesyncedEntityData(ServerGamePacketListenerImpl.this.player); // Paper - The entire mob gets deleted, so resend it.
-diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
-index 6c7f80723d67baf0ca036e0a12c3007144352edf..9c99a3df80c5f0a0d81e1f6a6516d088438a0b1e 100644
---- a/src/main/java/net/minecraft/world/entity/Entity.java
-+++ b/src/main/java/net/minecraft/world/entity/Entity.java
-@@ -381,7 +381,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
- private final Set tags;
- private final double[] pistonDeltas;
- private long pistonDeltasGameTime;
-- private EntityDimensions dimensions;
-+ protected EntityDimensions dimensions; // Purpur - private -> protected
- private float eyeHeight;
- public boolean isInPowderSnow;
- public boolean wasInPowderSnow;
-@@ -3080,6 +3080,13 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
- this.passengers = ImmutableList.copyOf(list);
- }
-
-+ // Purpur start
-+ if (isRidable() && this.passengers.get(0) == passenger && passenger instanceof Player player) {
-+ onMount(player);
-+ this.rider = player;
-+ }
-+ // Purpur end
-+
- this.gameEvent(GameEvent.ENTITY_MOUNT, passenger);
- }
- }
-@@ -3119,6 +3126,14 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
- return false;
- }
- // CraftBukkit end
-+
-+ // Purpur start
-+ if (this.rider != null && this.passengers.get(0) == this.rider) {
-+ onDismount(this.rider);
-+ this.rider = null;
-+ }
-+ // Purpur end
-+
- if (this.passengers.size() == 1 && this.passengers.get(0) == entity) {
- this.passengers = ImmutableList.of();
- } else {
-@@ -5009,4 +5024,44 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
- return ((net.minecraft.server.level.ServerChunkCache) level.getChunkSource()).isPositionTicking(this);
- }
- // Paper end - Expose entity id counter
-+ // Purpur start
-+ @Nullable
-+ private Player rider = null;
-+
-+ @Nullable
-+ public Player getRider() {
-+ return rider;
-+ }
-+
-+ public boolean isRidable() {
-+ return false;
-+ }
-+
-+ public boolean isControllable() {
-+ return true;
-+ }
-+
-+ public void onMount(Player rider) {
-+ if (this instanceof Mob) {
-+ ((Mob) this).setTarget(null, null, false);
-+ ((Mob) this).getNavigation().stop();
-+ }
-+ rider.setJumping(false); // fixes jump on mount
-+ }
-+
-+ public void onDismount(Player player) {
-+ }
-+
-+ public boolean onSpacebar() {
-+ return false;
-+ }
-+
-+ public boolean onClick(InteractionHand hand) {
-+ return false;
-+ }
-+
-+ public boolean processClick(InteractionHand hand) {
-+ return false;
-+ }
-+ // Purpur end
- }
-diff --git a/src/main/java/net/minecraft/world/entity/GlowSquid.java b/src/main/java/net/minecraft/world/entity/GlowSquid.java
-index 09fdea983772612ef3fff6b2da3cf469a34e4ec0..aa76a24421cdb3908a3544d92eb3d1e3c2ebedc4 100644
---- a/src/main/java/net/minecraft/world/entity/GlowSquid.java
-+++ b/src/main/java/net/minecraft/world/entity/GlowSquid.java
-@@ -23,6 +23,19 @@ public class GlowSquid extends Squid {
- super(type, world);
- }
-
-+ // Purpur start
-+ @Override
-+ public boolean isRidable() {
-+ return level().purpurConfig.glowSquidRidable;
-+ }
-+
-+
-+ @Override
-+ public boolean isControllable() {
-+ return level().purpurConfig.glowSquidControllable;
-+ }
-+ // Purpur end
-+
- @Override
- protected ParticleOptions getInkParticle() {
- return ParticleTypes.GLOW_SQUID_INK;
-diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java
-index 6e043457a29a890bcefd27fc5bb07c1a7e4e30f7..72bd60f691a639a0e7b6b5a98e5a3816305cfdaf 100644
---- a/src/main/java/net/minecraft/world/entity/LivingEntity.java
-+++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java
-@@ -229,9 +229,9 @@ public abstract class LivingEntity extends Entity implements Attackable {
- protected int deathScore;
- public float lastHurt;
- public boolean jumping;
-- public float xxa;
-- public float yya;
-- public float zza;
-+ public float xxa; public float getStrafeMot() { return xxa; } public void setStrafeMot(float strafe) { xxa = strafe; } // Purpur - OBFHELPER
-+ public float yya; public float getVerticalMot() { return yya; } public void setVerticalMot(float vertical) { yya = vertical; } // Purpur - OBFHELPER
-+ public float zza; public float getForwardMot() { return zza; } public void setForwardMot(float forward) { zza = forward; } // Purpur - OBFHELPER
- protected int lerpSteps;
- protected double lerpX;
- protected double lerpY;
-@@ -300,7 +300,7 @@ public abstract class LivingEntity extends Entity implements Attackable {
- this.useItem = ItemStack.EMPTY;
- this.lastClimbablePos = Optional.empty();
- this.appliedScale = 1.0F;
-- this.attributes = new AttributeMap(DefaultAttributes.getSupplier(type));
-+ this.attributes = new AttributeMap(DefaultAttributes.getSupplier(type), this); // Purpur
- this.craftAttributes = new CraftAttributeMap(this.attributes); // CraftBukkit
- // CraftBukkit - setHealth(getMaxHealth()) inlined and simplified to skip the instanceof check for EntityPlayer, as getBukkitEntity() is not initialized in constructor
- this.entityData.set(LivingEntity.DATA_HEALTH_ID, (float) this.getAttribute(Attributes.MAX_HEALTH).getValue());
-@@ -350,6 +350,7 @@ public abstract class LivingEntity extends Entity implements Attackable {
- public static AttributeSupplier.Builder createLivingAttributes() {
- return AttributeSupplier.builder().add(Attributes.MAX_HEALTH).add(Attributes.KNOCKBACK_RESISTANCE).add(Attributes.MOVEMENT_SPEED).add(Attributes.ARMOR).add(Attributes.ARMOR_TOUGHNESS).add(Attributes.MAX_ABSORPTION).add(Attributes.STEP_HEIGHT).add(Attributes.SCALE).add(Attributes.GRAVITY).add(Attributes.SAFE_FALL_DISTANCE).add(Attributes.FALL_DAMAGE_MULTIPLIER).add(Attributes.JUMP_STRENGTH);
- }
-+ public boolean shouldSendAttribute(Attribute attribute) { return true; } // Purpur
-
- @Override
- protected void checkFallDamage(double heightDifference, boolean onGround, BlockState state, BlockPos landedPosition) {
-@@ -2781,7 +2782,7 @@ public abstract class LivingEntity extends Entity implements Attackable {
- }
-
- protected long lastJumpTime = 0L; // Paper - Prevent excessive velocity through repeated crits
-- protected void jumpFromGround() {
-+ public void jumpFromGround() { // Purpur - protected -> public
- float f = this.getJumpPower();
-
- if (f > 1.0E-5F) {
-@@ -3494,8 +3495,10 @@ public abstract class LivingEntity extends Entity implements Attackable {
- this.pushEntities();
- this.level().getProfiler().pop();
- // Paper start - Add EntityMoveEvent
-- if (((ServerLevel) this.level()).hasEntityMoveEvent && !(this instanceof net.minecraft.world.entity.player.Player)) {
-- if (this.xo != this.getX() || this.yo != this.getY() || this.zo != this.getZ() || this.yRotO != this.getYRot() || this.xRotO != this.getXRot()) {
-+ // Purpur start
-+ if (this.xo != this.getX() || this.yo != this.getY() || this.zo != this.getZ() || this.yRotO != this.getYRot() || this.xRotO != this.getXRot()) {
-+ if (((ServerLevel) this.level()).hasEntityMoveEvent && !(this instanceof net.minecraft.world.entity.player.Player)) {
-+ // Purpur end
- Location from = new Location(this.level().getWorld(), this.xo, this.yo, this.zo, this.yRotO, this.xRotO);
- Location to = new Location(this.level().getWorld(), this.getX(), this.getY(), this.getZ(), this.getYRot(), this.getXRot());
- io.papermc.paper.event.entity.EntityMoveEvent event = new io.papermc.paper.event.entity.EntityMoveEvent(this.getBukkitLivingEntity(), from, to.clone());
-@@ -3505,6 +3508,21 @@ public abstract class LivingEntity extends Entity implements Attackable {
- this.absMoveTo(event.getTo().getX(), event.getTo().getY(), event.getTo().getZ(), event.getTo().getYaw(), event.getTo().getPitch());
- }
- }
-+ // Purpur start
-+ if (getRider() != null) {
-+ getRider().resetLastActionTime();
-+ if (((ServerLevel) level()).hasRidableMoveEvent && this instanceof Mob) {
-+ Location from = new Location(level().getWorld(), xo, yo, zo, this.yRotO, this.xRotO);
-+ Location to = new Location(level().getWorld(), getX(), getY(), getZ(), this.getYRot(), this.getXRot());
-+ org.purpurmc.purpur.event.entity.RidableMoveEvent event = new org.purpurmc.purpur.event.entity.RidableMoveEvent((org.bukkit.entity.Mob) getBukkitLivingEntity(), (Player) getRider().getBukkitEntity(), from, to.clone());
-+ if (!event.callEvent()) {
-+ absMoveTo(from.getX(), from.getY(), from.getZ(), from.getYaw(), from.getPitch());
-+ } else if (!to.equals(event.getTo())) {
-+ absMoveTo(to.getX(), to.getY(), to.getZ(), to.getYaw(), to.getPitch());
-+ }
-+ }
-+ }
-+ // Purpur end
- }
- // Paper end - Add EntityMoveEvent
- if (!this.level().isClientSide && this.isSensitiveToWater() && this.isInWaterRainOrBubble()) {
-diff --git a/src/main/java/net/minecraft/world/entity/Mob.java b/src/main/java/net/minecraft/world/entity/Mob.java
-index e89f9c3e887601d8461eb967ae0bf582b672f631..26b03594b6ccd69ca35156472e27543d11ba2077 100644
---- a/src/main/java/net/minecraft/world/entity/Mob.java
-+++ b/src/main/java/net/minecraft/world/entity/Mob.java
-@@ -165,8 +165,8 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Targeti
- this.restrictRadius = -1.0F;
- this.goalSelector = new GoalSelector(world.getProfilerSupplier());
- this.targetSelector = new GoalSelector(world.getProfilerSupplier());
-- this.lookControl = new LookControl(this);
-- this.moveControl = new MoveControl(this);
-+ this.lookControl = new org.purpurmc.purpur.controller.LookControllerWASD(this); // Purpur
-+ this.moveControl = new org.purpurmc.purpur.controller.MoveControllerWASD(this); // Purpur
- this.jumpControl = new JumpControl(this);
- this.bodyRotationControl = this.createBodyControl();
- this.navigation = this.createNavigation(world);
-@@ -1525,7 +1525,7 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Targeti
- protected void onOffspringSpawnedFromEgg(Player player, Mob child) {}
-
- protected InteractionResult mobInteract(Player player, InteractionHand hand) {
-- return InteractionResult.PASS;
-+ return tryRide(player, hand); // Purpur
- }
-
- public boolean isWithinRestriction() {
-@@ -1902,4 +1902,56 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Targeti
-
- return itemmonsteregg == null ? null : new ItemStack(itemmonsteregg);
- }
-+
-+ // Purpur start
-+ public double getMaxY() {
-+ return level().getHeight();
-+ }
-+
-+ public InteractionResult tryRide(Player player, InteractionHand hand) {
-+ return tryRide(player, hand, InteractionResult.PASS);
-+ }
-+
-+ public InteractionResult tryRide(Player player, InteractionHand hand, InteractionResult result) {
-+ if (!isRidable()) {
-+ return result;
-+ }
-+ if (hand != InteractionHand.MAIN_HAND) {
-+ return InteractionResult.PASS;
-+ }
-+ if (player.isShiftKeyDown()) {
-+ return InteractionResult.PASS;
-+ }
-+ if (!player.getItemInHand(hand).isEmpty()) {
-+ return InteractionResult.PASS;
-+ }
-+ if (!passengers.isEmpty() || player.isPassenger()) {
-+ return InteractionResult.PASS;
-+ }
-+ if (this instanceof TamableAnimal tamable) {
-+ if (tamable.isTame() && !tamable.isOwnedBy(player)) {
-+ return InteractionResult.PASS;
-+ }
-+ if (!tamable.isTame() && !level().purpurConfig.untamedTamablesAreRidable) {
-+ return InteractionResult.PASS;
-+ }
-+ }
-+ if (this instanceof AgeableMob ageable) {
-+ if (ageable.isBaby() && !level().purpurConfig.babiesAreRidable) {
-+ return InteractionResult.PASS;
-+ }
-+ }
-+ if (!player.getBukkitEntity().hasPermission("allow.ride." + net.minecraft.core.registries.BuiltInRegistries.ENTITY_TYPE.getKey(getType()).getPath())) {
-+ player.sendMiniMessage(org.purpurmc.purpur.PurpurConfig.cannotRideMob);
-+ return InteractionResult.PASS;
-+ }
-+ player.setYRot(this.getYRot());
-+ player.setXRot(this.getXRot());
-+ if (player.startRiding(this)) {
-+ return InteractionResult.SUCCESS;
-+ } else {
-+ return InteractionResult.PASS;
-+ }
-+ }
-+ // Purpur end
- }
-diff --git a/src/main/java/net/minecraft/world/entity/ai/attributes/AttributeMap.java b/src/main/java/net/minecraft/world/entity/ai/attributes/AttributeMap.java
-index 9ef8f014af332da129bfcd3370da983ec035ecc6..c51b429822d56761f69c49ecd4addfab7b90bad8 100644
---- a/src/main/java/net/minecraft/world/entity/ai/attributes/AttributeMap.java
-+++ b/src/main/java/net/minecraft/world/entity/ai/attributes/AttributeMap.java
-@@ -22,13 +22,20 @@ public class AttributeMap {
- private final Map, AttributeInstance> attributes = new Object2ObjectOpenHashMap<>();
- private final Set dirtyAttributes = new ObjectOpenHashSet<>();
- private final AttributeSupplier supplier;
-+ private final net.minecraft.world.entity.LivingEntity entity; // Purpur
-
- public AttributeMap(AttributeSupplier defaultAttributes) {
-+ // Purpur start
-+ this(defaultAttributes, null);
-+ }
-+ public AttributeMap(AttributeSupplier defaultAttributes, net.minecraft.world.entity.LivingEntity entity) {
-+ this.entity = entity;
-+ // Purpur end
- this.supplier = defaultAttributes;
- }
-
- private void onAttributeModified(AttributeInstance instance) {
-- if (instance.getAttribute().value().isClientSyncable()) {
-+ if (instance.getAttribute().value().isClientSyncable() && (entity == null || entity.shouldSendAttribute(instance.getAttribute().value()))) { // Purpur
- this.dirtyAttributes.add(instance);
- }
- }
-@@ -38,7 +45,7 @@ public class AttributeMap {
- }
-
- public Collection getSyncableAttributes() {
-- return this.attributes.values().stream().filter(attribute -> attribute.getAttribute().value().isClientSyncable()).collect(Collectors.toList());
-+ return this.attributes.values().stream().filter(attribute -> attribute.getAttribute().value().isClientSyncable() && (entity == null || entity.shouldSendAttribute(attribute.getAttribute().value()))).collect(Collectors.toList()); // Purpur
- }
-
- @Nullable
-diff --git a/src/main/java/net/minecraft/world/entity/ai/attributes/DefaultAttributes.java b/src/main/java/net/minecraft/world/entity/ai/attributes/DefaultAttributes.java
-index 10a1434313b11dae8210484583c6bf3b627416f7..35af18f371b3beaf81fcdca79fefe85e0a862b50 100644
---- a/src/main/java/net/minecraft/world/entity/ai/attributes/DefaultAttributes.java
-+++ b/src/main/java/net/minecraft/world/entity/ai/attributes/DefaultAttributes.java
-@@ -129,7 +129,7 @@ public class DefaultAttributes {
- .put(EntityType.OCELOT, Ocelot.createAttributes().build())
- .put(EntityType.PANDA, Panda.createAttributes().build())
- .put(EntityType.PARROT, Parrot.createAttributes().build())
-- .put(EntityType.PHANTOM, Monster.createMonsterAttributes().build())
-+ .put(EntityType.PHANTOM, net.minecraft.world.entity.monster.Phantom.createAttributes().build()) // Purpur
- .put(EntityType.PIG, Pig.createAttributes().build())
- .put(EntityType.PIGLIN, Piglin.createAttributes().build())
- .put(EntityType.PIGLIN_BRUTE, PiglinBrute.createAttributes().build())
-diff --git a/src/main/java/net/minecraft/world/entity/ai/control/MoveControl.java b/src/main/java/net/minecraft/world/entity/ai/control/MoveControl.java
-index c8fd5696de7c3623cdb4f498190a5c2708cf843e..e403d9dfeeaa3dcf53be790d761e7e922419efb0 100644
---- a/src/main/java/net/minecraft/world/entity/ai/control/MoveControl.java
-+++ b/src/main/java/net/minecraft/world/entity/ai/control/MoveControl.java
-@@ -29,6 +29,20 @@ public class MoveControl implements Control {
- this.mob = entity;
- }
-
-+ // Purpur start
-+ public void setSpeedModifier(double speed) {
-+ this.speedModifier = speed;
-+ }
-+
-+ public void setForward(float forward) {
-+ this.strafeForwards = forward;
-+ }
-+
-+ public void setStrafe(float strafe) {
-+ this.strafeRight = strafe;
-+ }
-+ // Purpur end
-+
- public boolean hasWanted() {
- return this.operation == MoveControl.Operation.MOVE_TO;
- }
-diff --git a/src/main/java/net/minecraft/world/entity/ai/control/SmoothSwimmingLookControl.java b/src/main/java/net/minecraft/world/entity/ai/control/SmoothSwimmingLookControl.java
-index fbfc2f2515ad709b2c1212aef9521e795547d66b..e77bd11af62682d5eca41f6c9e1aed30eb6879ce 100644
---- a/src/main/java/net/minecraft/world/entity/ai/control/SmoothSwimmingLookControl.java
-+++ b/src/main/java/net/minecraft/world/entity/ai/control/SmoothSwimmingLookControl.java
-@@ -3,7 +3,7 @@ package net.minecraft.world.entity.ai.control;
- import net.minecraft.util.Mth;
- import net.minecraft.world.entity.Mob;
-
--public class SmoothSwimmingLookControl extends LookControl {
-+public class SmoothSwimmingLookControl extends org.purpurmc.purpur.controller.LookControllerWASD { // Purpur
- private final int maxYRotFromCenter;
- private static final int HEAD_TILT_X = 10;
- private static final int HEAD_TILT_Y = 20;
-@@ -14,7 +14,7 @@ public class SmoothSwimmingLookControl extends LookControl {
- }
-
- @Override
-- public void tick() {
-+ public void vanillaTick() { // Purpur
- if (this.lookAtCooldown > 0) {
- this.lookAtCooldown--;
- this.getYRotD().ifPresent(yaw -> this.mob.yHeadRot = this.rotateTowards(this.mob.yHeadRot, yaw + 20.0F, this.yMaxRotSpeed));
-diff --git a/src/main/java/net/minecraft/world/entity/ambient/Bat.java b/src/main/java/net/minecraft/world/entity/ambient/Bat.java
-index dc27ddf5131e7398a5390a5187261d4c7fb6ccaa..c4a09778ca6bf5c15b588234bcadec3496017e3d 100644
---- a/src/main/java/net/minecraft/world/entity/ambient/Bat.java
-+++ b/src/main/java/net/minecraft/world/entity/ambient/Bat.java
-@@ -44,12 +44,59 @@ public class Bat extends AmbientCreature {
-
- public Bat(EntityType extends Bat> type, Level world) {
- super(type, world);
-+ this.moveControl = new org.purpurmc.purpur.controller.FlyingWithSpacebarMoveControllerWASD(this, 0.075F); // Purpur
- if (!world.isClientSide) {
- this.setResting(true);
- }
-
- }
-
-+ // Purpur start
-+ @Override
-+ public boolean shouldSendAttribute(net.minecraft.world.entity.ai.attributes.Attribute attribute) { return attribute != Attributes.FLYING_SPEED.value(); } // Fixes log spam on clients
-+
-+ @Override
-+ public boolean isRidable() {
-+ return level().purpurConfig.batRidable;
-+ }
-+
-+ @Override
-+ public boolean dismountsUnderwater() {
-+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.batRidableInWater;
-+ }
-+
-+ @Override
-+ public boolean isControllable() {
-+ return level().purpurConfig.batControllable;
-+ }
-+
-+ @Override
-+ public double getMaxY() {
-+ return level().purpurConfig.batMaxY;
-+ }
-+
-+ @Override
-+ public void onMount(Player rider) {
-+ super.onMount(rider);
-+ if (isResting()) {
-+ setResting(false);
-+ level().levelEvent(null, 1025, new BlockPos(this).above(), 0);
-+ }
-+ }
-+
-+ @Override
-+ public void travel(Vec3 vec3) {
-+ super.travel(vec3);
-+ if (getRider() != null && this.isControllable() && !onGround) {
-+ float speed = (float) getAttributeValue(Attributes.FLYING_SPEED) * 2;
-+ setSpeed(speed);
-+ Vec3 mot = getDeltaMovement();
-+ move(net.minecraft.world.entity.MoverType.SELF, mot.multiply(speed, 0.25, speed));
-+ setDeltaMovement(mot.scale(0.9D));
-+ }
-+ }
-+ // Purpur end
-+
- @Override
- public boolean isFlapping() {
- return !this.isResting() && (float) this.tickCount % 10.0F == 0.0F;
-@@ -99,7 +146,7 @@ public class Bat extends AmbientCreature {
- protected void pushEntities() {}
-
- public static AttributeSupplier.Builder createAttributes() {
-- return Mob.createMobAttributes().add(Attributes.MAX_HEALTH, 6.0D);
-+ return Mob.createMobAttributes().add(Attributes.MAX_HEALTH, 6.0D).add(Attributes.FLYING_SPEED, 0.6D); // Purpur
- }
-
- public boolean isResting() {
-@@ -132,6 +179,14 @@ public class Bat extends AmbientCreature {
-
- @Override
- protected void customServerAiStep() {
-+ // Purpur start
-+ if (getRider() != null && this.isControllable()) {
-+ Vec3 mot = getDeltaMovement();
-+ setDeltaMovement(mot.x(), mot.y() + (getVerticalMot() > 0 ? 0.07D : 0.0D), mot.z());
-+ return;
-+ }
-+ // Purpur end
-+
- super.customServerAiStep();
- BlockPos blockposition = this.blockPosition();
- BlockPos blockposition1 = blockposition.above();
-diff --git a/src/main/java/net/minecraft/world/entity/animal/AbstractFish.java b/src/main/java/net/minecraft/world/entity/animal/AbstractFish.java
-index 3231eaa6af2ddfe4095ff2d650f580ebd4d43aea..e8cb124d232f7316cc8c35dd8bd12f79bbcda7d6 100644
---- a/src/main/java/net/minecraft/world/entity/animal/AbstractFish.java
-+++ b/src/main/java/net/minecraft/world/entity/animal/AbstractFish.java
-@@ -87,6 +87,7 @@ public abstract class AbstractFish extends WaterAnimal implements Bucketable {
- @Override
- protected void registerGoals() {
- super.registerGoals();
-+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
- this.goalSelector.addGoal(0, new PanicGoal(this, 1.25));
- this.goalSelector.addGoal(2, new AvoidEntityGoal<>(this, Player.class, 8.0F, 1.6, 1.4, EntitySelector.NO_SPECTATORS::test));
- this.goalSelector.addGoal(4, new AbstractFish.FishSwimGoal(this));
-@@ -100,7 +101,7 @@ public abstract class AbstractFish extends WaterAnimal implements Bucketable {
- @Override
- public void travel(Vec3 movementInput) {
- if (this.isEffectiveAi() && this.isInWater()) {
-- this.moveRelative(0.01F, movementInput);
-+ this.moveRelative(getRider() != null ? getSpeed() : 0.01F, movementInput); // Purpur
- this.move(MoverType.SELF, this.getDeltaMovement());
- this.setDeltaMovement(this.getDeltaMovement().scale(0.9));
- if (this.getTarget() == null) {
-@@ -161,7 +162,7 @@ public abstract class AbstractFish extends WaterAnimal implements Bucketable {
- protected void playStepSound(BlockPos pos, BlockState state) {
- }
-
-- static class FishMoveControl extends MoveControl {
-+ static class FishMoveControl extends org.purpurmc.purpur.controller.WaterMoveControllerWASD { // Purpur
- private final AbstractFish fish;
-
- FishMoveControl(AbstractFish owner) {
-@@ -169,14 +170,22 @@ public abstract class AbstractFish extends WaterAnimal implements Bucketable {
- this.fish = owner;
- }
-
-+ // Purpur start
- @Override
-- public void tick() {
-+ public void purpurTick(Player rider) {
-+ super.purpurTick(rider);
-+ fish.setDeltaMovement(fish.getDeltaMovement().add(0.0D, 0.005D, 0.0D));
-+ }
-+ // Purpur end
-+
-+ @Override
-+ public void vanillaTick() { // Purpur
- if (this.fish.isEyeInFluid(FluidTags.WATER)) {
- this.fish.setDeltaMovement(this.fish.getDeltaMovement().add(0.0, 0.005, 0.0));
- }
-
- if (this.operation == MoveControl.Operation.MOVE_TO && !this.fish.getNavigation().isDone()) {
-- float f = (float)(this.speedModifier * this.fish.getAttributeValue(Attributes.MOVEMENT_SPEED));
-+ float f = (float)(this.getSpeedModifier() * this.fish.getAttributeValue(Attributes.MOVEMENT_SPEED)); // Purpur
- this.fish.setSpeed(Mth.lerp(0.125F, this.fish.getSpeed(), f));
- double d = this.wantedX - this.fish.getX();
- double e = this.wantedY - this.fish.getY();
-diff --git a/src/main/java/net/minecraft/world/entity/animal/Bee.java b/src/main/java/net/minecraft/world/entity/animal/Bee.java
-index 0dfb8109fd8c022b079da00f6a0e3fc85b57bf7a..4b84cf76d052112e00cd13c330182abfbe618820 100644
---- a/src/main/java/net/minecraft/world/entity/animal/Bee.java
-+++ b/src/main/java/net/minecraft/world/entity/animal/Bee.java
-@@ -143,6 +143,7 @@ public class Bee extends Animal implements NeutralMob, FlyingAnimal {
- public Bee(EntityType extends Bee> type, Level world) {
- super(type, world);
- this.remainingCooldownBeforeLocatingNewFlower = Mth.nextInt(this.random, 20, 60);
-+ final org.purpurmc.purpur.controller.FlyingMoveControllerWASD flyingController = new org.purpurmc.purpur.controller.FlyingMoveControllerWASD(this, 0.25F, 1.0F, false); // Purpur
- // Paper start - Fix MC-167279
- class BeeFlyingMoveControl extends FlyingMoveControl {
- public BeeFlyingMoveControl(final Mob entity, final int maxPitchChange, final boolean noGravity) {
-@@ -151,11 +152,24 @@ public class Bee extends Animal implements NeutralMob, FlyingAnimal {
-
- @Override
- public void tick() {
-+ // Purpur start
-+ if (mob.getRider() != null && mob.isControllable()) {
-+ flyingController.purpurTick(mob.getRider());
-+ return;
-+ }
-+ // Purpur end
- if (this.mob.getY() <= Bee.this.level().getMinBuildHeight()) {
- this.mob.setNoGravity(false);
- }
- super.tick();
- }
-+
-+ // Purpur start
-+ @Override
-+ public boolean hasWanted() {
-+ return mob.getRider() != null || !mob.isControllable() || super.hasWanted();
-+ }
-+ // Purpur end
- }
- this.moveControl = new BeeFlyingMoveControl(this, 20, true);
- // Paper end - Fix MC-167279
-@@ -167,6 +181,40 @@ public class Bee extends Animal implements NeutralMob, FlyingAnimal {
- this.setPathfindingMalus(PathType.FENCE, -1.0F);
- }
-
-+ // Purpur start
-+ @Override
-+ public boolean isRidable() {
-+ return level().purpurConfig.beeRidable;
-+ }
-+
-+ @Override
-+ public boolean dismountsUnderwater() {
-+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.beeRidableInWater;
-+ }
-+
-+ @Override
-+ public boolean isControllable() {
-+ return level().purpurConfig.beeControllable;
-+ }
-+
-+ @Override
-+ public double getMaxY() {
-+ return level().purpurConfig.beeMaxY;
-+ }
-+
-+ @Override
-+ public void travel(Vec3 vec3) {
-+ super.travel(vec3);
-+ if (getRider() != null && this.isControllable() && !onGround) {
-+ float speed = (float) getAttributeValue(Attributes.FLYING_SPEED) * 2;
-+ setSpeed(speed);
-+ Vec3 mot = getDeltaMovement();
-+ move(net.minecraft.world.entity.MoverType.SELF, mot.multiply(speed, speed, speed));
-+ setDeltaMovement(mot.scale(0.9D));
-+ }
-+ }
-+ // Purpur end
-+
- @Override
- protected void defineSynchedData(SynchedEntityData.Builder builder) {
- super.defineSynchedData(builder);
-@@ -181,6 +229,7 @@ public class Bee extends Animal implements NeutralMob, FlyingAnimal {
-
- @Override
- protected void registerGoals() {
-+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
- this.goalSelector.addGoal(0, new Bee.BeeAttackGoal(this, 1.399999976158142D, true));
- this.goalSelector.addGoal(1, new Bee.BeeEnterHiveGoal());
- this.goalSelector.addGoal(2, new BreedGoal(this, 1.0D));
-@@ -198,6 +247,7 @@ public class Bee extends Animal implements NeutralMob, FlyingAnimal {
- this.goalSelector.addGoal(7, new Bee.BeeGrowCropGoal());
- this.goalSelector.addGoal(8, new Bee.BeeWanderGoal());
- this.goalSelector.addGoal(9, new FloatGoal(this));
-+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
- this.targetSelector.addGoal(1, (new Bee.BeeHurtByOtherGoal(this)).setAlertOthers(new Class[0]));
- this.targetSelector.addGoal(2, new Bee.BeeBecomeAngryTargetGoal(this));
- this.targetSelector.addGoal(3, new ResetUniversalAngerTargetGoal<>(this, true));
-@@ -872,16 +922,16 @@ public class Bee extends Animal implements NeutralMob, FlyingAnimal {
- }
- }
-
-- private class BeeLookControl extends LookControl {
-+ private class BeeLookControl extends org.purpurmc.purpur.controller.LookControllerWASD { // Purpur
-
- BeeLookControl(final Mob entity) {
- super(entity);
- }
-
- @Override
-- public void tick() {
-+ public void vanillaTick() { // Purpur
- if (!Bee.this.isAngry()) {
-- super.tick();
-+ super.vanillaTick(); // Purpur
- }
- }
-
-diff --git a/src/main/java/net/minecraft/world/entity/animal/Cat.java b/src/main/java/net/minecraft/world/entity/animal/Cat.java
-index 07559b9629d4ecb40b511256f400a781e39820e0..3826c794ddde6b915e233e2d0395557e3ea867e0 100644
---- a/src/main/java/net/minecraft/world/entity/animal/Cat.java
-+++ b/src/main/java/net/minecraft/world/entity/animal/Cat.java
-@@ -104,6 +104,31 @@ public class Cat extends TamableAnimal implements VariantHolder(this, Rabbit.class, false, (Predicate) null));
- this.targetSelector.addGoal(1, new NonTameRandomTargetGoal<>(this, Turtle.class, false, Turtle.BABY_ON_LAND_SELECTOR));
- }
-@@ -377,6 +404,7 @@ public class Cat extends TamableAnimal implements VariantHolder {
-diff --git a/src/main/java/net/minecraft/world/entity/animal/Cod.java b/src/main/java/net/minecraft/world/entity/animal/Cod.java
-index 824e5e4fe7619ae46061c3c978c9a044db8c84ab..e2a98b45e56a368de19bb65e304370a5998c7cb9 100644
---- a/src/main/java/net/minecraft/world/entity/animal/Cod.java
-+++ b/src/main/java/net/minecraft/world/entity/animal/Cod.java
-@@ -13,6 +13,18 @@ public class Cod extends AbstractSchoolingFish {
- super(type, world);
- }
-
-+ // Purpur start
-+ @Override
-+ public boolean isRidable() {
-+ return level().purpurConfig.codRidable;
-+ }
-+
-+ @Override
-+ public boolean isControllable() {
-+ return level().purpurConfig.codControllable;
-+ }
-+ // Purpur end
-+
- @Override
- public ItemStack getBucketItemStack() {
- return new ItemStack(Items.COD_BUCKET);
-diff --git a/src/main/java/net/minecraft/world/entity/animal/Cow.java b/src/main/java/net/minecraft/world/entity/animal/Cow.java
-index 5a7b1be351834a6b8889b1380cede1be025cb302..41b6c79c31414378d433500a35e434e546738e42 100644
---- a/src/main/java/net/minecraft/world/entity/animal/Cow.java
-+++ b/src/main/java/net/minecraft/world/entity/animal/Cow.java
-@@ -44,9 +44,27 @@ public class Cow extends Animal {
- super(type, world);
- }
-
-+ // Purpur start
-+ @Override
-+ public boolean isRidable() {
-+ return level().purpurConfig.cowRidable;
-+ }
-+
-+ @Override
-+ public boolean dismountsUnderwater() {
-+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.cowRidableInWater;
-+ }
-+
-+ @Override
-+ public boolean isControllable() {
-+ return level().purpurConfig.cowControllable;
-+ }
-+ // Purpur end
-+
- @Override
- protected void registerGoals() {
- this.goalSelector.addGoal(0, new FloatGoal(this));
-+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
- this.goalSelector.addGoal(1, new PanicGoal(this, 2.0D));
- this.goalSelector.addGoal(2, new BreedGoal(this, 1.0D));
- this.goalSelector.addGoal(3, new TemptGoal(this, 1.25D, (itemstack) -> {
-@@ -94,6 +112,7 @@ public class Cow extends Animal {
-
- @Override
- public InteractionResult mobInteract(Player player, InteractionHand hand) {
-+ if (getRider() != null) return InteractionResult.PASS; // Purpur
- ItemStack itemstack = player.getItemInHand(hand);
-
- if (itemstack.is(Items.BUCKET) && !this.isBaby()) {
-@@ -101,7 +120,7 @@ public class Cow extends Animal {
- PlayerBucketFillEvent event = CraftEventFactory.callPlayerBucketFillEvent((ServerLevel) player.level(), player, this.blockPosition(), this.blockPosition(), null, itemstack, Items.MILK_BUCKET, hand);
-
- if (event.isCancelled()) {
-- return InteractionResult.PASS;
-+ return tryRide(player, hand); // Purpur
- }
- // CraftBukkit end
-
-diff --git a/src/main/java/net/minecraft/world/entity/animal/Dolphin.java b/src/main/java/net/minecraft/world/entity/animal/Dolphin.java
-index 1b1cb0e4d54e52ebe794199e386c54c5d84b3719..3af2017a4b860992a6db88edec3624fcb9884ae1 100644
---- a/src/main/java/net/minecraft/world/entity/animal/Dolphin.java
-+++ b/src/main/java/net/minecraft/world/entity/animal/Dolphin.java
-@@ -81,14 +81,82 @@ public class Dolphin extends WaterAnimal {
- public static final Predicate ALLOWED_ITEMS = (entityitem) -> {
- return !entityitem.hasPickUpDelay() && entityitem.isAlive() && entityitem.isInWater();
- };
-+ private int spitCooldown; // Purpur
-
- public Dolphin(EntityType extends Dolphin> type, Level world) {
- super(type, world);
-- this.moveControl = new SmoothSwimmingMoveControl(this, 85, 10, 0.02F, 0.1F, true);
-+ // Purpur start
-+ class DolphinMoveControl extends SmoothSwimmingMoveControl {
-+ private final org.purpurmc.purpur.controller.WaterMoveControllerWASD waterMoveControllerWASD;
-+ private final Dolphin dolphin;
-+
-+ public DolphinMoveControl(Dolphin dolphin, int pitchChange, int yawChange, float speedInWater, float speedInAir, boolean buoyant) {
-+ super(dolphin, pitchChange, yawChange, speedInWater, speedInAir, buoyant);
-+ this.dolphin = dolphin;
-+ this.waterMoveControllerWASD = new org.purpurmc.purpur.controller.WaterMoveControllerWASD(dolphin);
-+ }
-+
-+ @Override
-+ public void tick() {
-+ if (dolphin.getRider() != null && dolphin.isControllable()) {
-+ purpurTick(dolphin.getRider());
-+ } else {
-+ super.tick();
-+ }
-+ }
-+
-+ public void purpurTick(Player rider) {
-+ if (dolphin.getAirSupply() < 150) {
-+ // if drowning override player WASD controls to find air
-+ super.tick();
-+ } else {
-+ waterMoveControllerWASD.purpurTick(rider);
-+ dolphin.setDeltaMovement(dolphin.getDeltaMovement().add(0.0D, 0.005D, 0.0D));
-+ }
-+ }
-+ };
-+ this.moveControl = new DolphinMoveControl(this, 85, 10, 0.02F, 0.1F, true);
-+ // Purpur end
- this.lookControl = new SmoothSwimmingLookControl(this, 10);
- this.setCanPickUpLoot(true);
- }
-
-+ // Purpur start
-+ @Override
-+ public boolean isRidable() {
-+ return level().purpurConfig.dolphinRidable;
-+ }
-+
-+ @Override
-+ public boolean isControllable() {
-+ return level().purpurConfig.dolphinControllable;
-+ }
-+
-+ @Override
-+ public boolean onSpacebar() {
-+ if (spitCooldown == 0 && getRider() != null) {
-+ spitCooldown = level().purpurConfig.dolphinSpitCooldown;
-+
-+ org.bukkit.craftbukkit.entity.CraftPlayer player = (org.bukkit.craftbukkit.entity.CraftPlayer) getRider().getBukkitEntity();
-+ if (!player.hasPermission("allow.special.dolphin")) {
-+ return false;
-+ }
-+
-+ org.bukkit.Location loc = player.getEyeLocation();
-+ loc.setPitch(loc.getPitch() - 10);
-+ org.bukkit.util.Vector target = loc.getDirection().normalize().multiply(10).add(loc.toVector());
-+
-+ org.purpurmc.purpur.entity.DolphinSpit spit = new org.purpurmc.purpur.entity.DolphinSpit(level(), this);
-+ spit.shoot(target.getX() - getX(), target.getY() - getY(), target.getZ() - getZ(), level().purpurConfig.dolphinSpitSpeed, 5.0F);
-+
-+ level().addFreshEntity(spit);
-+ playSound(SoundEvents.DOLPHIN_ATTACK, 1.0F, 1.0F + (random.nextFloat() - random.nextFloat()) * 0.2F);
-+ return true;
-+ }
-+ return false;
-+ }
-+ // Purpur end
-+
- @Nullable
- @Override
- public SpawnGroupData finalizeSpawn(ServerLevelAccessor world, DifficultyInstance difficulty, MobSpawnType spawnReason, @Nullable SpawnGroupData entityData) {
-@@ -158,6 +226,7 @@ public class Dolphin extends WaterAnimal {
- protected void registerGoals() {
- this.goalSelector.addGoal(0, new BreathAirGoal(this));
- this.goalSelector.addGoal(0, new TryFindWaterGoal(this));
-+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
- this.goalSelector.addGoal(1, new Dolphin.DolphinSwimToTreasureGoal(this));
- this.goalSelector.addGoal(2, new Dolphin.DolphinSwimWithPlayerGoal(this, 4.0D));
- this.goalSelector.addGoal(4, new RandomSwimmingGoal(this, 1.0D, 10));
-@@ -168,6 +237,7 @@ public class Dolphin extends WaterAnimal {
- this.goalSelector.addGoal(8, new Dolphin.PlayWithItemsGoal());
- this.goalSelector.addGoal(8, new FollowBoatGoal(this));
- this.goalSelector.addGoal(9, new AvoidEntityGoal<>(this, Guardian.class, 8.0F, 1.0D, 1.0D));
-+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
- this.targetSelector.addGoal(1, (new HurtByTargetGoal(this, new Class[]{Guardian.class})).setAlertOthers());
- }
-
-@@ -214,7 +284,7 @@ public class Dolphin extends WaterAnimal {
-
- @Override
- protected boolean canRide(Entity entity) {
-- return true;
-+ return boardingCooldown <= 0; // Purpur - make dolphin honor ride cooldown like all other non-boss mobs;
- }
-
- @Override
-@@ -249,6 +319,11 @@ public class Dolphin extends WaterAnimal {
- @Override
- public void tick() {
- super.tick();
-+ // Purpur start
-+ if (spitCooldown > 0) {
-+ spitCooldown--;
-+ }
-+ // Purpur end
- if (this.isNoAi()) {
- this.setAirSupply(this.getMaxAirSupply());
- } else {
-diff --git a/src/main/java/net/minecraft/world/entity/animal/Fox.java b/src/main/java/net/minecraft/world/entity/animal/Fox.java
-index e705449496b1a06270ecbc13f4dce5357479845b..935dff4d167d8b6ef4b7cdc95bf000a2ce761f92 100644
---- a/src/main/java/net/minecraft/world/entity/animal/Fox.java
-+++ b/src/main/java/net/minecraft/world/entity/animal/Fox.java
-@@ -145,6 +145,44 @@ public class Fox extends Animal implements VariantHolder {
- this.setCanPickUpLoot(true);
- }
-
-+ // Purpur start
-+ @Override
-+ public boolean isRidable() {
-+ return level().purpurConfig.foxRidable;
-+ }
-+
-+ @Override
-+ public boolean dismountsUnderwater() {
-+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.foxRidableInWater;
-+ }
-+
-+ @Override
-+ public boolean isControllable() {
-+ return level().purpurConfig.foxControllable;
-+ }
-+
-+ @Override
-+ public float getJumpPower() {
-+ return getRider() != null && this.isControllable() ? 0.5F : super.getJumpPower();
-+ }
-+
-+ @Override
-+ public void onMount(Player rider) {
-+ super.onMount(rider);
-+ setCanPickUpLoot(false);
-+ clearStates();
-+ setIsPouncing(false);
-+ spitOutItem(getItemBySlot(EquipmentSlot.MAINHAND));
-+ setItemSlot(EquipmentSlot.MAINHAND, ItemStack.EMPTY);
-+ }
-+
-+ @Override
-+ public void onDismount(Player rider) {
-+ super.onDismount(rider);
-+ setCanPickUpLoot(true);
-+ }
-+ // Purpur end
-+
- @Override
- protected void defineSynchedData(SynchedEntityData.Builder builder) {
- super.defineSynchedData(builder);
-@@ -164,6 +202,7 @@ public class Fox extends Animal implements VariantHolder {
- return entityliving instanceof AbstractSchoolingFish;
- });
- this.goalSelector.addGoal(0, new Fox.FoxFloatGoal());
-+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
- this.goalSelector.addGoal(0, new ClimbOnTopOfPowderSnowGoal(this, this.level()));
- this.goalSelector.addGoal(1, new Fox.FaceplantGoal());
- this.goalSelector.addGoal(2, new Fox.FoxPanicGoal(2.2D));
-@@ -190,6 +229,7 @@ public class Fox extends Animal implements VariantHolder {
- this.goalSelector.addGoal(11, new Fox.FoxSearchForItemsGoal());
- this.goalSelector.addGoal(12, new Fox.FoxLookAtPlayerGoal(this, Player.class, 24.0F));
- this.goalSelector.addGoal(13, new Fox.PerchAndSearchGoal());
-+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
- this.targetSelector.addGoal(3, new Fox.DefendTrustedTargetGoal(LivingEntity.class, false, false, (entityliving) -> {
- return Fox.TRUSTED_TARGET_SELECTOR.test(entityliving) && !this.trusts(entityliving.getUUID());
- }));
-@@ -769,16 +809,16 @@ public class Fox extends Animal implements VariantHolder {
- return new Vec3(0.0D, (double) (0.55F * this.getEyeHeight()), (double) (this.getBbWidth() * 0.4F));
- }
-
-- public class FoxLookControl extends LookControl {
-+ public class FoxLookControl extends org.purpurmc.purpur.controller.LookControllerWASD { // Purpur
-
- public FoxLookControl() {
- super(Fox.this);
- }
-
- @Override
-- public void tick() {
-+ public void vanillaTick() { // Purpur
- if (!Fox.this.isSleeping()) {
-- super.tick();
-+ super.vanillaTick(); // Purpur
- }
-
- }
-@@ -789,16 +829,16 @@ public class Fox extends Animal implements VariantHolder {
- }
- }
-
-- private class FoxMoveControl extends MoveControl {
-+ private class FoxMoveControl extends org.purpurmc.purpur.controller.MoveControllerWASD { // Purpur
-
- public FoxMoveControl() {
- super(Fox.this);
- }
-
- @Override
-- public void tick() {
-+ public void vanillaTick() { // Purpur
- if (Fox.this.canMove()) {
-- super.tick();
-+ super.vanillaTick(); // Purpur
- }
-
- }
-diff --git a/src/main/java/net/minecraft/world/entity/animal/IronGolem.java b/src/main/java/net/minecraft/world/entity/animal/IronGolem.java
-index 932fae98c551052cadba4c6fc6e575fc30a25d58..4cc9138201b08aff8bb47720c6fe1e3447f03967 100644
---- a/src/main/java/net/minecraft/world/entity/animal/IronGolem.java
-+++ b/src/main/java/net/minecraft/world/entity/animal/IronGolem.java
-@@ -61,8 +61,27 @@ public class IronGolem extends AbstractGolem implements NeutralMob {
- super(type, world);
- }
-
-+ // Purpur start
-+ @Override
-+ public boolean isRidable() {
-+ return level().purpurConfig.ironGolemRidable;
-+ }
-+
-+ @Override
-+ public boolean dismountsUnderwater() {
-+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.ironGolemRidableInWater;
-+ }
-+
-+ @Override
-+ public boolean isControllable() {
-+ return level().purpurConfig.ironGolemControllable;
-+ }
-+ // Purpur end
-+
- @Override
- protected void registerGoals() {
-+ if (level().purpurConfig.ironGolemCanSwim) this.goalSelector.addGoal(0, new net.minecraft.world.entity.ai.goal.FloatGoal(this)); // Purpur
-+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
- this.goalSelector.addGoal(1, new MeleeAttackGoal(this, 1.0D, true));
- this.goalSelector.addGoal(2, new MoveTowardsTargetGoal(this, 0.9D, 32.0F));
- this.goalSelector.addGoal(2, new MoveBackToVillageGoal(this, 0.6D, false));
-@@ -70,6 +89,7 @@ public class IronGolem extends AbstractGolem implements NeutralMob {
- this.goalSelector.addGoal(5, new OfferFlowerGoal(this));
- this.goalSelector.addGoal(7, new LookAtPlayerGoal(this, Player.class, 6.0F));
- this.goalSelector.addGoal(8, new RandomLookAroundGoal(this));
-+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
- this.targetSelector.addGoal(1, new DefendVillageTargetGoal(this));
- this.targetSelector.addGoal(2, new HurtByTargetGoal(this, new Class[0]));
- this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, Player.class, 10, true, false, this::isAngryAt));
-@@ -265,13 +285,13 @@ public class IronGolem extends AbstractGolem implements NeutralMob {
- ItemStack itemstack = player.getItemInHand(hand);
-
- if (!itemstack.is(Items.IRON_INGOT)) {
-- return InteractionResult.PASS;
-+ return tryRide(player, hand); // Purpur
- } else {
- float f = this.getHealth();
-
- this.heal(25.0F);
- if (this.getHealth() == f) {
-- return InteractionResult.PASS;
-+ return tryRide(player, hand); // Purpur
- } else {
- float f1 = 1.0F + (this.random.nextFloat() - this.random.nextFloat()) * 0.2F;
-
-diff --git a/src/main/java/net/minecraft/world/entity/animal/MushroomCow.java b/src/main/java/net/minecraft/world/entity/animal/MushroomCow.java
-index 0c21959f57ae88fcd0a4d6dc911c1ce347c96528..11944ee34fc7e3e5551b9e18a563164f96898a54 100644
---- a/src/main/java/net/minecraft/world/entity/animal/MushroomCow.java
-+++ b/src/main/java/net/minecraft/world/entity/animal/MushroomCow.java
-@@ -64,6 +64,23 @@ public class MushroomCow extends Cow implements Shearable, VariantHolder optional = this.getEffectsFromItemStack(itemstack);
-
- if (optional.isEmpty()) {
-- return InteractionResult.PASS;
-+ return tryRide(player, hand); // Purpur
- }
-
- itemstack.consume(1, player);
-diff --git a/src/main/java/net/minecraft/world/entity/animal/Ocelot.java b/src/main/java/net/minecraft/world/entity/animal/Ocelot.java
-index 2c7491edbb60e7ec6a208ea7292cd28a3f8f9e31..2b074f68c1be2ff591543685bbb4e7ea1c7784f8 100644
---- a/src/main/java/net/minecraft/world/entity/animal/Ocelot.java
-+++ b/src/main/java/net/minecraft/world/entity/animal/Ocelot.java
-@@ -66,6 +66,23 @@ public class Ocelot extends Animal {
- this.reassessTrustingGoals();
- }
-
-+ // Purpur start
-+ @Override
-+ public boolean isRidable() {
-+ return level().purpurConfig.ocelotRidable;
-+ }
-+
-+ @Override
-+ public boolean dismountsUnderwater() {
-+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.ocelotRidableInWater;
-+ }
-+
-+ @Override
-+ public boolean isControllable() {
-+ return level().purpurConfig.ocelotControllable;
-+ }
-+ // Purpur end
-+
- public boolean isTrusting() {
- return (Boolean) this.entityData.get(Ocelot.DATA_TRUSTING);
- }
-@@ -99,12 +116,14 @@ public class Ocelot extends Animal {
- return itemstack.is(ItemTags.OCELOT_FOOD);
- }, true);
- this.goalSelector.addGoal(1, new FloatGoal(this));
-+ this.goalSelector.addGoal(1, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
- this.goalSelector.addGoal(3, this.temptGoal);
- this.goalSelector.addGoal(7, new LeapAtTargetGoal(this, 0.3F));
- this.goalSelector.addGoal(8, new OcelotAttackGoal(this));
- this.goalSelector.addGoal(9, new BreedGoal(this, 0.8D));
- this.goalSelector.addGoal(10, new WaterAvoidingRandomStrollGoal(this, 0.8D, 1.0000001E-5F));
- this.goalSelector.addGoal(11, new LookAtPlayerGoal(this, Player.class, 10.0F));
-+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
- this.targetSelector.addGoal(1, new NearestAttackableTargetGoal<>(this, Chicken.class, false));
- this.targetSelector.addGoal(1, new NearestAttackableTargetGoal<>(this, Turtle.class, 10, false, false, Turtle.BABY_ON_LAND_SELECTOR));
- }
-diff --git a/src/main/java/net/minecraft/world/entity/animal/Panda.java b/src/main/java/net/minecraft/world/entity/animal/Panda.java
-index db60b91c2b26ca8cdb66e05deab7742ffe212767..5f9b7ae2ed4acff0fa7cfee07a29a28b5c0d67f8 100644
---- a/src/main/java/net/minecraft/world/entity/animal/Panda.java
-+++ b/src/main/java/net/minecraft/world/entity/animal/Panda.java
-@@ -120,6 +120,32 @@ public class Panda extends Animal {
-
- }
-
-+ // Purpur start
-+ @Override
-+ public boolean isRidable() {
-+ return level().purpurConfig.pandaRidable;
-+ }
-+
-+ @Override
-+ public boolean dismountsUnderwater() {
-+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.pandaRidableInWater;
-+ }
-+
-+ @Override
-+ public boolean isControllable() {
-+ return level().purpurConfig.pandaControllable;
-+ }
-+
-+ @Override
-+ public void onMount(Player rider) {
-+ super.onMount(rider);
-+ setForwardMot(0.0F);
-+ sit(false);
-+ eat(false);
-+ setOnBack(false);
-+ }
-+ // Purpur end
-+
- @Override
- public boolean canTakeItem(ItemStack stack) {
- EquipmentSlot enumitemslot = Mob.getEquipmentSlotForItem(stack);
-@@ -281,6 +307,7 @@ public class Panda extends Animal {
- @Override
- protected void registerGoals() {
- this.goalSelector.addGoal(0, new FloatGoal(this));
-+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
- this.goalSelector.addGoal(2, new Panda.PandaPanicGoal(this, 2.0D));
- this.goalSelector.addGoal(2, new Panda.PandaBreedGoal(this, 1.0D));
- this.goalSelector.addGoal(3, new Panda.PandaAttackGoal(this, 1.2000000476837158D, true));
-@@ -298,6 +325,7 @@ public class Panda extends Animal {
- this.goalSelector.addGoal(12, new Panda.PandaRollGoal(this));
- this.goalSelector.addGoal(13, new FollowParentGoal(this, 1.25D));
- this.goalSelector.addGoal(14, new WaterAvoidingRandomStrollGoal(this, 1.0D));
-+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
- this.targetSelector.addGoal(1, (new Panda.PandaHurtByTargetGoal(this, new Class[0])).setAlertOthers(new Class[0]));
- }
-
-@@ -655,7 +683,7 @@ public class Panda extends Animal {
- ItemStack itemstack = player.getItemInHand(hand);
-
- if (this.isScared()) {
-- return InteractionResult.PASS;
-+ return tryRide(player, hand); // Purpur
- } else if (this.isOnBack()) {
- this.setOnBack(false);
- return InteractionResult.sidedSuccess(this.level().isClientSide);
-@@ -673,7 +701,7 @@ public class Panda extends Animal {
- this.setInLove(player, breedCopy); // Paper - Fix EntityBreedEvent copying
- } else {
- if (this.level().isClientSide || this.isSitting() || this.isInWater()) {
-- return InteractionResult.PASS;
-+ return tryRide(player, hand); // Purpur
- }
-
- this.tryToSit();
-@@ -692,7 +720,7 @@ public class Panda extends Animal {
-
- return InteractionResult.SUCCESS;
- } else {
-- return InteractionResult.PASS;
-+ return tryRide(player, hand); // Purpur
- }
- }
-
-@@ -737,7 +765,7 @@ public class Panda extends Animal {
- return this.isBaby() ? Panda.BABY_DIMENSIONS : super.getDefaultDimensions(pose);
- }
-
-- private static class PandaMoveControl extends MoveControl {
-+ private static class PandaMoveControl extends org.purpurmc.purpur.controller.MoveControllerWASD { // Purpur
-
- private final Panda panda;
-
-@@ -747,9 +775,9 @@ public class Panda extends Animal {
- }
-
- @Override
-- public void tick() {
-+ public void vanillaTick() { // Purpur
- if (this.panda.canPerformAction()) {
-- super.tick();
-+ super.vanillaTick(); // Purpur
- }
- }
- }
-diff --git a/src/main/java/net/minecraft/world/entity/animal/Parrot.java b/src/main/java/net/minecraft/world/entity/animal/Parrot.java
-index 5ca96541abbb754f4d9fbe01f37ebaf19c532bbb..12200cca54304d567a1880527a49f3e26ad08c7b 100644
---- a/src/main/java/net/minecraft/world/entity/animal/Parrot.java
-+++ b/src/main/java/net/minecraft/world/entity/animal/Parrot.java
-@@ -124,12 +124,68 @@ public class Parrot extends ShoulderRidingEntity implements VariantHolder type, Level world) {
- super(type, world);
-- this.moveControl = new FlyingMoveControl(this, 10, false);
-+ // Purpur start
-+ final org.purpurmc.purpur.controller.FlyingWithSpacebarMoveControllerWASD flyingController = new org.purpurmc.purpur.controller.FlyingWithSpacebarMoveControllerWASD(this, 0.3F);
-+ class ParrotMoveControl extends FlyingMoveControl {
-+ public ParrotMoveControl(Mob entity, int maxPitchChange, boolean noGravity) {
-+ super(entity, maxPitchChange, noGravity);
-+ }
-+
-+ @Override
-+ public void tick() {
-+ if (mob.getRider() != null && mob.isControllable()) {
-+ flyingController.purpurTick(mob.getRider());
-+ } else {
-+ super.tick();
-+ }
-+ }
-+
-+ @Override
-+ public boolean hasWanted() {
-+ return mob.getRider() != null && mob.isControllable() ? getForwardMot() != 0 || getStrafeMot() != 0 : super.hasWanted();
-+ }
-+ }
-+ this.moveControl = new ParrotMoveControl(this, 10, false);
-+ // Purpur end
- this.setPathfindingMalus(PathType.DANGER_FIRE, -1.0F);
- this.setPathfindingMalus(PathType.DAMAGE_FIRE, -1.0F);
- this.setPathfindingMalus(PathType.COCOA, -1.0F);
- }
-
-+ // Purpur start
-+ @Override
-+ public boolean isRidable() {
-+ return level().purpurConfig.parrotRidable;
-+ }
-+
-+ @Override
-+ public boolean dismountsUnderwater() {
-+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.parrotRidableInWater;
-+ }
-+
-+ @Override
-+ public boolean isControllable() {
-+ return level().purpurConfig.parrotControllable;
-+ }
-+
-+ @Override
-+ public double getMaxY() {
-+ return level().purpurConfig.parrotMaxY;
-+ }
-+
-+ @Override
-+ public void travel(Vec3 vec3) {
-+ super.travel(vec3);
-+ if (getRider() != null && this.isControllable() && !onGround) {
-+ float speed = (float) getAttributeValue(Attributes.FLYING_SPEED) * 2;
-+ setSpeed(speed);
-+ Vec3 mot = getDeltaMovement();
-+ move(net.minecraft.world.entity.MoverType.SELF, mot.multiply(speed, 0.25, speed));
-+ setDeltaMovement(mot.scale(0.9D));
-+ }
-+ }
-+ // Purpur end
-+
- @Nullable
- @Override
- public SpawnGroupData finalizeSpawn(ServerLevelAccessor world, DifficultyInstance difficulty, MobSpawnType spawnReason, @Nullable SpawnGroupData entityData) {
-@@ -148,8 +204,10 @@ public class Parrot extends ShoulderRidingEntity implements VariantHolder {
-diff --git a/src/main/java/net/minecraft/world/entity/animal/PolarBear.java b/src/main/java/net/minecraft/world/entity/animal/PolarBear.java
-index c87a57e8ceac32a6c8a603aa24f8cb053610e47c..d97314380b69ea2ad83b86c0f1bcaaccd4a1644f 100644
---- a/src/main/java/net/minecraft/world/entity/animal/PolarBear.java
-+++ b/src/main/java/net/minecraft/world/entity/animal/PolarBear.java
-@@ -59,11 +59,40 @@ public class PolarBear extends Animal implements NeutralMob {
- private int remainingPersistentAngerTime;
- @Nullable
- private UUID persistentAngerTarget;
-+ private int standTimer = 0; // Purpur
-
- public PolarBear(EntityType extends PolarBear> type, Level world) {
- super(type, world);
- }
-
-+ // Purpur start
-+ @Override
-+ public boolean isRidable() {
-+ return level().purpurConfig.polarBearRidable;
-+ }
-+
-+ @Override
-+ public boolean dismountsUnderwater() {
-+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.polarBearRidableInWater;
-+ }
-+
-+ @Override
-+ public boolean isControllable() {
-+ return level().purpurConfig.polarBearControllable;
-+ }
-+
-+ @Override
-+ public boolean onSpacebar() {
-+ if (!isStanding()) {
-+ if (getRider() != null && getRider().getForwardMot() == 0 && getRider().getStrafeMot() == 0) {
-+ setStanding(true);
-+ playSound(SoundEvents.POLAR_BEAR_WARNING, 1.0F, 1.0F);
-+ }
-+ }
-+ return false;
-+ }
-+ // Purpur end
-+
- @Nullable
- @Override
- public AgeableMob getBreedOffspring(ServerLevel world, AgeableMob entity) {
-@@ -79,12 +108,14 @@ public class PolarBear extends Animal implements NeutralMob {
- protected void registerGoals() {
- super.registerGoals();
- this.goalSelector.addGoal(0, new FloatGoal(this));
-+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
- this.goalSelector.addGoal(1, new PolarBear.PolarBearMeleeAttackGoal());
- this.goalSelector.addGoal(1, new PolarBear.PolarBearPanicGoal());
- this.goalSelector.addGoal(4, new FollowParentGoal(this, 1.25));
- this.goalSelector.addGoal(5, new RandomStrollGoal(this, 1.0));
- this.goalSelector.addGoal(6, new LookAtPlayerGoal(this, Player.class, 6.0F));
- this.goalSelector.addGoal(7, new RandomLookAroundGoal(this));
-+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
- this.targetSelector.addGoal(1, new PolarBear.PolarBearHurtByTargetGoal());
- this.targetSelector.addGoal(2, new PolarBear.PolarBearAttackPlayersGoal());
- this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, Player.class, 10, true, false, this::isAngryAt));
-@@ -201,6 +232,12 @@ public class PolarBear extends Animal implements NeutralMob {
- if (!this.level().isClientSide) {
- this.updatePersistentAnger((ServerLevel)this.level(), true);
- }
-+
-+ // Purpur start
-+ if (isStanding() && --standTimer <= 0) {
-+ setStanding(false);
-+ }
-+ // Purpur end
- }
-
- @Override
-@@ -230,6 +267,7 @@ public class PolarBear extends Animal implements NeutralMob {
-
- public void setStanding(boolean warning) {
- this.entityData.set(DATA_STANDING_ID, warning);
-+ standTimer = warning ? 20 : -1; // Purpur
- }
-
- public float getStandingAnimationScale(float tickDelta) {
-diff --git a/src/main/java/net/minecraft/world/entity/animal/Pufferfish.java b/src/main/java/net/minecraft/world/entity/animal/Pufferfish.java
-index 3f0fad476fe573c3ba946a9436d1b3f7c5260ee2..d75016647c513841358d08e5931821ecf8c21c2a 100644
---- a/src/main/java/net/minecraft/world/entity/animal/Pufferfish.java
-+++ b/src/main/java/net/minecraft/world/entity/animal/Pufferfish.java
-@@ -51,6 +51,18 @@ public class Pufferfish extends AbstractFish {
- this.refreshDimensions();
- }
-
-+ // Purpur start
-+ @Override
-+ public boolean isRidable() {
-+ return level().purpurConfig.pufferfishRidable;
-+ }
-+
-+ @Override
-+ public boolean isControllable() {
-+ return level().purpurConfig.pufferfishControllable;
-+ }
-+ // Purpur end
-+
- @Override
- protected void defineSynchedData(SynchedEntityData.Builder builder) {
- super.defineSynchedData(builder);
-diff --git a/src/main/java/net/minecraft/world/entity/animal/Rabbit.java b/src/main/java/net/minecraft/world/entity/animal/Rabbit.java
-index b58300e114e2e27ac68d7a9489bc52b127c9bc17..ba408a4195e03b484f143fbe66d5d3b13ebb8f11 100644
---- a/src/main/java/net/minecraft/world/entity/animal/Rabbit.java
-+++ b/src/main/java/net/minecraft/world/entity/animal/Rabbit.java
-@@ -86,6 +86,7 @@ public class Rabbit extends Animal implements VariantHolder {
- private boolean wasOnGround;
- private int jumpDelayTicks;
- public int moreCarrotTicks;
-+ private boolean actualJump; // Purpur
-
- public Rabbit(EntityType extends Rabbit> type, Level world) {
- super(type, world);
-@@ -93,9 +94,55 @@ public class Rabbit extends Animal implements VariantHolder