diff --git a/gradle.properties b/gradle.properties
index 7a9b0018c..e8a0bf5fc 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -1,7 +1,7 @@
group = org.purpurmc.purpur
version = 1.19.3-R0.1-SNAPSHOT
-paperCommit = f9dc371fd8c56f1ad1359fc3bf1f7a40921ec66f
+paperCommit = 7baf427e9065c2beee77ff24b6781d6d86bb56a3
org.gradle.caching = true
org.gradle.parallel = true
diff --git a/patches/api/0001-Pufferfish-API-Changes.patch b/patches/api/0001-Pufferfish-API-Changes.patch
index 1fa2ad8e1..5dca28114 100644
--- a/patches/api/0001-Pufferfish-API-Changes.patch
+++ b/patches/api/0001-Pufferfish-API-Changes.patch
@@ -20,7 +20,7 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see .
diff --git a/build.gradle.kts b/build.gradle.kts
-index c16ebc233bce9fd2020d3dde6253c3b90000c0f3..4e78a535b528a87ad0ec9c361541c79854929c6e 100644
+index 56333098150995cd36793de75a998b628e82881d..d65eb422de43b70496db2da85e8f2da30a5214fb 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -41,6 +41,7 @@ dependencies {
@@ -426,10 +426,10 @@ index 3a9aaca2e76411a9c27f9f5e0f22d060d5a66d06..9584e245144b561b4f6745b2f26a4f69
}
diff --git a/src/main/java/org/bukkit/plugin/SimplePluginManager.java b/src/main/java/org/bukkit/plugin/SimplePluginManager.java
-index b012ce40d82389c29d1b841ff685425ac10a7f9e..c077e7c883613fcb6e559b4e4776e794caa3b363 100644
+index 2b8308989fce7f8a16907f8711b362e671fdbfb6..bd4d1a40f53784662174d426533ef4b5433a15b7 100644
--- a/src/main/java/org/bukkit/plugin/SimplePluginManager.java
+++ b/src/main/java/org/bukkit/plugin/SimplePluginManager.java
-@@ -625,7 +625,9 @@ public final class SimplePluginManager implements PluginManager {
+@@ -584,7 +584,9 @@ public final class SimplePluginManager implements PluginManager {
// Paper start
private void handlePluginException(String msg, Throwable ex, Plugin plugin) {
@@ -439,7 +439,7 @@ index b012ce40d82389c29d1b841ff685425ac10a7f9e..c077e7c883613fcb6e559b4e4776e794
callEvent(new com.destroystokyo.paper.event.server.ServerExceptionEvent(new com.destroystokyo.paper.exception.ServerPluginEnableDisableException(msg, ex, plugin)));
}
// Paper end
-@@ -684,9 +686,11 @@ public final class SimplePluginManager implements PluginManager {
+@@ -654,9 +656,11 @@ public final class SimplePluginManager implements PluginManager {
));
}
} catch (Throwable ex) {
@@ -452,20 +452,20 @@ index b012ce40d82389c29d1b841ff685425ac10a7f9e..c077e7c883613fcb6e559b4e4776e794
callEvent(new com.destroystokyo.paper.event.server.ServerExceptionEvent(new com.destroystokyo.paper.exception.ServerEventException(msg, ex, registration.getPlugin(), registration.getListener(), event)));
}
diff --git a/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java b/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java
-index 7e5149a21b6747f64425a277c142e69f0ef43b3f..bf93183aa76af005d5daf8cf533f908d535166e8 100644
+index 88d852c1a729ffd5951da803da424b31591c9f9a..90fdaee8b07df0acf8863103b47a1c68e38a3e4f 100644
--- a/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java
+++ b/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java
-@@ -370,7 +370,9 @@ public final class JavaPluginLoader implements PluginLoader {
+@@ -336,7 +336,9 @@ public final class JavaPluginLoader implements PluginLoader {
try {
jPlugin.setEnabled(true);
} catch (Throwable ex) {
+ gg.pufferfish.pufferfish.sentry.SentryContext.setPluginContext(plugin); // Pufferfish
server.getLogger().log(Level.SEVERE, "Error occurred while enabling " + plugin.getDescription().getFullName() + " (Is it up to date?)", ex);
+ gg.pufferfish.pufferfish.sentry.SentryContext.removePluginContext(); // Pufferfish
- // Paper start - Disable plugins that fail to load
- this.server.getPluginManager().disablePlugin(jPlugin);
- return;
-@@ -399,7 +401,9 @@ public final class JavaPluginLoader implements PluginLoader {
+ }
+
+ // Perhaps abort here, rather than continue going, but as it stands,
+@@ -361,7 +363,9 @@ public final class JavaPluginLoader implements PluginLoader {
try {
jPlugin.setEnabled(false);
} catch (Throwable ex) {
@@ -476,19 +476,19 @@ index 7e5149a21b6747f64425a277c142e69f0ef43b3f..bf93183aa76af005d5daf8cf533f908d
if (cloader instanceof PluginClassLoader) {
diff --git a/src/main/java/org/bukkit/plugin/java/PluginClassLoader.java b/src/main/java/org/bukkit/plugin/java/PluginClassLoader.java
-index cb3c25ef46b279ffdde87f47f729eb8aa7549c1c..6fb179c4d420eac3a6399fbff104b5af6ba448ba 100644
+index 4b3380a42b4be54ef4df806c5db0d3a1be5909a6..a60ae10de96203772d6284e9399069df9949094f 100644
--- a/src/main/java/org/bukkit/plugin/java/PluginClassLoader.java
+++ b/src/main/java/org/bukkit/plugin/java/PluginClassLoader.java
-@@ -46,6 +46,8 @@ public final class PluginClassLoader extends URLClassLoader { // Spigot
- private final Set seenIllegalAccess = Collections.newSetFromMap(new ConcurrentHashMap<>());
- private java.util.logging.Logger logger; // Paper - add field
+@@ -49,6 +49,8 @@ public final class PluginClassLoader extends URLClassLoader implements io.paperm
+ private io.papermc.paper.plugin.provider.classloader.PluginClassLoaderGroup classLoaderGroup; // Paper
+ public io.papermc.paper.plugin.provider.entrypoint.DependencyContext dependencyContext; // Paper
+ private boolean closed = false; // Pufferfish
+
static {
ClassLoader.registerAsParallelCapable();
}
-@@ -177,6 +179,7 @@ public final class PluginClassLoader extends URLClassLoader { // Spigot
+@@ -199,6 +201,7 @@ public final class PluginClassLoader extends URLClassLoader implements io.paperm
throw new ClassNotFoundException(name);
}
@@ -496,7 +496,7 @@ index cb3c25ef46b279ffdde87f47f729eb8aa7549c1c..6fb179c4d420eac3a6399fbff104b5af
@Override
protected Class> findClass(String name) throws ClassNotFoundException {
if (name.startsWith("org.bukkit.") || name.startsWith("net.minecraft.")) {
-@@ -184,7 +187,7 @@ public final class PluginClassLoader extends URLClassLoader { // Spigot
+@@ -206,7 +209,7 @@ public final class PluginClassLoader extends URLClassLoader implements io.paperm
}
Class> result = classes.get(name);
@@ -505,16 +505,16 @@ index cb3c25ef46b279ffdde87f47f729eb8aa7549c1c..6fb179c4d420eac3a6399fbff104b5af
String path = name.replace('.', '/').concat(".class");
JarEntry entry = jar.getJarEntry(path);
-@@ -231,6 +234,7 @@ public final class PluginClassLoader extends URLClassLoader { // Spigot
- classes.put(name, result);
+@@ -253,6 +256,7 @@ public final class PluginClassLoader extends URLClassLoader implements io.paperm
+ this.setClass(name, result); // Paper
}
+ if (result == null) throw new ClassNotFoundException(name); // Pufferfish
return result;
}
-@@ -239,6 +243,7 @@ public final class PluginClassLoader extends URLClassLoader { // Spigot
- try {
+@@ -267,6 +271,7 @@ public final class PluginClassLoader extends URLClassLoader implements io.paperm
+ // Paper end
super.close();
} finally {
+ this.closed = true; // Pufferfish
diff --git a/patches/api/0025-Add-option-to-disable-zombie-aggressiveness-towards-.patch b/patches/api/0024-Add-option-to-disable-zombie-aggressiveness-towards-.patch
similarity index 100%
rename from patches/api/0025-Add-option-to-disable-zombie-aggressiveness-towards-.patch
rename to patches/api/0024-Add-option-to-disable-zombie-aggressiveness-towards-.patch
diff --git a/patches/api/0024-Spigot-Improve-output-of-plugins-command.patch b/patches/api/0024-Spigot-Improve-output-of-plugins-command.patch
deleted file mode 100644
index a44be9cf1..000000000
--- a/patches/api/0024-Spigot-Improve-output-of-plugins-command.patch
+++ /dev/null
@@ -1,115 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Parker Hawke
-Date: Sat, 27 Jun 2020 18:43:37 -0400
-Subject: [PATCH] Spigot - Improve output of plugins command
-
-Co-authored-by: Oharass
-
-diff --git a/src/main/java/org/bukkit/command/defaults/PluginsCommand.java b/src/main/java/org/bukkit/command/defaults/PluginsCommand.java
-index 1aa58c59e1e8738bbdc77752885ff3b18b29de42..46525191653e4ac0e0366c0357f368c0b709f20d 100644
---- a/src/main/java/org/bukkit/command/defaults/PluginsCommand.java
-+++ b/src/main/java/org/bukkit/command/defaults/PluginsCommand.java
-@@ -11,6 +11,14 @@ import org.bukkit.ChatColor;
- import org.bukkit.command.CommandSender;
- import org.bukkit.plugin.Plugin;
- import org.jetbrains.annotations.NotNull;
-+// Spigot start
-+import net.kyori.adventure.text.Component;
-+import net.kyori.adventure.text.TextComponent;
-+import net.kyori.adventure.text.format.NamedTextColor;
-+import net.kyori.adventure.text.event.ClickEvent;
-+import org.bukkit.entity.Player;
-+import org.bukkit.plugin.PluginDescriptionFile;
-+// Spigot end
-
- public class PluginsCommand extends BukkitCommand {
- public PluginsCommand(@NotNull String name) {
-@@ -25,7 +33,13 @@ public class PluginsCommand extends BukkitCommand {
- public boolean execute(@NotNull CommandSender sender, @NotNull String currentAlias, @NotNull String[] args) {
- if (!testPermission(sender)) return true;
-
-- sender.sendMessage("Plugins " + getPluginList());
-+ // Spigot start
-+ if (sender instanceof Player && sender.hasPermission("bukkit.command.version")) {
-+ sender.sendMessage(getPluginListSpigot());
-+ } else {
-+ sender.sendMessage("Plugins " + getPluginList());
-+ }
-+ // Spigot end
- return true;
- }
-
-@@ -71,4 +85,73 @@ public class PluginsCommand extends BukkitCommand {
- // Paper end
- }
-
-+ // Spigot start
-+ @NotNull
-+ private TextComponent getPluginListSpigot() {
-+ Plugin[] plugins = Bukkit.getPluginManager().getPlugins();
-+ TextComponent.Builder builder = Component.text();
-+ builder.append(Component.text("Plugins (" + plugins.length + "): "));
-+
-+ int index = 0;
-+ for (Plugin plugin : plugins) {
-+ if (index++ > 0) {
-+ builder.append(Component.text(", ", NamedTextColor.WHITE));
-+ }
-+
-+ // Event components
-+ PluginDescriptionFile description = plugin.getDescription();
-+ TextComponent.Builder hover = Component.text();
-+ hover.append(Component.text("Version: ", NamedTextColor.WHITE)).append(Component.text(description.getVersion(), NamedTextColor.GREEN));
-+
-+ if (description.getDescription() != null) {
-+ hover.append(Component.newline())
-+ .append(Component.text("Description: ", NamedTextColor.WHITE))
-+ .append(Component.text(description.getDescription(), NamedTextColor.GREEN));
-+ }
-+
-+ if (description.getWebsite() != null) {
-+ hover.append(Component.newline())
-+ .append(Component.text("Website: ", NamedTextColor.WHITE))
-+ .append(Component.text(description.getWebsite(), NamedTextColor.GREEN));
-+ }
-+
-+ if (!description.getAuthors().isEmpty()) {
-+ hover.append(Component.newline());
-+ if (description.getAuthors().size() == 1) {
-+ hover.append(Component.text("Author: "));
-+ } else {
-+ hover.append(Component.text("Authors: "));
-+ }
-+
-+ hover.append(getAuthors(description));
-+ }
-+
-+ // Plugin list entry
-+ builder.append(Component.text(plugin.getDescription().getName(), plugin.isEnabled() ? NamedTextColor.GREEN : NamedTextColor.RED)
-+ .hoverEvent(hover.build()).clickEvent(ClickEvent.suggestCommand("/version " + description.getName())));
-+
-+ if (plugin.getDescription().getProvides().size() > 0) {
-+ builder.append(Component.text(" (", NamedTextColor.WHITE)).append(Component.text(String.join(", ", plugin.getDescription().getProvides()))).append(Component.text(")"));
-+ }
-+ }
-+
-+ return builder.build();
-+ }
-+
-+ @NotNull
-+ private TextComponent getAuthors(@NotNull final PluginDescriptionFile description) {
-+ TextComponent.Builder builder = Component.text();
-+ List authors = description.getAuthors();
-+
-+ for (int i = 0; i < authors.size(); i++) {
-+ if (i > 0) {
-+ builder.append(Component.text(i < authors.size() - 1 ? ", " : " and ", NamedTextColor.WHITE));
-+ }
-+
-+ builder.append(Component.text(authors.get(i), NamedTextColor.GREEN));
-+ }
-+
-+ return builder.build();
-+ }
-+ // Spigot end
- }
diff --git a/patches/api/0026-Add-predicate-to-recipe-s-ExactChoice-ingredient.patch b/patches/api/0025-Add-predicate-to-recipe-s-ExactChoice-ingredient.patch
similarity index 100%
rename from patches/api/0026-Add-predicate-to-recipe-s-ExactChoice-ingredient.patch
rename to patches/api/0025-Add-predicate-to-recipe-s-ExactChoice-ingredient.patch
diff --git a/patches/api/0028-Rabid-Wolf-API.patch b/patches/api/0026-Rabid-Wolf-API.patch
similarity index 100%
rename from patches/api/0028-Rabid-Wolf-API.patch
rename to patches/api/0026-Rabid-Wolf-API.patch
diff --git a/patches/api/0027-Alphabetize-in-game-plugins-list.patch b/patches/api/0027-Alphabetize-in-game-plugins-list.patch
deleted file mode 100644
index c9ba2a0dc..000000000
--- a/patches/api/0027-Alphabetize-in-game-plugins-list.patch
+++ /dev/null
@@ -1,19 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: William Blake Galbreath
-Date: Tue, 8 Dec 2020 09:48:18 -0600
-Subject: [PATCH] Alphabetize in-game /plugins list
-
-
-diff --git a/src/main/java/org/bukkit/command/defaults/PluginsCommand.java b/src/main/java/org/bukkit/command/defaults/PluginsCommand.java
-index 46525191653e4ac0e0366c0357f368c0b709f20d..0b4937dc6d003caf0f43a01a5a0801d5804082bf 100644
---- a/src/main/java/org/bukkit/command/defaults/PluginsCommand.java
-+++ b/src/main/java/org/bukkit/command/defaults/PluginsCommand.java
-@@ -88,7 +88,7 @@ public class PluginsCommand extends BukkitCommand {
- // Spigot start
- @NotNull
- private TextComponent getPluginListSpigot() {
-- Plugin[] plugins = Bukkit.getPluginManager().getPlugins();
-+ Plugin[] plugins = Arrays.stream(Bukkit.getPluginManager().getPlugins()).sorted(java.util.Comparator.comparing(plugin -> plugin.getName().toLowerCase())).toArray(Plugin[]::new); // Purpur
- TextComponent.Builder builder = Component.text();
- builder.append(Component.text("Plugins (" + plugins.length + "): "));
-
diff --git a/patches/api/0029-PlayerBookTooLargeEvent.patch b/patches/api/0027-PlayerBookTooLargeEvent.patch
similarity index 100%
rename from patches/api/0029-PlayerBookTooLargeEvent.patch
rename to patches/api/0027-PlayerBookTooLargeEvent.patch
diff --git a/patches/api/0030-Full-netherite-armor-grants-fire-resistance.patch b/patches/api/0028-Full-netherite-armor-grants-fire-resistance.patch
similarity index 100%
rename from patches/api/0030-Full-netherite-armor-grants-fire-resistance.patch
rename to patches/api/0028-Full-netherite-armor-grants-fire-resistance.patch
diff --git a/patches/api/0031-Add-EntityTeleportHinderedEvent.patch b/patches/api/0029-Add-EntityTeleportHinderedEvent.patch
similarity index 100%
rename from patches/api/0031-Add-EntityTeleportHinderedEvent.patch
rename to patches/api/0029-Add-EntityTeleportHinderedEvent.patch
diff --git a/patches/api/0032-Add-enchantment-target-for-bows-and-crossbows.patch b/patches/api/0030-Add-enchantment-target-for-bows-and-crossbows.patch
similarity index 100%
rename from patches/api/0032-Add-enchantment-target-for-bows-and-crossbows.patch
rename to patches/api/0030-Add-enchantment-target-for-bows-and-crossbows.patch
diff --git a/patches/api/0033-Iron-golem-poppy-calms-anger.patch b/patches/api/0031-Iron-golem-poppy-calms-anger.patch
similarity index 100%
rename from patches/api/0033-Iron-golem-poppy-calms-anger.patch
rename to patches/api/0031-Iron-golem-poppy-calms-anger.patch
diff --git a/patches/api/0034-API-for-any-mob-to-burn-daylight.patch b/patches/api/0032-API-for-any-mob-to-burn-daylight.patch
similarity index 100%
rename from patches/api/0034-API-for-any-mob-to-burn-daylight.patch
rename to patches/api/0032-API-for-any-mob-to-burn-daylight.patch
diff --git a/patches/api/0035-Add-back-player-spawned-endermite-API.patch b/patches/api/0033-Add-back-player-spawned-endermite-API.patch
similarity index 100%
rename from patches/api/0035-Add-back-player-spawned-endermite-API.patch
rename to patches/api/0033-Add-back-player-spawned-endermite-API.patch
diff --git a/patches/api/0036-Fix-default-permission-system.patch b/patches/api/0034-Fix-default-permission-system.patch
similarity index 100%
rename from patches/api/0036-Fix-default-permission-system.patch
rename to patches/api/0034-Fix-default-permission-system.patch
diff --git a/patches/api/0037-Summoner-API.patch b/patches/api/0035-Summoner-API.patch
similarity index 100%
rename from patches/api/0037-Summoner-API.patch
rename to patches/api/0035-Summoner-API.patch
diff --git a/patches/api/0038-Clean-up-version-command-output.patch b/patches/api/0036-Clean-up-version-command-output.patch
similarity index 100%
rename from patches/api/0038-Clean-up-version-command-output.patch
rename to patches/api/0036-Clean-up-version-command-output.patch
diff --git a/patches/api/0039-Extended-OfflinePlayer-API.patch b/patches/api/0037-Extended-OfflinePlayer-API.patch
similarity index 100%
rename from patches/api/0039-Extended-OfflinePlayer-API.patch
rename to patches/api/0037-Extended-OfflinePlayer-API.patch
diff --git a/patches/api/0040-Added-the-ability-to-add-combustible-items.patch b/patches/api/0038-Added-the-ability-to-add-combustible-items.patch
similarity index 100%
rename from patches/api/0040-Added-the-ability-to-add-combustible-items.patch
rename to patches/api/0038-Added-the-ability-to-add-combustible-items.patch
diff --git a/patches/api/0041-Potion-NamespacedKey.patch b/patches/api/0039-Potion-NamespacedKey.patch
similarity index 100%
rename from patches/api/0041-Potion-NamespacedKey.patch
rename to patches/api/0039-Potion-NamespacedKey.patch
diff --git a/patches/api/0042-Grindstone-API.patch b/patches/api/0040-Grindstone-API.patch
similarity index 100%
rename from patches/api/0042-Grindstone-API.patch
rename to patches/api/0040-Grindstone-API.patch
diff --git a/patches/api/0043-Shears-can-have-looting-enchantment.patch b/patches/api/0041-Shears-can-have-looting-enchantment.patch
similarity index 100%
rename from patches/api/0043-Shears-can-have-looting-enchantment.patch
rename to patches/api/0041-Shears-can-have-looting-enchantment.patch
diff --git a/patches/api/0044-Lobotomize-stuck-villagers.patch b/patches/api/0042-Lobotomize-stuck-villagers.patch
similarity index 100%
rename from patches/api/0044-Lobotomize-stuck-villagers.patch
rename to patches/api/0042-Lobotomize-stuck-villagers.patch
diff --git a/patches/api/0046-Add-local-difficulty-api.patch b/patches/api/0043-Add-local-difficulty-api.patch
similarity index 100%
rename from patches/api/0046-Add-local-difficulty-api.patch
rename to patches/api/0043-Add-local-difficulty-api.patch
diff --git a/patches/api/0047-Remove-Timings.patch b/patches/api/0044-Remove-Timings.patch
similarity index 98%
rename from patches/api/0047-Remove-Timings.patch
rename to patches/api/0044-Remove-Timings.patch
index 2a5ad8866..fd88a5278 100644
--- a/patches/api/0047-Remove-Timings.patch
+++ b/patches/api/0044-Remove-Timings.patch
@@ -120,7 +120,7 @@ index 3132dc98d26c54c5e46162e53aaed195d7335c8d..b461b5c50f97f11cb9ef68abc520271b
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 0bc24d0effe9b2e44c41a1c00060b0ebf7395c4a..aad6edb02f690b875db28e8db9bcd4e36b90d766 100644
+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 {
diff --git a/patches/api/0048-Add-Bee-API.patch b/patches/api/0045-Add-Bee-API.patch
similarity index 100%
rename from patches/api/0048-Add-Bee-API.patch
rename to patches/api/0045-Add-Bee-API.patch
diff --git a/patches/api/0045-Spark-Profiler.patch b/patches/api/0045-Spark-Profiler.patch
deleted file mode 100644
index b86e87503..000000000
--- a/patches/api/0045-Spark-Profiler.patch
+++ /dev/null
@@ -1,23 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: granny
-Date: Thu, 30 Jun 2022 02:32:52 -0700
-Subject: [PATCH] Spark Profiler
-
-
-diff --git a/src/main/java/org/bukkit/plugin/SimplePluginManager.java b/src/main/java/org/bukkit/plugin/SimplePluginManager.java
-index c077e7c883613fcb6e559b4e4776e794caa3b363..75be5cdfeb30732975bbc38dc7aab52a0cdead9c 100644
---- a/src/main/java/org/bukkit/plugin/SimplePluginManager.java
-+++ b/src/main/java/org/bukkit/plugin/SimplePluginManager.java
-@@ -167,6 +167,12 @@ public final class SimplePluginManager implements PluginManager {
- server.getLogger().log(Level.SEVERE, "Could not load '" + file.getPath() + "' in folder '" + file.getParentFile().getPath() + "'", ex); // Paper
- continue;
- }
-+ // Purpur start
-+ if (!Boolean.getBoolean("Purpur.IReallyDontWantSpark") && plugins.containsKey(description.getName()) && description.getName().equalsIgnoreCase("spark")) {
-+ server.getLogger().log(Level.INFO, "Purpur: Using user-provided spark plugin instead of our own.");
-+ continue;
-+ }
-+ // Purpur end
-
- File replacedFile = plugins.put(description.getName(), file);
- if (replacedFile != null) {
diff --git a/patches/api/0049-Debug-Marker-API.patch b/patches/api/0046-Debug-Marker-API.patch
similarity index 100%
rename from patches/api/0049-Debug-Marker-API.patch
rename to patches/api/0046-Debug-Marker-API.patch
diff --git a/patches/api/0050-Add-death-screen-API.patch b/patches/api/0047-Add-death-screen-API.patch
similarity index 100%
rename from patches/api/0050-Add-death-screen-API.patch
rename to patches/api/0047-Add-death-screen-API.patch
diff --git a/patches/api/0051-Add-item-packet-serialize-event.patch b/patches/api/0048-Add-item-packet-serialize-event.patch
similarity index 100%
rename from patches/api/0051-Add-item-packet-serialize-event.patch
rename to patches/api/0048-Add-item-packet-serialize-event.patch
diff --git a/patches/api/0052-Language-API.patch b/patches/api/0049-Language-API.patch
similarity index 100%
rename from patches/api/0052-Language-API.patch
rename to patches/api/0049-Language-API.patch
diff --git a/patches/api/0053-Add-log-suppression-for-LibraryLoader.patch b/patches/api/0050-Add-log-suppression-for-LibraryLoader.patch
similarity index 80%
rename from patches/api/0053-Add-log-suppression-for-LibraryLoader.patch
rename to patches/api/0050-Add-log-suppression-for-LibraryLoader.patch
index c6841bce3..51485e60c 100644
--- a/patches/api/0053-Add-log-suppression-for-LibraryLoader.patch
+++ b/patches/api/0050-Add-log-suppression-for-LibraryLoader.patch
@@ -5,11 +5,11 @@ 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 bf93183aa76af005d5daf8cf533f908d535166e8..d7ac5d5bb6180f97706be9c5ef25956f244bc376 100644
+index 90fdaee8b07df0acf8863103b47a1c68e38a3e4f..6319f6c1428d0e272681984c1b15ec05a3272423 100644
--- a/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java
+++ b/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java
-@@ -58,6 +58,7 @@ public final class JavaPluginLoader implements PluginLoader {
- private final Map classLoadLockCount = new java.util.HashMap(); // Paper
+@@ -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
@@ -17,10 +17,10 @@ index bf93183aa76af005d5daf8cf533f908d535166e8..d7ac5d5bb6180f97706be9c5ef25956f
/**
* 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 6d634b0ea813ccb19f1562a7d0e5a59cea4eab21..06a7f27aa7c3963f63fe158438a83fdf67a20c51 100644
+index f9e67e20133d349e43d126dbb48b34d4d9aceb4c..f8efacb9447d72e66a1fd59db18425fe0629e266 100644
--- a/src/main/java/org/bukkit/plugin/java/LibraryLoader.java
+++ b/src/main/java/org/bukkit/plugin/java/LibraryLoader.java
-@@ -62,6 +62,7 @@ class LibraryLoader
+@@ -65,6 +65,7 @@ public class LibraryLoader
@Override
public void transferStarted(@NotNull TransferEvent event) throws TransferCancelledException
{
@@ -28,7 +28,7 @@ index 6d634b0ea813ccb19f1562a7d0e5a59cea4eab21..06a7f27aa7c3963f63fe158438a83fdf
logger.log( Level.INFO, "Downloading {0}", event.getResource().getRepositoryUrl() + event.getResource().getResourceName() );
}
} );
-@@ -77,6 +78,7 @@ class LibraryLoader
+@@ -80,6 +81,7 @@ public class LibraryLoader
{
return null;
}
@@ -36,7 +36,7 @@ index 6d634b0ea813ccb19f1562a7d0e5a59cea4eab21..06a7f27aa7c3963f63fe158438a83fdf
logger.log( Level.INFO, "[{0}] Loading {1} libraries... please wait", new Object[]
{
desc.getName(), desc.getLibraries().size()
-@@ -115,6 +117,7 @@ class LibraryLoader
+@@ -118,6 +120,7 @@ public class LibraryLoader
}
jarFiles.add( url );
diff --git a/patches/api/0054-Fire-Immunity-API.patch b/patches/api/0051-Fire-Immunity-API.patch
similarity index 100%
rename from patches/api/0054-Fire-Immunity-API.patch
rename to patches/api/0051-Fire-Immunity-API.patch
diff --git a/patches/api/0055-Added-goat-ram-event.patch b/patches/api/0052-Added-goat-ram-event.patch
similarity index 100%
rename from patches/api/0055-Added-goat-ram-event.patch
rename to patches/api/0052-Added-goat-ram-event.patch
diff --git a/patches/server/0001-Pufferfish-Server-Changes.patch b/patches/server/0001-Pufferfish-Server-Changes.patch
index 5e08fae96..a53ee5f04 100644
--- a/patches/server/0001-Pufferfish-Server-Changes.patch
+++ b/patches/server/0001-Pufferfish-Server-Changes.patch
@@ -3544,7 +3544,7 @@ index ebe65474a4a05ff1637d7f37ebcfe690af59def5..42142c512b12e5b269c19f1e821c50e7
@Nullable
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
-index f1a3d8dae2a61b875e423aebfdfcad2f77d498ff..5077c545e74711d5268d3cfa31926af1e995ea05 100644
+index 621919a4a45caa73a43020fdca24764d95d89fd2..fdabf7f68d1fa12598e3803cf2d72054645393b1 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
@@ -261,7 +261,7 @@ import javax.annotation.Nullable; // Paper
@@ -3556,7 +3556,7 @@ index f1a3d8dae2a61b875e423aebfdfcad2f77d498ff..5077c545e74711d5268d3cfa31926af1
private final String serverVersion;
private final String bukkitVersion = Versioning.getBukkitVersion();
private final Logger logger = Logger.getLogger("Minecraft");
-@@ -1067,6 +1067,11 @@ public final class CraftServer implements Server {
+@@ -1048,6 +1048,11 @@ public final class CraftServer implements Server {
plugin.getDescription().getName(),
"This plugin is not properly shutting down its async tasks when it is being shut down. This task may throw errors during the final shutdown logs and might not complete before process dies."
));
@@ -3581,10 +3581,10 @@ index f7ea77dd82d978ad307f99c743efacfb34478b3d..009ab06182359862b8f543030ec4fe4e
}
}
diff --git a/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java b/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java
-index 287e8c4c57ea1737460fdaf114e7eed627a25b2d..b9e4305f515faa14c2afc0ccaf23d205e9c9f858 100644
+index 4c50d0422fcc6860e502f8b745aac2695f47073d..258db803682fcc4f76c792ef741b77e63a65ce98 100644
--- a/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java
+++ b/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java
-@@ -462,7 +462,7 @@ public final class CraftMagicNumbers implements UnsafeValues {
+@@ -468,7 +468,7 @@ public final class CraftMagicNumbers implements UnsafeValues {
@Override
public com.destroystokyo.paper.util.VersionFetcher getVersionFetcher() {
diff --git a/patches/server/0003-Rebrand.patch b/patches/server/0003-Rebrand.patch
index 642d6a3a1..73ae19088 100644
--- a/patches/server/0003-Rebrand.patch
+++ b/patches/server/0003-Rebrand.patch
@@ -193,7 +193,7 @@ index 68d16efaf9c2d997afabadcf1ee24c5de685b5b3..861cd9f092a19aca520c2be4ba7a6ee3
public SystemReport fillSystemReport(SystemReport details) {
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
-index 5077c545e74711d5268d3cfa31926af1e995ea05..0c75b7f595dc14c02f6384f786a3943f4e16bcef 100644
+index fdabf7f68d1fa12598e3803cf2d72054645393b1..9f8f6ff6408a191012241846cce058b80969e5fe 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
@@ -261,7 +261,7 @@ import javax.annotation.Nullable; // Paper
@@ -259,10 +259,10 @@ index cdefb2025eedea7e204d70d568adaf1c1ec4c03c..d1526ed7197b883e1d1f07baf285bf5e
// (async tasks must live with race-conditions if they attempt to cancel between these few lines of code)
}
diff --git a/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java b/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java
-index b9e4305f515faa14c2afc0ccaf23d205e9c9f858..feacdb99ce7ec21fb85b5a13be7a7cb530a40e21 100644
+index 258db803682fcc4f76c792ef741b77e63a65ce98..a3a627eca3219d234c57ab491a30c770baf24395 100644
--- a/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java
+++ b/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java
-@@ -462,7 +462,7 @@ public final class CraftMagicNumbers implements UnsafeValues {
+@@ -468,7 +468,7 @@ public final class CraftMagicNumbers implements UnsafeValues {
@Override
public com.destroystokyo.paper.util.VersionFetcher getVersionFetcher() {
diff --git a/patches/server/0004-Purpur-config-files.patch b/patches/server/0004-Purpur-config-files.patch
index 26f265eec..5b9a62132 100644
--- a/patches/server/0004-Purpur-config-files.patch
+++ b/patches/server/0004-Purpur-config-files.patch
@@ -105,10 +105,10 @@ index 42299fd6ae663b01bb5f010b96887caa744dcb96..816f906f98adc180f37d2b216628e576
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 0c75b7f595dc14c02f6384f786a3943f4e16bcef..0fcc3685f3f5b5c81a85f6981541f2cecc386da3 100644
+index 9f8f6ff6408a191012241846cce058b80969e5fe..d633d9da06ea08b55a57fa4e9b8104ccb074385f 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
-@@ -981,6 +981,7 @@ public final class CraftServer implements Server {
+@@ -962,6 +962,7 @@ public final class CraftServer implements Server {
org.spigotmc.SpigotConfig.init((File) console.options.valueOf("spigot-settings")); // Spigot
this.console.paperConfigurations.reloadConfigs(this.console);
@@ -116,7 +116,7 @@ index 0c75b7f595dc14c02f6384f786a3943f4e16bcef..0fcc3685f3f5b5c81a85f6981541f2ce
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))
-@@ -996,6 +997,7 @@ public final class CraftServer implements Server {
+@@ -977,6 +978,7 @@ public final class CraftServer implements Server {
}
}
world.spigotConfig.init(); // Spigot
@@ -124,7 +124,7 @@ index 0c75b7f595dc14c02f6384f786a3943f4e16bcef..0fcc3685f3f5b5c81a85f6981541f2ce
}
Plugin[] pluginClone = pluginManager.getPlugins().clone(); // Paper
-@@ -1011,6 +1013,7 @@ public final class CraftServer implements Server {
+@@ -992,6 +994,7 @@ public final class CraftServer implements Server {
this.reloadData();
org.spigotmc.SpigotConfig.registerCommands(); // Spigot
io.papermc.paper.command.PaperCommands.registerCommands(this.console); // Paper
@@ -132,7 +132,7 @@ index 0c75b7f595dc14c02f6384f786a3943f4e16bcef..0fcc3685f3f5b5c81a85f6981541f2ce
this.overrideAllCommandBlockCommands = this.commandsConfiguration.getStringList("command-block-overrides").contains("*");
this.ignoreVanillaPermissions = this.commandsConfiguration.getBoolean("ignore-vanilla-permissions");
-@@ -2770,6 +2773,18 @@ public final class CraftServer implements Server {
+@@ -2751,6 +2754,18 @@ public final class CraftServer implements Server {
return CraftServer.this.console.paperConfigurations.createLegacyObject(CraftServer.this.console);
}
diff --git a/patches/server/0013-Bring-back-server-name.patch b/patches/server/0013-Bring-back-server-name.patch
index 3bdefdc90..0555dde9d 100644
--- a/patches/server/0013-Bring-back-server-name.patch
+++ b/patches/server/0013-Bring-back-server-name.patch
@@ -17,10 +17,10 @@ index c7e4330c93baff1f3027d7c75cf857b673d38970..5134fed0cd0eedbe0c2177bce91b978b
public final boolean spawnNpcs = this.get("spawn-npcs", true);
public final boolean pvp = this.get("pvp", true);
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
-index 0fcc3685f3f5b5c81a85f6981541f2cecc386da3..45f75ebc00f0e371066bb244a61c8d68a57c68c8 100644
+index d633d9da06ea08b55a57fa4e9b8104ccb074385f..1528e8ec7d475e59d93b28a8874676afdb5c96ac 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
-@@ -2960,4 +2960,11 @@ public final class CraftServer implements Server {
+@@ -2941,4 +2941,11 @@ public final class CraftServer implements Server {
}
// Paper end
diff --git a/patches/server/0016-Lagging-threshold.patch b/patches/server/0016-Lagging-threshold.patch
index 9d9f04c0e..5db057b12 100644
--- a/patches/server/0016-Lagging-threshold.patch
+++ b/patches/server/0016-Lagging-threshold.patch
@@ -25,10 +25,10 @@ index 70d3fc30e21a6c3c3d7a87ce2e43dd2eb8322b2c..7c731c72a19c43f4b291557310c632f3
}
// Spigot end
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
-index 45f75ebc00f0e371066bb244a61c8d68a57c68c8..e451348e26c024d48dfccbc6f182544baef7ff3f 100644
+index 1528e8ec7d475e59d93b28a8874676afdb5c96ac..d7279047dfe353ce9f4312ac0fc53623fa8d7a55 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
-@@ -2966,5 +2966,10 @@ public final class CraftServer implements Server {
+@@ -2947,5 +2947,10 @@ public final class CraftServer implements Server {
public String getServerName() {
return this.getProperties().serverName;
}
diff --git a/patches/server/0062-Add-5-second-tps-average-in-tps.patch b/patches/server/0062-Add-5-second-tps-average-in-tps.patch
index bdf85bed3..5d4e6ce38 100644
--- a/patches/server/0062-Add-5-second-tps-average-in-tps.patch
+++ b/patches/server/0062-Add-5-second-tps-average-in-tps.patch
@@ -69,10 +69,10 @@ index 779d14840fdb0b27e1bb49e680c59539294b2995..96f50760dec9fb7ec317b500ce5cd6a6
lagging = recentTps[0] < org.purpurmc.purpur.PurpurConfig.laggingThreshold; // Purpur
tickSection = curTime;
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
-index e451348e26c024d48dfccbc6f182544baef7ff3f..102302a6884b8a8cf850f25983bb48205af298b8 100644
+index d7279047dfe353ce9f4312ac0fc53623fa8d7a55..10a63a0e67f061d68264a6ebe0209bb51bbef5fc 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
-@@ -2727,6 +2727,7 @@ public final class CraftServer implements Server {
+@@ -2708,6 +2708,7 @@ public final class CraftServer implements Server {
@Override
public double[] getTPS() {
return new double[] {
diff --git a/patches/server/0217-Added-the-ability-to-add-combustible-items.patch b/patches/server/0217-Added-the-ability-to-add-combustible-items.patch
index fdb38eda2..e5a9883ee 100644
--- a/patches/server/0217-Added-the-ability-to-add-combustible-items.patch
+++ b/patches/server/0217-Added-the-ability-to-add-combustible-items.patch
@@ -51,10 +51,10 @@ index bbff7466cecf50285c97fadaf68682a6c6ea879f..5ae858b81e6f9903b7296077cf497f62
private int maxStack = MAX_STACK;
public List transaction = new java.util.ArrayList();
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
-index 102302a6884b8a8cf850f25983bb48205af298b8..ab3e5b4bfd3968d9d758d35bdcebfd7662dbb8a8 100644
+index 10a63a0e67f061d68264a6ebe0209bb51bbef5fc..0fe1f103f1df37ecb879a6101dac2e9800623f6d 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
-@@ -1455,6 +1455,19 @@ public final class CraftServer implements Server {
+@@ -1436,6 +1436,19 @@ public final class CraftServer implements Server {
return true;
}
diff --git a/patches/server/0268-Spark-Profiler.patch b/patches/server/0268-Spark-Profiler.patch
index 0e2c84ee6..ec37a214b 100644
--- a/patches/server/0268-Spark-Profiler.patch
+++ b/patches/server/0268-Spark-Profiler.patch
@@ -3,54 +3,105 @@ From: Ben Kerllenevich
Date: Sat, 25 Jun 2022 19:40:36 -0400
Subject: [PATCH] Spark Profiler
+Co-authored-by: granny
-diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
-index cd1906a3dcb52de5deed02e1b0005fe83c91f211..97f357a6c8d600daf68b249e04da1f7008b14302 100644
---- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
-+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
-@@ -434,7 +434,44 @@ public final class CraftServer implements Server {
- if (!pluginFolder.exists()) {
- pluginFolder.mkdirs();
- }
-- Plugin[] plugins = this.pluginManager.loadPlugins(pluginFolder, this.extraPluginJars());
-+ // Purpur start
-+ List extraJars = this.extraPluginJars();
-+ if (!Boolean.getBoolean("Purpur.IReallyDontWantSpark")) {
-+ try {
-+ File file = new File("cache", "spark.jar");
-+ file.getParentFile().mkdirs();
+diff --git a/src/main/java/io/papermc/paper/plugin/provider/source/SparkProviderSource.java b/src/main/java/io/papermc/paper/plugin/provider/source/SparkProviderSource.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..74e3334ec92e3864b84e299b33ca995224eb7c3f
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/plugin/provider/source/SparkProviderSource.java
+@@ -0,0 +1,82 @@
++package io.papermc.paper.plugin.provider.source;
+
-+ boolean shouldDownload = true;
-+ if (file.exists()) {
-+ String fileSha1 = String.format("%040x", new java.math.BigInteger(1, java.security.MessageDigest.getInstance("SHA-1").digest(java.nio.file.Files.readAllBytes(file.toPath()))));
-+ String sparkSha1;
-+ java.net.URLConnection urlConnection = new java.net.URL("https://sparkapi.lucko.me/download/bukkit/sha1").openConnection();
-+ urlConnection.setReadTimeout(5000);
-+ urlConnection.setConnectTimeout(5000);
-+ try (java.io.BufferedReader reader = new java.io.BufferedReader(new InputStreamReader(urlConnection.getInputStream()))) {
-+ sparkSha1 = reader.lines().collect(Collectors.joining(""));
-+ }
++import com.mojang.logging.LogUtils;
++import io.papermc.paper.plugin.entrypoint.Entrypoint;
++import io.papermc.paper.plugin.entrypoint.EntrypointHandler;
++import io.papermc.paper.plugin.entrypoint.LaunchEntryPointHandler;
++import io.papermc.paper.plugin.provider.PluginProvider;
++import java.io.BufferedReader;
++import java.io.File;
++import java.io.InputStreamReader;
++import java.math.BigInteger;
++import java.net.URL;
++import java.net.URLConnection;
++import java.nio.file.Files;
++import java.nio.file.Path;
++import java.nio.file.StandardCopyOption;
++import java.security.MessageDigest;
++import java.util.stream.Collectors;
++import org.bukkit.plugin.java.JavaPlugin;
++import org.slf4j.Logger;
+
-+ if (fileSha1.equals(sparkSha1)) {
-+ shouldDownload = false;
-+ }
++public class SparkProviderSource extends FileProviderSource {
++ public static final SparkProviderSource INSTANCE = new SparkProviderSource();
++
++ private static final Logger LOGGER = LogUtils.getLogger();
++
++ public SparkProviderSource() {
++ super("File '%s' specified by Purpur"::formatted);
++ }
++
++ @Override
++ public void registerProviders(EntrypointHandler entrypointHandler, Path context) throws Exception {
++ if (!Boolean.getBoolean("Purpur.IReallyDontWantSpark")) {
++ try {
++ File file = context.toFile();
++ file.getParentFile().mkdirs();
++
++ boolean shouldDownload = true;
++ if (file.exists()) {
++ String fileSha1 = String.format("%040x", new BigInteger(1, MessageDigest.getInstance("SHA-1").digest(Files.readAllBytes(file.toPath()))));
++ String sparkSha1;
++ URLConnection urlConnection = new URL("https://sparkapi.lucko.me/download/bukkit/sha1").openConnection();
++ urlConnection.setReadTimeout(5000);
++ urlConnection.setConnectTimeout(5000);
++ try (BufferedReader reader = new BufferedReader(new InputStreamReader(urlConnection.getInputStream()))) {
++ sparkSha1 = reader.lines().collect(Collectors.joining(""));
+ }
+
-+ if (shouldDownload) {
-+ java.net.URLConnection urlConnection = new java.net.URL("https://sparkapi.lucko.me/download/bukkit").openConnection();
-+ urlConnection.setReadTimeout(5000);
-+ urlConnection.setConnectTimeout(5000);
-+ java.nio.file.Files.copy(urlConnection.getInputStream(), file.toPath(), java.nio.file.StandardCopyOption.REPLACE_EXISTING);
++ if (fileSha1.equals(sparkSha1)) {
++ shouldDownload = false;
+ }
-+
-+ extraJars.add(file);
-+ } catch (Exception e) {
-+ getLogger().severe("Purpur: Failed to download and install spark plugin");
-+ e.printStackTrace();
+ }
++
++ if (shouldDownload) {
++ URLConnection urlConnection = new URL("https://sparkapi.lucko.me/download/bukkit").openConnection();
++ urlConnection.setReadTimeout(5000);
++ urlConnection.setConnectTimeout(5000);
++ Files.copy(urlConnection.getInputStream(), file.toPath(), StandardCopyOption.REPLACE_EXISTING);
++ }
++
++ if (hasSpark()) {
++ LOGGER.info("Purpur: Using user-provided spark plugin instead of our own.");
++ } else {
++ super.registerProviders(entrypointHandler, context);
++ }
++
++ } catch (Exception e) {
++ LOGGER.error("Purpur: Failed to download and install spark plugin");
++ e.printStackTrace();
+ }
-+ Plugin[] plugins = this.pluginManager.loadPlugins(pluginFolder, extraJars);
-+ // Purpur end
++ }
++ }
++
++ private static boolean hasSpark() {
++ for (PluginProvider provider : LaunchEntryPointHandler.INSTANCE.get(Entrypoint.PLUGIN).getRegisteredProviders()) {
++ if (provider.getMeta().getName().equalsIgnoreCase("spark")) {
++ return true;
++ }
++ }
++ return false;
++ }
++}
+diff --git a/src/main/java/net/minecraft/server/Main.java b/src/main/java/net/minecraft/server/Main.java
+index a821cb33fbc29109aec68f9d6a0eb2efc121ee13..066b18444e92508908dd823c8d928721e338d06d 100644
+--- a/src/main/java/net/minecraft/server/Main.java
++++ b/src/main/java/net/minecraft/server/Main.java
+@@ -122,6 +122,7 @@ public class Main {
+ // Register plugins from the flag
+ io.papermc.paper.plugin.util.EntrypointUtil.registerProvidersFromSource(io.papermc.paper.plugin.provider.source.PluginFlagProviderSource.INSTANCE, files);
// Paper end
- for (Plugin plugin : plugins) {
- try {
++ io.papermc.paper.plugin.util.EntrypointUtil.registerProvidersFromSource(io.papermc.paper.plugin.provider.source.SparkProviderSource.INSTANCE, new File("cache", "spark.jar").toPath()); // Purpur
+ Bootstrap.bootStrap();
+ Bootstrap.validate();
+ Util.startTimerHackThread();
diff --git a/patches/server/0282-Debug-Marker-API.patch b/patches/server/0282-Debug-Marker-API.patch
index e6edcc8da..ab2243a4d 100644
--- a/patches/server/0282-Debug-Marker-API.patch
+++ b/patches/server/0282-Debug-Marker-API.patch
@@ -5,10 +5,10 @@ Subject: [PATCH] Debug Marker API
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
-index 39ca67f8b6b81b807f14e12137f4cb28d81bc5d4..799fdf506d57a729ccbb28da70678ccb1e02d152 100644
+index 0fe1f103f1df37ecb879a6101dac2e9800623f6d..2bdabca25c75cbee0f53c1d50a07202bac9e1550 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
-@@ -1503,6 +1503,42 @@ public final class CraftServer implements Server {
+@@ -1447,6 +1447,42 @@ public final class CraftServer implements Server {
public void removeFuel(org.bukkit.Material material) {
net.minecraft.world.level.block.entity.AbstractFurnaceBlockEntity.removeFuel(net.minecraft.world.item.ItemStack.fromBukkitCopy(new ItemStack(material)));
}
diff --git a/patches/server/0295-Language-API.patch b/patches/server/0295-Language-API.patch
index 0c8da25b9..79b1c3e82 100644
--- a/patches/server/0295-Language-API.patch
+++ b/patches/server/0295-Language-API.patch
@@ -5,10 +5,10 @@ Subject: [PATCH] Language API
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
-index 72d9726ac8d06502f6123aea039e026ad6529661..c32e32005968b46d1f7d5162ab15c61e36f398f5 100644
+index 2bdabca25c75cbee0f53c1d50a07202bac9e1550..e5b5510806d183944f0ac926604e86f3b3dc1d7a 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
-@@ -321,6 +321,20 @@ public final class CraftServer implements Server {
+@@ -322,6 +322,20 @@ public final class CraftServer implements Server {
Bukkit.setServer(this);
io.papermc.paper.world.structure.PaperConfiguredStructure.init(); // Paper
diff --git a/patches/server/0310-Improve-output-of-plugins-command.patch b/patches/server/0310-Improve-output-of-plugins-command.patch
new file mode 100644
index 000000000..17f0dba60
--- /dev/null
+++ b/patches/server/0310-Improve-output-of-plugins-command.patch
@@ -0,0 +1,123 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Parker Hawke
+Date: Sat, 27 Jun 2020 18:43:37 -0400
+Subject: [PATCH] Improve output of plugins command
+
+Co-authored-by: Oharass
+Co-authored-by: granny
+
+diff --git a/src/main/java/io/papermc/paper/command/PaperPluginsCommand.java b/src/main/java/io/papermc/paper/command/PaperPluginsCommand.java
+index 72096a66a4046633de73a12f5a043ac6dff169b1..a0200653bc7919a974e3ee6260edfb7cf4c221b4 100644
+--- a/src/main/java/io/papermc/paper/command/PaperPluginsCommand.java
++++ b/src/main/java/io/papermc/paper/command/PaperPluginsCommand.java
+@@ -78,10 +78,10 @@ public class PaperPluginsCommand extends BukkitCommand {
+ this.setAliases(Arrays.asList("pl"));
+ }
+
+- private static List formatProviders(TreeMap> plugins) {
++ private static List formatProviders(TreeMap> plugins, @NotNull CommandSender sender) { // Purpur
+ List components = new ArrayList<>(plugins.size());
+ for (PluginProvider entry : plugins.values()) {
+- components.add(formatProvider(entry));
++ components.add(formatProvider(entry, sender)); // Purpur
+ }
+
+ boolean isFirst = true;
+@@ -109,7 +109,7 @@ public class PaperPluginsCommand extends BukkitCommand {
+ return formattedSublists;
+ }
+
+- private static Component formatProvider(PluginProvider> provider) {
++ private static Component formatProvider(PluginProvider> provider, @NotNull CommandSender sender) { // Purpur
+ TextComponent.Builder builder = Component.text();
+ if (provider instanceof SpigotPluginProvider spigotPluginProvider && CraftMagicNumbers.isLegacy(spigotPluginProvider.getMeta())) {
+ builder.append(LEGACY_PLUGIN_STAR);
+@@ -117,12 +117,64 @@ public class PaperPluginsCommand extends BukkitCommand {
+
+ String name = provider.getMeta().getName();
+ Component pluginName = Component.text(name, fromStatus(provider))
+- .clickEvent(ClickEvent.runCommand("/version " + name));
++ // Purpur start
++ .clickEvent(ClickEvent.suggestCommand("/version " + name));
++
++ if (sender instanceof org.bukkit.entity.Player && sender.hasPermission("bukkit.command.version")) {
++ // Event components
++ String description = provider.getMeta().getDescription();
++ TextComponent.Builder hover = Component.text();
++ hover.append(Component.text("Version: ", NamedTextColor.WHITE)).append(Component.text(provider.getMeta().getVersion(), NamedTextColor.GREEN));
++
++ if (description != null) {
++ hover.append(Component.newline())
++ .append(Component.text("Description: ", NamedTextColor.WHITE))
++ .append(Component.text(description, NamedTextColor.GREEN));
++ }
++
++ if (provider.getMeta().getWebsite() != null) {
++ hover.append(Component.newline())
++ .append(Component.text("Website: ", NamedTextColor.WHITE))
++ .append(Component.text(provider.getMeta().getWebsite(), NamedTextColor.GREEN));
++ }
++
++ if (!provider.getMeta().getAuthors().isEmpty()) {
++ hover.append(Component.newline());
++ if (provider.getMeta().getAuthors().size() == 1) {
++ hover.append(Component.text("Author: "));
++ } else {
++ hover.append(Component.text("Authors: "));
++ }
++
++ hover.append(getAuthors(provider.getMeta()));
++ }
++
++ pluginName.hoverEvent(hover.build());
++ }
+
+ builder.append(pluginName);
++ // Purpur end
++
++ return builder.build();
++ }
++
++ // Purpur start
++ @NotNull
++ private static TextComponent getAuthors(@NotNull final PluginMeta pluginMeta) {
++ TextComponent.Builder builder = Component.text();
++ List authors = pluginMeta.getAuthors();
++
++ for (int i = 0; i < authors.size(); i++) {
++ if (i > 0) {
++ builder.append(Component.text(i < authors.size() - 1 ? ", " : " and ", NamedTextColor.WHITE));
++ }
++
++ builder.append(Component.text(authors.get(i), NamedTextColor.GREEN));
++ }
+
+ return builder.build();
+ }
++ // Purpur end
+
+ private static Component asPlainComponents(String strings) {
+ net.kyori.adventure.text.TextComponent.Builder builder = Component.text();
+@@ -182,16 +234,16 @@ public class PaperPluginsCommand extends BukkitCommand {
+ }
+ }
+
+- Component infoMessage = Component.text("Server Plugins (%s):".formatted(paperPlugins.size() + spigotPlugins.size()), NamedTextColor.WHITE);
++ //Component infoMessage = Component.text("Server Plugins (%s):".formatted(paperPlugins.size() + spigotPlugins.size()), NamedTextColor.WHITE);
+ //.append(INFO_ICON_START.hoverEvent(SERVER_PLUGIN_INFO)); TODO: Add docs
+
+- sender.sendMessage(infoMessage);
+- sender.sendMessage(PAPER_HEADER);
+- for (Component component : formatProviders(paperPlugins)) {
++ //sender.sendMessage(infoMessage); // Purpur
++ sender.sendMessage(PAPER_HEADER.append(Component.text(" (%s):".formatted(paperPlugins.size())))); // Purpur
++ for (Component component : formatProviders(paperPlugins, sender)) { // Purpur
+ sender.sendMessage(component);
+ }
+- sender.sendMessage(BUKKIT_HEADER);
+- for (Component component : formatProviders(spigotPlugins)) {
++ sender.sendMessage(BUKKIT_HEADER.append(Component.text(" (%s):".formatted(spigotPlugins.size())))); // Purpur
++ for (Component component : formatProviders(spigotPlugins, sender)) { // Purpur
+ sender.sendMessage(component);
+ }
+