From 63e37b3633821788a0a2a7d6029032511f8c8417 Mon Sep 17 00:00:00 2001 From: AlphaKR93 Date: Tue, 14 May 2024 23:12:26 +0900 Subject: [PATCH] Add back Pufferfish patch --- .../tasks/CheckUpstreamCommitTasks.kt | 2 +- gradle.properties | 7 +- patches/api/0001-Pufferfish-API-Changes.patch | 512 ++ ...es.patch => 0002-Purpur-API-Changes.patch} | 36 +- ...=> 0003-Use-Gradle-Version-Catalogs.patch} | 7 +- ...{0003-Rebrand.patch => 0004-Rebrand.patch} | 0 ...patch => 0005-Plazma-Configurations.patch} | 0 ...ks.patch => 0006-Various-API-Tweaks.patch} | 0 .../0001-Pufferfish-Server-Changes.patch | 4237 +++++++++++++++++ ...patch => 0002-Purpur-Server-Changes.patch} | 827 ++-- ...=> 0003-Use-Gradle-Version-Catalogs.patch} | 57 +- ...{0003-Rebrand.patch => 0004-Rebrand.patch} | 29 +- ...and-Logo.patch => 0005-Rebrand-Logo.patch} | 0 ...patch => 0006-Plazma-Configurations.patch} | 51 +- ...-Setup-basic-configuration-sections.patch} | 0 ...artup.patch => 0008-Warn-on-startup.patch} | 0 ...ild.patch => 0009-Development-Build.patch} | 2 +- ...h => 0010-Port-SparklyPaper-patches.patch} | 60 +- ...ways-agree-EULA-on-development-mode.patch} | 2 +- ...rics.patch => 0012-Add-more-metrics.patch} | 2 +- ...013-Optimize-default-configurations.patch} | 18 +- ...patch => 0014-Tweak-console-logging.patch} | 6 +- ...issing-purpur-configuration-options.patch} | 12 +- ...6-Completely-remove-Mojang-Profiler.patch} | 48 +- ...option-to-change-nether-portal-size.patch} | 2 +- ... 0018-Reduce-create-random-instance.patch} | 4 +- ...=> 0019-Apply-various-optimizations.patch} | 6 +- ...ootTable-for-non-player-interaction.patch} | 2 +- ...Do-not-load-chunks-to-spawn-phantom.patch} | 2 +- ...able-moved-to-quickly-check-for-spe.patch} | 4 +- ... 0023-Ignore-useless-entity-packets.patch} | 6 +- ...024-Configurable-entity-sensor-tick.patch} | 8 +- ...0025-Variable-entity-wakeup-duration.patch | 6 +- ...uppress-errors-from-dirty-attributes.patch | 6 +- .../0031-Improve-SwingTime-ticking.patch | 4 +- patches/server/0033-Add-more-MSPT.patch | 6 +- ...4-Process-pathfinding-asynchronously.patch | 12 +- ...-when-running-the-test-server-with-g.patch | 4 +- ...instead-if-server-favicon-doesn-t-ex.patch | 4 +- .../server/0037-Implement-FreedomChat.patch | 6 +- ...024-Configurable-cave-lava-sea-level.patch | 0 41 files changed, 5472 insertions(+), 525 deletions(-) create mode 100644 patches/api/0001-Pufferfish-API-Changes.patch rename patches/api/{0001-Purpur-API-Changes.patch => 0002-Purpur-API-Changes.patch} (98%) rename patches/api/{0002-Use-Gradle-Version-Catalogs.patch => 0003-Use-Gradle-Version-Catalogs.patch} (96%) rename patches/api/{0003-Rebrand.patch => 0004-Rebrand.patch} (100%) rename patches/api/{0004-Plazma-Configurations.patch => 0005-Plazma-Configurations.patch} (100%) rename patches/api/{0005-Various-API-Tweaks.patch => 0006-Various-API-Tweaks.patch} (100%) create mode 100644 patches/server/0001-Pufferfish-Server-Changes.patch rename patches/server/{0001-Purpur-Server-Changes.patch => 0002-Purpur-Server-Changes.patch} (97%) rename patches/server/{0002-Use-Gradle-Version-Catalogs.patch => 0003-Use-Gradle-Version-Catalogs.patch} (86%) rename patches/server/{0003-Rebrand.patch => 0004-Rebrand.patch} (98%) rename patches/server/{0004-Rebrand-Logo.patch => 0005-Rebrand-Logo.patch} (100%) rename patches/server/{0005-Plazma-Configurations.patch => 0006-Plazma-Configurations.patch} (95%) rename patches/server/{0006-Setup-basic-configuration-sections.patch => 0007-Setup-basic-configuration-sections.patch} (100%) rename patches/server/{0007-Warn-on-startup.patch => 0008-Warn-on-startup.patch} (100%) rename patches/server/{0008-Development-Build.patch => 0009-Development-Build.patch} (92%) rename patches/server/{0009-Port-SparklyPaper-patches.patch => 0010-Port-SparklyPaper-patches.patch} (95%) rename patches/server/{0010-Always-agree-EULA-on-development-mode.patch => 0011-Always-agree-EULA-on-development-mode.patch} (89%) rename patches/server/{0011-Add-more-metrics.patch => 0012-Add-more-metrics.patch} (98%) rename patches/server/{0012-Optimize-default-configurations.patch => 0013-Optimize-default-configurations.patch} (98%) rename patches/server/{0013-Tweak-console-logging.patch => 0014-Tweak-console-logging.patch} (96%) rename patches/server/{0014-Add-missing-purpur-configuration-options.patch => 0015-Add-missing-purpur-configuration-options.patch} (96%) rename patches/server/{0015-Completely-remove-Mojang-Profiler.patch => 0016-Completely-remove-Mojang-Profiler.patch} (97%) rename patches/server/{0016-Add-option-to-change-nether-portal-size.patch => 0017-Add-option-to-change-nether-portal-size.patch} (97%) rename patches/server/{0017-Reduce-create-random-instance.patch => 0018-Reduce-create-random-instance.patch} (99%) rename patches/server/{0018-Apply-various-optimizations.patch => 0019-Apply-various-optimizations.patch} (87%) rename patches/server/{0019-Do-not-refresh-LootTable-for-non-player-interaction.patch => 0020-Do-not-refresh-LootTable-for-non-player-interaction.patch} (95%) rename patches/server/{0020-Do-not-load-chunks-to-spawn-phantom.patch => 0021-Do-not-load-chunks-to-spawn-phantom.patch} (95%) rename patches/server/{0021-Add-option-to-disable-moved-to-quickly-check-for-spe.patch => 0022-Add-option-to-disable-moved-to-quickly-check-for-spe.patch} (92%) rename patches/server/{0022-Ignore-useless-entity-packets.patch => 0023-Ignore-useless-entity-packets.patch} (93%) rename patches/server/{0023-Configurable-entity-sensor-tick.patch => 0024-Configurable-entity-sensor-tick.patch} (81%) rename patches/{ => unapplied}/server/0024-Configurable-cave-lava-sea-level.patch (100%) diff --git a/buildSrc/src/main/kotlin/org/plazmamc/alwaysuptodate/tasks/CheckUpstreamCommitTasks.kt b/buildSrc/src/main/kotlin/org/plazmamc/alwaysuptodate/tasks/CheckUpstreamCommitTasks.kt index 1690d7bee..3470647b8 100644 --- a/buildSrc/src/main/kotlin/org/plazmamc/alwaysuptodate/tasks/CheckUpstreamCommitTasks.kt +++ b/buildSrc/src/main/kotlin/org/plazmamc/alwaysuptodate/tasks/CheckUpstreamCommitTasks.kt @@ -46,7 +46,7 @@ fun Project.getLatest(repository: String, branch: String) : String { val regex = "[a-z0-9]{40}\trefs/heads/$branch".toRegex() val temp = Git(project.pathIO)("ls-remote", repository).readText() - return temp?.lines()?.first { regex.matches(it) }?.split("\t")?.first() + return temp?.lines()?.first(regex::matches)?.split("\t")?.first() ?: throw AlwaysUpToDateException("Failed to get latest commit of $repository") } diff --git a/gradle.properties b/gradle.properties index 26fb1d672..7495d3c37 100644 --- a/gradle.properties +++ b/gradle.properties @@ -17,10 +17,11 @@ jdkVersion = 21 paperRepo = https://github.com/PaperMC/Paper paperBranch = master -purpurRepo = https://github.com/PurpurMC/Purpur +purpurRepo = https://github.com/PlazmaMC/Purpur purpurBranch = ver/1.20.6 +pufferfishRepo = https://github.com/PlazmaMC/Pufferfish pufferfishBranch = ver/1.20 -usePufferfish = false +usePufferfish = true paperCommit = 3fc93581bb876e8149b2ca423375a98f5ca12d27 -purpurCommit = c637b74bade41e7be96ed3478bab7c3b923e7d84 +purpurCommit = 24b161b1be559e7f84d881f9cabe2830d693e621 diff --git a/patches/api/0001-Pufferfish-API-Changes.patch b/patches/api/0001-Pufferfish-API-Changes.patch new file mode 100644 index 000000000..7854ab516 --- /dev/null +++ b/patches/api/0001-Pufferfish-API-Changes.patch @@ -0,0 +1,512 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: AlphaKR93 +Date: Tue, 14 May 2024 19:21:24 +0900 +Subject: [PATCH] Pufferfish API Changes + + +diff --git a/build.gradle.kts b/build.gradle.kts +index fd39ed209b20c927054b8482c400beeeeab460a3..1336685fde70c4a88a1fb591cdfcd781a8f06d0b 100644 +--- a/build.gradle.kts ++++ b/build.gradle.kts +@@ -52,6 +52,7 @@ dependencies { + apiAndDocs("net.kyori:adventure-text-logger-slf4j") + api("org.apache.logging.log4j:log4j-api:$log4jVersion") + api("org.slf4j:slf4j-api:$slf4jVersion") ++ api("io.sentry:sentry:5.4.0") // Pufferfish + + implementation("org.ow2.asm:asm:9.7") + implementation("org.ow2.asm:asm-commons:9.7") +@@ -130,6 +131,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..ab5fea0b03224bf249352ce340e94704ff713345 +--- /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 && SIMDDetection.getJavaVersion() != 19) { ++ 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 c80faa079eca1564847070f0338fc98024639829..e632d51d3487eb4807243b6705999ad124466bf5 100644 +--- a/src/main/java/org/bukkit/map/MapPalette.java ++++ b/src/main/java/org/bukkit/map/MapPalette.java +@@ -1,6 +1,7 @@ + package org.bukkit.map; + + import com.google.common.base.Preconditions; ++import gg.pufferfish.pufferfish.simd.SIMDDetection; // Pufferfish + import java.awt.Color; + import java.awt.Graphics2D; + import java.awt.Image; +@@ -40,7 +41,7 @@ public final class MapPalette { + } + + @NotNull +- static final Color[] colors = { ++ public static final Color[] colors = { // Pufferfish - public access + c(0, 0, 0, 0), c(0, 0, 0, 0), c(0, 0, 0, 0), c(0, 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), +@@ -211,9 +212,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 07a9c9e254188c251165ca84c8e961fccda01175..5dc64d8c9aeae612fd31af0673f3530a9e777dfc 100644 +--- a/src/main/java/org/bukkit/plugin/SimplePluginManager.java ++++ b/src/main/java/org/bukkit/plugin/SimplePluginManager.java +@@ -596,7 +596,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 com.destroystokyo.paper.event.server.ServerExceptionEvent(new com.destroystokyo.paper.exception.ServerPluginEnableDisableException(msg, ex, plugin))); + } + // Paper end +@@ -666,9 +668,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 com.destroystokyo.paper.event.server.ServerExceptionEvent)) { // We don't want to cause an endless event loop + callEvent(new com.destroystokyo.paper.event.server.ServerExceptionEvent(new com.destroystokyo.paper.exception.ServerEventException(msg, ex, registration.getPlugin(), registration.getListener(), event))); + } +diff --git a/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java b/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java +index eaefbb00e9993d54906cc8cf35cf753c0d6c7707..301e82369603f3dd6e6c1bd380da4bacacd7ef6c 100644 +--- a/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java ++++ b/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java +@@ -336,7 +336,13 @@ 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; ++ // Paper end + } + + // Perhaps abort here, rather than continue going, but as it stands, +@@ -361,7 +367,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 7e4f7cb2afbc145e532285c793573ad107bc3033..12449e18180d604e9cbbc744da74a8b222a18e1f 100644 +--- a/src/main/java/org/bukkit/plugin/java/PluginClassLoader.java ++++ b/src/main/java/org/bukkit/plugin/java/PluginClassLoader.java +@@ -50,6 +50,8 @@ public final class PluginClassLoader extends URLClassLoader implements io.paperm + private io.papermc.paper.plugin.provider.classloader.PluginClassLoaderGroup classLoaderGroup; // Paper + public io.papermc.paper.plugin.provider.entrypoint.DependencyContext dependencyContext; // Paper + ++ private boolean closed = false; // Pufferfish ++ + static { + ClassLoader.registerAsParallelCapable(); + } +@@ -197,6 +199,7 @@ public final class PluginClassLoader extends URLClassLoader implements io.paperm + 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.")) { +@@ -204,7 +207,7 @@ public final class PluginClassLoader extends URLClassLoader implements io.paperm + } + 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); + +@@ -251,6 +254,7 @@ public final class PluginClassLoader extends URLClassLoader implements io.paperm + this.setClass(name, result); // Paper + } + ++ if (result == null) throw new ClassNotFoundException(name); // Pufferfish + return result; + } + +@@ -265,6 +269,7 @@ public final class PluginClassLoader extends URLClassLoader implements io.paperm + // Paper end + super.close(); + } finally { ++ this.closed = true; // Pufferfish + jar.close(); + } + } diff --git a/patches/api/0001-Purpur-API-Changes.patch b/patches/api/0002-Purpur-API-Changes.patch similarity index 98% rename from patches/api/0001-Purpur-API-Changes.patch rename to patches/api/0002-Purpur-API-Changes.patch index 4c4fed9ba..f0bfbb8e7 100644 --- a/patches/api/0001-Purpur-API-Changes.patch +++ b/patches/api/0002-Purpur-API-Changes.patch @@ -1,6 +1,6 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: granny -Date: Tue, 14 May 2024 17:29:02 +0900 +Date: Tue, 14 May 2024 22:59:42 +0900 Subject: [PATCH] Purpur API Changes PurpurMC @@ -25,10 +25,10 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/build.gradle.kts b/build.gradle.kts -index fd39ed209b20c927054b8482c400beeeeab460a3..6aef83558a5ef7e84873b127c3bb43a6468c9a24 100644 +index 1336685fde70c4a88a1fb591cdfcd781a8f06d0b..7aa138f7123dcfb7d1d9ae9efcf2359522f5d2fd 100644 --- a/build.gradle.kts +++ b/build.gradle.kts -@@ -142,6 +142,8 @@ tasks.jar { +@@ -150,6 +150,8 @@ tasks.jar { } tasks.withType { @@ -185,6 +185,19 @@ index a736d7bcdc5861a01b66ba36158db1c716339346..22fc165fd9c95f0f3ae1be7a0857e48c class DummyVersionFetcher implements VersionFetcher { @Override +diff --git a/src/main/java/gg/pufferfish/pufferfish/simd/SIMDChecker.java b/src/main/java/gg/pufferfish/pufferfish/simd/SIMDChecker.java +index ab5fea0b03224bf249352ce340e94704ff713345..3441cdad70da1bd523c5933b1a914688718c2657 100644 +--- a/src/main/java/gg/pufferfish/pufferfish/simd/SIMDChecker.java ++++ b/src/main/java/gg/pufferfish/pufferfish/simd/SIMDChecker.java +@@ -15,7 +15,7 @@ public class SIMDChecker { + @Deprecated + public static boolean canEnable(Logger logger) { + try { +- if (SIMDDetection.getJavaVersion() != 17 && SIMDDetection.getJavaVersion() != 18 && SIMDDetection.getJavaVersion() != 19) { ++ if (SIMDDetection.getJavaVersion() < 17 || SIMDDetection.getJavaVersion() > 21) { + return false; + } else { + SIMDDetection.testRun = true; diff --git a/src/main/java/org/bukkit/Bukkit.java b/src/main/java/org/bukkit/Bukkit.java index 5d1b55fdbcbe63f6b42b694d05211a3cc691a09d..70ef8f63ab79e102cb4326c21cc344488f4fbdd3 100644 --- a/src/main/java/org/bukkit/Bukkit.java @@ -2113,7 +2126,7 @@ index cd3296fea01648592d2af89b3d80135acb6d0958..45797a6fbae1d8edc4211cb30def24ad permissions.put(lname, new PermissionAttachmentInfo(parent, lname, attachment, value)); diff --git a/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java b/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java -index eaefbb00e9993d54906cc8cf35cf753c0d6c7707..f1e58639213be0c43cd2ff090b625e7d0a67e8be 100644 +index 301e82369603f3dd6e6c1bd380da4bacacd7ef6c..0c6ca7588fb3d6b6497ddf032fe75e5c6c9719e5 100644 --- a/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java +++ b/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java @@ -55,6 +55,7 @@ public final class JavaPluginLoader implements PluginLoader { @@ -3753,3 +3766,18 @@ index 12946bd55fcf7c40d39081779a7fa30049ee6165..9c2d605c50cbf9aefa56ec209df9f6ce + public void stopTiming() { /*handler.stopTiming();*/ } // Purpur } +diff --git a/src/test/java/org/bukkit/AnnotationTest.java b/src/test/java/org/bukkit/AnnotationTest.java +index 88f1ca89fa640a686231b8eec87e70419b2d73ef..d6b91c49a267c89d7df2ddee7ccfe64675d117be 100644 +--- a/src/test/java/org/bukkit/AnnotationTest.java ++++ b/src/test/java/org/bukkit/AnnotationTest.java +@@ -47,6 +47,10 @@ public class AnnotationTest { + "org/bukkit/plugin/java/PluginClassLoader", + // Generic functional interface + "org/bukkit/util/Consumer", ++ // Purpur start ++ "gg/pufferfish/pufferfish/sentry/SentryContext", ++ "gg/pufferfish/pufferfish/sentry/SentryContext$State", ++ // Purpur end + // Paper start + "io/papermc/paper/util/TransformingRandomAccessList", + "io/papermc/paper/util/TransformingRandomAccessList$TransformedListIterator", diff --git a/patches/api/0002-Use-Gradle-Version-Catalogs.patch b/patches/api/0003-Use-Gradle-Version-Catalogs.patch similarity index 96% rename from patches/api/0002-Use-Gradle-Version-Catalogs.patch rename to patches/api/0003-Use-Gradle-Version-Catalogs.patch index 41df53d08..466a34101 100644 --- a/patches/api/0002-Use-Gradle-Version-Catalogs.patch +++ b/patches/api/0003-Use-Gradle-Version-Catalogs.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Use Gradle Version Catalogs diff --git a/build.gradle.kts b/build.gradle.kts -index 6aef83558a5ef7e84873b127c3bb43a6468c9a24..dedd9703559dbff43adb4b0aee6765789f130d50 100644 +index 7aa138f7123dcfb7d1d9ae9efcf2359522f5d2fd..67324689a65e8ed65c9d27c9b50073488c235c51 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -9,11 +9,13 @@ java { @@ -22,7 +22,7 @@ index 6aef83558a5ef7e84873b127c3bb43a6468c9a24..dedd9703559dbff43adb4b0aee676578 val apiAndDocs: Configuration by configurations.creating { attributes { attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category.DOCUMENTATION)) -@@ -27,55 +29,31 @@ configurations.api { +@@ -27,56 +29,31 @@ configurations.api { } dependencies { @@ -51,6 +51,7 @@ index 6aef83558a5ef7e84873b127c3bb43a6468c9a24..dedd9703559dbff43adb4b0aee676578 - apiAndDocs("net.kyori:adventure-text-logger-slf4j") - api("org.apache.logging.log4j:log4j-api:$log4jVersion") - api("org.slf4j:slf4j-api:$slf4jVersion") +- api("io.sentry:sentry:5.4.0") // Pufferfish - - implementation("org.ow2.asm:asm:9.7") - implementation("org.ow2.asm:asm-commons:9.7") @@ -103,7 +104,7 @@ index 6aef83558a5ef7e84873b127c3bb43a6468c9a24..dedd9703559dbff43adb4b0aee676578 } // Paper start -@@ -149,25 +127,16 @@ tasks.withType { +@@ -157,25 +134,16 @@ tasks.withType { options.use() options.isDocFilesSubDirs = true options.links( diff --git a/patches/api/0003-Rebrand.patch b/patches/api/0004-Rebrand.patch similarity index 100% rename from patches/api/0003-Rebrand.patch rename to patches/api/0004-Rebrand.patch diff --git a/patches/api/0004-Plazma-Configurations.patch b/patches/api/0005-Plazma-Configurations.patch similarity index 100% rename from patches/api/0004-Plazma-Configurations.patch rename to patches/api/0005-Plazma-Configurations.patch diff --git a/patches/api/0005-Various-API-Tweaks.patch b/patches/api/0006-Various-API-Tweaks.patch similarity index 100% rename from patches/api/0005-Various-API-Tweaks.patch rename to patches/api/0006-Various-API-Tweaks.patch diff --git a/patches/server/0001-Pufferfish-Server-Changes.patch b/patches/server/0001-Pufferfish-Server-Changes.patch new file mode 100644 index 000000000..562699c82 --- /dev/null +++ b/patches/server/0001-Pufferfish-Server-Changes.patch @@ -0,0 +1,4237 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Kevin Raneri +Date: Tue, 14 May 2024 22:59:40 +0900 +Subject: [PATCH] Pufferfish Server Changes + +Pufferfish +Copyright (C) 2024 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 87bb3fd9b97506f61734ae7f2e6860610ba794e7..00b31bbf6a5832799eae65e5be67cc6e92c69a56 100644 +--- a/build.gradle.kts ++++ b/build.gradle.kts +@@ -12,7 +12,7 @@ configurations.named(log4jPlugins.compileClasspathConfigurationName) { + val alsoShade: Configuration by configurations.creating + + dependencies { +- implementation(project(":paper-api")) ++ implementation(project(":pufferfish-api")) // Pufferfish // Paper + // Paper start + implementation("org.jline:jline-terminal-jansi:3.21.0") + implementation("net.minecrell:terminalconsoleappender:1.3.0") +@@ -46,6 +46,14 @@ dependencies { + runtimeOnly("org.apache.maven.resolver:maven-resolver-connector-basic:1.9.18") + runtimeOnly("org.apache.maven.resolver:maven-resolver-transport-http:1.9.18") + ++ // Pufferfish start ++ implementation("org.yaml:snakeyaml:1.32") ++ implementation ("me.carleslc.Simple-YAML:Simple-Yaml:1.8.4") { ++ 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("org.junit.jupiter:junit-jupiter:5.10.2") + testImplementation("org.hamcrest:hamcrest:2.2") +@@ -66,6 +74,13 @@ paperweight { + craftBukkitPackageVersion.set("v1_20_R4") // also needs to be updated in MappingEnvironment + } + ++// Pufferfish Start ++tasks.withType { ++ val compilerArgs = options.compilerArgs ++ compilerArgs.add("--add-modules=jdk.incubator.vector") ++} ++// Pufferfish End ++ + tasks.jar { + archiveClassifier.set("dev") + +@@ -78,7 +93,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 7620c72a4c243cbeea245203ce03a97cbfa7d922..b35a9f4c5f8960864c402ede8a51fb5ab9c4fcc0 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 4b002e8b75d117b726b0de274a76d3596fce015b..692c962193cf9fcc6801fc93f3220bdc673d527b 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(); +@@ -607,11 +607,11 @@ public class Metrics { + final String implVersion = org.bukkit.craftbukkit.Main.class.getPackage().getImplementationVersion(); + if (implVersion != null) { + final String buildOrHash = implVersion.substring(implVersion.lastIndexOf('-') + 1); +- paperVersion = "git-Paper-%s-%s".formatted(Bukkit.getServer().getMinecraftVersion(), buildOrHash); ++ paperVersion = "git-Pufferfish-%s-%s".formatted(Bukkit.getServer().getMinecraftVersion(), buildOrHash); // Pufferfish + } else { + paperVersion = "unknown"; + } +- metrics.addCustomChart(new Metrics.SimplePie("paper_version", () -> paperVersion)); ++ metrics.addCustomChart(new Metrics.SimplePie("pufferfish_version", () -> paperVersion)); // Pufferfish + + metrics.addCustomChart(new Metrics.DrilldownPie("java_version", () -> { + Map> map = new HashMap<>(); +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 41b9405d6759d865e0d14dd4f95163e9690e967d..091b1ae822e1c0517e59572e7a9bda11e998c0ee 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..020368da69b9a492155f6de6297f74732f4ab6ea +--- /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..1ca25529245ed369b8705075a8c2e32fa4d9b1d1 +--- /dev/null ++++ b/src/main/java/gg/pufferfish/pufferfish/PufferfishConfig.java +@@ -0,0 +1,334 @@ ++package gg.pufferfish.pufferfish; ++ ++import gg.pufferfish.pufferfish.simd.SIMDDetection; ++import java.io.File; ++import java.io.IOException; ++import java.util.Collections; ++import net.minecraft.core.registries.BuiltInRegistries; ++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.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 && SIMDDetection.getJavaVersion() != 19; ++ } catch (NoClassDefFoundError | Exception ignored) { ++ ignored.printStackTrace(); ++ } ++ ++ 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, Java 18, and Java 19."); ++ } 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 tpsCatchup; ++ private static void tpsCatchup() { ++ tpsCatchup = getBoolean("tps-catchup", true, ++ "If this setting is true, the server will run faster after a lag spike in", ++ "an attempt to maintain 20 TPS. This option (defaults to true per", ++ "spigot/paper) can cause mobs to move fast after a lag spike."); ++ } ++ ++ 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 : BuiltInRegistries.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 : BuiltInRegistries.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() { ++ throttleInactiveGoalSelectorTick = 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 boolean allowEndCrystalRespawn; ++ private static void allowEndCrystalRespawn() { ++ allowEndCrystalRespawn = getBoolean("allow-end-crystal-respawn", true, ++ "Allows end crystals to respawn the ender dragon.", ++ "On servers that expect end crystal fights in the end dimension, disabling this", ++ "will prevent the server from performing an expensive search to attempt respawning", ++ "the ender dragon whenever a player places an end crystal."); ++ } ++ ++ 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; ++ public static boolean disableOutOfOrderChat; ++ private static void miscSettings() { ++ disableMethodProfiler = getBoolean("misc.disable-method-profiler", true); ++ disableOutOfOrderChat = getBoolean("misc.disable-out-of-order-chat", false); ++ 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..893d8c0946ef71a0561221dd76bffff0dc940d56 +--- /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.20/lastSuccessfulBuild/buildNumber"); ++ private static final String GITHUB_FORMAT = "https://api.github.com/repos/pufferfish-gg/Pufferfish/compare/ver/1.20...%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..4ad189d52b27560424ddb311d0817a334637dc95 +--- /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", // TODO: Figure out what to do with this. ++ "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..029d840e28d67d26d3c0dd6785e25dbf15f9226c +--- /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 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() { ++ if (true) return; // This doesn't work, and it's not worth fixing at the moment. ++ int entities = 0; ++ int chunkCount = 0; ++ int tileEntityCount = 0; ++ ++ if (!Bukkit.isStopping()) { ++ for (World world : Bukkit.getWorlds()) { ++ world.getEntityCount(); ++ 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..731ef11c7a025ae95ed8a757b530d834733d0621 +--- /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..1b29210ad0bbb4ada150f23357f0c80d331c996d +--- /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..8e5323d5d9af25c8a85c4b34a6be76cfc54384cf +--- /dev/null ++++ b/src/main/java/gg/pufferfish/pufferfish/util/AsyncExecutor.java +@@ -0,0 +1,73 @@ ++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.Condition; ++import java.util.concurrent.locks.Lock; ++import java.util.concurrent.locks.ReentrantLock; ++import java.util.logging.Level; ++ ++public class AsyncExecutor implements Runnable { ++ ++ private final Queue jobs = Queues.newArrayDeque(); ++ private final Lock mutex = new ReentrantLock(); ++ private final Condition cond = mutex.newCondition(); ++ private final Thread thread; ++ private volatile boolean killswitch = false; ++ ++ public AsyncExecutor(String threadName) { ++ this.thread = new Thread(this, threadName); ++ } ++ ++ public void start() { ++ thread.start(); ++ } ++ ++ public void kill() { ++ killswitch = true; ++ cond.signalAll(); ++ } ++ ++ public void submit(Runnable runnable) { ++ mutex.lock(); ++ try { ++ jobs.offer(runnable); ++ cond.signalAll(); ++ } finally { ++ mutex.unlock(); ++ } ++ } ++ ++ @Override ++ public void run() { ++ while (!killswitch) { ++ try { ++ Runnable runnable = takeRunnable(); ++ if (runnable != null) { ++ runnable.run(); ++ } ++ } catch (InterruptedException e) { ++ Thread.currentThread().interrupt(); ++ } catch (Exception e) { ++ PufferfishLogger.LOGGER.log(Level.SEVERE, e, () -> "Failed to execute async job for thread " + thread.getName()); ++ } ++ } ++ } ++ ++ private Runnable takeRunnable() throws InterruptedException { ++ mutex.lock(); ++ try { ++ while (jobs.isEmpty() && !killswitch) { ++ cond.await(); ++ } ++ ++ if (jobs.isEmpty()) return null; // We've set killswitch ++ ++ return jobs.remove(); ++ } finally { ++ mutex.unlock(); ++ } ++ } ++ ++} +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..fdcb62d12164024a5f354d60cc863821a18d1b2a +--- /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..facd55463d44cb7e3d2ca6892982f5497b8dded1 +--- /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/io/papermc/paper/configuration/GlobalConfiguration.java b/src/main/java/io/papermc/paper/configuration/GlobalConfiguration.java +index 2874bc3001c4e7d9191e47ba512c5a68369c21f1..32035e37b39ba42232fea948166e7c1d4d06190c 100644 +--- a/src/main/java/io/papermc/paper/configuration/GlobalConfiguration.java ++++ b/src/main/java/io/papermc/paper/configuration/GlobalConfiguration.java +@@ -8,6 +8,7 @@ import net.kyori.adventure.text.Component; + import net.kyori.adventure.text.format.NamedTextColor; + import net.minecraft.network.protocol.Packet; + import net.minecraft.network.protocol.game.ServerboundPlaceRecipePacket; ++import org.bukkit.Bukkit; // Pufferfish + import org.checkerframework.checker.nullness.qual.Nullable; + import org.slf4j.Logger; + import org.spongepowered.configurate.objectmapping.ConfigSerializable; +@@ -19,6 +20,7 @@ import org.spongepowered.configurate.objectmapping.meta.Setting; + import java.util.List; + import java.util.Map; + import java.util.Objects; ++import java.util.logging.Level; // Pufferfish + + @SuppressWarnings({"CanBeFinal", "FieldCanBeLocal", "FieldMayBeFinal", "NotNullFieldNotInitialized", "InnerClassMayBeStatic"}) + public class GlobalConfiguration extends ConfigurationPart { +@@ -97,6 +99,7 @@ public class GlobalConfiguration extends ConfigurationPart { + @Deprecated(forRemoval = true) + public class Timings extends ConfigurationPart { + public boolean enabled = true; ++ public boolean reallyEnabled = false; + public boolean verbose = true; + public String url = "https://timings.aikar.co/"; + public boolean serverNamePrivacy = false; +@@ -110,6 +113,14 @@ public class GlobalConfiguration extends ConfigurationPart { + + @PostProcess + private void postProcess() { ++ // Pufferfish start ++ if (enabled && !reallyEnabled) { ++ 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."); ++ } ++ enabled = reallyEnabled; ++ // Pufferfish end + MinecraftTimings.processConfig(this); + } + } +diff --git a/src/main/java/io/papermc/paper/util/MCUtil.java b/src/main/java/io/papermc/paper/util/MCUtil.java +index 1d6b3fe2ce240af4ede61588795456b046eee6c9..cdcb1bff7913bfe86fed008271016a3175b6df90 100644 +--- a/src/main/java/io/papermc/paper/util/MCUtil.java ++++ b/src/main/java/io/papermc/paper/util/MCUtil.java +@@ -215,7 +215,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/network/chat/SignedMessageChain.java b/src/main/java/net/minecraft/network/chat/SignedMessageChain.java +index 300929a406905f5ff1ede664d5b99fb0938d4d2e..01a6b1135420ea659d092ddca499f281d7240054 100644 +--- a/src/main/java/net/minecraft/network/chat/SignedMessageChain.java ++++ b/src/main/java/net/minecraft/network/chat/SignedMessageChain.java +@@ -45,7 +45,7 @@ public class SignedMessageChain { + SignedMessageLink signedMessageLink = SignedMessageChain.this.nextLink; + if (signedMessageLink == null) { + throw new SignedMessageChain.DecodeException(SignedMessageChain.DecodeException.CHAIN_BROKEN); +- } else if (body.timeStamp().isBefore(SignedMessageChain.this.lastTimeStamp)) { ++ } else if (!gg.pufferfish.pufferfish.PufferfishConfig.disableOutOfOrderChat && body.timeStamp().isBefore(SignedMessageChain.this.lastTimeStamp)) { // Pufferfish + this.setChainBroken(); + throw new SignedMessageChain.DecodeException(SignedMessageChain.DecodeException.OUT_OF_ORDER_CHAT, org.bukkit.event.player.PlayerKickEvent.Cause.OUT_OF_ORDER_CHAT); // Paper - kick event causes + } else { +diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java +index 39303bb4e336732db0ab19dee0c1f8b609bbb134..b5b5d467231b571455f82d1943e6a8a46d75c7dc 100644 +--- a/src/main/java/net/minecraft/server/MinecraftServer.java ++++ b/src/main/java/net/minecraft/server/MinecraftServer.java +@@ -319,6 +319,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop S spin(Function serverFactory) { + AtomicReference atomicreference = new AtomicReference(); +@@ -1240,6 +1241,12 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop // Paper + } + + public SystemReport fillSystemReport(SystemReport details) { +@@ -2561,6 +2568,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop needsChangeBroadcasting = new it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet<>(); +- public final com.destroystokyo.paper.util.misc.PlayerAreaMap playerMobSpawnMap = new com.destroystokyo.paper.util.misc.PlayerAreaMap(this.pooledLinkedPlayerHashSets); ++ public final com.destroystokyo.paper.util.misc.PlayerAreaMap playerMobSpawnMap = new gg.pufferfish.pufferfish.util.AsyncPlayerAreaMap(this.pooledLinkedPlayerHashSets); // Pufferfish + // Paper end - optimise chunk tick iteration + + public ChunkMap(ServerLevel world, LevelStorageSource.LevelStorageAccess session, DataFixer dataFixer, StructureTemplateManager structureTemplateManager, Executor executor, BlockableEventLoop mainThreadExecutor, LightChunkGetter chunkProvider, ChunkGenerator chunkGenerator, ChunkProgressListener worldGenerationProgressListener, ChunkStatusUpdateListener chunkStatusChangeListener, Supplier persistentStateManagerFactory, int viewDistance, boolean dsync) { +@@ -1463,8 +1463,28 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider + return ChunkMap.this.level.getServer().getScaledTrackingDistance(initialDistance); + } + ++ private static int getHighestRange(Entity parent, int highest) { ++ List 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()) { +@@ -1476,6 +1496,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 b99f50604bafecbc68835974c9ed0caa91911a40..cadbf2501c87ea22b3ff5db7d8e4aeebfa3d3abc 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]; + // Paper end + ++ public boolean firstRunSpawnCounts = true; // Pufferfish ++ public final java.util.concurrent.atomic.AtomicBoolean _pufferfish_spawnCountsReady = new java.util.concurrent.atomic.AtomicBoolean(false); // Pufferfish - optimize countmobs ++ + public ServerChunkCache(ServerLevel world, LevelStorageSource.LevelStorageAccess session, DataFixer dataFixer, StructureTemplateManager structureTemplateManager, Executor workerExecutor, ChunkGenerator chunkGenerator, int viewDistance, int simulationDistance, boolean dsync, ChunkProgressListener worldGenerationProgressListener, ChunkStatusUpdateListener chunkStatusChangeListener, Supplier persistentStateManagerFactory) { + this.level = world; + this.mainThreadProcessor = new ServerChunkCache.MainThreadExecutor(world); +@@ -511,6 +514,7 @@ public class ServerChunkCache extends ChunkSource { + + // Paper - optimise chunk tick iteration + ++ this.level.resetIceAndSnowTick(); // Pufferfish - reset ice & snow tick random + if (this.level.tickRateManager().runsNormally()) { + gameprofilerfiller.popPush("naturalSpawnCount"); + this.level.timings.countNaturalMobs.startTiming(); // Paper - timings +@@ -519,6 +523,7 @@ public class ServerChunkCache extends ChunkSource { + int naturalSpawnChunkCount = k; + NaturalSpawner.SpawnState spawnercreature_d; // moved down + if ((this.spawnFriendlies || this.spawnEnemies) && this.level.paperConfig().entities.spawning.perPlayerMobSpawns) { // don't count mobs when animals and monsters are disabled ++ if (!gg.pufferfish.pufferfish.PufferfishConfig.enableAsyncMobSpawning) { // Pufferfish - moved down when async processing + // re-set mob counts + for (ServerPlayer player : this.level.players) { + // Paper start - per player mob spawning backoff +@@ -533,14 +538,18 @@ public class ServerChunkCache extends ChunkSource { + } + // Paper end - per player mob spawning backoff + } +- spawnercreature_d = NaturalSpawner.createState(naturalSpawnChunkCount, this.level.getAllEntities(), this::getFullChunk, null, true); ++ lastSpawnState = NaturalSpawner.createState(naturalSpawnChunkCount, this.level.getAllEntities(), this::getFullChunk, null, true); // Pufferfish - async mob spawning ++ } // Pufferfish - (endif) moved down when async processing + } else { +- spawnercreature_d = NaturalSpawner.createState(naturalSpawnChunkCount, this.level.getAllEntities(), this::getFullChunk, !this.level.paperConfig().entities.spawning.perPlayerMobSpawns ? new LocalMobCapCalculator(this.chunkMap) : null, false); ++ // Pufferfish start - async mob spawning ++ lastSpawnState = NaturalSpawner.createState(naturalSpawnChunkCount, this.level.getAllEntities(), this::getFullChunk, !this.level.paperConfig().entities.spawning.perPlayerMobSpawns ? new LocalMobCapCalculator(this.chunkMap) : null, false); ++ _pufferfish_spawnCountsReady.set(true); ++ // Pufferfish end + } + // Paper end - Optional per player mob spawns + this.level.timings.countNaturalMobs.stopTiming(); // Paper - timings + +- this.lastSpawnState = spawnercreature_d; ++ // this.lastSpawnState = spawnercreature_d; // Pufferfish - this is managed asynchronously + gameprofilerfiller.popPush("spawnAndTick"); + boolean flag = this.level.getGameRules().getBoolean(GameRules.RULE_DOMOBSPAWNING) && !this.level.players().isEmpty(); // CraftBukkit + +@@ -630,8 +639,8 @@ public class ServerChunkCache extends ChunkSource { + if (tick && chunk1.chunkStatus.isOrAfter(net.minecraft.server.level.FullChunkStatus.ENTITY_TICKING)) { + // Paper end - optimise chunk tick iteration + chunk1.incrementInhabitedTime(j); +- if (spawn && flag && (this.spawnEnemies || this.spawnFriendlies) && this.level.getWorldBorder().isWithinBounds(chunkcoordintpair)) { // Spigot // Paper - optimise chunk tick iteration +- NaturalSpawner.spawnForChunk(this.level, chunk1, spawnercreature_d, this.spawnFriendlies, this.spawnEnemies, flag1); ++ if (spawn && flag && (!gg.pufferfish.pufferfish.PufferfishConfig.enableAsyncMobSpawning || _pufferfish_spawnCountsReady.get()) && (this.spawnEnemies || this.spawnFriendlies) && this.level.getWorldBorder().isWithinBounds(chunkcoordintpair)) { // Spigot // Paper - optimise chunk tick iteration // Pufferfish ++ NaturalSpawner.spawnForChunk(this.level, chunk1, lastSpawnState, this.spawnFriendlies, this.spawnEnemies, flag1); // Pufferfish + } + + if (true || this.level.shouldTickBlocksAt(chunkcoordintpair.toLong())) { // Paper - optimise chunk tick iteration +@@ -678,6 +687,40 @@ public class ServerChunkCache extends ChunkSource { + gameprofilerfiller.pop(); + gameprofilerfiller.pop(); + } ++ ++ // Pufferfish start - optimize mob spawning ++ if (gg.pufferfish.pufferfish.PufferfishConfig.enableAsyncMobSpawning) { ++ for (ServerPlayer player : this.level.players) { ++ // Paper start - per player mob spawning backoff ++ for (int ii = 0; ii < ServerPlayer.MOBCATEGORY_TOTAL_ENUMS; ii++) { ++ player.mobCounts[ii] = 0; ++ ++ int newBackoff = player.mobBackoffCounts[ii] - 1; // TODO make configurable bleed // TODO use nonlinear algorithm? ++ if (newBackoff < 0) { ++ newBackoff = 0; ++ } ++ player.mobBackoffCounts[ii] = newBackoff; ++ } ++ // Paper end - per player mob spawning backoff ++ } ++ if (firstRunSpawnCounts) { ++ firstRunSpawnCounts = false; ++ _pufferfish_spawnCountsReady.set(true); ++ } ++ if (_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 f16a69775332a08ed0e87d27acd0fc959359694c..05d7b66bb1087613e528b25b4e9e1e5d00e5a916 100644 +--- a/src/main/java/net/minecraft/server/level/ServerEntity.java ++++ b/src/main/java/net/minecraft/server/level/ServerEntity.java +@@ -191,6 +191,7 @@ public class ServerEntity { + boolean flag6 = k < -32768L || k > 32767L || l < -32768L || l > 32767L || i1 < -32768L || i1 > 32767L; + + if (!this.forceStateResync && !flag6 && this.teleportDelay <= 400 && !this.wasRiding && this.wasOnGround == this.entity.onGround()&& !(io.papermc.paper.configuration.GlobalConfiguration.get().collisions.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.onGround()); +@@ -204,6 +205,7 @@ public class ServerEntity { + flag4 = true; + flag5 = true; + } ++ } // Pufferfish + } else { + this.wasOnGround = this.entity.onGround(); + 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 ca56a0b596976448da6bb2a0e82b3d5cd4133e12..377ca8c01864d6817eff30d33403bc15bf85e6d4 100644 +--- a/src/main/java/net/minecraft/server/level/ServerLevel.java ++++ b/src/main/java/net/minecraft/server/level/ServerLevel.java +@@ -900,6 +900,7 @@ public class ServerLevel extends Level implements WorldGenLevel { + org.spigotmc.ActivationRange.activateEntities(this); // Spigot + this.timings.entityTick.startTiming(); // Spigot + this.entityTickList.forEach((entity) -> { ++ entity.activatedPriorityReset = false; // Pufferfish - DAB + if (!entity.isRemoved()) { + if (false && this.shouldDiscardEntity(entity)) { // CraftBukkit - We prevent spawning in general, so this butchering is not needed + entity.discard(); +@@ -919,7 +920,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(org.bukkit.event.entity.EntityRemoveEvent.Cause.DISCARD); ++ // Paper end ++ } ++ // Pufferfish end + gameprofilerfiller.pop(); + } + } +@@ -984,9 +998,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(this.random.nextLong()); ++ // private final io.papermc.paper.util.math.ThreadUnsafeRandom randomTickRandom = new io.papermc.paper.util.math.ThreadUnsafeRandom(); // Pufferfish - moved to super + // 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(); +@@ -997,7 +1013,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().environment.disableThunder && flag && this.isThundering() && this.spigotConfig.thunderChance > 0 && this.random.nextInt(this.spigotConfig.thunderChance) == 0) { // Spigot // Paper - Option to disable thunder ++ if (!this.paperConfig().environment.disableThunder && flag && this.isThundering() && this.spigotConfig.thunderChance > 0 && /*this.random.nextInt(this.spigotConfig.thunderChance) == 0 &&*/ chunk.shouldDoLightning(this.random)) { // Spigot // Paper - Option to disable thunder // Pufferfish - replace random with shouldDoLightning + blockposition.set(this.findLightningTargetAround(this.getBlockRandomPos(j, 0, k, 15))); // Paper + + if (this.isRainingAt(blockposition)) { +diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java +index b9b3277c8ed94e0cd30b20b9c00a33eaad48e5ac..309d173a74cb4966e567c0c1700c425a7f6203f4 100644 +--- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java ++++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java +@@ -1154,6 +1154,7 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl + + @Override + public void handleEditBook(ServerboundEditBookPacket packet) { ++ if (!gg.pufferfish.pufferfish.PufferfishConfig.enableBooks && !this.player.getBukkitEntity().hasPermission("pufferfish.usebooks")) return; // Pufferfish + // Paper start - Book size limits + if (!this.cserver.isPrimaryThread()) { + List pageList = packet.pages(); +diff --git a/src/main/java/net/minecraft/world/CompoundContainer.java b/src/main/java/net/minecraft/world/CompoundContainer.java +index 241fec02e6869c638d3a160819b32173a081467b..e1caa652db6035c12918dad2fcbc7f9df658e92d 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 f402dbbfe3a443e6bc51f88b85abe937852b52f0..c960ddfe628051257cca0d25da385c1da09d505e 100644 +--- a/src/main/java/net/minecraft/world/Container.java ++++ b/src/main/java/net/minecraft/world/Container.java +@@ -3,6 +3,7 @@ package net.minecraft.world; + import java.util.Set; + import java.util.function.Predicate; + import net.minecraft.core.BlockPos; ++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; +@@ -14,6 +15,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 + + float DEFAULT_DISTANCE_BUFFER = 4.0F; + +diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java +index 2bc85351e6e52f90da5fdb29d8d042a06132d742..c246be0aa8e88d9c60260176fe4cbc076ddbcbcf 100644 +--- a/src/main/java/net/minecraft/world/entity/Entity.java ++++ b/src/main/java/net/minecraft/world/entity/Entity.java +@@ -312,7 +312,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess + public double yo; + public double zo; + private Vec3 position; +- private BlockPos blockPosition; ++ public BlockPos blockPosition; // Pufferfish - private->public + private ChunkPos chunkPosition; + private Vec3 deltaMovement; + private float yRot; +@@ -426,6 +426,9 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess + private UUID originWorld; + public boolean freezeLocked = false; // Paper - Freeze Tick Lock API + public boolean fixedPose = false; // Paper - Expand Pose API ++ public boolean activatedPriorityReset = false; // Pufferfish - DAB ++ public int activatedPriority = gg.pufferfish.pufferfish.PufferfishConfig.maximumActivationPrio; // Pufferfish - DAB (golf score) ++ public final BlockPos.MutableBlockPos cachedBlockPos = new BlockPos.MutableBlockPos(); // Pufferfish - reduce entity allocations + + public void setOrigin(@javax.annotation.Nonnull Location location) { + this.origin = location.toVector(); +@@ -864,6 +867,12 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess + } + + public void tick() { ++ // Pufferfish start - entity TTL ++ if (type != EntityType.PLAYER && type.ttl >= 0 && this.tickCount >= type.ttl) { ++ discard(); ++ return; ++ } ++ // Pufferfish end - entity TTL + this.baseTick(); + } + +@@ -4478,16 +4487,18 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess + } + + 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; +@@ -4495,14 +4506,61 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess + 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; +@@ -4524,9 +4582,12 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess + // 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 a46bf73c608641bf1f00fd55242de71a0f2ee06e..e6edbe6177b168d85759bd9c414dc87ea8a394fe 100644 +--- a/src/main/java/net/minecraft/world/entity/EntityType.java ++++ b/src/main/java/net/minecraft/world/entity/EntityType.java +@@ -316,6 +316,8 @@ public class EntityType implements FeatureElement, EntityTypeT + private final boolean canSpawnFarFromPlayer; + private final int clientTrackingRange; + private final int updateInterval; ++ public boolean dabEnabled = false; // Pufferfish ++ public int ttl = -1; // Pufferfish + @Nullable + private String descriptionId; + @Nullable +diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java +index 517e10c3d8b1549cd30fd0e7cf2bcb35e88eb8ed..6bc0f6256851b1aa3c0e590692a949cd1e9c337d 100644 +--- a/src/main/java/net/minecraft/world/entity/LivingEntity.java ++++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java +@@ -150,7 +150,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 implements Attackable { + +@@ -427,7 +426,7 @@ public abstract class LivingEntity extends Entity implements Attackable { + boolean flag = this instanceof net.minecraft.world.entity.player.Player; + + if (!this.level().isClientSide) { +- if (this.isInWall()) { ++ if (shouldCheckForSuffocation() && this.isInWall()) { // Pufferfish - optimize suffocation + this.hurt(this.damageSources().inWall(), 1.0F); + } else if (flag && !this.level().getWorldBorder().isWithinBounds(this.getBoundingBox())) { + double d0 = this.level().getWorldBorder().getDistanceToBorder(this) + this.level().getWorldBorder().getDamageSafeZone(); +@@ -1431,6 +1430,19 @@ public abstract class LivingEntity extends Entity implements Attackable { + 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; ++ } ++ ++ public boolean shouldCheckForSuffocation() { ++ return !gg.pufferfish.pufferfish.PufferfishConfig.enableSuffocationOptimization || (tickCount % 10 == 0 && couldPossiblyBeHurt(1.0F)); ++ } ++ // Pufferfish end ++ + @Override + public boolean hurt(DamageSource source, float amount) { + if (this.isInvulnerableTo(source)) { +@@ -2050,6 +2062,20 @@ public abstract class LivingEntity extends Entity implements Attackable { + 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; +diff --git a/src/main/java/net/minecraft/world/entity/Mob.java b/src/main/java/net/minecraft/world/entity/Mob.java +index e89f9c3e887601d8461eb967ae0bf582b672f631..9aa42d6ce2c9f64142dd6f35a34319983bf7f3e4 100644 +--- a/src/main/java/net/minecraft/world/entity/Mob.java ++++ b/src/main/java/net/minecraft/world/entity/Mob.java +@@ -237,14 +237,16 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Targeti + 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(); + } + } +@@ -972,16 +974,20 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Targeti + + if (i % 2 != 0 && this.tickCount > 1) { + gameprofilerfiller.push("targetSelector"); ++ if (this.targetSelector.inactiveTick(this.activatedPriority, false)) // Pufferfish - use this to alternate ticking + this.targetSelector.tickRunningGoals(false); + gameprofilerfiller.pop(); + gameprofilerfiller.push("goalSelector"); ++ if (this.goalSelector.inactiveTick(this.activatedPriority, false)) // Pufferfish - use this to alternate ticking + this.goalSelector.tickRunningGoals(false); + gameprofilerfiller.pop(); + } else { + gameprofilerfiller.push("targetSelector"); ++ if (this.targetSelector.inactiveTick(this.activatedPriority, false)) // Pufferfish - use this to alternate ticking + this.targetSelector.tick(); + gameprofilerfiller.pop(); + gameprofilerfiller.push("goalSelector"); ++ if (this.goalSelector.inactiveTick(this.activatedPriority, false)) // Pufferfish - use this to alternate ticking + this.goalSelector.tick(); + gameprofilerfiller.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 9ef8f014af332da129bfcd3370da983ec035ecc6..bc178967affd21ad04b83ea26639a2dd9b497454 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, AttributeInstance> attributes = new Object2ObjectOpenHashMap<>(); + private final Set dirtyAttributes = new ObjectOpenHashSet<>(); + private final AttributeSupplier supplier; ++ private final java.util.function.Function, AttributeInstance> createInstance; // Pufferfish + + public AttributeMap(AttributeSupplier defaultAttributes) { + this.supplier = defaultAttributes; ++ this.createInstance = attributex -> this.supplier.createInstance(this::onAttributeModified, attributex); // Pufferfish + } + + private void onAttributeModified(AttributeInstance instance) { +@@ -43,7 +45,7 @@ public class AttributeMap { + + @Nullable + public AttributeInstance getInstance(Holder attribute) { +- return this.attributes.computeIfAbsent(attribute, attributex -> 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(Holder attribute) { +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 758f62416ca9c02351348ac0d41deeb4624abc0e..69130969c9a434ec2361e573c9a1ec9f462dfda2 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 +@@ -36,7 +36,11 @@ public class VillagerPanicTrigger extends Behavior { + + @Override + protected void tick(ServerLevel world, Villager entity, long time) { +- if (time % 100L == 0L) { ++ // Pufferfish start ++ if (entity.nextGolemPanic < 0) entity.nextGolemPanic = time + 100; ++ if (--entity.nextGolemPanic < time) { ++ entity.nextGolemPanic = -1; ++ // Pufferfish end + entity.spawnGolemIfNeeded(world, time, 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 74d4f653d5c7f1923c59019effd78337402f7025..59e1014cb7604e7a56096284be24a33f87fb580b 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 +@@ -43,9 +43,11 @@ 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 = 3; // reset to Paper's + this.curRate++; +- return this.curRate % 3 == 0; // TODO newGoalRate was already unused in 1.20.4, check if this is correct ++ return this.curRate % tickRate == 0; // TODO newGoalRate was already unused in 1.20.4, check if this is correct ++ // 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 aee0147649d458b87d92496eda0c1723ebe570d2..89e9ea999d2fbd81a1d74382ef3fcd675fc8b94e 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 +@@ -121,6 +121,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; + this.mob.movingTarget = mutableBlockPos == BlockPos.ZERO ? null : 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 d2f0c3b26d4beedb49d86e0242d843590d469d02..28cff997a1b263784e245f692adbff2a888a2d53 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 +@@ -76,9 +76,18 @@ public class TargetingConditions { + } + + if (this.range > 0.0) { +- double d = this.testInvisible ? targetEntity.getVisibilityPercent(baseEntity) : 1.0; +- double e = Math.max((this.useFollowRange ? this.getFollowRange(baseEntity) : this.range) * d, 2.0); // Paper - Fix MC-145656 ++ // 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.0; ++ double e = Math.max((followRangeRaw) * d, 2.0); // Paper - Fix MC-145656 ++ // 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 dc27ddf5131e7398a5390a5187261d4c7fb6ccaa..e44af851263f27aa0009b14a60bb2d0642a5ce74 100644 +--- a/src/main/java/net/minecraft/world/entity/ambient/Bat.java ++++ b/src/main/java/net/minecraft/world/entity/ambient/Bat.java +@@ -239,13 +239,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 + + private void setupAnimationStates() { + if (this.isResting()) { +diff --git a/src/main/java/net/minecraft/world/entity/animal/allay/Allay.java b/src/main/java/net/minecraft/world/entity/animal/allay/Allay.java +index 991e3274091c4e25eebc6debd44653e5b566eedb..789d3469ce4983868e4b880b6053bd2d8c33a10a 100644 +--- a/src/main/java/net/minecraft/world/entity/animal/allay/Allay.java ++++ b/src/main/java/net/minecraft/world/entity/animal/allay/Allay.java +@@ -215,9 +215,11 @@ public class Allay extends PathfinderMob implements InventoryCarrier, VibrationS + return 0.4F; + } + ++ private int behaviorTick = 0; // Pufferfish + @Override + protected void customServerAiStep() { + this.level().getProfiler().push("allayBrain"); ++ if (this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish + this.getBrain().tick((ServerLevel) this.level(), this); + this.level().getProfiler().pop(); + this.level().getProfiler().push("allayActivityUpdate"); +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 a8cc6ddbf45370fe632e5c5fb7ceef3d299e62a4..98766696556b520cf565ccadc8dba207832b4ae3 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 +@@ -268,9 +268,11 @@ public class Axolotl extends Animal implements LerpingModel, VariantHolder> { + .ifPresent(this::setVariant); + } + ++ private int behaviorTick = 0; // Pufferfish + @Override + protected void customServerAiStep() { + this.level().getProfiler().push("frogBrain"); ++ if (this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish + this.getBrain().tick((ServerLevel)this.level(), this); + this.level().getProfiler().pop(); + this.level().getProfiler().push("frogActivityUpdate"); +diff --git a/src/main/java/net/minecraft/world/entity/animal/frog/Tadpole.java b/src/main/java/net/minecraft/world/entity/animal/frog/Tadpole.java +index 290d41136f5ec7671bc4990dfe50da0a770c124d..0d34e9e0f7ce35c3c28a9216cf3cdd5eddfe90f6 100644 +--- a/src/main/java/net/minecraft/world/entity/animal/frog/Tadpole.java ++++ b/src/main/java/net/minecraft/world/entity/animal/frog/Tadpole.java +@@ -83,9 +83,11 @@ public class Tadpole extends AbstractFish { + return SoundEvents.TADPOLE_FLOP; + } + ++ private int behaviorTick = 0; // Pufferfish + @Override + protected void customServerAiStep() { + this.level().getProfiler().push("tadpoleBrain"); ++ if (this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish + this.getBrain().tick((ServerLevel) this.level(), this); + this.level().getProfiler().pop(); + this.level().getProfiler().push("tadpoleActivityUpdate"); +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 02e49c7ae5e120302b6479cf3e3934b9217eebf0..81c9f1c2895adedec246732df9f9dadddfa5f345 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 +@@ -190,9 +190,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/boss/wither/WitherBoss.java b/src/main/java/net/minecraft/world/entity/boss/wither/WitherBoss.java +index 7ddca52f7fe3f289b4b867e134326b1ead1a2aee..5040b1a7125dd9647a98fd812f558ec3a2171092 100644 +--- a/src/main/java/net/minecraft/world/entity/boss/wither/WitherBoss.java ++++ b/src/main/java/net/minecraft/world/entity/boss/wither/WitherBoss.java +@@ -154,6 +154,13 @@ public class WitherBoss extends Monster implements PowerableMob, RangedAttackMob + this.bossEvent.setName(this.getDisplayName()); + } + ++ // Pufferfish start - optimize suffocation ++ @Override ++ public boolean shouldCheckForSuffocation() { ++ return true; ++ } ++ // Pufferfish end ++ + @Override + protected SoundEvent getAmbientSound() { + return SoundEvents.WITHER_AMBIENT; +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 260202fab3ac300552c557b44dcf251f083c6a78..9cf4e2f309e182c69e9592ac606c0ae85a1200b6 100644 +--- a/src/main/java/net/minecraft/world/entity/monster/EnderMan.java ++++ b/src/main/java/net/minecraft/world/entity/monster/EnderMan.java +@@ -318,11 +318,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).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).blocksMotion()) { // Pufferfish + blockposition_mutableblockposition.move(Direction.DOWN); + } + +- BlockState iblockdata = this.level().getBlockState(blockposition_mutableblockposition); ++ BlockState iblockdata = chunk.getBlockState(blockposition_mutableblockposition); // Pufferfish + boolean flag = iblockdata.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 c583d883118ded5e1884c757427dc5e73c10dd27..6f8a22c51ba5e2713dcdfc61c61b35123f9bf326 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 +@@ -153,9 +153,11 @@ public class Hoglin extends Animal implements Enemy, HoglinBase { + return (Brain)super.getBrain(); + } + ++ 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 a8ab486c7e11ec137da48174af6f1030dfd48056..5878a6ecadc2b9f7ac9f70622a8c85e2940e139a 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 +@@ -294,9 +294,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/monster/warden/Warden.java b/src/main/java/net/minecraft/world/entity/monster/warden/Warden.java +index ddd60be52dce5773c80934be5aa5705db239e3dd..9cf56f2ada025aae0710099bcc3b5c62fd7bbb9e 100644 +--- a/src/main/java/net/minecraft/world/entity/monster/warden/Warden.java ++++ b/src/main/java/net/minecraft/world/entity/monster/warden/Warden.java +@@ -271,11 +271,13 @@ public class Warden extends Monster implements VibrationSystem { + + } + ++ private int behaviorTick = 0; // Pufferfish + @Override + protected void customServerAiStep() { + ServerLevel worldserver = (ServerLevel) this.level(); + + worldserver.getProfiler().push("wardenBrain"); ++ if (this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish + this.getBrain().tick(worldserver, this); + this.level().getProfiler().pop(); + super.customServerAiStep(); +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 a7930f9875aa4aca997caaead46ecdc21e5e11d7..81c2fb02779d416933502abd416324ad4c7ef22b 100644 +--- a/src/main/java/net/minecraft/world/entity/npc/Villager.java ++++ b/src/main/java/net/minecraft/world/entity/npc/Villager.java +@@ -143,6 +143,8 @@ public class Villager extends AbstractVillager implements ReputationEventHandler + return holder.is(PoiTypes.MEETING); + }); + ++ public long nextGolemPanic = -1; // Pufferfish ++ + public Villager(EntityType entityType, Level world) { + this(entityType, world, VillagerType.PLAINS); + } +@@ -246,6 +248,7 @@ public class Villager extends AbstractVillager implements ReputationEventHandler + } + // Spigot End + ++ private int behaviorTick = 0; // Pufferfish + @Override + @Deprecated // Paper + protected void customServerAiStep() { +@@ -255,7 +258,11 @@ public class Villager extends AbstractVillager implements ReputationEventHandler + protected void customServerAiStep(final boolean inactive) { + // Paper end + this.level().getProfiler().push("villagerBrain"); +- if (!inactive) this.getBrain().tick((ServerLevel) this.level(), this); // Paper ++ // Pufferfish start ++ if (!inactive && this.behaviorTick++ % this.activatedPriority == 0) { ++ 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 ca7fbe4f8c1e1d2fb90095aa35be4dda3029c23e..38c9fc3b740fcfaacae8c39defe2627c7442ca96 100644 +--- a/src/main/java/net/minecraft/world/entity/player/Inventory.java ++++ b/src/main/java/net/minecraft/world/entity/player/Inventory.java +@@ -643,6 +643,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()) { +@@ -657,11 +659,26 @@ 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() && ItemStack.isSameItemSameComponents(itemstack1, stack)) { ++ return true; ++ } ++ } ++ } ++ // Pufferfish end + + return false; + } + + public boolean contains(TagKey tag) { ++ // Pufferfish start - don't allocate iterators ++ /* + Iterator iterator = this.compartments.iterator(); + + while (iterator.hasNext()) { +@@ -676,11 +693,26 @@ 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 itemstack = list.get(j); ++ ++ if (!itemstack.isEmpty() && itemstack.is(tag)) { ++ return true; ++ } ++ } ++ } ++ // Pufferfish end + + return false; + } + + public boolean contains(Predicate predicate) { ++ // Pufferfish start - don't allocate iterators ++ /* + Iterator iterator = this.compartments.iterator(); + + while (iterator.hasNext()) { +@@ -695,6 +727,19 @@ 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 itemstack = list.get(j); ++ ++ if (predicate.test(itemstack)) { ++ 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 74c596264d4da551437bd2a23e1c70022cfc73fc..0e4d180d257d7180a8e1300f35924d0c52ce421a 100644 +--- a/src/main/java/net/minecraft/world/entity/projectile/Projectile.java ++++ b/src/main/java/net/minecraft/world/entity/projectile/Projectile.java +@@ -46,6 +46,36 @@ public abstract class Projectile extends Entity implements TraceableEntity { + 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 end ++ + 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 2704389bc3ec6dbbf1b568a4380972f8c0d62d15..c53cdaa59da87d96e34f06185b1f73e56a5bfc6c 100644 +--- a/src/main/java/net/minecraft/world/entity/vehicle/AbstractMinecartContainer.java ++++ b/src/main/java/net/minecraft/world/entity/vehicle/AbstractMinecartContainer.java +@@ -30,7 +30,10 @@ import org.bukkit.inventory.InventoryHolder; + + public abstract class AbstractMinecartContainer extends AbstractMinecart implements ContainerEntity { + ++ // Pufferfish start + private NonNullList itemStacks; ++ private gg.airplane.structs.ItemListWithBitset itemStacksOptimized; ++ // Pufferfish end + @Nullable + public ResourceKey lootTable; + public long lootTableSeed; +@@ -92,12 +95,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 +@@ -166,6 +175,10 @@ public abstract class AbstractMinecartContainer extends AbstractMinecart impleme + protected void readAdditionalSaveData(CompoundTag nbt) { + super.readAdditionalSaveData(nbt); + this.lootableData.loadNbt(nbt); // Paper ++ // Pufferfish start ++ this.itemStacksOptimized = new gg.airplane.structs.ItemListWithBitset(this.getContainerSize()); ++ this.itemStacks = this.itemStacksOptimized.nonNullList; ++ // Pufferfish end + this.readChestVehicleSaveData(nbt, this.registryAccess()); + } + +diff --git a/src/main/java/net/minecraft/world/item/EndCrystalItem.java b/src/main/java/net/minecraft/world/item/EndCrystalItem.java +index dd1bdb4bb87a3a59c229ba76b36841d199717624..42d13b03119af88838ca9cd36000162c04787493 100644 +--- a/src/main/java/net/minecraft/world/item/EndCrystalItem.java ++++ b/src/main/java/net/minecraft/world/item/EndCrystalItem.java +@@ -56,7 +56,7 @@ public class EndCrystalItem extends Item { + world.gameEvent((Entity) context.getPlayer(), (Holder) GameEvent.ENTITY_PLACE, blockposition1); + EndDragonFight enderdragonbattle = ((ServerLevel) world).getDragonFight(); + +- if (enderdragonbattle != null) { ++ if (enderdragonbattle != null && gg.pufferfish.pufferfish.PufferfishConfig.allowEndCrystalRespawn) { // Pufferfish + enderdragonbattle.tryRespawn(aboveBlockPosition); // Paper - Perf: Do crystal-portal proximity check before entity lookup + } + } +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 3554109bcc4651ca93b6275c914e57e007e2204e..60c7a9520335de8e2638572100affcfdd776c5a3 100644 +--- a/src/main/java/net/minecraft/world/item/crafting/ShapelessRecipe.java ++++ b/src/main/java/net/minecraft/world/item/crafting/ShapelessRecipe.java +@@ -27,8 +27,13 @@ public class ShapelessRecipe extends io.papermc.paper.inventory.recipe.RecipeBoo + final CraftingBookCategory category; + final ItemStack result; + final NonNullList ingredients; ++ private final boolean isBukkit; // Pufferfish + ++ // Pufferfish start + public ShapelessRecipe(String group, CraftingBookCategory category, ItemStack result, NonNullList ingredients) { ++ this(group, category, result, ingredients, false); ++ } ++ public ShapelessRecipe(String group, CraftingBookCategory category, ItemStack result, NonNullList ingredients, boolean isBukkit) { this.isBukkit = isBukkit; // Pufferfish end + this.group = group; + this.category = category; + this.result = result; +@@ -78,6 +83,28 @@ public class ShapelessRecipe extends io.papermc.paper.inventory.recipe.RecipeBoo + } + + 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(); + autorecipestackmanager.initialize(this); // Paper - better exact choice recipes + int i = 0; +diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java +index b4ef3ad2c17168085372f1fe46809f02d9dfe74a..876990240293740046c6a6b43aebad6d7d5075d6 100644 +--- a/src/main/java/net/minecraft/world/level/Level.java ++++ b/src/main/java/net/minecraft/world/level/Level.java +@@ -212,6 +212,8 @@ public abstract class Level implements LevelAccessor, AutoCloseable { + // Paper end + + public abstract ResourceKey getTypeKey(); ++ ++ protected final io.papermc.paper.util.math.ThreadUnsafeRandom randomTickRandom = new io.papermc.paper.util.math.ThreadUnsafeRandom(java.util.concurrent.ThreadLocalRandom.current().nextLong()); public net.minecraft.util.RandomSource getThreadUnsafeRandom() { return this.randomTickRandom; } // Pufferfish - move thread unsafe random initialization // Pufferfish - getter + + protected Level(WritableLevelData worlddatamutable, ResourceKey resourcekey, RegistryAccess iregistrycustom, Holder holder, Supplier supplier, boolean flag, boolean flag1, long i, int j, org.bukkit.generator.ChunkGenerator gen, org.bukkit.generator.BiomeProvider biomeProvider, org.bukkit.World.Environment env, java.util.function.Function paperWorldConfigCreator, java.util.concurrent.Executor executor) { // Paper - create paper world config; Async-Anti-Xray: Pass executor + this.spigotConfig = new org.spigotmc.SpigotWorldConfig(((net.minecraft.world.level.storage.PrimaryLevelData) worlddatamutable).getLevelName()); // Spigot +@@ -1308,13 +1310,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 block 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))); // Paper - ServerExceptionEvent +- entity.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.DISCARD); ++ entity.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.DISCARD); // Pufferfish - diff on change ServerLevel.tick + // Paper end - Prevent block entity and entity crashes + } + } +@@ -1789,6 +1791,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 ed8032495af9ce9c23419224814b8d27e4a97c17..382c5773f773837f8182ee5d4f97f588c15eaacd 100644 +--- a/src/main/java/net/minecraft/world/level/NaturalSpawner.java ++++ b/src/main/java/net/minecraft/world/level/NaturalSpawner.java +@@ -408,12 +408,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 15f82c9a1ce1fef2e951d1b3c7a65e64b82061ea..f9fbfb63f19decb3b15284306d7edda072e609af 100644 +--- a/src/main/java/net/minecraft/world/level/biome/Biome.java ++++ b/src/main/java/net/minecraft/world/level/biome/Biome.java +@@ -63,13 +63,18 @@ public final class Biome { + private final BiomeGenerationSettings generationSettings; + private final MobSpawnSettings mobSettings; + private final BiomeSpecialEffects specialEffects; +- private final ThreadLocal temperatureCache = ThreadLocal.withInitial(() -> Util.make(() -> { ++ // Pufferfish start - use our cache ++ private final ThreadLocal temperatureCache = ThreadLocal.withInitial(() -> 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 + })); + + Biome(Biome.ClimateSettings weather, BiomeSpecialEffects effects, BiomeGenerationSettings generationSettings, MobSpawnSettings spawnSettings) { +@@ -112,17 +117,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 b88aa184cd06a0485146f58a5b61a56a50911209..7b64c57795fe27d8397500bfa41f71f9d2103b20 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 + protected void loadAdditional(CompoundTag nbt, HolderLookup.Provider registryLookup) { + super.loadAdditional(nbt, registryLookup); +- 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, registryLookup); + } +@@ -188,7 +215,10 @@ public class ChestBlockEntity extends RandomizableContainerBlockEntity implement + + @Override + protected void setItems(NonNullList inventory) { +- this.items = inventory; ++ // Pufferfish start ++ this.optimizedItems = gg.airplane.structs.ItemListWithBitset.fromList(inventory); ++ 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 8310d132006043e93c612890514c4c7f3eb1c74d..1bd1f88aea2c841c20e21f1472f2ccfbde953bd4 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 +@@ -47,7 +47,10 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen + public static final int MOVE_ITEM_SPEED = 8; + public static final int HOPPER_CONTAINER_SIZE = 5; + private static final int[][] CACHED_SLOTS = new int[54][]; ++ // Pufferfish start + private NonNullList items; ++ private gg.airplane.structs.ItemListWithBitset optimizedItems; ++ // Pufferfish end + public int cooldownTime; + private long tickedGameTime; + private Direction facing; +@@ -84,15 +87,38 @@ 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; + this.facing = (Direction) state.getValue(HopperBlock.FACING); + } + ++ // 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 + protected void loadAdditional(CompoundTag nbt, HolderLookup.Provider registryLookup) { + super.loadAdditional(nbt, registryLookup); +- 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, registryLookup); + } +@@ -525,6 +551,7 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen + } + + private static boolean isFullContainer(Container inventory, Direction direction) { ++ if (true) return inventory.isCompletelyFull(direction); // Pufferfish - use bitsets + int[] aint = HopperBlockEntity.getSlots(inventory, direction); + int[] aint1 = aint; + int i = aint.length; +@@ -723,7 +750,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 +@@ -911,7 +938,10 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen + + @Override + protected void setItems(NonNullList inventory) { +- this.items = inventory; ++ // Pufferfish start ++ this.optimizedItems = gg.airplane.structs.ItemListWithBitset.fromList(inventory); ++ 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 17da915ffa638500a83b67db0940a7b9a9b333a1..5682039a18de20bacdac804db8da47953b22d653 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 +@@ -99,7 +99,7 @@ public abstract class RandomizableContainerBlockEntity extends BaseContainerBloc + @Override + public boolean isEmpty() { + this.unpackLootTable(null); +- return super.isEmpty(); ++ return this.isCompletelyEmpty(null); // Pufferfish - use super + } + + @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 2a8609e33716949ff1877b6d10f64a9d7a7c81e9..8d6b95bc46e17fcbda6dbec0f77a451df884cbaa 100644 +--- a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java ++++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java +@@ -88,6 +88,18 @@ public class LevelChunk extends ChunkAccess { + private final LevelChunkTicks fluidTicks; + public volatile FullChunkStatus chunkStatus = FullChunkStatus.INACCESSIBLE; // Paper - rewrite chunk system + ++ // 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(net.minecraft.util.RandomSource 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); + } +@@ -111,6 +123,8 @@ public class LevelChunk extends ChunkAccess { + this.postLoad = entityLoader; + this.blockTicks = blockTickScheduler; + this.fluidTicks = fluidTickScheduler; ++ ++ this.lightningTick = this.level.getThreadUnsafeRandom().nextInt(100000) << 1; // Pufferfish - initialize lightning tick + } + + // CraftBukkit start +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 a2a5aef769ee8bb638a5a9f3da9812fa4a85dda5..7288261b8924d08e93abecb664e2273c624a325b 100644 +--- a/src/main/java/net/minecraft/world/level/chunk/LevelChunkSection.java ++++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunkSection.java +@@ -25,6 +25,7 @@ public class LevelChunkSection { + public final PalettedContainer states; + // CraftBukkit start - read/write + private PalettedContainer> biomes; ++ public short fluidStateCount; // Pufferfish + public final com.destroystokyo.paper.util.maplist.IBlockDataList tickingList = new com.destroystokyo.paper.util.maplist.IBlockDataList(); // Paper + // Paper start - optimise collisions + private int specialCollidingBlocks; +@@ -102,6 +103,7 @@ public class LevelChunkSection { + + if (!fluid.isEmpty()) { + --this.tickingFluidCount; ++ --this.fluidStateCount; // Pufferfish + } + + if (!state.isAir()) { +@@ -116,6 +118,7 @@ public class LevelChunkSection { + + if (!fluid1.isEmpty()) { + ++this.tickingFluidCount; ++ ++this.fluidStateCount; // Pufferfish + } + + this.updateBlockCallback(x, y, z, iblockdata1, state); // Paper - optimise collisions +@@ -162,6 +165,7 @@ public class LevelChunkSection { + if (fluid.isRandomlyTicking()) { + this.tickingFluidCount = (short) (this.tickingFluidCount + 1); + } ++ this.fluidStateCount++; // Pufferfish + } + + // Paper start - optimise collisions +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 83a39f900551e39d5af6f17a339a386ddee4feef..0c8c534fc69172387f188af5282accfed7597ac7 100644 +--- a/src/main/java/net/minecraft/world/level/entity/EntityTickList.java ++++ b/src/main/java/net/minecraft/world/level/entity/EntityTickList.java +@@ -9,7 +9,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/material/FlowingFluid.java b/src/main/java/net/minecraft/world/level/material/FlowingFluid.java +index c2943d892b067b3f1fb3b93301a092e912d71f08..58296b67f80587af485b0068e461cfd3d8d6f96f 100644 +--- a/src/main/java/net/minecraft/world/level/material/FlowingFluid.java ++++ b/src/main/java/net/minecraft/world/level/material/FlowingFluid.java +@@ -45,6 +45,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) {} +@@ -53,6 +55,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() {} +@@ -251,6 +261,8 @@ public abstract class FlowingFluid extends Fluid { + return false; + } + // Paper end - optimise collisions ++ // Pufferfish start - modify to use our cache ++ /* + Object2ByteLinkedOpenHashMap object2bytelinkedopenhashmap; + + if (!state.getBlock().hasDynamicShape() && !fromState.getBlock().hasDynamicShape()) { +@@ -258,9 +270,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); +@@ -271,11 +290,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(); +@@ -283,6 +313,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/LootParams.java b/src/main/java/net/minecraft/world/level/storage/loot/LootParams.java +index 37a0002bbe6539648db5219bb373e0404ae48dc0..ca0571d232e102c4b177a1ea44b96f5f0f440211 100644 +--- a/src/main/java/net/minecraft/world/level/storage/loot/LootParams.java ++++ b/src/main/java/net/minecraft/world/level/storage/loot/LootParams.java +@@ -21,8 +21,10 @@ public class LootParams { + + public LootParams(ServerLevel world, Map, Object> parameters, Map dynamicDrops, float luck) { + this.level = world; +- this.params = parameters; +- this.dynamicDrops = dynamicDrops; ++ // 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(dynamicDrops); ++ // Pufferfish end + this.luck = luck; + } + +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 88a4a72bb390947dc17e5da09a99b2d1b3ac4621..284c76ddb9724b44bb2e93f590685c728e843e6d 100644 +--- a/src/main/java/net/minecraft/world/phys/shapes/EntityCollisionContext.java ++++ b/src/main/java/net/minecraft/world/phys/shapes/EntityCollisionContext.java +@@ -17,50 +17,69 @@ 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 + protected EntityCollisionContext(Entity entity) { +- this( +- entity.isDescending(), +- entity.getY(), +- entity instanceof LivingEntity ? ((LivingEntity)entity).getMainHandItem() : ItemStack.EMPTY, +- entity instanceof LivingEntity ? ((LivingEntity)entity)::canStandOnFluid : fluidState -> 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 -> 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 stateAbove, FluidState state) { +- return this.canStandOnFluid.test(state) && !stateAbove.getType().isSame(state.getType()); ++ // Pufferfish start ++ Entity entity = this.entity; ++ if (entity instanceof LivingEntity livingEntity) { ++ return livingEntity.canStandOnFluid(state) && !stateAbove.getType().isSame(state.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) - 1.0E-5F; ++ return (this.entity == null ? -Double.MAX_VALUE : entity.getY()) > (double)pos.getY() + shape.max(Direction.Axis.Y) - 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 da1aed63af837b193900bb85393611edbd56c363..6262147f9d3fc1a988d9362573d3736e3ddd88a1 100644 +--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java ++++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java +@@ -269,7 +269,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"); +@@ -1129,6 +1129,11 @@ public final class CraftServer implements Server { + plugin.getPluginMeta().getDisplayName(), + "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 - Wait for Async Tasks during shutdown +diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftShapelessRecipe.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftShapelessRecipe.java +index 96d772eb02f79f8c478f5e6f065e387aa7665b18..c5ce412f321b8b4f31cc042893659e213b081f29 100644 +--- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftShapelessRecipe.java ++++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftShapelessRecipe.java +@@ -45,6 +45,6 @@ public class CraftShapelessRecipe extends ShapelessRecipe implements CraftRecipe + data.set(i, this.toNMS(ingred.get(i), true)); + } + +- MinecraftServer.getServer().getRecipeManager().addRecipe(new RecipeHolder<>(CraftNamespacedKey.toMinecraft(this.getKey()), new net.minecraft.world.item.crafting.ShapelessRecipe(this.getGroup(), CraftRecipe.getCategory(this.getCategory()), CraftItemStack.asNMSCopy(this.getResult()), data))); ++ MinecraftServer.getServer().getRecipeManager().addRecipe(new RecipeHolder<>(CraftNamespacedKey.toMinecraft(this.getKey()), new net.minecraft.world.item.crafting.ShapelessRecipe(this.getGroup(), CraftRecipe.getCategory(this.getCategory()), CraftItemStack.asNMSCopy(this.getResult()), data, true))); // Pufferfish + } + } +diff --git a/src/main/java/org/bukkit/craftbukkit/scheduler/MinecraftInternalPlugin.java b/src/main/java/org/bukkit/craftbukkit/scheduler/MinecraftInternalPlugin.java +index 66bdac50130f523f9dc4379b103b7a469f9ca36b..ef780e0566dbac07da88ba26dc595b49c8ff5ab3 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"; +@@ -81,7 +82,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 +@@ -91,7 +97,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 5a382907285a288f2a223189e690d3dbdf45594c..16d6a515e93619a0c9fdcaec5b99e0a36f8faec5 100644 +--- a/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java ++++ b/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java +@@ -511,7 +511,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 e8e93538dfd71de86515d9405f728db1631e949a..3dff02fd97f001508e2f81192817bf1b0ef92446 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 3283ed99c35ffed6805567705e0518d9f84feedc..d7c7e12c0b8f77e59d94de130972f762ed227726 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 + { +@@ -223,6 +227,25 @@ public class ActivationRange + } + // Paper end - Configurable marker ticking + ActivationRange.activateEntity(entity); ++ ++ // Pufferfish start ++ if (gg.pufferfish.pufferfish.PufferfishConfig.dearEnabled && entity.getType().dabEnabled) { ++ if (!entity.activatedPriorityReset) { ++ entity.activatedPriorityReset = true; ++ entity.activatedPriority = gg.pufferfish.pufferfish.PufferfishConfig.maximumActivationPrio; ++ } ++ 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, entity.activatedPriority)) : ++ 1; ++ } else { ++ entity.activatedPriority = 1; ++ } ++ // Pufferfish end ++ + } + // Paper end + } +@@ -239,12 +262,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; + } + } +@@ -298,7 +321,7 @@ public class ActivationRange + if ( entity instanceof LivingEntity ) + { + LivingEntity living = (LivingEntity) entity; +- if ( living.onClimbable() || living.jumping || living.hurtTime > 0 || living.activeEffects.size() > 0 || living.isFreezing()) // Paper ++ if ( living.onClimableCached() || living.jumping || living.hurtTime > 0 || living.activeEffects.size() > 0 || living.isFreezing() ) // Paper // Pufferfish - use cached + { + return 1; // Paper + } diff --git a/patches/server/0001-Purpur-Server-Changes.patch b/patches/server/0002-Purpur-Server-Changes.patch similarity index 97% rename from patches/server/0001-Purpur-Server-Changes.patch rename to patches/server/0002-Purpur-Server-Changes.patch index 36a753bdc..0d4646400 100644 --- a/patches/server/0001-Purpur-Server-Changes.patch +++ b/patches/server/0002-Purpur-Server-Changes.patch @@ -1,6 +1,6 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: granny -Date: Tue, 14 May 2024 17:29:02 +0900 +Date: Tue, 14 May 2024 22:59:42 +0900 Subject: [PATCH] Purpur Server Changes PurpurMC @@ -25,21 +25,21 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/build.gradle.kts b/build.gradle.kts -index 87bb3fd9b97506f61734ae7f2e6860610ba794e7..89f21e21efc5f5ab99d1586f4f3b8f2f3c48ac06 100644 +index 00b31bbf6a5832799eae65e5be67cc6e92c69a56..3bece6335887414d1388505febf6b8796e32bf9a 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -12,7 +12,7 @@ configurations.named(log4jPlugins.compileClasspathConfigurationName) { val alsoShade: Configuration by configurations.creating dependencies { -- implementation(project(":paper-api")) -+ implementation(project(":purpur-api")) // Purpur +- implementation(project(":pufferfish-api")) // Pufferfish // Paper ++ implementation(project(":purpur-api")) // Pufferfish // Paper // Purpur // Paper start implementation("org.jline:jline-terminal-jansi:3.21.0") implementation("net.minecrell:terminalconsoleappender:1.3.0") -@@ -46,6 +46,10 @@ dependencies { - runtimeOnly("org.apache.maven.resolver:maven-resolver-connector-basic:1.9.18") - runtimeOnly("org.apache.maven.resolver:maven-resolver-transport-http:1.9.18") +@@ -54,6 +54,10 @@ dependencies { + // Pufferfish end + implementation("com.github.technove:Flare:34637f3f87") // Pufferfish - flare + implementation("org.mozilla:rhino-runtime:1.7.14") // Purpur + implementation("org.mozilla:rhino-engine:1.7.14") // Purpur @@ -48,16 +48,16 @@ index 87bb3fd9b97506f61734ae7f2e6860610ba794e7..89f21e21efc5f5ab99d1586f4f3b8f2f testImplementation("io.github.classgraph:classgraph:4.8.47") // Paper - mob goal test testImplementation("org.junit.jupiter:junit-jupiter:5.10.2") testImplementation("org.hamcrest:hamcrest:2.2") -@@ -78,7 +82,7 @@ tasks.jar { +@@ -93,7 +97,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-Version" to "git-Purpur-$implementationVersion", // Pufferfish // Purpur "Implementation-Vendor" to date, // Paper "Specification-Title" to "Bukkit", "Specification-Version" to project.version, -@@ -137,7 +141,7 @@ fun TaskContainer.registerRunTask( +@@ -152,7 +156,7 @@ fun TaskContainer.registerRunTask( name: String, block: JavaExec.() -> Unit ): TaskProvider = register(name) { @@ -158,14 +158,14 @@ index 0000000000000000000000000000000000000000..15a226e3854d731f7724025ea3459c8a + } +} diff --git a/src/main/java/com/destroystokyo/paper/Metrics.java b/src/main/java/com/destroystokyo/paper/Metrics.java -index 4b002e8b75d117b726b0de274a76d3596fce015b..8cde30544e14f8fc2dac32966ae3c21f8cf3a551 100644 +index 692c962193cf9fcc6801fc93f3220bdc673d527b..8cde30544e14f8fc2dac32966ae3c21f8cf3a551 100644 --- a/src/main/java/com/destroystokyo/paper/Metrics.java +++ b/src/main/java/com/destroystokyo/paper/Metrics.java @@ -593,7 +593,7 @@ public class Metrics { boolean logFailedRequests = config.getBoolean("logFailedRequests", false); // Only start Metrics, if it's enabled in the config if (config.getBoolean("enabled", true)) { -- Metrics metrics = new Metrics("Paper", serverUUID, logFailedRequests, Bukkit.getLogger()); +- Metrics metrics = new Metrics("Pufferfish", serverUUID, logFailedRequests, Bukkit.getLogger()); // Pufferfish + Metrics metrics = new Metrics("Purpur", serverUUID, logFailedRequests, Bukkit.getLogger()); // Pufferfish // Purpur metrics.addCustomChart(new Metrics.SimplePie("minecraft_version", () -> { @@ -179,11 +179,11 @@ index 4b002e8b75d117b726b0de274a76d3596fce015b..8cde30544e14f8fc2dac32966ae3c21f - final String implVersion = org.bukkit.craftbukkit.Main.class.getPackage().getImplementationVersion(); - if (implVersion != null) { - final String buildOrHash = implVersion.substring(implVersion.lastIndexOf('-') + 1); -- paperVersion = "git-Paper-%s-%s".formatted(Bukkit.getServer().getMinecraftVersion(), buildOrHash); +- paperVersion = "git-Pufferfish-%s-%s".formatted(Bukkit.getServer().getMinecraftVersion(), buildOrHash); // Pufferfish - } else { - paperVersion = "unknown"; - } -- metrics.addCustomChart(new Metrics.SimplePie("paper_version", () -> paperVersion)); +- metrics.addCustomChart(new Metrics.SimplePie("pufferfish_version", () -> paperVersion)); // Pufferfish + metrics.addCustomChart(new Metrics.SimplePie("online_mode", () -> Bukkit.getOnlineMode() ? "online" : (io.papermc.paper.configuration.GlobalConfiguration.get().proxies.isProxyOnlineMode() ? "bungee" : "offline"))); // Purpur + metrics.addCustomChart(new Metrics.SimplePie("purpur_version", () -> (org.bukkit.craftbukkit.Main.class.getPackage().getImplementationVersion() != null) ? org.bukkit.craftbukkit.Main.class.getPackage().getImplementationVersion() : "unknown")); // Purpur @@ -324,6 +324,63 @@ index 039a86034928a5eb7aaa2d7ca76a7bddcca346bd..308f67d0616e2d6bb135258f1fda53cc setListData(vector); } +diff --git a/src/main/java/gg/pufferfish/pufferfish/PufferfishConfig.java b/src/main/java/gg/pufferfish/pufferfish/PufferfishConfig.java +index 1ca25529245ed369b8705075a8c2e32fa4d9b1d1..cba3d725245223c5d9e8e59dcceec61f0acc1b85 100644 +--- a/src/main/java/gg/pufferfish/pufferfish/PufferfishConfig.java ++++ b/src/main/java/gg/pufferfish/pufferfish/PufferfishConfig.java +@@ -36,6 +36,7 @@ public class PufferfishConfig { + + private static final YamlFile config = new YamlFile(); + private static int updates = 0; ++ public static File pufferfishFile; // Purpur + + private static ConfigurationSection convertToBukkit(org.simpleyaml.configuration.ConfigurationSection section) { + ConfigurationSection newSection = new MemoryConfiguration(); +@@ -58,7 +59,7 @@ public class PufferfishConfig { + } + + public static void load() throws IOException { +- File configFile = new File("pufferfish.yml"); ++ File configFile = pufferfishFile; // Purpur + + if (configFile.exists()) { + try { +@@ -94,7 +95,7 @@ public class PufferfishConfig { + // Attempt to detect vectorization + try { + SIMDDetection.isEnabled = SIMDDetection.canEnable(PufferfishLogger.LOGGER); +- SIMDDetection.versionLimited = SIMDDetection.getJavaVersion() != 17 && SIMDDetection.getJavaVersion() != 18 && SIMDDetection.getJavaVersion() != 19; ++ SIMDDetection.versionLimited = SIMDDetection.getJavaVersion() < 17 || SIMDDetection.getJavaVersion() > 21; + } catch (NoClassDefFoundError | Exception ignored) { + ignored.printStackTrace(); + } +@@ -102,7 +103,7 @@ public class PufferfishConfig { + 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, Java 18, and Java 19."); ++ PufferfishLogger.LOGGER.warning("Will not enable SIMD! These optimizations are only safely supported on Java 17 through Java 21."); + } 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\"."); +@@ -240,7 +241,7 @@ public class PufferfishConfig { + public static int activationDistanceMod; + + private static void dynamicActivationOfBrains() throws IOException { +- dearEnabled = getBoolean("dab.enabled", "activation-range.enabled", true); ++ dearEnabled = getBoolean("dab.enabled", "activation-range.enabled", false); // Purpur + 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."); +@@ -284,7 +285,7 @@ public class PufferfishConfig { + + public static boolean throttleInactiveGoalSelectorTick; + private static void inactiveGoalSelectorThrottle() { +- throttleInactiveGoalSelectorTick = getBoolean("inactive-goal-selector-throttle", "inactive-goal-selector-disable", true, ++ throttleInactiveGoalSelectorTick = getBoolean("inactive-goal-selector-throttle", "inactive-goal-selector-disable", false, // Purpur + "Throttles the AI goal selector in entity inactive ticks.", + "This can improve performance by a few percent, but has minor gameplay implications."); + } diff --git a/src/main/java/io/papermc/paper/chunk/system/scheduling/ChunkHolderManager.java b/src/main/java/io/papermc/paper/chunk/system/scheduling/ChunkHolderManager.java index 6bc7c6f16a1649fc9e24e7cf90fca401e5bd4875..e1ffd62f4ebceecb9bc5471df3da406cffea0483 100644 --- a/src/main/java/io/papermc/paper/chunk/system/scheduling/ChunkHolderManager.java @@ -1068,7 +1125,7 @@ index 5443013060b62e3bfcc51cddca96d1c0bc59fe72..411f1f8c6be072cfc5ba88cbec38dbc4 Bootstrap.bootStrap(); Bootstrap.validate(); diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java -index 39303bb4e336732db0ab19dee0c1f8b609bbb134..dfe3d28bd97d1335457d1005ecf69afe75d5ff80 100644 +index b5b5d467231b571455f82d1943e6a8a46d75c7dc..4e989dd5919e29d04dcd9f6af0f3922f94d41742 100644 --- a/src/main/java/net/minecraft/server/MinecraftServer.java +++ b/src/main/java/net/minecraft/server/MinecraftServer.java @@ -300,6 +300,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop 0 && this.tickCount % autosavePeriod == 0; try { this.isSaving = true; -@@ -1601,20 +1636,20 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop { entityplayer.connection.suspendFlushing(); }); @@ -1355,7 +1405,7 @@ index 39303bb4e336732db0ab19dee0c1f8b609bbb134..dfe3d28bd97d1335457d1005ecf69afe // Paper start - Folia scheduler API ((io.papermc.paper.threadedregions.scheduler.FoliaGlobalRegionScheduler) Bukkit.getGlobalRegionScheduler()).tick(); getAllLevels().forEach(level -> { -@@ -1719,22 +1754,22 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop 0; // Paper - Add EntityMoveEvent net.minecraft.world.level.block.entity.HopperBlockEntity.skipHopperEvents = worldserver.paperConfig().hopper.disableMoveEvent || org.bukkit.event.inventory.InventoryMoveItemEvent.getHandlerList().getRegisteredListeners().length == 0; // Paper - Perf: Optimize Hoppers worldserver.updateLagCompensationTick(); // Paper - lag compensation @@ -1443,7 +1493,7 @@ index 39303bb4e336732db0ab19dee0c1f8b609bbb134..dfe3d28bd97d1335457d1005ecf69afe } catch (Throwable throwable) { CrashReport crashreport = CrashReport.forThrowable(throwable, "Exception ticking world"); -@@ -1796,33 +1832,33 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop // Paper + return org.purpurmc.purpur.PurpurConfig.serverModName; // Purpur - Purpur > // Pufferfish > // Paper } public SystemReport fillSystemReport(SystemReport details) { -@@ -2561,6 +2597,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop { this.executeBlocking(() -> { this.saveDebugReport(path.resolve("server")); -@@ -2817,40 +2854,40 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop> completablefuture = this.getChunkFutureMainThread(x, z, leastStatus, create); ServerChunkCache.MainThreadExecutor chunkproviderserver_b = this.mainThreadProcessor; -@@ -274,10 +274,10 @@ public class ServerChunkCache extends ChunkSource { +@@ -277,10 +277,10 @@ public class ServerChunkCache extends ChunkSource { if (!completablefuture.isDone()) { // Paper io.papermc.paper.chunk.system.scheduling.ChunkTaskScheduler.pushChunkWait(this.level, x1, z1); // Paper - rewrite chunk system com.destroystokyo.paper.io.SyncLoadFinder.logSyncLoad(this.level, x, z); // Paper - Add debug for sync chunk loads @@ -2112,7 +2166,7 @@ index b99f50604bafecbc68835974c9ed0caa91911a40..476a04d87a61b021816d2970e86042bd } // Paper ChunkResult chunkresult = (ChunkResult) completablefuture.join(); ChunkAccess ichunkaccess1 = (ChunkAccess) chunkresult.orElse(null); // CraftBukkit - decompile error -@@ -425,17 +425,17 @@ public class ServerChunkCache extends ChunkSource { +@@ -428,17 +428,17 @@ public class ServerChunkCache extends ChunkSource { public void save(boolean flush) { this.runDistanceManagerUpdates(); @@ -2134,7 +2188,7 @@ index b99f50604bafecbc68835974c9ed0caa91911a40..476a04d87a61b021816d2970e86042bd } // Paper end - Incremental chunk and player saving -@@ -459,40 +459,39 @@ public class ServerChunkCache extends ChunkSource { +@@ -462,40 +462,39 @@ public class ServerChunkCache extends ChunkSource { // CraftBukkit start - modelled on below public void purgeUnload() { if (true) return; // Paper - tickets will be removed later, this behavior isn't really well accounted for by the chunk system @@ -2188,7 +2242,7 @@ index b99f50604bafecbc68835974c9ed0caa91911a40..476a04d87a61b021816d2970e86042bd this.clearCache(); } -@@ -502,18 +501,18 @@ public class ServerChunkCache extends ChunkSource { +@@ -505,19 +504,19 @@ public class ServerChunkCache extends ChunkSource { this.lastInhabitedUpdate = i; if (!this.level.isDebug()) { @@ -2205,6 +2259,7 @@ index b99f50604bafecbc68835974c9ed0caa91911a40..476a04d87a61b021816d2970e86042bd // Paper - optimise chunk tick iteration + this.level.resetIceAndSnowTick(); // Pufferfish - reset ice & snow tick random if (this.level.tickRateManager().runsNormally()) { - gameprofilerfiller.popPush("naturalSpawnCount"); - this.level.timings.countNaturalMobs.startTiming(); // Paper - timings @@ -2213,20 +2268,20 @@ index b99f50604bafecbc68835974c9ed0caa91911a40..476a04d87a61b021816d2970e86042bd int k = this.distanceManager.getNaturalSpawnChunkCount(); // Paper start - Optional per player mob spawns int naturalSpawnChunkCount = k; -@@ -538,10 +537,10 @@ public class ServerChunkCache extends ChunkSource { - spawnercreature_d = NaturalSpawner.createState(naturalSpawnChunkCount, this.level.getAllEntities(), this::getFullChunk, !this.level.paperConfig().entities.spawning.perPlayerMobSpawns ? new LocalMobCapCalculator(this.chunkMap) : null, false); +@@ -547,10 +546,10 @@ public class ServerChunkCache extends ChunkSource { + // Pufferfish end } // Paper end - Optional per player mob spawns - this.level.timings.countNaturalMobs.stopTiming(); // Paper - timings + // this.level.timings.countNaturalMobs.stopTiming(); // Paper - timings // Purpur - this.lastSpawnState = spawnercreature_d; + // this.lastSpawnState = spawnercreature_d; // Pufferfish - this is managed asynchronously - gameprofilerfiller.popPush("spawnAndTick"); + //gameprofilerfiller.popPush("spawnAndTick"); // Purpur boolean flag = this.level.getGameRules().getBoolean(GameRules.RULE_DOMOBSPAWNING) && !this.level.players().isEmpty(); // CraftBukkit // Paper start - optimise chunk tick iteration -@@ -647,19 +646,19 @@ public class ServerChunkCache extends ChunkSource { +@@ -656,19 +655,19 @@ public class ServerChunkCache extends ChunkSource { } } // Paper end - optimise chunk tick iteration @@ -2252,7 +2307,7 @@ index b99f50604bafecbc68835974c9ed0caa91911a40..476a04d87a61b021816d2970e86042bd // Paper start - optimise chunk tick iteration if (!this.chunkMap.needsChangeBroadcasting.isEmpty()) { it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet copy = this.chunkMap.needsChangeBroadcasting.clone(); -@@ -673,10 +672,10 @@ public class ServerChunkCache extends ChunkSource { +@@ -682,10 +681,10 @@ public class ServerChunkCache extends ChunkSource { } } // Paper end - optimise chunk tick iteration @@ -2264,9 +2319,9 @@ index b99f50604bafecbc68835974c9ed0caa91911a40..476a04d87a61b021816d2970e86042bd + //gameprofilerfiller.pop(); // Purpur + //gameprofilerfiller.pop(); // Purpur } - } - -@@ -848,7 +847,7 @@ public class ServerChunkCache extends ChunkSource { + + // Pufferfish start - optimize mob spawning +@@ -891,7 +890,7 @@ public class ServerChunkCache extends ChunkSource { @Override protected void doRunTask(Runnable task) { @@ -2276,7 +2331,7 @@ index b99f50604bafecbc68835974c9ed0caa91911a40..476a04d87a61b021816d2970e86042bd } diff --git a/src/main/java/net/minecraft/server/level/ServerEntity.java b/src/main/java/net/minecraft/server/level/ServerEntity.java -index f16a69775332a08ed0e87d27acd0fc959359694c..dd6278fe99e2ee4daa95249c71ea935dd08b0025 100644 +index 05d7b66bb1087613e528b25b4e9e1e5d00e5a916..78be6cea03f40edfa90b5d9df014d5cf22b81f61 100644 --- a/src/main/java/net/minecraft/server/level/ServerEntity.java +++ b/src/main/java/net/minecraft/server/level/ServerEntity.java @@ -77,7 +77,7 @@ public class ServerEntity { @@ -2289,7 +2344,7 @@ index f16a69775332a08ed0e87d27acd0fc959359694c..dd6278fe99e2ee4daa95249c71ea935d public ServerEntity(ServerLevel worldserver, Entity entity, int i, boolean flag, Consumer> consumer, Set trackedPlayers) { this.trackedPlayers = trackedPlayers; diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java -index ca56a0b596976448da6bb2a0e82b3d5cd4133e12..7924e3c597164f71056cd58dd167ee8b1d41b9c3 100644 +index 377ca8c01864d6817eff30d33403bc15bf85e6d4..b3d47d16fb9733ca5cc4f90051ee8694ff3545c5 100644 --- a/src/main/java/net/minecraft/server/level/ServerLevel.java +++ b/src/main/java/net/minecraft/server/level/ServerLevel.java @@ -220,6 +220,8 @@ public class ServerLevel extends Level implements WorldGenLevel { @@ -2397,10 +2452,10 @@ index ca56a0b596976448da6bb2a0e82b3d5cd4133e12..7924e3c597164f71056cd58dd167ee8b + //gameprofilerfiller.popPush("raid"); // Purpur if (flag) { - this.timings.raids.startTiming(); // Paper - timings -+ // this.timings.raids.startTiming(); // Paper - timings // Purpur ++ //this.timings.raids.startTiming(); // Paper - timings // Purpur this.raids.tick(); - this.timings.raids.stopTiming(); // Paper - timings -+ // this.timings.raids.stopTiming(); // Paper - timings // Purpur ++ //this.timings.raids.stopTiming(); // Paper - timings // Purpur } - gameprofilerfiller.popPush("chunkSource"); @@ -2414,10 +2469,10 @@ index ca56a0b596976448da6bb2a0e82b3d5cd4133e12..7924e3c597164f71056cd58dd167ee8b + //gameprofilerfiller.popPush("blockEvents"); // Purpur if (flag) { - this.timings.doSounds.startTiming(); // Spigot -+ // this.timings.doSounds.startTiming(); // Spigot // Purpur ++ //this.timings.doSounds.startTiming(); // Spigot // Purpur this.runBlockEvents(); - this.timings.doSounds.stopTiming(); // Spigot -+ // this.timings.doSounds.stopTiming(); // Spigot // Purpur ++ //this.timings.doSounds.stopTiming(); // Spigot // Purpur } this.handlingTick = false; @@ -2426,7 +2481,7 @@ index ca56a0b596976448da6bb2a0e82b3d5cd4133e12..7924e3c597164f71056cd58dd167ee8b boolean flag1 = !paperConfig().unsupportedSettings.disableWorldTickingWhenEmpty || !this.players.isEmpty() || !this.getForcedChunks().isEmpty(); // CraftBukkit - this prevents entity cleanup, other issues on servers with no players // Paper - restore this if (flag1) { -@@ -889,24 +910,24 @@ public class ServerLevel extends Level implements WorldGenLevel { +@@ -889,25 +910,25 @@ public class ServerLevel extends Level implements WorldGenLevel { } if (flag1 || this.emptyTime++ < 300) { @@ -2446,6 +2501,7 @@ index ca56a0b596976448da6bb2a0e82b3d5cd4133e12..7924e3c597164f71056cd58dd167ee8b - this.timings.entityTick.startTiming(); // Spigot + //this.timings.entityTick.startTiming(); // Spigot // Purpur this.entityTickList.forEach((entity) -> { + entity.activatedPriorityReset = false; // Pufferfish - DAB if (!entity.isRemoved()) { if (false && this.shouldDiscardEntity(entity)) { // CraftBukkit - We prevent spawning in general, so this butchering is not needed entity.discard(); @@ -2458,13 +2514,19 @@ index ca56a0b596976448da6bb2a0e82b3d5cd4133e12..7924e3c597164f71056cd58dd167ee8b if (true || this.chunkSource.chunkMap.getDistanceManager().inEntityTickingRange(entity.chunkPosition().toLong())) { // Paper - now always true if in the ticking list Entity entity1 = entity.getVehicle(); -@@ -918,22 +939,21 @@ public class ServerLevel extends Level implements WorldGenLevel { +@@ -919,7 +940,7 @@ public class ServerLevel extends Level implements WorldGenLevel { entity.stopRiding(); } - gameprofilerfiller.push("tick"); + //gameprofilerfiller.push("tick"); // Purpur - this.guardEntityTick(this::tickNonPassenger, entity); + // Pufferfish start - copied from this.guardEntityTick + try { + this.tickNonPassenger(entity); // Pufferfish - changed +@@ -934,20 +955,20 @@ public class ServerLevel extends Level implements WorldGenLevel { + // Paper end + } + // Pufferfish end - gameprofilerfiller.pop(); + //gameprofilerfiller.pop(); // Purpur } @@ -2484,10 +2546,11 @@ index ca56a0b596976448da6bb2a0e82b3d5cd4133e12..7924e3c597164f71056cd58dd167ee8b + //gameprofilerfiller.push("entityManagement"); // Purpur //this.entityManager.tick(); // Paper - rewrite chunk system - gameprofilerfiller.pop(); ++ //gameprofilerfiller.pop(); // Purpur } @Override -@@ -951,6 +971,13 @@ public class ServerLevel extends Level implements WorldGenLevel { +@@ -965,6 +986,13 @@ public class ServerLevel extends Level implements WorldGenLevel { this.serverLevelData.setGameTime(i); this.serverLevelData.getScheduledEvents().tick(this.server, i); if (this.levelData.getGameRules().getBoolean(GameRules.RULE_DAYLIGHT)) { @@ -2501,7 +2564,7 @@ index ca56a0b596976448da6bb2a0e82b3d5cd4133e12..7924e3c597164f71056cd58dd167ee8b this.setDayTime(this.levelData.getDayTime() + 1L); } -@@ -959,7 +986,21 @@ public class ServerLevel extends Level implements WorldGenLevel { +@@ -973,7 +1001,21 @@ public class ServerLevel extends Level implements WorldGenLevel { public void setDayTime(long timeOfDay) { this.serverLevelData.setDayTime(timeOfDay); @@ -2523,7 +2586,16 @@ index ca56a0b596976448da6bb2a0e82b3d5cd4133e12..7924e3c597164f71056cd58dd167ee8b public void tickCustomSpawners(boolean spawnMonsters, boolean spawnAnimals) { Iterator iterator = this.customSpawners.iterator(); -@@ -992,9 +1033,9 @@ public class ServerLevel extends Level implements WorldGenLevel { +@@ -998,7 +1040,7 @@ 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(); // Pufferfish - moved to super ++ private final io.papermc.paper.util.math.ThreadUnsafeRandom randomTickRandom = new io.papermc.paper.util.math.ThreadUnsafeRandom(this.random.nextLong()); public net.minecraft.util.RandomSource getThreadUnsafeRandom() { return this.randomTickRandom; } // Pufferfish - moved to super // Purpur - dont break ABI + // Paper end + + private int currentIceAndSnowTick = 0; protected void resetIceAndSnowTick() { this.currentIceAndSnowTick = this.randomTickRandom.nextInt(16); } // Pufferfish +@@ -1008,9 +1050,9 @@ public class ServerLevel extends Level implements WorldGenLevel { boolean flag = this.isRaining(); int j = chunkcoordintpair.getMinBlockX(); int k = chunkcoordintpair.getMinBlockZ(); @@ -2534,8 +2606,8 @@ index ca56a0b596976448da6bb2a0e82b3d5cd4133e12..7924e3c597164f71056cd58dd167ee8b + //gameprofilerfiller.push("thunder"); // Purpur final BlockPos.MutableBlockPos blockposition = this.chunkTickMutablePosition; // Paper - use mutable to reduce allocation rate, final to force compile fail on change - if (!this.paperConfig().environment.disableThunder && flag && this.isThundering() && this.spigotConfig.thunderChance > 0 && this.random.nextInt(this.spigotConfig.thunderChance) == 0) { // Spigot // Paper - Option to disable thunder -@@ -1005,10 +1046,18 @@ public class ServerLevel extends Level implements WorldGenLevel { + if (!this.paperConfig().environment.disableThunder && flag && this.isThundering() && this.spigotConfig.thunderChance > 0 && /*this.random.nextInt(this.spigotConfig.thunderChance) == 0 &&*/ chunk.shouldDoLightning(this.random)) { // Spigot // Paper - Option to disable thunder // Pufferfish - replace random with shouldDoLightning +@@ -1021,10 +1063,18 @@ public class ServerLevel extends Level implements WorldGenLevel { boolean flag1 = this.getGameRules().getBoolean(GameRules.RULE_DOMOBSPAWNING) && this.random.nextDouble() < (double) difficultydamagescaler.getEffectiveDifficulty() * this.paperConfig().entities.spawning.skeletonHorseThunderSpawnChance.or(0.01D) && !this.getBlockState(blockposition.below()).is(Blocks.LIGHTNING_ROD); // Paper - Configurable spawn chances for skeleton horses if (flag1) { @@ -2556,7 +2628,7 @@ index ca56a0b596976448da6bb2a0e82b3d5cd4133e12..7924e3c597164f71056cd58dd167ee8b entityhorseskeleton.setAge(0); entityhorseskeleton.setPos((double) blockposition.getX(), (double) blockposition.getY(), (double) blockposition.getZ()); this.addFreshEntity(entityhorseskeleton, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.LIGHTNING); // CraftBukkit -@@ -1025,7 +1074,7 @@ public class ServerLevel extends Level implements WorldGenLevel { +@@ -1041,7 +1091,7 @@ public class ServerLevel extends Level implements WorldGenLevel { } } @@ -2565,7 +2637,7 @@ index ca56a0b596976448da6bb2a0e82b3d5cd4133e12..7924e3c597164f71056cd58dd167ee8b if (!this.paperConfig().environment.disableIceAndSnow) { // Paper - Option to disable ice and snow for (int l = 0; l < randomTickSpeed; ++l) { -@@ -1038,8 +1087,8 @@ public class ServerLevel extends Level implements WorldGenLevel { +@@ -1054,8 +1104,8 @@ public class ServerLevel extends Level implements WorldGenLevel { } } // Paper - Option to disable ice and snow @@ -2576,7 +2648,7 @@ index ca56a0b596976448da6bb2a0e82b3d5cd4133e12..7924e3c597164f71056cd58dd167ee8b if (randomTickSpeed > 0) { // Paper start - optimize random block ticking LevelChunkSection[] sections = chunk.getSections(); -@@ -1073,8 +1122,8 @@ public class ServerLevel extends Level implements WorldGenLevel { +@@ -1089,8 +1139,8 @@ public class ServerLevel extends Level implements WorldGenLevel { } // Paper end - optimise random block ticking @@ -2587,7 +2659,7 @@ index ca56a0b596976448da6bb2a0e82b3d5cd4133e12..7924e3c597164f71056cd58dd167ee8b } @VisibleForTesting -@@ -1132,7 +1181,7 @@ public class ServerLevel extends Level implements WorldGenLevel { +@@ -1148,7 +1198,7 @@ public class ServerLevel extends Level implements WorldGenLevel { return holder.is(PoiTypes.LIGHTNING_ROD); }, (blockposition1) -> { return blockposition1.getY() == this.getHeight(Heightmap.Types.WORLD_SURFACE, blockposition1.getX(), blockposition1.getZ()) - 1; @@ -2596,7 +2668,7 @@ index ca56a0b596976448da6bb2a0e82b3d5cd4133e12..7924e3c597164f71056cd58dd167ee8b return optional.map((blockposition1) -> { return blockposition1.above(1); -@@ -1181,11 +1230,27 @@ public class ServerLevel extends Level implements WorldGenLevel { +@@ -1197,11 +1247,27 @@ public class ServerLevel extends Level implements WorldGenLevel { if (this.canSleepThroughNights()) { if (!this.getServer().isSingleplayer() || this.getServer().isPublished()) { int i = this.getGameRules().getInt(GameRules.RULE_PLAYERS_SLEEPING_PERCENTAGE); @@ -2625,7 +2697,7 @@ index ca56a0b596976448da6bb2a0e82b3d5cd4133e12..7924e3c597164f71056cd58dd167ee8b ichatmutablecomponent = Component.translatable("sleep.players_sleeping", this.sleepStatus.amountSleeping(), this.sleepStatus.sleepersNeeded(i)); } -@@ -1325,6 +1390,7 @@ public class ServerLevel extends Level implements WorldGenLevel { +@@ -1341,6 +1407,7 @@ public class ServerLevel extends Level implements WorldGenLevel { @VisibleForTesting public void resetWeatherCycle() { // CraftBukkit start @@ -2633,7 +2705,7 @@ index ca56a0b596976448da6bb2a0e82b3d5cd4133e12..7924e3c597164f71056cd58dd167ee8b this.serverLevelData.setRaining(false, org.bukkit.event.weather.WeatherChangeEvent.Cause.SLEEP); // Paper - Add cause to Weather/ThunderChangeEvents // If we stop due to everyone sleeping we should reset the weather duration to some other random value. // Not that everyone ever manages to get the whole server to sleep at the same time.... -@@ -1332,6 +1398,7 @@ public class ServerLevel extends Level implements WorldGenLevel { +@@ -1348,6 +1415,7 @@ public class ServerLevel extends Level implements WorldGenLevel { this.serverLevelData.setRainTime(0); } // CraftBukkit end @@ -2641,7 +2713,7 @@ index ca56a0b596976448da6bb2a0e82b3d5cd4133e12..7924e3c597164f71056cd58dd167ee8b this.serverLevelData.setThundering(false, org.bukkit.event.weather.ThunderChangeEvent.Cause.SLEEP); // Paper - Add cause to Weather/ThunderChangeEvents // CraftBukkit start // If we stop due to everyone sleeping we should reset the weather duration to some other random value. -@@ -1399,24 +1466,24 @@ public class ServerLevel extends Level implements WorldGenLevel { +@@ -1415,24 +1483,24 @@ public class ServerLevel extends Level implements WorldGenLevel { // Spigot end // Paper start- timings final boolean isActive = org.spigotmc.ActivationRange.checkIfActive(entity); @@ -2674,7 +2746,7 @@ index ca56a0b596976448da6bb2a0e82b3d5cd4133e12..7924e3c597164f71056cd58dd167ee8b Iterator iterator = entity.getPassengers().iterator(); while (iterator.hasNext()) { -@@ -1439,17 +1506,17 @@ public class ServerLevel extends Level implements WorldGenLevel { +@@ -1455,17 +1523,17 @@ public class ServerLevel extends Level implements WorldGenLevel { if (passenger instanceof Player || this.entityTickList.contains(passenger)) { // Paper - EAR 2 final boolean isActive = org.spigotmc.ActivationRange.checkIfActive(passenger); @@ -2698,7 +2770,7 @@ index ca56a0b596976448da6bb2a0e82b3d5cd4133e12..7924e3c597164f71056cd58dd167ee8b // Paper start - EAR 2 if (isActive) { passenger.rideTick(); -@@ -1461,7 +1528,7 @@ public class ServerLevel extends Level implements WorldGenLevel { +@@ -1477,7 +1545,7 @@ public class ServerLevel extends Level implements WorldGenLevel { vehicle.positionRider(passenger); } // Paper end - EAR 2 @@ -2707,7 +2779,7 @@ index ca56a0b596976448da6bb2a0e82b3d5cd4133e12..7924e3c597164f71056cd58dd167ee8b Iterator iterator = passenger.getPassengers().iterator(); while (iterator.hasNext()) { -@@ -1470,7 +1537,7 @@ public class ServerLevel extends Level implements WorldGenLevel { +@@ -1486,7 +1554,7 @@ public class ServerLevel extends Level implements WorldGenLevel { this.tickPassenger(passenger, entity2); } @@ -2716,7 +2788,7 @@ index ca56a0b596976448da6bb2a0e82b3d5cd4133e12..7924e3c597164f71056cd58dd167ee8b } } else { passenger.stopRiding(); -@@ -1490,14 +1557,14 @@ public class ServerLevel extends Level implements WorldGenLevel { +@@ -1506,14 +1574,14 @@ public class ServerLevel extends Level implements WorldGenLevel { org.bukkit.Bukkit.getPluginManager().callEvent(new org.bukkit.event.world.WorldSaveEvent(getWorld())); } @@ -2734,7 +2806,7 @@ index ca56a0b596976448da6bb2a0e82b3d5cd4133e12..7924e3c597164f71056cd58dd167ee8b // Copied from save() // CraftBukkit start - moved from MinecraftServer.saveChunks -@@ -1509,7 +1576,7 @@ public class ServerLevel extends Level implements WorldGenLevel { +@@ -1525,7 +1593,7 @@ public class ServerLevel extends Level implements WorldGenLevel { this.convertable.saveDataTag(this.server.registryAccess(), this.serverLevelData, this.server.getPlayerList().getSingleplayerData()); } // CraftBukkit end @@ -2743,7 +2815,7 @@ index ca56a0b596976448da6bb2a0e82b3d5cd4133e12..7924e3c597164f71056cd58dd167ee8b } // Paper end - Incremental chunk and player saving -@@ -1523,7 +1590,7 @@ public class ServerLevel extends Level implements WorldGenLevel { +@@ -1539,7 +1607,7 @@ public class ServerLevel extends Level implements WorldGenLevel { if (!savingDisabled) { org.bukkit.Bukkit.getPluginManager().callEvent(new org.bukkit.event.world.WorldSaveEvent(this.getWorld())); // CraftBukkit @@ -2752,7 +2824,7 @@ index ca56a0b596976448da6bb2a0e82b3d5cd4133e12..7924e3c597164f71056cd58dd167ee8b if (progressListener != null) { progressListener.progressStartNoAbort(Component.translatable("menu.savingLevel")); } -@@ -1533,11 +1600,11 @@ public class ServerLevel extends Level implements WorldGenLevel { +@@ -1549,11 +1617,11 @@ public class ServerLevel extends Level implements WorldGenLevel { progressListener.progressStage(Component.translatable("menu.savingChunks")); } @@ -2767,7 +2839,7 @@ index ca56a0b596976448da6bb2a0e82b3d5cd4133e12..7924e3c597164f71056cd58dd167ee8b // Paper - rewrite chunk system - entity saving moved into ChunkHolder } else if (close) { chunkproviderserver.close(false); } // Paper - rewrite chunk system -@@ -2787,7 +2854,7 @@ public class ServerLevel extends Level implements WorldGenLevel { +@@ -2803,7 +2871,7 @@ public class ServerLevel extends Level implements WorldGenLevel { // Spigot Start if (entity.getBukkitEntity() instanceof org.bukkit.inventory.InventoryHolder && (!(entity instanceof ServerPlayer) || entity.getRemovalReason() != Entity.RemovalReason.KILLED)) { // SPIGOT-6876: closeInventory clears death message // Paper start - Fix merchant inventory not closing on entity removal @@ -3221,7 +3293,7 @@ index 308aef9c4933b2bcdd622a34b68efab4a220fe4d..b82a72775f9de5ad65ae46a8b97f93a7 private boolean checkIfClosed(long time) { diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -index b9b3277c8ed94e0cd30b20b9c00a33eaad48e5ac..0c3768bb183451e9dfe94bbc9b203bd79e474b99 100644 +index 309d173a74cb4966e567c0c1700c425a7f6203f4..87341fb1546bf01a8c14e687e841681b112ba23b 100644 --- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java +++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java @@ -332,6 +332,20 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl @@ -3275,7 +3347,7 @@ index b9b3277c8ed94e0cd30b20b9c00a33eaad48e5ac..0c3768bb183451e9dfe94bbc9b203bd7 return; } -@@ -1161,10 +1184,15 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl +@@ -1162,10 +1185,15 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl int maxBookPageSize = io.papermc.paper.configuration.GlobalConfiguration.get().itemValidation.bookSize.pageMax; double multiplier = Math.max(0.3D, Math.min(1D, io.papermc.paper.configuration.GlobalConfiguration.get().itemValidation.bookSize.totalMultiplier)); long byteAllowed = maxBookPageSize; @@ -3291,7 +3363,7 @@ index b9b3277c8ed94e0cd30b20b9c00a33eaad48e5ac..0c3768bb183451e9dfe94bbc9b203bd7 server.scheduleOnMain(() -> this.disconnect("Book too large!", org.bukkit.event.player.PlayerKickEvent.Cause.ILLEGAL_ACTION)); // Paper - kick event cause return; } -@@ -1188,6 +1216,7 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl +@@ -1189,6 +1217,7 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl if (byteTotal > byteAllowed) { ServerGamePacketListenerImpl.LOGGER.warn(this.player.getScoreboardName() + " tried to send too large of a book. Book Size: " + byteTotal + " - Allowed: "+ byteAllowed + " - Pages: " + pageList.size()); @@ -3299,7 +3371,7 @@ index b9b3277c8ed94e0cd30b20b9c00a33eaad48e5ac..0c3768bb183451e9dfe94bbc9b203bd7 server.scheduleOnMain(() -> this.disconnect("Book too large!", org.bukkit.event.player.PlayerKickEvent.Cause.ILLEGAL_ACTION)); // Paper - kick event cause return; } -@@ -1212,10 +1241,14 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl +@@ -1213,10 +1242,14 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl Objects.requireNonNull(list); stream.forEach(list::add); @@ -3316,7 +3388,7 @@ index b9b3277c8ed94e0cd30b20b9c00a33eaad48e5ac..0c3768bb183451e9dfe94bbc9b203bd7 }; this.filterTextPacket((List) list).thenAcceptAsync(consumer, this.server); -@@ -1223,13 +1256,18 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl +@@ -1224,13 +1257,18 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl } private void updateBookContents(List pages, int slotId) { @@ -3336,7 +3408,7 @@ index b9b3277c8ed94e0cd30b20b9c00a33eaad48e5ac..0c3768bb183451e9dfe94bbc9b203bd7 itemstack.set(DataComponents.WRITABLE_BOOK_CONTENT, new WritableBookContent(list1)); this.player.getInventory().setItem(slotId, CraftEventFactory.handleEditBookEvent(this.player, slotId, handItem, itemstack)); // CraftBukkit // Paper - Don't ignore result (see other callsite for handleEditBookEvent) -@@ -1237,6 +1275,11 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl +@@ -1238,6 +1276,11 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl } private void signBook(FilteredText title, List pages, int slotId) { @@ -3348,7 +3420,7 @@ index b9b3277c8ed94e0cd30b20b9c00a33eaad48e5ac..0c3768bb183451e9dfe94bbc9b203bd7 ItemStack itemstack = this.player.getInventory().getItem(slotId); if (itemstack.is(Items.WRITABLE_BOOK)) { -@@ -1244,10 +1287,10 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl +@@ -1245,10 +1288,10 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl itemstack1.remove(DataComponents.WRITABLE_BOOK_CONTENT); List> list1 = (List>) (List) pages.stream().map((filteredtext1) -> { // CraftBukkit - decompile error @@ -3361,7 +3433,7 @@ index b9b3277c8ed94e0cd30b20b9c00a33eaad48e5ac..0c3768bb183451e9dfe94bbc9b203bd7 CraftEventFactory.handleEditBookEvent(this.player, slotId, itemstack, itemstack1); // CraftBukkit this.player.getInventory().setItem(slotId, itemstack); // CraftBukkit - event factory updates the hand book } -@@ -1257,6 +1300,16 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl +@@ -1258,6 +1301,16 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl return this.player.isTextFilteringEnabled() ? Filterable.passThrough(message.filteredOrEmpty()) : Filterable.from(message); } @@ -3378,7 +3450,7 @@ index b9b3277c8ed94e0cd30b20b9c00a33eaad48e5ac..0c3768bb183451e9dfe94bbc9b203bd7 @Override public void handleEntityTagQuery(ServerboundEntityTagQueryPacket packet) { PacketUtils.ensureRunningOnSameThread(packet, this, this.player.serverLevel()); -@@ -1306,8 +1359,16 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl +@@ -1307,8 +1360,16 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl @Override public void handleMovePlayer(ServerboundMovePlayerPacket packet) { PacketUtils.ensureRunningOnSameThread(packet, this, this.player.serverLevel()); @@ -3396,7 +3468,7 @@ index b9b3277c8ed94e0cd30b20b9c00a33eaad48e5ac..0c3768bb183451e9dfe94bbc9b203bd7 } else { ServerLevel worldserver = this.player.serverLevel(); -@@ -1494,7 +1555,7 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl +@@ -1495,7 +1556,7 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl movedWrongly = true; if (event.getLogWarning()) // Paper end @@ -3405,7 +3477,7 @@ index b9b3277c8ed94e0cd30b20b9c00a33eaad48e5ac..0c3768bb183451e9dfe94bbc9b203bd7 } // Paper } -@@ -1562,6 +1623,8 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl +@@ -1563,6 +1624,8 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl this.lastYaw = to.getYaw(); this.lastPitch = to.getPitch(); @@ -3414,7 +3486,7 @@ index b9b3277c8ed94e0cd30b20b9c00a33eaad48e5ac..0c3768bb183451e9dfe94bbc9b203bd7 Location oldTo = to.clone(); PlayerMoveEvent event = new PlayerMoveEvent(player, from, to); this.cserver.getPluginManager().callEvent(event); -@@ -1603,6 +1666,13 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl +@@ -1604,6 +1667,13 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl this.player.resetCurrentImpulseContext(); } @@ -3428,7 +3500,7 @@ index b9b3277c8ed94e0cd30b20b9c00a33eaad48e5ac..0c3768bb183451e9dfe94bbc9b203bd7 this.player.checkMovementStatistics(this.player.getX() - d3, this.player.getY() - d4, this.player.getZ() - d5); this.lastGoodX = this.player.getX(); this.lastGoodY = this.player.getY(); -@@ -1642,6 +1712,15 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl +@@ -1643,6 +1713,15 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl return false; } // Paper end - optimise out extra getCubes @@ -3444,7 +3516,7 @@ index b9b3277c8ed94e0cd30b20b9c00a33eaad48e5ac..0c3768bb183451e9dfe94bbc9b203bd7 private boolean isPlayerCollidingWithAnythingNew(LevelReader world, AABB box, double newX, double newY, double newZ) { AABB axisalignedbb1 = this.player.getBoundingBox().move(newX - this.player.getX(), newY - this.player.getY(), newZ - this.player.getZ()); Iterable iterable = world.getCollisions(this.player, axisalignedbb1.deflate(9.999999747378752E-6D)); -@@ -1652,7 +1731,7 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl +@@ -1653,7 +1732,7 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl do { if (!iterator.hasNext()) { @@ -3453,7 +3525,7 @@ index b9b3277c8ed94e0cd30b20b9c00a33eaad48e5ac..0c3768bb183451e9dfe94bbc9b203bd7 } voxelshape1 = (VoxelShape) iterator.next(); -@@ -1990,6 +2069,7 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl +@@ -1991,6 +2070,7 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl boolean cancelled; if (movingobjectposition == null || movingobjectposition.getType() != HitResult.Type.BLOCK) { @@ -3461,7 +3533,7 @@ index b9b3277c8ed94e0cd30b20b9c00a33eaad48e5ac..0c3768bb183451e9dfe94bbc9b203bd7 org.bukkit.event.player.PlayerInteractEvent event = CraftEventFactory.callPlayerInteractEvent(this.player, Action.RIGHT_CLICK_AIR, itemstack, enumhand); cancelled = event.useItemInHand() == Event.Result.DENY; } else { -@@ -2759,6 +2839,7 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl +@@ -2760,6 +2840,7 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl AABB axisalignedbb = entity.getBoundingBox(); if (this.player.canInteractWithEntity(axisalignedbb, 1.0D)) { @@ -3469,7 +3541,7 @@ index b9b3277c8ed94e0cd30b20b9c00a33eaad48e5ac..0c3768bb183451e9dfe94bbc9b203bd7 packet.dispatch(new ServerboundInteractPacket.Handler() { private void performInteraction(InteractionHand enumhand, ServerGamePacketListenerImpl.EntityInteraction playerconnection_a, PlayerInteractEntityEvent event) { // CraftBukkit ItemStack itemstack = ServerGamePacketListenerImpl.this.player.getItemInHand(enumhand); -@@ -2772,6 +2853,8 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl +@@ -2773,6 +2854,8 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl ServerGamePacketListenerImpl.this.cserver.getPluginManager().callEvent(event); @@ -4080,7 +4152,7 @@ index f43bf280999ff3860cc702def50cc62b131eb1bd..66d9e99a351f5fc6cf58be3bee4397d9 } diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java -index 2bc85351e6e52f90da5fdb29d8d042a06132d742..3aae4fa4176c0bf170f4532ae187e3122c142a6a 100644 +index c246be0aa8e88d9c60260176fe4cbc076ddbcbcf..1a5aec1612947bbc1df78c0eeda9b98aa63cec94 100644 --- a/src/main/java/net/minecraft/world/entity/Entity.java +++ b/src/main/java/net/minecraft/world/entity/Entity.java @@ -163,7 +163,7 @@ import org.bukkit.plugin.PluginManager; @@ -4109,15 +4181,15 @@ index 2bc85351e6e52f90da5fdb29d8d042a06132d742..3aae4fa4176c0bf170f4532ae187e312 private float eyeHeight; public boolean isInPowderSnow; public boolean wasInPowderSnow; -@@ -426,6 +427,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess - private UUID originWorld; - public boolean freezeLocked = false; // Paper - Freeze Tick Lock API - public boolean fixedPose = false; // Paper - Expand Pose API +@@ -429,6 +430,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess + public boolean activatedPriorityReset = false; // Pufferfish - DAB + public int activatedPriority = gg.pufferfish.pufferfish.PufferfishConfig.maximumActivationPrio; // Pufferfish - DAB (golf score) + public final BlockPos.MutableBlockPos cachedBlockPos = new BlockPos.MutableBlockPos(); // Pufferfish - reduce entity allocations + public @Nullable Boolean immuneToFire = null; // Purpur - Fire immune API public void setOrigin(@javax.annotation.Nonnull Location location) { this.origin = location.toVector(); -@@ -557,6 +559,25 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -560,6 +562,25 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess return false; } @@ -4143,7 +4215,7 @@ index 2bc85351e6e52f90da5fdb29d8d042a06132d742..3aae4fa4176c0bf170f4532ae187e312 public final boolean hardCollides() { return this.hardCollides; } -@@ -577,7 +598,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -580,7 +601,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess this.bb = Entity.INITIAL_AABB; this.stuckSpeedMultiplier = Vec3.ZERO; this.nextStep = 1.0F; @@ -4152,7 +4224,16 @@ index 2bc85351e6e52f90da5fdb29d8d042a06132d742..3aae4fa4176c0bf170f4532ae187e312 this.remainingFireTicks = -this.getFireImmuneTicks(); this.fluidHeight = new Object2DoubleArrayMap(2); this.fluidOnEyes = new HashSet(); -@@ -877,7 +898,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -869,7 +890,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess + public void tick() { + // Pufferfish start - entity TTL + if (type != EntityType.PLAYER && type.ttl >= 0 && this.tickCount >= type.ttl) { +- discard(); ++ discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.DISCARD); // Purpur + return; + } + // Pufferfish end - entity TTL +@@ -886,7 +907,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess // CraftBukkit end public void baseTick() { @@ -4161,7 +4242,7 @@ index 2bc85351e6e52f90da5fdb29d8d042a06132d742..3aae4fa4176c0bf170f4532ae187e312 if (firstTick && this instanceof net.minecraft.world.entity.NeutralMob neutralMob) neutralMob.tickInitialPersistentAnger(level); // Paper - Prevent entity loading causing async lookups this.inBlockState = null; if (this.isPassenger() && this.getVehicle().isRemoved()) { -@@ -938,7 +959,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -947,7 +968,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess } this.firstTick = false; @@ -4170,7 +4251,7 @@ index 2bc85351e6e52f90da5fdb29d8d042a06132d742..3aae4fa4176c0bf170f4532ae187e312 } public void setSharedFlagOnFire(boolean onFire) { -@@ -947,10 +968,11 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -956,10 +977,11 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess public void checkBelowWorld() { // Paper start - Configurable nether ceiling damage @@ -4183,7 +4264,7 @@ index 2bc85351e6e52f90da5fdb29d8d042a06132d742..3aae4fa4176c0bf170f4532ae187e312 this.onBelowWorld(); } -@@ -1155,7 +1177,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -1164,7 +1186,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess } } @@ -4192,7 +4273,7 @@ index 2bc85351e6e52f90da5fdb29d8d042a06132d742..3aae4fa4176c0bf170f4532ae187e312 if (this.stuckSpeedMultiplier.lengthSqr() > 1.0E-7D) { movement = movement.multiply(this.stuckSpeedMultiplier); this.stuckSpeedMultiplier = Vec3.ZERO; -@@ -1164,7 +1186,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -1173,7 +1195,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess // Paper start - ignore movement changes while inactive. if (isTemporarilyActive && !(this instanceof ItemEntity) && movement == getDeltaMovement() && movementType == MoverType.SELF) { setDeltaMovement(Vec3.ZERO); @@ -4201,7 +4282,7 @@ index 2bc85351e6e52f90da5fdb29d8d042a06132d742..3aae4fa4176c0bf170f4532ae187e312 return; } // Paper end -@@ -1185,8 +1207,8 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -1194,8 +1216,8 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess this.setPos(this.getX() + vec3d1.x, this.getY() + vec3d1.y, this.getZ() + vec3d1.z); } @@ -4212,7 +4293,7 @@ index 2bc85351e6e52f90da5fdb29d8d042a06132d742..3aae4fa4176c0bf170f4532ae187e312 boolean flag = !Mth.equal(movement.x, vec3d1.x); boolean flag1 = !Mth.equal(movement.z, vec3d1.z); -@@ -1205,7 +1227,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -1214,7 +1236,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess this.checkFallDamage(vec3d1.y, this.onGround(), iblockdata, blockposition); if (this.isRemoved()) { @@ -4221,7 +4302,7 @@ index 2bc85351e6e52f90da5fdb29d8d042a06132d742..3aae4fa4176c0bf170f4532ae187e312 } else { if (this.horizontalCollision) { Vec3 vec3d2 = this.getDeltaMovement(); -@@ -1343,7 +1365,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -1352,7 +1374,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess this.setRemainingFireTicks(-this.getFireImmuneTicks()); } @@ -4230,7 +4311,7 @@ index 2bc85351e6e52f90da5fdb29d8d042a06132d742..3aae4fa4176c0bf170f4532ae187e312 } } // Paper start - detailed watchdog information -@@ -1858,7 +1880,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -1867,7 +1889,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess } public boolean fireImmune() { @@ -4239,7 +4320,7 @@ index 2bc85351e6e52f90da5fdb29d8d042a06132d742..3aae4fa4176c0bf170f4532ae187e312 } public boolean causeFallDamage(float fallDistance, float damageMultiplier, DamageSource damageSource) { -@@ -1931,7 +1953,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -1940,7 +1962,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess return this.isInWater() || flag; } @@ -4248,7 +4329,7 @@ index 2bc85351e6e52f90da5fdb29d8d042a06132d742..3aae4fa4176c0bf170f4532ae187e312 Entity entity = this.getVehicle(); if (entity instanceof Boat entityboat) { -@@ -2555,6 +2577,11 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -2564,6 +2586,11 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess nbttagcompound.putBoolean("Paper.FreezeLock", true); } // Paper end @@ -4260,7 +4341,7 @@ index 2bc85351e6e52f90da5fdb29d8d042a06132d742..3aae4fa4176c0bf170f4532ae187e312 return nbttagcompound; } catch (Throwable throwable) { CrashReport crashreport = CrashReport.forThrowable(throwable, "Saving entity NBT"); -@@ -2702,6 +2729,11 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -2711,6 +2738,11 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess freezeLocked = nbt.getBoolean("Paper.FreezeLock"); } // Paper end @@ -4272,7 +4353,7 @@ index 2bc85351e6e52f90da5fdb29d8d042a06132d742..3aae4fa4176c0bf170f4532ae187e312 } catch (Throwable throwable) { CrashReport crashreport = CrashReport.forThrowable(throwable, "Loading entity NBT"); -@@ -3080,6 +3112,13 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -3089,6 +3121,13 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess this.passengers = ImmutableList.copyOf(list); } @@ -4286,7 +4367,7 @@ index 2bc85351e6e52f90da5fdb29d8d042a06132d742..3aae4fa4176c0bf170f4532ae187e312 this.gameEvent(GameEvent.ENTITY_MOUNT, passenger); } } -@@ -3119,6 +3158,14 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -3128,6 +3167,14 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess return false; } // CraftBukkit end @@ -4301,7 +4382,7 @@ index 2bc85351e6e52f90da5fdb29d8d042a06132d742..3aae4fa4176c0bf170f4532ae187e312 if (this.passengers.size() == 1 && this.passengers.get(0) == entity) { this.passengers = ImmutableList.of(); } else { -@@ -3197,12 +3244,15 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -3206,12 +3253,15 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess return Vec3.directionFromRotation(this.getRotationVector()); } @@ -4318,7 +4399,7 @@ index 2bc85351e6e52f90da5fdb29d8d042a06132d742..3aae4fa4176c0bf170f4532ae187e312 } this.isInsidePortal = true; -@@ -3220,7 +3270,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -3229,7 +3279,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess ServerLevel worldserver1 = minecraftserver.getLevel(resourcekey); if (true && !this.isPassenger() && this.portalTime++ >= i) { // CraftBukkit @@ -4327,7 +4408,7 @@ index 2bc85351e6e52f90da5fdb29d8d042a06132d742..3aae4fa4176c0bf170f4532ae187e312 this.portalTime = i; // Paper start - Add EntityPortalReadyEvent io.papermc.paper.event.entity.EntityPortalReadyEvent event = new io.papermc.paper.event.entity.EntityPortalReadyEvent(this.getBukkitEntity(), worldserver1 == null ? null : worldserver1.getWorld(), org.bukkit.PortalType.NETHER); -@@ -3238,7 +3288,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -3247,7 +3297,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess } } // Paper - Add EntityPortalReadyEvent // CraftBukkit end @@ -4336,7 +4417,7 @@ index 2bc85351e6e52f90da5fdb29d8d042a06132d742..3aae4fa4176c0bf170f4532ae187e312 } this.isInsidePortal = false; -@@ -3429,7 +3479,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -3438,7 +3488,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess } public int getMaxAirSupply() { @@ -4345,7 +4426,7 @@ index 2bc85351e6e52f90da5fdb29d8d042a06132d742..3aae4fa4176c0bf170f4532ae187e312 } public int getAirSupply() { -@@ -3698,14 +3748,14 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -3707,14 +3757,14 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess } // Paper end - Fix item duplication and teleport issues if (this.level() instanceof ServerLevel && !this.isRemoved()) { @@ -4362,7 +4443,7 @@ index 2bc85351e6e52f90da5fdb29d8d042a06132d742..3aae4fa4176c0bf170f4532ae187e312 PortalInfo shapedetectorshape = (location == null) ? this.findDimensionEntryPoint(worldserver) : new PortalInfo(new Vec3(location.x(), location.y(), location.z()), Vec3.ZERO, this.yRot, this.xRot, worldserver, null); // CraftBukkit if (shapedetectorshape == null) { -@@ -3744,7 +3794,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -3753,7 +3803,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess this.unRide(); // CraftBukkit end @@ -4371,7 +4452,7 @@ index 2bc85351e6e52f90da5fdb29d8d042a06132d742..3aae4fa4176c0bf170f4532ae187e312 // Paper start - Fix item duplication and teleport issues if (this instanceof Mob) { ((Mob) this).dropLeash(true, true); // Paper drop lead -@@ -3771,10 +3821,10 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -3780,10 +3830,10 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess } this.removeAfterChangingDimensions(); @@ -4384,7 +4465,7 @@ index 2bc85351e6e52f90da5fdb29d8d042a06132d742..3aae4fa4176c0bf170f4532ae187e312 return entity; } } else { -@@ -3894,7 +3944,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -3903,7 +3953,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess } public boolean canChangeDimensions() { @@ -4393,7 +4474,7 @@ index 2bc85351e6e52f90da5fdb29d8d042a06132d742..3aae4fa4176c0bf170f4532ae187e312 } public float getBlockExplosionResistance(Explosion explosion, BlockGetter world, BlockPos pos, BlockState blockState, FluidState fluidState, float max) { -@@ -4190,6 +4240,20 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -4199,6 +4249,20 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess return SlotAccess.NULL; } @@ -4414,7 +4495,7 @@ index 2bc85351e6e52f90da5fdb29d8d042a06132d742..3aae4fa4176c0bf170f4532ae187e312 @Override public void sendSystemMessage(Component message) {} -@@ -4477,6 +4541,12 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -4486,6 +4550,12 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess this.yRotO = this.getYRot(); } @@ -4425,9 +4506,9 @@ index 2bc85351e6e52f90da5fdb29d8d042a06132d742..3aae4fa4176c0bf170f4532ae187e312 + // Purpur end + 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; -@@ -4823,7 +4893,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -4884,7 +4954,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess } public float maxUpStep() { @@ -4436,10 +4517,11 @@ index 2bc85351e6e52f90da5fdb29d8d042a06132d742..3aae4fa4176c0bf170f4532ae187e312 } public void onExplosionHit(@Nullable Entity entity) {} -@@ -4995,4 +5065,44 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -5056,4 +5126,46 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess return ((net.minecraft.server.level.ServerChunkCache) level.getChunkSource()).isPositionTicking(this); } // Paper end - Expose entity id counter ++ + // Purpur start + @Nullable + private Player rider = null; @@ -4480,6 +4562,7 @@ index 2bc85351e6e52f90da5fdb29d8d042a06132d742..3aae4fa4176c0bf170f4532ae187e312 + return false; + } + // Purpur end ++ } diff --git a/src/main/java/net/minecraft/world/entity/EntitySelector.java b/src/main/java/net/minecraft/world/entity/EntitySelector.java index d8cc5614502db7025349e085381b6b32ad32296a..f1b9e83206cc67e6ef29ebe088351b0aaa5eb349 100644 @@ -4494,10 +4577,10 @@ index d8cc5614502db7025349e085381b6b32ad32296a..f1b9e83206cc67e6ef29ebe088351b0a private EntitySelector() {} // Paper start - Affects Spawning API diff --git a/src/main/java/net/minecraft/world/entity/EntityType.java b/src/main/java/net/minecraft/world/entity/EntityType.java -index a46bf73c608641bf1f00fd55242de71a0f2ee06e..8120f39a9689dae1233b243b74825e9ff110eac3 100644 +index e6edbe6177b168d85759bd9c414dc87ea8a394fe..32a1b5a1d01fd4dc603a76fde259f3a0d4749fad 100644 --- a/src/main/java/net/minecraft/world/entity/EntityType.java +++ b/src/main/java/net/minecraft/world/entity/EntityType.java -@@ -322,7 +322,8 @@ public class EntityType implements FeatureElement, EntityTypeT +@@ -324,7 +324,8 @@ public class EntityType implements FeatureElement, EntityTypeT private Component description; @Nullable private ResourceKey lootTable; @@ -4507,7 +4590,7 @@ index a46bf73c608641bf1f00fd55242de71a0f2ee06e..8120f39a9689dae1233b243b74825e9f private final float spawnDimensionsScale; private final FeatureFlagSet requiredFeatures; -@@ -330,6 +331,16 @@ public class EntityType implements FeatureElement, EntityTypeT +@@ -332,6 +333,16 @@ public class EntityType implements FeatureElement, EntityTypeT return (EntityType) Registry.register(BuiltInRegistries.ENTITY_TYPE, id, (EntityType) type.build(id)); // CraftBukkit - decompile error } @@ -4524,7 +4607,7 @@ index a46bf73c608641bf1f00fd55242de71a0f2ee06e..8120f39a9689dae1233b243b74825e9f public static ResourceLocation getKey(EntityType type) { return BuiltInRegistries.ENTITY_TYPE.getKey(type); } -@@ -537,6 +548,16 @@ public class EntityType implements FeatureElement, EntityTypeT +@@ -539,6 +550,16 @@ public class EntityType implements FeatureElement, EntityTypeT return this.category; } @@ -4541,7 +4624,7 @@ index a46bf73c608641bf1f00fd55242de71a0f2ee06e..8120f39a9689dae1233b243b74825e9f public String getDescriptionId() { if (this.descriptionId == null) { this.descriptionId = Util.makeDescriptionId("entity", BuiltInRegistries.ENTITY_TYPE.getKey(this)); -@@ -604,6 +625,12 @@ public class EntityType implements FeatureElement, EntityTypeT +@@ -606,6 +627,12 @@ public class EntityType implements FeatureElement, EntityTypeT entity.load(nbt); }, () -> { EntityType.LOGGER.warn("Skipping Entity with id {}", nbt.getString("id")); @@ -4639,10 +4722,10 @@ index 09fdea983772612ef3fff6b2da3cf469a34e4ec0..3e2ea26c23e88c395856b65001f2895d protected ParticleOptions getInkParticle() { return ParticleTypes.GLOW_SQUID_INK; diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java -index 517e10c3d8b1549cd30fd0e7cf2bcb35e88eb8ed..3776b9bf7abce6b3024c50f04741031ef0e8e9c4 100644 +index 6bc0f6256851b1aa3c0e590692a949cd1e9c337d..12841498369db7020bb844040bc5464745312082 100644 --- a/src/main/java/net/minecraft/world/entity/LivingEntity.java +++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java -@@ -229,9 +229,9 @@ public abstract class LivingEntity extends Entity implements Attackable { +@@ -228,9 +228,9 @@ public abstract class LivingEntity extends Entity implements Attackable { protected int deathScore; public float lastHurt; public boolean jumping; @@ -4655,7 +4738,7 @@ index 517e10c3d8b1549cd30fd0e7cf2bcb35e88eb8ed..3776b9bf7abce6b3024c50f04741031e protected int lerpSteps; protected double lerpX; protected double lerpY; -@@ -274,6 +274,7 @@ public abstract class LivingEntity extends Entity implements Attackable { +@@ -273,6 +273,7 @@ public abstract class LivingEntity extends Entity implements Attackable { public org.bukkit.craftbukkit.entity.CraftLivingEntity getBukkitLivingEntity() { return (org.bukkit.craftbukkit.entity.CraftLivingEntity) super.getBukkitEntity(); } // Paper public boolean silentDeath = false; // Paper - mark entity as dying silently for cancellable death event public net.kyori.adventure.util.TriState frictionState = net.kyori.adventure.util.TriState.NOT_SET; // Paper - Friction API @@ -4663,7 +4746,7 @@ index 517e10c3d8b1549cd30fd0e7cf2bcb35e88eb8ed..3776b9bf7abce6b3024c50f04741031e @Override public float getBukkitYaw() { -@@ -300,7 +301,8 @@ public abstract class LivingEntity extends Entity implements Attackable { +@@ -299,7 +300,8 @@ public abstract class LivingEntity extends Entity implements Attackable { this.useItem = ItemStack.EMPTY; this.lastClimbablePos = Optional.empty(); this.appliedScale = 1.0F; @@ -4673,7 +4756,7 @@ index 517e10c3d8b1549cd30fd0e7cf2bcb35e88eb8ed..3776b9bf7abce6b3024c50f04741031e this.craftAttributes = new CraftAttributeMap(this.attributes); // CraftBukkit // CraftBukkit - setHealth(getMaxHealth()) inlined and simplified to skip the instanceof check for EntityPlayer, as getBukkitEntity() is not initialized in constructor this.entityData.set(LivingEntity.DATA_HEALTH_ID, (float) this.getAttribute(Attributes.MAX_HEALTH).getValue()); -@@ -315,6 +317,8 @@ public abstract class LivingEntity extends Entity implements Attackable { +@@ -314,6 +316,8 @@ public abstract class LivingEntity extends Entity implements Attackable { this.brain = this.makeBrain(new Dynamic(dynamicopsnbt, (Tag) dynamicopsnbt.createMap((Map) ImmutableMap.of(dynamicopsnbt.createString("memories"), (Tag) dynamicopsnbt.emptyMap())))); } @@ -4682,7 +4765,7 @@ index 517e10c3d8b1549cd30fd0e7cf2bcb35e88eb8ed..3776b9bf7abce6b3024c50f04741031e public Brain getBrain() { return this.brain; } -@@ -350,6 +354,7 @@ public abstract class LivingEntity extends Entity implements Attackable { +@@ -349,6 +353,7 @@ public abstract class LivingEntity extends Entity implements Attackable { public static AttributeSupplier.Builder createLivingAttributes() { return AttributeSupplier.builder().add(Attributes.MAX_HEALTH).add(Attributes.KNOCKBACK_RESISTANCE).add(Attributes.MOVEMENT_SPEED).add(Attributes.ARMOR).add(Attributes.ARMOR_TOUGHNESS).add(Attributes.MAX_ABSORPTION).add(Attributes.STEP_HEIGHT).add(Attributes.SCALE).add(Attributes.GRAVITY).add(Attributes.SAFE_FALL_DISTANCE).add(Attributes.FALL_DAMAGE_MULTIPLIER).add(Attributes.JUMP_STRENGTH); } @@ -4690,7 +4773,7 @@ index 517e10c3d8b1549cd30fd0e7cf2bcb35e88eb8ed..3776b9bf7abce6b3024c50f04741031e @Override protected void checkFallDamage(double heightDifference, boolean onGround, BlockState state, BlockPos landedPosition) { -@@ -418,7 +423,7 @@ public abstract class LivingEntity extends Entity implements Attackable { +@@ -417,7 +422,7 @@ public abstract class LivingEntity extends Entity implements Attackable { } super.baseTick(); @@ -4699,7 +4782,7 @@ index 517e10c3d8b1549cd30fd0e7cf2bcb35e88eb8ed..3776b9bf7abce6b3024c50f04741031e if (this.fireImmune() || this.level().isClientSide) { this.clearFire(); } -@@ -436,6 +441,7 @@ public abstract class LivingEntity extends Entity implements Attackable { +@@ -435,6 +440,7 @@ public abstract class LivingEntity extends Entity implements Attackable { double d1 = this.level().getWorldBorder().getDamagePerBlock(); if (d1 > 0.0D) { @@ -4707,7 +4790,7 @@ index 517e10c3d8b1549cd30fd0e7cf2bcb35e88eb8ed..3776b9bf7abce6b3024c50f04741031e this.hurt(this.damageSources().outOfBorder(), (float) Math.max(1, Mth.floor(-d0 * d1))); } } -@@ -447,7 +453,7 @@ public abstract class LivingEntity extends Entity implements Attackable { +@@ -446,7 +452,7 @@ public abstract class LivingEntity extends Entity implements Attackable { if (flag1) { this.setAirSupply(this.decreaseAirSupply(this.getAirSupply())); @@ -4716,7 +4799,7 @@ index 517e10c3d8b1549cd30fd0e7cf2bcb35e88eb8ed..3776b9bf7abce6b3024c50f04741031e this.setAirSupply(0); Vec3 vec3d = this.getDeltaMovement(); -@@ -459,7 +465,7 @@ public abstract class LivingEntity extends Entity implements Attackable { +@@ -458,7 +464,7 @@ public abstract class LivingEntity extends Entity implements Attackable { this.level().addParticle(ParticleTypes.BUBBLE, this.getX() + d2, this.getY() + d3, this.getZ() + d4, vec3d.x, vec3d.y, vec3d.z); } @@ -4725,7 +4808,7 @@ index 517e10c3d8b1549cd30fd0e7cf2bcb35e88eb8ed..3776b9bf7abce6b3024c50f04741031e } } -@@ -520,7 +526,7 @@ public abstract class LivingEntity extends Entity implements Attackable { +@@ -519,7 +525,7 @@ public abstract class LivingEntity extends Entity implements Attackable { this.yHeadRotO = this.yHeadRot; this.yRotO = this.getYRot(); this.xRotO = this.getXRot(); @@ -4734,7 +4817,7 @@ index 517e10c3d8b1549cd30fd0e7cf2bcb35e88eb8ed..3776b9bf7abce6b3024c50f04741031e } public boolean canSpawnSoulSpeedParticle() { -@@ -837,6 +843,7 @@ public abstract class LivingEntity extends Entity implements Attackable { +@@ -836,6 +842,7 @@ public abstract class LivingEntity extends Entity implements Attackable { dataresult.resultOrPartial(logger::error).ifPresent((nbtbase) -> { nbt.put("Brain", nbtbase); }); @@ -4742,7 +4825,7 @@ index 517e10c3d8b1549cd30fd0e7cf2bcb35e88eb8ed..3776b9bf7abce6b3024c50f04741031e } @Override -@@ -924,6 +931,11 @@ public abstract class LivingEntity extends Entity implements Attackable { +@@ -923,6 +930,11 @@ public abstract class LivingEntity extends Entity implements Attackable { this.brain = this.makeBrain(new Dynamic(NbtOps.INSTANCE, nbt.get("Brain"))); } @@ -4754,7 +4837,7 @@ index 517e10c3d8b1549cd30fd0e7cf2bcb35e88eb8ed..3776b9bf7abce6b3024c50f04741031e } // CraftBukkit start -@@ -1059,9 +1071,31 @@ public abstract class LivingEntity extends Entity implements Attackable { +@@ -1058,9 +1070,31 @@ public abstract class LivingEntity extends Entity implements Attackable { ItemStack itemstack = this.getItemBySlot(EquipmentSlot.HEAD); EntityType entitytypes = entity.getType(); @@ -4788,7 +4871,7 @@ index 517e10c3d8b1549cd30fd0e7cf2bcb35e88eb8ed..3776b9bf7abce6b3024c50f04741031e } return d0; -@@ -1120,6 +1154,7 @@ public abstract class LivingEntity extends Entity implements Attackable { +@@ -1119,6 +1153,7 @@ public abstract class LivingEntity extends Entity implements Attackable { for (flag = false; iterator.hasNext(); flag = true) { // CraftBukkit start MobEffectInstance effect = (MobEffectInstance) iterator.next(); @@ -4796,7 +4879,7 @@ index 517e10c3d8b1549cd30fd0e7cf2bcb35e88eb8ed..3776b9bf7abce6b3024c50f04741031e EntityPotionEffectEvent event = CraftEventFactory.callEntityPotionEffectChangeEvent(this, effect, null, cause, EntityPotionEffectEvent.Action.CLEARED); if (event.isCancelled()) { continue; -@@ -1527,13 +1562,13 @@ public abstract class LivingEntity extends Entity implements Attackable { +@@ -1539,13 +1574,13 @@ public abstract class LivingEntity extends Entity implements Attackable { if (entity1 instanceof net.minecraft.world.entity.player.Player) { net.minecraft.world.entity.player.Player entityhuman = (net.minecraft.world.entity.player.Player) entity1; @@ -4812,7 +4895,7 @@ index 517e10c3d8b1549cd30fd0e7cf2bcb35e88eb8ed..3776b9bf7abce6b3024c50f04741031e LivingEntity entityliving2 = entitywolf.getOwner(); if (entityliving2 instanceof net.minecraft.world.entity.player.Player) { -@@ -1648,6 +1683,18 @@ public abstract class LivingEntity extends Entity implements Attackable { +@@ -1660,6 +1695,18 @@ public abstract class LivingEntity extends Entity implements Attackable { } } @@ -4831,7 +4914,7 @@ index 517e10c3d8b1549cd30fd0e7cf2bcb35e88eb8ed..3776b9bf7abce6b3024c50f04741031e org.bukkit.inventory.EquipmentSlot handSlot = (hand != null) ? org.bukkit.craftbukkit.CraftEquipmentSlot.getHand(hand) : null; EntityResurrectEvent event = new EntityResurrectEvent((org.bukkit.entity.LivingEntity) this.getBukkitEntity(), handSlot); event.setCancelled(itemstack == null); -@@ -1814,7 +1861,7 @@ public abstract class LivingEntity extends Entity implements Attackable { +@@ -1826,7 +1873,7 @@ public abstract class LivingEntity extends Entity implements Attackable { boolean flag = false; if (this.dead && adversary instanceof WitherBoss) { // Paper @@ -4840,7 +4923,7 @@ index 517e10c3d8b1549cd30fd0e7cf2bcb35e88eb8ed..3776b9bf7abce6b3024c50f04741031e BlockPos blockposition = this.blockPosition(); BlockState iblockdata = Blocks.WITHER_ROSE.defaultBlockState(); -@@ -1860,6 +1907,7 @@ public abstract class LivingEntity extends Entity implements Attackable { +@@ -1872,6 +1919,7 @@ public abstract class LivingEntity extends Entity implements Attackable { this.dropEquipment(); // CraftBukkit - from below if (this.shouldDropLoot() && this.level().getGameRules().getBoolean(GameRules.RULE_DOMOBLOOT)) { @@ -4848,7 +4931,7 @@ index 517e10c3d8b1549cd30fd0e7cf2bcb35e88eb8ed..3776b9bf7abce6b3024c50f04741031e this.dropFromLootTable(source, flag); // Paper start final boolean prev = this.clearEquipmentSlots; -@@ -1868,6 +1916,7 @@ public abstract class LivingEntity extends Entity implements Attackable { +@@ -1880,6 +1928,7 @@ public abstract class LivingEntity extends Entity implements Attackable { // Paper end this.dropCustomDeathLoot(source, i, flag); this.clearEquipmentSlots = prev; // Paper @@ -4856,7 +4939,7 @@ index 517e10c3d8b1549cd30fd0e7cf2bcb35e88eb8ed..3776b9bf7abce6b3024c50f04741031e } // CraftBukkit start - Call death event // Paper start - call advancement triggers with correct entity equipment org.bukkit.event.entity.EntityDeathEvent deathEvent = CraftEventFactory.callEntityDeathEvent(this, source, this.drops, () -> { -@@ -2363,6 +2412,21 @@ public abstract class LivingEntity extends Entity implements Attackable { +@@ -2389,6 +2438,21 @@ public abstract class LivingEntity extends Entity implements Attackable { } } @@ -4878,7 +4961,7 @@ index 517e10c3d8b1549cd30fd0e7cf2bcb35e88eb8ed..3776b9bf7abce6b3024c50f04741031e if (f > 0 || !human) { if (human) { // PAIL: Be sure to drag all this code from the EntityHuman subclass each update. -@@ -2586,7 +2650,7 @@ public abstract class LivingEntity extends Entity implements Attackable { +@@ -2612,7 +2676,7 @@ public abstract class LivingEntity extends Entity implements Attackable { @Override protected void onBelowWorld() { @@ -4887,7 +4970,7 @@ index 517e10c3d8b1549cd30fd0e7cf2bcb35e88eb8ed..3776b9bf7abce6b3024c50f04741031e } protected void updateSwingTime() { -@@ -2781,7 +2845,7 @@ public abstract class LivingEntity extends Entity implements Attackable { +@@ -2807,7 +2871,7 @@ public abstract class LivingEntity extends Entity implements Attackable { } protected long lastJumpTime = 0L; // Paper - Prevent excessive velocity through repeated crits @@ -4896,7 +4979,7 @@ index 517e10c3d8b1549cd30fd0e7cf2bcb35e88eb8ed..3776b9bf7abce6b3024c50f04741031e float f = this.getJumpPower(); if (f > 1.0E-5F) { -@@ -2941,6 +3005,7 @@ public abstract class LivingEntity extends Entity implements Attackable { +@@ -2967,6 +3031,7 @@ public abstract class LivingEntity extends Entity implements Attackable { if (f3 > 0.0F) { this.playSound(this.getFallDamageSound((int) f3), 1.0F, 1.0F); @@ -4904,7 +4987,7 @@ index 517e10c3d8b1549cd30fd0e7cf2bcb35e88eb8ed..3776b9bf7abce6b3024c50f04741031e this.hurt(this.damageSources().flyIntoWall(), f3); } } -@@ -3163,10 +3228,10 @@ public abstract class LivingEntity extends Entity implements Attackable { +@@ -3189,10 +3254,10 @@ public abstract class LivingEntity extends Entity implements Attackable { } this.run += (f3 - this.run) * 0.3F; @@ -4918,7 +5001,7 @@ index 517e10c3d8b1549cd30fd0e7cf2bcb35e88eb8ed..3776b9bf7abce6b3024c50f04741031e // Paper start - stop large pitch and yaw changes from crashing the server this.yRotO += Math.round((this.getYRot() - this.yRotO) / 360.0F) * 360.0F; -@@ -3178,7 +3243,7 @@ public abstract class LivingEntity extends Entity implements Attackable { +@@ -3204,7 +3269,7 @@ public abstract class LivingEntity extends Entity implements Attackable { this.yHeadRotO += Math.round((this.yHeadRot - this.yHeadRotO) / 360.0F) * 360.0F; // Paper end @@ -4927,7 +5010,7 @@ index 517e10c3d8b1549cd30fd0e7cf2bcb35e88eb8ed..3776b9bf7abce6b3024c50f04741031e this.animStep += f2; if (this.isFallFlying()) { ++this.fallFlyTicks; -@@ -3401,19 +3466,19 @@ public abstract class LivingEntity extends Entity implements Attackable { +@@ -3427,19 +3492,19 @@ public abstract class LivingEntity extends Entity implements Attackable { } this.setDeltaMovement(d0, d1, d2); @@ -4952,7 +5035,7 @@ index 517e10c3d8b1549cd30fd0e7cf2bcb35e88eb8ed..3776b9bf7abce6b3024c50f04741031e if (this.jumping && this.isAffectedByFluids()) { double d3; -@@ -3440,8 +3505,8 @@ public abstract class LivingEntity extends Entity implements Attackable { +@@ -3466,8 +3531,8 @@ public abstract class LivingEntity extends Entity implements Attackable { this.noJumpDelay = 0; } @@ -4963,18 +5046,18 @@ index 517e10c3d8b1549cd30fd0e7cf2bcb35e88eb8ed..3776b9bf7abce6b3024c50f04741031e this.xxa *= 0.98F; this.zza *= 0.98F; this.updateFallFlying(); -@@ -3466,8 +3531,8 @@ public abstract class LivingEntity extends Entity implements Attackable { +@@ -3492,8 +3557,8 @@ public abstract class LivingEntity extends Entity implements Attackable { this.travel(vec3d1); } - this.level().getProfiler().pop(); - this.level().getProfiler().push("freezing"); + //this.level().getProfiler().pop(); // Purpur -+ //this.level().getProfiler().push("freezing"); // Purpur ++ //this.level().getProfiler().push("freezing"); // Purpur.level().getProfiler().push("freezing"); if (!this.level().isClientSide && !this.isDeadOrDying() && !this.freezeLocked) { // Paper - Freeze Tick Lock API int i = this.getTicksFrozen(); -@@ -3484,18 +3549,20 @@ public abstract class LivingEntity extends Entity implements Attackable { +@@ -3510,18 +3575,20 @@ public abstract class LivingEntity extends Entity implements Attackable { this.hurt(this.damageSources().freeze(), 1.0F); } @@ -5000,7 +5083,7 @@ index 517e10c3d8b1549cd30fd0e7cf2bcb35e88eb8ed..3776b9bf7abce6b3024c50f04741031e Location from = new Location(this.level().getWorld(), this.xo, this.yo, this.zo, this.yRotO, this.xRotO); Location to = new Location(this.level().getWorld(), this.getX(), this.getY(), this.getZ(), this.getYRot(), this.getXRot()); io.papermc.paper.event.entity.EntityMoveEvent event = new io.papermc.paper.event.entity.EntityMoveEvent(this.getBukkitLivingEntity(), from, to.clone()); -@@ -3505,12 +3572,48 @@ public abstract class LivingEntity extends Entity implements Attackable { +@@ -3531,12 +3598,48 @@ public abstract class LivingEntity extends Entity implements Attackable { this.absMoveTo(event.getTo().getX(), event.getTo().getY(), event.getTo().getZ(), event.getTo().getYaw(), event.getTo().getPitch()); } } @@ -5049,7 +5132,7 @@ index 517e10c3d8b1549cd30fd0e7cf2bcb35e88eb8ed..3776b9bf7abce6b3024c50f04741031e } public boolean isSensitiveToWater() { -@@ -3531,7 +3634,16 @@ public abstract class LivingEntity extends Entity implements Attackable { +@@ -3557,7 +3660,16 @@ public abstract class LivingEntity extends Entity implements Attackable { int j = i / 10; if (j % 2 == 0) { @@ -5068,7 +5151,7 @@ index 517e10c3d8b1549cd30fd0e7cf2bcb35e88eb8ed..3776b9bf7abce6b3024c50f04741031e this.gameEvent(GameEvent.ELYTRA_GLIDE); diff --git a/src/main/java/net/minecraft/world/entity/Mob.java b/src/main/java/net/minecraft/world/entity/Mob.java -index e89f9c3e887601d8461eb967ae0bf582b672f631..56da8a4600688efd1987d82d4fcad1757e33f4f2 100644 +index 9aa42d6ce2c9f64142dd6f35a34319983bf7f3e4..0d138d36b86e4d31b28455640a82b6097e0a2b36 100644 --- a/src/main/java/net/minecraft/world/entity/Mob.java +++ b/src/main/java/net/minecraft/world/entity/Mob.java @@ -75,6 +75,7 @@ import net.minecraft.world.item.SpawnEggItem; @@ -5098,7 +5181,7 @@ index e89f9c3e887601d8461eb967ae0bf582b672f631..56da8a4600688efd1987d82d4fcad175 this.jumpControl = new JumpControl(this); this.bodyRotationControl = this.createBodyControl(); this.navigation = this.createNavigation(world); -@@ -338,6 +340,7 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Targeti +@@ -340,6 +342,7 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Targeti entityliving = null; } } @@ -5106,7 +5189,7 @@ index e89f9c3e887601d8461eb967ae0bf582b672f631..56da8a4600688efd1987d82d4fcad175 this.target = entityliving; return true; // CraftBukkit end -@@ -373,15 +376,35 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Targeti +@@ -375,15 +378,35 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Targeti @Override public void baseTick() { super.baseTick(); @@ -5144,7 +5227,7 @@ index e89f9c3e887601d8461eb967ae0bf582b672f631..56da8a4600688efd1987d82d4fcad175 @Override protected void playHurtSound(DamageSource damageSource) { this.resetAmbientSoundTime(); -@@ -584,6 +607,7 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Targeti +@@ -586,6 +609,7 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Targeti } nbt.putBoolean("Bukkit.Aware", this.aware); // CraftBukkit @@ -5152,7 +5235,7 @@ index e89f9c3e887601d8461eb967ae0bf582b672f631..56da8a4600688efd1987d82d4fcad175 } @Override -@@ -668,6 +692,11 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Targeti +@@ -670,6 +694,11 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Targeti this.aware = nbt.getBoolean("Bukkit.Aware"); } // CraftBukkit end @@ -5164,7 +5247,7 @@ index e89f9c3e887601d8461eb967ae0bf582b672f631..56da8a4600688efd1987d82d4fcad175 } @Override -@@ -718,8 +747,8 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Targeti +@@ -720,8 +749,8 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Targeti @Override public void aiStep() { super.aiStep(); @@ -5175,7 +5258,7 @@ index e89f9c3e887601d8461eb967ae0bf582b672f631..56da8a4600688efd1987d82d4fcad175 Vec3i baseblockposition = this.getPickupReach(); List list = this.level().getEntitiesOfClass(ItemEntity.class, this.getBoundingBox().inflate((double) baseblockposition.getX(), (double) baseblockposition.getY(), (double) baseblockposition.getZ())); Iterator iterator = list.iterator(); -@@ -738,7 +767,7 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Targeti +@@ -740,7 +769,7 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Targeti } } @@ -5184,7 +5267,7 @@ index e89f9c3e887601d8461eb967ae0bf582b672f631..56da8a4600688efd1987d82d4fcad175 } protected Vec3i getPickupReach() { -@@ -963,44 +992,44 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Targeti +@@ -965,48 +994,48 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Targeti return; } // Paper end - Allow nerfed mobs to jump and float @@ -5201,22 +5284,26 @@ index e89f9c3e887601d8461eb967ae0bf582b672f631..56da8a4600688efd1987d82d4fcad175 if (i % 2 != 0 && this.tickCount > 1) { - gameprofilerfiller.push("targetSelector"); + //gameprofilerfiller.push("targetSelector"); // Purpur + if (this.targetSelector.inactiveTick(this.activatedPriority, false)) // Pufferfish - use this to alternate ticking this.targetSelector.tickRunningGoals(false); - gameprofilerfiller.pop(); - gameprofilerfiller.push("goalSelector"); + //gameprofilerfiller.pop(); // Purpur + //gameprofilerfiller.push("goalSelector"); // Purpur + if (this.goalSelector.inactiveTick(this.activatedPriority, false)) // Pufferfish - use this to alternate ticking this.goalSelector.tickRunningGoals(false); - gameprofilerfiller.pop(); + //gameprofilerfiller.pop(); // Purpur } else { - gameprofilerfiller.push("targetSelector"); + //gameprofilerfiller.push("targetSelector"); // Purpur + if (this.targetSelector.inactiveTick(this.activatedPriority, false)) // Pufferfish - use this to alternate ticking this.targetSelector.tick(); - gameprofilerfiller.pop(); - gameprofilerfiller.push("goalSelector"); + //gameprofilerfiller.pop(); // Purpur + //gameprofilerfiller.push("goalSelector"); // Purpur + if (this.goalSelector.inactiveTick(this.activatedPriority, false)) // Pufferfish - use this to alternate ticking this.goalSelector.tick(); - gameprofilerfiller.pop(); + //gameprofilerfiller.pop(); // Purpur @@ -5250,7 +5337,7 @@ index e89f9c3e887601d8461eb967ae0bf582b672f631..56da8a4600688efd1987d82d4fcad175 this.sendDebugPackets(); } -@@ -1309,6 +1338,12 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Targeti +@@ -1315,6 +1344,12 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Targeti } @@ -5263,7 +5350,7 @@ index e89f9c3e887601d8461eb967ae0bf582b672f631..56da8a4600688efd1987d82d4fcad175 @Nullable public static Item getEquipmentForSlot(EquipmentSlot equipmentSlot, int equipmentLevel) { switch (equipmentSlot) { -@@ -1403,7 +1438,7 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Targeti +@@ -1409,7 +1444,7 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Targeti RandomSource randomsource = world.getRandom(); this.getAttribute(Attributes.FOLLOW_RANGE).addPermanentModifier(new AttributeModifier("Random spawn bonus", randomsource.triangle(0.0D, 0.11485000000000001D), AttributeModifier.Operation.ADD_MULTIPLIED_BASE)); @@ -5272,7 +5359,7 @@ index e89f9c3e887601d8461eb967ae0bf582b672f631..56da8a4600688efd1987d82d4fcad175 return entityData; } -@@ -1450,6 +1485,7 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Targeti +@@ -1456,6 +1491,7 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Targeti if (!this.isAlive()) { return InteractionResult.PASS; } else if (this.getLeashHolder() == player) { @@ -5280,7 +5367,7 @@ index e89f9c3e887601d8461eb967ae0bf582b672f631..56da8a4600688efd1987d82d4fcad175 // CraftBukkit start - fire PlayerUnleashEntityEvent // Paper start - Expand EntityUnleashEvent org.bukkit.event.player.PlayerUnleashEntityEvent event = CraftEventFactory.callPlayerUnleashEntityEvent(this, player, hand, !player.hasInfiniteMaterials()); -@@ -1525,7 +1561,7 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Targeti +@@ -1531,7 +1567,7 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Targeti protected void onOffspringSpawnedFromEgg(Player player, Mob child) {} protected InteractionResult mobInteract(Player player, InteractionHand hand) { @@ -5289,7 +5376,7 @@ index e89f9c3e887601d8461eb967ae0bf582b672f631..56da8a4600688efd1987d82d4fcad175 } public boolean isWithinRestriction() { -@@ -1840,21 +1876,12 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Targeti +@@ -1846,21 +1882,12 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Targeti this.setLastHurtMob(target); } @@ -5313,7 +5400,7 @@ index e89f9c3e887601d8461eb967ae0bf582b672f631..56da8a4600688efd1987d82d4fcad175 } @Override -@@ -1902,4 +1929,56 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Targeti +@@ -1908,4 +1935,56 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Targeti return itemmonsteregg == null ? null : new ItemStack(itemmonsteregg); } @@ -5384,13 +5471,13 @@ index 2ee48ac3b665db2b02bcb1a30ec972d43a3725b0..59e8f5431ce5026209e1428b5fa5b548 } // Paper end - custom shear drops diff --git a/src/main/java/net/minecraft/world/entity/ai/attributes/AttributeMap.java b/src/main/java/net/minecraft/world/entity/ai/attributes/AttributeMap.java -index 9ef8f014af332da129bfcd3370da983ec035ecc6..c51b429822d56761f69c49ecd4addfab7b90bad8 100644 +index bc178967affd21ad04b83ea26639a2dd9b497454..0b0ae682ded83b136d99686b74b1e7c9efe679b7 100644 --- a/src/main/java/net/minecraft/world/entity/ai/attributes/AttributeMap.java +++ b/src/main/java/net/minecraft/world/entity/ai/attributes/AttributeMap.java -@@ -22,13 +22,20 @@ public class AttributeMap { - private final Map, AttributeInstance> attributes = new Object2ObjectOpenHashMap<>(); +@@ -23,14 +23,21 @@ public class AttributeMap { private final Set dirtyAttributes = new ObjectOpenHashSet<>(); private final AttributeSupplier supplier; + private final java.util.function.Function, AttributeInstance> createInstance; // Pufferfish + private final net.minecraft.world.entity.LivingEntity entity; // Purpur public AttributeMap(AttributeSupplier defaultAttributes) { @@ -5401,6 +5488,7 @@ index 9ef8f014af332da129bfcd3370da983ec035ecc6..c51b429822d56761f69c49ecd4addfab + this.entity = entity; + // Purpur end this.supplier = defaultAttributes; + this.createInstance = attributex -> this.supplier.createInstance(this::onAttributeModified, attributex); // Pufferfish } private void onAttributeModified(AttributeInstance instance) { @@ -5409,7 +5497,7 @@ index 9ef8f014af332da129bfcd3370da983ec035ecc6..c51b429822d56761f69c49ecd4addfab this.dirtyAttributes.add(instance); } } -@@ -38,7 +45,7 @@ public class AttributeMap { +@@ -40,7 +47,7 @@ public class AttributeMap { } public Collection getSyncableAttributes() { @@ -5734,10 +5822,10 @@ index 4e2c23ccdf4e4a4d65b291dbe20952bae1838bff..0da884a833f6c707fea512e826658c3b this.level.setBlock(blockposition1, Blocks.DIRT.defaultBlockState(), 2); } 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 74d4f653d5c7f1923c59019effd78337402f7025..b4e4670536f6dcea109c029d75d9710cb386f1d0 100644 +index 59e1014cb7604e7a56096284be24a33f87fb580b..45be322111556f113d6c74c497b623d82275281e 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 -@@ -87,8 +87,8 @@ public class GoalSelector { +@@ -89,8 +89,8 @@ public class GoalSelector { } public void tick() { @@ -5748,7 +5836,7 @@ index 74d4f653d5c7f1923c59019effd78337402f7025..b4e4670536f6dcea109c029d75d9710c for (WrappedGoal wrappedGoal : this.availableGoals) { if (wrappedGoal.isRunning() && (goalContainsAnyFlags(wrappedGoal, this.goalTypes) || !wrappedGoal.canContinueToUse())) { // Paper - Perf: optimize goal types by removing streams -@@ -97,8 +97,8 @@ public class GoalSelector { +@@ -99,8 +99,8 @@ public class GoalSelector { } this.lockedFlags.entrySet().removeIf(entry -> !entry.getValue().isRunning()); @@ -5759,7 +5847,7 @@ index 74d4f653d5c7f1923c59019effd78337402f7025..b4e4670536f6dcea109c029d75d9710c for (WrappedGoal wrappedGoal2 : this.availableGoals) { // Paper start -@@ -118,13 +118,13 @@ public class GoalSelector { +@@ -120,13 +120,13 @@ public class GoalSelector { } } @@ -5776,7 +5864,7 @@ index 74d4f653d5c7f1923c59019effd78337402f7025..b4e4670536f6dcea109c029d75d9710c for (WrappedGoal wrappedGoal : this.availableGoals) { if (wrappedGoal.isRunning() && (tickAll || wrappedGoal.requiresUpdateEveryTick())) { -@@ -132,7 +132,7 @@ public class GoalSelector { +@@ -134,7 +134,7 @@ public class GoalSelector { } } @@ -5972,7 +6060,7 @@ index 85b4b24361e785acf75571ff98f924c00ae80748..09a7b418ddf564c0be13297f7c216db2 } 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 d2f0c3b26d4beedb49d86e0242d843590d469d02..7463eefb7d09ea55fe8780210e7e967c2fe7896d 100644 +index 28cff997a1b263784e245f692adbff2a888a2d53..13b8141bdb2a1663431be645eb091f9e7638f3d0 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 @@ -64,6 +64,10 @@ public class TargetingConditions { @@ -5987,7 +6075,7 @@ index d2f0c3b26d4beedb49d86e0242d843590d469d02..7463eefb7d09ea55fe8780210e7e967c if (baseEntity == null) { if (this.isCombat && (!targetEntity.canBeSeenAsEnemy() || targetEntity.level().getDifficulty() == Difficulty.PEACEFUL)) { diff --git a/src/main/java/net/minecraft/world/entity/ambient/Bat.java b/src/main/java/net/minecraft/world/entity/ambient/Bat.java -index dc27ddf5131e7398a5390a5187261d4c7fb6ccaa..47a7c7f9527e1c4ea457eeafe0e11145653a871f 100644 +index e44af851263f27aa0009b14a60bb2d0642a5ce74..a70255995f380a9b0f7057cb5cb820f7655b93fc 100644 --- a/src/main/java/net/minecraft/world/entity/ambient/Bat.java +++ b/src/main/java/net/minecraft/world/entity/ambient/Bat.java @@ -44,12 +44,59 @@ public class Bat extends AmbientCreature { @@ -6112,14 +6200,14 @@ index dc27ddf5131e7398a5390a5187261d4c7fb6ccaa..47a7c7f9527e1c4ea457eeafe0e11145 b0 = 7; } else if (random.nextBoolean()) { return false; -@@ -239,6 +316,7 @@ public class Bat extends AmbientCreature { - } - } - +@@ -243,6 +320,7 @@ public class Bat extends AmbientCreature { + private static boolean isSpookySeason = false; + private static final int ONE_HOUR = 20 * 60 * 60; + private static int lastSpookyCheck = -ONE_HOUR; + public static boolean isHalloweenSeason(Level level) { return level.purpurConfig.forceHalloweenSeason || isHalloween(); } // Purpur 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); diff --git a/src/main/java/net/minecraft/world/entity/animal/AbstractFish.java b/src/main/java/net/minecraft/world/entity/animal/AbstractFish.java index 3231eaa6af2ddfe4095ff2d650f580ebd4d43aea..e8cb124d232f7316cc8c35dd8bd12f79bbcda7d6 100644 --- a/src/main/java/net/minecraft/world/entity/animal/AbstractFish.java @@ -8967,7 +9055,7 @@ index cebbb7341f86b13dcbfc3a41cbe264e9d4b68d60..e1f6202df983be2510436538904a45be this.navigation.stop(); this.setTarget((LivingEntity) null); diff --git a/src/main/java/net/minecraft/world/entity/animal/allay/Allay.java b/src/main/java/net/minecraft/world/entity/animal/allay/Allay.java -index 991e3274091c4e25eebc6debd44653e5b566eedb..bca7b7192debb3a34a08047010a2438e7b7e2a78 100644 +index 789d3469ce4983868e4b880b6053bd2d8c33a10a..569f42645a7730451944fbbd6b96910493feeca5 100644 --- a/src/main/java/net/minecraft/world/entity/animal/allay/Allay.java +++ b/src/main/java/net/minecraft/world/entity/animal/allay/Allay.java @@ -100,10 +100,23 @@ public class Allay extends PathfinderMob implements InventoryCarrier, VibrationS @@ -9024,13 +9112,14 @@ index 991e3274091c4e25eebc6debd44653e5b566eedb..bca7b7192debb3a34a08047010a2438e @Override protected Brain.Provider brainProvider() { return Brain.provider(Allay.MEMORY_TYPES, Allay.SENSOR_TYPES); -@@ -217,12 +252,13 @@ public class Allay extends PathfinderMob implements InventoryCarrier, VibrationS - +@@ -218,13 +253,13 @@ public class Allay extends PathfinderMob implements InventoryCarrier, VibrationS + private int behaviorTick = 0; // Pufferfish @Override protected void customServerAiStep() { - this.level().getProfiler().push("allayBrain"); +- if (this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish + //this.level().getProfiler().push("allayBrain"); // Purpur -+ //if ((getRider() == null || !this.isControllable()) && this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish // Purpur - only use brain if no rider // Purpur - TODO: Pufferfish ++ if ((getRider() == null || !this.isControllable()) && this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish // Purpur - only use brain if no rider this.getBrain().tick((ServerLevel) this.level(), this); - this.level().getProfiler().pop(); - this.level().getProfiler().push("allayActivityUpdate"); @@ -9076,7 +9165,7 @@ index b38281f963377cc82b360e8457da7cad033b8c36..f8790ab5b7c1279719271ee57c00f4f2 + // Purpur end } 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 a8cc6ddbf45370fe632e5c5fb7ceef3d299e62a4..d330f79e860662bc93a1703215e66e6564d181b9 100644 +index 98766696556b520cf565ccadc8dba207832b4ae3..6cd6943ddf86ef7c93e7ade55a0d3ff0aedcf86a 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 @@ -96,6 +96,43 @@ public class Axolotl extends Animal implements LerpingModel, VariantHolder getModelRotationValues() { return this.modelRotationValues; -@@ -270,12 +307,13 @@ public class Axolotl extends Animal implements LerpingModel, VariantHolder optional = this.getBrain().getMemory(MemoryModuleType.PLAY_DEAD_TICKS); -@@ -503,14 +541,22 @@ public class Axolotl extends Animal implements LerpingModel, VariantHolder> { @@ -9313,13 +9403,14 @@ index 816977990639ec0559b652fc9666afd5046f0a5d..ee8c232ddaa518377bdfa54e83ffc04f } @Override -@@ -183,12 +237,13 @@ public class Frog extends Animal implements VariantHolder> { - +@@ -184,13 +238,13 @@ public class Frog extends Animal implements VariantHolder> { + private int behaviorTick = 0; // Pufferfish @Override protected void customServerAiStep() { - this.level().getProfiler().push("frogBrain"); +- if (this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish + //this.level().getProfiler().push("frogBrain"); // Purpur -+ //if ((getRider() == null || !this.isControllable()) && this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish // Purpur - only use brain if no rider // Purpur - TODO: Pufferfish ++ if ((getRider() == null || !this.isControllable()) && this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish // Purpur - only use brain if no rider this.getBrain().tick((ServerLevel)this.level(), this); - this.level().getProfiler().pop(); - this.level().getProfiler().push("frogActivityUpdate"); @@ -9331,7 +9422,7 @@ index 816977990639ec0559b652fc9666afd5046f0a5d..ee8c232ddaa518377bdfa54e83ffc04f super.customServerAiStep(); } -@@ -371,7 +426,7 @@ public class Frog extends Animal implements VariantHolder> { +@@ -373,7 +427,7 @@ public class Frog extends Animal implements VariantHolder> { return world.getBlockState(pos.below()).is(BlockTags.FROGS_SPAWNABLE_ON) && isBrightEnoughToSpawn(world, pos); } @@ -9341,7 +9432,7 @@ index 816977990639ec0559b652fc9666afd5046f0a5d..ee8c232ddaa518377bdfa54e83ffc04f super(entity); } diff --git a/src/main/java/net/minecraft/world/entity/animal/frog/Tadpole.java b/src/main/java/net/minecraft/world/entity/animal/frog/Tadpole.java -index 290d41136f5ec7671bc4990dfe50da0a770c124d..09c4cf772df4644413e40055fedcdf42ee8064fd 100644 +index 0d34e9e0f7ce35c3c28a9216cf3cdd5eddfe90f6..64d0e2edf0e47bfe3bc5fbdbabac26c494110467 100644 --- a/src/main/java/net/minecraft/world/entity/animal/frog/Tadpole.java +++ b/src/main/java/net/minecraft/world/entity/animal/frog/Tadpole.java @@ -51,13 +51,50 @@ public class Tadpole extends AbstractFish { @@ -9396,13 +9487,14 @@ index 290d41136f5ec7671bc4990dfe50da0a770c124d..09c4cf772df4644413e40055fedcdf42 @Override protected PathNavigation createNavigation(Level world) { return new WaterBoundPathNavigation(this, world); -@@ -85,12 +122,13 @@ public class Tadpole extends AbstractFish { - +@@ -86,13 +123,13 @@ public class Tadpole extends AbstractFish { + private int behaviorTick = 0; // Pufferfish @Override protected void customServerAiStep() { - this.level().getProfiler().push("tadpoleBrain"); +- if (this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish + //this.level().getProfiler().push("tadpoleBrain"); // Purpur -+ //if ((getRider() == null || !this.isControllable()) && this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish // Purpur - only use brain if no rider // Purpur - TODO: Pufferfish ++ if ((getRider() == null || !this.isControllable()) && this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish // Purpur - only use brain if no rider this.getBrain().tick((ServerLevel) this.level(), this); - this.level().getProfiler().pop(); - this.level().getProfiler().push("tadpoleActivityUpdate"); @@ -9415,7 +9507,7 @@ index 290d41136f5ec7671bc4990dfe50da0a770c124d..09c4cf772df4644413e40055fedcdf42 } 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 02e49c7ae5e120302b6479cf3e3934b9217eebf0..6a3c68839d3b993c82fabd4e17f53e38ce9c38f7 100644 +index 81c9f1c2895adedec246732df9f9dadddfa5f345..bbcee6755f8ca24e944b310be505fc5c4cc87e0b 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 @@ -91,6 +91,38 @@ public class Goat extends Animal { @@ -9457,13 +9549,14 @@ index 02e49c7ae5e120302b6479cf3e3934b9217eebf0..6a3c68839d3b993c82fabd4e17f53e38 @Override protected Brain.Provider brainProvider() { return Brain.provider(Goat.MEMORY_TYPES, Goat.SENSOR_TYPES); -@@ -192,12 +224,13 @@ public class Goat extends Animal { - +@@ -193,13 +225,13 @@ public class Goat extends Animal { + private int behaviorTick = 0; // Pufferfish @Override protected void customServerAiStep() { - this.level().getProfiler().push("goatBrain"); +- if (this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish + //this.level().getProfiler().push("goatBrain"); // Purpur -+ //if ((getRider() == null || !this.isControllable()) && this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish // Purpur - only use brain if no rider // Purpur - TODO: Pufferfish ++ if ((getRider() == null || !this.isControllable()) && this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish // Purpur - only use brain if no rider this.getBrain().tick((ServerLevel) this.level(), this); - this.level().getProfiler().pop(); - this.level().getProfiler().push("goatActivityUpdate"); @@ -9475,7 +9568,7 @@ index 02e49c7ae5e120302b6479cf3e3934b9217eebf0..6a3c68839d3b993c82fabd4e17f53e38 super.customServerAiStep(); } -@@ -394,6 +427,7 @@ public class Goat extends Animal { +@@ -396,6 +428,7 @@ public class Goat extends Animal { // Paper start - Goat ram API public void ram(net.minecraft.world.entity.LivingEntity entity) { @@ -10411,7 +10504,7 @@ index 0e797e2714a2fd103cbd51548764577fd9b6412d..52e1dd6e064dc03312e18ca515a24e7d } diff --git a/src/main/java/net/minecraft/world/entity/boss/wither/WitherBoss.java b/src/main/java/net/minecraft/world/entity/boss/wither/WitherBoss.java -index 7ddca52f7fe3f289b4b867e134326b1ead1a2aee..4a98027a12c2535d1df3a9f6390eb85146398403 100644 +index 5040b1a7125dd9647a98fd812f558ec3a2171092..9facda1076794b9efe9a43e2d5f032246e8ee59e 100644 --- a/src/main/java/net/minecraft/world/entity/boss/wither/WitherBoss.java +++ b/src/main/java/net/minecraft/world/entity/boss/wither/WitherBoss.java @@ -88,20 +88,59 @@ public class WitherBoss extends Monster implements PowerableMob, RangedAttackMob @@ -10605,7 +10698,7 @@ index 7ddca52f7fe3f289b4b867e134326b1ead1a2aee..4a98027a12c2535d1df3a9f6390eb851 } -@@ -263,6 +404,16 @@ public class WitherBoss extends Monster implements PowerableMob, RangedAttackMob +@@ -270,6 +411,16 @@ public class WitherBoss extends Monster implements PowerableMob, RangedAttackMob @Override protected void customServerAiStep() { @@ -10622,7 +10715,7 @@ index 7ddca52f7fe3f289b4b867e134326b1ead1a2aee..4a98027a12c2535d1df3a9f6390eb851 int i; if (this.getInvulnerableTicks() > 0) { -@@ -279,7 +430,7 @@ public class WitherBoss extends Monster implements PowerableMob, RangedAttackMob +@@ -286,7 +437,7 @@ public class WitherBoss extends Monster implements PowerableMob, RangedAttackMob } // CraftBukkit end @@ -10631,7 +10724,7 @@ index 7ddca52f7fe3f289b4b867e134326b1ead1a2aee..4a98027a12c2535d1df3a9f6390eb851 // CraftBukkit start - Use relative location for far away sounds // this.level().globalLevelEvent(1023, new BlockPosition(this), 0); int viewDistance = ((ServerLevel) this.level()).getCraftServer().getViewDistance() * 16; -@@ -304,7 +455,7 @@ public class WitherBoss extends Monster implements PowerableMob, RangedAttackMob +@@ -311,7 +462,7 @@ public class WitherBoss extends Monster implements PowerableMob, RangedAttackMob this.setInvulnerableTicks(i); if (this.tickCount % 10 == 0) { @@ -10640,7 +10733,7 @@ index 7ddca52f7fe3f289b4b867e134326b1ead1a2aee..4a98027a12c2535d1df3a9f6390eb851 } } else { -@@ -364,7 +515,7 @@ public class WitherBoss extends Monster implements PowerableMob, RangedAttackMob +@@ -371,7 +522,7 @@ public class WitherBoss extends Monster implements PowerableMob, RangedAttackMob if (this.destroyBlocksTick > 0) { --this.destroyBlocksTick; @@ -10649,7 +10742,7 @@ index 7ddca52f7fe3f289b4b867e134326b1ead1a2aee..4a98027a12c2535d1df3a9f6390eb851 boolean flag = false; j = Mth.floor(this.getBbWidth() / 2.0F + 1.0F); -@@ -391,8 +542,10 @@ public class WitherBoss extends Monster implements PowerableMob, RangedAttackMob +@@ -398,8 +549,10 @@ public class WitherBoss extends Monster implements PowerableMob, RangedAttackMob } } @@ -10662,7 +10755,7 @@ index 7ddca52f7fe3f289b4b867e134326b1ead1a2aee..4a98027a12c2535d1df3a9f6390eb851 } this.bossEvent.setProgress(this.getHealth() / this.getMaxHealth()); -@@ -580,11 +733,11 @@ public class WitherBoss extends Monster implements PowerableMob, RangedAttackMob +@@ -587,11 +740,11 @@ public class WitherBoss extends Monster implements PowerableMob, RangedAttackMob } public int getAlternativeTarget(int headIndex) { @@ -10676,7 +10769,7 @@ index 7ddca52f7fe3f289b4b867e134326b1ead1a2aee..4a98027a12c2535d1df3a9f6390eb851 } @Override -@@ -594,6 +747,7 @@ public class WitherBoss extends Monster implements PowerableMob, RangedAttackMob +@@ -601,6 +754,7 @@ public class WitherBoss extends Monster implements PowerableMob, RangedAttackMob @Override protected boolean canRide(Entity entity) { @@ -11482,7 +11575,7 @@ index fd995b1f29c47884e9db2cb92f1dd615d62ae032..7e8603ef5df722f19e85b9c5cdd4ebfd return Guardian.createAttributes().add(Attributes.MOVEMENT_SPEED, 0.30000001192092896D).add(Attributes.ATTACK_DAMAGE, 8.0D).add(Attributes.MAX_HEALTH, 80.0D); } 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 260202fab3ac300552c557b44dcf251f083c6a78..5b49a6b1884c33bedafca5cff0214cdfccd87302 100644 +index 9cf4e2f309e182c69e9592ac606c0ae85a1200b6..356c4a26dabcf07374234ee2fc6b796bec1d7b40 100644 --- a/src/main/java/net/minecraft/world/entity/monster/EnderMan.java +++ b/src/main/java/net/minecraft/world/entity/monster/EnderMan.java @@ -90,12 +90,40 @@ public class EnderMan extends Monster implements NeutralMob { @@ -11563,7 +11656,7 @@ index 260202fab3ac300552c557b44dcf251f083c6a78..5b49a6b1884c33bedafca5cff0214cdf float f = this.getLightLevelDependentMagicValue(); if (f > 0.5F && this.level().canSeeSky(this.blockPosition()) && this.random.nextFloat() * 30.0F < (f - 0.4F) * 2.0F && this.tryEscape(com.destroystokyo.paper.event.entity.EndermanEscapeEvent.Reason.RUNAWAY)) { // Paper - EndermanEscapeEvent -@@ -394,6 +423,8 @@ public class EnderMan extends Monster implements NeutralMob { +@@ -400,6 +429,8 @@ public class EnderMan extends Monster implements NeutralMob { public boolean hurt(DamageSource source, float amount) { if (this.isInvulnerableTo(source)) { return false; @@ -11572,7 +11665,7 @@ index 260202fab3ac300552c557b44dcf251f083c6a78..5b49a6b1884c33bedafca5cff0214cdf } else { boolean flag = source.getDirectEntity() instanceof ThrownPotion; boolean flag1; -@@ -408,6 +439,7 @@ public class EnderMan extends Monster implements NeutralMob { +@@ -414,6 +445,7 @@ public class EnderMan extends Monster implements NeutralMob { } else { flag1 = flag && this.hurtWithCleanWater(source, (ThrownPotion) source.getDirectEntity(), amount); @@ -11580,7 +11673,7 @@ index 260202fab3ac300552c557b44dcf251f083c6a78..5b49a6b1884c33bedafca5cff0214cdf if (this.tryEscape(com.destroystokyo.paper.event.entity.EndermanEscapeEvent.Reason.INDIRECT)) { // Paper - EndermanEscapeEvent for (int i = 0; i < 64; ++i) { if (this.teleport()) { -@@ -452,7 +484,7 @@ public class EnderMan extends Monster implements NeutralMob { +@@ -458,7 +490,7 @@ public class EnderMan extends Monster implements NeutralMob { @Override public boolean requiresCustomPersistence() { @@ -11589,7 +11682,7 @@ index 260202fab3ac300552c557b44dcf251f083c6a78..5b49a6b1884c33bedafca5cff0214cdf } private static class EndermanFreezeWhenLookedAt extends Goal { -@@ -499,7 +531,16 @@ public class EnderMan extends Monster implements NeutralMob { +@@ -505,7 +537,16 @@ public class EnderMan extends Monster implements NeutralMob { @Override public boolean canUse() { @@ -11607,7 +11700,7 @@ index 260202fab3ac300552c557b44dcf251f083c6a78..5b49a6b1884c33bedafca5cff0214cdf } @Override -@@ -544,7 +585,16 @@ public class EnderMan extends Monster implements NeutralMob { +@@ -550,7 +591,16 @@ public class EnderMan extends Monster implements NeutralMob { @Override public boolean canUse() { @@ -14349,7 +14442,7 @@ index 796ce24185ab9e80864116f9523c4289fcaad243..82391c84789c27353212d3142c036cc5 } 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 c583d883118ded5e1884c757427dc5e73c10dd27..757d2b7bcb83f5bdcddf85a00e90288f3b82a2d6 100644 +index 6f8a22c51ba5e2713dcdfc61c61b35123f9bf326..dfda93dac6b0dd9dfbec8cca2c1b58b93160ad40 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 @@ -90,6 +90,43 @@ public class Hoglin extends Animal implements Enemy, HoglinBase { @@ -14396,13 +14489,14 @@ index c583d883118ded5e1884c757427dc5e73c10dd27..757d2b7bcb83f5bdcddf85a00e90288f @Override public boolean canBeLeashed(Player player) { return !this.isLeashed(); -@@ -155,9 +192,10 @@ public class Hoglin extends Animal implements Enemy, HoglinBase { - +@@ -156,10 +193,10 @@ public class Hoglin extends Animal implements Enemy, HoglinBase { + private int behaviorTick; // Pufferfish @Override protected void customServerAiStep() { - this.level().getProfiler().push("hoglinBrain"); +- if (this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish + //this.level().getProfiler().push("hoglinBrain"); // Purpur -+ //if ((getRider() == null || !this.isControllable()) && this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish // Purpur - only use brain if no rider // Purpur - TODO: Pufferfish ++ if ((getRider() == null || !this.isControllable()) && this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish // Purpur - only use brain if no rider this.getBrain().tick((ServerLevel)this.level(), this); - this.level().getProfiler().pop(); + //this.level().getProfiler().pop(); // Purpur @@ -14410,7 +14504,7 @@ index c583d883118ded5e1884c757427dc5e73c10dd27..757d2b7bcb83f5bdcddf85a00e90288f if (this.isConverting()) { this.timeInOverworld++; 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 a8ab486c7e11ec137da48174af6f1030dfd48056..1b5977aa14d9a7254e7692bb152cc2808d52107a 100644 +index 5878a6ecadc2b9f7ac9f70622a8c85e2940e139a..8973d6544af7d5e558dad06b5f881683474c2b26 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 @@ -94,6 +94,38 @@ public class Piglin extends AbstractPiglin implements CrossbowAttackMob, Invento @@ -14452,20 +14546,21 @@ index a8ab486c7e11ec137da48174af6f1030dfd48056..1b5977aa14d9a7254e7692bb152cc280 @Override public void addAdditionalSaveData(CompoundTag nbt) { super.addAdditionalSaveData(nbt); -@@ -296,9 +328,10 @@ public class Piglin extends AbstractPiglin implements CrossbowAttackMob, Invento - +@@ -297,10 +329,10 @@ public class Piglin extends AbstractPiglin implements CrossbowAttackMob, Invento + private int behaviorTick; // Pufferfish @Override protected void customServerAiStep() { - this.level().getProfiler().push("piglinBrain"); +- if (this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish + //this.level().getProfiler().push("piglinBrain"); // Purpur -+ //if ((getRider() == null || !this.isControllable()) && this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish // Purpur - only use brain if no rider // Purpur - TODO: Pufferfish ++ if ((getRider() == null || !this.isControllable()) && this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish // Purpur - only use brain if no rider this.getBrain().tick((ServerLevel) this.level(), this); - this.level().getProfiler().pop(); + //this.level().getProfiler().pop(); // Purpur PiglinAi.updateActivity(this); super.customServerAiStep(); } -@@ -389,7 +422,7 @@ public class Piglin extends AbstractPiglin implements CrossbowAttackMob, Invento +@@ -391,7 +423,7 @@ public class Piglin extends AbstractPiglin implements CrossbowAttackMob, Invento @Override public boolean wantsToPickUp(ItemStack stack) { @@ -14555,7 +14650,7 @@ index fcadd7f28ccb81bbb36e97d8b8d8a8ba3f3d6a16..407a0f27719d3944b3a005c664d80246 PiglinBruteAi.maybePlayActivitySound(this); super.customServerAiStep(); diff --git a/src/main/java/net/minecraft/world/entity/monster/warden/Warden.java b/src/main/java/net/minecraft/world/entity/monster/warden/Warden.java -index ddd60be52dce5773c80934be5aa5705db239e3dd..0bb577ec9ba0d23a741ccf067ac35f6be68312ca 100644 +index 9cf56f2ada025aae0710099bcc3b5c62fd7bbb9e..722e1fed4b333958e95a847b27cfc67d04f13263 100644 --- a/src/main/java/net/minecraft/world/entity/monster/warden/Warden.java +++ b/src/main/java/net/minecraft/world/entity/monster/warden/Warden.java @@ -123,8 +123,32 @@ public class Warden extends Monster implements VibrationSystem { @@ -14591,20 +14686,20 @@ index ddd60be52dce5773c80934be5aa5705db239e3dd..0bb577ec9ba0d23a741ccf067ac35f6b @Override public Packet getAddEntityPacket() { return new ClientboundAddEntityPacket(this, this.hasPose(Pose.EMERGING) ? 1 : 0); -@@ -275,9 +299,10 @@ public class Warden extends Monster implements VibrationSystem { +@@ -276,10 +300,10 @@ public class Warden extends Monster implements VibrationSystem { protected void customServerAiStep() { ServerLevel worldserver = (ServerLevel) this.level(); - worldserver.getProfiler().push("wardenBrain"); + //worldserver.getProfiler().push("wardenBrain"); // Purpur -+ //if (this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish // Purpur - TODO: Move to Ridables patch + if (this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish this.getBrain().tick(worldserver, this); - this.level().getProfiler().pop(); + //this.level().getProfiler().pop(); // Purpur super.customServerAiStep(); if ((this.tickCount + this.getId()) % 120 == 0) { Warden.applyDarknessAround(worldserver, this.position(), this, 20); -@@ -392,17 +417,14 @@ public class Warden extends Monster implements VibrationSystem { +@@ -394,17 +418,14 @@ public class Warden extends Monster implements VibrationSystem { @Contract("null->false") public boolean canTargetEntity(@Nullable Entity entity) { @@ -14679,19 +14774,19 @@ index e0e5046c84941a8d17e18c177f3daea9cb631940..d503d7a5837dbeb98e58dbe8f7e5de45 } 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 a7930f9875aa4aca997caaead46ecdc21e5e11d7..4be218129188c1be8736940170a861adc10fdb7d 100644 +index 81c2fb02779d416933502abd416324ad4c7ef22b..ec9619590da04f3970037a7ebe6c0799d335b69b 100644 --- a/src/main/java/net/minecraft/world/entity/npc/Villager.java +++ b/src/main/java/net/minecraft/world/entity/npc/Villager.java -@@ -142,6 +142,8 @@ public class Villager extends AbstractVillager implements ReputationEventHandler - }, MemoryModuleType.MEETING_POINT, (entityvillager, holder) -> { - return holder.is(PoiTypes.MEETING); +@@ -144,6 +144,8 @@ public class Villager extends AbstractVillager implements ReputationEventHandler }); + + public long nextGolemPanic = -1; // Pufferfish + private boolean isLobotomized = false; public boolean isLobotomized() { return this.isLobotomized; } // Purpur + private int notLobotomizedCount = 0; // Purpur public Villager(EntityType entityType, Level world) { this(entityType, world, VillagerType.PLAINS); -@@ -154,6 +156,91 @@ public class Villager extends AbstractVillager implements ReputationEventHandler +@@ -156,6 +158,91 @@ public class Villager extends AbstractVillager implements ReputationEventHandler this.getNavigation().setCanFloat(true); this.setCanPickUpLoot(true); this.setVillagerData(this.getVillagerData().setType(type).setProfession(VillagerProfession.NONE)); @@ -14783,7 +14878,7 @@ index a7930f9875aa4aca997caaead46ecdc21e5e11d7..4be218129188c1be8736940170a861ad } @Override -@@ -190,7 +277,7 @@ public class Villager extends AbstractVillager implements ReputationEventHandler +@@ -192,7 +279,7 @@ public class Villager extends AbstractVillager implements ReputationEventHandler brain.addActivity(Activity.PLAY, VillagerGoalPackages.getPlayPackage(0.5F)); } else { brain.setSchedule(Schedule.VILLAGER_DEFAULT); @@ -14792,7 +14887,7 @@ index a7930f9875aa4aca997caaead46ecdc21e5e11d7..4be218129188c1be8736940170a861ad } brain.addActivity(Activity.CORE, VillagerGoalPackages.getCorePackage(villagerprofession, 0.5F)); -@@ -252,11 +339,25 @@ public class Villager extends AbstractVillager implements ReputationEventHandler +@@ -255,15 +342,24 @@ public class Villager extends AbstractVillager implements ReputationEventHandler // Paper start this.customServerAiStep(false); } @@ -14809,19 +14904,19 @@ index a7930f9875aa4aca997caaead46ecdc21e5e11d7..4be218129188c1be8736940170a861ad + this.isLobotomized = false; + } + // Purpur end - if (!inactive) this.getBrain().tick((ServerLevel) this.level(), this); // Paper + // Pufferfish start +- if (!inactive && this.behaviorTick++ % this.activatedPriority == 0) { ++ if (!inactive && (getRider() == null || !this.isControllable()) && this.behaviorTick++ % this.activatedPriority == 0) { // Purpur - only use brain if no rider + this.getBrain().tick((ServerLevel) this.level(), this); // Paper + } + // Pufferfish end - this.level().getProfiler().pop(); + else if (this.isLobotomized && shouldRestock()) restock(); // Purpur -+ /*// Purpur start // Purpur - TODO: Pufferfish -+ if (!inactive && (getRider() == null || !this.isControllable()) && this.behaviorTick++ % this.activatedPriority == 0) { // Purpur - only use brain if no rider -+ this.getBrain().tick((ServerLevel) this.level(), this); // Paper -+ } -+ // Purpur end*/ // Purpur - TODO: Pufferfish + //this.level().getProfiler().pop(); // Purpur if (this.assignProfessionWhenSpawned) { this.assignProfessionWhenSpawned = false; } -@@ -312,7 +413,7 @@ public class Villager extends AbstractVillager implements ReputationEventHandler +@@ -319,7 +415,7 @@ public class Villager extends AbstractVillager implements ReputationEventHandler if (!itemstack.is(Items.VILLAGER_SPAWN_EGG) && this.isAlive() && !this.isTrading() && !this.isSleeping()) { if (this.isBaby()) { this.setUnhappy(); @@ -14830,7 +14925,7 @@ index a7930f9875aa4aca997caaead46ecdc21e5e11d7..4be218129188c1be8736940170a861ad } else { boolean flag = this.getOffers().isEmpty(); -@@ -325,9 +426,10 @@ public class Villager extends AbstractVillager implements ReputationEventHandler +@@ -332,9 +428,10 @@ public class Villager extends AbstractVillager implements ReputationEventHandler } if (flag) { @@ -14843,7 +14938,7 @@ index a7930f9875aa4aca997caaead46ecdc21e5e11d7..4be218129188c1be8736940170a861ad this.startTrading(player); } -@@ -496,7 +598,7 @@ public class Villager extends AbstractVillager implements ReputationEventHandler +@@ -503,7 +600,7 @@ public class Villager extends AbstractVillager implements ReputationEventHandler while (iterator.hasNext()) { MerchantOffer merchantrecipe = (MerchantOffer) iterator.next(); @@ -14852,7 +14947,7 @@ index a7930f9875aa4aca997caaead46ecdc21e5e11d7..4be218129188c1be8736940170a861ad } } -@@ -738,7 +840,7 @@ public class Villager extends AbstractVillager implements ReputationEventHandler +@@ -745,7 +842,7 @@ public class Villager extends AbstractVillager implements ReputationEventHandler @Override public boolean canBreed() { @@ -14861,7 +14956,7 @@ index a7930f9875aa4aca997caaead46ecdc21e5e11d7..4be218129188c1be8736940170a861ad } private boolean hungry() { -@@ -931,6 +1033,11 @@ public class Villager extends AbstractVillager implements ReputationEventHandler +@@ -938,6 +1035,11 @@ public class Villager extends AbstractVillager implements ReputationEventHandler public boolean hasFarmSeeds() { return this.getInventory().hasAnyMatching((itemstack) -> { @@ -14873,7 +14968,7 @@ index a7930f9875aa4aca997caaead46ecdc21e5e11d7..4be218129188c1be8736940170a861ad return itemstack.is(ItemTags.VILLAGER_PLANTABLE_SEEDS); }); } -@@ -988,6 +1095,7 @@ public class Villager extends AbstractVillager implements ReputationEventHandler +@@ -995,6 +1097,7 @@ public class Villager extends AbstractVillager implements ReputationEventHandler } public void spawnGolemIfNeeded(ServerLevel world, long time, int requiredCount) { @@ -14881,7 +14976,7 @@ index a7930f9875aa4aca997caaead46ecdc21e5e11d7..4be218129188c1be8736940170a861ad if (this.wantsToSpawnGolem(time)) { AABB axisalignedbb = this.getBoundingBox().inflate(10.0D, 10.0D, 10.0D); List list = world.getEntitiesOfClass(Villager.class, axisalignedbb); -@@ -1052,6 +1160,12 @@ public class Villager extends AbstractVillager implements ReputationEventHandler +@@ -1059,6 +1162,12 @@ public class Villager extends AbstractVillager implements ReputationEventHandler @Override public void startSleeping(BlockPos pos) { @@ -15245,10 +15340,19 @@ index ffd01d24cbfc90e2a8807757e61b2cf20a944354..a419820d5001079ed839e67c757bc8fa protected double getDefaultGravity() { return 0.06D; 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 74c596264d4da551437bd2a23e1c70022cfc73fc..e4d4ff0ef4a0f3283aa42fe2304816cd6d9475a8 100644 +index 0e4d180d257d7180a8e1300f35924d0c52ce421a..a5d23805023737a5ab92228826632901600f540e 100644 --- a/src/main/java/net/minecraft/world/entity/projectile/Projectile.java +++ b/src/main/java/net/minecraft/world/entity/projectile/Projectile.java -@@ -343,7 +343,7 @@ public abstract class Projectile extends Entity implements TraceableEntity { +@@ -65,7 +65,7 @@ public abstract class Projectile extends Entity implements TraceableEntity { + if (!isLoaded) { + if (Projectile.loadedThisTick > gg.pufferfish.pufferfish.PufferfishConfig.maxProjectileLoadsPerTick) { + if (++this.loadedLifetime > gg.pufferfish.pufferfish.PufferfishConfig.maxProjectileLoadsPerProjectile) { +- this.discard(); ++ this.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.DISCARD); // Purpur + } + return; + } +@@ -373,7 +373,7 @@ public abstract class Projectile extends Entity implements TraceableEntity { public boolean mayInteract(Level world, BlockPos pos) { Entity entity = this.getOwner(); @@ -16361,7 +16465,7 @@ index 4ebd634cff286b10868e26eeb3ecf34abdcab22e..7dc811335caa46870d1d895899a1e6c2 com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent event = new com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent((org.bukkit.entity.Player) user.getBukkitEntity(), org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemstack), (org.bukkit.entity.Projectile) entityegg.getBukkitEntity()); if (event.callEvent() && world.addFreshEntity(entityegg)) { diff --git a/src/main/java/net/minecraft/world/item/EndCrystalItem.java b/src/main/java/net/minecraft/world/item/EndCrystalItem.java -index dd1bdb4bb87a3a59c229ba76b36841d199717624..54607cea2622f259aedfe425b60e2317e4167bf9 100644 +index 42d13b03119af88838ca9cd36000162c04787493..a6622bd680be69e1bdcda03392f0f936fbd289c9 100644 --- a/src/main/java/net/minecraft/world/item/EndCrystalItem.java +++ b/src/main/java/net/minecraft/world/item/EndCrystalItem.java @@ -27,7 +27,7 @@ public class EndCrystalItem extends Item { @@ -17004,7 +17108,7 @@ index 082b804f4793f72e76361f5427f0358273454b3d..133d994d9ac9d4f9527135ac0c77e574 if (this.fire) { diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java -index b4ef3ad2c17168085372f1fe46809f02d9dfe74a..eda2f8cc034cf46293be1be117a60cf8b663c303 100644 +index 876990240293740046c6a6b43aebad6d7d5075d6..16eb159ab4533f5101461bc4f417b9420a321d82 100644 --- a/src/main/java/net/minecraft/world/level/Level.java +++ b/src/main/java/net/minecraft/world/level/Level.java @@ -170,6 +170,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable { @@ -17065,7 +17169,15 @@ index b4ef3ad2c17168085372f1fe46809f02d9dfe74a..eda2f8cc034cf46293be1be117a60cf8 public CraftWorld getWorld() { return this.world; } -@@ -216,6 +260,8 @@ public abstract class Level implements LevelAccessor, AutoCloseable { +@@ -212,12 +256,14 @@ public abstract class Level implements LevelAccessor, AutoCloseable { + // Paper end + + public abstract ResourceKey getTypeKey(); +- +- protected final io.papermc.paper.util.math.ThreadUnsafeRandom randomTickRandom = new io.papermc.paper.util.math.ThreadUnsafeRandom(java.util.concurrent.ThreadLocalRandom.current().nextLong()); public net.minecraft.util.RandomSource getThreadUnsafeRandom() { return this.randomTickRandom; } // Pufferfish - move thread unsafe random initialization // Pufferfish - getter ++ ++ //protected final io.papermc.paper.util.math.ThreadUnsafeRandom randomTickRandom = new io.papermc.paper.util.math.ThreadUnsafeRandom(java.util.concurrent.ThreadLocalRandom.current().nextLong()); public net.minecraft.util.RandomSource getThreadUnsafeRandom() { return this.randomTickRandom; } // Pufferfish - move thread unsafe random initialization // Pufferfish - getter // Purpur - dont break ABI + protected Level(WritableLevelData worlddatamutable, ResourceKey resourcekey, RegistryAccess iregistrycustom, Holder holder, Supplier supplier, boolean flag, boolean flag1, long i, int j, org.bukkit.generator.ChunkGenerator gen, org.bukkit.generator.BiomeProvider biomeProvider, org.bukkit.World.Environment env, java.util.function.Function paperWorldConfigCreator, java.util.concurrent.Executor executor) { // Paper - create paper world config; Async-Anti-Xray: Pass executor this.spigotConfig = new org.spigotmc.SpigotWorldConfig(((net.minecraft.world.level.storage.PrimaryLevelData) worlddatamutable).getLevelName()); // Spigot this.paperConfig = paperWorldConfigCreator.apply(this.spigotConfig); // Paper - create paper world config @@ -17074,7 +17186,7 @@ index b4ef3ad2c17168085372f1fe46809f02d9dfe74a..eda2f8cc034cf46293be1be117a60cf8 this.generator = gen; this.world = new CraftWorld((ServerLevel) this, gen, biomeProvider, env); -@@ -1257,18 +1303,18 @@ public abstract class Level implements LevelAccessor, AutoCloseable { +@@ -1259,18 +1305,18 @@ public abstract class Level implements LevelAccessor, AutoCloseable { } protected void tickBlockEntities() { @@ -17098,7 +17210,7 @@ index b4ef3ad2c17168085372f1fe46809f02d9dfe74a..eda2f8cc034cf46293be1be117a60cf8 // Spigot start // Iterator iterator = this.blockEntityTickers.iterator(); boolean flag = this.tickRateManager().runsNormally(); -@@ -1297,10 +1343,10 @@ public abstract class Level implements LevelAccessor, AutoCloseable { +@@ -1299,10 +1345,10 @@ public abstract class Level implements LevelAccessor, AutoCloseable { } this.blockEntityTickers.removeAll(toRemove); // Paper - Fix MC-117075 @@ -17111,7 +17223,7 @@ index b4ef3ad2c17168085372f1fe46809f02d9dfe74a..eda2f8cc034cf46293be1be117a60cf8 this.spigotConfig.currentPrimedTnt = 0; // Spigot } -@@ -1515,7 +1561,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable { +@@ -1517,7 +1563,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable { @Override public List getEntities(@Nullable Entity except, AABB box, Predicate predicate) { @@ -17120,7 +17232,7 @@ index b4ef3ad2c17168085372f1fe46809f02d9dfe74a..eda2f8cc034cf46293be1be117a60cf8 List list = Lists.newArrayList(); ((ServerLevel)this).getEntityLookup().getEntities(except, box, list, predicate); // Paper - optimise this call return list; -@@ -1534,7 +1580,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable { +@@ -1536,7 +1582,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable { } public void getEntities(EntityTypeTest filter, AABB box, Predicate predicate, List result, int limit) { @@ -17129,15 +17241,16 @@ index b4ef3ad2c17168085372f1fe46809f02d9dfe74a..eda2f8cc034cf46293be1be117a60cf8 // Paper start - optimise this call //TODO use limit if (filter instanceof net.minecraft.world.entity.EntityType entityTypeTest) { -@@ -1789,6 +1835,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable { +@@ -1791,7 +1837,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable { } public ProfilerFiller getProfiler() { -+ //if (true || gg.pufferfish.pufferfish.PufferfishConfig.disableMethodProfiler) return net.minecraft.util.profiling.InactiveProfiler.INSTANCE; // Pufferfish // Purpur // Purpur - TODO: Pufferfish +- if (gg.pufferfish.pufferfish.PufferfishConfig.disableMethodProfiler) return net.minecraft.util.profiling.InactiveProfiler.INSTANCE; // Pufferfish ++ if (true || gg.pufferfish.pufferfish.PufferfishConfig.disableMethodProfiler) return net.minecraft.util.profiling.InactiveProfiler.INSTANCE; // Pufferfish // Purpur return (ProfilerFiller) this.profiler.get(); } -@@ -1900,4 +1947,13 @@ public abstract class Level implements LevelAccessor, AutoCloseable { +@@ -1903,4 +1949,13 @@ public abstract class Level implements LevelAccessor, AutoCloseable { return null; } // Paper end - optimize redstone (Alternate Current) @@ -17152,7 +17265,7 @@ index b4ef3ad2c17168085372f1fe46809f02d9dfe74a..eda2f8cc034cf46293be1be117a60cf8 + // Purpur end } diff --git a/src/main/java/net/minecraft/world/level/NaturalSpawner.java b/src/main/java/net/minecraft/world/level/NaturalSpawner.java -index ed8032495af9ce9c23419224814b8d27e4a97c17..0f90a6803851eba51e164772c984b1cd1193d882 100644 +index 382c5773f773837f8182ee5d4f97f588c15eaacd..d5c13c1067c000fa2354fdea959882cd3688136e 100644 --- a/src/main/java/net/minecraft/world/level/NaturalSpawner.java +++ b/src/main/java/net/minecraft/world/level/NaturalSpawner.java @@ -127,8 +127,8 @@ public final class NaturalSpawner { @@ -19121,10 +19234,19 @@ index 2034ca2edd3aff61d94416266e75402babd3e741..031fc626d2075cbe0941fecc18840671 protected ResourceKey drops; 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 2a8609e33716949ff1877b6d10f64a9d7a7c81e9..fd637415625fdabcac07e120e9168d09c06141d4 100644 +index 8d6b95bc46e17fcbda6dbec0f77a451df884cbaa..56dd36a88f11aeac528e7be2998e52324c01fde1 100644 --- a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java +++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java -@@ -443,11 +443,11 @@ public class LevelChunk extends ChunkAccess { +@@ -124,7 +124,7 @@ public class LevelChunk extends ChunkAccess { + this.blockTicks = blockTickScheduler; + this.fluidTicks = fluidTickScheduler; + +- this.lightningTick = this.level.getThreadUnsafeRandom().nextInt(100000) << 1; // Pufferfish - initialize lightning tick ++ this.lightningTick = java.util.concurrent.ThreadLocalRandom.current().nextInt(100000) << 1; // Pufferfish - initialize lightning tick // Purpur - any random will do + } + + // CraftBukkit start +@@ -457,11 +457,11 @@ public class LevelChunk extends ChunkAccess { if (LightEngine.hasDifferentLightProperties(this, blockposition, iblockdata1, iblockdata)) { ProfilerFiller gameprofilerfiller = this.level.getProfiler(); @@ -19139,7 +19261,7 @@ index 2a8609e33716949ff1877b6d10f64a9d7a7c81e9..fd637415625fdabcac07e120e9168d09 } boolean flag3 = iblockdata1.hasBlockEntity(); -@@ -785,7 +785,7 @@ public class LevelChunk extends ChunkAccess { +@@ -799,7 +799,7 @@ public class LevelChunk extends ChunkAccess { this.chunkHolder.getEntityChunk().callEntitiesLoadEvent(); // Paper - rewrite chunk system if (this.needsDecoration) { @@ -19148,7 +19270,7 @@ index 2a8609e33716949ff1877b6d10f64a9d7a7c81e9..fd637415625fdabcac07e120e9168d09 this.needsDecoration = false; java.util.Random random = new java.util.Random(); random.setSeed(this.level.getSeed()); -@@ -805,7 +805,7 @@ public class LevelChunk extends ChunkAccess { +@@ -819,7 +819,7 @@ public class LevelChunk extends ChunkAccess { } } server.getPluginManager().callEvent(new org.bukkit.event.world.ChunkPopulateEvent(bukkitChunk)); @@ -19157,7 +19279,7 @@ index 2a8609e33716949ff1877b6d10f64a9d7a7c81e9..fd637415625fdabcac07e120e9168d09 } } } -@@ -1158,10 +1158,10 @@ public class LevelChunk extends ChunkAccess { +@@ -1172,10 +1172,10 @@ public class LevelChunk extends ChunkAccess { if (LevelChunk.this.isTicking(blockposition)) { try { @@ -19171,7 +19293,7 @@ index 2a8609e33716949ff1877b6d10f64a9d7a7c81e9..fd637415625fdabcac07e120e9168d09 BlockState iblockdata = LevelChunk.this.getBlockState(blockposition); if (this.blockEntity.getType().isValid(iblockdata)) { -@@ -1177,7 +1177,7 @@ public class LevelChunk extends ChunkAccess { +@@ -1191,7 +1191,7 @@ public class LevelChunk extends ChunkAccess { // Paper end - Remove the Block Entity if it's invalid } @@ -19180,7 +19302,7 @@ index 2a8609e33716949ff1877b6d10f64a9d7a7c81e9..fd637415625fdabcac07e120e9168d09 } catch (Throwable throwable) { if (throwable instanceof ThreadDeath) throw throwable; // Paper // Paper start - Prevent block entity and entity crashes -@@ -1188,7 +1188,7 @@ public class LevelChunk extends ChunkAccess { +@@ -1202,7 +1202,7 @@ public class LevelChunk extends ChunkAccess { // Paper end - Prevent block entity and entity crashes // Spigot start } finally { @@ -19237,10 +19359,10 @@ index 1b1b475ca27e799e251d6f8a8c9fe1a4fd8bae83..04f67f7b43d2f461c776c76614dc3e5f for (int l = 0; l < k; ++l) { // Paper start - PhantomPreSpawnEvent 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 c2943d892b067b3f1fb3b93301a092e912d71f08..a091c51476214977d7a9729b5c72e8478fe4a391 100644 +index 58296b67f80587af485b0068e461cfd3d8d6f96f..16f1ed279fb05d47d97692f26d083683dd942dc4 100644 --- a/src/main/java/net/minecraft/world/level/material/FlowingFluid.java +++ b/src/main/java/net/minecraft/world/level/material/FlowingFluid.java -@@ -217,7 +217,7 @@ public abstract class FlowingFluid extends Fluid { +@@ -227,7 +227,7 @@ public abstract class FlowingFluid extends Fluid { } } @@ -19249,7 +19371,7 @@ index c2943d892b067b3f1fb3b93301a092e912d71f08..a091c51476214977d7a9729b5c72e847 BlockState iblockdata2 = world.getBlockState(pos.below()); FluidState fluid1 = iblockdata2.getFluidState(); -@@ -301,6 +301,12 @@ public abstract class FlowingFluid extends Fluid { +@@ -336,6 +336,12 @@ public abstract class FlowingFluid extends Fluid { protected abstract boolean canConvertToSource(Level world); @@ -19683,14 +19805,14 @@ index 9d93130f23addb18b97d7f5ec013faef17a74529..29d2fb87a65778926aea2cfc7a5b486c + // Purpur end - OfflinePlayer API } diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java -index da1aed63af837b193900bb85393611edbd56c363..b82c66c402823874e3a5fc9bb7b1df73c708aec5 100644 +index 6262147f9d3fc1a988d9362573d3736e3ddd88a1..ba939f147edcedf0e8311e73a1ab85048354ac96 100644 --- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java +++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java @@ -269,7 +269,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 serverName = "Purpur"; // Paper // Pufferfish // Purpur private final String serverVersion; private final String bukkitVersion = Versioning.getBukkitVersion(); @@ -19740,7 +19862,7 @@ index da1aed63af837b193900bb85393611edbd56c363..b82c66c402823874e3a5fc9bb7b1df73 this.overrideAllCommandBlockCommands = this.commandsConfiguration.getStringList("command-block-overrides").contains("*"); this.ignoreVanillaPermissions = this.commandsConfiguration.getBoolean("ignore-vanilla-permissions"); -@@ -1577,6 +1594,55 @@ public final class CraftServer implements Server { +@@ -1582,6 +1599,55 @@ public final class CraftServer implements Server { return true; } @@ -19796,7 +19918,7 @@ index da1aed63af837b193900bb85393611edbd56c363..b82c66c402823874e3a5fc9bb7b1df73 @Override public List getRecipesFor(ItemStack result) { Preconditions.checkArgument(result != null, "ItemStack cannot be null"); -@@ -3039,6 +3105,18 @@ public final class CraftServer implements Server { +@@ -3044,6 +3110,18 @@ public final class CraftServer implements Server { return CraftServer.this.console.paperConfigurations.createLegacyObject(CraftServer.this.console); } @@ -19815,7 +19937,7 @@ index da1aed63af837b193900bb85393611edbd56c363..b82c66c402823874e3a5fc9bb7b1df73 @Override public void restart() { org.spigotmc.RestartCommand.restart(); -@@ -3068,6 +3146,7 @@ public final class CraftServer implements Server { +@@ -3073,6 +3151,7 @@ public final class CraftServer implements Server { @Override public double[] getTPS() { return new double[] { @@ -19823,7 +19945,7 @@ index da1aed63af837b193900bb85393611edbd56c363..b82c66c402823874e3a5fc9bb7b1df73 net.minecraft.server.MinecraftServer.getServer().tps1.getAverage(), net.minecraft.server.MinecraftServer.getServer().tps5.getAverage(), net.minecraft.server.MinecraftServer.getServer().tps15.getAverage() -@@ -3269,4 +3348,16 @@ public final class CraftServer implements Server { +@@ -3274,4 +3353,16 @@ public final class CraftServer implements Server { return this.potionBrewer; } // Paper end @@ -19894,10 +20016,10 @@ index f2b20ed5063a293f0b464548f590d652170cd1d8..226ff7c6048b510be2e71ecc5d5ff358 public Collection getStructures(int x, int z) { return this.getStructures(x, z, struct -> true); diff --git a/src/main/java/org/bukkit/craftbukkit/Main.java b/src/main/java/org/bukkit/craftbukkit/Main.java -index c988afa496d25314451435eedd64079a0d87cef0..a0a7bfe8f5a40b945b83096de1efd650adeb9ee3 100644 +index c988afa496d25314451435eedd64079a0d87cef0..43df9d815e86e8fb009b47cf16a419ddc6dbe6a7 100644 --- a/src/main/java/org/bukkit/craftbukkit/Main.java +++ b/src/main/java/org/bukkit/craftbukkit/Main.java -@@ -175,6 +175,14 @@ public class Main { +@@ -175,6 +175,20 @@ public class Main { .describedAs("Jar file"); // Paper end @@ -19907,12 +20029,18 @@ index c988afa496d25314451435eedd64079a0d87cef0..a0a7bfe8f5a40b945b83096de1efd650 + .ofType(File.class) + .defaultsTo(new File("purpur.yml")) + .describedAs("Yml file"); ++ ++ acceptsAll(asList("pufferfish", "pufferfish-settings"), "File for pufferfish settings") ++ .withRequiredArg() ++ .ofType(File.class) ++ .defaultsTo(new File("pufferfish.yml")) ++ .describedAs("Yml file"); + // Purpur end + // Paper start acceptsAll(asList("server-name"), "Name of the server") .withRequiredArg() -@@ -294,7 +302,7 @@ public class Main { +@@ -294,7 +308,7 @@ public class Main { System.setProperty(net.minecrell.terminalconsole.TerminalConsoleAppender.JLINE_OVERRIDE_PROPERTY, "false"); // Paper } @@ -20731,27 +20859,27 @@ index b3e1adeb932da9b3bed16acd94e2f16da48a7c72..d3ec817e95628f1fc8be4a29c9a0f13c // Paper end - add timings for scoreboard search } diff --git a/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java b/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java -index 5a382907285a288f2a223189e690d3dbdf45594c..3daf674f1e0de3fdd89db5536cc1e34140a5abb8 100644 +index 16d6a515e93619a0c9fdcaec5b99e0a36f8faec5..ef222478b46b5edc7b4c6d636a5b452fb10221bc 100644 --- a/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java +++ b/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java @@ -511,7 +511,7 @@ public final class CraftMagicNumbers implements UnsafeValues { @Override public com.destroystokyo.paper.util.VersionFetcher getVersionFetcher() { -- return new com.destroystokyo.paper.PaperVersionFetcher(); -+ return new com.destroystokyo.paper.PaperVersionFetcher(); // Purpur - TODO: Pufferfish +- return new gg.pufferfish.pufferfish.PufferfishVersionFetcher(); // Pufferfish ++ return new com.destroystokyo.paper.PaperVersionFetcher(); // Pufferfish // Purpur } @Override diff --git a/src/main/java/org/bukkit/craftbukkit/util/Versioning.java b/src/main/java/org/bukkit/craftbukkit/util/Versioning.java -index 774556a62eb240da42e84db4502e2ed43495be17..99597258e8e88cd9e2c901c4ac3ff7faeeabee2b 100644 +index 80553face9c70c2a3d897681e7761df85b22d464..99597258e8e88cd9e2c901c4ac3ff7faeeabee2b 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 + InputStream stream = Bukkit.class.getClassLoader().getResourceAsStream("META-INF/maven/org.purpurmc.purpur/purpur-api/pom.properties"); // Pufferfish // Purpur Properties properties = new Properties(); @@ -20779,10 +20907,10 @@ index 52649f82351ab4f675c3cc3cd6640956b0f76b91..eb51c88c7a0658190d3a8bfd5d18dca7 DefaultPermissions.registerPermission(CommandPermissions.PREFIX + "seed", "Allows the user to view the seed of the world", PermissionDefault.OP, commands); diff --git a/src/main/java/org/purpurmc/purpur/PurpurConfig.java b/src/main/java/org/purpurmc/purpur/PurpurConfig.java new file mode 100644 -index 0000000000000000000000000000000000000000..de70bfdbdd7740fdf20dd063a7a623a9e96cef98 +index 0000000000000000000000000000000000000000..1b1981e80bb0ca5f5edceef96b88168bd9cc839b --- /dev/null +++ b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -@@ -0,0 +1,601 @@ +@@ -0,0 +1,596 @@ +package org.purpurmc.purpur; + +import com.google.common.base.Throwables; @@ -21265,11 +21393,6 @@ index 0000000000000000000000000000000000000000..de70bfdbdd7740fdf20dd063a7a623a9 + org.bukkit.plugin.java.JavaPluginLoader.SuppressLibraryLoaderLogger = loggerSuppressLibraryLoader; + } + -+ public static boolean tpsCatchup = true; -+ private static void tpsCatchup() { -+ tpsCatchup = getBoolean("settings.tps-catchup", tpsCatchup); -+ } -+ + public static boolean useUPnP = false; + public static boolean maxJoinsPerSecond = false; + public static boolean kickForOutOfOrderChat = true; @@ -27019,7 +27142,7 @@ index 0000000000000000000000000000000000000000..b7586f494528f30eb0da82420d3bcf5b + } +} diff --git a/src/main/java/org/spigotmc/ActivationRange.java b/src/main/java/org/spigotmc/ActivationRange.java -index 3283ed99c35ffed6805567705e0518d9f84feedc..de2b469f06f6679aed1d20156052bfbef5e7c30b 100644 +index d7c7e12c0b8f77e59d94de130972f762ed227726..56e52b16b419c882440a15947f037ae1a902bc70 100644 --- a/src/main/java/org/spigotmc/ActivationRange.java +++ b/src/main/java/org/spigotmc/ActivationRange.java @@ -15,6 +15,7 @@ import net.minecraft.world.entity.ambient.AmbientCreature; @@ -27030,7 +27153,7 @@ index 3283ed99c35ffed6805567705e0518d9f84feedc..de2b469f06f6679aed1d20156052bfbe import net.minecraft.world.entity.animal.WaterAnimal; import net.minecraft.world.entity.animal.horse.Llama; import net.minecraft.world.entity.boss.EnderDragonPart; -@@ -167,7 +168,7 @@ public class ActivationRange +@@ -171,7 +172,7 @@ public class ActivationRange */ public static void activateEntities(Level world) { @@ -27039,7 +27162,7 @@ index 3283ed99c35ffed6805567705e0518d9f84feedc..de2b469f06f6679aed1d20156052bfbe final int miscActivationRange = world.spigotConfig.miscActivationRange; final int raiderActivationRange = world.spigotConfig.raiderActivationRange; final int animalActivationRange = world.spigotConfig.animalActivationRange; -@@ -201,6 +202,7 @@ public class ActivationRange +@@ -205,6 +206,7 @@ public class ActivationRange continue; } @@ -27047,7 +27170,7 @@ index 3283ed99c35ffed6805567705e0518d9f84feedc..de2b469f06f6679aed1d20156052bfbe // Paper start int worldHeight = world.getHeight(); ActivationRange.maxBB = player.getBoundingBox().inflate( maxRange, worldHeight, maxRange ); -@@ -226,7 +228,7 @@ public class ActivationRange +@@ -249,7 +251,7 @@ public class ActivationRange } // Paper end } @@ -27056,7 +27179,7 @@ index 3283ed99c35ffed6805567705e0518d9f84feedc..de2b469f06f6679aed1d20156052bfbe } /** -@@ -379,6 +381,7 @@ public class ActivationRange +@@ -402,6 +404,7 @@ public class ActivationRange */ public static boolean checkIfActive(Entity entity) { diff --git a/patches/server/0002-Use-Gradle-Version-Catalogs.patch b/patches/server/0003-Use-Gradle-Version-Catalogs.patch similarity index 86% rename from patches/server/0002-Use-Gradle-Version-Catalogs.patch rename to patches/server/0003-Use-Gradle-Version-Catalogs.patch index e21d3f241..649ba1a32 100644 --- a/patches/server/0002-Use-Gradle-Version-Catalogs.patch +++ b/patches/server/0003-Use-Gradle-Version-Catalogs.patch @@ -5,13 +5,14 @@ Subject: [PATCH] Use Gradle Version Catalogs diff --git a/build.gradle.kts b/build.gradle.kts -index 89f21e21efc5f5ab99d1586f4f3b8f2f3c48ac06..ec151f06541d7c3b4ffb1cac75e94bf3914dacaf 100644 +index 3bece6335887414d1388505febf6b8796e32bf9a..055c64f6dbe6598e88fa2a01e80062fedcce4e20 100644 --- a/build.gradle.kts +++ b/build.gradle.kts -@@ -13,57 +13,26 @@ val alsoShade: Configuration by configurations.creating +@@ -12,66 +12,28 @@ configurations.named(log4jPlugins.compileClasspathConfigurationName) { + val alsoShade: Configuration by configurations.creating dependencies { - implementation(project(":purpur-api")) // Purpur +- implementation(project(":purpur-api")) // Pufferfish // Paper // Purpur - // Paper start - implementation("org.jline:jline-terminal-jansi:3.21.0") - implementation("net.minecrell:terminalconsoleappender:1.3.0") @@ -40,29 +41,11 @@ index 89f21e21efc5f5ab99d1586f4f3b8f2f3c48ac06..ec151f06541d7c3b4ffb1cac75e94bf3 - isTransitive = false - } - // Paper end - Use Velocity cipher -- ++ implementation(project(":purpur-api")) // Purpur + - runtimeOnly("org.apache.maven:maven-resolver-provider:3.9.6") - runtimeOnly("org.apache.maven.resolver:maven-resolver-connector-basic:1.9.18") - runtimeOnly("org.apache.maven.resolver:maven-resolver-transport-http:1.9.18") -- -- implementation("org.mozilla:rhino-runtime:1.7.14") // Purpur -- implementation("org.mozilla:rhino-engine:1.7.14") // Purpur -- implementation("dev.omega24:upnp4j:1.0") // Purpur -- -- testImplementation("io.github.classgraph:classgraph:4.8.47") // Paper - mob goal test -- testImplementation("org.junit.jupiter:junit-jupiter:5.10.2") -- testImplementation("org.hamcrest:hamcrest:2.2") -- testImplementation("org.mockito:mockito-core:5.11.0") -- testImplementation("org.ow2.asm:asm-tree:9.7") -- testImplementation("org.junit-pioneer:junit-pioneer:2.2.0") // Paper - CartesianTest -- implementation("net.neoforged:srgutils:1.0.9") // Paper - mappings handling -- implementation("net.neoforged:AutoRenamingTool:2.0.3") // Paper - remap plugins -- // Paper start - Remap reflection -- val reflectionRewriterVersion = "0.0.1" -- implementation("io.papermc:reflection-rewriter:$reflectionRewriterVersion") -- implementation("io.papermc:reflection-rewriter-runtime:$reflectionRewriterVersion") -- implementation("io.papermc:reflection-rewriter-proxy-generator:$reflectionRewriterVersion") -- // Paper end - Remap reflection + // Plazma start - Use Gradle Version Catalogs + implementation(server.bundles.implementation) + implementation(common.asm.commons) @@ -82,7 +65,33 @@ index 89f21e21efc5f5ab99d1586f4f3b8f2f3c48ac06..ec151f06541d7c3b4ffb1cac75e94bf3 + testImplementation(server.bundles.test) + testImplementation(common.bundles.test) + // Plazma end - Use Gradle Version Catalogs -+ + +- // Pufferfish start +- implementation("org.yaml:snakeyaml:1.32") +- implementation ("me.carleslc.Simple-YAML:Simple-Yaml:1.8.4") { +- exclude(group="org.yaml", module="snakeyaml") +- } +- // Pufferfish end +- implementation("com.github.technove:Flare:34637f3f87") // Pufferfish - flare +- +- implementation("org.mozilla:rhino-runtime:1.7.14") // Purpur +- implementation("org.mozilla:rhino-engine:1.7.14") // Purpur +- implementation("dev.omega24:upnp4j:1.0") // Purpur +- +- testImplementation("io.github.classgraph:classgraph:4.8.47") // Paper - mob goal test +- testImplementation("org.junit.jupiter:junit-jupiter:5.10.2") +- testImplementation("org.hamcrest:hamcrest:2.2") +- testImplementation("org.mockito:mockito-core:5.11.0") +- testImplementation("org.ow2.asm:asm-tree:9.7") +- testImplementation("org.junit-pioneer:junit-pioneer:2.2.0") // Paper - CartesianTest +- implementation("net.neoforged:srgutils:1.0.9") // Paper - mappings handling +- implementation("net.neoforged:AutoRenamingTool:2.0.3") // Paper - remap plugins +- // Paper start - Remap reflection +- val reflectionRewriterVersion = "0.0.1" +- implementation("io.papermc:reflection-rewriter:$reflectionRewriterVersion") +- implementation("io.papermc:reflection-rewriter-runtime:$reflectionRewriterVersion") +- implementation("io.papermc:reflection-rewriter-proxy-generator:$reflectionRewriterVersion") +- // Paper end - Remap reflection } paperweight { diff --git a/patches/server/0003-Rebrand.patch b/patches/server/0004-Rebrand.patch similarity index 98% rename from patches/server/0003-Rebrand.patch rename to patches/server/0004-Rebrand.patch index 87c34a523..64685dff9 100644 --- a/patches/server/0003-Rebrand.patch +++ b/patches/server/0004-Rebrand.patch @@ -32,19 +32,20 @@ index 0000000000000000000000000000000000000000..67d405764ed68083d0164b1af0eabc0a +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF +OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/build.gradle.kts b/build.gradle.kts -index ec151f06541d7c3b4ffb1cac75e94bf3914dacaf..3cf30564db4e5cb2eeb09258940d0b2b5f9b0edc 100644 +index 055c64f6dbe6598e88fa2a01e80062fedcce4e20..d6482fd7d3ca31208cacd420317a30f4597ef83a 100644 --- a/build.gradle.kts +++ b/build.gradle.kts -@@ -12,7 +12,7 @@ configurations.named(log4jPlugins.compileClasspathConfigurationName) { +@@ -12,8 +12,8 @@ configurations.named(log4jPlugins.compileClasspathConfigurationName) { val alsoShade: Configuration by configurations.creating dependencies { - implementation(project(":purpur-api")) // Purpur + + implementation(project(":${parent!!.property("projectName").toString().lowercase()}-api")) // Plazma - Rebrand // Plazma start - Use Gradle Version Catalogs implementation(server.bundles.implementation) implementation(common.asm.commons) -@@ -51,7 +51,7 @@ tasks.jar { +@@ -59,7 +59,7 @@ tasks.jar { attributes( "Main-Class" to "org.bukkit.craftbukkit.Main", "Implementation-Title" to "CraftBukkit", @@ -244,10 +245,10 @@ index 411f1f8c6be072cfc5ba88cbec38dbc4300a41d1..0fb3eeab083a0b1e9b495bd2c5c556fc Main.LOGGER.error(awtException); return; diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java -index dfe3d28bd97d1335457d1005ecf69afe75d5ff80..dc571ad9882a6c35e25b20086da1f2c51d3b4b57 100644 +index 4e989dd5919e29d04dcd9f6af0f3922f94d41742..2b015360df398ad8118f7b108cb9474edf373121 100644 --- a/src/main/java/net/minecraft/server/MinecraftServer.java +++ b/src/main/java/net/minecraft/server/MinecraftServer.java -@@ -495,8 +495,8 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop getTypeKey(); + //protected final io.papermc.paper.util.math.ThreadUnsafeRandom randomTickRandom = new io.papermc.paper.util.math.ThreadUnsafeRandom(java.util.concurrent.ThreadLocalRandom.current().nextLong()); public net.minecraft.util.RandomSource getThreadUnsafeRandom() { return this.randomTickRandom; } // Pufferfish - move thread unsafe random initialization // Pufferfish - getter // Purpur - dont break ABI - protected Level(WritableLevelData worlddatamutable, ResourceKey resourcekey, RegistryAccess iregistrycustom, Holder holder, Supplier supplier, boolean flag, boolean flag1, long i, int j, org.bukkit.generator.ChunkGenerator gen, org.bukkit.generator.BiomeProvider biomeProvider, org.bukkit.World.Environment env, java.util.function.Function paperWorldConfigCreator, java.util.concurrent.Executor executor) { // Paper - create paper world config; Async-Anti-Xray: Pass executor + // Plazma start - Configurable Plazma @@ -704,8 +704,33 @@ index eda2f8cc034cf46293be1be117a60cf8b663c303..1e5964f315b3e944d249032744991721 this.purpurConfig = new org.purpurmc.purpur.PurpurWorldConfig(((net.minecraft.world.level.storage.PrimaryLevelData) worlddatamutable).getLevelName(), env); // Purpur this.playerBreedingCooldowns = this.getNewBreedingCooldownCache(); // Purpur this.generator = gen; +@@ -1356,13 +1381,13 @@ public abstract class Level implements LevelAccessor, AutoCloseable { + try { + tickConsumer.accept(entity); + MinecraftServer.getServer().executeMidTickTasks(); // Paper - execute chunk tasks mid tick +- } catch (Throwable throwable) { // Pufferfish - diff on change ServerLevel.tick ++ } catch (Throwable throwable) { + if (throwable instanceof ThreadDeath) throw throwable; // Paper + // Paper start - Prevent block 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))); // Paper - ServerExceptionEvent +- entity.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.DISCARD); // Pufferfish - diff on change ServerLevel.tick ++ entity.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.DISCARD); + // Paper end - Prevent block entity and entity crashes + } + } +@@ -1837,7 +1862,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable { + } + + public ProfilerFiller getProfiler() { +- if (true || gg.pufferfish.pufferfish.PufferfishConfig.disableMethodProfiler) return net.minecraft.util.profiling.InactiveProfiler.INSTANCE; // Pufferfish // Purpur ++ //if (true || gg.pufferfish.pufferfish.PufferfishConfig.disableMethodProfiler) return net.minecraft.util.profiling.InactiveProfiler.INSTANCE; // Pufferfish // Purpur // Purpur - TODO: Pufferfish + return (ProfilerFiller) this.profiler.get(); + } + diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java -index ebc75eb4ae38dd2f24eb81e5797ae48e25ae93c7..841ce275ff3f49783a61af71d83020b8cda35473 100644 +index fe78cb60fe6000004c9e567e9046c45dd0e22d59..1afae84afd5a6fcc11227daf75123e8b65642b01 100644 --- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java +++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java @@ -1052,6 +1052,7 @@ public final class CraftServer implements Server { @@ -716,7 +741,7 @@ index ebc75eb4ae38dd2f24eb81e5797ae48e25ae93c7..841ce275ff3f49783a61af71d83020b8 org.purpurmc.purpur.PurpurConfig.init((File) console.options.valueOf("purpur-settings")); // Purpur for (ServerLevel world : this.console.getAllLevels()) { // world.serverLevelData.setDifficulty(config.difficulty); // Paper - per level difficulty -@@ -3117,6 +3118,13 @@ public final class CraftServer implements Server { +@@ -3122,6 +3123,13 @@ public final class CraftServer implements Server { } // Purpur end @@ -731,10 +756,10 @@ index ebc75eb4ae38dd2f24eb81e5797ae48e25ae93c7..841ce275ff3f49783a61af71d83020b8 public void restart() { org.spigotmc.RestartCommand.restart(); diff --git a/src/main/java/org/bukkit/craftbukkit/Main.java b/src/main/java/org/bukkit/craftbukkit/Main.java -index 369543328531b0bd12c76aa8552e565bc40b51f7..40fd40da9a7fc2b12035b99960e7f38c78eb2a5e 100644 +index 7d7134820ce73de1eef242d14d57a520d3db2a25..3187d9fdfe32aa38a979406e798f6a6bcc1158a0 100644 --- a/src/main/java/org/bukkit/craftbukkit/Main.java +++ b/src/main/java/org/bukkit/craftbukkit/Main.java -@@ -190,6 +190,14 @@ public class Main { +@@ -196,6 +196,14 @@ public class Main { .defaultsTo("A " + org.bukkit.craftbukkit.util.Versioning.BRANDING + " Server") // Plazma - Rebrand .describedAs("Name"); // Paper end diff --git a/patches/server/0006-Setup-basic-configuration-sections.patch b/patches/server/0007-Setup-basic-configuration-sections.patch similarity index 100% rename from patches/server/0006-Setup-basic-configuration-sections.patch rename to patches/server/0007-Setup-basic-configuration-sections.patch diff --git a/patches/server/0007-Warn-on-startup.patch b/patches/server/0008-Warn-on-startup.patch similarity index 100% rename from patches/server/0007-Warn-on-startup.patch rename to patches/server/0008-Warn-on-startup.patch diff --git a/patches/server/0008-Development-Build.patch b/patches/server/0009-Development-Build.patch similarity index 92% rename from patches/server/0008-Development-Build.patch rename to patches/server/0009-Development-Build.patch index e5382753b..cd648ed62 100644 --- a/patches/server/0008-Development-Build.patch +++ b/patches/server/0009-Development-Build.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Development Build diff --git a/src/main/java/org/bukkit/craftbukkit/util/Versioning.java b/src/main/java/org/bukkit/craftbukkit/util/Versioning.java -index c08b91752f66a99bc6cd4495a33a400fba2b5429..02787dc98e26e505182c041049dae3922de63683 100644 +index 317ac7c5f1c2d542be260f7e86f9a7281506ebe0..2612df0680800e8c4644599b1885230778c812b7 100644 --- a/src/main/java/org/bukkit/craftbukkit/util/Versioning.java +++ b/src/main/java/org/bukkit/craftbukkit/util/Versioning.java @@ -18,7 +18,7 @@ public final class Versioning { diff --git a/patches/server/0009-Port-SparklyPaper-patches.patch b/patches/server/0010-Port-SparklyPaper-patches.patch similarity index 95% rename from patches/server/0009-Port-SparklyPaper-patches.patch rename to patches/server/0010-Port-SparklyPaper-patches.patch index dd5181d54..75835c122 100644 --- a/patches/server/0009-Port-SparklyPaper-patches.patch +++ b/patches/server/0010-Port-SparklyPaper-patches.patch @@ -137,18 +137,18 @@ index f164256d59b761264876ca0c85f812d101bfd5de..deaeb134c47da8710afa747bf980bd00 final TrackedChunk chunk = this.byChunk.get(CoordinateUtils.getChunkKey(chunkX, chunkZ)); diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java -index 7d20729d18187d2f576a1fe2f9b633429ee96923..9d5257f810f9f88504b913fa0c7c214acc1da2cb 100644 +index 690423f4159ddff2d90d7ccf931db72903cb822c..c9a5c3b915f09a316f0f2725c51b696da111ccd3 100644 --- a/src/main/java/net/minecraft/server/MinecraftServer.java +++ b/src/main/java/net/minecraft/server/MinecraftServer.java -@@ -323,6 +323,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop entitiesWithScheduledTasks = java.util.concurrent.ConcurrentHashMap.newKeySet(); // Plazma - Port SparklyPaper patches; Skip EntityScheduler's executeTick checks if there isn't any tasks to be run public static S spin(Function serverFactory) { AtomicReference atomicreference = new AtomicReference(); -@@ -1743,17 +1744,15 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop playersNearby @@ -230,7 +239,7 @@ index 476a04d87a61b021816d2970e86042bde32d95a2..53c0b48966558f43bb9bd7d29d9588f9 continue; } diff --git a/src/main/java/net/minecraft/server/level/ServerEntity.java b/src/main/java/net/minecraft/server/level/ServerEntity.java -index dd6278fe99e2ee4daa95249c71ea935dd08b0025..a528685040bf5596fe20fc055d691fd7e663762c 100644 +index 78be6cea03f40edfa90b5d9df014d5cf22b81f61..c376997ea2b8f61c2dbd80b31d51ba731fd5ed4c 100644 --- a/src/main/java/net/minecraft/server/level/ServerEntity.java +++ b/src/main/java/net/minecraft/server/level/ServerEntity.java @@ -78,6 +78,7 @@ public class ServerEntity { @@ -241,7 +250,7 @@ index dd6278fe99e2ee4daa95249c71ea935dd08b0025..a528685040bf5596fe20fc055d691fd7 public ServerEntity(ServerLevel worldserver, Entity entity, int i, boolean flag, Consumer> consumer, Set trackedPlayers) { this.trackedPlayers = trackedPlayers; -@@ -215,12 +216,15 @@ public class ServerEntity { +@@ -217,12 +218,15 @@ public class ServerEntity { if ((this.trackDelta || this.entity.hasImpulse || this.entity instanceof LivingEntity && ((LivingEntity) this.entity).isFallFlying()) && this.tickCount > 0) { Vec3 vec3d1 = this.entity.getDeltaMovement(); @@ -263,7 +272,7 @@ index dd6278fe99e2ee4daa95249c71ea935dd08b0025..a528685040bf5596fe20fc055d691fd7 if (packet1 != null) { diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java -index 5df20b97f3c88248ab70e7686b5fa0ca8e5a58c2..d1b2e68a702ce01c33cfe0e1297a8809e3acf088 100644 +index 5456d2ded6f6e75c48868147e5a2dde5eb23bec8..e2de0c343098d65d830f27b4f80c3f8a9f18a7ae 100644 --- a/src/main/java/net/minecraft/server/level/ServerLevel.java +++ b/src/main/java/net/minecraft/server/level/ServerLevel.java @@ -233,6 +233,12 @@ public class ServerLevel extends Level implements WorldGenLevel { @@ -316,7 +325,7 @@ index f890738d3bb9fb5e70a9d323c6cec97f9948f9cf..eb3fc1af68768383d524cf0f50c4f4b3 } } 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 47a7c7f9527e1c4ea457eeafe0e11145653a871f..d2151a355e3ee8a6f8b4f28b2ce7ac382c939ad3 100644 +index a70255995f380a9b0f7057cb5cb820f7655b93fc..2bffc790496fd9b09516d0d908b5319c9dbb1c63 100644 --- a/src/main/java/net/minecraft/world/entity/ambient/Bat.java +++ b/src/main/java/net/minecraft/world/entity/ambient/Bat.java @@ -306,7 +306,7 @@ public class Bat extends AmbientCreature { @@ -328,11 +337,12 @@ index 47a7c7f9527e1c4ea457eeafe0e11145653a871f..d2151a355e3ee8a6f8b4f28b2ce7ac38 b0 = 7; } else if (random.nextBoolean()) { return false; -@@ -316,7 +316,24 @@ public class Bat extends AmbientCreature { - } - } - +@@ -320,7 +320,25 @@ public class Bat extends AmbientCreature { + private static boolean isSpookySeason = false; + private static final int ONE_HOUR = 20 * 60 * 60; + private static int lastSpookyCheck = -ONE_HOUR; - public static boolean isHalloweenSeason(Level level) { return level.purpurConfig.forceHalloweenSeason || isHalloween(); } // Purpur ++ + // Plazma start - Port SparklyPaper patches; Optimize Spooky Season + private static boolean isSpookySeason(Level level) { + if (level.purpurConfig.forceHalloweenSeason) return true; @@ -352,8 +362,8 @@ index 47a7c7f9527e1c4ea457eeafe0e11145653a871f..d2151a355e3ee8a6f8b4f28b2ce7ac38 + @SuppressWarnings("RedundantExplicitChronoField") + // Plazma end - Port SparklyPaper patches; Optimize Spooky Season 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); diff --git a/src/main/java/net/minecraft/world/item/MapItem.java b/src/main/java/net/minecraft/world/item/MapItem.java index 608390ed36710a419de1542b80340dd3fcc7299c..043f068345ca3c50209c1c3cc1feb6277a3da61a 100644 --- a/src/main/java/net/minecraft/world/item/MapItem.java @@ -373,10 +383,10 @@ index 608390ed36710a419de1542b80340dd3fcc7299c..043f068345ca3c50209c1c3cc1feb627 mapItemSavedData.tickCarriedBy(player, stack); } diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java -index 1e5964f315b3e944d249032744991721c73a62ce..cc402c84734dc1841ce09f7bfd07936c745e6662 100644 +index 59d6d25ce3d37a7d408b168aec3c2a90a6d1dfab..23f10ed05e2c4aabfa3a565a9117dabe52f175f7 100644 --- a/src/main/java/net/minecraft/world/level/Level.java +++ b/src/main/java/net/minecraft/world/level/Level.java -@@ -1345,6 +1345,8 @@ public abstract class Level implements LevelAccessor, AutoCloseable { +@@ -1347,6 +1347,8 @@ public abstract class Level implements LevelAccessor, AutoCloseable { boolean flag = this.tickRateManager().runsNormally(); int tilesThisCycle = 0; @@ -385,7 +395,7 @@ index 1e5964f315b3e944d249032744991721c73a62ce..cc402c84734dc1841ce09f7bfd07936c var toRemove = new it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet(); // Paper - Fix MC-117075; use removeAll toRemove.add(null); // Paper - Fix MC-117075 for (tileTickPosition = 0; tileTickPosition < this.blockEntityTickers.size(); tileTickPosition++) { // Paper - Disable tick limiters -@@ -1357,14 +1359,28 @@ public abstract class Level implements LevelAccessor, AutoCloseable { +@@ -1359,14 +1361,28 @@ public abstract class Level implements LevelAccessor, AutoCloseable { tilesThisCycle--; toRemove.add(tickingblockentity); // Paper - Fix MC-117075; use removeAll // Spigot end @@ -689,7 +699,7 @@ index 1aac95b03a9e2e37c24f2a30bcb259c1424e1c78..e7c0c36fd3455c0536e98259b46dbcc9 this.levelHeightAccessor = heightLimitView; this.sections = new LevelChunkSection[heightLimitView.getSectionsCount()]; 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 fd637415625fdabcac07e120e9168d09c06141d4..37120fcb31f4c9a90be37db252b17eb93db2cb0f 100644 +index 56dd36a88f11aeac528e7be2998e52324c01fde1..e1853318350f9a10e8db106a7bc015307db0de6c 100644 --- a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java +++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java @@ -75,6 +75,8 @@ public class LevelChunk extends ChunkAccess { @@ -701,7 +711,7 @@ index fd637415625fdabcac07e120e9168d09c06141d4..37120fcb31f4c9a90be37db252b17eb9 }; private final Map tickersInLevel; public boolean loaded; -@@ -1087,7 +1089,7 @@ public class LevelChunk extends ChunkAccess { +@@ -1101,7 +1103,7 @@ public class LevelChunk extends ChunkAccess { } private TickingBlockEntity createTicker(T blockEntity, BlockEntityTicker blockEntityTicker) { @@ -710,7 +720,7 @@ index fd637415625fdabcac07e120e9168d09c06141d4..37120fcb31f4c9a90be37db252b17eb9 } @FunctionalInterface -@@ -1138,17 +1140,21 @@ public class LevelChunk extends ChunkAccess { +@@ -1152,17 +1154,21 @@ public class LevelChunk extends ChunkAccess { public String toString() { return String.valueOf(this.ticker) + " "; } @@ -733,7 +743,7 @@ index fd637415625fdabcac07e120e9168d09c06141d4..37120fcb31f4c9a90be37db252b17eb9 } @Override -@@ -1216,5 +1222,7 @@ public class LevelChunk extends ChunkAccess { +@@ -1230,5 +1236,7 @@ public class LevelChunk extends ChunkAccess { return "Level ticker for " + s + "@" + String.valueOf(this.getPos()); } diff --git a/patches/server/0010-Always-agree-EULA-on-development-mode.patch b/patches/server/0011-Always-agree-EULA-on-development-mode.patch similarity index 89% rename from patches/server/0010-Always-agree-EULA-on-development-mode.patch rename to patches/server/0011-Always-agree-EULA-on-development-mode.patch index 6a05aab74..5edf5004c 100644 --- a/patches/server/0010-Always-agree-EULA-on-development-mode.patch +++ b/patches/server/0011-Always-agree-EULA-on-development-mode.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Always agree EULA on development mode diff --git a/src/main/java/net/minecraft/server/Main.java b/src/main/java/net/minecraft/server/Main.java -index c59a1b6a365bf80ac913c1a9c939ad2cc5f3b963..20d215e53b4a02d7f52197decf05fd1e6ee44669 100644 +index ce7782dd216922ad536bb435034255508271bfa4..c464e8440e854bd0d68042fdb9937fcfa8b77485 100644 --- a/src/main/java/net/minecraft/server/Main.java +++ b/src/main/java/net/minecraft/server/Main.java @@ -193,6 +193,7 @@ public class Main { diff --git a/patches/server/0011-Add-more-metrics.patch b/patches/server/0012-Add-more-metrics.patch similarity index 98% rename from patches/server/0011-Add-more-metrics.patch rename to patches/server/0012-Add-more-metrics.patch index d1f981a50..a1d65aa4e 100644 --- a/patches/server/0011-Add-more-metrics.patch +++ b/patches/server/0012-Add-more-metrics.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Add more metrics diff --git a/src/main/java/com/destroystokyo/paper/Metrics.java b/src/main/java/com/destroystokyo/paper/Metrics.java -index 1de03b16c513d83550077bab46b52ae6e40eb98e..0935884fa747407d262178c25388073957d4547e 100644 +index 5eefe30d47b055fd544caebfb77f6f5a12894fda..b52df693c32b86617ed3afefb4b24c91eab67273 100644 --- a/src/main/java/com/destroystokyo/paper/Metrics.java +++ b/src/main/java/com/destroystokyo/paper/Metrics.java @@ -636,38 +636,59 @@ public class Metrics { diff --git a/patches/server/0012-Optimize-default-configurations.patch b/patches/server/0013-Optimize-default-configurations.patch similarity index 98% rename from patches/server/0012-Optimize-default-configurations.patch rename to patches/server/0013-Optimize-default-configurations.patch index 02e647536..d24943bed 100644 --- a/patches/server/0012-Optimize-default-configurations.patch +++ b/patches/server/0013-Optimize-default-configurations.patch @@ -9,10 +9,10 @@ Subject: [PATCH] Optimize default configurations - YouHaveTrouble/minecraft-exploits-and-how-to-fix-them diff --git a/src/main/java/io/papermc/paper/configuration/GlobalConfiguration.java b/src/main/java/io/papermc/paper/configuration/GlobalConfiguration.java -index 2874bc3001c4e7d9191e47ba512c5a68369c21f1..750dd556123f3f912f8463ca3018256aaca5a2cb 100644 +index 32035e37b39ba42232fea948166e7c1d4d06190c..8445a0b25d647b2c1f9a44f849084cdec0842e18 100644 --- a/src/main/java/io/papermc/paper/configuration/GlobalConfiguration.java +++ b/src/main/java/io/papermc/paper/configuration/GlobalConfiguration.java -@@ -156,7 +156,7 @@ public class GlobalConfiguration extends ConfigurationPart { +@@ -167,7 +167,7 @@ public class GlobalConfiguration extends ConfigurationPart { public class Watchdog extends ConfigurationPart { public int earlyWarningEvery = 5000; @@ -21,7 +21,7 @@ index 2874bc3001c4e7d9191e47ba512c5a68369c21f1..750dd556123f3f912f8463ca3018256a } public SpamLimiter spamLimiter; -@@ -196,7 +196,7 @@ public class GlobalConfiguration extends ConfigurationPart { +@@ -207,7 +207,7 @@ public class GlobalConfiguration extends ConfigurationPart { public Commands commands; public class Commands extends ConfigurationPart { @@ -30,7 +30,7 @@ index 2874bc3001c4e7d9191e47ba512c5a68369c21f1..750dd556123f3f912f8463ca3018256a public boolean fixTargetSelectorTagCompletion = true; public boolean timeCommandAffectsAllWorlds = false; } -@@ -245,7 +245,7 @@ public class GlobalConfiguration extends ConfigurationPart { +@@ -256,7 +256,7 @@ public class GlobalConfiguration extends ConfigurationPart { public BookSize bookSize; public class BookSize extends ConfigurationPart { @@ -39,7 +39,7 @@ index 2874bc3001c4e7d9191e47ba512c5a68369c21f1..750dd556123f3f912f8463ca3018256a public double totalMultiplier = 0.98D; // TODO this should probably be merged into the above inner class } public boolean resolveSelectorsInBooks = false; -@@ -256,7 +256,15 @@ public class GlobalConfiguration extends ConfigurationPart { +@@ -267,7 +267,15 @@ public class GlobalConfiguration extends ConfigurationPart { public class PacketLimiter extends ConfigurationPart { public Component kickMessage = Component.translatable("disconnect.exceeded_packet_rate", NamedTextColor.RED); public PacketLimit allPackets = new PacketLimit(7.0, 500.0, PacketLimit.ViolateAction.KICK); @@ -56,7 +56,7 @@ index 2874bc3001c4e7d9191e47ba512c5a68369c21f1..750dd556123f3f912f8463ca3018256a @ConfigSerializable public record PacketLimit(@Required double interval, @Required double maxPacketRate, ViolateAction action) { -@@ -324,7 +332,7 @@ public class GlobalConfiguration extends ConfigurationPart { +@@ -335,7 +343,7 @@ public class GlobalConfiguration extends ConfigurationPart { executor.setMaximumPoolSize(_chatExecutorMaxSize); } } @@ -324,7 +324,7 @@ index 55e16ed1151c5d53f492581f66a3406ac418b653..9af7df196ed74a48a6b6426df36353f9 this.enableJmxMonitoring = this.get("enable-jmx-monitoring", false); this.enableStatus = this.get("enable-status", true); diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java -index 841ce275ff3f49783a61af71d83020b8cda35473..509c7536b84019c854163471cb9d42b969747366 100644 +index 1afae84afd5a6fcc11227daf75123e8b65642b01..6de746c3e0f30bcf3dc24b2a4d369c86db445738 100644 --- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java +++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java @@ -433,7 +433,7 @@ public final class CraftServer implements Server { @@ -368,7 +368,7 @@ index 09053b4ccf268fd204c81dbb8d4f10fa9edcad5f..93f67f125b3674e645cfdae27e579e12 #### ENGLISH #### This is the %s configuration file for Plazma. diff --git a/src/main/java/org/purpurmc/purpur/PurpurConfig.java b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -index 85f4a9300aebfd4ee64d179dfa1f7ffd314cb3b8..fa209964eec7dae8e2a0fa7bb44e0e8791e75248 100644 +index 2494f0e1c4a1838f6d217d8727d25384180218f5..69066fd1e82cefff44c7dbd565b19373768e2a62 100644 --- a/src/main/java/org/purpurmc/purpur/PurpurConfig.java +++ b/src/main/java/org/purpurmc/purpur/PurpurConfig.java @@ -49,6 +49,7 @@ public class PurpurConfig { @@ -388,7 +388,7 @@ index 85f4a9300aebfd4ee64d179dfa1f7ffd314cb3b8..fa209964eec7dae8e2a0fa7bb44e0e87 private static void useAlternateKeepAlive() { useAlternateKeepAlive = getBoolean("settings.use-alternate-keepalive", useAlternateKeepAlive); } -@@ -486,7 +487,7 @@ public class PurpurConfig { +@@ -481,7 +482,7 @@ public class PurpurConfig { } public static boolean useUPnP = false; diff --git a/patches/server/0013-Tweak-console-logging.patch b/patches/server/0014-Tweak-console-logging.patch similarity index 96% rename from patches/server/0013-Tweak-console-logging.patch rename to patches/server/0014-Tweak-console-logging.patch index c4fc70d20..b53e80f9e 100644 --- a/patches/server/0013-Tweak-console-logging.patch +++ b/patches/server/0014-Tweak-console-logging.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Tweak console logging diff --git a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java -index ca1e8da7540457ecba5684acd42129509c8204f5..9af4efcf9a2c8dee2208ddf37b1bb963a2894666 100644 +index 53134751868d37a2659aef74e37c0466b9238197..5057997c3d89e058f17798f81c27766c99c0cba7 100644 --- a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java +++ b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java @@ -193,16 +193,6 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface @@ -43,7 +43,7 @@ index ca1e8da7540457ecba5684acd42129509c8204f5..9af4efcf9a2c8dee2208ddf37b1bb963 // Paper start - fix converting txt to json file; convert old users earlier after PlayerList creation but before file load/save if (this.convertOldUsers()) { this.getProfileCache().save(false); // Paper -@@ -330,6 +331,7 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface +@@ -334,6 +335,7 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface String proxyFlavor = (io.papermc.paper.configuration.GlobalConfiguration.get().proxies.velocity.enabled) ? "Velocity" : "BungeeCord"; String proxyLink = (io.papermc.paper.configuration.GlobalConfiguration.get().proxies.velocity.enabled) ? "https://docs.papermc.io/velocity/security" : "http://www.spigotmc.org/wiki/firewall-guide/"; // Paper end - Add Velocity IP Forwarding Support @@ -51,7 +51,7 @@ index ca1e8da7540457ecba5684acd42129509c8204f5..9af4efcf9a2c8dee2208ddf37b1bb963 if (!this.usesAuthentication()) { DedicatedServer.LOGGER.warn("**** SERVER IS RUNNING IN OFFLINE/INSECURE MODE!"); DedicatedServer.LOGGER.warn("The server will make no attempt to authenticate usernames. Beware."); -@@ -343,7 +345,7 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface +@@ -347,7 +349,7 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface DedicatedServer.LOGGER.warn("While this makes the game possible to play without internet access, it also opens up the ability for hackers to connect with any username they choose."); } // Spigot end diff --git a/patches/server/0014-Add-missing-purpur-configuration-options.patch b/patches/server/0015-Add-missing-purpur-configuration-options.patch similarity index 96% rename from patches/server/0014-Add-missing-purpur-configuration-options.patch rename to patches/server/0015-Add-missing-purpur-configuration-options.patch index a5727c34d..1288b6e20 100644 --- a/patches/server/0014-Add-missing-purpur-configuration-options.patch +++ b/patches/server/0015-Add-missing-purpur-configuration-options.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Add missing purpur configuration options diff --git a/src/main/java/net/minecraft/world/entity/animal/allay/Allay.java b/src/main/java/net/minecraft/world/entity/animal/allay/Allay.java -index bca7b7192debb3a34a08047010a2438e7b7e2a78..b77aa7518b6ca6c6a387fc0f7270d7ff2dca4ac9 100644 +index 569f42645a7730451944fbbd6b96910493feeca5..a14d2ec3b8c89045916b95da5ce2a18d1e866bcf 100644 --- a/src/main/java/net/minecraft/world/entity/animal/allay/Allay.java +++ b/src/main/java/net/minecraft/world/entity/animal/allay/Allay.java @@ -150,6 +150,23 @@ public class Allay extends PathfinderMob implements InventoryCarrier, VibrationS @@ -56,7 +56,7 @@ index 24a1663cf1cd3f888981a13907811b55bdbf6133..fdc3aa9672077787e841d240fe1e690d @Override diff --git a/src/main/java/net/minecraft/world/entity/animal/frog/Frog.java b/src/main/java/net/minecraft/world/entity/animal/frog/Frog.java -index ee8c232ddaa518377bdfa54e83ffc04f7a2f2c9a..2c45f4d321629979926bb9e677ba4bc8b44eec8d 100644 +index 4c861b18fbfbae3cb2f1e3563393cfcf4005591c..651f30b4fb02dc03fabad34b62d7d86fa0889754 100644 --- a/src/main/java/net/minecraft/world/entity/animal/frog/Frog.java +++ b/src/main/java/net/minecraft/world/entity/animal/frog/Frog.java @@ -160,6 +160,23 @@ public class Frog extends Animal implements VariantHolder> { @@ -84,7 +84,7 @@ index ee8c232ddaa518377bdfa54e83ffc04f7a2f2c9a..2c45f4d321629979926bb9e677ba4bc8 public int getPurpurBreedTime() { diff --git a/src/main/java/net/minecraft/world/entity/animal/frog/Tadpole.java b/src/main/java/net/minecraft/world/entity/animal/frog/Tadpole.java -index 09c4cf772df4644413e40055fedcdf42ee8064fd..73b2e74199d54d87aa6d2b7132f10f29eefa11db 100644 +index 64d0e2edf0e47bfe3bc5fbdbabac26c494110467..e077a0bffedd0bb11c9ab5eb507451a0cf6317ea 100644 --- a/src/main/java/net/minecraft/world/entity/animal/frog/Tadpole.java +++ b/src/main/java/net/minecraft/world/entity/animal/frog/Tadpole.java @@ -93,6 +93,23 @@ public class Tadpole extends AbstractFish { @@ -135,7 +135,7 @@ index 21bdcbd51d9f440e0734750b40cefa4c08cdaf5f..a2cc1efaa621861828b007759536ac49 @Override diff --git a/src/main/java/net/minecraft/world/entity/monster/warden/Warden.java b/src/main/java/net/minecraft/world/entity/monster/warden/Warden.java -index 0bb577ec9ba0d23a741ccf067ac35f6be68312ca..358811e71b8b8a8a09fb6c56ed51bcc11b052add 100644 +index 722e1fed4b333958e95a847b27cfc67d04f13263..16891921341f8967c9115f415f91ec379fcd5b0c 100644 --- a/src/main/java/net/minecraft/world/entity/monster/warden/Warden.java +++ b/src/main/java/net/minecraft/world/entity/monster/warden/Warden.java @@ -147,6 +147,23 @@ public class Warden extends Monster implements VibrationSystem { @@ -191,7 +191,7 @@ index b04f7ce0805453f6c737fa9dc11c4129ca64e934..9840678549e25c6da00aea222a585e7f @Override diff --git a/src/main/java/org/purpurmc/purpur/PurpurConfig.java b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -index f9779f8cf0399e434ebe9e49e7bae5386ce01410..c5cfda1a1f0bd434efcf32e253c87738eee92def 100644 +index 69066fd1e82cefff44c7dbd565b19373768e2a62..42a8009d3bb07cd89e92b71f88ef46dd994bc6eb 100644 --- a/src/main/java/org/purpurmc/purpur/PurpurConfig.java +++ b/src/main/java/org/purpurmc/purpur/PurpurConfig.java @@ -329,6 +329,7 @@ public class PurpurConfig { @@ -211,7 +211,7 @@ index f9779f8cf0399e434ebe9e49e7bae5386ce01410..c5cfda1a1f0bd434efcf32e253c87738 org.bukkit.event.inventory.InventoryType.ENDER_CHEST.setDefaultSize(enderChestSixRows ? 54 : 27); enderChestPermissionRows = getBoolean("settings.blocks.ender_chest.use-permissions-for-rows", enderChestPermissionRows); diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 72dc81d3015dc7c6365997de9abc269b261649b1..73f574e3c7bb7fee3a34f61573b67af27a025dc3 100644 +index b6a6a74dfd00a4536258c8693048c9d1ac4df435..182e8c3f9a631a98576b2efb63371d57f9f5f25f 100644 --- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java +++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java @@ -1178,7 +1178,15 @@ public class PurpurWorldConfig { diff --git a/patches/server/0015-Completely-remove-Mojang-Profiler.patch b/patches/server/0016-Completely-remove-Mojang-Profiler.patch similarity index 97% rename from patches/server/0015-Completely-remove-Mojang-Profiler.patch rename to patches/server/0016-Completely-remove-Mojang-Profiler.patch index 075f6248f..b80455f2a 100644 --- a/patches/server/0015-Completely-remove-Mojang-Profiler.patch +++ b/patches/server/0016-Completely-remove-Mojang-Profiler.patch @@ -65,7 +65,7 @@ index f626a2f28f2aebb3237cebb6afef3c4fa1a6cb37..467e17bfce31d0919d603698c9d88a04 public int forkLimit() { return this.forkLimit; diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java -index 9d5257f810f9f88504b913fa0c7c214acc1da2cb..e0f50dcfe30abf439cea3ecd7837569802bd16c8 100644 +index c9a5c3b915f09a316f0f2725c51b696da111ccd3..983caa80431f5672cca341e91d9ccc40749a56e4 100644 --- a/src/main/java/net/minecraft/server/MinecraftServer.java +++ b/src/main/java/net/minecraft/server/MinecraftServer.java @@ -104,18 +104,18 @@ import net.minecraft.util.datafix.DataFixers; @@ -120,20 +120,20 @@ index 9d5257f810f9f88504b913fa0c7c214acc1da2cb..e0f50dcfe30abf439cea3ecd78375698 private ServerConnectionListener connection; public final ChunkProgressListenerFactory progressListenerFactory; @Nullable -@@ -2605,10 +2605,12 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop { -@@ -2878,12 +2881,12 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop resultConsumer, Consumer dumpConsumer) { @@ -156,7 +156,7 @@ index 9d5257f810f9f88504b913fa0c7c214acc1da2cb..e0f50dcfe30abf439cea3ecd78375698 } public void stopRecordingMetrics() { -@@ -2898,6 +2901,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop, RecipeHolder> builder = ImmutableMultimap.builder(); com.google.common.collect.ImmutableMap.Builder> com_google_common_collect_immutablemap_builder = ImmutableMap.builder(); diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java -index cc402c84734dc1841ce09f7bfd07936c745e6662..2a6bbd570ba885c4666ea4be3dd044ddfe3d9eb9 100644 +index 23f10ed05e2c4aabfa3a565a9117dabe52f175f7..2ad60c53890567742f0eff85987a41b0f6f50673 100644 --- a/src/main/java/net/minecraft/world/level/Level.java +++ b/src/main/java/net/minecraft/world/level/Level.java @@ -35,7 +35,7 @@ import net.minecraft.sounds.SoundSource; @@ -1309,7 +1309,7 @@ index cc402c84734dc1841ce09f7bfd07936c745e6662..2a6bbd570ba885c4666ea4be3dd044dd public final boolean isClientSide; private final WorldBorder worldBorder; private final BiomeManager biomeManager; -@@ -269,7 +269,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable { +@@ -271,7 +271,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable { ResourceKey resourcekey, RegistryAccess iregistrycustom, Holder holder, @@ -1318,7 +1318,7 @@ index cc402c84734dc1841ce09f7bfd07936c745e6662..2a6bbd570ba885c4666ea4be3dd044dd boolean flag, boolean flag1, long i, -@@ -298,7 +298,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable { +@@ -300,7 +300,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable { } // CraftBukkit end @@ -1327,7 +1327,7 @@ index cc402c84734dc1841ce09f7bfd07936c745e6662..2a6bbd570ba885c4666ea4be3dd044dd this.levelData = worlddatamutable; this.dimensionTypeRegistration = holder; final DimensionType dimensionmanager = (DimensionType) holder.value(); -@@ -1875,6 +1875,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable { +@@ -1877,6 +1877,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable { return false; } @@ -1335,7 +1335,7 @@ index cc402c84734dc1841ce09f7bfd07936c745e6662..2a6bbd570ba885c4666ea4be3dd044dd public ProfilerFiller getProfiler() { //if (true || gg.pufferfish.pufferfish.PufferfishConfig.disableMethodProfiler) return net.minecraft.util.profiling.InactiveProfiler.INSTANCE; // Pufferfish // Purpur // Purpur - TODO: Pufferfish return (ProfilerFiller) this.profiler.get(); -@@ -1883,6 +1884,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable { +@@ -1885,6 +1886,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable { public Supplier getProfilerSupplier() { return this.profiler; } @@ -1367,7 +1367,7 @@ index c5454b92ca2565461c799d7340160f9fb72c1b0f..760a4f4b15a02f9cbb7a368961286ae3 + */ // Plazma - Completely remove Mojang's Profiler } 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 37120fcb31f4c9a90be37db252b17eb93db2cb0f..8764e00de0ae8730bf1f12414f431d6cf75613b8 100644 +index e1853318350f9a10e8db106a7bc015307db0de6c..977d38a2be585ffad27843704a29eeaf50310518 100644 --- a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java +++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java @@ -26,7 +26,7 @@ import net.minecraft.network.FriendlyByteBuf; @@ -1379,7 +1379,7 @@ index 37120fcb31f4c9a90be37db252b17eb93db2cb0f..8764e00de0ae8730bf1f12414f431d6c import net.minecraft.world.entity.Entity; import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.Level; -@@ -443,7 +443,7 @@ public class LevelChunk extends ChunkAccess { +@@ -457,7 +457,7 @@ public class LevelChunk extends ChunkAccess { } if (LightEngine.hasDifferentLightProperties(this, blockposition, iblockdata1, iblockdata)) { diff --git a/patches/server/0016-Add-option-to-change-nether-portal-size.patch b/patches/server/0017-Add-option-to-change-nether-portal-size.patch similarity index 97% rename from patches/server/0016-Add-option-to-change-nether-portal-size.patch rename to patches/server/0017-Add-option-to-change-nether-portal-size.patch index 296dc459c..2b2092402 100644 --- a/patches/server/0016-Add-option-to-change-nether-portal-size.patch +++ b/patches/server/0017-Add-option-to-change-nether-portal-size.patch @@ -36,7 +36,7 @@ index afe6b2170846273b41b694aa53dca4c31bf78b3f..52043e451ca0641c93205aac62aba542 // CraftBukkit start - return boolean diff --git a/src/main/java/org/plazmamc/plazma/configurations/WorldConfigurations.java b/src/main/java/org/plazmamc/plazma/configurations/WorldConfigurations.java -index 42d639688b6f0b9ff53debf49a920f5c8e232a19..36728e206de513e071f387d36a608e70a5984a58 100644 +index 069a9f8504c74d939b1df569f082cc7bd33d9cfc..b5b4229ccdecd63bd3e689e8247e44341d7c30cc 100644 --- a/src/main/java/org/plazmamc/plazma/configurations/WorldConfigurations.java +++ b/src/main/java/org/plazmamc/plazma/configurations/WorldConfigurations.java @@ -42,6 +42,21 @@ public class WorldConfigurations extends ConfigurationPart { diff --git a/patches/server/0017-Reduce-create-random-instance.patch b/patches/server/0018-Reduce-create-random-instance.patch similarity index 99% rename from patches/server/0017-Reduce-create-random-instance.patch rename to patches/server/0018-Reduce-create-random-instance.patch index 120093b57..0e86ac243 100644 --- a/patches/server/0017-Reduce-create-random-instance.patch +++ b/patches/server/0018-Reduce-create-random-instance.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Reduce create random instance diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java -index e0f50dcfe30abf439cea3ecd7837569802bd16c8..5a5661c3f4759c1e7501c2b59f541be11af0a83d 100644 +index 983caa80431f5672cca341e91d9ccc40749a56e4..be20a08f07ea0a073d72d74d13650910f52530c9 100644 --- a/src/main/java/net/minecraft/server/MinecraftServer.java +++ b/src/main/java/net/minecraft/server/MinecraftServer.java @@ -235,6 +235,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop 0) { -@@ -295,6 +297,19 @@ public class ServerEntity { +@@ -297,6 +299,19 @@ public class ServerEntity { }); } diff --git a/patches/server/0023-Configurable-entity-sensor-tick.patch b/patches/server/0024-Configurable-entity-sensor-tick.patch similarity index 81% rename from patches/server/0023-Configurable-entity-sensor-tick.patch rename to patches/server/0024-Configurable-entity-sensor-tick.patch index 574681c11..a51e109e5 100644 --- a/patches/server/0023-Configurable-entity-sensor-tick.patch +++ b/patches/server/0024-Configurable-entity-sensor-tick.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Configurable entity sensor tick diff --git a/src/main/java/net/minecraft/world/entity/Mob.java b/src/main/java/net/minecraft/world/entity/Mob.java -index 274ca06fbda0f54805fc82614d43b6e1a4a5d4a8..688f3c01cb939a6c959efbb3b59ac8ee60db3633 100644 +index 9e9199b83fce2c9c8f03f4116c14a331279a234a..e691c4dc89bbf841730813fbd8dc21208482a59c 100644 --- a/src/main/java/net/minecraft/world/entity/Mob.java +++ b/src/main/java/net/minecraft/world/entity/Mob.java -@@ -995,10 +995,11 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Targeti +@@ -997,10 +997,11 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Targeti //ProfilerFiller gameprofilerfiller = this.level().getProfiler(); // Purpur //gameprofilerfiller.push("sensing"); // Purpur @@ -20,9 +20,9 @@ index 274ca06fbda0f54805fc82614d43b6e1a4a5d4a8..688f3c01cb939a6c959efbb3b59ac8ee + if (i % this.level().plazmaConfig().entity.sensorTick == 0) this.sensing.tick(); // Plazma - Configurable entity sensor tick if (i % 2 != 0 && this.tickCount > 1) { //gameprofilerfiller.push("targetSelector"); // Purpur - this.targetSelector.tickRunningGoals(false); + if (this.targetSelector.inactiveTick(this.activatedPriority, false)) // Pufferfish - use this to alternate ticking diff --git a/src/main/java/org/plazmamc/plazma/configurations/WorldConfigurations.java b/src/main/java/org/plazmamc/plazma/configurations/WorldConfigurations.java -index c8a3e91d0d6510b519e927027f5a534cddf7d0a3..bc283729cab4df2c596febf1df54241571227564 100644 +index 9debf554deb5b7aa021502ff7d600905f56fa25c..dceb5b2cec5f0c20be74d892e8cab98823d2f59a 100644 --- a/src/main/java/org/plazmamc/plazma/configurations/WorldConfigurations.java +++ b/src/main/java/org/plazmamc/plazma/configurations/WorldConfigurations.java @@ -30,6 +30,7 @@ public class WorldConfigurations extends ConfigurationPart { diff --git a/patches/server/0025-Variable-entity-wakeup-duration.patch b/patches/server/0025-Variable-entity-wakeup-duration.patch index 4f6cd20a3..fc18930cd 100644 --- a/patches/server/0025-Variable-entity-wakeup-duration.patch +++ b/patches/server/0025-Variable-entity-wakeup-duration.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Variable entity wakeup duration diff --git a/src/main/java/org/plazmamc/plazma/configurations/WorldConfigurations.java b/src/main/java/org/plazmamc/plazma/configurations/WorldConfigurations.java -index bc283729cab4df2c596febf1df54241571227564..1e7c23c8851ba1d0f37d2b17fd334220e060888e 100644 +index dceb5b2cec5f0c20be74d892e8cab98823d2f59a..bc35244533621436e3c0fb871edf7834ad937f81 100644 --- a/src/main/java/org/plazmamc/plazma/configurations/WorldConfigurations.java +++ b/src/main/java/org/plazmamc/plazma/configurations/WorldConfigurations.java @@ -39,6 +39,20 @@ public class WorldConfigurations extends ConfigurationPart { @@ -30,10 +30,10 @@ index bc283729cab4df2c596febf1df54241571227564..1e7c23c8851ba1d0f37d2b17fd334220 public Structure structure; diff --git a/src/main/java/org/spigotmc/ActivationRange.java b/src/main/java/org/spigotmc/ActivationRange.java -index de2b469f06f6679aed1d20156052bfbef5e7c30b..ebd0764dde2e1d79fb41b5d881990ee07a0590b1 100644 +index 56e52b16b419c882440a15947f037ae1a902bc70..583647f6c9ca0fb092f30c97693bd548f692dfd3 100644 --- a/src/main/java/org/spigotmc/ActivationRange.java +++ b/src/main/java/org/spigotmc/ActivationRange.java -@@ -64,29 +64,36 @@ public class ActivationRange +@@ -68,29 +68,36 @@ public class ActivationRange Activity.PANIC }; diff --git a/patches/server/0027-Suppress-errors-from-dirty-attributes.patch b/patches/server/0027-Suppress-errors-from-dirty-attributes.patch index 25bf7c6a6..7e2bb94ed 100644 --- a/patches/server/0027-Suppress-errors-from-dirty-attributes.patch +++ b/patches/server/0027-Suppress-errors-from-dirty-attributes.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Suppress errors from dirty attributes diff --git a/src/main/java/net/minecraft/server/level/ServerEntity.java b/src/main/java/net/minecraft/server/level/ServerEntity.java -index 9fe8dcea8f5075a5420a4355bb3c27c2311d8f94..acf1cb25f75b35e064e7099d85693632073df858 100644 +index 6f19b5b67c99becbc1cb720274d41d15c7723c31..4ef3d8541c93567e3a9b712c0d4dd5f6cfeb2990 100644 --- a/src/main/java/net/minecraft/server/level/ServerEntity.java +++ b/src/main/java/net/minecraft/server/level/ServerEntity.java -@@ -413,7 +413,8 @@ public class ServerEntity { +@@ -415,7 +415,8 @@ public class ServerEntity { } if (this.entity instanceof LivingEntity) { @@ -18,7 +18,7 @@ index 9fe8dcea8f5075a5420a4355bb3c27c2311d8f94..acf1cb25f75b35e064e7099d85693632 if (!set.isEmpty()) { // CraftBukkit start - Send scaled max health -@@ -424,7 +425,7 @@ public class ServerEntity { +@@ -426,7 +427,7 @@ public class ServerEntity { this.broadcastAndSend(new ClientboundUpdateAttributesPacket(this.entity.getId(), set)); } diff --git a/patches/server/0031-Improve-SwingTime-ticking.patch b/patches/server/0031-Improve-SwingTime-ticking.patch index f0976ed70..696bb91fa 100644 --- a/patches/server/0031-Improve-SwingTime-ticking.patch +++ b/patches/server/0031-Improve-SwingTime-ticking.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Improve SwingTime ticking diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java -index fa698cfefccdddf5e5e9938a2959004c70f743a7..b24754e36e210a635524be099cffe4553220a6a9 100644 +index 12841498369db7020bb844040bc5464745312082..decf23f5320d728088085c86009eb502e6af072b 100644 --- a/src/main/java/net/minecraft/world/entity/LivingEntity.java +++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java -@@ -2654,6 +2654,7 @@ public abstract class LivingEntity extends Entity implements Attackable { +@@ -2680,6 +2680,7 @@ public abstract class LivingEntity extends Entity implements Attackable { } protected void updateSwingTime() { diff --git a/patches/server/0033-Add-more-MSPT.patch b/patches/server/0033-Add-more-MSPT.patch index 5d19b6a8b..d4de0f8a5 100644 --- a/patches/server/0033-Add-more-MSPT.patch +++ b/patches/server/0033-Add-more-MSPT.patch @@ -94,7 +94,7 @@ index 03be23690a94a14d7343526acad67ccf53b85c70..416c0a736edf47f76a37be0bc5fe8678 ) ); diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java -index 5a5661c3f4759c1e7501c2b59f541be11af0a83d..7f5ea3282f5069a4a911b69c758981b6f4dcadc0 100644 +index be20a08f07ea0a073d72d74d13650910f52530c9..87cb1529a6bba0f0778cf97c3c5473c3a8503b59 100644 --- a/src/main/java/net/minecraft/server/MinecraftServer.java +++ b/src/main/java/net/minecraft/server/MinecraftServer.java @@ -257,8 +257,10 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop> { +@@ -491,11 +491,25 @@ public class Frog extends Animal implements VariantHolder> { return nodeType != PathType.WATER_BORDER && super.canCutCorner(nodeType); } @@ -999,10 +999,10 @@ index 5ea5bf9c0e11b0e1f9fe50093899c6e35ee6cf4f..310d942dca115d2048812e7d7e6e7fab @Override protected boolean hasValidPathType(PathType pathType) { diff --git a/src/main/java/net/minecraft/world/entity/monster/warden/Warden.java b/src/main/java/net/minecraft/world/entity/monster/warden/Warden.java -index 358811e71b8b8a8a09fb6c56ed51bcc11b052add..f08d6b2729e133772a540fa9011a2b3af271aace 100644 +index 16891921341f8967c9115f415f91ec379fcd5b0c..524d2304a1dbc873a47c95c55ad70c4120f07ad4 100644 --- a/src/main/java/net/minecraft/world/entity/monster/warden/Warden.java +++ b/src/main/java/net/minecraft/world/entity/monster/warden/Warden.java -@@ -635,6 +635,15 @@ public class Warden extends Monster implements VibrationSystem { +@@ -636,6 +636,15 @@ public class Warden extends Monster implements VibrationSystem { protected PathFinder createPathFinder(int range) { this.nodeEvaluator = new WalkNodeEvaluator(); this.nodeEvaluator.setCanPassDoors(true); @@ -1285,10 +1285,10 @@ index 1c83926923f50fb4da1a83dc91614c20a831555f..aec2d0f9a957be65d031957dbff874d8 Command.broadcastCommandMessage(sender, text("Successfully reloaded Plazma configuration files.", NamedTextColor.GREEN)); diff --git a/src/main/java/org/plazmamc/plazma/configurations/GlobalConfiguration.java b/src/main/java/org/plazmamc/plazma/configurations/GlobalConfiguration.java -index fd2544c52f0948f7dae14814fbf0310d8e49170d..b18dfe9223b75d8a36b4b3a934625914f9a6a553 100644 +index e8e8c2d55b934b3043c1f83092b63dee1c5fcc4e..c809a4692fcabf87c16fcd932ef25a718c2935d3 100644 --- a/src/main/java/org/plazmamc/plazma/configurations/GlobalConfiguration.java +++ b/src/main/java/org/plazmamc/plazma/configurations/GlobalConfiguration.java -@@ -63,6 +63,26 @@ public class GlobalConfiguration extends ConfigurationPart { +@@ -44,6 +44,26 @@ public class GlobalConfiguration extends ConfigurationPart { boolean skipSqrWhenNoDeltaChanges = OPTIMIZE; public boolean spookyOptimize = OPTIMIZE; diff --git a/patches/server/0035-Use-Akair-s-flag-when-running-the-test-server-with-g.patch b/patches/server/0035-Use-Akair-s-flag-when-running-the-test-server-with-g.patch index 1e863769d..4189ec55a 100644 --- a/patches/server/0035-Use-Akair-s-flag-when-running-the-test-server-with-g.patch +++ b/patches/server/0035-Use-Akair-s-flag-when-running-the-test-server-with-g.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Use Akair's flag when running the test server with gradle diff --git a/build.gradle.kts b/build.gradle.kts -index 92826a38773b6d76acfbf18774ada8a395e9007f..99bf51cb7dd5a8a2d9f87c18bcf9745cead4ba61 100644 +index c02fba3660ab368f64bd120db8c0a9de6e4f763d..7e19f7aba5140675c3163a45c3c54c626573de4e 100644 --- a/build.gradle.kts +++ b/build.gradle.kts -@@ -121,7 +121,7 @@ fun TaskContainer.registerRunTask( +@@ -129,7 +129,7 @@ fun TaskContainer.registerRunTask( languageVersion.set(JavaLanguageVersion.of(21)) vendor.set(JvmVendorSpec.JETBRAINS) }) diff --git a/patches/server/0036-Use-Plazma-logo-instead-if-server-favicon-doesn-t-ex.patch b/patches/server/0036-Use-Plazma-logo-instead-if-server-favicon-doesn-t-ex.patch index 85deef256..e853ee9eb 100644 --- a/patches/server/0036-Use-Plazma-logo-instead-if-server-favicon-doesn-t-ex.patch +++ b/patches/server/0036-Use-Plazma-logo-instead-if-server-favicon-doesn-t-ex.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Use Plazma logo instead if server favicon doesn't exist diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java -index 7f5ea3282f5069a4a911b69c758981b6f4dcadc0..0df87edbd9625b2888b303c1000aefa9bdbd2f26 100644 +index 87cb1529a6bba0f0778cf97c3c5473c3a8503b59..f388a07678ef253b19da814f6d90c4d787204517 100644 --- a/src/main/java/net/minecraft/server/MinecraftServer.java +++ b/src/main/java/net/minecraft/server/MinecraftServer.java -@@ -1559,29 +1559,36 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop loadStatusIcon() { diff --git a/patches/server/0037-Implement-FreedomChat.patch b/patches/server/0037-Implement-FreedomChat.patch index c944c3cf0..5634d823c 100644 --- a/patches/server/0037-Implement-FreedomChat.patch +++ b/patches/server/0037-Implement-FreedomChat.patch @@ -20,10 +20,10 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . diff --git a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java -index cbc884db2cacdcba264033dd06988150ba4ee0ba..99479a594f573aaac2a96bbecd9ef70e845f4677 100644 +index c19942b5f0ec44439a68fc7c8f55897653d20e56..c5804a3d03433a3a1dba807f23305850b826851b 100644 --- a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java +++ b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java -@@ -704,6 +704,7 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface +@@ -709,6 +709,7 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface // Paper start - Add setting for proxy online mode status return dedicatedserverproperties.enforceSecureProfile && io.papermc.paper.configuration.GlobalConfiguration.get().proxies.isProxyOnlineMode() @@ -52,7 +52,7 @@ index c36ebab552c480d34e7fd48a45f0a320a97b9677..fdfe8f5bc5ad0e5f5ded41d87756a586 player.loadGameTypes((CompoundTag) optional.orElse(null)); // CraftBukkit - decompile error ServerGamePacketListenerImpl playerconnection = new ServerGamePacketListenerImpl(this.server, connection, player, clientData); diff --git a/src/main/java/org/plazmamc/plazma/configurations/GlobalConfiguration.java b/src/main/java/org/plazmamc/plazma/configurations/GlobalConfiguration.java -index b18dfe9223b75d8a36b4b3a934625914f9a6a553..19b198fb1d0ffee28a2cfd516cb31204fd1a310c 100644 +index c809a4692fcabf87c16fcd932ef25a718c2935d3..a237a4ea3b60ba3b5a2cfa12748ac682240a8a4e 100644 --- a/src/main/java/org/plazmamc/plazma/configurations/GlobalConfiguration.java +++ b/src/main/java/org/plazmamc/plazma/configurations/GlobalConfiguration.java @@ -29,6 +29,15 @@ public class GlobalConfiguration extends ConfigurationPart { diff --git a/patches/server/0024-Configurable-cave-lava-sea-level.patch b/patches/unapplied/server/0024-Configurable-cave-lava-sea-level.patch similarity index 100% rename from patches/server/0024-Configurable-cave-lava-sea-level.patch rename to patches/unapplied/server/0024-Configurable-cave-lava-sea-level.patch