diff --git a/README.md b/README.md index 3320bb4af..d61e758d6 100644 --- a/README.md +++ b/README.md @@ -64,7 +64,7 @@ Maven org.purpurmc.purpur purpur-api - 1.18.2-R0.1-SNAPSHOT + 1.19-R0.1-SNAPSHOT provided ``` @@ -77,7 +77,7 @@ repositories { ``` ```kotlin dependencies { - compileOnly("org.purpurmc.purpur", "purpur-api", "1.18.2-R0.1-SNAPSHOT") + compileOnly("org.purpurmc.purpur", "purpur-api", "1.19-R0.1-SNAPSHOT") } ``` diff --git a/build.gradle.kts b/build.gradle.kts index 3794af36b..a1f93f1da 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -18,7 +18,7 @@ repositories { dependencies { remapper("net.fabricmc:tiny-remapper:0.8.2:fat") - decompiler("net.minecraftforge:forgeflower:1.5.498.29") + decompiler("net.minecraftforge:forgeflower:1.5.605.7") paperclip("io.papermc:paperclip:3.0.2") } diff --git a/gradle.properties b/gradle.properties index 0e3e9476a..36dfbe297 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,7 +1,7 @@ group = org.purpurmc.purpur -version = 1.18.2-R0.1-SNAPSHOT +version = 1.19-R0.1-SNAPSHOT -paperCommit = 276d830d223ddf68611beacc248285ae5a4e8a1f +paperCommit = a9c507310b02836e37d5d1e3ed9ba9620289fdb1 org.gradle.caching = true org.gradle.parallel = true diff --git a/patches/api/0002-Add-pufferfish-added-classes-to-junit-exemptions.patch b/patches/api/0001-Add-pufferfish-added-classes-to-junit-exemptions.patch similarity index 100% rename from patches/api/0002-Add-pufferfish-added-classes-to-junit-exemptions.patch rename to patches/api/0001-Add-pufferfish-added-classes-to-junit-exemptions.patch diff --git a/patches/api/0001-Pufferfish-API-Changes.patch b/patches/api/0001-Pufferfish-API-Changes.patch deleted file mode 100644 index 1473bd727..000000000 --- a/patches/api/0001-Pufferfish-API-Changes.patch +++ /dev/null @@ -1,514 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Kevin Raneri -Date: Tue, 9 Nov 2021 14:01:56 -0500 -Subject: [PATCH] Pufferfish API Changes - -Pufferfish -Copyright (C) 2022 Pufferfish Studios LLC - -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/build.gradle.kts b/build.gradle.kts -index 01798255d45f2a642df00156f11dd2bcd8108079..9d7bd0f965c7dc3a60246310688aa5f93a4594a4 100644 ---- a/build.gradle.kts -+++ b/build.gradle.kts -@@ -41,6 +41,7 @@ dependencies { - apiAndDocs("net.kyori:adventure-text-serializer-plain") - api("org.apache.logging.log4j:log4j-api:2.17.1") - api("org.slf4j:slf4j-api:1.8.0-beta4") -+ api("io.sentry:sentry:5.4.0") // Pufferfish - - implementation("org.ow2.asm:asm:9.2") - implementation("org.ow2.asm:asm-commons:9.2") -@@ -82,6 +83,13 @@ val generateApiVersioningFile by tasks.registering { - } - } - -+// Pufferfish Start -+tasks.withType { -+ val compilerArgs = options.compilerArgs -+ compilerArgs.add("--add-modules=jdk.incubator.vector") -+} -+// Pufferfish End -+ - tasks.jar { - from(generateApiVersioningFile.map { it.outputs.files.singleFile }) { - into("META-INF/maven/${project.group}/${project.name}") -diff --git a/src/main/java/gg/pufferfish/pufferfish/sentry/SentryContext.java b/src/main/java/gg/pufferfish/pufferfish/sentry/SentryContext.java -new file mode 100644 -index 0000000000000000000000000000000000000000..10310fdd53de28efb8a8250f6d3b0c8eb08fb68a ---- /dev/null -+++ b/src/main/java/gg/pufferfish/pufferfish/sentry/SentryContext.java -@@ -0,0 +1,161 @@ -+package gg.pufferfish.pufferfish.sentry; -+ -+import com.google.gson.Gson; -+import java.lang.reflect.Field; -+import java.lang.reflect.Modifier; -+import java.util.Map; -+import java.util.TreeMap; -+import org.apache.logging.log4j.ThreadContext; -+import org.bukkit.command.Command; -+import org.bukkit.command.CommandSender; -+import org.bukkit.entity.Player; -+import org.bukkit.event.Event; -+import org.bukkit.event.player.PlayerEvent; -+import org.bukkit.plugin.Plugin; -+import org.bukkit.plugin.RegisteredListener; -+import org.jetbrains.annotations.Nullable; -+ -+public class SentryContext { -+ -+ private static final Gson GSON = new Gson(); -+ -+ public static void setPluginContext(@Nullable Plugin plugin) { -+ if (plugin != null) { -+ ThreadContext.put("pufferfishsentry_pluginname", plugin.getName()); -+ ThreadContext.put("pufferfishsentry_pluginversion", plugin.getDescription().getVersion()); -+ } -+ } -+ -+ public static void removePluginContext() { -+ ThreadContext.remove("pufferfishsentry_pluginname"); -+ ThreadContext.remove("pufferfishsentry_pluginversion"); -+ } -+ -+ public static void setSenderContext(@Nullable CommandSender sender) { -+ if (sender != null) { -+ ThreadContext.put("pufferfishsentry_playername", sender.getName()); -+ if (sender instanceof Player player) { -+ ThreadContext.put("pufferfishsentry_playerid", player.getUniqueId().toString()); -+ } -+ } -+ } -+ -+ public static void removeSenderContext() { -+ ThreadContext.remove("pufferfishsentry_playername"); -+ ThreadContext.remove("pufferfishsentry_playerid"); -+ } -+ -+ public static void setEventContext(Event event, RegisteredListener registration) { -+ setPluginContext(registration.getPlugin()); -+ -+ try { -+ // Find the player that was involved with this event -+ Player player = null; -+ if (event instanceof PlayerEvent) { -+ player = ((PlayerEvent) event).getPlayer(); -+ } else { -+ Class eventClass = event.getClass(); -+ -+ Field playerField = null; -+ -+ for (Field field : eventClass.getDeclaredFields()) { -+ if (field.getType().equals(Player.class)) { -+ playerField = field; -+ break; -+ } -+ } -+ -+ if (playerField != null) { -+ playerField.setAccessible(true); -+ player = (Player) playerField.get(event); -+ } -+ } -+ -+ if (player != null) { -+ setSenderContext(player); -+ } -+ } catch (Exception e) {} // We can't really safely log exceptions. -+ -+ ThreadContext.put("pufferfishsentry_eventdata", GSON.toJson(serializeFields(event))); -+ } -+ -+ public static void removeEventContext() { -+ removePluginContext(); -+ removeSenderContext(); -+ ThreadContext.remove("pufferfishsentry_eventdata"); -+ } -+ -+ private static Map serializeFields(Object object) { -+ Map fields = new TreeMap<>(); -+ fields.put("_class", object.getClass().getName()); -+ for (Field declaredField : object.getClass().getDeclaredFields()) { -+ try { -+ if (Modifier.isStatic(declaredField.getModifiers())) { -+ continue; -+ } -+ -+ String fieldName = declaredField.getName(); -+ if (fieldName.equals("handlers")) { -+ continue; -+ } -+ declaredField.setAccessible(true); -+ Object value = declaredField.get(object); -+ if (value != null) { -+ fields.put(fieldName, value.toString()); -+ } else { -+ fields.put(fieldName, ""); -+ } -+ } catch (Exception e) {} // We can't really safely log exceptions. -+ } -+ return fields; -+ } -+ -+ public static class State { -+ -+ private Plugin plugin; -+ private Command command; -+ private String commandLine; -+ private Event event; -+ private RegisteredListener registeredListener; -+ -+ public Plugin getPlugin() { -+ return plugin; -+ } -+ -+ public void setPlugin(Plugin plugin) { -+ this.plugin = plugin; -+ } -+ -+ public Command getCommand() { -+ return command; -+ } -+ -+ public void setCommand(Command command) { -+ this.command = command; -+ } -+ -+ public String getCommandLine() { -+ return commandLine; -+ } -+ -+ public void setCommandLine(String commandLine) { -+ this.commandLine = commandLine; -+ } -+ -+ public Event getEvent() { -+ return event; -+ } -+ -+ public void setEvent(Event event) { -+ this.event = event; -+ } -+ -+ public RegisteredListener getRegisteredListener() { -+ return registeredListener; -+ } -+ -+ public void setRegisteredListener(RegisteredListener registeredListener) { -+ this.registeredListener = registeredListener; -+ } -+ } -+} -diff --git a/src/main/java/gg/pufferfish/pufferfish/simd/SIMDChecker.java b/src/main/java/gg/pufferfish/pufferfish/simd/SIMDChecker.java -new file mode 100644 -index 0000000000000000000000000000000000000000..93f5d7ca36e043e6c0f959450d38e6946b348eaf ---- /dev/null -+++ b/src/main/java/gg/pufferfish/pufferfish/simd/SIMDChecker.java -@@ -0,0 +1,40 @@ -+package gg.pufferfish.pufferfish.simd; -+ -+import java.util.logging.Level; -+import java.util.logging.Logger; -+import jdk.incubator.vector.FloatVector; -+import jdk.incubator.vector.IntVector; -+import jdk.incubator.vector.VectorSpecies; -+ -+/** -+ * Basically, java is annoying and we have to push this out to its own class. -+ */ -+@Deprecated -+public class SIMDChecker { -+ -+ @Deprecated -+ public static boolean canEnable(Logger logger) { -+ try { -+ if (SIMDDetection.getJavaVersion() != 17 && SIMDDetection.getJavaVersion() != 18) { -+ return false; -+ } else { -+ SIMDDetection.testRun = true; -+ -+ VectorSpecies ISPEC = IntVector.SPECIES_PREFERRED; -+ VectorSpecies FSPEC = FloatVector.SPECIES_PREFERRED; -+ -+ logger.log(Level.INFO, "Max SIMD vector size on this system is " + ISPEC.vectorBitSize() + " bits (int)"); -+ logger.log(Level.INFO, "Max SIMD vector size on this system is " + FSPEC.vectorBitSize() + " bits (float)"); -+ -+ if (ISPEC.elementSize() < 2 || FSPEC.elementSize() < 2) { -+ logger.log(Level.WARNING, "SIMD is not properly supported on this system!"); -+ return false; -+ } -+ -+ return true; -+ } -+ } catch (NoClassDefFoundError | Exception ignored) {} // Basically, we don't do anything. This lets us detect if it's not functional and disable it. -+ return false; -+ } -+ -+} -diff --git a/src/main/java/gg/pufferfish/pufferfish/simd/SIMDDetection.java b/src/main/java/gg/pufferfish/pufferfish/simd/SIMDDetection.java -new file mode 100644 -index 0000000000000000000000000000000000000000..a84889d3e9cfc4d7ab5f867820a6484c6070711b ---- /dev/null -+++ b/src/main/java/gg/pufferfish/pufferfish/simd/SIMDDetection.java -@@ -0,0 +1,35 @@ -+package gg.pufferfish.pufferfish.simd; -+ -+import java.util.logging.Logger; -+ -+@Deprecated -+public class SIMDDetection { -+ -+ public static boolean isEnabled = false; -+ public static boolean versionLimited = false; -+ public static boolean testRun = false; -+ -+ @Deprecated -+ public static boolean canEnable(Logger logger) { -+ try { -+ return SIMDChecker.canEnable(logger); -+ } catch (NoClassDefFoundError | Exception ignored) { -+ return false; -+ } -+ } -+ -+ @Deprecated -+ public static int getJavaVersion() { -+ // https://stackoverflow.com/a/2591122 -+ String version = System.getProperty("java.version"); -+ if(version.startsWith("1.")) { -+ version = version.substring(2, 3); -+ } else { -+ int dot = version.indexOf("."); -+ if(dot != -1) { version = version.substring(0, dot); } -+ } -+ version = version.split("-")[0]; // Azul is stupid -+ return Integer.parseInt(version); -+ } -+ -+} -diff --git a/src/main/java/gg/pufferfish/pufferfish/simd/VectorMapPalette.java b/src/main/java/gg/pufferfish/pufferfish/simd/VectorMapPalette.java -new file mode 100644 -index 0000000000000000000000000000000000000000..ae2464920c9412ac90b819a540ee58be0741465f ---- /dev/null -+++ b/src/main/java/gg/pufferfish/pufferfish/simd/VectorMapPalette.java -@@ -0,0 +1,83 @@ -+package gg.pufferfish.pufferfish.simd; -+ -+import java.awt.Color; -+import jdk.incubator.vector.FloatVector; -+import jdk.incubator.vector.IntVector; -+import jdk.incubator.vector.VectorMask; -+import jdk.incubator.vector.VectorSpecies; -+import org.bukkit.map.MapPalette; -+ -+@Deprecated -+public class VectorMapPalette { -+ -+ private static final VectorSpecies I_SPEC = IntVector.SPECIES_PREFERRED; -+ private static final VectorSpecies F_SPEC = FloatVector.SPECIES_PREFERRED; -+ -+ @Deprecated -+ public static void matchColorVectorized(int[] in, byte[] out) { -+ int speciesLength = I_SPEC.length(); -+ int i; -+ for (i = 0; i < in.length - speciesLength; i += speciesLength) { -+ float[] redsArr = new float[speciesLength]; -+ float[] bluesArr = new float[speciesLength]; -+ float[] greensArr = new float[speciesLength]; -+ int[] alphasArr = new int[speciesLength]; -+ -+ for (int j = 0; j < speciesLength; j++) { -+ alphasArr[j] = (in[i + j] >> 24) & 0xFF; -+ redsArr[j] = (in[i + j] >> 16) & 0xFF; -+ greensArr[j] = (in[i + j] >> 8) & 0xFF; -+ bluesArr[j] = (in[i + j] >> 0) & 0xFF; -+ } -+ -+ IntVector alphas = IntVector.fromArray(I_SPEC, alphasArr, 0); -+ FloatVector reds = FloatVector.fromArray(F_SPEC, redsArr, 0); -+ FloatVector greens = FloatVector.fromArray(F_SPEC, greensArr, 0); -+ FloatVector blues = FloatVector.fromArray(F_SPEC, bluesArr, 0); -+ IntVector resultIndex = IntVector.zero(I_SPEC); -+ VectorMask modificationMask = VectorMask.fromLong(I_SPEC, 0xffffffff); -+ -+ modificationMask = modificationMask.and(alphas.lt(128).not()); -+ FloatVector bestDistances = FloatVector.broadcast(F_SPEC, Float.MAX_VALUE); -+ -+ for (int c = 4; c < MapPalette.colors.length; c++) { -+ // We're using 32-bit floats here because it's 2x faster and nobody will know the difference. -+ // For correctness, the original algorithm uses 64-bit floats instead. Completely unnecessary. -+ FloatVector compReds = FloatVector.broadcast(F_SPEC, MapPalette.colors[c].getRed()); -+ FloatVector compGreens = FloatVector.broadcast(F_SPEC, MapPalette.colors[c].getGreen()); -+ FloatVector compBlues = FloatVector.broadcast(F_SPEC, MapPalette.colors[c].getBlue()); -+ -+ FloatVector rMean = reds.add(compReds).div(2.0f); -+ FloatVector rDiff = reds.sub(compReds); -+ FloatVector gDiff = greens.sub(compGreens); -+ FloatVector bDiff = blues.sub(compBlues); -+ -+ FloatVector weightR = rMean.div(256.0f).add(2); -+ FloatVector weightG = FloatVector.broadcast(F_SPEC, 4.0f); -+ FloatVector weightB = FloatVector.broadcast(F_SPEC, 255.0f).sub(rMean).div(256.0f).add(2.0f); -+ -+ FloatVector distance = weightR.mul(rDiff).mul(rDiff).add(weightG.mul(gDiff).mul(gDiff)).add(weightB.mul(bDiff).mul(bDiff)); -+ -+ // Now we compare to the best distance we've found. -+ // This mask contains a "1" if better, and a "0" otherwise. -+ VectorMask bestDistanceMask = distance.lt(bestDistances); -+ bestDistances = bestDistances.blend(distance, bestDistanceMask); // Update the best distances -+ -+ // Update the result array -+ // We also AND with the modification mask because we don't want to interfere if the alpha value isn't large enough. -+ resultIndex = resultIndex.blend(c, bestDistanceMask.cast(I_SPEC).and(modificationMask)); // Update the results -+ } -+ -+ for (int j = 0; j < speciesLength; j++) { -+ int index = resultIndex.lane(j); -+ out[i + j] = (byte) (index < 128 ? index : -129 + (index - 127)); -+ } -+ } -+ -+ // For the final ones, fall back to the regular method -+ for (; i < in.length; i++) { -+ out[i] = MapPalette.matchColor(new Color(in[i], true)); -+ } -+ } -+ -+} -diff --git a/src/main/java/org/bukkit/map/MapPalette.java b/src/main/java/org/bukkit/map/MapPalette.java -index b937441d2fb46b108644c49fcf073859765aa02e..d95b01bfd0657cf089c0f5412453cca08e36c02f 100644 ---- a/src/main/java/org/bukkit/map/MapPalette.java -+++ b/src/main/java/org/bukkit/map/MapPalette.java -@@ -1,5 +1,6 @@ - package org.bukkit.map; - -+import gg.pufferfish.pufferfish.simd.SIMDDetection; - import java.awt.Color; - import java.awt.Graphics2D; - import java.awt.Image; -@@ -34,7 +35,7 @@ public final class MapPalette { - } - - @NotNull -- static final Color[] colors = { -+ public static final Color[] colors = { // Pufferfish - public access - c(0, 0, 0), c(0, 0, 0), c(0, 0, 0), c(0, 0, 0), - c(89, 125, 39), c(109, 153, 48), c(127, 178, 56), c(67, 94, 29), - c(174, 164, 115), c(213, 201, 140), c(247, 233, 163), c(130, 123, 86), -@@ -205,9 +206,15 @@ public final class MapPalette { - temp.getRGB(0, 0, temp.getWidth(), temp.getHeight(), pixels, 0, temp.getWidth()); - - byte[] result = new byte[temp.getWidth() * temp.getHeight()]; -+ // Pufferfish start -+ if (!SIMDDetection.isEnabled) { - for (int i = 0; i < pixels.length; i++) { - result[i] = matchColor(new Color(pixels[i], true)); - } -+ } else { -+ gg.pufferfish.pufferfish.simd.VectorMapPalette.matchColorVectorized(pixels, result); -+ } -+ // Pufferfish end - return result; - } - -diff --git a/src/main/java/org/bukkit/plugin/SimplePluginManager.java b/src/main/java/org/bukkit/plugin/SimplePluginManager.java -index bab8bb3a52cdeef5f7052d4e3f404c42f37d117d..dba9041784e7d3051b5248cbc24e4879e60103c1 100644 ---- a/src/main/java/org/bukkit/plugin/SimplePluginManager.java -+++ b/src/main/java/org/bukkit/plugin/SimplePluginManager.java -@@ -622,7 +622,9 @@ public final class SimplePluginManager implements PluginManager { - - // Paper start - private void handlePluginException(String msg, Throwable ex, Plugin plugin) { -+ gg.pufferfish.pufferfish.sentry.SentryContext.setPluginContext(plugin); // Pufferfish - server.getLogger().log(Level.SEVERE, msg, ex); -+ gg.pufferfish.pufferfish.sentry.SentryContext.removePluginContext(); // Pufferfish - callEvent(new ServerExceptionEvent(new ServerPluginEnableDisableException(msg, ex, plugin))); - } - // Paper end -@@ -681,9 +683,11 @@ public final class SimplePluginManager implements PluginManager { - )); - } - } catch (Throwable ex) { -+ gg.pufferfish.pufferfish.sentry.SentryContext.setEventContext(event, registration); // Pufferfish - // Paper start - error reporting - String msg = "Could not pass event " + event.getEventName() + " to " + registration.getPlugin().getDescription().getFullName(); - server.getLogger().log(Level.SEVERE, msg, ex); -+ gg.pufferfish.pufferfish.sentry.SentryContext.removeEventContext(); // Pufferfish - if (!(event instanceof ServerExceptionEvent)) { // We don't want to cause an endless event loop - callEvent(new ServerExceptionEvent(new 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 c8b11793c6a3baabc1c9566e0463ab1d6e293827..2b9218ddd262e89180588c3014dad328317dd8db 100644 ---- a/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java -+++ b/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java -@@ -369,7 +369,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; -@@ -398,7 +400,9 @@ public final class JavaPluginLoader implements PluginLoader { - try { - jPlugin.setEnabled(false); - } catch (Throwable ex) { -+ gg.pufferfish.pufferfish.sentry.SentryContext.setPluginContext(plugin); // Pufferfish - server.getLogger().log(Level.SEVERE, "Error occurred while disabling " + plugin.getDescription().getFullName() + " (Is it up to date?)", ex); -+ gg.pufferfish.pufferfish.sentry.SentryContext.removePluginContext(); // Pufferfish - } - - 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 9938ebb38353f4aa2adf1bb08cd1c347ddd9fc88..dc76cdbe93a0229a8ff552e4048613e3d8e050ce 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 - -+ private boolean closed = false; // Pufferfish -+ - static { - ClassLoader.registerAsParallelCapable(); - } -@@ -151,6 +153,7 @@ public final class PluginClassLoader extends URLClassLoader { // Spigot - throw new ClassNotFoundException(name); - } - -+ public boolean _airplane_hasClass(@NotNull String name) { return this.classes.containsKey(name); } // Pufferfish - @Override - protected Class findClass(String name) throws ClassNotFoundException { - if (name.startsWith("org.bukkit.") || name.startsWith("net.minecraft.")) { -@@ -158,7 +161,7 @@ public final class PluginClassLoader extends URLClassLoader { // Spigot - } - Class result = classes.get(name); - -- if (result == null) { -+ if (result == null && !this.closed) { // Pufferfish - String path = name.replace('.', '/').concat(".class"); - JarEntry entry = jar.getJarEntry(path); - -@@ -213,6 +216,7 @@ public final class PluginClassLoader extends URLClassLoader { // Spigot - try { - super.close(); - } finally { -+ this.closed = true; // Pufferfish - jar.close(); - } - } diff --git a/patches/api/0003-Build-System-Changes.patch b/patches/api/0002-Build-System-Changes.patch similarity index 85% rename from patches/api/0003-Build-System-Changes.patch rename to patches/api/0002-Build-System-Changes.patch index 44d5b02e9..d79b12a71 100644 --- a/patches/api/0003-Build-System-Changes.patch +++ b/patches/api/0002-Build-System-Changes.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Build System Changes diff --git a/build.gradle.kts b/build.gradle.kts -index 0007c6b1d81373cb6592c8b9e02f464405050f68..30d3086fae6aaac493a3ad536447911ef5b2c1b1 100644 +index ac0a4aea48436201b5712b166413bb7dc0d0b9d8..6326d99ed2fb157da200bb53d67a639a2c3d6cc1 100644 --- a/build.gradle.kts +++ b/build.gradle.kts -@@ -102,6 +102,8 @@ tasks.jar { +@@ -93,6 +93,8 @@ tasks.jar { } tasks.withType { diff --git a/patches/api/0004-Purpur-config-files.patch b/patches/api/0003-Purpur-config-files.patch similarity index 100% rename from patches/api/0004-Purpur-config-files.patch rename to patches/api/0003-Purpur-config-files.patch diff --git a/patches/api/0005-Purpur-client-support.patch b/patches/api/0004-Purpur-client-support.patch similarity index 100% rename from patches/api/0005-Purpur-client-support.patch rename to patches/api/0004-Purpur-client-support.patch diff --git a/patches/api/0006-Default-permissions.patch b/patches/api/0005-Default-permissions.patch similarity index 100% rename from patches/api/0006-Default-permissions.patch rename to patches/api/0005-Default-permissions.patch diff --git a/patches/api/0007-Ridables.patch b/patches/api/0006-Ridables.patch similarity index 100% rename from patches/api/0007-Ridables.patch rename to patches/api/0006-Ridables.patch diff --git a/patches/api/0008-Allow-inventory-resizing.patch b/patches/api/0007-Allow-inventory-resizing.patch similarity index 100% rename from patches/api/0008-Allow-inventory-resizing.patch rename to patches/api/0007-Allow-inventory-resizing.patch diff --git a/patches/api/0009-Llama-API.patch b/patches/api/0008-Llama-API.patch similarity index 100% rename from patches/api/0009-Llama-API.patch rename to patches/api/0008-Llama-API.patch diff --git a/patches/api/0010-AFK-API.patch b/patches/api/0009-AFK-API.patch similarity index 100% rename from patches/api/0010-AFK-API.patch rename to patches/api/0009-AFK-API.patch diff --git a/patches/api/0011-Bring-back-server-name.patch b/patches/api/0010-Bring-back-server-name.patch similarity index 93% rename from patches/api/0011-Bring-back-server-name.patch rename to patches/api/0010-Bring-back-server-name.patch index bef4594d7..400583e20 100644 --- a/patches/api/0011-Bring-back-server-name.patch +++ b/patches/api/0010-Bring-back-server-name.patch @@ -5,7 +5,7 @@ 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 d8666481f9a407403d0114ff02024fd3c50c27c4..a1374b608c20d47fde15135089bf8aceb98d6129 100644 +index a87399fa4838d4b2c1ff9cc35d433ae76cc149bf..2d9a065aa8c6a835e49eee76acdf8cfa6af420bf 100644 --- a/src/main/java/org/bukkit/Bukkit.java +++ b/src/main/java/org/bukkit/Bukkit.java @@ -2368,4 +2368,15 @@ public final class Bukkit { diff --git a/patches/api/0012-ExecuteCommandEvent.patch b/patches/api/0011-ExecuteCommandEvent.patch similarity index 92% rename from patches/api/0012-ExecuteCommandEvent.patch rename to patches/api/0011-ExecuteCommandEvent.patch index 8b83d17e9..741382c89 100644 --- a/patches/api/0012-ExecuteCommandEvent.patch +++ b/patches/api/0011-ExecuteCommandEvent.patch @@ -5,10 +5,10 @@ Subject: [PATCH] ExecuteCommandEvent diff --git a/src/main/java/org/bukkit/command/SimpleCommandMap.java b/src/main/java/org/bukkit/command/SimpleCommandMap.java -index 74252236b138969560e6513f24e7ecc6dc4a4127..9c3d02b949fe806e17200d0ff6127fc367ef2abc 100644 +index b8623575b1c1b565560c2dd6438190716845a652..0bc24d0effe9b2e44c41a1c00060b0ebf7395c4a 100644 --- a/src/main/java/org/bukkit/command/SimpleCommandMap.java +++ b/src/main/java/org/bukkit/command/SimpleCommandMap.java -@@ -147,6 +147,19 @@ public class SimpleCommandMap implements CommandMap { +@@ -143,6 +143,19 @@ public class SimpleCommandMap implements CommandMap { return false; } @@ -28,7 +28,7 @@ index 74252236b138969560e6513f24e7ecc6dc4a4127..9c3d02b949fe806e17200d0ff6127fc3 // Paper start - Plugins do weird things to workaround normal registration if (target.timings == null) { target.timings = co.aikar.timings.TimingsManager.getCommandTiming(null, target); -@@ -156,7 +169,7 @@ public class SimpleCommandMap implements CommandMap { +@@ -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) @@ -36,7 +36,7 @@ index 74252236b138969560e6513f24e7ecc6dc4a4127..9c3d02b949fe806e17200d0ff6127fc3 + target.execute(sender, sentCommandLabel, parsedArgs); // Purpur } // target.timings.stopTiming(); // Spigot // Paper } catch (CommandException ex) { - server.getPluginManager().callEvent(new ServerExceptionEvent(new ServerCommandException(ex, target, sender, args))); // Paper + 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..103d5699d0993f358749b3d288b924e48b734693 diff --git a/patches/api/0013-LivingEntity-safeFallDistance.patch b/patches/api/0012-LivingEntity-safeFallDistance.patch similarity index 100% rename from patches/api/0013-LivingEntity-safeFallDistance.patch rename to patches/api/0012-LivingEntity-safeFallDistance.patch diff --git a/patches/api/0014-Lagging-threshold.patch b/patches/api/0013-Lagging-threshold.patch similarity index 93% rename from patches/api/0014-Lagging-threshold.patch rename to patches/api/0013-Lagging-threshold.patch index 43338ea34..6abe7fbbe 100644 --- a/patches/api/0014-Lagging-threshold.patch +++ b/patches/api/0013-Lagging-threshold.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Lagging threshold diff --git a/src/main/java/org/bukkit/Bukkit.java b/src/main/java/org/bukkit/Bukkit.java -index a1374b608c20d47fde15135089bf8aceb98d6129..e7bca7fc74a6a2914c966183c9f83340dd510bf0 100644 +index 2d9a065aa8c6a835e49eee76acdf8cfa6af420bf..cd7e04aa1de8f051ff4bb23f36912830ab573987 100644 --- a/src/main/java/org/bukkit/Bukkit.java +++ b/src/main/java/org/bukkit/Bukkit.java @@ -2378,5 +2378,14 @@ public final class Bukkit { diff --git a/patches/api/0015-PlayerSetSpawnerTypeWithEggEvent.patch b/patches/api/0014-PlayerSetSpawnerTypeWithEggEvent.patch similarity index 100% rename from patches/api/0015-PlayerSetSpawnerTypeWithEggEvent.patch rename to patches/api/0014-PlayerSetSpawnerTypeWithEggEvent.patch diff --git a/patches/api/0016-EMC-MonsterEggSpawnEvent.patch b/patches/api/0015-EMC-MonsterEggSpawnEvent.patch similarity index 100% rename from patches/api/0016-EMC-MonsterEggSpawnEvent.patch rename to patches/api/0015-EMC-MonsterEggSpawnEvent.patch diff --git a/patches/api/0017-Player-invulnerabilities.patch b/patches/api/0016-Player-invulnerabilities.patch similarity index 100% rename from patches/api/0017-Player-invulnerabilities.patch rename to patches/api/0016-Player-invulnerabilities.patch diff --git a/patches/api/0018-Anvil-API.patch b/patches/api/0017-Anvil-API.patch similarity index 100% rename from patches/api/0018-Anvil-API.patch rename to patches/api/0017-Anvil-API.patch diff --git a/patches/api/0019-ItemStack-convenience-methods.patch b/patches/api/0018-ItemStack-convenience-methods.patch similarity index 98% rename from patches/api/0019-ItemStack-convenience-methods.patch rename to patches/api/0018-ItemStack-convenience-methods.patch index 311f7c0b0..b49fd6353 100644 --- a/patches/api/0019-ItemStack-convenience-methods.patch +++ b/patches/api/0018-ItemStack-convenience-methods.patch @@ -5,10 +5,10 @@ Subject: [PATCH] ItemStack convenience methods diff --git a/src/main/java/org/bukkit/Material.java b/src/main/java/org/bukkit/Material.java -index a39e7af2529fb8e65641d76f78e2d8eb12900853..8418eecabcb5319ef4db0785f4350920fa433278 100644 +index 2d39ecea67cd033858eaa713e405260a87c718a3..b20b3d54342a8c81d1ff4062028c7b7d2eec4fe5 100644 --- a/src/main/java/org/bukkit/Material.java +++ b/src/main/java/org/bukkit/Material.java -@@ -9871,4 +9871,39 @@ public enum Material implements Keyed, net.kyori.adventure.translation.Translata +@@ -10219,4 +10219,39 @@ public enum Material implements Keyed, net.kyori.adventure.translation.Translata return Bukkit.getUnsafe().getCreativeCategory(this); } @@ -49,10 +49,10 @@ index a39e7af2529fb8e65641d76f78e2d8eb12900853..8418eecabcb5319ef4db0785f4350920 + // Purpur end } diff --git a/src/main/java/org/bukkit/inventory/ItemStack.java b/src/main/java/org/bukkit/inventory/ItemStack.java -index 62841846ec3e14daa46564509671cab146984cc6..6ebfa971cf5bd8dac84eb29402b764feb5e6b974 100644 +index b8a344fd900dcbd4b28085a54b85b16c742e9c6f..346788b5524248e5dd431927ab206f073db01c38 100644 --- a/src/main/java/org/bukkit/inventory/ItemStack.java +++ b/src/main/java/org/bukkit/inventory/ItemStack.java -@@ -17,6 +17,18 @@ import org.bukkit.inventory.meta.ItemMeta; +@@ -16,6 +16,18 @@ import org.bukkit.inventory.meta.ItemMeta; import org.bukkit.material.MaterialData; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -71,7 +71,7 @@ index 62841846ec3e14daa46564509671cab146984cc6..6ebfa971cf5bd8dac84eb29402b764fe /** * Represents a stack of items. -@@ -979,4 +991,626 @@ public class ItemStack implements Cloneable, ConfigurationSerializable, net.kyor +@@ -978,4 +990,626 @@ public class ItemStack implements Cloneable, ConfigurationSerializable, net.kyor return Bukkit.getUnsafe().isValidRepairItemStack(toBeRepaired, this); } // Paper end diff --git a/patches/api/0020-Phantoms-attracted-to-crystals-and-crystals-shoot-ph.patch b/patches/api/0019-Phantoms-attracted-to-crystals-and-crystals-shoot-ph.patch similarity index 100% rename from patches/api/0020-Phantoms-attracted-to-crystals-and-crystals-shoot-ph.patch rename to patches/api/0019-Phantoms-attracted-to-crystals-and-crystals-shoot-ph.patch diff --git a/patches/api/0021-ChatColor-conveniences.patch b/patches/api/0020-ChatColor-conveniences.patch similarity index 93% rename from patches/api/0021-ChatColor-conveniences.patch rename to patches/api/0020-ChatColor-conveniences.patch index 77b1b7d02..e2497dc96 100644 --- a/patches/api/0021-ChatColor-conveniences.patch +++ b/patches/api/0020-ChatColor-conveniences.patch @@ -5,17 +5,17 @@ Subject: [PATCH] ChatColor conveniences diff --git a/src/main/java/org/bukkit/ChatColor.java b/src/main/java/org/bukkit/ChatColor.java -index 4594701d77c5d0f744bece871b98d9f6f73eb5a7..924af5982b1990492cafe6ef8d9f284f7933e7c4 100644 +index f6eb30f53dad684f156102cf7147b2f00c82c71e..f1239a2618b08fa92e0e20692d1c3d20d1558502 100644 --- a/src/main/java/org/bukkit/ChatColor.java +++ b/src/main/java/org/bukkit/ChatColor.java -@@ -2,6 +2,7 @@ package org.bukkit; - +@@ -3,6 +3,7 @@ package org.bukkit; + import com.google.common.base.Preconditions; import com.google.common.collect.Maps; import java.util.Map; +import java.util.regex.Matcher; import java.util.regex.Pattern; - import org.apache.commons.lang.Validate; import org.jetbrains.annotations.Contract; + import org.jetbrains.annotations.NotNull; @@ -413,4 +414,77 @@ public enum ChatColor { BY_CHAR.put(color.code, color); } diff --git a/patches/api/0022-LivingEntity-broadcastItemBreak.patch b/patches/api/0021-LivingEntity-broadcastItemBreak.patch similarity index 100% rename from patches/api/0022-LivingEntity-broadcastItemBreak.patch rename to patches/api/0021-LivingEntity-broadcastItemBreak.patch diff --git a/patches/api/0023-Item-entity-immunities.patch b/patches/api/0022-Item-entity-immunities.patch similarity index 100% rename from patches/api/0023-Item-entity-immunities.patch rename to patches/api/0022-Item-entity-immunities.patch diff --git a/patches/api/0024-Spigot-Improve-output-of-plugins-command.patch b/patches/api/0023-Spigot-Improve-output-of-plugins-command.patch similarity index 100% rename from patches/api/0024-Spigot-Improve-output-of-plugins-command.patch rename to patches/api/0023-Spigot-Improve-output-of-plugins-command.patch 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/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/0027-Alphabetize-in-game-plugins-list.patch b/patches/api/0026-Alphabetize-in-game-plugins-list.patch similarity index 100% rename from patches/api/0027-Alphabetize-in-game-plugins-list.patch rename to patches/api/0026-Alphabetize-in-game-plugins-list.patch diff --git a/patches/api/0028-Rabid-Wolf-API.patch b/patches/api/0027-Rabid-Wolf-API.patch similarity index 100% rename from patches/api/0028-Rabid-Wolf-API.patch rename to patches/api/0027-Rabid-Wolf-API.patch diff --git a/patches/api/0029-PlayerBookTooLargeEvent.patch b/patches/api/0028-PlayerBookTooLargeEvent.patch similarity index 100% rename from patches/api/0029-PlayerBookTooLargeEvent.patch rename to patches/api/0028-PlayerBookTooLargeEvent.patch diff --git a/patches/api/0030-Full-netherite-armor-grants-fire-resistance.patch b/patches/api/0029-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/0029-Full-netherite-armor-grants-fire-resistance.patch diff --git a/patches/api/0031-Add-EntityTeleportHinderedEvent.patch b/patches/api/0030-Add-EntityTeleportHinderedEvent.patch similarity index 100% rename from patches/api/0031-Add-EntityTeleportHinderedEvent.patch rename to patches/api/0030-Add-EntityTeleportHinderedEvent.patch diff --git a/patches/api/0032-Conflict-on-change-for-adventure-deprecations.patch b/patches/api/0031-Conflict-on-change-for-adventure-deprecations.patch similarity index 99% rename from patches/api/0032-Conflict-on-change-for-adventure-deprecations.patch rename to patches/api/0031-Conflict-on-change-for-adventure-deprecations.patch index 760cba3a6..7c6ef90c9 100644 --- a/patches/api/0032-Conflict-on-change-for-adventure-deprecations.patch +++ b/patches/api/0031-Conflict-on-change-for-adventure-deprecations.patch @@ -591,7 +591,7 @@ index 943d324435350d3f16fad3e21cb472a01a3ff60b..9f66a44977ab3248ce41733e424b5b71 this.message = net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().deserialize(message); // Paper } diff --git a/src/main/java/org/bukkit/event/server/ServerListPingEvent.java b/src/main/java/org/bukkit/event/server/ServerListPingEvent.java -index 172697ac5dc0ea3551a61b5589416ac68f372cd1..fffe2ab5a9f282b60d5d7ae316c063d8945a65c0 100644 +index 12cdae95c338d21684991d34aea5a643f4b4bcd3..f88356e5be4d33f403617b004e5cd76fe17f2e24 100644 --- a/src/main/java/org/bukkit/event/server/ServerListPingEvent.java +++ b/src/main/java/org/bukkit/event/server/ServerListPingEvent.java @@ -113,7 +113,7 @@ public class ServerListPingEvent extends ServerEvent implements Iterable diff --git a/patches/api/0033-Add-enchantment-target-for-bows-and-crossbows.patch b/patches/api/0032-Add-enchantment-target-for-bows-and-crossbows.patch similarity index 100% rename from patches/api/0033-Add-enchantment-target-for-bows-and-crossbows.patch rename to patches/api/0032-Add-enchantment-target-for-bows-and-crossbows.patch diff --git a/patches/api/0034-Iron-golem-poppy-calms-anger.patch b/patches/api/0033-Iron-golem-poppy-calms-anger.patch similarity index 100% rename from patches/api/0034-Iron-golem-poppy-calms-anger.patch rename to patches/api/0033-Iron-golem-poppy-calms-anger.patch diff --git a/patches/api/0035-API-for-any-mob-to-burn-daylight.patch b/patches/api/0034-API-for-any-mob-to-burn-daylight.patch similarity index 100% rename from patches/api/0035-API-for-any-mob-to-burn-daylight.patch rename to patches/api/0034-API-for-any-mob-to-burn-daylight.patch diff --git a/patches/api/0036-Flying-Fall-Damage-API.patch b/patches/api/0035-Flying-Fall-Damage-API.patch similarity index 100% rename from patches/api/0036-Flying-Fall-Damage-API.patch rename to patches/api/0035-Flying-Fall-Damage-API.patch diff --git a/patches/api/0037-Add-back-player-spawned-endermite-API.patch b/patches/api/0036-Add-back-player-spawned-endermite-API.patch similarity index 100% rename from patches/api/0037-Add-back-player-spawned-endermite-API.patch rename to patches/api/0036-Add-back-player-spawned-endermite-API.patch diff --git a/patches/api/0038-Fix-default-permission-system.patch b/patches/api/0037-Fix-default-permission-system.patch similarity index 100% rename from patches/api/0038-Fix-default-permission-system.patch rename to patches/api/0037-Fix-default-permission-system.patch diff --git a/patches/api/0039-Summoner-API.patch b/patches/api/0038-Summoner-API.patch similarity index 100% rename from patches/api/0039-Summoner-API.patch rename to patches/api/0038-Summoner-API.patch diff --git a/patches/api/0040-Clean-up-version-command-output.patch b/patches/api/0039-Clean-up-version-command-output.patch similarity index 59% rename from patches/api/0040-Clean-up-version-command-output.patch rename to patches/api/0039-Clean-up-version-command-output.patch index 3954833b1..279ab404e 100644 --- a/patches/api/0040-Clean-up-version-command-output.patch +++ b/patches/api/0039-Clean-up-version-command-output.patch @@ -22,30 +22,30 @@ index a736d7bcdc5861a01b66ba36158db1c716339346..22fc165fd9c95f0f3ae1be7a0857e48c @Override diff --git a/src/main/java/org/bukkit/command/defaults/VersionCommand.java b/src/main/java/org/bukkit/command/defaults/VersionCommand.java -index 57a21495843f3a144cd73473cdc8781d6129b7ca..b7fa160a305ee89004c11a3d8a01ac3b721f59b6 100644 +index e40f017f87d6b6b4770501b106c76dc69ec69abb..eac5830986cd0638950bbb1e6f10a30e246e09a7 100644 --- a/src/main/java/org/bukkit/command/defaults/VersionCommand.java +++ b/src/main/java/org/bukkit/command/defaults/VersionCommand.java -@@ -199,7 +199,7 @@ public class VersionCommand extends BukkitCommand { +@@ -198,7 +198,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?", net.kyori.adventure.text.format.NamedTextColor.YELLOW)); -+ setVersionMessage(Component.text("* Unknown version, custom build?", net.kyori.adventure.text.format.NamedTextColor.RED)); // Purpur +- setVersionMessage(net.kyori.adventure.text.Component.text("Unknown version, custom build?", net.kyori.adventure.text.format.NamedTextColor.YELLOW)); ++ setVersionMessage(net.kyori.adventure.text.Component.text("* Unknown version, custom build?", net.kyori.adventure.text.format.NamedTextColor.RED)); // Purpur return; } setVersionMessage(getVersionFetcher().getVersionMessage(version)); -@@ -240,9 +240,11 @@ public class VersionCommand extends BukkitCommand { +@@ -239,9 +239,11 @@ public class VersionCommand extends BukkitCommand { // Paper start - private void setVersionMessage(final @NotNull Component msg) { + private void setVersionMessage(final @NotNull net.kyori.adventure.text.Component msg) { lastCheck = System.currentTimeMillis(); -- final Component message = net.kyori.adventure.text.TextComponent.ofChildren( -- Component.text(Bukkit.getVersionMessage(), net.kyori.adventure.text.format.NamedTextColor.WHITE), -- Component.newline(), +- final net.kyori.adventure.text.Component message = net.kyori.adventure.text.TextComponent.ofChildren( +- net.kyori.adventure.text.Component.text(Bukkit.getVersionMessage(), net.kyori.adventure.text.format.NamedTextColor.WHITE), +- net.kyori.adventure.text.Component.newline(), + // Purpur start + int distance = getVersionFetcher().distance(); -+ final Component message = Component.join(net.kyori.adventure.text.JoinConfiguration.separator(Component.newline()), ++ final net.kyori.adventure.text.Component message = net.kyori.adventure.text.Component.join(net.kyori.adventure.text.JoinConfiguration.separator(net.kyori.adventure.text.Component.newline()), + ChatColor.parseMM("Current: %s%s*", distance == 0 ? "" : distance > 0 ? "" : "", Bukkit.getVersion()), + // Purpur end msg ); - this.versionMessage = Component.text() + this.versionMessage = net.kyori.adventure.text.Component.text() diff --git a/patches/api/0041-Extended-OfflinePlayer-API.patch b/patches/api/0040-Extended-OfflinePlayer-API.patch similarity index 100% rename from patches/api/0041-Extended-OfflinePlayer-API.patch rename to patches/api/0040-Extended-OfflinePlayer-API.patch diff --git a/patches/api/0042-Added-the-ability-to-add-combustible-items.patch b/patches/api/0041-Added-the-ability-to-add-combustible-items.patch similarity index 95% rename from patches/api/0042-Added-the-ability-to-add-combustible-items.patch rename to patches/api/0041-Added-the-ability-to-add-combustible-items.patch index e0225ab90..87e352001 100644 --- a/patches/api/0042-Added-the-ability-to-add-combustible-items.patch +++ b/patches/api/0041-Added-the-ability-to-add-combustible-items.patch @@ -5,7 +5,7 @@ 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 9189ada0644a226038eeb7967d45c72ddfc89085..8c37680ca16adbf030cfd3098330d2592d4aaa23 100644 +index 9ff99a1ea5183042d7eea89bb310386c6630663a..c17e49f1eea2a70c70eea84820fc17fd5e263304 100644 --- a/src/main/java/org/bukkit/Bukkit.java +++ b/src/main/java/org/bukkit/Bukkit.java @@ -2387,5 +2387,24 @@ public final class Bukkit { diff --git a/patches/api/0043-Potion-NamespacedKey.patch b/patches/api/0042-Potion-NamespacedKey.patch similarity index 97% rename from patches/api/0043-Potion-NamespacedKey.patch rename to patches/api/0042-Potion-NamespacedKey.patch index 97873f4d0..00e7fc1b7 100644 --- a/patches/api/0043-Potion-NamespacedKey.patch +++ b/patches/api/0042-Potion-NamespacedKey.patch @@ -5,12 +5,12 @@ Subject: [PATCH] Potion NamespacedKey diff --git a/src/main/java/org/bukkit/potion/PotionEffect.java b/src/main/java/org/bukkit/potion/PotionEffect.java -index 74767751199bce03d63f2a9524712656193f850c..52a31aa6d77e6c9afac78e76829d0265e903b029 100644 +index 24e36cdf580da885ac64002673a786b9c5a3f787..d20cc4d4f5b37a3de9cb3cf47af7a908e9dbc2fc 100644 --- a/src/main/java/org/bukkit/potion/PotionEffect.java +++ b/src/main/java/org/bukkit/potion/PotionEffect.java -@@ -5,6 +5,7 @@ import java.util.Map; +@@ -5,6 +5,7 @@ import com.google.common.collect.ImmutableMap; + import java.util.Map; import java.util.NoSuchElementException; - import org.apache.commons.lang.Validate; import org.bukkit.Color; +import org.bukkit.NamespacedKey; import org.bukkit.configuration.serialization.ConfigurationSerializable; @@ -65,7 +65,7 @@ index 74767751199bce03d63f2a9524712656193f850c..52a31aa6d77e6c9afac78e76829d0265 + */ + public PotionEffect(@NotNull PotionEffectType type, int duration, int amplifier, boolean ambient, boolean particles, boolean icon, @Nullable NamespacedKey key) { + // Purpur end - Validate.notNull(type, "effect type cannot be null"); + Preconditions.checkArgument(type != null, "effect type cannot be null"); this.type = type; this.duration = duration; @@ -51,6 +84,7 @@ public class PotionEffect implements ConfigurationSerializable { diff --git a/patches/api/0044-Grindstone-API.patch b/patches/api/0043-Grindstone-API.patch similarity index 100% rename from patches/api/0044-Grindstone-API.patch rename to patches/api/0043-Grindstone-API.patch diff --git a/patches/api/0045-Shears-can-have-looting-enchantment.patch b/patches/api/0044-Shears-can-have-looting-enchantment.patch similarity index 100% rename from patches/api/0045-Shears-can-have-looting-enchantment.patch rename to patches/api/0044-Shears-can-have-looting-enchantment.patch diff --git a/patches/api/0046-Lobotomize-stuck-villagers.patch b/patches/api/0045-Lobotomize-stuck-villagers.patch similarity index 100% rename from patches/api/0046-Lobotomize-stuck-villagers.patch rename to patches/api/0045-Lobotomize-stuck-villagers.patch diff --git a/patches/server/.keep b/patches/server/.keep new file mode 100644 index 000000000..e69de29bb diff --git a/patches/server/0001-Pufferfish-Server-Changes.patch b/patches/server/0001-Pufferfish-Server-Changes.patch deleted file mode 100644 index 9a9b1b8de..000000000 --- a/patches/server/0001-Pufferfish-Server-Changes.patch +++ /dev/null @@ -1,4331 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Kevin Raneri -Date: Wed, 3 Feb 2021 23:02:38 -0600 -Subject: [PATCH] Pufferfish Server Changes - -Pufferfish -Copyright (C) 2022 Pufferfish Studios LLC - -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/build.gradle.kts b/build.gradle.kts -index 0282e3b75470e1a68ea1fc228082483514ba432e..b04e9ab317fbce9c090b61076eb07c40f069dc59 100644 ---- a/build.gradle.kts -+++ b/build.gradle.kts -@@ -9,8 +9,12 @@ plugins { - } - - dependencies { -- implementation(project(":paper-api")) -- implementation(project(":paper-mojangapi")) -+ implementation(project(":pufferfish-api")) // Pufferfish // Paper -+ // Pufferfish start -+ implementation("io.papermc.paper:paper-mojangapi:1.18.2-R0.1-SNAPSHOT") { -+ exclude("io.papermc.paper", "paper-api") -+ } -+ // Pufferfish end - // Paper start - implementation("org.jline:jline-terminal-jansi:3.21.0") - implementation("net.minecrell:terminalconsoleappender:1.3.0") -@@ -42,12 +46,28 @@ dependencies { - } - // Paper end - -+ // Pufferfish start -+ implementation("org.yaml:snakeyaml:1.30") -+ implementation ("me.carleslc.Simple-YAML:Simple-Yaml:1.8") { -+ exclude(group="org.yaml", module="snakeyaml") -+ } -+ // Pufferfish end -+ implementation("com.github.technove:Flare:34637f3f87") // Pufferfish - flare -+ - testImplementation("io.github.classgraph:classgraph:4.8.47") // Paper - mob goal test - testImplementation("junit:junit:4.13.2") - testImplementation("org.hamcrest:hamcrest-library:1.3") - } - - val craftbukkitPackageVersion = "1_18_R2" // Paper -+ -+// Pufferfish Start -+tasks.withType { -+ val compilerArgs = options.compilerArgs -+ compilerArgs.add("--add-modules=jdk.incubator.vector") -+} -+// Pufferfish End -+ - tasks.jar { - archiveClassifier.set("dev") - -@@ -60,7 +80,7 @@ tasks.jar { - attributes( - "Main-Class" to "org.bukkit.craftbukkit.Main", - "Implementation-Title" to "CraftBukkit", -- "Implementation-Version" to "git-Paper-$implementationVersion", -+ "Implementation-Version" to "git-Pufferfish-$implementationVersion", // Pufferfish - "Implementation-Vendor" to date, // Paper - "Specification-Title" to "Bukkit", - "Specification-Version" to project.version, -diff --git a/src/main/java/co/aikar/timings/TimingsExport.java b/src/main/java/co/aikar/timings/TimingsExport.java -index f3bf9df8c0bd56cad461210ce8551ade3a220b6b..088585a1075cd8790e2599eb6a8372adcff050e3 100644 ---- a/src/main/java/co/aikar/timings/TimingsExport.java -+++ b/src/main/java/co/aikar/timings/TimingsExport.java -@@ -240,7 +240,8 @@ public class TimingsExport extends Thread { - parent.put("config", createObject( - pair("spigot", mapAsJSON(Bukkit.spigot().getSpigotConfig(), null)), - pair("bukkit", mapAsJSON(Bukkit.spigot().getBukkitConfig(), null)), -- pair("paper", mapAsJSON(Bukkit.spigot().getPaperConfig(), null)) -+ pair("paper", mapAsJSON(Bukkit.spigot().getPaperConfig(), null)), // Pufferfish -+ pair("pufferfish", mapAsJSON(gg.pufferfish.pufferfish.PufferfishConfig.getConfigCopy(), null)) // Pufferfish - )); - - new TimingsExport(listeners, parent, history).start(); -diff --git a/src/main/java/com/destroystokyo/paper/Metrics.java b/src/main/java/com/destroystokyo/paper/Metrics.java -index 218f5bafeed8551b55b91c7fccaf6935c8b631ca..f2fe6ea3719ff8b2913b7a3a939d7a5b75cb8b28 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("Pufferfish", serverUUID, logFailedRequests, Bukkit.getLogger()); // Pufferfish - - metrics.addCustomChart(new Metrics.SimplePie("minecraft_version", () -> { - String minecraftVersion = Bukkit.getVersion(); -@@ -603,7 +603,7 @@ public class Metrics { - - metrics.addCustomChart(new Metrics.SingleLineChart("players", () -> Bukkit.getOnlinePlayers().size())); - metrics.addCustomChart(new Metrics.SimplePie("online_mode", () -> Bukkit.getOnlineMode() ? "online" : "offline")); -- metrics.addCustomChart(new Metrics.SimplePie("paper_version", () -> (Metrics.class.getPackage().getImplementationVersion() != null) ? Metrics.class.getPackage().getImplementationVersion() : "unknown")); -+ metrics.addCustomChart(new Metrics.SimplePie("pufferfish_version", () -> (Metrics.class.getPackage().getImplementationVersion() != null) ? Metrics.class.getPackage().getImplementationVersion() : "unknown")); - - metrics.addCustomChart(new Metrics.DrilldownPie("java_version", () -> { - Map> map = new HashMap<>(); -diff --git a/src/main/java/com/destroystokyo/paper/PaperConfig.java b/src/main/java/com/destroystokyo/paper/PaperConfig.java -index 8379c6313f06ab3eeaf02bad41d8b835d50e093f..7a7d0566611aafafba30b7b25c2f1f3e78b054fa 100644 ---- a/src/main/java/com/destroystokyo/paper/PaperConfig.java -+++ b/src/main/java/com/destroystokyo/paper/PaperConfig.java -@@ -241,6 +241,15 @@ public class PaperConfig { - public static String timingsServerName; - private static void timings() { - boolean timings = getBoolean("timings.enabled", true); -+ // Pufferfish start -+ boolean reallyEnableTimings = getBoolean("timings.really-enabled", false); -+ if (timings && !reallyEnableTimings) { -+ Bukkit.getLogger().log(Level.WARNING, "[Pufferfish] To improve performance, timings have been disabled by default"); -+ Bukkit.getLogger().log(Level.WARNING, "[Pufferfish] You can still use timings by using /timings on, but they will not start on server startup unless you set timings.really-enabled to true in paper.yml"); -+ Bukkit.getLogger().log(Level.WARNING, "[Pufferfish] If you would like to disable this message, either set timings.really-enabled to true or timings.enabled to false."); -+ } -+ timings = reallyEnableTimings; -+ // Pufferfish end - boolean verboseTimings = getBoolean("timings.verbose", true); - TimingsManager.url = getString("timings.url", "https://timings.aikar.co/"); - if (!TimingsManager.url.endsWith("/")) { -diff --git a/src/main/java/com/destroystokyo/paper/util/misc/AreaMap.java b/src/main/java/com/destroystokyo/paper/util/misc/AreaMap.java -index c89f6986eda5a132a948732ea1b6923370685317..a69c13e20040c1561d9c2d4d89ec7d4e635134fc 100644 ---- a/src/main/java/com/destroystokyo/paper/util/misc/AreaMap.java -+++ b/src/main/java/com/destroystokyo/paper/util/misc/AreaMap.java -@@ -26,7 +26,7 @@ public abstract class AreaMap { - - // we use linked for better iteration. - // map of: coordinate to set of objects in coordinate -- protected final Long2ObjectOpenHashMap> areaMap = new Long2ObjectOpenHashMap<>(1024, 0.7f); -+ protected Long2ObjectOpenHashMap> areaMap = new Long2ObjectOpenHashMap<>(1024, 0.7f); // Pufferfish - not actually final - protected final PooledLinkedHashSets pooledHashSets; - - protected final ChangeCallback addCallback; -@@ -160,7 +160,8 @@ public abstract class AreaMap { - protected abstract PooledLinkedHashSets.PooledObjectLinkedOpenHashSet getEmptySetFor(final E object); - - // expensive op, only for debug -- protected void validate(final E object, final int viewDistance) { -+ protected void validate0(final E object, final int viewDistance) { // Pufferfish - rename this thing just in case it gets used I'd rather a compile time error. -+ if (true) throw new UnsupportedOperationException(); // Pufferfish - not going to put in the effort to fix this if it doesn't ever get used. - int entiesGot = 0; - int expectedEntries = (2 * viewDistance + 1); - expectedEntries *= expectedEntries; -diff --git a/src/main/java/com/destroystokyo/paper/util/misc/PlayerAreaMap.java b/src/main/java/com/destroystokyo/paper/util/misc/PlayerAreaMap.java -index 46954db7ecd35ac4018fdf476df7c8020d7ce6c8..1ad890a244bdf6df48a8db68cb43450e08c788a6 100644 ---- a/src/main/java/com/destroystokyo/paper/util/misc/PlayerAreaMap.java -+++ b/src/main/java/com/destroystokyo/paper/util/misc/PlayerAreaMap.java -@@ -5,7 +5,7 @@ import net.minecraft.server.level.ServerPlayer; - /** - * @author Spottedleaf - */ --public final class PlayerAreaMap extends AreaMap { -+public class PlayerAreaMap extends AreaMap { // Pufferfish - not actually final - - public PlayerAreaMap() { - super(); -diff --git a/src/main/java/gg/airplane/structs/FluidDirectionCache.java b/src/main/java/gg/airplane/structs/FluidDirectionCache.java -new file mode 100644 -index 0000000000000000000000000000000000000000..aa8467b9dda1f7707e41f50ac7b3e9d7343723ec ---- /dev/null -+++ b/src/main/java/gg/airplane/structs/FluidDirectionCache.java -@@ -0,0 +1,136 @@ -+package gg.airplane.structs; -+ -+import it.unimi.dsi.fastutil.HashCommon; -+ -+/** -+ * This is a replacement for the cache used in FluidTypeFlowing. -+ * The requirements for the previous cache were: -+ * - Store 200 entries -+ * - Look for the flag in the cache -+ * - If it exists, move to front of cache -+ * - If it doesn't exist, remove last entry in cache and insert in front -+ * -+ * This class accomplishes something similar, however has a few different -+ * requirements put into place to make this more optimize: -+ * -+ * - maxDistance is the most amount of entries to be checked, instead -+ * of having to check the entire list. -+ * - In combination with that, entries are all tracked by age and how -+ * frequently they're used. This enables us to remove old entries, -+ * without constantly shifting any around. -+ * -+ * Usage of the previous map would have to reset the head every single usage, -+ * shifting the entire map. Here, nothing happens except an increment when -+ * the cache is hit, and when it needs to replace an old element only a single -+ * element is modified. -+ */ -+public class FluidDirectionCache { -+ -+ private static class FluidDirectionEntry { -+ private final T data; -+ private final boolean flag; -+ private int uses = 0; -+ private int age = 0; -+ -+ private FluidDirectionEntry(T data, boolean flag) { -+ this.data = data; -+ this.flag = flag; -+ } -+ -+ public int getValue() { -+ return this.uses - (this.age >> 1); // age isn't as important as uses -+ } -+ -+ public void incrementUses() { -+ this.uses = this.uses + 1 & Integer.MAX_VALUE; -+ } -+ -+ public void incrementAge() { -+ this.age = this.age + 1 & Integer.MAX_VALUE; -+ } -+ } -+ -+ private final FluidDirectionEntry[] entries; -+ private final int mask; -+ private final int maxDistance; // the most amount of entries to check for a value -+ -+ public FluidDirectionCache(int size) { -+ int arraySize = HashCommon.nextPowerOfTwo(size); -+ this.entries = new FluidDirectionEntry[arraySize]; -+ this.mask = arraySize - 1; -+ this.maxDistance = Math.min(arraySize, 4); -+ } -+ -+ public Boolean getValue(T data) { -+ FluidDirectionEntry curr; -+ int pos; -+ -+ if ((curr = this.entries[pos = HashCommon.mix(data.hashCode()) & this.mask]) == null) { -+ return null; -+ } else if (data.equals(curr.data)) { -+ curr.incrementUses(); -+ return curr.flag; -+ } -+ -+ int checked = 1; // start at 1 because we already checked the first spot above -+ -+ while ((curr = this.entries[pos = (pos + 1) & this.mask]) != null) { -+ if (data.equals(curr.data)) { -+ curr.incrementUses(); -+ return curr.flag; -+ } else if (++checked >= this.maxDistance) { -+ break; -+ } -+ } -+ -+ return null; -+ } -+ -+ public void putValue(T data, boolean flag) { -+ FluidDirectionEntry curr; -+ int pos; -+ -+ if ((curr = this.entries[pos = HashCommon.mix(data.hashCode()) & this.mask]) == null) { -+ this.entries[pos] = new FluidDirectionEntry<>(data, flag); // add -+ return; -+ } else if (data.equals(curr.data)) { -+ curr.incrementUses(); -+ return; -+ } -+ -+ int checked = 1; // start at 1 because we already checked the first spot above -+ -+ while ((curr = this.entries[pos = (pos + 1) & this.mask]) != null) { -+ if (data.equals(curr.data)) { -+ curr.incrementUses(); -+ return; -+ } else if (++checked >= this.maxDistance) { -+ this.forceAdd(data, flag); -+ return; -+ } -+ } -+ -+ this.entries[pos] = new FluidDirectionEntry<>(data, flag); // add -+ } -+ -+ private void forceAdd(T data, boolean flag) { -+ int expectedPos = HashCommon.mix(data.hashCode()) & this.mask; -+ -+ int toRemovePos = expectedPos; -+ FluidDirectionEntry entryToRemove = this.entries[toRemovePos]; -+ -+ for (int i = expectedPos + 1; i < expectedPos + this.maxDistance; i++) { -+ int pos = i & this.mask; -+ FluidDirectionEntry entry = this.entries[pos]; -+ if (entry.getValue() < entryToRemove.getValue()) { -+ toRemovePos = pos; -+ entryToRemove = entry; -+ } -+ -+ entry.incrementAge(); // use this as a mechanism to age the other entries -+ } -+ -+ // remove the least used/oldest entry -+ this.entries[toRemovePos] = new FluidDirectionEntry(data, flag); -+ } -+} -diff --git a/src/main/java/gg/airplane/structs/ItemListWithBitset.java b/src/main/java/gg/airplane/structs/ItemListWithBitset.java -new file mode 100644 -index 0000000000000000000000000000000000000000..1b7a4ee47f4445d7f2ac91d3a73ae113edbdddb2 ---- /dev/null -+++ b/src/main/java/gg/airplane/structs/ItemListWithBitset.java -@@ -0,0 +1,114 @@ -+package gg.airplane.structs; -+ -+import net.minecraft.core.NonNullList; -+import net.minecraft.world.item.ItemStack; -+import org.apache.commons.lang.Validate; -+import org.jetbrains.annotations.NotNull; -+import org.jetbrains.annotations.Nullable; -+ -+import java.util.AbstractList; -+import java.util.Arrays; -+import java.util.List; -+ -+public class ItemListWithBitset extends AbstractList { -+ public static ItemListWithBitset fromList(List list) { -+ if (list instanceof ItemListWithBitset ours) { -+ return ours; -+ } -+ return new ItemListWithBitset(list); -+ } -+ -+ private static ItemStack[] createArray(int size) { -+ ItemStack[] array = new ItemStack[size]; -+ Arrays.fill(array, ItemStack.EMPTY); -+ return array; -+ } -+ -+ private final ItemStack[] items; -+ -+ private long bitSet = 0; -+ private final long allBits; -+ -+ private static class OurNonNullList extends NonNullList { -+ protected OurNonNullList(List delegate) { -+ super(delegate, ItemStack.EMPTY); -+ } -+ } -+ -+ public final NonNullList nonNullList = new OurNonNullList(this); -+ -+ private ItemListWithBitset(List list) { -+ this(list.size()); -+ -+ for (int i = 0; i < list.size(); i++) { -+ this.set(i, list.get(i)); -+ } -+ } -+ -+ public ItemListWithBitset(int size) { -+ Validate.isTrue(size < Long.BYTES * 8, "size is too large"); -+ -+ this.items = createArray(size); -+ this.allBits = ((1L << size) - 1); -+ } -+ -+ public boolean isCompletelyEmpty() { -+ return this.bitSet == 0; -+ } -+ -+ public boolean hasFullStacks() { -+ return (this.bitSet & this.allBits) == allBits; -+ } -+ -+ @Override -+ public ItemStack set(int index, @NotNull ItemStack itemStack) { -+ ItemStack existing = this.items[index]; -+ -+ this.items[index] = itemStack; -+ -+ if (itemStack == ItemStack.EMPTY) { -+ this.bitSet &= ~(1L << index); -+ } else { -+ this.bitSet |= 1L << index; -+ } -+ -+ return existing; -+ } -+ -+ @NotNull -+ @Override -+ public ItemStack get(int var0) { -+ return this.items[var0]; -+ } -+ -+ @Override -+ public int size() { -+ return this.items.length; -+ } -+ -+ @Override -+ public void clear() { -+ Arrays.fill(this.items, ItemStack.EMPTY); -+ } -+ -+ // these are unsupported for block inventories which have a static size -+ @Override -+ public void add(int var0, ItemStack var1) { -+ throw new UnsupportedOperationException(); -+ } -+ -+ @Override -+ public ItemStack remove(int var0) { -+ throw new UnsupportedOperationException(); -+ } -+ -+ @Override -+ public String toString() { -+ return "ItemListWithBitset{" + -+ "items=" + Arrays.toString(items) + -+ ", bitSet=" + Long.toString(bitSet, 2) + -+ ", allBits=" + Long.toString(allBits, 2) + -+ ", size=" + this.items.length + -+ '}'; -+ } -+} -diff --git a/src/main/java/gg/airplane/structs/Long2FloatAgingCache.java b/src/main/java/gg/airplane/structs/Long2FloatAgingCache.java -new file mode 100644 -index 0000000000000000000000000000000000000000..a7f297ebb569f7c1f205e967ca485be70013a714 ---- /dev/null -+++ b/src/main/java/gg/airplane/structs/Long2FloatAgingCache.java -@@ -0,0 +1,119 @@ -+package gg.airplane.structs; -+ -+import it.unimi.dsi.fastutil.HashCommon; -+ -+/** -+ * A replacement for the cache used in Biome. -+ */ -+public class Long2FloatAgingCache { -+ -+ private static class AgingEntry { -+ private long data; -+ private float value; -+ private int uses = 0; -+ private int age = 0; -+ -+ private AgingEntry(long data, float value) { -+ this.data = data; -+ this.value = value; -+ } -+ -+ public void replace(long data, float flag) { -+ this.data = data; -+ this.value = flag; -+ } -+ -+ public int getValue() { -+ return this.uses - (this.age >> 1); // age isn't as important as uses -+ } -+ -+ public void incrementUses() { -+ this.uses = this.uses + 1 & Integer.MAX_VALUE; -+ } -+ -+ public void incrementAge() { -+ this.age = this.age + 1 & Integer.MAX_VALUE; -+ } -+ } -+ -+ private final AgingEntry[] entries; -+ private final int mask; -+ private final int maxDistance; // the most amount of entries to check for a value -+ -+ public Long2FloatAgingCache(int size) { -+ int arraySize = HashCommon.nextPowerOfTwo(size); -+ this.entries = new AgingEntry[arraySize]; -+ this.mask = arraySize - 1; -+ this.maxDistance = Math.min(arraySize, 4); -+ } -+ -+ public float getValue(long data) { -+ AgingEntry curr; -+ int pos; -+ -+ if ((curr = this.entries[pos = HashCommon.mix(HashCommon.long2int(data)) & this.mask]) == null) { -+ return Float.NaN; -+ } else if (data == curr.data) { -+ curr.incrementUses(); -+ return curr.value; -+ } -+ -+ int checked = 1; // start at 1 because we already checked the first spot above -+ -+ while ((curr = this.entries[pos = (pos + 1) & this.mask]) != null) { -+ if (data == curr.data) { -+ curr.incrementUses(); -+ return curr.value; -+ } else if (++checked >= this.maxDistance) { -+ break; -+ } -+ } -+ -+ return Float.NaN; -+ } -+ -+ public void putValue(long data, float value) { -+ AgingEntry curr; -+ int pos; -+ -+ if ((curr = this.entries[pos = HashCommon.mix(HashCommon.long2int(data)) & this.mask]) == null) { -+ this.entries[pos] = new AgingEntry(data, value); // add -+ return; -+ } else if (data == curr.data) { -+ curr.incrementUses(); -+ return; -+ } -+ -+ int checked = 1; // start at 1 because we already checked the first spot above -+ -+ while ((curr = this.entries[pos = (pos + 1) & this.mask]) != null) { -+ if (data == curr.data) { -+ curr.incrementUses(); -+ return; -+ } else if (++checked >= this.maxDistance) { -+ this.forceAdd(data, value); -+ return; -+ } -+ } -+ -+ this.entries[pos] = new AgingEntry(data, value); // add -+ } -+ -+ private void forceAdd(long data, float value) { -+ int expectedPos = HashCommon.mix(HashCommon.long2int(data)) & this.mask; -+ AgingEntry entryToRemove = this.entries[expectedPos]; -+ -+ for (int i = expectedPos + 1; i < expectedPos + this.maxDistance; i++) { -+ int pos = i & this.mask; -+ AgingEntry entry = this.entries[pos]; -+ if (entry.getValue() < entryToRemove.getValue()) { -+ entryToRemove = entry; -+ } -+ -+ entry.incrementAge(); // use this as a mechanism to age the other entries -+ } -+ -+ // remove the least used/oldest entry -+ entryToRemove.replace(data, value); -+ } -+} -diff --git a/src/main/java/gg/pufferfish/pufferfish/PufferfishCommand.java b/src/main/java/gg/pufferfish/pufferfish/PufferfishCommand.java -new file mode 100644 -index 0000000000000000000000000000000000000000..e164237e749bcc43466d4ed7aeada5ab9fddf8a6 ---- /dev/null -+++ b/src/main/java/gg/pufferfish/pufferfish/PufferfishCommand.java -@@ -0,0 +1,68 @@ -+package gg.pufferfish.pufferfish; -+ -+import java.io.IOException; -+import java.util.Collections; -+import java.util.List; -+import java.util.stream.Collectors; -+import java.util.stream.Stream; -+import net.kyori.adventure.text.Component; -+import net.kyori.adventure.text.format.NamedTextColor; -+import net.md_5.bungee.api.ChatColor; -+import net.minecraft.server.MinecraftServer; -+import org.bukkit.Bukkit; -+import org.bukkit.Location; -+import org.bukkit.command.Command; -+import org.bukkit.command.CommandSender; -+ -+public class PufferfishCommand extends Command { -+ -+ public PufferfishCommand() { -+ super("pufferfish"); -+ this.description = "Pufferfish related commands"; -+ this.usageMessage = "/pufferfish [reload | version]"; -+ this.setPermission("bukkit.command.pufferfish"); -+ } -+ -+ public static void init() { -+ MinecraftServer.getServer().server.getCommandMap().register("pufferfish", "Pufferfish", new PufferfishCommand()); -+ } -+ -+ @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; -+ String prefix = ChatColor.of("#12fff6") + "" + ChatColor.BOLD + "Pufferfish » " + ChatColor.of("#e8f9f9"); -+ -+ if (args.length != 1) { -+ sender.sendMessage(prefix + "Usage: " + usageMessage); -+ args = new String[]{"version"}; -+ } -+ -+ if (args[0].equalsIgnoreCase("reload")) { -+ MinecraftServer console = MinecraftServer.getServer(); -+ try { -+ PufferfishConfig.load(); -+ } catch (IOException e) { -+ sender.sendMessage(Component.text("Failed to reload.", NamedTextColor.RED)); -+ e.printStackTrace(); -+ return true; -+ } -+ console.server.reloadCount++; -+ -+ Command.broadcastCommandMessage(sender, prefix + "Pufferfish configuration has been reloaded."); -+ } else if (args[0].equalsIgnoreCase("version")) { -+ Command.broadcastCommandMessage(sender, prefix + "This server is running " + Bukkit.getName() + " version " + Bukkit.getVersion() + " (Implementing API version " + Bukkit.getBukkitVersion() + ")"); -+ } -+ -+ return true; -+ } -+} -diff --git a/src/main/java/gg/pufferfish/pufferfish/PufferfishConfig.java b/src/main/java/gg/pufferfish/pufferfish/PufferfishConfig.java -new file mode 100644 -index 0000000000000000000000000000000000000000..7572cbc662a5b824435d75e1b3b7ea0e58144c9c ---- /dev/null -+++ b/src/main/java/gg/pufferfish/pufferfish/PufferfishConfig.java -@@ -0,0 +1,313 @@ -+package gg.pufferfish.pufferfish; -+ -+import gg.pufferfish.pufferfish.simd.SIMDDetection; -+import java.io.File; -+import java.io.IOException; -+import java.util.Collections; -+import java.util.Locale; -+import java.util.Map; -+import net.minecraft.server.MinecraftServer; -+import net.minecraft.tags.TagKey; -+import org.apache.logging.log4j.Level; -+import org.bukkit.configuration.ConfigurationSection; -+import net.minecraft.core.Registry; -+import net.minecraft.world.entity.EntityType; -+import java.lang.reflect.Method; -+import java.lang.reflect.Modifier; -+import java.util.List; -+import gg.pufferfish.pufferfish.flare.FlareCommand; -+import net.minecraft.server.MinecraftServer; -+import org.apache.logging.log4j.Level; -+import org.bukkit.configuration.ConfigurationSection; -+import org.bukkit.configuration.MemoryConfiguration; -+import org.jetbrains.annotations.Nullable; -+import org.simpleyaml.configuration.comments.CommentType; -+import org.simpleyaml.configuration.file.YamlFile; -+import org.simpleyaml.exceptions.InvalidConfigurationException; -+import org.bukkit.command.SimpleCommandMap; -+ -+import java.lang.reflect.Method; -+import java.lang.reflect.Modifier; -+import java.util.List; -+import java.net.URI; -+import java.util.Collections; -+ -+public class PufferfishConfig { -+ -+ private static final YamlFile config = new YamlFile(); -+ private static int updates = 0; -+ -+ private static ConfigurationSection convertToBukkit(org.simpleyaml.configuration.ConfigurationSection section) { -+ ConfigurationSection newSection = new MemoryConfiguration(); -+ for (String key : section.getKeys(false)) { -+ if (section.isConfigurationSection(key)) { -+ newSection.set(key, convertToBukkit(section.getConfigurationSection(key))); -+ } else { -+ newSection.set(key, section.get(key)); -+ } -+ } -+ return newSection; -+ } -+ -+ public static ConfigurationSection getConfigCopy() { -+ return convertToBukkit(config); -+ } -+ -+ public static int getUpdates() { -+ return updates; -+ } -+ -+ public static void load() throws IOException { -+ File configFile = new File("pufferfish.yml"); -+ -+ if (configFile.exists()) { -+ try { -+ config.load(configFile); -+ } catch (InvalidConfigurationException e) { -+ throw new IOException(e); -+ } -+ } -+ -+ getString("info.version", "1.0"); -+ setComment("info", -+ "Pufferfish Configuration", -+ "Check out Pufferfish Host for maximum performance server hosting: https://pufferfish.host", -+ "Join our Discord for support: https://discord.gg/reZw4vQV9H", -+ "Download new builds at https://ci.pufferfish.host/job/Pufferfish"); -+ -+ for (Method method : PufferfishConfig.class.getDeclaredMethods()) { -+ if (Modifier.isStatic(method.getModifiers()) && Modifier.isPrivate(method.getModifiers()) && method.getParameterCount() == 0 && -+ method.getReturnType() == Void.TYPE && !method.getName().startsWith("lambda")) { -+ method.setAccessible(true); -+ try { -+ method.invoke(null); -+ } catch (Throwable t) { -+ MinecraftServer.LOGGER.warn("Failed to load configuration option from " + method.getName(), t); -+ } -+ } -+ } -+ -+ updates++; -+ -+ config.save(configFile); -+ -+ // Attempt to detect vectorization -+ try { -+ SIMDDetection.isEnabled = SIMDDetection.canEnable(PufferfishLogger.LOGGER); -+ SIMDDetection.versionLimited = SIMDDetection.getJavaVersion() != 17 && SIMDDetection.getJavaVersion() != 18; -+ } catch (NoClassDefFoundError | Exception ignored) {} -+ -+ if (SIMDDetection.isEnabled) { -+ PufferfishLogger.LOGGER.info("SIMD operations detected as functional. Will replace some operations with faster versions."); -+ } else if (SIMDDetection.versionLimited) { -+ PufferfishLogger.LOGGER.warning("Will not enable SIMD! These optimizations are only safely supported on Java 17 and Java 18."); -+ } else { -+ PufferfishLogger.LOGGER.warning("SIMD operations are available for your server, but are not configured!"); -+ PufferfishLogger.LOGGER.warning("To enable additional optimizations, add \"--add-modules=jdk.incubator.vector\" to your startup flags, BEFORE the \"-jar\"."); -+ PufferfishLogger.LOGGER.warning("If you have already added this flag, then SIMD operations are not supported on your JVM or CPU."); -+ PufferfishLogger.LOGGER.warning("Debug: Java: " + System.getProperty("java.version") + ", test run: " + SIMDDetection.testRun); -+ } -+ } -+ -+ private static void setComment(String key, String... comment) { -+ if (config.contains(key)) { -+ config.setComment(key, String.join("\n", comment), CommentType.BLOCK); -+ } -+ } -+ -+ private static void ensureDefault(String key, Object defaultValue, String... comment) { -+ if (!config.contains(key)) { -+ config.set(key, defaultValue); -+ config.setComment(key, String.join("\n", comment), CommentType.BLOCK); -+ } -+ } -+ -+ private static boolean getBoolean(String key, boolean defaultValue, String... comment) { -+ return getBoolean(key, null, defaultValue, comment); -+ } -+ -+ private static boolean getBoolean(String key, @Nullable String oldKey, boolean defaultValue, String... comment) { -+ ensureDefault(key, defaultValue, comment); -+ return config.getBoolean(key, defaultValue); -+ } -+ -+ private static int getInt(String key, int defaultValue, String... comment) { -+ return getInt(key, null, defaultValue, comment); -+ } -+ -+ private static int getInt(String key, @Nullable String oldKey, int defaultValue, String... comment) { -+ ensureDefault(key, defaultValue, comment); -+ return config.getInt(key, defaultValue); -+ } -+ -+ private static double getDouble(String key, double defaultValue, String... comment) { -+ return getDouble(key, null, defaultValue, comment); -+ } -+ -+ private static double getDouble(String key, @Nullable String oldKey, double defaultValue, String... comment) { -+ ensureDefault(key, defaultValue, comment); -+ return config.getDouble(key, defaultValue); -+ } -+ -+ private static String getString(String key, String defaultValue, String... comment) { -+ return getOldString(key, null, defaultValue, comment); -+ } -+ -+ private static String getOldString(String key, @Nullable String oldKey, String defaultValue, String... comment) { -+ ensureDefault(key, defaultValue, comment); -+ return config.getString(key, defaultValue); -+ } -+ -+ private static List getStringList(String key, List defaultValue, String... comment) { -+ return getStringList(key, null, defaultValue, comment); -+ } -+ -+ private static List getStringList(String key, @Nullable String oldKey, List defaultValue, String... comment) { -+ ensureDefault(key, defaultValue, comment); -+ return config.getStringList(key); -+ } -+ -+ public static String sentryDsn; -+ private static void sentry() { -+ String sentryEnvironment = System.getenv("SENTRY_DSN"); -+ String sentryConfig = getString("sentry-dsn", "", "Sentry DSN for improved error logging, leave blank to disable", "Obtain from https://sentry.io/"); -+ -+ sentryDsn = sentryEnvironment == null ? sentryConfig : sentryEnvironment; -+ if (sentryDsn != null && !sentryDsn.isBlank()) { -+ gg.pufferfish.pufferfish.sentry.SentryManager.init(); -+ } -+ } -+ -+ public static boolean enableBooks; -+ private static void books() { -+ enableBooks = getBoolean("enable-books", true, -+ "Whether or not books should be writeable.", -+ "Servers that anticipate being a target for duping may want to consider", -+ "disabling this option.", -+ "This can be overridden per-player with the permission pufferfish.usebooks"); -+ } -+ -+ public static boolean enableSuffocationOptimization; -+ private static void suffocationOptimization() { -+ enableSuffocationOptimization = getBoolean("enable-suffocation-optimization", true, -+ "Optimizes the suffocation check by selectively skipping", -+ "the check in a way that still appears vanilla. This should", -+ "be left enabled on most servers, but is provided as a", -+ "configuration option if the vanilla deviation is undesirable."); -+ } -+ -+ public static boolean enableAsyncMobSpawning; -+ public static boolean asyncMobSpawningInitialized; -+ private static void asyncMobSpawning() { -+ boolean temp = getBoolean("enable-async-mob-spawning", true, -+ "Whether or not asynchronous mob spawning should be enabled.", -+ "On servers with many entities, this can improve performance by up to 15%. You must have", -+ "paper's per-player-mob-spawns setting set to true for this to work.", -+ "One quick note - this does not actually spawn mobs async (that would be very unsafe).", -+ "This just offloads some expensive calculations that are required for mob spawning."); -+ -+ // This prevents us from changing the value during a reload. -+ if (!asyncMobSpawningInitialized) { -+ asyncMobSpawningInitialized = true; -+ enableAsyncMobSpawning = temp; -+ } -+ } -+ -+ public static int maxProjectileLoadsPerTick; -+ public static int maxProjectileLoadsPerProjectile; -+ private static void projectileLoading() { -+ maxProjectileLoadsPerTick = getInt("projectile.max-loads-per-tick", 10, "Controls how many chunks are allowed", "to be sync loaded by projectiles in a tick."); -+ maxProjectileLoadsPerProjectile = getInt("projectile.max-loads-per-projectile", 10, "Controls how many chunks a projectile", "can load in its lifetime before it gets", "automatically removed."); -+ -+ setComment("projectile", "Optimizes projectile settings"); -+ } -+ -+ -+ public static boolean dearEnabled; -+ public static int startDistance; -+ public static int startDistanceSquared; -+ public static int maximumActivationPrio; -+ public static int activationDistanceMod; -+ -+ private static void dynamicActivationOfBrains() throws IOException { -+ dearEnabled = getBoolean("dab.enabled", "activation-range.enabled", true); -+ startDistance = getInt("dab.start-distance", "activation-range.start-distance", 12, -+ "This value determines how far away an entity has to be", -+ "from the player to start being effected by DEAR."); -+ startDistanceSquared = startDistance * startDistance; -+ maximumActivationPrio = getInt("dab.max-tick-freq", "activation-range.max-tick-freq", 20, -+ "This value defines how often in ticks, the furthest entity", -+ "will get their pathfinders and behaviors ticked. 20 = 1s"); -+ activationDistanceMod = getInt("dab.activation-dist-mod", "activation-range.activation-dist-mod", 8, -+ "This value defines how much distance modifies an entity's", -+ "tick frequency. freq = (distanceToPlayer^2) / (2^value)", -+ "If you want further away entities to tick less often, use 7.", -+ "If you want further away entities to tick more often, try 9."); -+ -+ for (EntityType entityType : Registry.ENTITY_TYPE) { -+ entityType.dabEnabled = true; // reset all, before setting the ones to true -+ } -+ getStringList("dab.blacklisted-entities", "activation-range.blacklisted-entities", Collections.emptyList(), "A list of entities to ignore for activation") -+ .forEach(name -> EntityType.byString(name).ifPresentOrElse(entityType -> { -+ entityType.dabEnabled = false; -+ }, () -> MinecraftServer.LOGGER.warn("Unknown entity \"" + name + "\""))); -+ -+ setComment("dab", "Optimizes entity brains when", "they're far away from the player"); -+ } -+ -+ public static Map projectileTimeouts; -+ private static void projectileTimeouts() { -+ // Set some defaults -+ getInt("entity_timeouts.SNOWBALL", -1); -+ getInt("entity_timeouts.LLAMA_SPIT", -1); -+ setComment("entity_timeouts", -+ "These values define a entity's maximum lifespan. If an", -+ "entity is in this list and it has survived for longer than", -+ "that number of ticks, then it will be removed. Setting a value to", -+ "-1 disables this feature."); -+ -+ for (EntityType entityType : Registry.ENTITY_TYPE) { -+ String type = EntityType.getKey(entityType).getPath().toUpperCase(Locale.ROOT); -+ entityType.ttl = config.getInt("entity_timeouts." + type, -1); -+ } -+ } -+ -+ public static boolean throttleInactiveGoalSelectorTick; -+ private static void inactiveGoalSelectorThrottle() { -+ getBoolean("inactive-goal-selector-throttle", "inactive-goal-selector-disable", true, -+ "Throttles the AI goal selector in entity inactive ticks.", -+ "This can improve performance by a few percent, but has minor gameplay implications."); -+ } -+ -+ public static URI profileWebUrl; -+ private static void profilerOptions() { -+ profileWebUrl = URI.create(getString("flare.url", "https://flare.airplane.gg", "Sets the server to use for profiles.")); -+ -+ setComment("flare", "Configures Flare, the built-in profiler"); -+ } -+ -+ -+ public static String accessToken; -+ private static void airplaneWebServices() { -+ accessToken = getString("web-services.token", ""); -+ // todo lookup token (off-thread) and let users know if their token is valid -+ if (accessToken.length() > 0) { -+ gg.pufferfish.pufferfish.flare.FlareSetup.init(); // Pufferfish -+ SimpleCommandMap commandMap = MinecraftServer.getServer().server.getCommandMap(); -+ if (commandMap.getCommand("flare") == null) { -+ commandMap.register("flare", "Pufferfish", new FlareCommand()); -+ } -+ } -+ -+ setComment("web-services", "Options for connecting to Pufferfish/Airplane's online utilities"); -+ -+ } -+ -+ -+ public static boolean disableMethodProfiler; -+ private static void miscSettings() { -+ disableMethodProfiler = getBoolean("misc.disable-method-profiler", true); -+ setComment("misc", "Settings for things that don't belong elsewhere"); -+ } -+ -+} -diff --git a/src/main/java/gg/pufferfish/pufferfish/PufferfishLogger.java b/src/main/java/gg/pufferfish/pufferfish/PufferfishLogger.java -new file mode 100644 -index 0000000000000000000000000000000000000000..53f2df00c6809618a9ee3d2ea72e85e8052fbcf1 ---- /dev/null -+++ b/src/main/java/gg/pufferfish/pufferfish/PufferfishLogger.java -@@ -0,0 +1,16 @@ -+package gg.pufferfish.pufferfish; -+ -+import java.util.logging.Level; -+import java.util.logging.Logger; -+import org.bukkit.Bukkit; -+ -+public class PufferfishLogger extends Logger { -+ public static final PufferfishLogger LOGGER = new PufferfishLogger(); -+ -+ private PufferfishLogger() { -+ super("Pufferfish", null); -+ -+ setParent(Bukkit.getLogger()); -+ setLevel(Level.ALL); -+ } -+} -diff --git a/src/main/java/gg/pufferfish/pufferfish/PufferfishVersionFetcher.java b/src/main/java/gg/pufferfish/pufferfish/PufferfishVersionFetcher.java -new file mode 100644 -index 0000000000000000000000000000000000000000..461022af9ad85fe00329678f0f61d684d291c628 ---- /dev/null -+++ b/src/main/java/gg/pufferfish/pufferfish/PufferfishVersionFetcher.java -@@ -0,0 +1,136 @@ -+package gg.pufferfish.pufferfish; -+ -+import static net.kyori.adventure.text.Component.text; -+import static net.kyori.adventure.text.format.NamedTextColor.GREEN; -+import static net.kyori.adventure.text.format.NamedTextColor.RED; -+ -+import com.destroystokyo.paper.VersionHistoryManager; -+import com.destroystokyo.paper.util.VersionFetcher; -+import com.google.gson.Gson; -+import com.google.gson.JsonObject; -+import java.io.IOException; -+import java.net.URI; -+import java.net.http.HttpClient; -+import java.net.http.HttpRequest; -+import java.net.http.HttpResponse; -+import java.nio.charset.StandardCharsets; -+import java.util.concurrent.TimeUnit; -+import java.util.logging.Level; -+import java.util.logging.Logger; -+import net.kyori.adventure.text.Component; -+import net.kyori.adventure.text.JoinConfiguration; -+import net.kyori.adventure.text.format.NamedTextColor; -+import net.kyori.adventure.text.format.TextDecoration; -+import org.bukkit.craftbukkit.CraftServer; -+import org.jetbrains.annotations.NotNull; -+import org.jetbrains.annotations.Nullable; -+ -+public class PufferfishVersionFetcher implements VersionFetcher { -+ -+ private static final Logger LOGGER = Logger.getLogger("PufferfishVersionFetcher"); -+ private static final HttpClient client = HttpClient.newHttpClient(); -+ -+ private static final URI JENKINS_URI = URI.create("https://ci.pufferfish.host/job/Pufferfish-1.18/lastSuccessfulBuild/buildNumber"); -+ private static final String GITHUB_FORMAT = "https://api.github.com/repos/pufferfish-gg/Pufferfish/compare/ver/1.18...%s"; -+ -+ private static final HttpResponse.BodyHandler JSON_OBJECT_BODY_HANDLER = responseInfo -> HttpResponse.BodySubscribers -+ .mapping( -+ HttpResponse.BodySubscribers.ofString(StandardCharsets.UTF_8), -+ string -> new Gson().fromJson(string, JsonObject.class) -+ ); -+ -+ @Override -+ public long getCacheTime() { -+ return TimeUnit.MINUTES.toMillis(30); -+ } -+ -+ @Override -+ public @NotNull Component getVersionMessage(final @NotNull String serverVersion) { -+ final String[] parts = CraftServer.class.getPackage().getImplementationVersion().split("-"); -+ @NotNull Component component; -+ -+ if (parts.length != 3) { -+ component = text("Unknown server version.", RED); -+ } else { -+ final String versionString = parts[2]; -+ -+ try { -+ component = this.fetchJenkinsVersion(Integer.parseInt(versionString)); -+ } catch (NumberFormatException e) { -+ component = this.fetchGithubVersion(versionString.substring(1, versionString.length() - 1)); -+ } -+ } -+ -+ final @Nullable Component history = this.getHistory(); -+ return history != null ? Component -+ .join(JoinConfiguration.noSeparators(), component, Component.newline(), this.getHistory()) : component; -+ } -+ -+ private @NotNull Component fetchJenkinsVersion(final int versionNumber) { -+ final HttpRequest request = HttpRequest.newBuilder(JENKINS_URI).build(); -+ try { -+ final HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); -+ if (response.statusCode() != 200) { -+ return text("Received invalid status code (" + response.statusCode() + ") from server.", RED); -+ } -+ -+ int latestVersionNumber; -+ try { -+ latestVersionNumber = Integer.parseInt(response.body()); -+ } catch (NumberFormatException e) { -+ LOGGER.log(Level.WARNING, "Received invalid response from Jenkins \"" + response.body() + "\"."); -+ return text("Received invalid response from server.", RED); -+ } -+ -+ final int versionDiff = latestVersionNumber - versionNumber; -+ return this.getResponseMessage(versionDiff); -+ } catch (IOException | InterruptedException e) { -+ LOGGER.log(Level.WARNING, "Failed to look up version from Jenkins", e); -+ return text("Failed to retrieve version from server.", RED); -+ } -+ } -+ -+ // Based off code contributed by Techcable in Paper/GH-65 -+ private @NotNull Component fetchGithubVersion(final @NotNull String hash) { -+ final URI uri = URI.create(String.format(GITHUB_FORMAT, hash)); -+ final HttpRequest request = HttpRequest.newBuilder(uri).build(); -+ try { -+ final HttpResponse response = client.send(request, JSON_OBJECT_BODY_HANDLER); -+ if (response.statusCode() != 200) { -+ return text("Received invalid status code (" + response.statusCode() + ") from server.", RED); -+ } -+ -+ final JsonObject obj = response.body(); -+ final int versionDiff = obj.get("behind_by").getAsInt(); -+ -+ return this.getResponseMessage(versionDiff); -+ } catch (IOException | InterruptedException e) { -+ LOGGER.log(Level.WARNING, "Failed to look up version from GitHub", e); -+ return text("Failed to retrieve version from server.", RED); -+ } -+ } -+ -+ private @NotNull Component getResponseMessage(final int versionDiff) { -+ return switch (Math.max(-1, Math.min(1, versionDiff))) { -+ case -1 -> text("You are running an unsupported version of Pufferfish.", RED); -+ case 0 -> text("You are on the latest version!", GREEN); -+ default -> text("You are running " + versionDiff + " version" + (versionDiff == 1 ? "" : "s") + " beyond. " + -+ "Please update your server when possible to maintain stability, security, and receive the latest optimizations.", -+ RED); -+ }; -+ } -+ -+ private @Nullable Component getHistory() { -+ final VersionHistoryManager.VersionData data = VersionHistoryManager.INSTANCE.getVersionData(); -+ if (data == null) { -+ return null; -+ } -+ -+ final String oldVersion = data.getOldVersion(); -+ if (oldVersion == null) { -+ return null; -+ } -+ -+ return Component.text("Previous version: " + oldVersion, NamedTextColor.GRAY, TextDecoration.ITALIC); -+ } -+} -\ No newline at end of file -diff --git a/src/main/java/gg/pufferfish/pufferfish/compat/ServerConfigurations.java b/src/main/java/gg/pufferfish/pufferfish/compat/ServerConfigurations.java -new file mode 100644 -index 0000000000000000000000000000000000000000..f0ec1f3944c00d38537f6f3b3f13236b22562458 ---- /dev/null -+++ b/src/main/java/gg/pufferfish/pufferfish/compat/ServerConfigurations.java -@@ -0,0 +1,78 @@ -+package gg.pufferfish.pufferfish.compat; -+ -+import co.aikar.timings.TimingsManager; -+import com.google.common.io.Files; -+import org.bukkit.configuration.InvalidConfigurationException; -+import org.bukkit.configuration.file.YamlConfiguration; -+ -+import java.io.ByteArrayOutputStream; -+import java.io.File; -+import java.io.FileInputStream; -+import java.io.IOException; -+import java.nio.charset.StandardCharsets; -+import java.util.Arrays; -+import java.util.HashMap; -+import java.util.List; -+import java.util.Map; -+import java.util.Properties; -+import java.util.stream.Collectors; -+ -+public class ServerConfigurations { -+ -+ public static final String[] configurationFiles = new String[]{ -+ "server.properties", -+ "bukkit.yml", -+ "spigot.yml", -+ "paper.yml", -+ "pufferfish.yml" -+ }; -+ -+ public static Map getCleanCopies() throws IOException { -+ Map files = new HashMap<>(configurationFiles.length); -+ for (String file : configurationFiles) { -+ files.put(file, getCleanCopy(file)); -+ } -+ return files; -+ } -+ -+ public static String getCleanCopy(String configName) throws IOException { -+ File file = new File(configName); -+ List hiddenConfigs = TimingsManager.hiddenConfigs; -+ -+ switch (Files.getFileExtension(configName)) { -+ case "properties": { -+ Properties properties = new Properties(); -+ try (FileInputStream inputStream = new FileInputStream(file)) { -+ properties.load(inputStream); -+ } -+ for (String hiddenConfig : hiddenConfigs) { -+ properties.remove(hiddenConfig); -+ } -+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); -+ properties.store(outputStream, ""); -+ return Arrays.stream(outputStream.toString() -+ .split("\n")) -+ .filter(line -> !line.startsWith("#")) -+ .collect(Collectors.joining("\n")); -+ } -+ case "yml": { -+ YamlConfiguration configuration = new YamlConfiguration(); -+ try { -+ configuration.load(file); -+ } catch (InvalidConfigurationException e) { -+ throw new IOException(e); -+ } -+ configuration.options().header(null); -+ for (String key : configuration.getKeys(true)) { -+ if (hiddenConfigs.contains(key)) { -+ configuration.set(key, null); -+ } -+ } -+ return configuration.saveToString(); -+ } -+ default: -+ throw new IllegalArgumentException("Bad file type " + configName); -+ } -+ } -+ -+} -diff --git a/src/main/java/gg/pufferfish/pufferfish/flare/CustomCategories.java b/src/main/java/gg/pufferfish/pufferfish/flare/CustomCategories.java -new file mode 100644 -index 0000000000000000000000000000000000000000..401b42e29bccb5251684062f10b2e0f8b091bc95 ---- /dev/null -+++ b/src/main/java/gg/pufferfish/pufferfish/flare/CustomCategories.java -@@ -0,0 +1,8 @@ -+package gg.pufferfish.pufferfish.flare; -+ -+import co.technove.flare.live.category.GraphCategory; -+ -+public class CustomCategories { -+ public static final GraphCategory MC_PERF = new GraphCategory("MC Performance"); -+ public static final GraphCategory ENTITIES_AND_CHUNKS = new GraphCategory("Entities & Chunks"); -+} -diff --git a/src/main/java/gg/pufferfish/pufferfish/flare/FlareCommand.java b/src/main/java/gg/pufferfish/pufferfish/flare/FlareCommand.java -new file mode 100644 -index 0000000000000000000000000000000000000000..3785d1512eb650f91d58903672c059e7449598fc ---- /dev/null -+++ b/src/main/java/gg/pufferfish/pufferfish/flare/FlareCommand.java -@@ -0,0 +1,136 @@ -+package gg.pufferfish.pufferfish.flare; -+ -+import co.technove.flare.exceptions.UserReportableException; -+import co.technove.flare.internal.profiling.ProfileType; -+import gg.pufferfish.pufferfish.PufferfishConfig; -+import net.kyori.adventure.text.Component; -+import net.kyori.adventure.text.event.ClickEvent; -+import net.kyori.adventure.text.format.NamedTextColor; -+import net.kyori.adventure.text.format.TextColor; -+import net.kyori.adventure.text.format.TextDecoration; -+import net.minecraft.server.MinecraftServer; -+import org.apache.logging.log4j.Level; -+import org.bukkit.Bukkit; -+import org.bukkit.command.Command; -+import org.bukkit.command.CommandSender; -+import org.bukkit.command.ConsoleCommandSender; -+import org.bukkit.craftbukkit.scheduler.MinecraftInternalPlugin; -+import org.bukkit.util.StringUtil; -+import org.jetbrains.annotations.NotNull; -+ -+import java.time.Duration; -+import java.util.ArrayList; -+import java.util.Collections; -+import java.util.List; -+ -+public class FlareCommand extends Command { -+ -+ private static final String BASE_URL = "https://blog.airplane.gg/flare-tutorial/#setting-the-access-token"; -+ private static final TextColor HEX = TextColor.fromHexString("#e3eaea"); -+ private static final Component PREFIX = Component.text() -+ .append(Component.text("Flare ✈") -+ .color(TextColor.fromHexString("#6a7eda")) -+ .decoration(TextDecoration.BOLD, true) -+ .append(Component.text(" ", HEX) -+ .decoration(TextDecoration.BOLD, false))) -+ .asComponent(); -+ -+ public FlareCommand() { -+ super("flare", "Profile your server with Flare", "/flare", Collections.singletonList("profile")); -+ this.setPermission("airplane.flare"); -+ } -+ -+ @Override -+ public boolean execute(@NotNull CommandSender sender, @NotNull String commandLabel, String @NotNull [] args) { -+ if (!testPermission(sender)) return true; -+ if (PufferfishConfig.accessToken.length() == 0) { -+ Component clickable = Component.text(BASE_URL, HEX, TextDecoration.UNDERLINED).clickEvent(ClickEvent.clickEvent(ClickEvent.Action.OPEN_URL, BASE_URL)); -+ -+ sender.sendMessage(PREFIX.append(Component.text("Flare currently requires an access token to use. To learn more, visit ").color(HEX).append(clickable))); -+ return true; -+ } -+ -+ if (!FlareSetup.isSupported()) { -+ sender.sendMessage(PREFIX.append( -+ Component.text("Profiling is not supported in this environment, check your startup logs for the error.", NamedTextColor.RED))); -+ return true; -+ } -+ if (ProfilingManager.isProfiling()) { -+ if (args.length == 1 && args[0].equalsIgnoreCase("status")) { -+ sender.sendMessage(PREFIX.append(Component.text("Current profile has been ran for " + ProfilingManager.getTimeRan().toString(), HEX))); -+ return true; -+ } -+ if (ProfilingManager.stop()) { -+ if (!(sender instanceof ConsoleCommandSender)) { -+ sender.sendMessage(PREFIX.append(Component.text("Profiling has been stopped.", HEX))); -+ } -+ } else { -+ sender.sendMessage(PREFIX.append(Component.text("Profiling has already been stopped.", HEX))); -+ } -+ } else { -+ ProfileType profileType = ProfileType.ITIMER; -+ if (args.length > 0) { -+ try { -+ profileType = ProfileType.valueOf(args[0].toUpperCase()); -+ } catch (Exception e) { -+ sender.sendMessage(PREFIX.append(Component -+ .text("Invalid profile type ", HEX) -+ .append(Component.text(args[0], HEX, TextDecoration.BOLD) -+ .append(Component.text("!", HEX))) -+ )); -+ } -+ } -+ ProfileType finalProfileType = profileType; -+ Bukkit.getScheduler().runTaskAsynchronously(new MinecraftInternalPlugin(), () -> { -+ try { -+ if (ProfilingManager.start(finalProfileType)) { -+ if (!(sender instanceof ConsoleCommandSender)) { -+ sender.sendMessage(PREFIX.append(Component -+ .text("Flare has been started: " + ProfilingManager.getProfilingUri(), HEX) -+ .clickEvent(ClickEvent.openUrl(ProfilingManager.getProfilingUri())) -+ )); -+ sender.sendMessage(PREFIX.append(Component.text(" Run /" + commandLabel + " to stop the Flare.", HEX))); -+ } -+ } else { -+ sender.sendMessage(PREFIX.append(Component -+ .text("Flare has already been started: " + ProfilingManager.getProfilingUri(), HEX) -+ .clickEvent(ClickEvent.openUrl(ProfilingManager.getProfilingUri())) -+ )); -+ } -+ } catch (UserReportableException e) { -+ sender.sendMessage(Component.text("Flare failed to start: " + e.getUserError(), NamedTextColor.RED)); -+ if (e.getCause() != null) { -+ MinecraftServer.LOGGER.warn("Flare failed to start", e); -+ } -+ } -+ }); -+ } -+ return true; -+ } -+ -+ @Override -+ public @NotNull List tabComplete(@NotNull CommandSender sender, @NotNull String alias, String @NotNull [] args) throws IllegalArgumentException { -+ List list = new ArrayList<>(); -+ if (ProfilingManager.isProfiling()) { -+ if (args.length == 1) { -+ String lastWord = args[0]; -+ if (StringUtil.startsWithIgnoreCase("status", lastWord)) { -+ list.add("status"); -+ } -+ if (StringUtil.startsWithIgnoreCase("stop", lastWord)) { -+ list.add("stop"); -+ } -+ } -+ } else { -+ if (args.length <= 1) { -+ String lastWord = args.length == 0 ? "" : args[0]; -+ for (ProfileType value : ProfileType.values()) { -+ if (StringUtil.startsWithIgnoreCase(value.getInternalName(), lastWord)) { -+ list.add(value.name().toLowerCase()); -+ } -+ } -+ } -+ } -+ return list; -+ } -+} -diff --git a/src/main/java/gg/pufferfish/pufferfish/flare/FlareSetup.java b/src/main/java/gg/pufferfish/pufferfish/flare/FlareSetup.java -new file mode 100644 -index 0000000000000000000000000000000000000000..cd22e4dcc8b7b57b10a95ef084637249a98e524f ---- /dev/null -+++ b/src/main/java/gg/pufferfish/pufferfish/flare/FlareSetup.java -@@ -0,0 +1,33 @@ -+package gg.pufferfish.pufferfish.flare; -+ -+import co.technove.flare.FlareInitializer; -+import co.technove.flare.internal.profiling.InitializationException; -+import net.minecraft.server.MinecraftServer; -+import org.apache.logging.log4j.Level; -+ -+public class FlareSetup { -+ -+ private static boolean initialized = false; -+ private static boolean supported = false; -+ -+ public static void init() { -+ if (initialized) { -+ return; -+ } -+ -+ initialized = true; -+ try { -+ for (String warning : FlareInitializer.initialize()) { -+ MinecraftServer.LOGGER.warn("Flare warning: " + warning); -+ } -+ supported = true; -+ } catch (InitializationException e) { -+ MinecraftServer.LOGGER.warn("Failed to enable Flare:", e); -+ } -+ } -+ -+ public static boolean isSupported() { -+ return supported; -+ } -+ -+} -diff --git a/src/main/java/gg/pufferfish/pufferfish/flare/PluginLookup.java b/src/main/java/gg/pufferfish/pufferfish/flare/PluginLookup.java -new file mode 100644 -index 0000000000000000000000000000000000000000..74aab5eb4b54ffbaf19b8976ffb8ca4a64584006 ---- /dev/null -+++ b/src/main/java/gg/pufferfish/pufferfish/flare/PluginLookup.java -@@ -0,0 +1,44 @@ -+package gg.pufferfish.pufferfish.flare; -+ -+import com.google.common.cache.Cache; -+import com.google.common.cache.CacheBuilder; -+import org.bukkit.Bukkit; -+import org.bukkit.plugin.Plugin; -+import org.bukkit.plugin.java.PluginClassLoader; -+ -+import java.util.Optional; -+import java.util.concurrent.TimeUnit; -+ -+public class PluginLookup { -+ private static final Cache pluginNameCache = CacheBuilder.newBuilder() -+ .expireAfterAccess(1, TimeUnit.MINUTES) -+ .maximumSize(1024) -+ .build(); -+ -+ public static Optional getPluginForClass(String name) { -+ if (name.startsWith("net.minecraft") || name.startsWith("java.") || name.startsWith("com.mojang") || -+ name.startsWith("com.google") || name.startsWith("it.unimi") || name.startsWith("sun")) { -+ return Optional.empty(); -+ } -+ -+ String existing = pluginNameCache.getIfPresent(name); -+ if (existing != null) { -+ return Optional.ofNullable(existing.isEmpty() ? null : existing); -+ } -+ -+ String newValue = ""; -+ -+ for (Plugin plugin : Bukkit.getPluginManager().getPlugins()) { -+ ClassLoader classLoader = plugin.getClass().getClassLoader(); -+ if (classLoader instanceof PluginClassLoader) { -+ if (((PluginClassLoader) classLoader)._airplane_hasClass(name)) { -+ newValue = plugin.getName(); -+ break; -+ } -+ } -+ } -+ -+ pluginNameCache.put(name, newValue); -+ return Optional.ofNullable(newValue.isEmpty() ? null : newValue); -+ } -+} -diff --git a/src/main/java/gg/pufferfish/pufferfish/flare/ProfilingManager.java b/src/main/java/gg/pufferfish/pufferfish/flare/ProfilingManager.java -new file mode 100644 -index 0000000000000000000000000000000000000000..e3f76eb11a261c3347f0cd89b5da309bc2dc82f9 ---- /dev/null -+++ b/src/main/java/gg/pufferfish/pufferfish/flare/ProfilingManager.java -@@ -0,0 +1,151 @@ -+package gg.pufferfish.pufferfish.flare; -+ -+import co.technove.flare.Flare; -+import co.technove.flare.FlareAuth; -+import co.technove.flare.FlareBuilder; -+import co.technove.flare.exceptions.UserReportableException; -+import co.technove.flare.internal.profiling.ProfileType; -+import gg.pufferfish.pufferfish.PufferfishConfig; -+import gg.pufferfish.pufferfish.PufferfishLogger; -+import gg.pufferfish.pufferfish.compat.ServerConfigurations; -+import gg.pufferfish.pufferfish.flare.collectors.GCEventCollector; -+import gg.pufferfish.pufferfish.flare.collectors.StatCollector; -+import gg.pufferfish.pufferfish.flare.collectors.TPSCollector; -+import gg.pufferfish.pufferfish.flare.collectors.WorldCountCollector; -+import org.bukkit.Bukkit; -+import org.bukkit.craftbukkit.scheduler.MinecraftInternalPlugin; -+import org.bukkit.scheduler.BukkitTask; -+import oshi.SystemInfo; -+import oshi.hardware.CentralProcessor; -+import oshi.hardware.GlobalMemory; -+import oshi.hardware.HardwareAbstractionLayer; -+import oshi.hardware.VirtualMemory; -+import oshi.software.os.OperatingSystem; -+ -+import java.io.IOException; -+import java.net.URI; -+import java.time.Duration; -+import java.util.Objects; -+import java.util.logging.Level; -+ -+public class ProfilingManager { -+ -+ private static Flare currentFlare; -+ private static BukkitTask currentTask = null; -+ -+ public static synchronized boolean isProfiling() { -+ return currentFlare != null && currentFlare.isRunning(); -+ } -+ -+ public static synchronized String getProfilingUri() { -+ return Objects.requireNonNull(currentFlare).getURI().map(URI::toString).orElse("Flare is not running"); -+ } -+ -+ public static Duration getTimeRan() { -+ Flare flare = currentFlare; // copy reference so no need to sync -+ if (flare == null) { -+ return Duration.ofMillis(0); -+ } -+ return flare.getCurrentDuration(); -+ } -+ -+ public static synchronized boolean start(ProfileType profileType) throws UserReportableException { -+ if (currentFlare != null && !currentFlare.isRunning()) { -+ currentFlare = null; // errored out -+ } -+ if (isProfiling()) { -+ return false; -+ } -+ if (Bukkit.isPrimaryThread()) { -+ throw new UserReportableException("Profiles should be started off-thread"); -+ } -+ -+ try { -+ OperatingSystem os = new SystemInfo().getOperatingSystem(); -+ -+ SystemInfo systemInfo = new SystemInfo(); -+ HardwareAbstractionLayer hardware = systemInfo.getHardware(); -+ -+ CentralProcessor processor = hardware.getProcessor(); -+ CentralProcessor.ProcessorIdentifier processorIdentifier = processor.getProcessorIdentifier(); -+ -+ GlobalMemory memory = hardware.getMemory(); -+ VirtualMemory virtualMemory = memory.getVirtualMemory(); -+ -+ FlareBuilder builder = new FlareBuilder() -+ .withProfileType(profileType) -+ .withMemoryProfiling(true) -+ .withAuth(FlareAuth.fromTokenAndUrl(PufferfishConfig.accessToken, PufferfishConfig.profileWebUrl)) -+ -+ .withFiles(ServerConfigurations.getCleanCopies()) -+ .withVersion("Primary Version", Bukkit.getVersion()) -+ .withVersion("Bukkit Version", Bukkit.getBukkitVersion()) -+ .withVersion("Minecraft Version", Bukkit.getMinecraftVersion()) -+ -+ .withGraphCategories(CustomCategories.ENTITIES_AND_CHUNKS, CustomCategories.MC_PERF) -+ .withCollectors(new TPSCollector(), new WorldCountCollector(), new GCEventCollector(), new StatCollector()) -+ .withClassIdentifier(PluginLookup::getPluginForClass) -+ -+ .withHardware(new FlareBuilder.HardwareBuilder() -+ .setCoreCount(processor.getPhysicalProcessorCount()) -+ .setThreadCount(processor.getLogicalProcessorCount()) -+ .setCpuModel(processorIdentifier.getName()) -+ .setCpuFrequency(processor.getMaxFreq()) -+ -+ .setTotalMemory(memory.getTotal()) -+ .setTotalSwap(virtualMemory.getSwapTotal()) -+ .setTotalVirtual(virtualMemory.getVirtualMax()) -+ ) -+ -+ .withOperatingSystem(new FlareBuilder.OperatingSystemBuilder() -+ .setManufacturer(os.getManufacturer()) -+ .setFamily(os.getFamily()) -+ .setVersion(os.getVersionInfo().toString()) -+ .setBitness(os.getBitness()) -+ ); -+ -+ currentFlare = builder.build(); -+ } catch (IOException e) { -+ PufferfishLogger.LOGGER.log(Level.WARNING, "Failed to read configuration files:", e); -+ throw new UserReportableException("Failed to load configuration files, check logs for further details."); -+ } -+ -+ try { -+ currentFlare.start(); -+ } catch (IllegalStateException e) { -+ PufferfishLogger.LOGGER.log(Level.WARNING, "Error starting Flare:", e); -+ throw new UserReportableException("Failed to start Flare, check logs for further details."); -+ } -+ -+ currentTask = Bukkit.getScheduler().runTaskLater(new MinecraftInternalPlugin(), ProfilingManager::stop, 20 * 60 * 15); -+ PufferfishLogger.LOGGER.log(Level.INFO, "Flare has been started: " + getProfilingUri()); -+ return true; -+ } -+ -+ public static synchronized boolean stop() { -+ if (!isProfiling()) { -+ return false; -+ } -+ if (!currentFlare.isRunning()) { -+ currentFlare = null; -+ return true; -+ } -+ PufferfishLogger.LOGGER.log(Level.INFO, "Flare has been stopped: " + getProfilingUri()); -+ try { -+ currentFlare.stop(); -+ } catch (IllegalStateException e) { -+ PufferfishLogger.LOGGER.log(Level.WARNING, "Error occurred stopping Flare", e); -+ } -+ currentFlare = null; -+ -+ try { -+ currentTask.cancel(); -+ } catch (Throwable t) { -+ PufferfishLogger.LOGGER.log(Level.WARNING, "Error occurred stopping Flare", t); -+ } -+ -+ currentTask = null; -+ return true; -+ } -+ -+} -diff --git a/src/main/java/gg/pufferfish/pufferfish/flare/collectors/GCEventCollector.java b/src/main/java/gg/pufferfish/pufferfish/flare/collectors/GCEventCollector.java -new file mode 100644 -index 0000000000000000000000000000000000000000..d426575c669020f369960107da1e2de2f11f082f ---- /dev/null -+++ b/src/main/java/gg/pufferfish/pufferfish/flare/collectors/GCEventCollector.java -@@ -0,0 +1,66 @@ -+package gg.pufferfish.pufferfish.flare.collectors; -+ -+import co.technove.flare.Flare; -+import co.technove.flare.internal.FlareInternal; -+import co.technove.flare.live.CollectorData; -+import co.technove.flare.live.EventCollector; -+import co.technove.flare.live.LiveEvent; -+import co.technove.flare.live.category.GraphCategory; -+import co.technove.flare.live.formatter.DataFormatter; -+import com.google.common.collect.ImmutableMap; -+import com.sun.management.GarbageCollectionNotificationInfo; -+ -+import javax.management.ListenerNotFoundException; -+import javax.management.Notification; -+import javax.management.NotificationEmitter; -+import javax.management.NotificationListener; -+import javax.management.openmbean.CompositeData; -+import java.lang.management.GarbageCollectorMXBean; -+import java.lang.management.ManagementFactory; -+ -+public class GCEventCollector extends EventCollector implements NotificationListener { -+ -+ private static final CollectorData MINOR_GC = new CollectorData("builtin:gc:minor", "Minor GC", "A small pause in the program to allow Garbage Collection to run.", DataFormatter.MILLISECONDS, GraphCategory.SYSTEM); -+ private static final CollectorData MAJOR_GC = new CollectorData("builtin:gc:major", "Major GC", "A large pause in the program to allow Garbage Collection to run.", DataFormatter.MILLISECONDS, GraphCategory.SYSTEM); -+ private static final CollectorData UNKNOWN_GC = new CollectorData("builtin:gc:generic", "Major GC", "A run of the Garbage Collection.", DataFormatter.MILLISECONDS, GraphCategory.SYSTEM); -+ -+ public GCEventCollector() { -+ super(MINOR_GC, MAJOR_GC, UNKNOWN_GC); -+ } -+ -+ private static CollectorData fromString(String string) { -+ if (string.endsWith("minor GC")) { -+ return MINOR_GC; -+ } else if (string.endsWith("major GC")) { -+ return MAJOR_GC; -+ } -+ return UNKNOWN_GC; -+ } -+ -+ @Override -+ public void start(Flare flare) { -+ for (GarbageCollectorMXBean garbageCollectorBean : ManagementFactory.getGarbageCollectorMXBeans()) { -+ NotificationEmitter notificationEmitter = (NotificationEmitter) garbageCollectorBean; -+ notificationEmitter.addNotificationListener(this, null, null); -+ } -+ } -+ -+ @Override -+ public void stop(Flare flare) { -+ for (GarbageCollectorMXBean garbageCollectorBean : ManagementFactory.getGarbageCollectorMXBeans()) { -+ NotificationEmitter notificationEmitter = (NotificationEmitter) garbageCollectorBean; -+ try { -+ notificationEmitter.removeNotificationListener(this); -+ } catch (ListenerNotFoundException e) { -+ } -+ } -+ } -+ -+ @Override -+ public void handleNotification(Notification notification, Object o) { -+ if (notification.getType().equals(GarbageCollectionNotificationInfo.GARBAGE_COLLECTION_NOTIFICATION)) { -+ GarbageCollectionNotificationInfo gcInfo = GarbageCollectionNotificationInfo.from((CompositeData) notification.getUserData()); -+ reportEvent(new LiveEvent(fromString(gcInfo.getGcAction()), System.currentTimeMillis(), (int) gcInfo.getGcInfo().getDuration(), ImmutableMap.of())); -+ } -+ } -+} -diff --git a/src/main/java/gg/pufferfish/pufferfish/flare/collectors/StatCollector.java b/src/main/java/gg/pufferfish/pufferfish/flare/collectors/StatCollector.java -new file mode 100644 -index 0000000000000000000000000000000000000000..a22c6dbae53667e4c72464fa27153aee30c7946e ---- /dev/null -+++ b/src/main/java/gg/pufferfish/pufferfish/flare/collectors/StatCollector.java -@@ -0,0 +1,41 @@ -+package gg.pufferfish.pufferfish.flare.collectors; -+ -+import co.technove.flare.live.CollectorData; -+import co.technove.flare.live.LiveCollector; -+import co.technove.flare.live.category.GraphCategory; -+import co.technove.flare.live.formatter.DataFormatter; -+import com.sun.management.OperatingSystemMXBean; -+import oshi.SystemInfo; -+import oshi.hardware.CentralProcessor; -+ -+import java.lang.management.ManagementFactory; -+import java.time.Duration; -+ -+public class StatCollector extends LiveCollector { -+ -+ private static final CollectorData CPU = new CollectorData("builtin:stat:cpu", "CPU Load", "The total amount of CPU usage across all cores.", DataFormatter.PERCENT, GraphCategory.SYSTEM); -+ private static final CollectorData CPU_PROCESS = new CollectorData("builtin:stat:cpu_process", "Process CPU", "The amount of CPU being used by this process.", DataFormatter.PERCENT, GraphCategory.SYSTEM); -+ private static final CollectorData MEMORY = new CollectorData("builtin:stat:memory_used", "Memory", "The amount of memory being used currently.", DataFormatter.BYTES, GraphCategory.SYSTEM); -+ private static final CollectorData MEMORY_TOTAL = new CollectorData("builtin:stat:memory_total", "Memory Total", "The total amount of memory allocated.", DataFormatter.BYTES, GraphCategory.SYSTEM); -+ -+ private final OperatingSystemMXBean bean; -+ private final CentralProcessor processor; -+ -+ public StatCollector() { -+ super(CPU, CPU_PROCESS, MEMORY, MEMORY_TOTAL); -+ this.interval = Duration.ofSeconds(5); -+ -+ this.bean = (OperatingSystemMXBean) ManagementFactory.getOperatingSystemMXBean(); -+ this.processor = new SystemInfo().getHardware().getProcessor(); -+ } -+ -+ @Override -+ public void run() { -+ Runtime runtime = Runtime.getRuntime(); -+ -+ this.report(CPU, this.processor.getSystemLoadAverage(1)[0] / 100); // percentage -+ this.report(CPU_PROCESS, this.bean.getProcessCpuLoad()); -+ this.report(MEMORY, runtime.totalMemory() - runtime.freeMemory()); -+ this.report(MEMORY_TOTAL, runtime.totalMemory()); -+ } -+} -diff --git a/src/main/java/gg/pufferfish/pufferfish/flare/collectors/TPSCollector.java b/src/main/java/gg/pufferfish/pufferfish/flare/collectors/TPSCollector.java -new file mode 100644 -index 0000000000000000000000000000000000000000..40447d00aefb5ffedb8a2ee87155a04088f0649f ---- /dev/null -+++ b/src/main/java/gg/pufferfish/pufferfish/flare/collectors/TPSCollector.java -@@ -0,0 +1,31 @@ -+package gg.pufferfish.pufferfish.flare.collectors; -+ -+import co.technove.flare.live.CollectorData; -+import co.technove.flare.live.LiveCollector; -+import co.technove.flare.live.formatter.SuffixFormatter; -+import gg.pufferfish.pufferfish.flare.CustomCategories; -+import net.minecraft.server.MinecraftServer; -+import org.bukkit.Bukkit; -+ -+import java.time.Duration; -+import java.util.Arrays; -+ -+public class TPSCollector extends LiveCollector { -+ private static final CollectorData TPS = new CollectorData("airplane:tps", "TPS", "Ticks per second, or how fast the server updates. For a smooth server this should be a constant 20TPS.", SuffixFormatter.of("TPS"), CustomCategories.MC_PERF); -+ private static final CollectorData MSPT = new CollectorData("airplane:mspt", "MSPT", "Milliseconds per tick, which can show how well your server is performing. This value should always be under 50mspt.", SuffixFormatter.of("mspt"), CustomCategories.MC_PERF); -+ -+ public TPSCollector() { -+ super(TPS, MSPT); -+ -+ this.interval = Duration.ofSeconds(5); -+ } -+ -+ @Override -+ public void run() { -+ long[] times = MinecraftServer.getServer().tickTimes5s.getTimes(); -+ double mspt = ((double) Arrays.stream(times).sum() / (double) times.length) * 1.0E-6D; -+ -+ this.report(TPS, Math.min(20D, Math.round(Bukkit.getServer().getTPS()[0] * 100d) / 100d)); -+ this.report(MSPT, (double) Math.round(mspt * 100d) / 100d); -+ } -+} -diff --git a/src/main/java/gg/pufferfish/pufferfish/flare/collectors/WorldCountCollector.java b/src/main/java/gg/pufferfish/pufferfish/flare/collectors/WorldCountCollector.java -new file mode 100644 -index 0000000000000000000000000000000000000000..db15d3fbe2b65fc8035573f5fdbea382055db9b2 ---- /dev/null -+++ b/src/main/java/gg/pufferfish/pufferfish/flare/collectors/WorldCountCollector.java -@@ -0,0 +1,45 @@ -+package gg.pufferfish.pufferfish.flare.collectors; -+ -+import co.technove.flare.live.CollectorData; -+import co.technove.flare.live.LiveCollector; -+import co.technove.flare.live.formatter.SuffixFormatter; -+import gg.pufferfish.pufferfish.flare.CustomCategories; -+import org.bukkit.Bukkit; -+import org.bukkit.World; -+import org.bukkit.craftbukkit.CraftWorld; -+ -+import java.time.Duration; -+ -+public class WorldCountCollector extends LiveCollector { -+ -+ private static final CollectorData PLAYER_COUNT = new CollectorData("airplane:world:playercount", "Player Count", "The number of players currently on the server.", new SuffixFormatter(" Player", " Players"), CustomCategories.ENTITIES_AND_CHUNKS); -+ private static final CollectorData ENTITY_COUNT = new CollectorData("airplane:world:entitycount", "Entity Count", "The number of entities in all worlds", new SuffixFormatter(" Entity", " Entities"), CustomCategories.ENTITIES_AND_CHUNKS); -+ private static final CollectorData CHUNK_COUNT = new CollectorData("airplane:world:chunkcount", "Chunk Count", "The number of chunks currently loaded.", new SuffixFormatter(" Chunk", " Chunks"), CustomCategories.ENTITIES_AND_CHUNKS); -+ private static final CollectorData TILE_ENTITY_COUNT = new CollectorData("airplane:world:blockentitycount", "Block Entity Count", "The number of block entities currently loaded.", new SuffixFormatter(" Block Entity", " Block Entities"), CustomCategories.ENTITIES_AND_CHUNKS); -+ -+ public WorldCountCollector() { -+ super(PLAYER_COUNT, ENTITY_COUNT, CHUNK_COUNT, TILE_ENTITY_COUNT); -+ -+ this.interval = Duration.ofSeconds(5); -+ } -+ -+ @Override -+ public void run() { -+ int entities = 0; -+ int chunkCount = 0; -+ int tileEntityCount = 0; -+ -+ if (!Bukkit.isStopping()) { -+ for (World world : Bukkit.getWorlds()) { -+ entities += ((CraftWorld) world).getHandle().entityManager.getEntityGetter().getCount(); -+ chunkCount += world.getChunkCount(); -+ tileEntityCount += world.getTileEntityCount(); -+ } -+ } -+ -+ this.report(PLAYER_COUNT, Bukkit.getOnlinePlayers().size()); -+ this.report(ENTITY_COUNT, entities); -+ this.report(CHUNK_COUNT, chunkCount); -+ this.report(TILE_ENTITY_COUNT, tileEntityCount); -+ } -+} -diff --git a/src/main/java/gg/pufferfish/pufferfish/sentry/PufferfishSentryAppender.java b/src/main/java/gg/pufferfish/pufferfish/sentry/PufferfishSentryAppender.java -new file mode 100644 -index 0000000000000000000000000000000000000000..d04a8a4336566dbe6e1b9ec0d574cff43e003fa8 ---- /dev/null -+++ b/src/main/java/gg/pufferfish/pufferfish/sentry/PufferfishSentryAppender.java -@@ -0,0 +1,135 @@ -+package gg.pufferfish.pufferfish.sentry; -+ -+import com.google.common.reflect.TypeToken; -+import com.google.gson.Gson; -+import io.sentry.Breadcrumb; -+import io.sentry.Sentry; -+import io.sentry.SentryEvent; -+import io.sentry.SentryLevel; -+import io.sentry.protocol.Message; -+import io.sentry.protocol.User; -+import java.util.Map; -+import org.apache.logging.log4j.Level; -+import org.apache.logging.log4j.LogManager; -+import org.apache.logging.log4j.Marker; -+import org.apache.logging.log4j.core.LogEvent; -+import org.apache.logging.log4j.core.Logger; -+import org.apache.logging.log4j.core.appender.AbstractAppender; -+import org.apache.logging.log4j.core.filter.AbstractFilter; -+ -+public class PufferfishSentryAppender extends AbstractAppender { -+ -+ private static final org.apache.logging.log4j.Logger logger = LogManager.getLogger(PufferfishSentryAppender.class); -+ private static final Gson GSON = new Gson(); -+ -+ public PufferfishSentryAppender() { -+ super("PufferfishSentryAdapter", new SentryFilter(), null); -+ } -+ -+ @Override -+ public void append(LogEvent logEvent) { -+ if (logEvent.getThrown() != null && logEvent.getLevel().isMoreSpecificThan(Level.WARN)) { -+ try { -+ logException(logEvent); -+ } catch (Exception e) { -+ logger.warn("Failed to log event with sentry", e); -+ } -+ } else { -+ try { -+ logBreadcrumb(logEvent); -+ } catch (Exception e) { -+ logger.warn("Failed to log event with sentry", e); -+ } -+ } -+ } -+ -+ private void logException(LogEvent e) { -+ SentryEvent event = new SentryEvent(e.getThrown()); -+ -+ Message sentryMessage = new Message(); -+ sentryMessage.setMessage(e.getMessage().getFormattedMessage()); -+ -+ event.setThrowable(e.getThrown()); -+ event.setLevel(getLevel(e.getLevel())); -+ event.setLogger(e.getLoggerName()); -+ event.setTransaction(e.getLoggerName()); -+ event.setExtra("thread_name", e.getThreadName()); -+ -+ boolean hasContext = e.getContextData() != null; -+ -+ if (hasContext && e.getContextData().containsKey("pufferfishsentry_playerid")) { -+ User user = new User(); -+ user.setId(e.getContextData().getValue("pufferfishsentry_playerid")); -+ user.setUsername(e.getContextData().getValue("pufferfishsentry_playername")); -+ event.setUser(user); -+ } -+ -+ if (hasContext && e.getContextData().containsKey("pufferfishsentry_pluginname")) { -+ event.setExtra("plugin.name", e.getContextData().getValue("pufferfishsentry_pluginname")); -+ event.setExtra("plugin.version", e.getContextData().getValue("pufferfishsentry_pluginversion")); -+ event.setTransaction(e.getContextData().getValue("pufferfishsentry_pluginname")); -+ } -+ -+ if (hasContext && e.getContextData().containsKey("pufferfishsentry_eventdata")) { -+ Map eventFields = GSON.fromJson((String) e.getContextData().getValue("pufferfishsentry_eventdata"), new TypeToken>() {}.getType()); -+ if (eventFields != null) { -+ event.setExtra("event", eventFields); -+ } -+ } -+ -+ Sentry.captureEvent(event); -+ } -+ -+ private void logBreadcrumb(LogEvent e) { -+ Breadcrumb breadcrumb = new Breadcrumb(); -+ -+ breadcrumb.setLevel(getLevel(e.getLevel())); -+ breadcrumb.setCategory(e.getLoggerName()); -+ breadcrumb.setType(e.getLoggerName()); -+ breadcrumb.setMessage(e.getMessage().getFormattedMessage()); -+ -+ Sentry.addBreadcrumb(breadcrumb); -+ } -+ -+ private SentryLevel getLevel(Level level) { -+ switch (level.getStandardLevel()) { -+ case TRACE: -+ case DEBUG: -+ return SentryLevel.DEBUG; -+ case WARN: -+ return SentryLevel.WARNING; -+ case ERROR: -+ return SentryLevel.ERROR; -+ case FATAL: -+ return SentryLevel.FATAL; -+ case INFO: -+ default: -+ return SentryLevel.INFO; -+ } -+ } -+ -+ private static class SentryFilter extends AbstractFilter { -+ -+ @Override -+ public Result filter(Logger logger, org.apache.logging.log4j.Level level, Marker marker, String msg, -+ Object... params) { -+ return this.filter(logger.getName()); -+ } -+ -+ @Override -+ public Result filter(Logger logger, org.apache.logging.log4j.Level level, Marker marker, Object msg, Throwable t) { -+ return this.filter(logger.getName()); -+ } -+ -+ @Override -+ public Result filter(LogEvent event) { -+ return this.filter(event == null ? null : event.getLoggerName()); -+ } -+ -+ private Result filter(String loggerName) { -+ return loggerName != null && loggerName.startsWith("gg.castaway.pufferfish.sentry") ? Result.DENY -+ : Result.NEUTRAL; -+ } -+ -+ } -+} -diff --git a/src/main/java/gg/pufferfish/pufferfish/sentry/SentryManager.java b/src/main/java/gg/pufferfish/pufferfish/sentry/SentryManager.java -new file mode 100644 -index 0000000000000000000000000000000000000000..b011abbeb80b42de6be3785e47c7ba3c0f6dc161 ---- /dev/null -+++ b/src/main/java/gg/pufferfish/pufferfish/sentry/SentryManager.java -@@ -0,0 +1,40 @@ -+package gg.pufferfish.pufferfish.sentry; -+ -+import gg.pufferfish.pufferfish.PufferfishConfig; -+import io.sentry.Sentry; -+import org.apache.logging.log4j.LogManager; -+import org.apache.logging.log4j.Logger; -+ -+public class SentryManager { -+ -+ private static final Logger logger = LogManager.getLogger(SentryManager.class); -+ -+ private SentryManager() { -+ -+ } -+ -+ private static boolean initialized = false; -+ -+ public static synchronized void init() { -+ if (initialized) { -+ return; -+ } -+ try { -+ initialized = true; -+ -+ Sentry.init(options -> { -+ options.setDsn(PufferfishConfig.sentryDsn); -+ options.setMaxBreadcrumbs(100); -+ }); -+ -+ PufferfishSentryAppender appender = new PufferfishSentryAppender(); -+ appender.start(); -+ ((org.apache.logging.log4j.core.Logger) LogManager.getRootLogger()).addAppender(appender); -+ logger.info("Sentry logging started!"); -+ } catch (Exception e) { -+ logger.warn("Failed to initialize sentry!", e); -+ initialized = false; -+ } -+ } -+ -+} -diff --git a/src/main/java/gg/pufferfish/pufferfish/util/AsyncExecutor.java b/src/main/java/gg/pufferfish/pufferfish/util/AsyncExecutor.java -new file mode 100644 -index 0000000000000000000000000000000000000000..0772fbc77a198b3571b4ed1e8cc85e90ccd6e38a ---- /dev/null -+++ b/src/main/java/gg/pufferfish/pufferfish/util/AsyncExecutor.java -@@ -0,0 +1,51 @@ -+package gg.pufferfish.pufferfish.util; -+ -+import com.google.common.collect.Queues; -+import gg.pufferfish.pufferfish.PufferfishLogger; -+import java.util.Queue; -+import java.util.concurrent.locks.LockSupport; -+import java.util.function.BooleanSupplier; -+import java.util.logging.Level; -+ -+public class AsyncExecutor implements Runnable { -+ -+ private Queue jobs = Queues.newConcurrentLinkedQueue(); -+ private final Thread thread; -+ private final BooleanSupplier shouldRun; -+ private volatile boolean killswitch = false; -+ -+ public AsyncExecutor(String threadName, BooleanSupplier shouldRun) { -+ this.thread = new Thread(this, threadName); -+ this.shouldRun = shouldRun; -+ } -+ -+ public void start() { -+ thread.start(); -+ } -+ -+ public void kill() { -+ killswitch = true; -+ } -+ -+ public void submit(Runnable runnable) { -+ jobs.offer(runnable); -+ } -+ -+ @Override -+ public void run() { -+ while (!killswitch) { -+ if (shouldRun.getAsBoolean()) { -+ try { -+ Runnable runnable; -+ while ((runnable = jobs.poll()) != null) { -+ runnable.run(); -+ } -+ } catch (Exception e) { -+ PufferfishLogger.LOGGER.log(Level.SEVERE, e, () -> "Failed to execute async job for thread " + thread.getName()); -+ } -+ } -+ LockSupport.parkNanos("executing tasks", 1000L); -+ } -+ } -+ -+} -diff --git a/src/main/java/gg/pufferfish/pufferfish/util/AsyncPlayerAreaMap.java b/src/main/java/gg/pufferfish/pufferfish/util/AsyncPlayerAreaMap.java -new file mode 100644 -index 0000000000000000000000000000000000000000..a93ee99c2399def1e221260547a3e6bce2d621fa ---- /dev/null -+++ b/src/main/java/gg/pufferfish/pufferfish/util/AsyncPlayerAreaMap.java -@@ -0,0 +1,31 @@ -+package gg.pufferfish.pufferfish.util; -+ -+import com.destroystokyo.paper.util.misc.PlayerAreaMap; -+import com.destroystokyo.paper.util.misc.PooledLinkedHashSets; -+import java.util.concurrent.ConcurrentHashMap; -+import net.minecraft.server.level.ServerPlayer; -+ -+public final class AsyncPlayerAreaMap extends PlayerAreaMap { -+ -+ public AsyncPlayerAreaMap() { -+ super(); -+ this.areaMap = new Long2ObjectOpenHashMapWrapper<>(new ConcurrentHashMap<>(1024, 0.7f)); -+ } -+ -+ public AsyncPlayerAreaMap(final PooledLinkedHashSets pooledHashSets) { -+ super(pooledHashSets); -+ this.areaMap = new Long2ObjectOpenHashMapWrapper<>(new ConcurrentHashMap<>(1024, 0.7f)); -+ } -+ -+ public AsyncPlayerAreaMap(final PooledLinkedHashSets pooledHashSets, final ChangeCallback addCallback, -+ final ChangeCallback removeCallback) { -+ this(pooledHashSets, addCallback, removeCallback, null); -+ } -+ -+ public AsyncPlayerAreaMap(final PooledLinkedHashSets pooledHashSets, final ChangeCallback addCallback, -+ final ChangeCallback removeCallback, final ChangeSourceCallback changeSourceCallback) { -+ super(pooledHashSets, addCallback, removeCallback, changeSourceCallback); -+ this.areaMap = new Long2ObjectOpenHashMapWrapper<>(new ConcurrentHashMap<>(1024, 0.7f)); -+ } -+ -+} -diff --git a/src/main/java/gg/pufferfish/pufferfish/util/IterableWrapper.java b/src/main/java/gg/pufferfish/pufferfish/util/IterableWrapper.java -new file mode 100644 -index 0000000000000000000000000000000000000000..c1929840254a3e6d721816f4a20415bea1742580 ---- /dev/null -+++ b/src/main/java/gg/pufferfish/pufferfish/util/IterableWrapper.java -@@ -0,0 +1,20 @@ -+package gg.pufferfish.pufferfish.util; -+ -+import java.util.Iterator; -+import org.jetbrains.annotations.NotNull; -+ -+public class IterableWrapper implements Iterable { -+ -+ private final Iterator iterator; -+ -+ public IterableWrapper(Iterator iterator) { -+ this.iterator = iterator; -+ } -+ -+ @NotNull -+ @Override -+ public Iterator iterator() { -+ return iterator; -+ } -+ -+} -diff --git a/src/main/java/gg/pufferfish/pufferfish/util/Long2ObjectOpenHashMapWrapper.java b/src/main/java/gg/pufferfish/pufferfish/util/Long2ObjectOpenHashMapWrapper.java -new file mode 100644 -index 0000000000000000000000000000000000000000..42cdc43d6b739973a0944f502089757247ee6c61 ---- /dev/null -+++ b/src/main/java/gg/pufferfish/pufferfish/util/Long2ObjectOpenHashMapWrapper.java -@@ -0,0 +1,40 @@ -+package gg.pufferfish.pufferfish.util; -+ -+import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; -+import java.util.Map; -+import org.jetbrains.annotations.Nullable; -+ -+public class Long2ObjectOpenHashMapWrapper extends Long2ObjectOpenHashMap { -+ -+ private final Map backingMap; -+ -+ public Long2ObjectOpenHashMapWrapper(Map map) { -+ backingMap = map; -+ } -+ -+ @Override -+ public V put(Long key, V value) { -+ return backingMap.put(key, value); -+ } -+ -+ @Override -+ public V get(Object key) { -+ return backingMap.get(key); -+ } -+ -+ @Override -+ public V remove(Object key) { -+ return backingMap.remove(key); -+ } -+ -+ @Nullable -+ @Override -+ public V putIfAbsent(Long key, V value) { -+ return backingMap.putIfAbsent(key, value); -+ } -+ -+ @Override -+ public int size() { -+ return backingMap.size(); -+ } -+} -diff --git a/src/main/java/net/minecraft/server/MCUtil.java b/src/main/java/net/minecraft/server/MCUtil.java -index 6aec679e75aa6655b47a552db011924ea3a6c922..e8e49e1bdf1ff4ea60a4feb6ec52d999cbced256 100644 ---- a/src/main/java/net/minecraft/server/MCUtil.java -+++ b/src/main/java/net/minecraft/server/MCUtil.java -@@ -209,7 +209,7 @@ public final class MCUtil { - } - - public static long getCoordinateKey(final Entity entity) { -- return ((long)(MCUtil.fastFloor(entity.getZ()) >> 4) << 32) | ((MCUtil.fastFloor(entity.getX()) >> 4) & 0xFFFFFFFFL); -+ return ((long)(entity.blockPosition.getZ() >> 4) << 32) | ((entity.blockPosition.getX() >> 4) & 0xFFFFFFFFL); // Pufferfish - eliminate double->long cast in hotpath - } - - public static long getCoordinateKey(final ChunkPos pair) { -diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java -index da15a224b8b974e78b9d8d5f514229b3b2a5a63e..22ad31a4320906ee6b56180324a48258c6e461f2 100644 ---- a/src/main/java/net/minecraft/server/MinecraftServer.java -+++ b/src/main/java/net/minecraft/server/MinecraftServer.java -@@ -310,6 +310,8 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop true); // Pufferfish - optimize mob spawning -+ - public static S spin(Function serverFactory) { - AtomicReference atomicreference = new AtomicReference(); - Thread thread = new Thread(() -> { -@@ -1696,7 +1698,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop // Spigot - Spigot > // CraftBukkit - cb > vanilla! -+ return "Pufferfish"; // Pufferfish - Pufferfish > // Paper - Paper > // Spigot - Spigot > // CraftBukkit - cb > vanilla! - } - - public SystemReport fillSystemReport(SystemReport details) { -@@ -2255,6 +2257,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop passengers = parent.getPassengers(); -+ -+ for (int i = 0, size = passengers.size(); i < size; i++) { -+ Entity entity = passengers.get(i); -+ int range = entity.getType().clientTrackingRange() * 16; -+ range = org.spigotmc.TrackingRange.getEntityTrackingRange(entity, range); // Paper -+ -+ if (range > highest) { // Paper - we need the lowest range thanks to the fact that our tracker doesn't account for passenger logic // Tuinity - not anymore! -+ highest = range; -+ } -+ -+ highest = getHighestRange(entity, highest); -+ } -+ -+ return highest; -+ } -+ - private int getEffectiveRange() { - int i = this.range; -+ // Pufferfish start - remove iterators and streams -+ /* - Iterator iterator = this.entity.getIndirectPassengers().iterator(); - - while (iterator.hasNext()) { -@@ -2368,6 +2388,9 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider - i = j; - } - } -+ */ -+ i = getHighestRange(this.entity, i); -+ // Pufferfish end - - return this.scaledRange(i); - } -diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java -index fb0757d4bb32123641535a88a22bc074b8d2623f..3241f4f73395e8e5c0d15d85b2b77711a5984677 100644 ---- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java -+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java -@@ -75,6 +75,9 @@ public class ServerChunkCache extends ChunkSource { - - private final LevelChunk[] lastLoadedChunks = new LevelChunk[4 * 4]; - -+ public boolean firstRunSpawnCounts = true; // Pufferfish -+ public final java.util.concurrent.atomic.AtomicBoolean _pufferfish_spawnCountsReady = new java.util.concurrent.atomic.AtomicBoolean(false); // Pufferfish - optimize countmobs -+ - private static int getChunkCacheKey(int x, int z) { - return x & 3 | ((z & 3) << 2); - } -@@ -961,6 +964,7 @@ public class ServerChunkCache extends ChunkSource { - ProfilerFiller gameprofilerfiller = this.level.getProfiler(); - - gameprofilerfiller.push("pollingChunks"); -+ this.level.resetIceAndSnowTick(); // Pufferfish - reset ice & snow tick random - int k = this.level.getGameRules().getInt(GameRules.RULE_RANDOMTICKING); - boolean flag1 = level.ticksPerSpawnCategory.getLong(org.bukkit.entity.SpawnCategory.ANIMAL) != 0L && worlddata.getGameTime() % level.ticksPerSpawnCategory.getLong(org.bukkit.entity.SpawnCategory.ANIMAL) == 0L; // CraftBukkit - -@@ -970,18 +974,25 @@ public class ServerChunkCache extends ChunkSource { - // Paper start - per player mob spawning - NaturalSpawner.SpawnState spawnercreature_d; // moved down - if ((this.spawnFriendlies || this.spawnEnemies) && this.chunkMap.playerMobDistanceMap != null) { // don't count mobs when animals and monsters are disabled -- // re-set mob counts -- for (ServerPlayer player : this.level.players) { -- Arrays.fill(player.mobCounts, 0); -+ // Pufferfish start - moved down when async processing -+ if (!gg.pufferfish.pufferfish.PufferfishConfig.enableAsyncMobSpawning) { -+ // re-set mob counts -+ for (ServerPlayer player : this.level.players) { -+ Arrays.fill(player.mobCounts, 0); -+ } -+ lastSpawnState = NaturalSpawner.createState(l, this.level.getAllEntities(), this::getFullChunk, null, true); - } -- spawnercreature_d = NaturalSpawner.createState(l, this.level.getAllEntities(), this::getFullChunk, null, true); -+ // Pufferfish end - } else { -- spawnercreature_d = NaturalSpawner.createState(l, this.level.getAllEntities(), this::getFullChunk, this.chunkMap.playerMobDistanceMap == null ? new LocalMobCapCalculator(this.chunkMap) : null, false); -+ // Pufferfish start - this is only implemented for per-player mob spawning so this makes everything work if this setting is disabled. -+ lastSpawnState = NaturalSpawner.createState(l, this.level.getAllEntities(), this::getFullChunk, this.chunkMap.playerMobDistanceMap == null ? new LocalMobCapCalculator(this.chunkMap) : null, false); -+ _pufferfish_spawnCountsReady.set(true); -+ // Pufferfish end - } - // Paper end - this.level.timings.countNaturalMobs.stopTiming(); // Paper - timings - -- this.lastSpawnState = spawnercreature_d; -+ //this.lastSpawnState = spawnercreature_d; // Pufferfish - this is managed asynchronously - gameprofilerfiller.popPush("filteringLoadedChunks"); - // Paper - moved down - this.level.timings.chunkTicks.startTiming(); // Paper -@@ -1019,8 +1030,8 @@ public class ServerChunkCache extends ChunkSource { - - if ((true || this.level.isNaturalSpawningAllowed(chunkcoordintpair)) && this.chunkMap.anyPlayerCloseEnoughForSpawning(holder, chunkcoordintpair, false)) { // Paper - optimise anyPlayerCloseEnoughForSpawning // Paper - replace player chunk loader system - chunk1.incrementInhabitedTime(j); -- if (flag2 && (this.spawnEnemies || this.spawnFriendlies) && this.level.getWorldBorder().isWithinBounds(chunkcoordintpair) && this.chunkMap.anyPlayerCloseEnoughForSpawning(holder, chunkcoordintpair, true)) { // Spigot // Paper - optimise anyPlayerCloseEnoughForSpawning & optimise chunk tick iteration -- NaturalSpawner.spawnForChunk(this.level, chunk1, spawnercreature_d, this.spawnFriendlies, this.spawnEnemies, flag1); -+ if (flag2 && (!gg.pufferfish.pufferfish.PufferfishConfig.enableAsyncMobSpawning || _pufferfish_spawnCountsReady.get()) && (this.spawnEnemies || this.spawnFriendlies) && this.level.getWorldBorder().isWithinBounds(chunkcoordintpair) && this.chunkMap.anyPlayerCloseEnoughForSpawning(holder, chunkcoordintpair, true)) { // Spigot // Paper - optimise anyPlayerCloseEnoughForSpawning & optimise chunk tick iteration -+ NaturalSpawner.spawnForChunk(this.level, chunk1, lastSpawnState, this.spawnFriendlies, this.spawnEnemies, flag1); // Pufferfish - } - - if (true || this.level.shouldTickBlocksAt(chunkcoordintpair.toLong())) { // Paper - replace player chunk loader system -@@ -1082,6 +1093,30 @@ public class ServerChunkCache extends ChunkSource { - } - // Paper end - controlled flush for entity tracker packets - } -+ -+ // Pufferfish start - optimize mob spawning -+ if (gg.pufferfish.pufferfish.PufferfishConfig.enableAsyncMobSpawning) { -+ for (ServerPlayer player : this.level.players) { -+ Arrays.fill(player.mobCounts, 0); -+ } -+ if (firstRunSpawnCounts) { -+ firstRunSpawnCounts = false; -+ _pufferfish_spawnCountsReady.set(true); -+ } -+ if (chunkMap.playerMobDistanceMap != null && _pufferfish_spawnCountsReady.getAndSet(false)) { -+ net.minecraft.server.MinecraftServer.getServer().mobSpawnExecutor.submit(() -> { -+ int mapped = distanceManager.getNaturalSpawnChunkCount(); -+ io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet.Iterator objectiterator = -+ level.entityTickList.entities.iterator(io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet.ITERATOR_FLAG_SEE_ADDITIONS); -+ gg.pufferfish.pufferfish.util.IterableWrapper wrappedIterator = -+ new gg.pufferfish.pufferfish.util.IterableWrapper<>(objectiterator); -+ lastSpawnState = NaturalSpawner.createState(mapped, wrappedIterator, this::getFullChunk, null, true); -+ objectiterator.finishedIterating(); -+ _pufferfish_spawnCountsReady.set(true); -+ }); -+ } -+ } -+ // Pufferfish end - } - - private void getFullChunk(long pos, Consumer chunkConsumer) { -diff --git a/src/main/java/net/minecraft/server/level/ServerEntity.java b/src/main/java/net/minecraft/server/level/ServerEntity.java -index f91e1a876ad4c46a7c92cead18947a941b4d9e68..d52072ab126f2fdee30bb114f3058338edd72fd5 100644 ---- a/src/main/java/net/minecraft/server/level/ServerEntity.java -+++ b/src/main/java/net/minecraft/server/level/ServerEntity.java -@@ -174,6 +174,7 @@ public class ServerEntity { - if (this.entity instanceof net.minecraft.world.entity.decoration.Painting) {flag4 = true;} // Paper - Always send exact position for paintings - - if (!flag4 && this.teleportDelay <= 400 && !this.wasRiding && this.wasOnGround == this.entity.isOnGround() && !(com.destroystokyo.paper.PaperConfig.sendFullPosForHardCollidingEntities && this.entity.hardCollides())) { // Paper - send full pos for hard colliding entities to prevent collision problems due to desync -+ if (flag2 || flag3 || this.entity instanceof AbstractArrow) { // Pufferfish - if ((!flag2 || !flag3) && !(this.entity instanceof AbstractArrow)) { - if (flag2) { - packet1 = new ClientboundMoveEntityPacket.Pos(this.entity.getId(), (short) ((int) k), (short) ((int) l), (short) ((int) i1), this.entity.isOnGround()); -@@ -183,6 +184,7 @@ public class ServerEntity { - } else { - packet1 = new ClientboundMoveEntityPacket.PosRot(this.entity.getId(), (short) ((int) k), (short) ((int) l), (short) ((int) i1), (byte) i, (byte) j, this.entity.isOnGround()); - } -+ } // Pufferfish - } else { - this.wasOnGround = this.entity.isOnGround(); - this.teleportDelay = 0; -diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java -index 98209532ad3e692d7e459640123f78bbd9a65889..93c1f9e3e9d62cc993fedb93fe43cbeed7d39542 100644 ---- a/src/main/java/net/minecraft/server/level/ServerLevel.java -+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java -@@ -671,7 +671,20 @@ public class ServerLevel extends Level implements WorldGenLevel { - } - - gameprofilerfiller.push("tick"); -- this.guardEntityTick(this::tickNonPassenger, entity); -+ // Pufferfish start - copied from this.guardEntityTick -+ try { -+ this.tickNonPassenger(entity); // Pufferfish - changed -+ MinecraftServer.getServer().executeMidTickTasks(); // Tuinity - execute chunk tasks mid tick -+ } catch (Throwable throwable) { -+ if (throwable instanceof ThreadDeath) throw throwable; // Paper -+ // Paper start - Prevent tile entity and entity crashes -+ final String msg = String.format("Entity threw exception at %s:%s,%s,%s", entity.level.getWorld().getName(), entity.getX(), entity.getY(), entity.getZ()); -+ MinecraftServer.LOGGER.error(msg, throwable); -+ getCraftServer().getPluginManager().callEvent(new com.destroystokyo.paper.event.server.ServerExceptionEvent(new com.destroystokyo.paper.exception.ServerInternalException(msg, throwable))); -+ entity.discard(); -+ // Paper end -+ } -+ // Pufferfish end - gameprofilerfiller.pop(); - } - } -@@ -736,9 +749,11 @@ public class ServerLevel extends Level implements WorldGenLevel { - } - // Paper start - optimise random block ticking - private final BlockPos.MutableBlockPos chunkTickMutablePosition = new BlockPos.MutableBlockPos(); -- private final io.papermc.paper.util.math.ThreadUnsafeRandom randomTickRandom = new io.papermc.paper.util.math.ThreadUnsafeRandom(); -+ private final io.papermc.paper.util.math.ThreadUnsafeRandom randomTickRandom = new io.papermc.paper.util.math.ThreadUnsafeRandom(); public java.util.Random getThreadUnsafeRandom() { return this.randomTickRandom; } // Pufferfish - getter - // Paper end - -+ private int currentIceAndSnowTick = 0; protected void resetIceAndSnowTick() { this.currentIceAndSnowTick = this.randomTickRandom.nextInt(16); } // Pufferfish -+ - public void tickChunk(LevelChunk chunk, int randomTickSpeed) { - ChunkPos chunkcoordintpair = chunk.getPos(); - boolean flag = this.isRaining(); -@@ -749,7 +764,7 @@ public class ServerLevel extends Level implements WorldGenLevel { - gameprofilerfiller.push("thunder"); - final BlockPos.MutableBlockPos blockposition = this.chunkTickMutablePosition; // Paper - use mutable to reduce allocation rate, final to force compile fail on change - -- if (!this.paperConfig.disableThunder && flag && this.isThundering() && this.spigotConfig.thunderChance > 0 && this.random.nextInt(this.spigotConfig.thunderChance) == 0) { // Spigot // Paper - disable thunder -+ if (!this.paperConfig.disableThunder && flag && this.isThundering() && this.spigotConfig.thunderChance > 0 && /*this.random.nextInt(this.spigotConfig.thunderChance) == 0 &&*/ chunk.shouldDoLightning(this.random)) { // Spigot // Paper - disable thunder // Pufferfish - replace random with shouldDoLightning - blockposition.set(this.findLightningTargetAround(this.getBlockRandomPos(j, 0, k, 15))); // Paper - if (this.isRainingAt(blockposition)) { - DifficultyInstance difficultydamagescaler = this.getCurrentDifficultyAt(blockposition); -@@ -773,7 +788,7 @@ public class ServerLevel extends Level implements WorldGenLevel { - } - - gameprofilerfiller.popPush("iceandsnow"); -- if (!this.paperConfig.disableIceAndSnow && this.random.nextInt(16) == 0) { // Paper - Disable ice and snow -+ if (!this.paperConfig.disableIceAndSnow && (this.currentIceAndSnowTick++ & 15) == 0) { // Paper - Disable ice and snow // Paper - optimise random ticking // Pufferfish - optimize further random ticking - // Paper start - optimise chunk ticking - this.getRandomBlockPosition(j, 0, k, 15, blockposition); - int normalY = chunk.getHeight(Heightmap.Types.MOTION_BLOCKING, blockposition.getX() & 15, blockposition.getZ() & 15) + 1; -diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -index 0b7923f67d27549f41c0d398d1b99737f0c8a746..f98c6804ef8ec33551857606800295712f2e9057 100644 ---- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -+++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -@@ -1117,6 +1117,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Ser - - @Override - public void handleEditBook(ServerboundEditBookPacket packet) { -+ if (!gg.pufferfish.pufferfish.PufferfishConfig.enableBooks && !this.player.getBukkitEntity().hasPermission("pufferfish.usebooks")) return; // Pufferfish - // Paper start - if (!this.cserver.isPrimaryThread()) { - List pageList = packet.getPages(); -diff --git a/src/main/java/net/minecraft/world/CompoundContainer.java b/src/main/java/net/minecraft/world/CompoundContainer.java -index 241fec02e6869c638d3a160819b32173a081467b..6a8f9e8f5bf108674c47018def28906e2d0a729c 100644 ---- a/src/main/java/net/minecraft/world/CompoundContainer.java -+++ b/src/main/java/net/minecraft/world/CompoundContainer.java -@@ -1,5 +1,6 @@ - package net.minecraft.world; - -+import net.minecraft.core.Direction; // Pufferfish - import net.minecraft.world.entity.player.Player; - import net.minecraft.world.item.ItemStack; - -@@ -64,6 +65,23 @@ public class CompoundContainer implements Container { - this.container2 = second; - } - -+ // Pufferfish start -+ @Override -+ public boolean hasEmptySlot(Direction enumdirection) { -+ return this.container1.hasEmptySlot(null) || this.container2.hasEmptySlot(null); -+ } -+ -+ @Override -+ public boolean isCompletelyFull(Direction enumdirection) { -+ return this.container1.isCompletelyFull(null) && this.container2.isCompletelyFull(null); -+ } -+ -+ @Override -+ public boolean isCompletelyEmpty(Direction enumdirection) { -+ return this.container1.isCompletelyEmpty(null) && this.container2.isCompletelyEmpty(null); -+ } -+ // Pufferfish end -+ - @Override - public int getContainerSize() { - return this.container1.getContainerSize() + this.container2.getContainerSize(); -diff --git a/src/main/java/net/minecraft/world/Container.java b/src/main/java/net/minecraft/world/Container.java -index 7437f01ca8f416e2c9150250e324af4725a4efb6..7ac51dbfce18a2bc52faa7a915abeccc6068909d 100644 ---- a/src/main/java/net/minecraft/world/Container.java -+++ b/src/main/java/net/minecraft/world/Container.java -@@ -1,6 +1,8 @@ - package net.minecraft.world; - - import java.util.Set; -+ -+import net.minecraft.core.Direction; // Pufferfish - import net.minecraft.world.entity.player.Player; - import net.minecraft.world.item.Item; - import net.minecraft.world.item.ItemStack; -@@ -9,6 +11,63 @@ import org.bukkit.craftbukkit.entity.CraftHumanEntity; - // CraftBukkit end - - public interface Container extends Clearable { -+ // Pufferfish start - allow the inventory to override and optimize these frequent calls -+ default boolean hasEmptySlot(@org.jetbrains.annotations.Nullable Direction enumdirection) { // there is a slot with 0 items in it -+ if (this instanceof WorldlyContainer worldlyContainer) { -+ for (int i : worldlyContainer.getSlotsForFace(enumdirection)) { -+ if (this.getItem(i).isEmpty()) { -+ return true; -+ } -+ } -+ } else { -+ int size = this.getContainerSize(); -+ for (int i = 0; i < size; i++) { -+ if (this.getItem(i).isEmpty()) { -+ return true; -+ } -+ } -+ } -+ return false; -+ } -+ -+ default boolean isCompletelyFull(@org.jetbrains.annotations.Nullable Direction enumdirection) { // every stack is maxed -+ if (this instanceof WorldlyContainer worldlyContainer) { -+ for (int i : worldlyContainer.getSlotsForFace(enumdirection)) { -+ ItemStack itemStack = this.getItem(i); -+ if (itemStack.getCount() < itemStack.getMaxStackSize()) { -+ return false; -+ } -+ } -+ } else { -+ int size = this.getContainerSize(); -+ for (int i = 0; i < size; i++) { -+ ItemStack itemStack = this.getItem(i); -+ if (itemStack.getCount() < itemStack.getMaxStackSize()) { -+ return false; -+ } -+ } -+ } -+ return true; -+ } -+ -+ default boolean isCompletelyEmpty(@org.jetbrains.annotations.Nullable Direction enumdirection) { -+ if (this instanceof WorldlyContainer worldlyContainer) { -+ for (int i : worldlyContainer.getSlotsForFace(enumdirection)) { -+ if (!this.getItem(i).isEmpty()) { -+ return false; -+ } -+ } -+ } else { -+ int size = this.getContainerSize(); -+ for (int i = 0; i < size; i++) { -+ if (!this.getItem(i).isEmpty()) { -+ return false; -+ } -+ } -+ } -+ return true; -+ } -+ // Pufferfish end - - int LARGE_MAX_STACK_SIZE = 64; - -diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java -index dfd1f37757af1bd808cc2e2d8bf97123adf638bb..b47165433d43592742e508f9b918ab36b42c6df6 100644 ---- a/src/main/java/net/minecraft/world/entity/Entity.java -+++ b/src/main/java/net/minecraft/world/entity/Entity.java -@@ -230,7 +230,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { - public double yo; - public double zo; - private Vec3 position; -- private BlockPos blockPosition; -+ public BlockPos blockPosition; // Pufferfish - private->public - private ChunkPos chunkPosition; - private Vec3 deltaMovement; - public float yRot; // Paper - private->public -@@ -352,6 +352,11 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { - return this.originWorld; - } - // Paper end -+ // Pufferfish start -+ public int activatedPriority = gg.pufferfish.pufferfish.PufferfishConfig.maximumActivationPrio; // golf score -+ public final BlockPos.MutableBlockPos cachedBlockPos = new BlockPos.MutableBlockPos(); // used where needed -+ // Pufferfish end -+ - public float getBukkitYaw() { - return this.yRot; - } -@@ -375,17 +380,36 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { - this.isLegacyTrackingEntity = isLegacyTrackingEntity; - } - -+ private org.spigotmc.TrackingRange.TrackingRangeType getFurthestEntity(Entity entity, net.minecraft.server.level.ChunkMap chunkMap, org.spigotmc.TrackingRange.TrackingRangeType type, int range) { -+ List passengers = entity.getPassengers(); -+ for (int i = 0, size = passengers.size(); i < size; i++) { -+ Entity passenger = passengers.get(i); -+ org.spigotmc.TrackingRange.TrackingRangeType passengerType = passenger.trackingRangeType; -+ int passengerRange = chunkMap.getEntityTrackerRange(passengerType.ordinal()); -+ if (passengerRange > range) { -+ type = passengerType; -+ range = passengerRange; -+ } -+ -+ type = this.getFurthestEntity(passenger, chunkMap, type, range); -+ } -+ -+ return type; -+ } -+ - public final com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet getPlayersInTrackRange() { - // determine highest range of passengers - if (this.passengers.isEmpty()) { - return ((ServerLevel)this.level).getChunkSource().chunkMap.playerEntityTrackerTrackMaps[this.trackingRangeType.ordinal()] - .getObjectsInRange(MCUtil.getCoordinateKey(this)); - } -- Iterable passengers = this.getIndirectPassengers(); -+ //Iterable passengers = this.getIndirectPassengers(); // Pufferfish - net.minecraft.server.level.ChunkMap chunkMap = ((ServerLevel)this.level).getChunkSource().chunkMap; - org.spigotmc.TrackingRange.TrackingRangeType type = this.trackingRangeType; - int range = chunkMap.getEntityTrackerRange(type.ordinal()); - -+ // Pufferfish start - use getFurthestEntity to skip getIndirectPassengers -+ /* - for (Entity passenger : passengers) { - org.spigotmc.TrackingRange.TrackingRangeType passengerType = passenger.trackingRangeType; - int passengerRange = chunkMap.getEntityTrackerRange(passengerType.ordinal()); -@@ -394,6 +418,9 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { - range = passengerRange; - } - } -+ */ -+ type = this.getFurthestEntity(this, chunkMap, type, range); -+ // Pufferfish end - - return chunkMap.playerEntityTrackerTrackMaps[type.ordinal()].getObjectsInRange(MCUtil.getCoordinateKey(this)); - } -@@ -724,6 +751,12 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { - // CraftBukkit end - - public void baseTick() { -+ // Pufferfish start - entity TTL -+ if (type != EntityType.PLAYER && type.ttl >= 0 && this.tickCount >= type.ttl) { -+ remove(RemovalReason.DISCARDED); -+ return; -+ } -+ // Pufferfish end - entity TTL - this.level.getProfiler().push("entityBaseTick"); - if (firstTick && this instanceof net.minecraft.world.entity.NeutralMob neutralMob) neutralMob.tickInitialPersistentAnger(level); // Paper - Update last hurt when ticking - this.feetBlockState = null; -@@ -3887,16 +3920,18 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { - } - - public boolean updateFluidHeightAndDoFluidPushing(TagKey tag, double speed) { -- if (this.touchingUnloadedChunk()) { -+ if (false && this.touchingUnloadedChunk()) { // Pufferfish - cost of a lookup here is the same cost as below, so skip - return false; - } else { - AABB axisalignedbb = this.getBoundingBox().deflate(0.001D); -- int i = Mth.floor(axisalignedbb.minX); -- int j = Mth.ceil(axisalignedbb.maxX); -- int k = Mth.floor(axisalignedbb.minY); -- int l = Mth.ceil(axisalignedbb.maxY); -- int i1 = Mth.floor(axisalignedbb.minZ); -- int j1 = Mth.ceil(axisalignedbb.maxZ); -+ // Pufferfish start - rename -+ int minBlockX = Mth.floor(axisalignedbb.minX); -+ int maxBlockX = Mth.ceil(axisalignedbb.maxX); -+ int minBlockY = Mth.floor(axisalignedbb.minY); -+ int maxBlockY = Mth.ceil(axisalignedbb.maxY); -+ int minBlockZ = Mth.floor(axisalignedbb.minZ); -+ int maxBlockZ = Mth.ceil(axisalignedbb.maxZ); -+ // Pufferfish end - double d1 = 0.0D; - boolean flag = this.isPushedByFluid(); - boolean flag1 = false; -@@ -3904,14 +3939,61 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { - int k1 = 0; - BlockPos.MutableBlockPos blockposition_mutableblockposition = new BlockPos.MutableBlockPos(); - -- for (int l1 = i; l1 < j; ++l1) { -- for (int i2 = k; i2 < l; ++i2) { -- for (int j2 = i1; j2 < j1; ++j2) { -- blockposition_mutableblockposition.set(l1, i2, j2); -- FluidState fluid = this.level.getFluidState(blockposition_mutableblockposition); -+ // Pufferfish start - based off CollisionUtil.getCollisionsForBlocksOrWorldBorder -+ final int minSection = io.papermc.paper.util.WorldUtil.getMinSection(this.level); -+ final int maxSection = io.papermc.paper.util.WorldUtil.getMaxSection(this.level); -+ final int minBlock = minSection << 4; -+ final int maxBlock = (maxSection << 4) | 15; -+ -+ // special cases: -+ if (minBlockY > maxBlock || maxBlockY < minBlock) { -+ // no point in checking -+ return false; -+ } -+ -+ int minYIterate = Math.max(minBlock, minBlockY); -+ int maxYIterate = Math.min(maxBlock, maxBlockY); -+ -+ int minChunkX = minBlockX >> 4; -+ int maxChunkX = maxBlockX >> 4; -+ -+ int minChunkZ = minBlockZ >> 4; -+ int maxChunkZ = maxBlockZ >> 4; -+ -+ for (int currChunkZ = minChunkZ; currChunkZ <= maxChunkZ; ++currChunkZ) { -+ int minZ = currChunkZ == minChunkZ ? minBlockZ & 15 : 0; // coordinate in chunk -+ int maxZ = currChunkZ == maxChunkZ ? maxBlockZ & 15 : 16; // coordinate in chunk -+ -+ for (int currChunkX = minChunkX; currChunkX <= maxChunkX; ++currChunkX) { -+ int minX = currChunkX == minChunkX ? minBlockX & 15 : 0; // coordinate in chunk -+ int maxX = currChunkX == maxChunkX ? maxBlockX & 15 : 16; // coordinate in chunk -+ -+ net.minecraft.world.level.chunk.ChunkAccess chunk = this.level.getChunkIfLoadedImmediately(currChunkX, currChunkZ); -+ if (chunk == null) { -+ return false; // if we're touching an unloaded chunk then it's false -+ } -+ -+ net.minecraft.world.level.chunk.LevelChunkSection[] sections = chunk.getSections(); -+ -+ for (int currY = minYIterate; currY < maxYIterate; ++currY) { -+ net.minecraft.world.level.chunk.LevelChunkSection section = sections[(currY >> 4) - minSection]; -+ -+ if (section == null || section.hasOnlyAir() || section.fluidStateCount == 0) { // if no fluids, nothing in this section -+ // empty -+ // skip to next section -+ currY = (currY & ~(15)) + 15; // increment by 15: iterator loop increments by the extra one -+ continue; -+ } -+ -+ net.minecraft.world.level.chunk.PalettedContainer blocks = section.states; -+ -+ for (int currZ = minZ; currZ < maxZ; ++currZ) { -+ for (int currX = minX; currX < maxX; ++currX) { -+ FluidState fluid = blocks.get(currX & 15, currY & 15, currZ & 15).getFluidState(); - - if (fluid.is(tag)) { -- double d2 = (double) ((float) i2 + fluid.getHeight(this.level, blockposition_mutableblockposition)); -+ blockposition_mutableblockposition.set((currChunkX << 4) + currX, currY, (currChunkZ << 4) + currZ); -+ double d2 = (double) ((float) currY + fluid.getHeight(this.level, blockposition_mutableblockposition)); - - if (d2 >= axisalignedbb.minY) { - flag1 = true; -@@ -3933,9 +4015,12 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { - // CraftBukkit end - } - } -+ } -+ } - } - } - } -+ // Pufferfish end - - if (vec3d.length() > 0.0D) { - if (k1 > 0) { -diff --git a/src/main/java/net/minecraft/world/entity/EntityType.java b/src/main/java/net/minecraft/world/entity/EntityType.java -index 79abd84696ef099f6b12ddeaa6e398f18c53316a..5ec5e8a30c75b36412bacb0b60b560dfab7eeb22 100644 ---- a/src/main/java/net/minecraft/world/entity/EntityType.java -+++ b/src/main/java/net/minecraft/world/entity/EntityType.java -@@ -296,6 +296,8 @@ public class EntityType implements EntityTypeTest { - return Registry.ENTITY_TYPE.getOptional(ResourceLocation.tryParse(id)); - } - -+ public boolean dabEnabled = false; // Pufferfish -+ public int ttl = -1; // Pufferfish - // Paper start - add id - public final String id; - -diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java -index d6ef140bcc35b10f78f2ec2f10c8a153395fc9df..33fbb85228f1570e40f2a825489362626bea3912 100644 ---- a/src/main/java/net/minecraft/world/entity/LivingEntity.java -+++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java -@@ -141,7 +141,6 @@ import org.bukkit.event.entity.EntityTeleportEvent; - import org.bukkit.event.player.PlayerItemConsumeEvent; - // CraftBukkit end - --import co.aikar.timings.MinecraftTimings; // Paper - - public abstract class LivingEntity extends Entity { - -@@ -396,8 +395,7 @@ public abstract class LivingEntity extends Entity { - - if (this.isAlive()) { - boolean flag = this instanceof net.minecraft.world.entity.player.Player; -- -- if (this.isInWall()) { -+ if ((!gg.pufferfish.pufferfish.PufferfishConfig.enableSuffocationOptimization || (tickCount % 10 == 0 && couldPossiblyBeHurt(1.0F))) && this.isInWall()) { // Pufferfish - optimize suffocation - this.hurt(DamageSource.IN_WALL, 1.0F); - } else if (flag && !this.level.getWorldBorder().isWithinBounds(this.getBoundingBox())) { - double d0 = this.level.getWorldBorder().getDistanceToBorder(this) + this.level.getWorldBorder().getDamageSafeZone(); -@@ -1290,6 +1288,15 @@ public abstract class LivingEntity extends Entity { - return this.getHealth() <= 0.0F; - } - -+ // Pufferfish start - optimize suffocation -+ public boolean couldPossiblyBeHurt(float amount) { -+ if ((float) this.invulnerableTime > (float) this.invulnerableDuration / 2.0F && amount <= this.lastHurt) { -+ return false; -+ } -+ return true; -+ } -+ // Pufferfish end -+ - @Override - public boolean hurt(DamageSource source, float amount) { - if (this.isInvulnerableTo(source)) { -@@ -1851,6 +1858,20 @@ public abstract class LivingEntity extends Entity { - return this.lastClimbablePos; - } - -+ -+ // Pufferfish start -+ private boolean cachedOnClimable = false; -+ private BlockPos lastClimbingPosition = null; -+ -+ public boolean onClimableCached() { -+ if (!this.blockPosition().equals(this.lastClimbingPosition)) { -+ this.cachedOnClimable = this.onClimbable(); -+ this.lastClimbingPosition = this.blockPosition(); -+ } -+ return this.cachedOnClimable; -+ } -+ // Pufferfish end -+ - public boolean onClimbable() { - if (this.isSpectator()) { - return false; -@@ -3548,7 +3569,10 @@ public abstract class LivingEntity extends Entity { - Vec3 vec3d1 = new Vec3(entity.getX(), entity.getEyeY(), entity.getZ()); - - // Paper - diff on change - used in CraftLivingEntity#hasLineOfSight(Location) and CraftWorld#lineOfSightExists -- return vec3d1.distanceToSqr(vec3d) > 128D * 128D ? false : this.level.clip(new ClipContext(vec3d, vec3d1, ClipContext.Block.COLLIDER, ClipContext.Fluid.NONE, this)).getType() == HitResult.Type.MISS; // Paper - use distanceToSqr -+ // Pufferfish start -+ //return vec3d1.distanceToSqr(vec3d) > 128D * 128D ? false : this.level.clip(new ClipContext(vec3d, vec3d1, ClipContext.Block.COLLIDER, ClipContext.Fluid.NONE, this)).getType() == HitResult.Type.MISS; // Paper - use distanceToSqr -+ return vec3d1.distanceToSqr(vec3d) > 128D * 128D ? false : this.level.rayTraceDirect(vec3d, vec3d1, net.minecraft.world.phys.shapes.CollisionContext.of(this)) == net.minecraft.world.phys.BlockHitResult.Type.MISS; -+ // Pufferfish end - } - } - -diff --git a/src/main/java/net/minecraft/world/entity/Mob.java b/src/main/java/net/minecraft/world/entity/Mob.java -index e3227a806d9e19923783122ea94ae19e7dbe71da..8ee0a80e3427ecef9fa15b7388a8287e26a25fd6 100644 ---- a/src/main/java/net/minecraft/world/entity/Mob.java -+++ b/src/main/java/net/minecraft/world/entity/Mob.java -@@ -205,14 +205,16 @@ public abstract class Mob extends LivingEntity { - return this.lookControl; - } - -+ int _pufferfish_inactiveTickDisableCounter = 0; // Pufferfish - throttle inactive goal selector ticking - // Paper start - @Override - public void inactiveTick() { - super.inactiveTick(); -- if (this.goalSelector.inactiveTick()) { -+ boolean isThrottled = gg.pufferfish.pufferfish.PufferfishConfig.throttleInactiveGoalSelectorTick && _pufferfish_inactiveTickDisableCounter++ % 20 != 0; // Pufferfish - throttle inactive goal selector ticking -+ if (this.goalSelector.inactiveTick(this.activatedPriority, true) && !isThrottled) { // Pufferfish - pass activated priroity // Pufferfish - throttle inactive goal selector ticking - this.goalSelector.tick(); - } -- if (this.targetSelector.inactiveTick()) { -+ if (this.targetSelector.inactiveTick(this.activatedPriority, true)) { // Pufferfish - pass activated priority - this.targetSelector.tick(); - } - } -@@ -843,16 +845,20 @@ public abstract class Mob extends LivingEntity { - - if (i % 2 != 0 && this.tickCount > 1) { - this.level.getProfiler().push("targetSelector"); -+ if (this.targetSelector.inactiveTick(this.activatedPriority, false)) // Pufferfish - use this to alternate ticking - this.targetSelector.tickRunningGoals(false); - this.level.getProfiler().pop(); - this.level.getProfiler().push("goalSelector"); -+ if (this.goalSelector.inactiveTick(this.activatedPriority, false)) // Pufferfish - use this to alternate ticking - this.goalSelector.tickRunningGoals(false); - this.level.getProfiler().pop(); - } else { - this.level.getProfiler().push("targetSelector"); -+ if (this.targetSelector.inactiveTick(this.activatedPriority, false)) // Pufferfish - use this to alternate ticking - this.targetSelector.tick(); - this.level.getProfiler().pop(); - this.level.getProfiler().push("goalSelector"); -+ if (this.goalSelector.inactiveTick(this.activatedPriority, false)) // Pufferfish - use this to alternate ticking - this.goalSelector.tick(); - this.level.getProfiler().pop(); - } -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 c770ee21b7b699522941f6a1584d532001c04082..9bce290eb0c2cfef4896a3f2076c80bf3d76bd56 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,9 +22,11 @@ public class AttributeMap { - private final Map attributes = Maps.newHashMap(); - private final Set dirtyAttributes = Sets.newHashSet(); - private final AttributeSupplier supplier; -+ private final java.util.function.Function createInstance; // Pufferfish - - public AttributeMap(AttributeSupplier defaultAttributes) { - this.supplier = defaultAttributes; -+ this.createInstance = attribute -> this.supplier.createInstance(this::onAttributeModified, attribute); // Pufferfish - } - - private void onAttributeModified(AttributeInstance instance) { -@@ -44,11 +46,10 @@ public class AttributeMap { - }).collect(Collectors.toList()); - } - -+ - @Nullable - public AttributeInstance getInstance(Attribute attribute) { -- return this.attributes.computeIfAbsent(attribute, (attributex) -> { -- return this.supplier.createInstance(this::onAttributeModified, attributex); -- }); -+ return this.attributes.computeIfAbsent(attribute, this.createInstance); // Pufferfish - cache lambda, as for some reason java allocates it anyways - } - - public boolean hasAttribute(Attribute attribute) { -diff --git a/src/main/java/net/minecraft/world/entity/ai/behavior/AcquirePoi.java b/src/main/java/net/minecraft/world/entity/ai/behavior/AcquirePoi.java -index 63c997ba5e7194218e844480419c6b59a4d26e9a..6492ed0cca1158d06d8a50ca9d1fca5288d8310b 100644 ---- a/src/main/java/net/minecraft/world/entity/ai/behavior/AcquirePoi.java -+++ b/src/main/java/net/minecraft/world/entity/ai/behavior/AcquirePoi.java -@@ -67,6 +67,7 @@ public class AcquirePoi extends Behavior { - @Override - protected void start(ServerLevel world, PathfinderMob entity, long time) { - this.nextScheduledStart = time + 20L + (long)world.getRandom().nextInt(20); -+ if (entity.getNavigation().isStuck()) this.nextScheduledStart += 200L; // Pufferfish - wait an additional 10s to check again if they're stuck - PoiManager poiManager = world.getPoiManager(); - this.batchCache.long2ObjectEntrySet().removeIf((entry) -> { - return !entry.getValue().isStillValid(time); -diff --git a/src/main/java/net/minecraft/world/entity/ai/behavior/VillagerPanicTrigger.java b/src/main/java/net/minecraft/world/entity/ai/behavior/VillagerPanicTrigger.java -index 42d466f7f162943886078eba3db18f2dfc2d7bee..6c0dda1ce018ec6bb2ebb97147045fffae0a89d5 100644 ---- a/src/main/java/net/minecraft/world/entity/ai/behavior/VillagerPanicTrigger.java -+++ b/src/main/java/net/minecraft/world/entity/ai/behavior/VillagerPanicTrigger.java -@@ -37,7 +37,11 @@ public class VillagerPanicTrigger extends Behavior { - - @Override - protected void tick(ServerLevel serverLevel, Villager villager, long l) { -- if (l % 100L == 0L) { -+ // Pufferfish start -+ if (villager.nextGolemPanic < 0) villager.nextGolemPanic = l + 100; -+ if (--villager.nextGolemPanic < l) { -+ villager.nextGolemPanic = -1; -+ // Pufferfish end - villager.spawnGolemIfNeeded(serverLevel, l, 3); - } - -diff --git a/src/main/java/net/minecraft/world/entity/ai/goal/GoalSelector.java b/src/main/java/net/minecraft/world/entity/ai/goal/GoalSelector.java -index a910189177da0c1134c954b3d81b9e9bc4bc1420..0cc0d719e95e108263683b7a40f4ce3a8ca9465b 100644 ---- a/src/main/java/net/minecraft/world/entity/ai/goal/GoalSelector.java -+++ b/src/main/java/net/minecraft/world/entity/ai/goal/GoalSelector.java -@@ -50,9 +50,12 @@ public class GoalSelector { - } - - // Paper start -- public boolean inactiveTick() { -+ public boolean inactiveTick(int tickRate, boolean inactive) { // Pufferfish start -+ if (inactive && !gg.pufferfish.pufferfish.PufferfishConfig.dearEnabled) tickRate = 4; // reset to Paper's -+ tickRate = Math.min(tickRate, this.newGoalRate); - this.curRate++; -- return this.curRate % this.newGoalRate == 0; -+ return this.curRate % tickRate == 0; -+ // Pufferfish end - } - public boolean hasTasks() { - for (WrappedGoal task : this.availableGoals) { -diff --git a/src/main/java/net/minecraft/world/entity/ai/goal/MoveToBlockGoal.java b/src/main/java/net/minecraft/world/entity/ai/goal/MoveToBlockGoal.java -index 26bf383caea68834c654b25653ced9017f1b1b22..615eb55e24d365d994fbfe9d45d2be387fd5d561 100644 ---- a/src/main/java/net/minecraft/world/entity/ai/goal/MoveToBlockGoal.java -+++ b/src/main/java/net/minecraft/world/entity/ai/goal/MoveToBlockGoal.java -@@ -119,6 +119,7 @@ public abstract class MoveToBlockGoal extends Goal { - for(int m = 0; m <= l; m = m > 0 ? -m : 1 - m) { - for(int n = m < l && m > -l ? l : 0; n <= l; n = n > 0 ? -n : 1 - n) { - mutableBlockPos.setWithOffset(blockPos, m, k - 1, n); -+ if (!this.mob.level.hasChunkAt(mutableBlockPos)) continue; // Pufferfish - if this block isn't loaded, continue - if (this.mob.isWithinRestriction(mutableBlockPos) && this.isValidTarget(this.mob.level, mutableBlockPos)) { - this.blockPos = mutableBlockPos; - setTargetPosition(mutableBlockPos.immutable()); // Paper -diff --git a/src/main/java/net/minecraft/world/entity/ai/targeting/TargetingConditions.java b/src/main/java/net/minecraft/world/entity/ai/targeting/TargetingConditions.java -index a7575b5ef56af6f53448d391abb4956e130148ca..e752c83df50fb9b670ecea2abc95426c2a009b6f 100644 ---- a/src/main/java/net/minecraft/world/entity/ai/targeting/TargetingConditions.java -+++ b/src/main/java/net/minecraft/world/entity/ai/targeting/TargetingConditions.java -@@ -75,9 +75,18 @@ public class TargetingConditions { - } - - if (this.range > 0.0D) { -- double d = this.testInvisible ? targetEntity.getVisibilityPercent(baseEntity) : 1.0D; -- double e = Math.max((this.useFollowRange ? this.getFollowRange(baseEntity) : this.range) * d, 2.0D); // Paper -+ // Pufferfish start - check range before getting visibility -+ // d = invisibility percent, e = follow range adjusted for invisibility, f = distance - double f = baseEntity.distanceToSqr(targetEntity.getX(), targetEntity.getY(), targetEntity.getZ()); -+ double followRangeRaw = this.useFollowRange ? this.getFollowRange(baseEntity) : this.range; -+ -+ if (f > followRangeRaw * followRangeRaw) { // the actual follow range will always be this value or smaller, so if the distance is larger then it never will return true after getting invis -+ return false; -+ } -+ -+ double d = this.testInvisible ? targetEntity.getVisibilityPercent(baseEntity) : 1.0D; -+ double e = Math.max((followRangeRaw) * d, 2.0D); // Paper -+ // Pufferfish end - if (f > e * e) { - return false; - } -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 ed617223942b410fb6f799be7db2001909db67ed..f11eb6df9f095109378caaf0fd16575bfb2e4976 100644 ---- a/src/main/java/net/minecraft/world/entity/ambient/Bat.java -+++ b/src/main/java/net/minecraft/world/entity/ambient/Bat.java -@@ -253,13 +253,22 @@ public class Bat extends AmbientCreature { - } - } - -+ // Pufferfish start - only check for spooky season once an hour -+ private static boolean isSpookySeason = false; -+ private static final int ONE_HOUR = 20 * 60 * 60; -+ private static int lastSpookyCheck = -ONE_HOUR; - private static boolean isHalloween() { -+ if (net.minecraft.server.MinecraftServer.currentTick - lastSpookyCheck > ONE_HOUR) { - LocalDate localdate = LocalDate.now(); - int i = localdate.get(ChronoField.DAY_OF_MONTH); - int j = localdate.get(ChronoField.MONTH_OF_YEAR); - -- return j == 10 && i >= 20 || j == 11 && i <= 3; -+ isSpookySeason = j == 10 && i >= 20 || j == 11 && i <= 3; -+ lastSpookyCheck = net.minecraft.server.MinecraftServer.currentTick; -+ } -+ return isSpookySeason; - } -+ // Pufferfish end - - @Override - protected float getStandingEyeHeight(Pose pose, EntityDimensions dimensions) { -diff --git a/src/main/java/net/minecraft/world/entity/animal/axolotl/Axolotl.java b/src/main/java/net/minecraft/world/entity/animal/axolotl/Axolotl.java -index e5617c013f1bd6f89dfbb2ccd404489180cd0573..bdc65bf098fd3107e36d3738583455e87af82cf4 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/axolotl/Axolotl.java -+++ b/src/main/java/net/minecraft/world/entity/animal/axolotl/Axolotl.java -@@ -283,9 +283,11 @@ public class Axolotl extends Animal implements LerpingModel, Bucketable { - return true; - } - -+ private int behaviorTick = 0; // Pufferfish - @Override - protected void customServerAiStep() { - this.level.getProfiler().push("axolotlBrain"); -+ if (this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish - this.getBrain().tick((ServerLevel) this.level, this); - this.level.getProfiler().pop(); - this.level.getProfiler().push("axolotlActivityUpdate"); -diff --git a/src/main/java/net/minecraft/world/entity/animal/goat/Goat.java b/src/main/java/net/minecraft/world/entity/animal/goat/Goat.java -index 78f1082b0a3bad923c1e142d15bc7dad2ae5ff15..042e852cd440e62003b5e72d92728e2dc602f279 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/goat/Goat.java -+++ b/src/main/java/net/minecraft/world/entity/animal/goat/Goat.java -@@ -141,9 +141,11 @@ public class Goat extends Animal { - return (Brain) super.getBrain(); // CraftBukkit - decompile error - } - -+ private int behaviorTick = 0; // Pufferfish - @Override - protected void customServerAiStep() { - this.level.getProfiler().push("goatBrain"); -+ if (this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish - this.getBrain().tick((ServerLevel) this.level, this); - this.level.getProfiler().pop(); - this.level.getProfiler().push("goatActivityUpdate"); -diff --git a/src/main/java/net/minecraft/world/entity/item/ItemEntity.java b/src/main/java/net/minecraft/world/entity/item/ItemEntity.java -index 336d753bbd836e2a08207c5e3d41acbe2b72543f..dae989bf664b9ab29d13825d5fce32e19852259e 100644 ---- a/src/main/java/net/minecraft/world/entity/item/ItemEntity.java -+++ b/src/main/java/net/minecraft/world/entity/item/ItemEntity.java -@@ -243,10 +243,16 @@ public class ItemEntity extends Entity { - if (entityitem.isMergable()) { - // Paper Start - Fix items merging through walls - if (this.level.paperConfig.fixItemsMergingThroughWalls) { -+ // Pufferfish start - skip the allocations -+ /* - net.minecraft.world.level.ClipContext rayTrace = new net.minecraft.world.level.ClipContext(this.position(), entityitem.position(), - net.minecraft.world.level.ClipContext.Block.COLLIDER, net.minecraft.world.level.ClipContext.Fluid.NONE, this); - net.minecraft.world.phys.BlockHitResult rayTraceResult = level.clip(rayTrace); - if (rayTraceResult.getType() == net.minecraft.world.phys.HitResult.Type.BLOCK) continue; -+ */ -+ if (level.rayTraceDirect(this.position(), entityitem.position(), net.minecraft.world.phys.shapes.CollisionContext.of(this)) == -+ net.minecraft.world.phys.HitResult.Type.BLOCK) continue; -+ // Pufferfish end - } - // Paper End - this.tryToMerge(entityitem); -diff --git a/src/main/java/net/minecraft/world/entity/monster/EnderMan.java b/src/main/java/net/minecraft/world/entity/monster/EnderMan.java -index 411eb4d495907aa1d646673bf07d56ff01e52eee..1d9c542f3fe7da7bb9ea8b13496651a7b3dcde07 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/EnderMan.java -+++ b/src/main/java/net/minecraft/world/entity/monster/EnderMan.java -@@ -317,11 +317,17 @@ public class EnderMan extends Monster implements NeutralMob { - private boolean teleport(double x, double y, double z) { - BlockPos.MutableBlockPos blockposition_mutableblockposition = new BlockPos.MutableBlockPos(x, y, z); - -- while (blockposition_mutableblockposition.getY() > this.level.getMinBuildHeight() && !this.level.getBlockState(blockposition_mutableblockposition).getMaterial().blocksMotion()) { -+ // Pufferfish start - single chunk lookup -+ net.minecraft.world.level.chunk.LevelChunk chunk = this.level.getChunkIfLoaded(blockposition_mutableblockposition); -+ if (chunk == null) { -+ return false; -+ } -+ // Pufferfish end -+ while (blockposition_mutableblockposition.getY() > this.level.getMinBuildHeight() && !chunk.getBlockState(blockposition_mutableblockposition).getMaterial().blocksMotion()) { // Pufferfish - blockposition_mutableblockposition.move(Direction.DOWN); - } - -- BlockState iblockdata = this.level.getBlockState(blockposition_mutableblockposition); -+ BlockState iblockdata = chunk.getBlockState(blockposition_mutableblockposition); // Pufferfish - boolean flag = iblockdata.getMaterial().blocksMotion(); - boolean flag1 = iblockdata.getFluidState().is(FluidTags.WATER); - -diff --git a/src/main/java/net/minecraft/world/entity/monster/hoglin/Hoglin.java b/src/main/java/net/minecraft/world/entity/monster/hoglin/Hoglin.java -index 49ca4f6a9e33fdb5295dae2b059d071551353c24..127c839c4f9b0ade39065588e275984ea639380b 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/hoglin/Hoglin.java -+++ b/src/main/java/net/minecraft/world/entity/monster/hoglin/Hoglin.java -@@ -126,9 +126,11 @@ public class Hoglin extends Animal implements Enemy, HoglinBase { - return (Brain) super.getBrain(); // Paper - decompile fix - } - -+ private int behaviorTick; // Pufferfish - @Override - protected void customServerAiStep() { - this.level.getProfiler().push("hoglinBrain"); -+ if (this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish - this.getBrain().tick((ServerLevel)this.level, this); - this.level.getProfiler().pop(); - HoglinAi.updateActivity(this); -diff --git a/src/main/java/net/minecraft/world/entity/monster/piglin/Piglin.java b/src/main/java/net/minecraft/world/entity/monster/piglin/Piglin.java -index 63cfda124b5bad20af3050a0b62d73883b6abf7d..05e05793b1fd10cceb4b19ac60c8a10b0c5b7d45 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/piglin/Piglin.java -+++ b/src/main/java/net/minecraft/world/entity/monster/piglin/Piglin.java -@@ -288,9 +288,11 @@ public class Piglin extends AbstractPiglin implements CrossbowAttackMob, Invento - return !this.cannotHunt; - } - -+ private int behaviorTick; // Pufferfish - @Override - protected void customServerAiStep() { - this.level.getProfiler().push("piglinBrain"); -+ if (this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish - this.getBrain().tick((ServerLevel) this.level, this); - this.level.getProfiler().pop(); - PiglinAi.updateActivity(this); -diff --git a/src/main/java/net/minecraft/world/entity/npc/Villager.java b/src/main/java/net/minecraft/world/entity/npc/Villager.java -index 268524e256a034520438d5c825e5e419d72d29ce..63903bd02c6f31d3190a20d106997d4f7b777df8 100644 ---- a/src/main/java/net/minecraft/world/entity/npc/Villager.java -+++ b/src/main/java/net/minecraft/world/entity/npc/Villager.java -@@ -140,6 +140,8 @@ public class Villager extends AbstractVillager implements ReputationEventHandler - return villageplacetype == PoiType.MEETING; - }); - -+ public long nextGolemPanic = -1; // Pufferfish -+ - public Villager(EntityType entityType, Level world) { - this(entityType, world, VillagerType.PLAINS); - } -@@ -243,11 +245,17 @@ public class Villager extends AbstractVillager implements ReputationEventHandler - } - // Spigot End - -+ private int behaviorTick = 0; // Pufferfish - @Override - protected void customServerAiStep() { mobTick(false); } - protected void mobTick(boolean inactive) { - this.level.getProfiler().push("villagerBrain"); -- if (!inactive) this.getBrain().tick((ServerLevel) this.level, this); // Paper -+ // Pufferfish start -+ if (!inactive) { -+ if (this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish -+ this.getBrain().tick((ServerLevel) this.level, this); // Paper -+ } -+ // Pufferfish end - this.level.getProfiler().pop(); - if (this.assignProfessionWhenSpawned) { - this.assignProfessionWhenSpawned = false; -diff --git a/src/main/java/net/minecraft/world/entity/player/Inventory.java b/src/main/java/net/minecraft/world/entity/player/Inventory.java -index b770e7840b00b46a127ae05cc872a21f7a2dffad..772200b9efe232cb407e460f2353a156c1a92c89 100644 ---- a/src/main/java/net/minecraft/world/entity/player/Inventory.java -+++ b/src/main/java/net/minecraft/world/entity/player/Inventory.java -@@ -682,6 +682,8 @@ public class Inventory implements Container, Nameable { - } - - public boolean contains(ItemStack stack) { -+ // Pufferfish start - don't allocate iterators -+ /* - Iterator iterator = this.compartments.iterator(); - - while (iterator.hasNext()) { -@@ -696,6 +698,18 @@ public class Inventory implements Container, Nameable { - } - } - } -+ */ -+ for (int i = 0; i < this.compartments.size(); i++) { -+ List list = this.compartments.get(i); -+ for (int j = 0; j < list.size(); j++) { -+ ItemStack itemstack1 = list.get(j); -+ -+ if (!itemstack1.isEmpty() && itemstack1.sameItem(stack)) { -+ return true; -+ } -+ } -+ } -+ // Pufferfish end - - return false; - } -diff --git a/src/main/java/net/minecraft/world/entity/projectile/Projectile.java b/src/main/java/net/minecraft/world/entity/projectile/Projectile.java -index ccaa97956e40c6a79371d813719f65687991ec5b..0c7d31c7ffd603e54fb19010331ad220c7b15627 100644 ---- a/src/main/java/net/minecraft/world/entity/projectile/Projectile.java -+++ b/src/main/java/net/minecraft/world/entity/projectile/Projectile.java -@@ -42,6 +42,36 @@ public abstract class Projectile extends Entity { - super(type, world); - } - -+ // Pufferfish start -+ private static int loadedThisTick = 0; -+ private static int loadedTick; -+ -+ private int loadedLifetime = 0; -+ @Override -+ public void setPos(double x, double y, double z) { -+ int currentTick = net.minecraft.server.MinecraftServer.currentTick; -+ if (loadedTick != currentTick) { -+ loadedTick = currentTick; -+ loadedThisTick = 0; -+ } -+ int previousX = Mth.floor(this.getX()) >> 4, previousZ = Mth.floor(this.getZ()) >> 4; -+ int newX = Mth.floor(x) >> 4, newZ = Mth.floor(z) >> 4; -+ if (previousX != newX || previousZ != newZ) { -+ boolean isLoaded = ((net.minecraft.server.level.ServerChunkCache) this.level.getChunkSource()).getChunkAtIfLoadedMainThread(newX, newZ) != null; -+ if (!isLoaded) { -+ if (Projectile.loadedThisTick > gg.pufferfish.pufferfish.PufferfishConfig.maxProjectileLoadsPerTick) { -+ if (++this.loadedLifetime > gg.pufferfish.pufferfish.PufferfishConfig.maxProjectileLoadsPerProjectile) { -+ this.discard(); -+ } -+ return; -+ } -+ Projectile.loadedThisTick++; -+ } -+ } -+ super.setPos(x, y, z); -+ } -+ // Pufferfish start -+ - public void setOwner(@Nullable Entity entity) { - if (entity != null) { - this.ownerUUID = entity.getUUID(); -diff --git a/src/main/java/net/minecraft/world/entity/vehicle/AbstractMinecartContainer.java b/src/main/java/net/minecraft/world/entity/vehicle/AbstractMinecartContainer.java -index 8a07d5d25086d7544757bb86181fbe2b5e743d29..9be1ce7f5b956f0c82151ea02e555e7dd4a57241 100644 ---- a/src/main/java/net/minecraft/world/entity/vehicle/AbstractMinecartContainer.java -+++ b/src/main/java/net/minecraft/world/entity/vehicle/AbstractMinecartContainer.java -@@ -40,7 +40,10 @@ import org.bukkit.inventory.InventoryHolder; - - public abstract class AbstractMinecartContainer extends AbstractMinecart implements Container, MenuProvider { - -+ // Pufferfish start - private NonNullList itemStacks; -+ private gg.airplane.structs.ItemListWithBitset itemStacksOptimized; -+ // Pufferfish end - @Nullable - public ResourceLocation lootTable; - public long lootTableSeed; -@@ -89,12 +92,18 @@ public abstract class AbstractMinecartContainer extends AbstractMinecart impleme - - protected AbstractMinecartContainer(EntityType type, Level world) { - super(type, world); -- this.itemStacks = NonNullList.withSize(this.getContainerSize(), ItemStack.EMPTY); // CraftBukkit - SPIGOT-3513 -+ // Pufferfish start -+ this.itemStacksOptimized = new gg.airplane.structs.ItemListWithBitset(this.getContainerSize()); // CraftBukkit - SPIGOT-3513 -+ this.itemStacks = this.itemStacksOptimized.nonNullList; -+ // Pufferfish end - } - - protected AbstractMinecartContainer(EntityType type, double x, double y, double z, Level world) { - super(type, world, x, y, z); -- this.itemStacks = NonNullList.withSize(this.getContainerSize(), ItemStack.EMPTY); // CraftBukkit - SPIGOT-3513 -+ // Pufferfish start -+ this.itemStacksOptimized = new gg.airplane.structs.ItemListWithBitset(this.getContainerSize()); // CraftBukkit - SPIGOT-3513 -+ this.itemStacks = this.itemStacksOptimized.nonNullList; -+ // Pufferfish end - } - - @Override -@@ -217,7 +226,10 @@ public abstract class AbstractMinecartContainer extends AbstractMinecart impleme - protected void readAdditionalSaveData(CompoundTag nbt) { - super.readAdditionalSaveData(nbt); - this.lootableData.loadNbt(nbt); // Paper -- this.itemStacks = NonNullList.withSize(this.getContainerSize(), ItemStack.EMPTY); -+ // Pufferfish start -+ this.itemStacksOptimized = new gg.airplane.structs.ItemListWithBitset(this.getContainerSize()); -+ this.itemStacks = this.itemStacksOptimized.nonNullList; -+ // Pufferfish end - if (nbt.contains("LootTable", 8)) { - this.lootTable = new ResourceLocation(nbt.getString("LootTable")); - this.lootTableSeed = nbt.getLong("LootTableSeed"); -diff --git a/src/main/java/net/minecraft/world/item/crafting/ShapelessRecipe.java b/src/main/java/net/minecraft/world/item/crafting/ShapelessRecipe.java -index ffe5476d8ed15ee4384b679c341688787205ce59..9051559e78851257a56a998b4b882ebbcc394639 100644 ---- a/src/main/java/net/minecraft/world/item/crafting/ShapelessRecipe.java -+++ b/src/main/java/net/minecraft/world/item/crafting/ShapelessRecipe.java -@@ -25,8 +25,13 @@ public class ShapelessRecipe implements CraftingRecipe { - final String group; - final ItemStack result; - final NonNullList ingredients; -+ private final boolean isBukkit; // Pufferfish - -+ // Pufferfish start - public ShapelessRecipe(ResourceLocation id, String group, ItemStack output, NonNullList input) { -+ this(id, group, output, input, false); -+ } -+ public ShapelessRecipe(ResourceLocation id, String group, ItemStack output, NonNullList input, boolean isBukkit) { this.isBukkit = isBukkit; // Pufferfish end - this.id = id; - this.group = group; - this.result = output; -@@ -73,6 +78,28 @@ public class ShapelessRecipe implements CraftingRecipe { - } - - public boolean matches(CraftingContainer inventory, Level world) { -+ // Pufferfish start -+ if (!this.isBukkit) { -+ java.util.List ingredients = com.google.common.collect.Lists.newArrayList(this.ingredients.toArray(new Ingredient[0])); -+ -+ inventory: for (int index = 0; index < inventory.getContainerSize(); index++) { -+ ItemStack itemStack = inventory.getItem(index); -+ -+ if (!itemStack.isEmpty()) { -+ for (int i = 0; i < ingredients.size(); i++) { -+ if (ingredients.get(i).test(itemStack)) { -+ ingredients.remove(i); -+ continue inventory; -+ } -+ } -+ return false; -+ } -+ } -+ -+ return ingredients.isEmpty(); -+ } -+ // Pufferfish end -+ - StackedContents autorecipestackmanager = new StackedContents(); - int i = 0; - -diff --git a/src/main/java/net/minecraft/world/level/BlockGetter.java b/src/main/java/net/minecraft/world/level/BlockGetter.java -index d1eefa6ef3e9abfe7af4d8310aa64465fa2d5463..0f4aa330e5b179bb706a31917c671f165e22b9cd 100644 ---- a/src/main/java/net/minecraft/world/level/BlockGetter.java -+++ b/src/main/java/net/minecraft/world/level/BlockGetter.java -@@ -73,6 +73,16 @@ public interface BlockGetter extends LevelHeightAccessor { - }); - } - -+ // Pufferfish start - broken down variant of below rayTraceBlock, used by World#rayTraceDirect -+ default net.minecraft.world.phys.BlockHitResult.Type rayTraceBlockDirect(Vec3 vec3d, Vec3 vec3d1, BlockPos blockposition, BlockState iblockdata, net.minecraft.world.phys.shapes.CollisionContext voxelshapecoll) { -+ if (iblockdata.isAir()) return null; // Tuinity - optimise air cases -+ VoxelShape voxelshape = ClipContext.Block.COLLIDER.get(iblockdata, this, blockposition, voxelshapecoll); -+ net.minecraft.world.phys.BlockHitResult movingobjectpositionblock = this.clipWithInteractionOverride(vec3d, vec3d1, blockposition, voxelshape, iblockdata); -+ -+ return movingobjectpositionblock == null ? null : movingobjectpositionblock.getType(); -+ } -+ // Pufferfish end -+ - // CraftBukkit start - moved block handling into separate method for use by Block#rayTrace - default BlockHitResult clip(ClipContext raytrace1, BlockPos blockposition) { - // Paper start - Prevent raytrace from loading chunks -diff --git a/src/main/java/net/minecraft/world/level/GameRules.java b/src/main/java/net/minecraft/world/level/GameRules.java -index 22feab6477bad023c2d6cc9ac99d392d0afe0a31..fb2626d2a6babe531dd1849333b119ab36200d80 100644 ---- a/src/main/java/net/minecraft/world/level/GameRules.java -+++ b/src/main/java/net/minecraft/world/level/GameRules.java -@@ -90,6 +90,7 @@ public class GameRules { - public static final GameRules.Key RULE_UNIVERSAL_ANGER = GameRules.register("universalAnger", GameRules.Category.MOBS, GameRules.BooleanValue.create(false)); - public static final GameRules.Key RULE_PLAYERS_SLEEPING_PERCENTAGE = GameRules.register("playersSleepingPercentage", GameRules.Category.PLAYER, GameRules.IntegerValue.create(100)); - private final Map, GameRules.Value> rules; -+ private final GameRules.Value[] gameruleArray; - - private static > GameRules.Key register(String name, GameRules.Category category, GameRules.Type type) { - GameRules.Key gamerules_gamerulekey = new GameRules.Key<>(name, category); -@@ -108,17 +109,33 @@ public class GameRules { - } - - public GameRules() { -- this.rules = (Map) GameRules.GAME_RULE_TYPES.entrySet().stream().collect(ImmutableMap.toImmutableMap(Entry::getKey, (entry) -> { -+ // Pufferfish start - use this to ensure gameruleArray is initialized -+ this((Map) GameRules.GAME_RULE_TYPES.entrySet().stream().collect(ImmutableMap.toImmutableMap(Entry::getKey, (entry) -> { - return ((GameRules.Type) entry.getValue()).createRule(); -- })); -+ }))); -+ // Pufferfish end - } - - private GameRules(Map, GameRules.Value> rules) { - this.rules = rules; -+ -+ // Pufferfish start -+ int arraySize = rules.keySet().stream().mapToInt(key -> key.gameRuleIndex).max().orElse(-1) + 1; -+ GameRules.Value[] values = new GameRules.Value[arraySize]; -+ -+ for (Entry, GameRules.Value> entry : rules.entrySet()) { -+ values[entry.getKey().gameRuleIndex] = entry.getValue(); -+ } -+ -+ this.gameruleArray = values; -+ // Pufferfish end - } - - public > T getRule(GameRules.Key key) { -- return (T) this.rules.get(key); // CraftBukkit - decompile error -+ // Pufferfish start -+ return key == null ? null : (T) this.gameruleArray[key.gameRuleIndex]; -+ //return (T) this.rules.get(key); // CraftBukkit - decompile error -+ // Pufferfish end - } - - public CompoundTag createTag() { -@@ -177,6 +194,10 @@ public class GameRules { - } - - public static final class Key> { -+ // Pufferfish start -+ private static int lastGameRuleIndex = 0; -+ public final int gameRuleIndex = lastGameRuleIndex++; -+ // Pufferfish end - - final String id; - private final GameRules.Category category; -diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java -index fee8996f35b38fd79946cdfd677763e0201eb57d..b830c2199a22252116619520f407fd84eddb28f3 100644 ---- a/src/main/java/net/minecraft/world/level/Level.java -+++ b/src/main/java/net/minecraft/world/level/Level.java -@@ -261,6 +261,15 @@ public abstract class Level implements LevelAccessor, AutoCloseable { - - public abstract ResourceKey getTypeKey(); - -+ // Pufferfish start - ensure these get inlined -+ private final int minBuildHeight, minSection, height, maxBuildHeight, maxSection; -+ @Override public final int getMaxBuildHeight() { return this.maxBuildHeight; } -+ @Override public final int getMinSection() { return this.minSection; } -+ @Override public final int getMaxSection() { return this.maxSection; } -+ @Override public final int getMinBuildHeight() { return this.minBuildHeight; } -+ @Override public final int getHeight() { return this.height; } -+ // Pufferfish end -+ - protected Level(WritableLevelData worlddatamutable, ResourceKey resourcekey, Holder holder, Supplier supplier, boolean flag, boolean flag1, long i, org.bukkit.generator.ChunkGenerator gen, org.bukkit.generator.BiomeProvider biomeProvider, org.bukkit.World.Environment env, java.util.concurrent.Executor executor) { // Paper - Async-Anti-Xray - Pass executor - this.spigotConfig = new org.spigotmc.SpigotWorldConfig(((net.minecraft.world.level.storage.PrimaryLevelData) worlddatamutable).getLevelName()); // Spigot - this.paperConfig = new com.destroystokyo.paper.PaperWorldConfig(((net.minecraft.world.level.storage.PrimaryLevelData) worlddatamutable).getLevelName(), this.spigotConfig); // Paper -@@ -279,6 +288,13 @@ public abstract class Level implements LevelAccessor, AutoCloseable { - this.levelData = worlddatamutable; - this.dimensionTypeRegistration = holder; - this.dimensionType = (DimensionType) holder.value(); -+ // Pufferfish start -+ this.minBuildHeight = dimensionType.minY(); -+ this.minSection = SectionPos.blockToSectionCoord(this.minBuildHeight); -+ this.height = dimensionType.height(); -+ this.maxBuildHeight = this.minBuildHeight + this.height; -+ this.maxSection = SectionPos.blockToSectionCoord(this.maxBuildHeight - 1) + 1; -+ // Pufferfish end - this.dimension = resourcekey; - this.isClientSide = flag; - if (this.dimensionType.coordinateScale() != 1.0D) { -@@ -394,6 +410,91 @@ public abstract class Level implements LevelAccessor, AutoCloseable { - return null; - } - -+ // Pufferfish start - broken down method of raytracing for EntityLiving#hasLineOfSight, replaces IBlockAccess#rayTrace(RayTrace) -+ public net.minecraft.world.phys.BlockHitResult.Type rayTraceDirect(net.minecraft.world.phys.Vec3 vec3d, net.minecraft.world.phys.Vec3 vec3d1, net.minecraft.world.phys.shapes.CollisionContext voxelshapecoll) { -+ // most of this code comes from IBlockAccess#a(RayTrace, BiFunction, Function), but removes the needless functions -+ if (vec3d.equals(vec3d1)) { -+ return net.minecraft.world.phys.BlockHitResult.Type.MISS; -+ } -+ -+ double endX = Mth.lerp(-1.0E-7D, vec3d1.x, vec3d.x); -+ double endY = Mth.lerp(-1.0E-7D, vec3d1.y, vec3d.y); -+ double endZ = Mth.lerp(-1.0E-7D, vec3d1.z, vec3d.z); -+ -+ double startX = Mth.lerp(-1.0E-7D, vec3d.x, vec3d1.x); -+ double startY = Mth.lerp(-1.0E-7D, vec3d.y, vec3d1.y); -+ double startZ = Mth.lerp(-1.0E-7D, vec3d.z, vec3d1.z); -+ -+ int currentX = Mth.floor(startX); -+ int currentY = Mth.floor(startY); -+ int currentZ = Mth.floor(startZ); -+ -+ BlockPos.MutableBlockPos currentBlock = new BlockPos.MutableBlockPos(currentX, currentY, currentZ); -+ -+ LevelChunk chunk = this.getChunkIfLoaded(currentBlock); -+ if (chunk == null) { -+ return net.minecraft.world.phys.BlockHitResult.Type.MISS; -+ } -+ -+ net.minecraft.world.phys.BlockHitResult.Type initialCheck = this.rayTraceBlockDirect(vec3d, vec3d1, currentBlock, chunk.getBlockState(currentBlock), voxelshapecoll); -+ -+ if (initialCheck != null) { -+ return initialCheck; -+ } -+ -+ double diffX = endX - startX; -+ double diffY = endY - startY; -+ double diffZ = endZ - startZ; -+ -+ int xDirection = Mth.sign(diffX); -+ int yDirection = Mth.sign(diffY); -+ int zDirection = Mth.sign(diffZ); -+ -+ double normalizedX = xDirection == 0 ? Double.MAX_VALUE : (double) xDirection / diffX; -+ double normalizedY = yDirection == 0 ? Double.MAX_VALUE : (double) yDirection / diffY; -+ double normalizedZ = zDirection == 0 ? Double.MAX_VALUE : (double) zDirection / diffZ; -+ -+ double normalizedXDirection = normalizedX * (xDirection > 0 ? 1.0D - Mth.frac(startX) : Mth.frac(startX)); -+ double normalizedYDirection = normalizedY * (yDirection > 0 ? 1.0D - Mth.frac(startY) : Mth.frac(startY)); -+ double normalizedZDirection = normalizedZ * (zDirection > 0 ? 1.0D - Mth.frac(startZ) : Mth.frac(startZ)); -+ -+ net.minecraft.world.phys.BlockHitResult.Type result; -+ -+ do { -+ if (normalizedXDirection > 1.0D && normalizedYDirection > 1.0D && normalizedZDirection > 1.0D) { -+ return net.minecraft.world.phys.BlockHitResult.Type.MISS; -+ } -+ -+ if (normalizedXDirection < normalizedYDirection) { -+ if (normalizedXDirection < normalizedZDirection) { -+ currentX += xDirection; -+ normalizedXDirection += normalizedX; -+ } else { -+ currentZ += zDirection; -+ normalizedZDirection += normalizedZ; -+ } -+ } else if (normalizedYDirection < normalizedZDirection) { -+ currentY += yDirection; -+ normalizedYDirection += normalizedY; -+ } else { -+ currentZ += zDirection; -+ normalizedZDirection += normalizedZ; -+ } -+ -+ currentBlock.set(currentX, currentY, currentZ); -+ if (chunk.getPos().x != currentBlock.getX() >> 4 || chunk.getPos().z != currentBlock.getZ() >> 4) { -+ chunk = this.getChunkIfLoaded(currentBlock); -+ if (chunk == null) { -+ return net.minecraft.world.phys.BlockHitResult.Type.MISS; -+ } -+ } -+ result = this.rayTraceBlockDirect(vec3d, vec3d1, currentBlock, chunk.getBlockState(currentBlock), voxelshapecoll); -+ } while (result == null); -+ -+ return result; -+ } -+ // Pufferfish end -+ - public boolean isInWorldBounds(BlockPos pos) { - return pos.isInsideBuildHeightAndWorldBoundsHorizontal(this); // Paper - use better/optimized check - } -@@ -935,13 +1036,13 @@ public abstract class Level implements LevelAccessor, AutoCloseable { - try { - tickConsumer.accept(entity); - MinecraftServer.getServer().executeMidTickTasks(); // Paper - execute chunk tasks mid tick -- } catch (Throwable throwable) { -+ } catch (Throwable throwable) { // Pufferfish - diff on change ServerLevel.tick - if (throwable instanceof ThreadDeath) throw throwable; // Paper - // Paper start - Prevent tile entity and entity crashes - final String msg = String.format("Entity threw exception at %s:%s,%s,%s", entity.level.getWorld().getName(), entity.getX(), entity.getY(), entity.getZ()); - MinecraftServer.LOGGER.error(msg, throwable); - getCraftServer().getPluginManager().callEvent(new ServerExceptionEvent(new ServerInternalException(msg, throwable))); -- entity.discard(); -+ entity.discard(); // Pufferfish - diff on change ServerLevel.tick - // Paper end - } - } -@@ -1405,6 +1506,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable { - } - - public ProfilerFiller getProfiler() { -+ if (gg.pufferfish.pufferfish.PufferfishConfig.disableMethodProfiler) return net.minecraft.util.profiling.InactiveProfiler.INSTANCE; // Pufferfish - return (ProfilerFiller) this.profiler.get(); - } - -diff --git a/src/main/java/net/minecraft/world/level/NaturalSpawner.java b/src/main/java/net/minecraft/world/level/NaturalSpawner.java -index d87900e2a9559a34ee66e3cffab851a578c0278e..6e24b86ce315510133b6294f5d348e5c4e7f547f 100644 ---- a/src/main/java/net/minecraft/world/level/NaturalSpawner.java -+++ b/src/main/java/net/minecraft/world/level/NaturalSpawner.java -@@ -413,12 +413,12 @@ public final class NaturalSpawner { - } - } - -- private static BlockPos getRandomPosWithin(Level world, LevelChunk chunk) { -+ private static BlockPos getRandomPosWithin(ServerLevel world, LevelChunk chunk) { // Pufferfish - accept serverlevel - ChunkPos chunkcoordintpair = chunk.getPos(); -- int i = chunkcoordintpair.getMinBlockX() + world.random.nextInt(16); -- int j = chunkcoordintpair.getMinBlockZ() + world.random.nextInt(16); -+ int i = chunkcoordintpair.getMinBlockX() + world.getThreadUnsafeRandom().nextInt(16); // Pufferfish - use thread unsafe random -+ int j = chunkcoordintpair.getMinBlockZ() + world.getThreadUnsafeRandom().nextInt(16); // Pufferfish - int k = chunk.getHeight(Heightmap.Types.WORLD_SURFACE, i, j) + 1; -- int l = Mth.randomBetweenInclusive(world.random, world.getMinBuildHeight(), k); -+ int l = Mth.randomBetweenInclusive(world.getThreadUnsafeRandom(), world.getMinBuildHeight(), k); // Pufferfish - - return new BlockPos(i, l, j); - } -diff --git a/src/main/java/net/minecraft/world/level/biome/Biome.java b/src/main/java/net/minecraft/world/level/biome/Biome.java -index 32fc9f9162aafe312ed757be2d03577b5f95841d..da7e940f7a0cf96a81372e920412c7d09bab8a3c 100644 ---- a/src/main/java/net/minecraft/world/level/biome/Biome.java -+++ b/src/main/java/net/minecraft/world/level/biome/Biome.java -@@ -74,14 +74,20 @@ public final class Biome { - private final MobSpawnSettings mobSettings; - private final Biome.BiomeCategory biomeCategory; - private final BiomeSpecialEffects specialEffects; -- private final ThreadLocal temperatureCache = ThreadLocal.withInitial(() -> { -+ // Pufferfish start - use our cache -+ private final ThreadLocal temperatureCache = ThreadLocal.withInitial(() -> { - return Util.make(() -> { -+ /* - Long2FloatLinkedOpenHashMap long2FloatLinkedOpenHashMap = new Long2FloatLinkedOpenHashMap(1024, 0.25F) { - protected void rehash(int i) { - } - }; - long2FloatLinkedOpenHashMap.defaultReturnValue(Float.NaN); - return long2FloatLinkedOpenHashMap; -+ -+ */ -+ return new gg.airplane.structs.Long2FloatAgingCache(TEMPERATURE_CACHE_SIZE); -+ // Pufferfish end - }); - }); - -@@ -123,17 +129,15 @@ public final class Biome { - @Deprecated - public float getTemperature(BlockPos blockPos) { - long l = blockPos.asLong(); -- Long2FloatLinkedOpenHashMap long2FloatLinkedOpenHashMap = this.temperatureCache.get(); -- float f = long2FloatLinkedOpenHashMap.get(l); -+ // Pufferfish start -+ gg.airplane.structs.Long2FloatAgingCache cache = this.temperatureCache.get(); -+ float f = cache.getValue(l); - if (!Float.isNaN(f)) { - return f; - } else { - float g = this.getHeightAdjustedTemperature(blockPos); -- if (long2FloatLinkedOpenHashMap.size() == 1024) { -- long2FloatLinkedOpenHashMap.removeFirstFloat(); -- } -- -- long2FloatLinkedOpenHashMap.put(l, g); -+ cache.putValue(l, g); -+ // Pufferfish end - return g; - } - } -diff --git a/src/main/java/net/minecraft/world/level/block/entity/ChestBlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/ChestBlockEntity.java -index e56f7d76b501dab7d549efd2fafd514a9625c24e..066f43deb5df6f79428eb0f325c2abfb31ea9254 100644 ---- a/src/main/java/net/minecraft/world/level/block/entity/ChestBlockEntity.java -+++ b/src/main/java/net/minecraft/world/level/block/entity/ChestBlockEntity.java -@@ -32,7 +32,10 @@ import org.bukkit.entity.HumanEntity; - public class ChestBlockEntity extends RandomizableContainerBlockEntity implements LidBlockEntity { - - private static final int EVENT_SET_OPEN_COUNT = 1; -+ // Pufferfish start - private NonNullList items; -+ private gg.airplane.structs.ItemListWithBitset optimizedItems; -+ // Pufferfish end - public final ContainerOpenersCounter openersCounter; - private final ChestLidController chestLidController; - -@@ -66,9 +69,13 @@ public class ChestBlockEntity extends RandomizableContainerBlockEntity implement - } - // CraftBukkit end - -+ private final boolean isNative = getClass().equals(ChestBlockEntity.class); // Pufferfish - protected ChestBlockEntity(BlockEntityType type, BlockPos pos, BlockState state) { - super(type, pos, state); -- this.items = NonNullList.withSize(27, ItemStack.EMPTY); -+ // Pufferfish start -+ this.optimizedItems = new gg.airplane.structs.ItemListWithBitset(27); -+ this.items = this.optimizedItems.nonNullList; -+ // Pufferfish end - this.openersCounter = new ContainerOpenersCounter() { - @Override - protected void onOpen(Level world, BlockPos pos, BlockState state) { -@@ -99,6 +106,23 @@ public class ChestBlockEntity extends RandomizableContainerBlockEntity implement - this.chestLidController = new ChestLidController(); - } - -+ // Pufferfish start -+ @Override -+ public boolean hasEmptySlot(Direction enumdirection) { -+ return isNative ? !this.optimizedItems.hasFullStacks() : super.hasEmptySlot(enumdirection); -+ } -+ -+ @Override -+ public boolean isCompletelyFull(Direction enumdirection) { -+ return isNative ? this.optimizedItems.hasFullStacks() && super.isCompletelyFull(enumdirection) : super.isCompletelyFull(enumdirection); -+ } -+ -+ @Override -+ public boolean isCompletelyEmpty(Direction enumdirection) { -+ return isNative && this.optimizedItems.isCompletelyEmpty() || super.isCompletelyEmpty(enumdirection); -+ } -+ // Pufferfish end -+ - public ChestBlockEntity(BlockPos pos, BlockState state) { - this(BlockEntityType.CHEST, pos, state); - } -@@ -116,7 +140,10 @@ public class ChestBlockEntity extends RandomizableContainerBlockEntity implement - @Override - public void load(CompoundTag nbt) { - super.load(nbt); -- this.items = NonNullList.withSize(this.getContainerSize(), ItemStack.EMPTY); -+ // Pufferfish start -+ this.optimizedItems = new gg.airplane.structs.ItemListWithBitset(this.getContainerSize()); -+ this.items = this.optimizedItems.nonNullList; -+ // Pufferfish end - if (!this.tryLoadLootTable(nbt)) { - ContainerHelper.loadAllItems(nbt, this.items); - } -@@ -188,7 +215,10 @@ public class ChestBlockEntity extends RandomizableContainerBlockEntity implement - - @Override - protected void setItems(NonNullList list) { -- this.items = list; -+ // Pufferfish start -+ this.optimizedItems = gg.airplane.structs.ItemListWithBitset.fromList(list); -+ this.items = this.optimizedItems.nonNullList; -+ // Pufferfish end - } - - @Override -diff --git a/src/main/java/net/minecraft/world/level/block/entity/HopperBlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/HopperBlockEntity.java -index d241b25acc223982109495542fd17e0fbdf53437..9cc130087dfda6639def3b5bfd347c59602da3f3 100644 ---- a/src/main/java/net/minecraft/world/level/block/entity/HopperBlockEntity.java -+++ b/src/main/java/net/minecraft/world/level/block/entity/HopperBlockEntity.java -@@ -44,7 +44,10 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen - - public static final int MOVE_ITEM_SPEED = 8; - public static final int HOPPER_CONTAINER_SIZE = 5; -+ // Pufferfish start - private NonNullList items; -+ private gg.airplane.structs.ItemListWithBitset optimizedItems; // Pufferfish -+ // Pufferfish end - private int cooldownTime; - private long tickedGameTime; - -@@ -80,14 +83,37 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen - - public HopperBlockEntity(BlockPos pos, BlockState state) { - super(BlockEntityType.HOPPER, pos, state); -- this.items = NonNullList.withSize(5, ItemStack.EMPTY); -+ // Pufferfish start -+ this.optimizedItems = new gg.airplane.structs.ItemListWithBitset(5); -+ this.items = this.optimizedItems.nonNullList; -+ // Pufferfish end - this.cooldownTime = -1; - } - -+ // Pufferfish start -+ @Override -+ public boolean hasEmptySlot(Direction enumdirection) { -+ return !this.optimizedItems.hasFullStacks(); -+ } -+ -+ @Override -+ public boolean isCompletelyFull(Direction enumdirection) { -+ return this.optimizedItems.hasFullStacks() && super.isCompletelyFull(enumdirection); -+ } -+ -+ @Override -+ public boolean isCompletelyEmpty(Direction enumdirection) { -+ return this.optimizedItems.isCompletelyEmpty() || super.isCompletelyEmpty(enumdirection); -+ } -+ // Pufferfish end -+ - @Override - public void load(CompoundTag nbt) { - super.load(nbt); -- this.items = NonNullList.withSize(this.getContainerSize(), ItemStack.EMPTY); -+ // Pufferfish start -+ this.optimizedItems = new gg.airplane.structs.ItemListWithBitset(this.getContainerSize()); -+ this.items = this.optimizedItems.nonNullList; -+ // Pufferfish end - if (!this.tryLoadLootTable(nbt)) { - ContainerHelper.loadAllItems(nbt, this.items); - } -@@ -159,7 +185,7 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen - flag = HopperBlockEntity.ejectItems(world, pos, state, (Container) blockEntity, blockEntity); // CraftBukkit - } - -- if (!blockEntity.inventoryFull()) { -+ if (!blockEntity.optimizedItems.hasFullStacks() || !blockEntity.inventoryFull()) { // Pufferfish - use bitset first - flag |= booleansupplier.getAsBoolean(); - } - -@@ -198,7 +224,7 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen - skipPushModeEventFire = skipHopperEvents; - boolean foundItem = false; - for (int i = 0; i < hopper.getContainerSize(); ++i) { -- ItemStack item = hopper.getItem(i); -+ ItemStack item = hopper.getItem(i); // Pufferfish - if (!item.isEmpty()) { - foundItem = true; - ItemStack origItemStack = item; -@@ -401,12 +427,18 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen - } - - private static boolean isFullContainer(Container inventory, Direction direction) { -- return allMatch(inventory, direction, STACK_SIZE_TEST); // Paper - no streams -+ // Pufferfish start - use bitsets -+ //return allMatch(inventory, direction, STACK_SIZE_TEST); // Paper - no streams -+ return inventory.isCompletelyFull(direction); -+ // Pufferfish end - } - - private static boolean isEmptyContainer(Container inv, Direction facing) { - // Paper start -- return allMatch(inv, facing, IS_EMPTY_TEST); -+ // Pufferfish start - use bitsets -+ //return allMatch(inv, facing, IS_EMPTY_TEST); -+ return inv.isCompletelyEmpty(facing); -+ // Pufferfish end - } - private static boolean allMatch(Container iinventory, Direction enumdirection, java.util.function.BiPredicate test) { - if (iinventory instanceof WorldlyContainer) { -@@ -583,7 +615,7 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen - - if (HopperBlockEntity.canPlaceItemInContainer(to, stack, slot, side)) { - boolean flag = false; -- boolean flag1 = to.isEmpty(); -+ boolean flag1 = to.isCompletelyEmpty(side); // Pufferfish - - if (itemstack1.isEmpty()) { - // Spigot start - SPIGOT-6693, InventorySubcontainer#setItem -@@ -731,7 +763,10 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen - - @Override - protected void setItems(NonNullList list) { -- this.items = list; -+ // Pufferfish start -+ this.optimizedItems = gg.airplane.structs.ItemListWithBitset.fromList(list); -+ this.items = this.optimizedItems.nonNullList; -+ // Pufferfish end - } - - public static void entityInside(Level world, BlockPos pos, BlockState state, Entity entity, HopperBlockEntity blockEntity) { -diff --git a/src/main/java/net/minecraft/world/level/block/entity/RandomizableContainerBlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/RandomizableContainerBlockEntity.java -index ed3518fe7c841d9e1a9c97626acaa3d765a6d76f..da00f75ccc9f8c2e2174d4450479202a50844245 100644 ---- a/src/main/java/net/minecraft/world/level/block/entity/RandomizableContainerBlockEntity.java -+++ b/src/main/java/net/minecraft/world/level/block/entity/RandomizableContainerBlockEntity.java -@@ -96,13 +96,8 @@ public abstract class RandomizableContainerBlockEntity extends BaseContainerBloc - public boolean isEmpty() { - this.unpackLootTable((Player)null); - // Paper start -- for (ItemStack itemStack : this.getItems()) { -- if (!itemStack.isEmpty()) { -- return false; -- } -- } -+ return this.isCompletelyEmpty(null); // Pufferfish - use super - // Paper end -- return true; - } - - @Override -diff --git a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java -index f667dafd44b6652788d3367cbbc76eef3bead23b..ec727ff5318f90e6241dc423a3156504c6e20c1d 100644 ---- a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java -+++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java -@@ -94,6 +94,18 @@ public class LevelChunk extends ChunkAccess { - } - // Paper end - -+ // Pufferfish start - instead of using a random every time the chunk is ticked, define when lightning strikes preemptively -+ private int lightningTick; -+ // shouldDoLightning compiles down to 29 bytes, which with the default of 35 byte inlining should guarantee an inline -+ public final boolean shouldDoLightning(java.util.Random random) { -+ if (this.lightningTick-- <= 0) { -+ this.lightningTick = random.nextInt(this.level.spigotConfig.thunderChance) << 1; -+ return true; -+ } -+ return false; -+ } -+ // Pufferfish end -+ - public LevelChunk(Level world, ChunkPos pos) { - this(world, pos, UpgradeData.EMPTY, new LevelChunkTicks<>(), new LevelChunkTicks<>(), 0L, (LevelChunkSection[]) null, (LevelChunk.PostLoadProcessor) null, (BlendingData) null); - } -@@ -124,6 +136,7 @@ public class LevelChunk extends ChunkAccess { - this.fluidTicks = fluidTickScheduler; - // CraftBukkit start - this.bukkitChunk = new org.bukkit.craftbukkit.CraftChunk(this); -+ this.lightningTick = this.level.random.nextInt(100000) << 1; // Pufferfish - initialize lightning tick - } - - public org.bukkit.Chunk bukkitChunk; -diff --git a/src/main/java/net/minecraft/world/level/chunk/LevelChunkSection.java b/src/main/java/net/minecraft/world/level/chunk/LevelChunkSection.java -index b11ad90c0956ac1b8ee069fa3f4553a2b4ce88e9..d69fe40d3af983144999963ec25b32500237e515 100644 ---- a/src/main/java/net/minecraft/world/level/chunk/LevelChunkSection.java -+++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunkSection.java -@@ -26,6 +26,7 @@ public class LevelChunkSection { - private short tickingFluidCount; - public final PalettedContainer states; - private final PalettedContainer> biomes; -+ public short fluidStateCount; // Pufferfish - public final com.destroystokyo.paper.util.maplist.IBlockDataList tickingList = new com.destroystokyo.paper.util.maplist.IBlockDataList(); // Paper - - public LevelChunkSection(int chunkPos, PalettedContainer blockStateContainer, PalettedContainer> biomeContainer) { -@@ -196,6 +197,7 @@ public class LevelChunkSection { - - if (!fluid.isEmpty()) { - --this.tickingFluidCount; -+ --this.fluidStateCount; // Pufferfish - } - - if (!state.isAir()) { -@@ -210,6 +212,7 @@ public class LevelChunkSection { - - if (!fluid1.isEmpty()) { - ++this.tickingFluidCount; -+ ++this.fluidStateCount; // Pufferfish - } - - this.updateKnownBlockInfo(x | (z << 4) | (y << 8), iblockdata1, state); // Paper -@@ -258,6 +261,7 @@ public class LevelChunkSection { - if (fluid.isRandomlyTicking()) { - this.tickingFluidCount = (short) (this.tickingFluidCount + 1); - } -+ this.fluidStateCount++; // Pufferfish - } - - }); -diff --git a/src/main/java/net/minecraft/world/level/entity/EntityTickList.java b/src/main/java/net/minecraft/world/level/entity/EntityTickList.java -index 4cdfc433df67afcd455422e9baf56f167dd712ae..57fcf3910f45ce371ac2e237b277b1034caaac4e 100644 ---- a/src/main/java/net/minecraft/world/level/entity/EntityTickList.java -+++ b/src/main/java/net/minecraft/world/level/entity/EntityTickList.java -@@ -8,7 +8,7 @@ import javax.annotation.Nullable; - import net.minecraft.world.entity.Entity; - - public class EntityTickList { -- private final io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet entities = new io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet<>(true); // Paper - rewrite this, always keep this updated - why would we EVER tick an entity that's not ticking? -+ public final io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet entities = new io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet<>(true); // Paper - rewrite this, always keep this updated - why would we EVER tick an entity that's not ticking? // Pufferfish - private->public - - private void ensureActiveIsNotIterated() { - // Paper - replace with better logic, do not delay removals -diff --git a/src/main/java/net/minecraft/world/level/entity/LevelEntityGetter.java b/src/main/java/net/minecraft/world/level/entity/LevelEntityGetter.java -index da1ad0b2679e392ed81b50c15f012c63cb5c939e..9c6ab057dccd6331e9c577e0c6192c0d84718906 100644 ---- a/src/main/java/net/minecraft/world/level/entity/LevelEntityGetter.java -+++ b/src/main/java/net/minecraft/world/level/entity/LevelEntityGetter.java -@@ -6,6 +6,8 @@ import javax.annotation.Nullable; - import net.minecraft.world.phys.AABB; - - public interface LevelEntityGetter { -+ int getCount(); // Pufferfish -+ - @Nullable - T get(int id); - -diff --git a/src/main/java/net/minecraft/world/level/entity/LevelEntityGetterAdapter.java b/src/main/java/net/minecraft/world/level/entity/LevelEntityGetterAdapter.java -index 3b13f6ea36a3bfecabe09221eb5c48dddab119db..563b6c47df0c5ae9efcb91fd53a065b1da1cdb94 100644 ---- a/src/main/java/net/minecraft/world/level/entity/LevelEntityGetterAdapter.java -+++ b/src/main/java/net/minecraft/world/level/entity/LevelEntityGetterAdapter.java -@@ -14,6 +14,8 @@ public class LevelEntityGetterAdapter implements LevelEn - this.sectionStorage = cache; - } - -+ @Override public int getCount() { return this.visibleEntities.count(); } // Pufferfish -+ - @Nullable - @Override - public T get(int id) { -diff --git a/src/main/java/net/minecraft/world/level/material/FlowingFluid.java b/src/main/java/net/minecraft/world/level/material/FlowingFluid.java -index ff40fe323964f173561a6838fb443e79abf9df38..c2c3ed6ba79f9f41497e042571f699a0fc6e9335 100644 ---- a/src/main/java/net/minecraft/world/level/material/FlowingFluid.java -+++ b/src/main/java/net/minecraft/world/level/material/FlowingFluid.java -@@ -43,6 +43,8 @@ public abstract class FlowingFluid extends Fluid { - public static final BooleanProperty FALLING = BlockStateProperties.FALLING; - public static final IntegerProperty LEVEL = BlockStateProperties.LEVEL_FLOWING; - private static final int CACHE_SIZE = 200; -+ // Pufferfish start - use our own cache -+ /* - private static final ThreadLocal> OCCLUSION_CACHE = ThreadLocal.withInitial(() -> { - Object2ByteLinkedOpenHashMap object2bytelinkedopenhashmap = new Object2ByteLinkedOpenHashMap(200) { - protected void rehash(int i) {} -@@ -51,6 +53,14 @@ public abstract class FlowingFluid extends Fluid { - object2bytelinkedopenhashmap.defaultReturnValue((byte) 127); - return object2bytelinkedopenhashmap; - }); -+ */ -+ -+ private static final ThreadLocal> localFluidDirectionCache = ThreadLocal.withInitial(() -> { -+ // Pufferfish todo - mess with this number for performance -+ // with 2048 it seems very infrequent on a small world that it has to remove old entries -+ return new gg.airplane.structs.FluidDirectionCache<>(2048); -+ }); -+ // Pufferfish end - private final Map shapes = Maps.newIdentityHashMap(); - - public FlowingFluid() {} -@@ -239,6 +249,8 @@ public abstract class FlowingFluid extends Fluid { - } - - private boolean canPassThroughWall(Direction face, BlockGetter world, BlockPos pos, BlockState state, BlockPos fromPos, BlockState fromState) { -+ // Pufferfish start - modify to use our cache -+ /* - Object2ByteLinkedOpenHashMap object2bytelinkedopenhashmap; - - if (!state.getBlock().hasDynamicShape() && !fromState.getBlock().hasDynamicShape()) { -@@ -246,9 +258,16 @@ public abstract class FlowingFluid extends Fluid { - } else { - object2bytelinkedopenhashmap = null; - } -+ */ -+ gg.airplane.structs.FluidDirectionCache cache = null; -+ -+ if (!state.getBlock().hasDynamicShape() && !fromState.getBlock().hasDynamicShape()) { -+ cache = localFluidDirectionCache.get(); -+ } - - Block.BlockStatePairKey block_a; - -+ /* - if (object2bytelinkedopenhashmap != null) { - block_a = new Block.BlockStatePairKey(state, fromState, face); - byte b0 = object2bytelinkedopenhashmap.getAndMoveToFirst(block_a); -@@ -259,11 +278,22 @@ public abstract class FlowingFluid extends Fluid { - } else { - block_a = null; - } -+ */ -+ if (cache != null) { -+ block_a = new Block.BlockStatePairKey(state, fromState, face); -+ Boolean flag = cache.getValue(block_a); -+ if (flag != null) { -+ return flag; -+ } -+ } else { -+ block_a = null; -+ } - - VoxelShape voxelshape = state.getCollisionShape(world, pos); - VoxelShape voxelshape1 = fromState.getCollisionShape(world, fromPos); - boolean flag = !Shapes.mergedFaceOccludes(voxelshape, voxelshape1, face); - -+ /* - if (object2bytelinkedopenhashmap != null) { - if (object2bytelinkedopenhashmap.size() == 200) { - object2bytelinkedopenhashmap.removeLastByte(); -@@ -271,6 +301,11 @@ public abstract class FlowingFluid extends Fluid { - - object2bytelinkedopenhashmap.putAndMoveToFirst(block_a, (byte) (flag ? 1 : 0)); - } -+ */ -+ if (cache != null) { -+ cache.putValue(block_a, flag); -+ } -+ // Pufferfish end - - return flag; - } -diff --git a/src/main/java/net/minecraft/world/level/storage/loot/LootContext.java b/src/main/java/net/minecraft/world/level/storage/loot/LootContext.java -index 05b64f2730bfe836bd1d72dcfccd9f536908a099..d33af84300db18ea2b71dba2c9ed43390a293500 100644 ---- a/src/main/java/net/minecraft/world/level/storage/loot/LootContext.java -+++ b/src/main/java/net/minecraft/world/level/storage/loot/LootContext.java -@@ -41,8 +41,10 @@ public class LootContext { - this.level = world; - this.lootTables = tableGetter; - this.conditions = conditionGetter; -- this.params = ImmutableMap.copyOf(parameters); -- this.dynamicDrops = ImmutableMap.copyOf(drops); -+ // Pufferfish start - use unmodifiable maps instead of immutable ones to skip the copy -+ this.params = java.util.Collections.unmodifiableMap(parameters); -+ this.dynamicDrops = java.util.Collections.unmodifiableMap(drops); -+ // Pufferfish end - } - - public boolean hasParam(LootContextParam parameter) { -diff --git a/src/main/java/net/minecraft/world/phys/shapes/EntityCollisionContext.java b/src/main/java/net/minecraft/world/phys/shapes/EntityCollisionContext.java -index b1992ed5136cc7dcf04219868b94b3c37ae36b4b..5b5339cba819368f4d6b7eaf404fa59bca4c0518 100644 ---- a/src/main/java/net/minecraft/world/phys/shapes/EntityCollisionContext.java -+++ b/src/main/java/net/minecraft/world/phys/shapes/EntityCollisionContext.java -@@ -19,47 +19,66 @@ public class EntityCollisionContext implements CollisionContext { - return defaultValue; - } - }; -- private final boolean descending; -- private final double entityBottom; -- private final ItemStack heldItem; -- private final Predicate canStandOnFluid; -+ // Pufferfish start - remove these and pray no plugin uses them -+ // private final boolean descending; -+ // private final double entityBottom; -+ // private final ItemStack heldItem; -+ // private final Predicate canStandOnFluid; -+ // Pufferfish end - @Nullable - private final Entity entity; - - protected EntityCollisionContext(boolean descending, double minY, ItemStack heldItem, Predicate walkOnFluidPredicate, @Nullable Entity entity) { -- this.descending = descending; -- this.entityBottom = minY; -- this.heldItem = heldItem; -- this.canStandOnFluid = walkOnFluidPredicate; -+ // Pufferfish start - remove these -+ // this.descending = descending; -+ // this.entityBottom = minY; -+ // this.heldItem = heldItem; -+ // this.canStandOnFluid = walkOnFluidPredicate; -+ // Pufferfish end - this.entity = entity; - } - - /** @deprecated */ - @Deprecated - protected EntityCollisionContext(Entity entity) { -- this(entity.isDescending(), entity.getY(), entity instanceof LivingEntity ? ((LivingEntity)entity).getMainHandItem() : ItemStack.EMPTY, entity instanceof LivingEntity ? ((LivingEntity)entity)::canStandOnFluid : (fluidState) -> { -- return false; -- }, entity); -+ // Pufferfish start - remove this -+ // this(entity.isDescending(), entity.getY(), entity instanceof LivingEntity ? ((LivingEntity)entity).getMainHandItem() : ItemStack.EMPTY, entity instanceof LivingEntity ? ((LivingEntity)entity)::canStandOnFluid : (fluidState) -> { -+ // return false; -+ // }, entity); -+ // Pufferfish end -+ this.entity = entity; - } - - @Override - public boolean isHoldingItem(Item item) { -- return this.heldItem.is(item); -+ // Pufferfish start -+ Entity entity = this.entity; -+ if (entity instanceof LivingEntity livingEntity) { -+ return livingEntity.getMainHandItem().is(item); -+ } -+ return ItemStack.EMPTY.is(item); -+ // Pufferfish end - } - - @Override - public boolean canStandOnFluid(FluidState state, FluidState fluidState) { -- return this.canStandOnFluid.test(fluidState) && !state.getType().isSame(fluidState.getType()); -+ // Pufferfish start -+ Entity entity = this.entity; -+ if (entity instanceof LivingEntity livingEntity) { -+ return livingEntity.canStandOnFluid(fluidState) && !state.getType().isSame(fluidState.getType()); -+ } -+ return false; -+ // Pufferfish end - } - - @Override - public boolean isDescending() { -- return this.descending; -+ return this.entity != null && this.entity.isDescending(); // Pufferfish - } - - @Override - public boolean isAbove(VoxelShape shape, BlockPos pos, boolean defaultValue) { -- return this.entityBottom > (double)pos.getY() + shape.max(Direction.Axis.Y) - (double)1.0E-5F; -+ return (this.entity == null ? -Double.MAX_VALUE : entity.getY()) > (double)pos.getY() + shape.max(Direction.Axis.Y) - (double)1.0E-5F; // Pufferfish - } - - @Nullable -diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java -index 8a248e34fce1e04de4460f98b7627df495c66af1..04a1c7b37e44de50a1a201df3d61255b3bcd9b3e 100644 ---- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java -+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java -@@ -249,7 +249,7 @@ import javax.annotation.Nullable; // Paper - import javax.annotation.Nonnull; // Paper - - public final class CraftServer implements Server { -- private final String serverName = "Paper"; // Paper -+ private final String serverName = "Pufferfish"; // Paper // Pufferfish - private final String serverVersion; - private final String bukkitVersion = Versioning.getBukkitVersion(); - private final Logger logger = Logger.getLogger("Minecraft"); -@@ -1043,6 +1043,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." - )); -+ getLogger().log(Level.SEVERE, String.format("%s Stacktrace", worker.getThread().getName())); -+ StackTraceElement[] stackTrace = worker.getThread().getStackTrace(); -+ for (StackTraceElement element : stackTrace) { -+ getLogger().log(Level.SEVERE, " " + element.toString()); -+ } - } - } - // Paper end -diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftShapelessRecipe.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftShapelessRecipe.java -index 0b3b46348ac9195bff1492ffc11fcbff7d3f5c6f..4010052c53f3a2831b4d5aa1c041d85897856acb 100644 ---- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftShapelessRecipe.java -+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftShapelessRecipe.java -@@ -43,6 +43,6 @@ public class CraftShapelessRecipe extends ShapelessRecipe implements CraftRecipe - data.set(i, toNMS(ingred.get(i), true)); - } - -- MinecraftServer.getServer().getRecipeManager().addRecipe(new net.minecraft.world.item.crafting.ShapelessRecipe(CraftNamespacedKey.toMinecraft(this.getKey()), this.getGroup(), CraftItemStack.asNMSCopy(this.getResult()), data)); -+ MinecraftServer.getServer().getRecipeManager().addRecipe(new net.minecraft.world.item.crafting.ShapelessRecipe(CraftNamespacedKey.toMinecraft(this.getKey()), this.getGroup(), CraftItemStack.asNMSCopy(this.getResult()), data, true)); - } - } -diff --git a/src/main/java/org/bukkit/craftbukkit/scheduler/MinecraftInternalPlugin.java b/src/main/java/org/bukkit/craftbukkit/scheduler/MinecraftInternalPlugin.java -index 909b2c98e7a9117d2f737245e4661792ffafb744..0d9e2b3728f9ab500bd5e44702718535d338e9a5 100644 ---- a/src/main/java/org/bukkit/craftbukkit/scheduler/MinecraftInternalPlugin.java -+++ b/src/main/java/org/bukkit/craftbukkit/scheduler/MinecraftInternalPlugin.java -@@ -22,7 +22,8 @@ public class MinecraftInternalPlugin extends PluginBase { - private boolean enabled = true; - - private final String pluginName; -- private PluginDescriptionFile pdf; -+ private org.bukkit.plugin.PluginLogger logger; -+ private PluginDescriptionFile pdf; // Pufferfish - - public MinecraftInternalPlugin() { - this.pluginName = "Minecraft"; -@@ -75,7 +76,12 @@ public class MinecraftInternalPlugin extends PluginBase { - - @Override - public PluginLogger getLogger() { -- throw new UnsupportedOperationException("Not supported."); -+ // Pufferfish start -+ if (this.logger == null) { -+ this.logger = new org.bukkit.plugin.PluginLogger(this); // Pufferfish -+ } -+ return this.logger; -+ // Pufferfish end - } - - @Override -@@ -85,7 +91,7 @@ public class MinecraftInternalPlugin extends PluginBase { - - @Override - public Server getServer() { -- throw new UnsupportedOperationException("Not supported."); -+ return org.bukkit.Bukkit.getServer(); // Pufferfish - impl - } - - @Override -diff --git a/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java b/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java -index 88abd2d3a9626501b1800ee754b98c6c29c75fb9..f71af7ecad4b6b0328da8cf8c4e5a8e163866e19 100644 ---- a/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java -+++ b/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java -@@ -430,7 +430,7 @@ public final class CraftMagicNumbers implements UnsafeValues { - - @Override - public com.destroystokyo.paper.util.VersionFetcher getVersionFetcher() { -- return new com.destroystokyo.paper.PaperVersionFetcher(); -+ return new gg.pufferfish.pufferfish.PufferfishVersionFetcher(); // Pufferfish - } - - @Override -diff --git a/src/main/java/org/bukkit/craftbukkit/util/ServerShutdownThread.java b/src/main/java/org/bukkit/craftbukkit/util/ServerShutdownThread.java -index d752720f2f234b9dbd2117333fee1bfad663ec02..f1be8a98c49a63d09c838a85eb58041733f71776 100644 ---- a/src/main/java/org/bukkit/craftbukkit/util/ServerShutdownThread.java -+++ b/src/main/java/org/bukkit/craftbukkit/util/ServerShutdownThread.java -@@ -11,6 +11,7 @@ public class ServerShutdownThread extends Thread { - - @Override - public void run() { -+ try { gg.pufferfish.pufferfish.flare.ProfilingManager.stop(); } catch (Throwable t) {} // Pufferfish - shut down Flare if it's running - try { - // Paper start - try to shutdown on main - server.safeShutdown(false, false); -diff --git a/src/main/java/org/bukkit/craftbukkit/util/Versioning.java b/src/main/java/org/bukkit/craftbukkit/util/Versioning.java -index 774556a62eb240da42e84db4502e2ed43495be17..80553face9c70c2a3d897681e7761df85b22d464 100644 ---- a/src/main/java/org/bukkit/craftbukkit/util/Versioning.java -+++ b/src/main/java/org/bukkit/craftbukkit/util/Versioning.java -@@ -11,7 +11,7 @@ public final class Versioning { - public static String getBukkitVersion() { - String result = "Unknown-Version"; - -- InputStream stream = Bukkit.class.getClassLoader().getResourceAsStream("META-INF/maven/io.papermc.paper/paper-api/pom.properties"); -+ InputStream stream = Bukkit.class.getClassLoader().getResourceAsStream("META-INF/maven/gg.pufferfish.pufferfish/pufferfish-api/pom.properties"); // Pufferfish - Properties properties = new Properties(); - - if (stream != null) { -diff --git a/src/main/java/org/spigotmc/ActivationRange.java b/src/main/java/org/spigotmc/ActivationRange.java -index 1a1a1f4d0ac025daccc2d3f84faf6592819f4d5c..8643df8d81714edf60eebd6984bac2c933d1fbd6 100644 ---- a/src/main/java/org/spigotmc/ActivationRange.java -+++ b/src/main/java/org/spigotmc/ActivationRange.java -@@ -38,6 +38,10 @@ import co.aikar.timings.MinecraftTimings; - import net.minecraft.world.entity.schedule.Activity; - import net.minecraft.world.level.Level; - import net.minecraft.world.phys.AABB; -+// Pufferfish start -+import net.minecraft.world.phys.Vec3; -+import java.util.List; -+// Pufferfish end - - public class ActivationRange - { -@@ -215,6 +219,21 @@ public class ActivationRange - for (int i = 0; i < entities.size(); i++) { - Entity entity = entities.get(i); - ActivationRange.activateEntity(entity); -+ -+ // Pufferfish start -+ if (gg.pufferfish.pufferfish.PufferfishConfig.dearEnabled && entity.getType().dabEnabled) { -+ Vec3 playerVec = player.position(); -+ Vec3 entityVec = entity.position(); -+ double diffX = playerVec.x - entityVec.x, diffY = playerVec.y - entityVec.y, diffZ = playerVec.z - entityVec.z; -+ int squaredDistance = (int) (diffX * diffX + diffY * diffY + diffZ * diffZ); -+ entity.activatedPriority = squaredDistance > gg.pufferfish.pufferfish.PufferfishConfig.startDistanceSquared ? -+ Math.max(1, Math.min(squaredDistance >> gg.pufferfish.pufferfish.PufferfishConfig.activationDistanceMod, gg.pufferfish.pufferfish.PufferfishConfig.maximumActivationPrio)) : -+ 1; -+ } else { -+ entity.activatedPriority = 1; -+ } -+ // Pufferfish end -+ - } - // Paper end - } -@@ -231,12 +250,12 @@ public class ActivationRange - if ( MinecraftServer.currentTick > entity.activatedTick ) - { - if ( entity.defaultActivationState ) -- { -+ { // Pufferfish - diff on change - entity.activatedTick = MinecraftServer.currentTick; - return; - } - if ( entity.activationType.boundingBox.intersects( entity.getBoundingBox() ) ) -- { -+ { // Pufferfish - diff on change - entity.activatedTick = MinecraftServer.currentTick; - } - } -@@ -290,7 +309,7 @@ public class ActivationRange - if ( entity instanceof LivingEntity ) - { - LivingEntity living = (LivingEntity) entity; -- if ( living.onClimbable() || living.jumping || living.hurtTime > 0 || living.activeEffects.size() > 0 ) // Paper -+ if ( living.onClimableCached() || living.jumping || living.hurtTime > 0 || living.activeEffects.size() > 0 ) // Paper // Pufferfish - use cached - { - return 1; // Paper - } diff --git a/patches/todo/api/0031-Add-StructureGenerateEvent.patch b/patches/todo/api/0031-Add-StructureGenerateEvent.patch deleted file mode 100644 index f228663db..000000000 --- a/patches/todo/api/0031-Add-StructureGenerateEvent.patch +++ /dev/null @@ -1,112 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Nahuel -Date: Sat, 9 Jan 2021 15:33:52 +0100 -Subject: [PATCH] Add StructureGenerateEvent - -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 . - -Co-authored-by: Mariell Hoversholm - -diff --git a/src/main/java/org/bukkit/event/world/WorldEvent.java b/src/main/java/org/bukkit/event/world/WorldEvent.java -index cffeff33f007d3b03b7c862b25be453f705da739..1fa083d53dce161ef9e9f19407f230c94b2d7d15 100644 ---- a/src/main/java/org/bukkit/event/world/WorldEvent.java -+++ b/src/main/java/org/bukkit/event/world/WorldEvent.java -@@ -10,6 +10,13 @@ import org.jetbrains.annotations.NotNull; - public abstract class WorldEvent extends Event { - private final World world; - -+ // Purpur start -+ public WorldEvent(boolean isAsync, @NotNull final World world) { -+ super(isAsync); -+ this.world = world; -+ } -+ // Purpur end -+ - public WorldEvent(@NotNull final World world) { - this.world = world; - } -diff --git a/src/main/java/org/purpurmc/purpur/event/world/StructureGenerateEvent.java b/src/main/java/org/purpurmc/purpur/event/world/StructureGenerateEvent.java -new file mode 100644 -index 0000000000000000000000000000000000000000..c49d0da74f59e06176e616f80dffd65c918e1389 ---- /dev/null -+++ b/src/main/java/org/purpurmc/purpur/event/world/StructureGenerateEvent.java -@@ -0,0 +1,68 @@ -+package org.purpurmc.purpur.event.world; -+ -+import org.bukkit.Bukkit; -+import org.bukkit.StructureType; -+import org.bukkit.World; -+import org.bukkit.event.Cancellable; -+import org.bukkit.event.HandlerList; -+import org.bukkit.event.world.WorldEvent; -+import org.jetbrains.annotations.NotNull; -+ -+public class StructureGenerateEvent extends WorldEvent implements Cancellable { -+ private static final HandlerList handlers = new HandlerList(); -+ -+ @NotNull -+ private final StructureType structureType; -+ private final int chunkX; -+ private final int chunkZ; -+ -+ private boolean cancel = false; -+ -+ public StructureGenerateEvent(@NotNull World world, -+ @NotNull StructureType structureType, int chunkX, int chunkZ) { -+ super(!Bukkit.isPrimaryThread(), world); // Structure generation is not necessarily on the main thread as of 1.16. -+ this.structureType = structureType; -+ this.chunkX = chunkX; -+ this.chunkZ = chunkZ; -+ } -+ -+ @NotNull -+ @Override -+ public World getWorld() { -+ return super.getWorld(); -+ } -+ -+ @NotNull -+ public StructureType getStructureType() { -+ return structureType; -+ } -+ -+ public int getChunkX() { -+ return chunkX; -+ } -+ -+ public int getChunkZ() { -+ return chunkZ; -+ } -+ -+ @Override -+ public void setCancelled(boolean cancel) { -+ this.cancel = cancel; -+ } -+ -+ @Override -+ public boolean isCancelled() { -+ return this.cancel; -+ } -+ -+ @NotNull -+ @Override -+ public HandlerList getHandlers() { -+ return handlers; -+ } -+ -+ @NotNull -+ public static HandlerList getHandlerList() { -+ return handlers; -+ } -+} diff --git a/patches/server/0002-Rebrand.patch b/patches/todo/server/0002-Rebrand.patch similarity index 100% rename from patches/server/0002-Rebrand.patch rename to patches/todo/server/0002-Rebrand.patch diff --git a/patches/server/0003-Purpur-config-files.patch b/patches/todo/server/0003-Purpur-config-files.patch similarity index 100% rename from patches/server/0003-Purpur-config-files.patch rename to patches/todo/server/0003-Purpur-config-files.patch diff --git a/patches/server/0004-Purpur-client-support.patch b/patches/todo/server/0004-Purpur-client-support.patch similarity index 100% rename from patches/server/0004-Purpur-client-support.patch rename to patches/todo/server/0004-Purpur-client-support.patch diff --git a/patches/server/0005-Component-related-conveniences.patch b/patches/todo/server/0005-Component-related-conveniences.patch similarity index 100% rename from patches/server/0005-Component-related-conveniences.patch rename to patches/todo/server/0005-Component-related-conveniences.patch diff --git a/patches/server/0006-Ridables.patch b/patches/todo/server/0006-Ridables.patch similarity index 100% rename from patches/server/0006-Ridables.patch rename to patches/todo/server/0006-Ridables.patch diff --git a/patches/server/0007-Configurable-entity-base-attributes.patch b/patches/todo/server/0007-Configurable-entity-base-attributes.patch similarity index 100% rename from patches/server/0007-Configurable-entity-base-attributes.patch rename to patches/todo/server/0007-Configurable-entity-base-attributes.patch diff --git a/patches/server/0008-Timings-stuff.patch b/patches/todo/server/0008-Timings-stuff.patch similarity index 100% rename from patches/server/0008-Timings-stuff.patch rename to patches/todo/server/0008-Timings-stuff.patch diff --git a/patches/server/0009-Barrels-and-enderchests-6-rows.patch b/patches/todo/server/0009-Barrels-and-enderchests-6-rows.patch similarity index 100% rename from patches/server/0009-Barrels-and-enderchests-6-rows.patch rename to patches/todo/server/0009-Barrels-and-enderchests-6-rows.patch diff --git a/patches/server/0010-Llama-API.patch b/patches/todo/server/0010-Llama-API.patch similarity index 100% rename from patches/server/0010-Llama-API.patch rename to patches/todo/server/0010-Llama-API.patch diff --git a/patches/server/0011-AFK-API.patch b/patches/todo/server/0011-AFK-API.patch similarity index 100% rename from patches/server/0011-AFK-API.patch rename to patches/todo/server/0011-AFK-API.patch diff --git a/patches/server/0012-Bring-back-server-name.patch b/patches/todo/server/0012-Bring-back-server-name.patch similarity index 100% rename from patches/server/0012-Bring-back-server-name.patch rename to patches/todo/server/0012-Bring-back-server-name.patch diff --git a/patches/server/0013-Configurable-server-mod-name.patch b/patches/todo/server/0013-Configurable-server-mod-name.patch similarity index 100% rename from patches/server/0013-Configurable-server-mod-name.patch rename to patches/todo/server/0013-Configurable-server-mod-name.patch diff --git a/patches/server/0014-LivingEntity-safeFallDistance.patch b/patches/todo/server/0014-LivingEntity-safeFallDistance.patch similarity index 100% rename from patches/server/0014-LivingEntity-safeFallDistance.patch rename to patches/todo/server/0014-LivingEntity-safeFallDistance.patch diff --git a/patches/server/0015-Lagging-threshold.patch b/patches/todo/server/0015-Lagging-threshold.patch similarity index 100% rename from patches/server/0015-Lagging-threshold.patch rename to patches/todo/server/0015-Lagging-threshold.patch diff --git a/patches/server/0016-PlayerSetSpawnerTypeWithEggEvent.patch b/patches/todo/server/0016-PlayerSetSpawnerTypeWithEggEvent.patch similarity index 100% rename from patches/server/0016-PlayerSetSpawnerTypeWithEggEvent.patch rename to patches/todo/server/0016-PlayerSetSpawnerTypeWithEggEvent.patch diff --git a/patches/server/0017-EMC-MonsterEggSpawnEvent.patch b/patches/todo/server/0017-EMC-MonsterEggSpawnEvent.patch similarity index 100% rename from patches/server/0017-EMC-MonsterEggSpawnEvent.patch rename to patches/todo/server/0017-EMC-MonsterEggSpawnEvent.patch diff --git a/patches/server/0018-Player-invulnerabilities.patch b/patches/todo/server/0018-Player-invulnerabilities.patch similarity index 100% rename from patches/server/0018-Player-invulnerabilities.patch rename to patches/todo/server/0018-Player-invulnerabilities.patch diff --git a/patches/server/0019-Anvil-API.patch b/patches/todo/server/0019-Anvil-API.patch similarity index 100% rename from patches/server/0019-Anvil-API.patch rename to patches/todo/server/0019-Anvil-API.patch diff --git a/patches/server/0020-Configurable-villager-brain-ticks.patch b/patches/todo/server/0020-Configurable-villager-brain-ticks.patch similarity index 100% rename from patches/server/0020-Configurable-villager-brain-ticks.patch rename to patches/todo/server/0020-Configurable-villager-brain-ticks.patch diff --git a/patches/server/0021-Alternative-Keepalive-Handling.patch b/patches/todo/server/0021-Alternative-Keepalive-Handling.patch similarity index 100% rename from patches/server/0021-Alternative-Keepalive-Handling.patch rename to patches/todo/server/0021-Alternative-Keepalive-Handling.patch diff --git a/patches/server/0022-Silk-touch-spawners.patch b/patches/todo/server/0022-Silk-touch-spawners.patch similarity index 100% rename from patches/server/0022-Silk-touch-spawners.patch rename to patches/todo/server/0022-Silk-touch-spawners.patch diff --git a/patches/server/0023-MC-168772-Fix-Add-turtle-egg-block-options.patch b/patches/todo/server/0023-MC-168772-Fix-Add-turtle-egg-block-options.patch similarity index 100% rename from patches/server/0023-MC-168772-Fix-Add-turtle-egg-block-options.patch rename to patches/todo/server/0023-MC-168772-Fix-Add-turtle-egg-block-options.patch diff --git a/patches/server/0024-Fix-vanilla-command-permission-handler.patch b/patches/todo/server/0024-Fix-vanilla-command-permission-handler.patch similarity index 100% rename from patches/server/0024-Fix-vanilla-command-permission-handler.patch rename to patches/todo/server/0024-Fix-vanilla-command-permission-handler.patch diff --git a/patches/server/0025-Logger-settings-suppressing-pointless-logs.patch b/patches/todo/server/0025-Logger-settings-suppressing-pointless-logs.patch similarity index 100% rename from patches/server/0025-Logger-settings-suppressing-pointless-logs.patch rename to patches/todo/server/0025-Logger-settings-suppressing-pointless-logs.patch diff --git a/patches/server/0026-Disable-outdated-build-check.patch b/patches/todo/server/0026-Disable-outdated-build-check.patch similarity index 100% rename from patches/server/0026-Disable-outdated-build-check.patch rename to patches/todo/server/0026-Disable-outdated-build-check.patch diff --git a/patches/server/0027-Giants-AI-settings.patch b/patches/todo/server/0027-Giants-AI-settings.patch similarity index 100% rename from patches/server/0027-Giants-AI-settings.patch rename to patches/todo/server/0027-Giants-AI-settings.patch diff --git a/patches/server/0028-Zombie-horse-naturally-spawn.patch b/patches/todo/server/0028-Zombie-horse-naturally-spawn.patch similarity index 100% rename from patches/server/0028-Zombie-horse-naturally-spawn.patch rename to patches/todo/server/0028-Zombie-horse-naturally-spawn.patch diff --git a/patches/server/0029-Charged-creeper-naturally-spawn.patch b/patches/todo/server/0029-Charged-creeper-naturally-spawn.patch similarity index 100% rename from patches/server/0029-Charged-creeper-naturally-spawn.patch rename to patches/todo/server/0029-Charged-creeper-naturally-spawn.patch diff --git a/patches/server/0030-Rabbit-naturally-spawn-toast-and-killer.patch b/patches/todo/server/0030-Rabbit-naturally-spawn-toast-and-killer.patch similarity index 100% rename from patches/server/0030-Rabbit-naturally-spawn-toast-and-killer.patch rename to patches/todo/server/0030-Rabbit-naturally-spawn-toast-and-killer.patch diff --git a/patches/server/0031-Fix-outdated-server-showing-in-ping-before-server-fu.patch b/patches/todo/server/0031-Fix-outdated-server-showing-in-ping-before-server-fu.patch similarity index 100% rename from patches/server/0031-Fix-outdated-server-showing-in-ping-before-server-fu.patch rename to patches/todo/server/0031-Fix-outdated-server-showing-in-ping-before-server-fu.patch diff --git a/patches/server/0032-Tulips-change-fox-type.patch b/patches/todo/server/0032-Tulips-change-fox-type.patch similarity index 100% rename from patches/server/0032-Tulips-change-fox-type.patch rename to patches/todo/server/0032-Tulips-change-fox-type.patch diff --git a/patches/server/0033-Breedable-Polar-Bears.patch b/patches/todo/server/0033-Breedable-Polar-Bears.patch similarity index 100% rename from patches/server/0033-Breedable-Polar-Bears.patch rename to patches/todo/server/0033-Breedable-Polar-Bears.patch diff --git a/patches/server/0034-Chickens-can-retaliate.patch b/patches/todo/server/0034-Chickens-can-retaliate.patch similarity index 100% rename from patches/server/0034-Chickens-can-retaliate.patch rename to patches/todo/server/0034-Chickens-can-retaliate.patch diff --git a/patches/server/0035-Add-option-to-set-armorstand-step-height.patch b/patches/todo/server/0035-Add-option-to-set-armorstand-step-height.patch similarity index 100% rename from patches/server/0035-Add-option-to-set-armorstand-step-height.patch rename to patches/todo/server/0035-Add-option-to-set-armorstand-step-height.patch diff --git a/patches/server/0036-Cat-spawning-options.patch b/patches/todo/server/0036-Cat-spawning-options.patch similarity index 100% rename from patches/server/0036-Cat-spawning-options.patch rename to patches/todo/server/0036-Cat-spawning-options.patch diff --git a/patches/server/0037-MC-147659-Fix-non-black-cats-spawning-in-swamp-huts.patch b/patches/todo/server/0037-MC-147659-Fix-non-black-cats-spawning-in-swamp-huts.patch similarity index 100% rename from patches/server/0037-MC-147659-Fix-non-black-cats-spawning-in-swamp-huts.patch rename to patches/todo/server/0037-MC-147659-Fix-non-black-cats-spawning-in-swamp-huts.patch diff --git a/patches/server/0038-Cows-eat-mushrooms.patch b/patches/todo/server/0038-Cows-eat-mushrooms.patch similarity index 100% rename from patches/server/0038-Cows-eat-mushrooms.patch rename to patches/todo/server/0038-Cows-eat-mushrooms.patch diff --git a/patches/server/0039-Fix-cow-rotation-when-shearing-mooshroom.patch b/patches/todo/server/0039-Fix-cow-rotation-when-shearing-mooshroom.patch similarity index 100% rename from patches/server/0039-Fix-cow-rotation-when-shearing-mooshroom.patch rename to patches/todo/server/0039-Fix-cow-rotation-when-shearing-mooshroom.patch diff --git a/patches/server/0040-Pigs-give-saddle-back.patch b/patches/todo/server/0040-Pigs-give-saddle-back.patch similarity index 100% rename from patches/server/0040-Pigs-give-saddle-back.patch rename to patches/todo/server/0040-Pigs-give-saddle-back.patch diff --git a/patches/server/0041-Snowman-drop-and-put-back-pumpkin.patch b/patches/todo/server/0041-Snowman-drop-and-put-back-pumpkin.patch similarity index 100% rename from patches/server/0041-Snowman-drop-and-put-back-pumpkin.patch rename to patches/todo/server/0041-Snowman-drop-and-put-back-pumpkin.patch diff --git a/patches/server/0042-Ender-dragon-always-drop-full-exp.patch b/patches/todo/server/0042-Ender-dragon-always-drop-full-exp.patch similarity index 100% rename from patches/server/0042-Ender-dragon-always-drop-full-exp.patch rename to patches/todo/server/0042-Ender-dragon-always-drop-full-exp.patch diff --git a/patches/server/0043-Signs-editable-on-right-click.patch b/patches/todo/server/0043-Signs-editable-on-right-click.patch similarity index 100% rename from patches/server/0043-Signs-editable-on-right-click.patch rename to patches/todo/server/0043-Signs-editable-on-right-click.patch diff --git a/patches/server/0044-Allow-soil-to-moisten-from-water-directly-under-it.patch b/patches/todo/server/0044-Allow-soil-to-moisten-from-water-directly-under-it.patch similarity index 100% rename from patches/server/0044-Allow-soil-to-moisten-from-water-directly-under-it.patch rename to patches/todo/server/0044-Allow-soil-to-moisten-from-water-directly-under-it.patch diff --git a/patches/server/0045-Minecart-settings-and-WASD-controls.patch b/patches/todo/server/0045-Minecart-settings-and-WASD-controls.patch similarity index 100% rename from patches/server/0045-Minecart-settings-and-WASD-controls.patch rename to patches/todo/server/0045-Minecart-settings-and-WASD-controls.patch diff --git a/patches/server/0046-Disable-loot-drops-on-death-by-cramming.patch b/patches/todo/server/0046-Disable-loot-drops-on-death-by-cramming.patch similarity index 100% rename from patches/server/0046-Disable-loot-drops-on-death-by-cramming.patch rename to patches/todo/server/0046-Disable-loot-drops-on-death-by-cramming.patch diff --git a/patches/server/0047-Option-to-toggle-milk-curing-bad-omen.patch b/patches/todo/server/0047-Option-to-toggle-milk-curing-bad-omen.patch similarity index 100% rename from patches/server/0047-Option-to-toggle-milk-curing-bad-omen.patch rename to patches/todo/server/0047-Option-to-toggle-milk-curing-bad-omen.patch diff --git a/patches/server/0048-End-gateway-should-check-if-entity-can-use-portal.patch b/patches/todo/server/0048-End-gateway-should-check-if-entity-can-use-portal.patch similarity index 100% rename from patches/server/0048-End-gateway-should-check-if-entity-can-use-portal.patch rename to patches/todo/server/0048-End-gateway-should-check-if-entity-can-use-portal.patch diff --git a/patches/server/0049-Fix-the-dead-lagging-the-server.patch b/patches/todo/server/0049-Fix-the-dead-lagging-the-server.patch similarity index 100% rename from patches/server/0049-Fix-the-dead-lagging-the-server.patch rename to patches/todo/server/0049-Fix-the-dead-lagging-the-server.patch diff --git a/patches/server/0050-Skip-events-if-there-s-no-listeners.patch b/patches/todo/server/0050-Skip-events-if-there-s-no-listeners.patch similarity index 100% rename from patches/server/0050-Skip-events-if-there-s-no-listeners.patch rename to patches/todo/server/0050-Skip-events-if-there-s-no-listeners.patch diff --git a/patches/server/0051-Add-permission-for-F3-N-debug.patch b/patches/todo/server/0051-Add-permission-for-F3-N-debug.patch similarity index 100% rename from patches/server/0051-Add-permission-for-F3-N-debug.patch rename to patches/todo/server/0051-Add-permission-for-F3-N-debug.patch diff --git a/patches/server/0052-Configurable-TPS-Catchup.patch b/patches/todo/server/0052-Configurable-TPS-Catchup.patch similarity index 100% rename from patches/server/0052-Configurable-TPS-Catchup.patch rename to patches/todo/server/0052-Configurable-TPS-Catchup.patch diff --git a/patches/server/0053-Add-option-to-allow-loyalty-on-tridents-to-work-in-t.patch b/patches/todo/server/0053-Add-option-to-allow-loyalty-on-tridents-to-work-in-t.patch similarity index 100% rename from patches/server/0053-Add-option-to-allow-loyalty-on-tridents-to-work-in-t.patch rename to patches/todo/server/0053-Add-option-to-allow-loyalty-on-tridents-to-work-in-t.patch diff --git a/patches/server/0054-Add-enderman-and-creeper-griefing-controls.patch b/patches/todo/server/0054-Add-enderman-and-creeper-griefing-controls.patch similarity index 100% rename from patches/server/0054-Add-enderman-and-creeper-griefing-controls.patch rename to patches/todo/server/0054-Add-enderman-and-creeper-griefing-controls.patch diff --git a/patches/server/0055-Villagers-follow-emerald-blocks.patch b/patches/todo/server/0055-Villagers-follow-emerald-blocks.patch similarity index 100% rename from patches/server/0055-Villagers-follow-emerald-blocks.patch rename to patches/todo/server/0055-Villagers-follow-emerald-blocks.patch diff --git a/patches/server/0056-Allow-leashing-villagers.patch b/patches/todo/server/0056-Allow-leashing-villagers.patch similarity index 100% rename from patches/server/0056-Allow-leashing-villagers.patch rename to patches/todo/server/0056-Allow-leashing-villagers.patch diff --git a/patches/server/0057-Implement-configurable-search-radius-for-villagers-t.patch b/patches/todo/server/0057-Implement-configurable-search-radius-for-villagers-t.patch similarity index 100% rename from patches/server/0057-Implement-configurable-search-radius-for-villagers-t.patch rename to patches/todo/server/0057-Implement-configurable-search-radius-for-villagers-t.patch diff --git a/patches/server/0058-Implement-infinite-liquids.patch b/patches/todo/server/0058-Implement-infinite-liquids.patch similarity index 100% rename from patches/server/0058-Implement-infinite-liquids.patch rename to patches/todo/server/0058-Implement-infinite-liquids.patch diff --git a/patches/server/0059-Make-lava-flow-speed-configurable.patch b/patches/todo/server/0059-Make-lava-flow-speed-configurable.patch similarity index 100% rename from patches/server/0059-Make-lava-flow-speed-configurable.patch rename to patches/todo/server/0059-Make-lava-flow-speed-configurable.patch diff --git a/patches/server/0060-Add-player-death-exp-control-options.patch b/patches/todo/server/0060-Add-player-death-exp-control-options.patch similarity index 100% rename from patches/server/0060-Add-player-death-exp-control-options.patch rename to patches/todo/server/0060-Add-player-death-exp-control-options.patch diff --git a/patches/server/0061-Configurable-void-damage-height-and-damage.patch b/patches/todo/server/0061-Configurable-void-damage-height-and-damage.patch similarity index 100% rename from patches/server/0061-Configurable-void-damage-height-and-damage.patch rename to patches/todo/server/0061-Configurable-void-damage-height-and-damage.patch diff --git a/patches/server/0062-Add-canSaveToDisk-to-Entity.patch b/patches/todo/server/0062-Add-canSaveToDisk-to-Entity.patch similarity index 100% rename from patches/server/0062-Add-canSaveToDisk-to-Entity.patch rename to patches/todo/server/0062-Add-canSaveToDisk-to-Entity.patch diff --git a/patches/server/0063-Dispenser-curse-of-binding-protection.patch b/patches/todo/server/0063-Dispenser-curse-of-binding-protection.patch similarity index 100% rename from patches/server/0063-Dispenser-curse-of-binding-protection.patch rename to patches/todo/server/0063-Dispenser-curse-of-binding-protection.patch diff --git a/patches/server/0064-Add-option-for-boats-to-eject-players-on-land.patch b/patches/todo/server/0064-Add-option-for-boats-to-eject-players-on-land.patch similarity index 100% rename from patches/server/0064-Add-option-for-boats-to-eject-players-on-land.patch rename to patches/todo/server/0064-Add-option-for-boats-to-eject-players-on-land.patch diff --git a/patches/server/0065-Mending-mends-most-damages-equipment-first.patch b/patches/todo/server/0065-Mending-mends-most-damages-equipment-first.patch similarity index 100% rename from patches/server/0065-Mending-mends-most-damages-equipment-first.patch rename to patches/todo/server/0065-Mending-mends-most-damages-equipment-first.patch diff --git a/patches/server/0066-Add-5-second-tps-average-in-tps.patch b/patches/todo/server/0066-Add-5-second-tps-average-in-tps.patch similarity index 100% rename from patches/server/0066-Add-5-second-tps-average-in-tps.patch rename to patches/todo/server/0066-Add-5-second-tps-average-in-tps.patch diff --git a/patches/server/0067-Implement-elytra-settings.patch b/patches/todo/server/0067-Implement-elytra-settings.patch similarity index 100% rename from patches/server/0067-Implement-elytra-settings.patch rename to patches/todo/server/0067-Implement-elytra-settings.patch diff --git a/patches/server/0068-Item-entity-immunities.patch b/patches/todo/server/0068-Item-entity-immunities.patch similarity index 100% rename from patches/server/0068-Item-entity-immunities.patch rename to patches/todo/server/0068-Item-entity-immunities.patch diff --git a/patches/server/0069-Add-ping-command.patch b/patches/todo/server/0069-Add-ping-command.patch similarity index 100% rename from patches/server/0069-Add-ping-command.patch rename to patches/todo/server/0069-Add-ping-command.patch diff --git a/patches/server/0070-Add-demo-command.patch b/patches/todo/server/0070-Add-demo-command.patch similarity index 100% rename from patches/server/0070-Add-demo-command.patch rename to patches/todo/server/0070-Add-demo-command.patch diff --git a/patches/server/0071-Add-credits-command.patch b/patches/todo/server/0071-Add-credits-command.patch similarity index 100% rename from patches/server/0071-Add-credits-command.patch rename to patches/todo/server/0071-Add-credits-command.patch diff --git a/patches/server/0072-Configurable-jockey-options.patch b/patches/todo/server/0072-Configurable-jockey-options.patch similarity index 100% rename from patches/server/0072-Configurable-jockey-options.patch rename to patches/todo/server/0072-Configurable-jockey-options.patch diff --git a/patches/server/0073-Phantoms-attracted-to-crystals-and-crystals-shoot-ph.patch b/patches/todo/server/0073-Phantoms-attracted-to-crystals-and-crystals-shoot-ph.patch similarity index 100% rename from patches/server/0073-Phantoms-attracted-to-crystals-and-crystals-shoot-ph.patch rename to patches/todo/server/0073-Phantoms-attracted-to-crystals-and-crystals-shoot-ph.patch diff --git a/patches/server/0074-Add-phantom-spawning-options.patch b/patches/todo/server/0074-Add-phantom-spawning-options.patch similarity index 100% rename from patches/server/0074-Add-phantom-spawning-options.patch rename to patches/todo/server/0074-Add-phantom-spawning-options.patch diff --git a/patches/server/0075-Implement-bed-explosion-options.patch b/patches/todo/server/0075-Implement-bed-explosion-options.patch similarity index 100% rename from patches/server/0075-Implement-bed-explosion-options.patch rename to patches/todo/server/0075-Implement-bed-explosion-options.patch diff --git a/patches/server/0076-Implement-respawn-anchor-explosion-options.patch b/patches/todo/server/0076-Implement-respawn-anchor-explosion-options.patch similarity index 100% rename from patches/server/0076-Implement-respawn-anchor-explosion-options.patch rename to patches/todo/server/0076-Implement-respawn-anchor-explosion-options.patch diff --git a/patches/server/0077-Add-allow-water-in-end-world-option.patch b/patches/todo/server/0077-Add-allow-water-in-end-world-option.patch similarity index 100% rename from patches/server/0077-Add-allow-water-in-end-world-option.patch rename to patches/todo/server/0077-Add-allow-water-in-end-world-option.patch diff --git a/patches/server/0078-Allow-color-codes-in-books.patch b/patches/todo/server/0078-Allow-color-codes-in-books.patch similarity index 100% rename from patches/server/0078-Allow-color-codes-in-books.patch rename to patches/todo/server/0078-Allow-color-codes-in-books.patch diff --git a/patches/server/0079-Entity-lifespan.patch b/patches/todo/server/0079-Entity-lifespan.patch similarity index 100% rename from patches/server/0079-Entity-lifespan.patch rename to patches/todo/server/0079-Entity-lifespan.patch diff --git a/patches/server/0080-Add-option-to-teleport-to-spawn-if-outside-world-bor.patch b/patches/todo/server/0080-Add-option-to-teleport-to-spawn-if-outside-world-bor.patch similarity index 100% rename from patches/server/0080-Add-option-to-teleport-to-spawn-if-outside-world-bor.patch rename to patches/todo/server/0080-Add-option-to-teleport-to-spawn-if-outside-world-bor.patch diff --git a/patches/server/0081-Squid-EAR-immunity.patch b/patches/todo/server/0081-Squid-EAR-immunity.patch similarity index 100% rename from patches/server/0081-Squid-EAR-immunity.patch rename to patches/todo/server/0081-Squid-EAR-immunity.patch diff --git a/patches/server/0082-Phantoms-burn-in-light.patch b/patches/todo/server/0082-Phantoms-burn-in-light.patch similarity index 100% rename from patches/server/0082-Phantoms-burn-in-light.patch rename to patches/todo/server/0082-Phantoms-burn-in-light.patch diff --git a/patches/server/0083-Configurable-villager-breeding.patch b/patches/todo/server/0083-Configurable-villager-breeding.patch similarity index 100% rename from patches/server/0083-Configurable-villager-breeding.patch rename to patches/todo/server/0083-Configurable-villager-breeding.patch diff --git a/patches/server/0084-Redstone-deactivates-spawners.patch b/patches/todo/server/0084-Redstone-deactivates-spawners.patch similarity index 100% rename from patches/server/0084-Redstone-deactivates-spawners.patch rename to patches/todo/server/0084-Redstone-deactivates-spawners.patch diff --git a/patches/server/0085-Totems-work-in-inventory.patch b/patches/todo/server/0085-Totems-work-in-inventory.patch similarity index 100% rename from patches/server/0085-Totems-work-in-inventory.patch rename to patches/todo/server/0085-Totems-work-in-inventory.patch diff --git a/patches/server/0086-Add-vindicator-johnny-spawn-chance.patch b/patches/todo/server/0086-Add-vindicator-johnny-spawn-chance.patch similarity index 100% rename from patches/server/0086-Add-vindicator-johnny-spawn-chance.patch rename to patches/todo/server/0086-Add-vindicator-johnny-spawn-chance.patch diff --git a/patches/server/0087-Add-option-to-disable-certain-block-updates.patch b/patches/todo/server/0087-Add-option-to-disable-certain-block-updates.patch similarity index 100% rename from patches/server/0087-Add-option-to-disable-certain-block-updates.patch rename to patches/todo/server/0087-Add-option-to-disable-certain-block-updates.patch diff --git a/patches/server/0088-Dispensers-place-anvils-option.patch b/patches/todo/server/0088-Dispensers-place-anvils-option.patch similarity index 100% rename from patches/server/0088-Dispensers-place-anvils-option.patch rename to patches/todo/server/0088-Dispensers-place-anvils-option.patch diff --git a/patches/server/0089-Allow-anvil-colors.patch b/patches/todo/server/0089-Allow-anvil-colors.patch similarity index 100% rename from patches/server/0089-Allow-anvil-colors.patch rename to patches/todo/server/0089-Allow-anvil-colors.patch diff --git a/patches/server/0090-Add-option-to-disable-dolphin-treasure-searching.patch b/patches/todo/server/0090-Add-option-to-disable-dolphin-treasure-searching.patch similarity index 100% rename from patches/server/0090-Add-option-to-disable-dolphin-treasure-searching.patch rename to patches/todo/server/0090-Add-option-to-disable-dolphin-treasure-searching.patch diff --git a/patches/server/0091-Short-enderman-height.patch b/patches/todo/server/0091-Short-enderman-height.patch similarity index 100% rename from patches/server/0091-Short-enderman-height.patch rename to patches/todo/server/0091-Short-enderman-height.patch diff --git a/patches/server/0092-Stop-squids-floating-on-top-of-water.patch b/patches/todo/server/0092-Stop-squids-floating-on-top-of-water.patch similarity index 100% rename from patches/server/0092-Stop-squids-floating-on-top-of-water.patch rename to patches/todo/server/0092-Stop-squids-floating-on-top-of-water.patch diff --git a/patches/server/0093-Crying-obsidian-valid-for-portal-frames.patch b/patches/todo/server/0093-Crying-obsidian-valid-for-portal-frames.patch similarity index 100% rename from patches/server/0093-Crying-obsidian-valid-for-portal-frames.patch rename to patches/todo/server/0093-Crying-obsidian-valid-for-portal-frames.patch diff --git a/patches/server/0094-Entities-can-use-portals-configuration.patch b/patches/todo/server/0094-Entities-can-use-portals-configuration.patch similarity index 100% rename from patches/server/0094-Entities-can-use-portals-configuration.patch rename to patches/todo/server/0094-Entities-can-use-portals-configuration.patch diff --git a/patches/server/0095-LivingEntity-broadcastItemBreak.patch b/patches/todo/server/0095-LivingEntity-broadcastItemBreak.patch similarity index 100% rename from patches/server/0095-LivingEntity-broadcastItemBreak.patch rename to patches/todo/server/0095-LivingEntity-broadcastItemBreak.patch diff --git a/patches/server/0096-Customizable-wither-health-and-healing.patch b/patches/todo/server/0096-Customizable-wither-health-and-healing.patch similarity index 100% rename from patches/server/0096-Customizable-wither-health-and-healing.patch rename to patches/todo/server/0096-Customizable-wither-health-and-healing.patch diff --git a/patches/server/0097-Allow-toggling-special-MobSpawners-per-world.patch b/patches/todo/server/0097-Allow-toggling-special-MobSpawners-per-world.patch similarity index 100% rename from patches/server/0097-Allow-toggling-special-MobSpawners-per-world.patch rename to patches/todo/server/0097-Allow-toggling-special-MobSpawners-per-world.patch diff --git a/patches/server/0098-Raid-cooldown-setting.patch b/patches/todo/server/0098-Raid-cooldown-setting.patch similarity index 100% rename from patches/server/0098-Raid-cooldown-setting.patch rename to patches/todo/server/0098-Raid-cooldown-setting.patch diff --git a/patches/server/0099-Despawn-rate-config-options-per-projectile-type.patch b/patches/todo/server/0099-Despawn-rate-config-options-per-projectile-type.patch similarity index 100% rename from patches/server/0099-Despawn-rate-config-options-per-projectile-type.patch rename to patches/todo/server/0099-Despawn-rate-config-options-per-projectile-type.patch diff --git a/patches/server/0100-Add-option-to-disable-zombie-aggressiveness-towards-.patch b/patches/todo/server/0100-Add-option-to-disable-zombie-aggressiveness-towards-.patch similarity index 100% rename from patches/server/0100-Add-option-to-disable-zombie-aggressiveness-towards-.patch rename to patches/todo/server/0100-Add-option-to-disable-zombie-aggressiveness-towards-.patch diff --git a/patches/server/0101-Add-predicate-to-recipe-s-ExactChoice-ingredient.patch b/patches/todo/server/0101-Add-predicate-to-recipe-s-ExactChoice-ingredient.patch similarity index 100% rename from patches/server/0101-Add-predicate-to-recipe-s-ExactChoice-ingredient.patch rename to patches/todo/server/0101-Add-predicate-to-recipe-s-ExactChoice-ingredient.patch diff --git a/patches/server/0102-Flying-squids-Oh-my.patch b/patches/todo/server/0102-Flying-squids-Oh-my.patch similarity index 100% rename from patches/server/0102-Flying-squids-Oh-my.patch rename to patches/todo/server/0102-Flying-squids-Oh-my.patch diff --git a/patches/server/0103-Infinity-bow-settings.patch b/patches/todo/server/0103-Infinity-bow-settings.patch similarity index 100% rename from patches/server/0103-Infinity-bow-settings.patch rename to patches/todo/server/0103-Infinity-bow-settings.patch diff --git a/patches/server/0104-Stonecutter-damage.patch b/patches/todo/server/0104-Stonecutter-damage.patch similarity index 100% rename from patches/server/0104-Stonecutter-damage.patch rename to patches/todo/server/0104-Stonecutter-damage.patch diff --git a/patches/server/0105-Configurable-daylight-cycle.patch b/patches/todo/server/0105-Configurable-daylight-cycle.patch similarity index 100% rename from patches/server/0105-Configurable-daylight-cycle.patch rename to patches/todo/server/0105-Configurable-daylight-cycle.patch diff --git a/patches/server/0106-Allow-infinite-and-mending-enchantments-together.patch b/patches/todo/server/0106-Allow-infinite-and-mending-enchantments-together.patch similarity index 100% rename from patches/server/0106-Allow-infinite-and-mending-enchantments-together.patch rename to patches/todo/server/0106-Allow-infinite-and-mending-enchantments-together.patch diff --git a/patches/server/0107-Furnace-uses-lava-from-underneath.patch b/patches/todo/server/0107-Furnace-uses-lava-from-underneath.patch similarity index 100% rename from patches/server/0107-Furnace-uses-lava-from-underneath.patch rename to patches/todo/server/0107-Furnace-uses-lava-from-underneath.patch diff --git a/patches/server/0108-Arrows-should-not-reset-despawn-counter.patch b/patches/todo/server/0108-Arrows-should-not-reset-despawn-counter.patch similarity index 100% rename from patches/server/0108-Arrows-should-not-reset-despawn-counter.patch rename to patches/todo/server/0108-Arrows-should-not-reset-despawn-counter.patch diff --git a/patches/server/0109-Ability-to-re-add-farmland-mechanics-from-Alpha.patch b/patches/todo/server/0109-Ability-to-re-add-farmland-mechanics-from-Alpha.patch similarity index 100% rename from patches/server/0109-Ability-to-re-add-farmland-mechanics-from-Alpha.patch rename to patches/todo/server/0109-Ability-to-re-add-farmland-mechanics-from-Alpha.patch diff --git a/patches/server/0110-Add-adjustable-breeding-cooldown-to-config.patch b/patches/todo/server/0110-Add-adjustable-breeding-cooldown-to-config.patch similarity index 100% rename from patches/server/0110-Add-adjustable-breeding-cooldown-to-config.patch rename to patches/todo/server/0110-Add-adjustable-breeding-cooldown-to-config.patch diff --git a/patches/server/0111-Make-entity-breeding-times-configurable.patch b/patches/todo/server/0111-Make-entity-breeding-times-configurable.patch similarity index 100% rename from patches/server/0111-Make-entity-breeding-times-configurable.patch rename to patches/todo/server/0111-Make-entity-breeding-times-configurable.patch diff --git a/patches/server/0112-Apply-display-names-from-item-forms-of-entities-to-e.patch b/patches/todo/server/0112-Apply-display-names-from-item-forms-of-entities-to-e.patch similarity index 100% rename from patches/server/0112-Apply-display-names-from-item-forms-of-entities-to-e.patch rename to patches/todo/server/0112-Apply-display-names-from-item-forms-of-entities-to-e.patch diff --git a/patches/server/0113-Set-name-visible-when-using-a-Name-Tag-on-an-Armor-S.patch b/patches/todo/server/0113-Set-name-visible-when-using-a-Name-Tag-on-an-Armor-S.patch similarity index 100% rename from patches/server/0113-Set-name-visible-when-using-a-Name-Tag-on-an-Armor-S.patch rename to patches/todo/server/0113-Set-name-visible-when-using-a-Name-Tag-on-an-Armor-S.patch diff --git a/patches/server/0114-Add-config-for-allowing-Endermen-to-despawn-even-whi.patch b/patches/todo/server/0114-Add-config-for-allowing-Endermen-to-despawn-even-whi.patch similarity index 100% rename from patches/server/0114-Add-config-for-allowing-Endermen-to-despawn-even-whi.patch rename to patches/todo/server/0114-Add-config-for-allowing-Endermen-to-despawn-even-whi.patch diff --git a/patches/server/0115-Add-configurable-snowball-damage.patch b/patches/todo/server/0115-Add-configurable-snowball-damage.patch similarity index 100% rename from patches/server/0115-Add-configurable-snowball-damage.patch rename to patches/todo/server/0115-Add-configurable-snowball-damage.patch diff --git a/patches/server/0116-Changeable-Mob-Left-Handed-Chance.patch b/patches/todo/server/0116-Changeable-Mob-Left-Handed-Chance.patch similarity index 100% rename from patches/server/0116-Changeable-Mob-Left-Handed-Chance.patch rename to patches/todo/server/0116-Changeable-Mob-Left-Handed-Chance.patch diff --git a/patches/server/0117-Add-boat-fall-damage-config.patch b/patches/todo/server/0117-Add-boat-fall-damage-config.patch similarity index 100% rename from patches/server/0117-Add-boat-fall-damage-config.patch rename to patches/todo/server/0117-Add-boat-fall-damage-config.patch diff --git a/patches/server/0118-Snow-Golem-rate-of-fire-config.patch b/patches/todo/server/0118-Snow-Golem-rate-of-fire-config.patch similarity index 100% rename from patches/server/0118-Snow-Golem-rate-of-fire-config.patch rename to patches/todo/server/0118-Snow-Golem-rate-of-fire-config.patch diff --git a/patches/server/0119-EMC-Configurable-disable-give-dropping.patch b/patches/todo/server/0119-EMC-Configurable-disable-give-dropping.patch similarity index 100% rename from patches/server/0119-EMC-Configurable-disable-give-dropping.patch rename to patches/todo/server/0119-EMC-Configurable-disable-give-dropping.patch diff --git a/patches/server/0120-Option-for-Villager-Clerics-to-farm-Nether-Wart.patch b/patches/todo/server/0120-Option-for-Villager-Clerics-to-farm-Nether-Wart.patch similarity index 100% rename from patches/server/0120-Option-for-Villager-Clerics-to-farm-Nether-Wart.patch rename to patches/todo/server/0120-Option-for-Villager-Clerics-to-farm-Nether-Wart.patch diff --git a/patches/server/0121-Toggle-for-Zombified-Piglin-death-always-counting-as.patch b/patches/todo/server/0121-Toggle-for-Zombified-Piglin-death-always-counting-as.patch similarity index 100% rename from patches/server/0121-Toggle-for-Zombified-Piglin-death-always-counting-as.patch rename to patches/todo/server/0121-Toggle-for-Zombified-Piglin-death-always-counting-as.patch diff --git a/patches/server/0122-Spread-out-and-optimise-player-list-ticksSpread-out-.patch b/patches/todo/server/0122-Spread-out-and-optimise-player-list-ticksSpread-out-.patch similarity index 100% rename from patches/server/0122-Spread-out-and-optimise-player-list-ticksSpread-out-.patch rename to patches/todo/server/0122-Spread-out-and-optimise-player-list-ticksSpread-out-.patch diff --git a/patches/server/0123-Configurable-chance-for-wolves-to-spawn-rabid.patch b/patches/todo/server/0123-Configurable-chance-for-wolves-to-spawn-rabid.patch similarity index 100% rename from patches/server/0123-Configurable-chance-for-wolves-to-spawn-rabid.patch rename to patches/todo/server/0123-Configurable-chance-for-wolves-to-spawn-rabid.patch diff --git a/patches/server/0124-Configurable-default-collar-color.patch b/patches/todo/server/0124-Configurable-default-collar-color.patch similarity index 100% rename from patches/server/0124-Configurable-default-collar-color.patch rename to patches/todo/server/0124-Configurable-default-collar-color.patch diff --git a/patches/server/0125-Phantom-flames-on-swoop.patch b/patches/todo/server/0125-Phantom-flames-on-swoop.patch similarity index 100% rename from patches/server/0125-Phantom-flames-on-swoop.patch rename to patches/todo/server/0125-Phantom-flames-on-swoop.patch diff --git a/patches/server/0126-Option-for-chests-to-open-even-with-a-solid-block-on.patch b/patches/todo/server/0126-Option-for-chests-to-open-even-with-a-solid-block-on.patch similarity index 100% rename from patches/server/0126-Option-for-chests-to-open-even-with-a-solid-block-on.patch rename to patches/todo/server/0126-Option-for-chests-to-open-even-with-a-solid-block-on.patch diff --git a/patches/server/0127-Implement-TPSBar.patch b/patches/todo/server/0127-Implement-TPSBar.patch similarity index 100% rename from patches/server/0127-Implement-TPSBar.patch rename to patches/todo/server/0127-Implement-TPSBar.patch diff --git a/patches/server/0128-Striders-give-saddle-back.patch b/patches/todo/server/0128-Striders-give-saddle-back.patch similarity index 100% rename from patches/server/0128-Striders-give-saddle-back.patch rename to patches/todo/server/0128-Striders-give-saddle-back.patch diff --git a/patches/server/0129-PlayerBookTooLargeEvent.patch b/patches/todo/server/0129-PlayerBookTooLargeEvent.patch similarity index 100% rename from patches/server/0129-PlayerBookTooLargeEvent.patch rename to patches/todo/server/0129-PlayerBookTooLargeEvent.patch diff --git a/patches/server/0130-Full-netherite-armor-grants-fire-resistance.patch b/patches/todo/server/0130-Full-netherite-armor-grants-fire-resistance.patch similarity index 100% rename from patches/server/0130-Full-netherite-armor-grants-fire-resistance.patch rename to patches/todo/server/0130-Full-netherite-armor-grants-fire-resistance.patch diff --git a/patches/server/0131-Fix-rotating-UP-DOWN-CW-and-CCW.patch b/patches/todo/server/0131-Fix-rotating-UP-DOWN-CW-and-CCW.patch similarity index 100% rename from patches/server/0131-Fix-rotating-UP-DOWN-CW-and-CCW.patch rename to patches/todo/server/0131-Fix-rotating-UP-DOWN-CW-and-CCW.patch diff --git a/patches/server/0132-Add-mobGriefing-bypass-to-everything-affected.patch b/patches/todo/server/0132-Add-mobGriefing-bypass-to-everything-affected.patch similarity index 100% rename from patches/server/0132-Add-mobGriefing-bypass-to-everything-affected.patch rename to patches/todo/server/0132-Add-mobGriefing-bypass-to-everything-affected.patch diff --git a/patches/server/0133-Config-to-allow-Note-Block-sounds-when-blocked.patch b/patches/todo/server/0133-Config-to-allow-Note-Block-sounds-when-blocked.patch similarity index 100% rename from patches/server/0133-Config-to-allow-Note-Block-sounds-when-blocked.patch rename to patches/todo/server/0133-Config-to-allow-Note-Block-sounds-when-blocked.patch diff --git a/patches/server/0134-Add-EntityTeleportHinderedEvent.patch b/patches/todo/server/0134-Add-EntityTeleportHinderedEvent.patch similarity index 100% rename from patches/server/0134-Add-EntityTeleportHinderedEvent.patch rename to patches/todo/server/0134-Add-EntityTeleportHinderedEvent.patch diff --git a/patches/server/0135-Farmland-trampling-changes.patch b/patches/todo/server/0135-Farmland-trampling-changes.patch similarity index 100% rename from patches/server/0135-Farmland-trampling-changes.patch rename to patches/todo/server/0135-Farmland-trampling-changes.patch diff --git a/patches/server/0136-Movement-options-for-armor-stands.patch b/patches/todo/server/0136-Movement-options-for-armor-stands.patch similarity index 100% rename from patches/server/0136-Movement-options-for-armor-stands.patch rename to patches/todo/server/0136-Movement-options-for-armor-stands.patch diff --git a/patches/server/0137-Fix-stuck-in-portals.patch b/patches/todo/server/0137-Fix-stuck-in-portals.patch similarity index 100% rename from patches/server/0137-Fix-stuck-in-portals.patch rename to patches/todo/server/0137-Fix-stuck-in-portals.patch diff --git a/patches/server/0138-Toggle-for-water-sensitive-mob-damage.patch b/patches/todo/server/0138-Toggle-for-water-sensitive-mob-damage.patch similarity index 100% rename from patches/server/0138-Toggle-for-water-sensitive-mob-damage.patch rename to patches/todo/server/0138-Toggle-for-water-sensitive-mob-damage.patch diff --git a/patches/todo/server/0139-Add-StructureGenerateEvent.patch b/patches/todo/server/0139-Add-StructureGenerateEvent.patch deleted file mode 100644 index c41be0bc3..000000000 --- a/patches/todo/server/0139-Add-StructureGenerateEvent.patch +++ /dev/null @@ -1,39 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Nahuel -Date: Sat, 9 Jan 2021 15:36:59 +0100 -Subject: [PATCH] Add StructureGenerateEvent - -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 . - -Co-authored-by: Mariell Hoversholm - -diff --git a/src/main/java/net/minecraft/world/level/chunk/ChunkGenerator.java b/src/main/java/net/minecraft/world/level/chunk/ChunkGenerator.java -index e4591c0b3c8547cc6f4e2a0891fc378ee4334d9e..11b0697f668f6c0965a74081c149585fdf49b209 100644 ---- a/src/main/java/net/minecraft/world/level/chunk/ChunkGenerator.java -+++ b/src/main/java/net/minecraft/world/level/chunk/ChunkGenerator.java -@@ -458,6 +458,14 @@ public abstract class ChunkGenerator implements BiomeManager.NoiseBiomeSource { - if (structurestart == null || !structurestart.isValid()) { - StructureStart structurestart1 = StructureFeatures.STRONGHOLD.generate(registryManager, this, this.biomeSource, structureManager, worldSeed, chunkcoordintpair, ChunkGenerator.fetchReferences(structureAccessor, chunk, sectionposition, StructureFeature.STRONGHOLD), structuresettingsfeature, chunk, ChunkGenerator::validStrongholdBiome); - -+ // Purpur start -+ if (new org.purpurmc.purpur.event.world.StructureGenerateEvent( -+ structureAccessor.getWorld().getWorld(), -+ org.bukkit.StructureType.getStructureTypes().get(structurestart1.getFeature().getFeatureName().toLowerCase()), -+ chunkcoordintpair.x, -+ chunkcoordintpair.z -+ ).callEvent()) -+ // Purpur end - structureAccessor.setStartForFeature(sectionposition, StructureFeature.STRONGHOLD, structurestart1, chunk); - } - } diff --git a/patches/server/0139-Config-to-always-tame-in-Creative.patch b/patches/todo/server/0139-Config-to-always-tame-in-Creative.patch similarity index 100% rename from patches/server/0139-Config-to-always-tame-in-Creative.patch rename to patches/todo/server/0139-Config-to-always-tame-in-Creative.patch diff --git a/patches/server/0140-End-crystal-explosion-options.patch b/patches/todo/server/0140-End-crystal-explosion-options.patch similarity index 100% rename from patches/server/0140-End-crystal-explosion-options.patch rename to patches/todo/server/0140-End-crystal-explosion-options.patch diff --git a/patches/server/0141-Configs-for-if-Wither-Ender-Dragon-can-ride-vehicles.patch b/patches/todo/server/0141-Configs-for-if-Wither-Ender-Dragon-can-ride-vehicles.patch similarity index 100% rename from patches/server/0141-Configs-for-if-Wither-Ender-Dragon-can-ride-vehicles.patch rename to patches/todo/server/0141-Configs-for-if-Wither-Ender-Dragon-can-ride-vehicles.patch diff --git a/patches/server/0142-Dont-run-with-scissors.patch b/patches/todo/server/0142-Dont-run-with-scissors.patch similarity index 100% rename from patches/server/0142-Dont-run-with-scissors.patch rename to patches/todo/server/0142-Dont-run-with-scissors.patch diff --git a/patches/server/0143-One-Punch-Man.patch b/patches/todo/server/0143-One-Punch-Man.patch similarity index 100% rename from patches/server/0143-One-Punch-Man.patch rename to patches/todo/server/0143-One-Punch-Man.patch diff --git a/patches/server/0144-Add-config-for-snow-on-blue-ice.patch b/patches/todo/server/0144-Add-config-for-snow-on-blue-ice.patch similarity index 100% rename from patches/server/0144-Add-config-for-snow-on-blue-ice.patch rename to patches/todo/server/0144-Add-config-for-snow-on-blue-ice.patch diff --git a/patches/server/0145-Configurable-Ender-Pearl-cooldown-damage-and-Endermi.patch b/patches/todo/server/0145-Configurable-Ender-Pearl-cooldown-damage-and-Endermi.patch similarity index 100% rename from patches/server/0145-Configurable-Ender-Pearl-cooldown-damage-and-Endermi.patch rename to patches/todo/server/0145-Configurable-Ender-Pearl-cooldown-damage-and-Endermi.patch diff --git a/patches/server/0146-Config-to-ignore-nearby-mobs-when-sleeping.patch b/patches/todo/server/0146-Config-to-ignore-nearby-mobs-when-sleeping.patch similarity index 100% rename from patches/server/0146-Config-to-ignore-nearby-mobs-when-sleeping.patch rename to patches/todo/server/0146-Config-to-ignore-nearby-mobs-when-sleeping.patch diff --git a/patches/server/0147-Add-back-player-spawned-endermite-API.patch b/patches/todo/server/0147-Add-back-player-spawned-endermite-API.patch similarity index 100% rename from patches/server/0147-Add-back-player-spawned-endermite-API.patch rename to patches/todo/server/0147-Add-back-player-spawned-endermite-API.patch diff --git a/patches/server/0148-Config-Enderman-aggressiveness-towards-Endermites.patch b/patches/todo/server/0148-Config-Enderman-aggressiveness-towards-Endermites.patch similarity index 100% rename from patches/server/0148-Config-Enderman-aggressiveness-towards-Endermites.patch rename to patches/todo/server/0148-Config-Enderman-aggressiveness-towards-Endermites.patch diff --git a/patches/server/0149-Config-to-ignore-Dragon-Head-wearers-and-stare-aggro.patch b/patches/todo/server/0149-Config-to-ignore-Dragon-Head-wearers-and-stare-aggro.patch similarity index 100% rename from patches/server/0149-Config-to-ignore-Dragon-Head-wearers-and-stare-aggro.patch rename to patches/todo/server/0149-Config-to-ignore-Dragon-Head-wearers-and-stare-aggro.patch diff --git a/patches/server/0150-Tick-fluids-config.patch b/patches/todo/server/0150-Tick-fluids-config.patch similarity index 100% rename from patches/server/0150-Tick-fluids-config.patch rename to patches/todo/server/0150-Tick-fluids-config.patch diff --git a/patches/server/0151-Config-to-disable-Llama-caravans.patch b/patches/todo/server/0151-Config-to-disable-Llama-caravans.patch similarity index 100% rename from patches/server/0151-Config-to-disable-Llama-caravans.patch rename to patches/todo/server/0151-Config-to-disable-Llama-caravans.patch diff --git a/patches/server/0152-Config-to-make-Creepers-explode-on-death.patch b/patches/todo/server/0152-Config-to-make-Creepers-explode-on-death.patch similarity index 100% rename from patches/server/0152-Config-to-make-Creepers-explode-on-death.patch rename to patches/todo/server/0152-Config-to-make-Creepers-explode-on-death.patch diff --git a/patches/server/0153-Configurable-ravager-griefable-blocks-list.patch b/patches/todo/server/0153-Configurable-ravager-griefable-blocks-list.patch similarity index 100% rename from patches/server/0153-Configurable-ravager-griefable-blocks-list.patch rename to patches/todo/server/0153-Configurable-ravager-griefable-blocks-list.patch diff --git a/patches/server/0154-Sneak-to-bulk-process-composter.patch b/patches/todo/server/0154-Sneak-to-bulk-process-composter.patch similarity index 100% rename from patches/server/0154-Sneak-to-bulk-process-composter.patch rename to patches/todo/server/0154-Sneak-to-bulk-process-composter.patch diff --git a/patches/server/0155-Config-for-skipping-night.patch b/patches/todo/server/0155-Config-for-skipping-night.patch similarity index 100% rename from patches/server/0155-Config-for-skipping-night.patch rename to patches/todo/server/0155-Config-for-skipping-night.patch diff --git a/patches/server/0156-Add-config-for-villager-trading.patch b/patches/todo/server/0156-Add-config-for-villager-trading.patch similarity index 100% rename from patches/server/0156-Add-config-for-villager-trading.patch rename to patches/todo/server/0156-Add-config-for-villager-trading.patch diff --git a/patches/server/0157-Allow-infinity-on-crossbows.patch b/patches/todo/server/0157-Allow-infinity-on-crossbows.patch similarity index 100% rename from patches/server/0157-Allow-infinity-on-crossbows.patch rename to patches/todo/server/0157-Allow-infinity-on-crossbows.patch diff --git a/patches/server/0158-Drowning-Settings.patch b/patches/todo/server/0158-Drowning-Settings.patch similarity index 100% rename from patches/server/0158-Drowning-Settings.patch rename to patches/todo/server/0158-Drowning-Settings.patch diff --git a/patches/server/0159-Break-individual-slabs-when-sneaking.patch b/patches/todo/server/0159-Break-individual-slabs-when-sneaking.patch similarity index 100% rename from patches/server/0159-Break-individual-slabs-when-sneaking.patch rename to patches/todo/server/0159-Break-individual-slabs-when-sneaking.patch diff --git a/patches/server/0160-Config-to-disable-hostile-mob-spawn-on-ice.patch b/patches/todo/server/0160-Config-to-disable-hostile-mob-spawn-on-ice.patch similarity index 100% rename from patches/server/0160-Config-to-disable-hostile-mob-spawn-on-ice.patch rename to patches/todo/server/0160-Config-to-disable-hostile-mob-spawn-on-ice.patch diff --git a/patches/server/0161-Config-to-show-Armor-Stand-arms-on-spawn.patch b/patches/todo/server/0161-Config-to-show-Armor-Stand-arms-on-spawn.patch similarity index 100% rename from patches/server/0161-Config-to-show-Armor-Stand-arms-on-spawn.patch rename to patches/todo/server/0161-Config-to-show-Armor-Stand-arms-on-spawn.patch diff --git a/patches/server/0162-Option-to-make-doors-require-redstone.patch b/patches/todo/server/0162-Option-to-make-doors-require-redstone.patch similarity index 100% rename from patches/server/0162-Option-to-make-doors-require-redstone.patch rename to patches/todo/server/0162-Option-to-make-doors-require-redstone.patch diff --git a/patches/server/0163-Config-to-allow-for-unsafe-enchants.patch b/patches/todo/server/0163-Config-to-allow-for-unsafe-enchants.patch similarity index 100% rename from patches/server/0163-Config-to-allow-for-unsafe-enchants.patch rename to patches/todo/server/0163-Config-to-allow-for-unsafe-enchants.patch diff --git a/patches/server/0164-Configurable-sponge-absorption.patch b/patches/todo/server/0164-Configurable-sponge-absorption.patch similarity index 100% rename from patches/server/0164-Configurable-sponge-absorption.patch rename to patches/todo/server/0164-Configurable-sponge-absorption.patch diff --git a/patches/server/0165-Projectile-offset-config.patch b/patches/todo/server/0165-Projectile-offset-config.patch similarity index 100% rename from patches/server/0165-Projectile-offset-config.patch rename to patches/todo/server/0165-Projectile-offset-config.patch diff --git a/patches/server/0166-Config-for-powered-rail-activation-distance.patch b/patches/todo/server/0166-Config-for-powered-rail-activation-distance.patch similarity index 100% rename from patches/server/0166-Config-for-powered-rail-activation-distance.patch rename to patches/todo/server/0166-Config-for-powered-rail-activation-distance.patch diff --git a/patches/server/0167-Piglin-portal-spawn-modifier.patch b/patches/todo/server/0167-Piglin-portal-spawn-modifier.patch similarity index 100% rename from patches/server/0167-Piglin-portal-spawn-modifier.patch rename to patches/todo/server/0167-Piglin-portal-spawn-modifier.patch diff --git a/patches/server/0168-Config-to-change-max-number-of-bees.patch b/patches/todo/server/0168-Config-to-change-max-number-of-bees.patch similarity index 100% rename from patches/server/0168-Config-to-change-max-number-of-bees.patch rename to patches/todo/server/0168-Config-to-change-max-number-of-bees.patch diff --git a/patches/server/0169-Configurable-damage-settings-for-magma-blocks.patch b/patches/todo/server/0169-Configurable-damage-settings-for-magma-blocks.patch similarity index 100% rename from patches/server/0169-Configurable-damage-settings-for-magma-blocks.patch rename to patches/todo/server/0169-Configurable-damage-settings-for-magma-blocks.patch diff --git a/patches/server/0170-Config-for-wither-explosion-radius.patch b/patches/todo/server/0170-Config-for-wither-explosion-radius.patch similarity index 100% rename from patches/server/0170-Config-for-wither-explosion-radius.patch rename to patches/todo/server/0170-Config-for-wither-explosion-radius.patch diff --git a/patches/server/0171-Gamemode-extra-permissions.patch b/patches/todo/server/0171-Gamemode-extra-permissions.patch similarity index 100% rename from patches/server/0171-Gamemode-extra-permissions.patch rename to patches/todo/server/0171-Gamemode-extra-permissions.patch diff --git a/patches/server/0172-Config-for-changing-the-blocks-that-turn-into-dirt-p.patch b/patches/todo/server/0172-Config-for-changing-the-blocks-that-turn-into-dirt-p.patch similarity index 100% rename from patches/server/0172-Config-for-changing-the-blocks-that-turn-into-dirt-p.patch rename to patches/todo/server/0172-Config-for-changing-the-blocks-that-turn-into-dirt-p.patch diff --git a/patches/server/0173-Configurable-piston-push-limit.patch b/patches/todo/server/0173-Configurable-piston-push-limit.patch similarity index 100% rename from patches/server/0173-Configurable-piston-push-limit.patch rename to patches/todo/server/0173-Configurable-piston-push-limit.patch diff --git a/patches/server/0174-Configurable-broadcast-settings.patch b/patches/todo/server/0174-Configurable-broadcast-settings.patch similarity index 100% rename from patches/server/0174-Configurable-broadcast-settings.patch rename to patches/todo/server/0174-Configurable-broadcast-settings.patch diff --git a/patches/server/0175-Configurable-mob-blindness.patch b/patches/todo/server/0175-Configurable-mob-blindness.patch similarity index 100% rename from patches/server/0175-Configurable-mob-blindness.patch rename to patches/todo/server/0175-Configurable-mob-blindness.patch diff --git a/patches/server/0176-Hide-hidden-players-from-entity-selector.patch b/patches/todo/server/0176-Hide-hidden-players-from-entity-selector.patch similarity index 100% rename from patches/server/0176-Hide-hidden-players-from-entity-selector.patch rename to patches/todo/server/0176-Hide-hidden-players-from-entity-selector.patch diff --git a/patches/server/0177-Config-for-health-to-impact-Creeper-explosion-radius.patch b/patches/todo/server/0177-Config-for-health-to-impact-Creeper-explosion-radius.patch similarity index 100% rename from patches/server/0177-Config-for-health-to-impact-Creeper-explosion-radius.patch rename to patches/todo/server/0177-Config-for-health-to-impact-Creeper-explosion-radius.patch diff --git a/patches/server/0178-Iron-golem-calm-anger-options.patch b/patches/todo/server/0178-Iron-golem-calm-anger-options.patch similarity index 100% rename from patches/server/0178-Iron-golem-calm-anger-options.patch rename to patches/todo/server/0178-Iron-golem-calm-anger-options.patch diff --git a/patches/server/0179-Breedable-parrots.patch b/patches/todo/server/0179-Breedable-parrots.patch similarity index 100% rename from patches/server/0179-Breedable-parrots.patch rename to patches/todo/server/0179-Breedable-parrots.patch diff --git a/patches/server/0180-Configurable-powered-rail-boost-modifier.patch b/patches/todo/server/0180-Configurable-powered-rail-boost-modifier.patch similarity index 100% rename from patches/server/0180-Configurable-powered-rail-boost-modifier.patch rename to patches/todo/server/0180-Configurable-powered-rail-boost-modifier.patch diff --git a/patches/server/0181-Add-config-change-multiplier-critical-damage-value.patch b/patches/todo/server/0181-Add-config-change-multiplier-critical-damage-value.patch similarity index 100% rename from patches/server/0181-Add-config-change-multiplier-critical-damage-value.patch rename to patches/todo/server/0181-Add-config-change-multiplier-critical-damage-value.patch diff --git a/patches/server/0182-Option-to-disable-dragon-egg-teleporting.patch b/patches/todo/server/0182-Option-to-disable-dragon-egg-teleporting.patch similarity index 100% rename from patches/server/0182-Option-to-disable-dragon-egg-teleporting.patch rename to patches/todo/server/0182-Option-to-disable-dragon-egg-teleporting.patch diff --git a/patches/server/0183-Config-for-unverified-username-message.patch b/patches/todo/server/0183-Config-for-unverified-username-message.patch similarity index 100% rename from patches/server/0183-Config-for-unverified-username-message.patch rename to patches/todo/server/0183-Config-for-unverified-username-message.patch diff --git a/patches/server/0184-Make-anvil-cumulative-cost-configurable.patch b/patches/todo/server/0184-Make-anvil-cumulative-cost-configurable.patch similarity index 100% rename from patches/server/0184-Make-anvil-cumulative-cost-configurable.patch rename to patches/todo/server/0184-Make-anvil-cumulative-cost-configurable.patch diff --git a/patches/server/0185-ShulkerBox-allow-oversized-stacks.patch b/patches/todo/server/0185-ShulkerBox-allow-oversized-stacks.patch similarity index 100% rename from patches/server/0185-ShulkerBox-allow-oversized-stacks.patch rename to patches/todo/server/0185-ShulkerBox-allow-oversized-stacks.patch diff --git a/patches/server/0186-Bee-can-work-when-raining-or-at-night.patch b/patches/todo/server/0186-Bee-can-work-when-raining-or-at-night.patch similarity index 100% rename from patches/server/0186-Bee-can-work-when-raining-or-at-night.patch rename to patches/todo/server/0186-Bee-can-work-when-raining-or-at-night.patch diff --git a/patches/server/0187-API-for-any-mob-to-burn-daylight.patch b/patches/todo/server/0187-API-for-any-mob-to-burn-daylight.patch similarity index 100% rename from patches/server/0187-API-for-any-mob-to-burn-daylight.patch rename to patches/todo/server/0187-API-for-any-mob-to-burn-daylight.patch diff --git a/patches/server/0188-Config-MobEffect-by-world.patch b/patches/todo/server/0188-Config-MobEffect-by-world.patch similarity index 100% rename from patches/server/0188-Config-MobEffect-by-world.patch rename to patches/todo/server/0188-Config-MobEffect-by-world.patch diff --git a/patches/server/0189-Beacon-Activation-Range-Configurable.patch b/patches/todo/server/0189-Beacon-Activation-Range-Configurable.patch similarity index 100% rename from patches/server/0189-Beacon-Activation-Range-Configurable.patch rename to patches/todo/server/0189-Beacon-Activation-Range-Configurable.patch diff --git a/patches/server/0190-Add-toggle-for-sand-duping-fix.patch b/patches/todo/server/0190-Add-toggle-for-sand-duping-fix.patch similarity index 100% rename from patches/server/0190-Add-toggle-for-sand-duping-fix.patch rename to patches/todo/server/0190-Add-toggle-for-sand-duping-fix.patch diff --git a/patches/server/0191-Add-toggle-for-end-portal-safe-teleporting.patch b/patches/todo/server/0191-Add-toggle-for-end-portal-safe-teleporting.patch similarity index 100% rename from patches/server/0191-Add-toggle-for-end-portal-safe-teleporting.patch rename to patches/todo/server/0191-Add-toggle-for-end-portal-safe-teleporting.patch diff --git a/patches/server/0192-Flying-Fall-Damage-API.patch b/patches/todo/server/0192-Flying-Fall-Damage-API.patch similarity index 100% rename from patches/server/0192-Flying-Fall-Damage-API.patch rename to patches/todo/server/0192-Flying-Fall-Damage-API.patch diff --git a/patches/server/0193-Make-lightning-rod-range-configurable.patch b/patches/todo/server/0193-Make-lightning-rod-range-configurable.patch similarity index 100% rename from patches/server/0193-Make-lightning-rod-range-configurable.patch rename to patches/todo/server/0193-Make-lightning-rod-range-configurable.patch diff --git a/patches/server/0194-Burp-after-eating-food-fills-hunger-bar-completely.patch b/patches/todo/server/0194-Burp-after-eating-food-fills-hunger-bar-completely.patch similarity index 100% rename from patches/server/0194-Burp-after-eating-food-fills-hunger-bar-completely.patch rename to patches/todo/server/0194-Burp-after-eating-food-fills-hunger-bar-completely.patch diff --git a/patches/server/0195-Allow-player-join-full-server-by-permission.patch b/patches/todo/server/0195-Allow-player-join-full-server-by-permission.patch similarity index 100% rename from patches/server/0195-Allow-player-join-full-server-by-permission.patch rename to patches/todo/server/0195-Allow-player-join-full-server-by-permission.patch diff --git a/patches/server/0196-Add-permission-bypass-for-portal-waiting.patch b/patches/todo/server/0196-Add-permission-bypass-for-portal-waiting.patch similarity index 100% rename from patches/server/0196-Add-permission-bypass-for-portal-waiting.patch rename to patches/todo/server/0196-Add-permission-bypass-for-portal-waiting.patch diff --git a/patches/server/0197-Shulker-spawn-from-bullet-options.patch b/patches/todo/server/0197-Shulker-spawn-from-bullet-options.patch similarity index 100% rename from patches/server/0197-Shulker-spawn-from-bullet-options.patch rename to patches/todo/server/0197-Shulker-spawn-from-bullet-options.patch diff --git a/patches/server/0198-Eating-glow-berries-adds-glow-effect.patch b/patches/todo/server/0198-Eating-glow-berries-adds-glow-effect.patch similarity index 100% rename from patches/server/0198-Eating-glow-berries-adds-glow-effect.patch rename to patches/todo/server/0198-Eating-glow-berries-adds-glow-effect.patch diff --git a/patches/server/0199-Option-to-make-drowned-break-doors.patch b/patches/todo/server/0199-Option-to-make-drowned-break-doors.patch similarity index 100% rename from patches/server/0199-Option-to-make-drowned-break-doors.patch rename to patches/todo/server/0199-Option-to-make-drowned-break-doors.patch diff --git a/patches/server/0200-Configurable-hunger-starvation-damage.patch b/patches/todo/server/0200-Configurable-hunger-starvation-damage.patch similarity index 100% rename from patches/server/0200-Configurable-hunger-starvation-damage.patch rename to patches/todo/server/0200-Configurable-hunger-starvation-damage.patch diff --git a/patches/server/0201-Enhance-SysoutCatcher.patch b/patches/todo/server/0201-Enhance-SysoutCatcher.patch similarity index 100% rename from patches/server/0201-Enhance-SysoutCatcher.patch rename to patches/todo/server/0201-Enhance-SysoutCatcher.patch diff --git a/patches/server/0202-Armor-click-equip-options.patch b/patches/todo/server/0202-Armor-click-equip-options.patch similarity index 100% rename from patches/server/0202-Armor-click-equip-options.patch rename to patches/todo/server/0202-Armor-click-equip-options.patch diff --git a/patches/server/0203-Add-uptime-command.patch b/patches/todo/server/0203-Add-uptime-command.patch similarity index 100% rename from patches/server/0203-Add-uptime-command.patch rename to patches/todo/server/0203-Add-uptime-command.patch diff --git a/patches/server/0204-Structure-seed-options.patch b/patches/todo/server/0204-Structure-seed-options.patch similarity index 100% rename from patches/server/0204-Structure-seed-options.patch rename to patches/todo/server/0204-Structure-seed-options.patch diff --git a/patches/server/0205-Tool-actionable-options.patch b/patches/todo/server/0205-Tool-actionable-options.patch similarity index 100% rename from patches/server/0205-Tool-actionable-options.patch rename to patches/todo/server/0205-Tool-actionable-options.patch diff --git a/patches/server/0206-Store-placer-on-Block-when-placed.patch b/patches/todo/server/0206-Store-placer-on-Block-when-placed.patch similarity index 100% rename from patches/server/0206-Store-placer-on-Block-when-placed.patch rename to patches/todo/server/0206-Store-placer-on-Block-when-placed.patch diff --git a/patches/server/0207-Summoner-API.patch b/patches/todo/server/0207-Summoner-API.patch similarity index 100% rename from patches/server/0207-Summoner-API.patch rename to patches/todo/server/0207-Summoner-API.patch diff --git a/patches/server/0208-Customizable-sleeping-actionbar-messages.patch b/patches/todo/server/0208-Customizable-sleeping-actionbar-messages.patch similarity index 100% rename from patches/server/0208-Customizable-sleeping-actionbar-messages.patch rename to patches/todo/server/0208-Customizable-sleeping-actionbar-messages.patch diff --git a/patches/server/0209-option-to-disable-shulker-box-items-from-dropping-co.patch b/patches/todo/server/0209-option-to-disable-shulker-box-items-from-dropping-co.patch similarity index 100% rename from patches/server/0209-option-to-disable-shulker-box-items-from-dropping-co.patch rename to patches/todo/server/0209-option-to-disable-shulker-box-items-from-dropping-co.patch diff --git a/patches/server/0210-Silk-touchable-budding-amethyst.patch b/patches/todo/server/0210-Silk-touchable-budding-amethyst.patch similarity index 100% rename from patches/server/0210-Silk-touchable-budding-amethyst.patch rename to patches/todo/server/0210-Silk-touchable-budding-amethyst.patch diff --git a/patches/server/0211-Big-dripleaf-tilt-delay.patch b/patches/todo/server/0211-Big-dripleaf-tilt-delay.patch similarity index 100% rename from patches/server/0211-Big-dripleaf-tilt-delay.patch rename to patches/todo/server/0211-Big-dripleaf-tilt-delay.patch diff --git a/patches/server/0212-Player-ridable-in-water-option.patch b/patches/todo/server/0212-Player-ridable-in-water-option.patch similarity index 100% rename from patches/server/0212-Player-ridable-in-water-option.patch rename to patches/todo/server/0212-Player-ridable-in-water-option.patch diff --git a/patches/server/0213-Config-to-disable-Enderman-teleport-on-projectile-hi.patch b/patches/todo/server/0213-Config-to-disable-Enderman-teleport-on-projectile-hi.patch similarity index 100% rename from patches/server/0213-Config-to-disable-Enderman-teleport-on-projectile-hi.patch rename to patches/todo/server/0213-Config-to-disable-Enderman-teleport-on-projectile-hi.patch diff --git a/patches/server/0214-Add-compass-command.patch b/patches/todo/server/0214-Add-compass-command.patch similarity index 100% rename from patches/server/0214-Add-compass-command.patch rename to patches/todo/server/0214-Add-compass-command.patch diff --git a/patches/server/0215-Config-to-prevent-horses-from-standing-with-riders.patch b/patches/todo/server/0215-Config-to-prevent-horses-from-standing-with-riders.patch similarity index 100% rename from patches/server/0215-Config-to-prevent-horses-from-standing-with-riders.patch rename to patches/todo/server/0215-Config-to-prevent-horses-from-standing-with-riders.patch diff --git a/patches/server/0216-Toggle-for-kinetic-damage.patch b/patches/todo/server/0216-Toggle-for-kinetic-damage.patch similarity index 100% rename from patches/server/0216-Toggle-for-kinetic-damage.patch rename to patches/todo/server/0216-Toggle-for-kinetic-damage.patch diff --git a/patches/server/0217-Add-Option-for-disable-observer-clocks.patch b/patches/todo/server/0217-Add-Option-for-disable-observer-clocks.patch similarity index 100% rename from patches/server/0217-Add-Option-for-disable-observer-clocks.patch rename to patches/todo/server/0217-Add-Option-for-disable-observer-clocks.patch diff --git a/patches/server/0218-Customizeable-Zombie-Villager-curing-times.patch b/patches/todo/server/0218-Customizeable-Zombie-Villager-curing-times.patch similarity index 100% rename from patches/server/0218-Customizeable-Zombie-Villager-curing-times.patch rename to patches/todo/server/0218-Customizeable-Zombie-Villager-curing-times.patch diff --git a/patches/server/0219-Option-for-sponges-to-work-on-lava.patch b/patches/todo/server/0219-Option-for-sponges-to-work-on-lava.patch similarity index 100% rename from patches/server/0219-Option-for-sponges-to-work-on-lava.patch rename to patches/todo/server/0219-Option-for-sponges-to-work-on-lava.patch diff --git a/patches/server/0220-Toggle-for-Wither-s-spawn-sound.patch b/patches/todo/server/0220-Toggle-for-Wither-s-spawn-sound.patch similarity index 100% rename from patches/server/0220-Toggle-for-Wither-s-spawn-sound.patch rename to patches/todo/server/0220-Toggle-for-Wither-s-spawn-sound.patch diff --git a/patches/server/0221-Cactus-breaks-from-solid-neighbors-config.patch b/patches/todo/server/0221-Cactus-breaks-from-solid-neighbors-config.patch similarity index 100% rename from patches/server/0221-Cactus-breaks-from-solid-neighbors-config.patch rename to patches/todo/server/0221-Cactus-breaks-from-solid-neighbors-config.patch diff --git a/patches/server/0222-Config-to-remove-curse-of-binding-with-weakness.patch b/patches/todo/server/0222-Config-to-remove-curse-of-binding-with-weakness.patch similarity index 100% rename from patches/server/0222-Config-to-remove-curse-of-binding-with-weakness.patch rename to patches/todo/server/0222-Config-to-remove-curse-of-binding-with-weakness.patch diff --git a/patches/server/0223-Conduit-behavior-configuration.patch b/patches/todo/server/0223-Conduit-behavior-configuration.patch similarity index 100% rename from patches/server/0223-Conduit-behavior-configuration.patch rename to patches/todo/server/0223-Conduit-behavior-configuration.patch diff --git a/patches/server/0224-Cauldron-fill-chances.patch b/patches/todo/server/0224-Cauldron-fill-chances.patch similarity index 100% rename from patches/server/0224-Cauldron-fill-chances.patch rename to patches/todo/server/0224-Cauldron-fill-chances.patch diff --git a/patches/server/0225-Config-to-allow-mobs-to-pathfind-over-rails.patch b/patches/todo/server/0225-Config-to-allow-mobs-to-pathfind-over-rails.patch similarity index 100% rename from patches/server/0225-Config-to-allow-mobs-to-pathfind-over-rails.patch rename to patches/todo/server/0225-Config-to-allow-mobs-to-pathfind-over-rails.patch diff --git a/patches/server/0226-Shulker-change-color-with-dye.patch b/patches/todo/server/0226-Shulker-change-color-with-dye.patch similarity index 100% rename from patches/server/0226-Shulker-change-color-with-dye.patch rename to patches/todo/server/0226-Shulker-change-color-with-dye.patch diff --git a/patches/server/0227-Extended-OfflinePlayer-API.patch b/patches/todo/server/0227-Extended-OfflinePlayer-API.patch similarity index 100% rename from patches/server/0227-Extended-OfflinePlayer-API.patch rename to patches/todo/server/0227-Extended-OfflinePlayer-API.patch diff --git a/patches/server/0228-Added-the-ability-to-add-combustible-items.patch b/patches/todo/server/0228-Added-the-ability-to-add-combustible-items.patch similarity index 100% rename from patches/server/0228-Added-the-ability-to-add-combustible-items.patch rename to patches/todo/server/0228-Added-the-ability-to-add-combustible-items.patch diff --git a/patches/server/0229-Option-for-if-rain-and-thunder-should-stop-on-sleep.patch b/patches/todo/server/0229-Option-for-if-rain-and-thunder-should-stop-on-sleep.patch similarity index 100% rename from patches/server/0229-Option-for-if-rain-and-thunder-should-stop-on-sleep.patch rename to patches/todo/server/0229-Option-for-if-rain-and-thunder-should-stop-on-sleep.patch diff --git a/patches/server/0230-Chance-for-azalea-blocks-to-grow-into-trees-naturall.patch b/patches/todo/server/0230-Chance-for-azalea-blocks-to-grow-into-trees-naturall.patch similarity index 100% rename from patches/server/0230-Chance-for-azalea-blocks-to-grow-into-trees-naturall.patch rename to patches/todo/server/0230-Chance-for-azalea-blocks-to-grow-into-trees-naturall.patch diff --git a/patches/server/0231-Shift-right-click-to-use-exp-for-mending.patch b/patches/todo/server/0231-Shift-right-click-to-use-exp-for-mending.patch similarity index 100% rename from patches/server/0231-Shift-right-click-to-use-exp-for-mending.patch rename to patches/todo/server/0231-Shift-right-click-to-use-exp-for-mending.patch diff --git a/patches/server/0232-Dolphins-naturally-aggressive-to-players-chance.patch b/patches/todo/server/0232-Dolphins-naturally-aggressive-to-players-chance.patch similarity index 100% rename from patches/server/0232-Dolphins-naturally-aggressive-to-players-chance.patch rename to patches/todo/server/0232-Dolphins-naturally-aggressive-to-players-chance.patch diff --git a/patches/server/0233-Cows-naturally-aggressive-to-players-chance.patch b/patches/todo/server/0233-Cows-naturally-aggressive-to-players-chance.patch similarity index 100% rename from patches/server/0233-Cows-naturally-aggressive-to-players-chance.patch rename to patches/todo/server/0233-Cows-naturally-aggressive-to-players-chance.patch diff --git a/patches/server/0234-Option-for-beds-to-explode-on-villager-sleep.patch b/patches/todo/server/0234-Option-for-beds-to-explode-on-villager-sleep.patch similarity index 100% rename from patches/server/0234-Option-for-beds-to-explode-on-villager-sleep.patch rename to patches/todo/server/0234-Option-for-beds-to-explode-on-villager-sleep.patch diff --git a/patches/server/0235-Halloween-options-and-optimizations.patch b/patches/todo/server/0235-Halloween-options-and-optimizations.patch similarity index 100% rename from patches/server/0235-Halloween-options-and-optimizations.patch rename to patches/todo/server/0235-Halloween-options-and-optimizations.patch diff --git a/patches/server/0236-Config-for-grindstones.patch b/patches/todo/server/0236-Config-for-grindstones.patch similarity index 100% rename from patches/server/0236-Config-for-grindstones.patch rename to patches/todo/server/0236-Config-for-grindstones.patch diff --git a/patches/server/0237-UPnP-Port-Forwarding.patch b/patches/todo/server/0237-UPnP-Port-Forwarding.patch similarity index 100% rename from patches/server/0237-UPnP-Port-Forwarding.patch rename to patches/todo/server/0237-UPnP-Port-Forwarding.patch diff --git a/patches/server/0238-Campfire-option-for-lit-when-placed.patch b/patches/todo/server/0238-Campfire-option-for-lit-when-placed.patch similarity index 100% rename from patches/server/0238-Campfire-option-for-lit-when-placed.patch rename to patches/todo/server/0238-Campfire-option-for-lit-when-placed.patch diff --git a/patches/server/0239-options-to-extinguish-fire-blocks-with-snowballs.patch b/patches/todo/server/0239-options-to-extinguish-fire-blocks-with-snowballs.patch similarity index 100% rename from patches/server/0239-options-to-extinguish-fire-blocks-with-snowballs.patch rename to patches/todo/server/0239-options-to-extinguish-fire-blocks-with-snowballs.patch diff --git a/patches/server/0240-Add-option-to-disable-zombie-villagers-cure.patch b/patches/todo/server/0240-Add-option-to-disable-zombie-villagers-cure.patch similarity index 100% rename from patches/server/0240-Add-option-to-disable-zombie-villagers-cure.patch rename to patches/todo/server/0240-Add-option-to-disable-zombie-villagers-cure.patch diff --git a/patches/server/0241-Persistent-BlockEntity-Lore-and-DisplayName.patch b/patches/todo/server/0241-Persistent-BlockEntity-Lore-and-DisplayName.patch similarity index 100% rename from patches/server/0241-Persistent-BlockEntity-Lore-and-DisplayName.patch rename to patches/todo/server/0241-Persistent-BlockEntity-Lore-and-DisplayName.patch diff --git a/patches/server/0242-Signs-allow-color-codes.patch b/patches/todo/server/0242-Signs-allow-color-codes.patch similarity index 100% rename from patches/server/0242-Signs-allow-color-codes.patch rename to patches/todo/server/0242-Signs-allow-color-codes.patch diff --git a/patches/server/0243-Kelp-weeping-and-twisting-vines-configurable-max-gro.patch b/patches/todo/server/0243-Kelp-weeping-and-twisting-vines-configurable-max-gro.patch similarity index 100% rename from patches/server/0243-Kelp-weeping-and-twisting-vines-configurable-max-gro.patch rename to patches/todo/server/0243-Kelp-weeping-and-twisting-vines-configurable-max-gro.patch diff --git a/patches/server/0244-Mobs-always-drop-experience.patch b/patches/todo/server/0244-Mobs-always-drop-experience.patch similarity index 100% rename from patches/server/0244-Mobs-always-drop-experience.patch rename to patches/todo/server/0244-Mobs-always-drop-experience.patch diff --git a/patches/server/0245-Potion-NamespacedKey.patch b/patches/todo/server/0245-Potion-NamespacedKey.patch similarity index 100% rename from patches/server/0245-Potion-NamespacedKey.patch rename to patches/todo/server/0245-Potion-NamespacedKey.patch diff --git a/patches/server/0246-Grindstone-API.patch b/patches/todo/server/0246-Grindstone-API.patch similarity index 100% rename from patches/server/0246-Grindstone-API.patch rename to patches/todo/server/0246-Grindstone-API.patch diff --git a/patches/server/0247-Option-to-prevent-spiders-from-climbing-world-border.patch b/patches/todo/server/0247-Option-to-prevent-spiders-from-climbing-world-border.patch similarity index 100% rename from patches/server/0247-Option-to-prevent-spiders-from-climbing-world-border.patch rename to patches/todo/server/0247-Option-to-prevent-spiders-from-climbing-world-border.patch diff --git a/patches/server/0248-Ability-for-hoe-to-replant-crops-and-nether-warts.patch b/patches/todo/server/0248-Ability-for-hoe-to-replant-crops-and-nether-warts.patch similarity index 100% rename from patches/server/0248-Ability-for-hoe-to-replant-crops-and-nether-warts.patch rename to patches/todo/server/0248-Ability-for-hoe-to-replant-crops-and-nether-warts.patch diff --git a/patches/server/0249-Shearing-jeb-produces-random-color-wool.patch b/patches/todo/server/0249-Shearing-jeb-produces-random-color-wool.patch similarity index 100% rename from patches/server/0249-Shearing-jeb-produces-random-color-wool.patch rename to patches/todo/server/0249-Shearing-jeb-produces-random-color-wool.patch diff --git a/patches/server/0250-Turtle-eggs-random-tick-crack-chance.patch b/patches/todo/server/0250-Turtle-eggs-random-tick-crack-chance.patch similarity index 100% rename from patches/server/0250-Turtle-eggs-random-tick-crack-chance.patch rename to patches/todo/server/0250-Turtle-eggs-random-tick-crack-chance.patch diff --git a/patches/server/0251-Mob-head-visibility-percent.patch b/patches/todo/server/0251-Mob-head-visibility-percent.patch similarity index 100% rename from patches/server/0251-Mob-head-visibility-percent.patch rename to patches/todo/server/0251-Mob-head-visibility-percent.patch diff --git a/patches/server/0252-Configurable-valid-characters-for-usernames.patch b/patches/todo/server/0252-Configurable-valid-characters-for-usernames.patch similarity index 100% rename from patches/server/0252-Configurable-valid-characters-for-usernames.patch rename to patches/todo/server/0252-Configurable-valid-characters-for-usernames.patch diff --git a/patches/server/0253-Shears-can-have-looting-enchantment.patch b/patches/todo/server/0253-Shears-can-have-looting-enchantment.patch similarity index 100% rename from patches/server/0253-Shears-can-have-looting-enchantment.patch rename to patches/todo/server/0253-Shears-can-have-looting-enchantment.patch diff --git a/patches/server/0254-Stop-bees-from-dying-after-stinging.patch b/patches/todo/server/0254-Stop-bees-from-dying-after-stinging.patch similarity index 100% rename from patches/server/0254-Stop-bees-from-dying-after-stinging.patch rename to patches/todo/server/0254-Stop-bees-from-dying-after-stinging.patch diff --git a/patches/server/0255-Give-bee-counts-in-beehives-to-Purpur-clients.patch b/patches/todo/server/0255-Give-bee-counts-in-beehives-to-Purpur-clients.patch similarity index 100% rename from patches/server/0255-Give-bee-counts-in-beehives-to-Purpur-clients.patch rename to patches/todo/server/0255-Give-bee-counts-in-beehives-to-Purpur-clients.patch diff --git a/patches/server/0256-Configurable-farmland-trample-height.patch b/patches/todo/server/0256-Configurable-farmland-trample-height.patch similarity index 100% rename from patches/server/0256-Configurable-farmland-trample-height.patch rename to patches/todo/server/0256-Configurable-farmland-trample-height.patch diff --git a/patches/server/0257-Configurable-player-pickup-exp-delay.patch b/patches/todo/server/0257-Configurable-player-pickup-exp-delay.patch similarity index 100% rename from patches/server/0257-Configurable-player-pickup-exp-delay.patch rename to patches/todo/server/0257-Configurable-player-pickup-exp-delay.patch diff --git a/patches/server/0258-Allow-void-trading.patch b/patches/todo/server/0258-Allow-void-trading.patch similarity index 100% rename from patches/server/0258-Allow-void-trading.patch rename to patches/todo/server/0258-Allow-void-trading.patch diff --git a/patches/server/0259-Dont-eat-blocks-in-non-ticking-chunks.patch b/patches/todo/server/0259-Dont-eat-blocks-in-non-ticking-chunks.patch similarity index 100% rename from patches/server/0259-Dont-eat-blocks-in-non-ticking-chunks.patch rename to patches/todo/server/0259-Dont-eat-blocks-in-non-ticking-chunks.patch diff --git a/patches/server/0260-Configurable-phantom-size.patch b/patches/todo/server/0260-Configurable-phantom-size.patch similarity index 100% rename from patches/server/0260-Configurable-phantom-size.patch rename to patches/todo/server/0260-Configurable-phantom-size.patch diff --git a/patches/server/0261-Configurable-food-attributes.patch b/patches/todo/server/0261-Configurable-food-attributes.patch similarity index 100% rename from patches/server/0261-Configurable-food-attributes.patch rename to patches/todo/server/0261-Configurable-food-attributes.patch diff --git a/patches/server/0262-Max-joins-per-second.patch b/patches/todo/server/0262-Max-joins-per-second.patch similarity index 100% rename from patches/server/0262-Max-joins-per-second.patch rename to patches/todo/server/0262-Max-joins-per-second.patch diff --git a/patches/server/0263-Configurable-minimum-demand-for-trades.patch b/patches/todo/server/0263-Configurable-minimum-demand-for-trades.patch similarity index 100% rename from patches/server/0263-Configurable-minimum-demand-for-trades.patch rename to patches/todo/server/0263-Configurable-minimum-demand-for-trades.patch diff --git a/patches/server/0264-Lobotomize-stuck-villagers.patch b/patches/todo/server/0264-Lobotomize-stuck-villagers.patch similarity index 100% rename from patches/server/0264-Lobotomize-stuck-villagers.patch rename to patches/todo/server/0264-Lobotomize-stuck-villagers.patch diff --git a/patches/server/0265-Option-for-villager-display-trade-item.patch b/patches/todo/server/0265-Option-for-villager-display-trade-item.patch similarity index 100% rename from patches/server/0265-Option-for-villager-display-trade-item.patch rename to patches/todo/server/0265-Option-for-villager-display-trade-item.patch diff --git a/patches/server/0266-Fill-command-max-area-option.patch b/patches/todo/server/0266-Fill-command-max-area-option.patch similarity index 100% rename from patches/server/0266-Fill-command-max-area-option.patch rename to patches/todo/server/0266-Fill-command-max-area-option.patch diff --git a/patches/server/0267-MC-238526-Fix-spawner-not-spawning-water-animals-cor.patch b/patches/todo/server/0267-MC-238526-Fix-spawner-not-spawning-water-animals-cor.patch similarity index 100% rename from patches/server/0267-MC-238526-Fix-spawner-not-spawning-water-animals-cor.patch rename to patches/todo/server/0267-MC-238526-Fix-spawner-not-spawning-water-animals-cor.patch diff --git a/patches/server/0268-Config-for-mob-last-hurt-by-player-time.patch b/patches/todo/server/0268-Config-for-mob-last-hurt-by-player-time.patch similarity index 100% rename from patches/server/0268-Config-for-mob-last-hurt-by-player-time.patch rename to patches/todo/server/0268-Config-for-mob-last-hurt-by-player-time.patch diff --git a/patches/server/0269-Anvil-repair-damage-options.patch b/patches/todo/server/0269-Anvil-repair-damage-options.patch similarity index 100% rename from patches/server/0269-Anvil-repair-damage-options.patch rename to patches/todo/server/0269-Anvil-repair-damage-options.patch diff --git a/patches/server/0270-Fix-legacy-colors-in-console.patch b/patches/todo/server/0270-Fix-legacy-colors-in-console.patch similarity index 100% rename from patches/server/0270-Fix-legacy-colors-in-console.patch rename to patches/todo/server/0270-Fix-legacy-colors-in-console.patch diff --git a/patches/server/0271-Option-to-disable-turtle-egg-trampling-with-feather-.patch b/patches/todo/server/0271-Option-to-disable-turtle-egg-trampling-with-feather-.patch similarity index 100% rename from patches/server/0271-Option-to-disable-turtle-egg-trampling-with-feather-.patch rename to patches/todo/server/0271-Option-to-disable-turtle-egg-trampling-with-feather-.patch diff --git a/patches/server/0272-Add-toggle-for-enchant-level-clamping.patch b/patches/todo/server/0272-Add-toggle-for-enchant-level-clamping.patch similarity index 100% rename from patches/server/0272-Add-toggle-for-enchant-level-clamping.patch rename to patches/todo/server/0272-Add-toggle-for-enchant-level-clamping.patch diff --git a/patches/server/0273-Config-to-prevent-horses-from-standing-when-hurt.patch b/patches/todo/server/0273-Config-to-prevent-horses-from-standing-when-hurt.patch similarity index 100% rename from patches/server/0273-Config-to-prevent-horses-from-standing-when-hurt.patch rename to patches/todo/server/0273-Config-to-prevent-horses-from-standing-when-hurt.patch diff --git a/patches/server/0274-Drop-incompatible-tests.patch b/patches/todo/server/0274-Drop-incompatible-tests.patch similarity index 100% rename from patches/server/0274-Drop-incompatible-tests.patch rename to patches/todo/server/0274-Drop-incompatible-tests.patch