diff --git a/build.gradle.kts b/build.gradle.kts index 66fc12ce2..ee2bb9f42 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -3,7 +3,7 @@ import io.papermc.paperweight.util.constants.* plugins { java `maven-publish` - id("com.github.johnrengelman.shadow") version "7.1.1" apply false + id("com.github.johnrengelman.shadow") version "7.1.2" apply false id("io.papermc.paperweight.patcher") version "1.3.3" } @@ -56,6 +56,7 @@ subprojects { maven("https://repo.md-5.net/content/repositories/releases/") maven("https://hub.spigotmc.org/nexus/content/groups/public/") maven("https://oss.sonatype.org/content/repositories/snapshots/") + maven("https://jitpack.io") } } diff --git a/patches/api/0001-Pufferfish-API-Changes.patch b/patches/api/0001-Pufferfish-API-Changes.patch new file mode 100644 index 000000000..969c9ad90 --- /dev/null +++ b/patches/api/0001-Pufferfish-API-Changes.patch @@ -0,0 +1,294 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Kevin Raneri +Date: Tue, 9 Nov 2021 14:01:56 -0500 +Subject: [PATCH] Pufferfish API Changes + +Pufferfish +Copyright (C) 2022 pufferfish-gg + +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 001c2b963205012f340db0d539e4033c748124ce..554f5e35954f35aecaf454853a0a2999f15d19bc 100644 +--- a/build.gradle.kts ++++ b/build.gradle.kts +@@ -40,6 +40,7 @@ dependencies { + apiAndDocs("net.kyori:adventure-text-serializer-plain") + api("org.apache.logging.log4j:log4j-api:2.17.1") + api("org.slf4j:slf4j-api:1.8.0-beta4") ++ api("io.sentry:sentry:5.4.0") // Pufferfish + + implementation("org.ow2.asm:asm:9.2") + implementation("org.ow2.asm:asm-commons:9.2") +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/org/bukkit/plugin/SimplePluginManager.java b/src/main/java/org/bukkit/plugin/SimplePluginManager.java +index 1366496271c4c7f72d1e5f990e51775b1c371f99..05bb388407b5bd8a942478237580a38ffaa388c8 100644 +--- a/src/main/java/org/bukkit/plugin/SimplePluginManager.java ++++ b/src/main/java/org/bukkit/plugin/SimplePluginManager.java +@@ -581,7 +581,9 @@ public final class SimplePluginManager implements PluginManager { + + // Paper start + private void handlePluginException(String msg, Throwable ex, Plugin plugin) { ++ gg.pufferfish.pufferfish.sentry.SentryContext.setPluginContext(plugin); // Pufferfish + server.getLogger().log(Level.SEVERE, msg, ex); ++ gg.pufferfish.pufferfish.sentry.SentryContext.removePluginContext(); // Pufferfish + callEvent(new ServerExceptionEvent(new ServerPluginEnableDisableException(msg, ex, plugin))); + } + // Paper end +@@ -640,14 +642,16 @@ public final class SimplePluginManager implements PluginManager { + )); + } + } catch (Throwable ex) { ++ gg.pufferfish.pufferfish.sentry.SentryContext.setEventContext(event, registration); // Pufferfish + // Paper start - error reporting + String msg = "Could not pass event " + event.getEventName() + " to " + registration.getPlugin().getDescription().getFullName(); + server.getLogger().log(Level.SEVERE, msg, ex); ++ gg.pufferfish.pufferfish.sentry.SentryContext.removeEventContext(); // Pufferfish + if (!(event instanceof ServerExceptionEvent)) { // We don't want to cause an endless event loop + callEvent(new ServerExceptionEvent(new ServerEventException(msg, ex, registration.getPlugin(), registration.getListener(), event))); + } + // Paper end +- } ++ } finally { gg.pufferfish.pufferfish.sentry.SentryContext.removeEventContext(); } // Pufferfish + } + } + +diff --git a/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java b/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java +index c8b11793c6a3baabc1c9566e0463ab1d6e293827..2b9218ddd262e89180588c3014dad328317dd8db 100644 +--- a/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java ++++ b/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java +@@ -369,7 +369,9 @@ public final class JavaPluginLoader implements PluginLoader { + try { + jPlugin.setEnabled(true); + } catch (Throwable ex) { ++ gg.pufferfish.pufferfish.sentry.SentryContext.setPluginContext(plugin); // Pufferfish + server.getLogger().log(Level.SEVERE, "Error occurred while enabling " + plugin.getDescription().getFullName() + " (Is it up to date?)", ex); ++ gg.pufferfish.pufferfish.sentry.SentryContext.removePluginContext(); // Pufferfish + // Paper start - Disable plugins that fail to load + this.server.getPluginManager().disablePlugin(jPlugin); + return; +@@ -398,7 +400,9 @@ public final class JavaPluginLoader implements PluginLoader { + try { + jPlugin.setEnabled(false); + } catch (Throwable ex) { ++ gg.pufferfish.pufferfish.sentry.SentryContext.setPluginContext(plugin); // Pufferfish + server.getLogger().log(Level.SEVERE, "Error occurred while disabling " + plugin.getDescription().getFullName() + " (Is it up to date?)", ex); ++ gg.pufferfish.pufferfish.sentry.SentryContext.removePluginContext(); // Pufferfish + } + + if (cloader instanceof PluginClassLoader) { +diff --git a/src/main/java/org/bukkit/plugin/java/PluginClassLoader.java b/src/main/java/org/bukkit/plugin/java/PluginClassLoader.java +index 8a39c48fce819d72a94d5309db8dfc42930989af..c67e91316f35750b18e082746206a01e783f1740 100644 +--- a/src/main/java/org/bukkit/plugin/java/PluginClassLoader.java ++++ b/src/main/java/org/bukkit/plugin/java/PluginClassLoader.java +@@ -46,6 +46,8 @@ public final class PluginClassLoader extends URLClassLoader { // Spigot + private final Set seenIllegalAccess = Collections.newSetFromMap(new ConcurrentHashMap<>()); + private java.util.logging.Logger logger; // Paper - add field + ++ private boolean closed = false; // Pufferfish ++ + static { + ClassLoader.registerAsParallelCapable(); + } +@@ -151,6 +153,7 @@ public final class PluginClassLoader extends URLClassLoader { // Spigot + throw new ClassNotFoundException(name); + } + ++ public boolean _airplane_hasClass(@NotNull String name) { return this.classes.containsKey(name); } // Pufferfish + @Override + protected Class findClass(String name) throws ClassNotFoundException { + if (name.startsWith("org.bukkit.") || name.startsWith("net.minecraft.")) { +@@ -158,7 +161,7 @@ public final class PluginClassLoader extends URLClassLoader { // Spigot + } + Class result = classes.get(name); + +- if (result == null) { ++ if (result == null && !this.closed) { // Pufferfish + String path = name.replace('.', '/').concat(".class"); + JarEntry entry = jar.getJarEntry(path); + +@@ -213,6 +216,7 @@ public final class PluginClassLoader extends URLClassLoader { // Spigot + try { + super.close(); + } finally { ++ this.closed = true; // Pufferfish + jar.close(); + } + } diff --git a/patches/api/0002-Add-pufferfish-added-classes-to-junit-exemptions.patch b/patches/api/0002-Add-pufferfish-added-classes-to-junit-exemptions.patch new file mode 100644 index 000000000..1b6d7a047 --- /dev/null +++ b/patches/api/0002-Add-pufferfish-added-classes-to-junit-exemptions.patch @@ -0,0 +1,21 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: BillyGalbreath +Date: Tue, 4 Jan 2022 23:05:41 -0600 +Subject: [PATCH] Add pufferfish added classes to junit exemptions + + +diff --git a/src/test/java/org/bukkit/AnnotationTest.java b/src/test/java/org/bukkit/AnnotationTest.java +index 57cf9fab2501da6f1abd12e14697630ca82b0a6d..a5bced98d5b8860f4f05e42cfd8a355c162b8b6e 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/0001-Build-System-Changes.patch b/patches/api/0003-Build-System-Changes.patch similarity index 75% rename from patches/api/0001-Build-System-Changes.patch rename to patches/api/0003-Build-System-Changes.patch index 8d067bb49..31330b220 100644 --- a/patches/api/0001-Build-System-Changes.patch +++ b/patches/api/0003-Build-System-Changes.patch @@ -6,13 +6,13 @@ Subject: [PATCH] Build System Changes todo: merge with rebrand patch diff --git a/build.gradle.kts b/build.gradle.kts -index 001c2b963205012f340db0d539e4033c748124ce..1ab728d05891b3a17094a4a61b5af4ad6de5e074 100644 +index 554f5e35954f35aecaf454853a0a2999f15d19bc..9203d7aa155319abfab0a65d4ef6804cb7e48769 100644 --- a/build.gradle.kts +++ b/build.gradle.kts -@@ -40,6 +40,7 @@ dependencies { - apiAndDocs("net.kyori:adventure-text-serializer-plain") +@@ -41,6 +41,7 @@ dependencies { api("org.apache.logging.log4j:log4j-api:2.17.1") api("org.slf4j:slf4j-api:1.8.0-beta4") + api("io.sentry:sentry:5.4.0") // Pufferfish + api("net.kyori:adventure-text-minimessage:4.1.0-SNAPSHOT") // Purpur implementation("org.ow2.asm:asm:9.2") diff --git a/patches/api/0002-Purpur-config-files.patch b/patches/api/0004-Purpur-config-files.patch similarity index 100% rename from patches/api/0002-Purpur-config-files.patch rename to patches/api/0004-Purpur-config-files.patch diff --git a/patches/api/0003-Purpur-client-support.patch b/patches/api/0005-Purpur-client-support.patch similarity index 100% rename from patches/api/0003-Purpur-client-support.patch rename to patches/api/0005-Purpur-client-support.patch diff --git a/patches/api/0004-Default-permissions.patch b/patches/api/0006-Default-permissions.patch similarity index 100% rename from patches/api/0004-Default-permissions.patch rename to patches/api/0006-Default-permissions.patch diff --git a/patches/api/0005-Ridables.patch b/patches/api/0007-Ridables.patch similarity index 100% rename from patches/api/0005-Ridables.patch rename to patches/api/0007-Ridables.patch diff --git a/patches/api/0006-Allow-inventory-resizing.patch b/patches/api/0008-Allow-inventory-resizing.patch similarity index 100% rename from patches/api/0006-Allow-inventory-resizing.patch rename to patches/api/0008-Allow-inventory-resizing.patch diff --git a/patches/api/0007-Llama-API.patch b/patches/api/0009-Llama-API.patch similarity index 100% rename from patches/api/0007-Llama-API.patch rename to patches/api/0009-Llama-API.patch diff --git a/patches/api/0008-AFK-API.patch b/patches/api/0010-AFK-API.patch similarity index 100% rename from patches/api/0008-AFK-API.patch rename to patches/api/0010-AFK-API.patch diff --git a/patches/api/0009-Bring-back-server-name.patch b/patches/api/0011-Bring-back-server-name.patch similarity index 100% rename from patches/api/0009-Bring-back-server-name.patch rename to patches/api/0011-Bring-back-server-name.patch diff --git a/patches/api/0010-ExecuteCommandEvent.patch b/patches/api/0012-ExecuteCommandEvent.patch similarity index 100% rename from patches/api/0010-ExecuteCommandEvent.patch rename to patches/api/0012-ExecuteCommandEvent.patch diff --git a/patches/api/0011-LivingEntity-safeFallDistance.patch b/patches/api/0013-LivingEntity-safeFallDistance.patch similarity index 100% rename from patches/api/0011-LivingEntity-safeFallDistance.patch rename to patches/api/0013-LivingEntity-safeFallDistance.patch diff --git a/patches/api/0012-Lagging-threshold.patch b/patches/api/0014-Lagging-threshold.patch similarity index 100% rename from patches/api/0012-Lagging-threshold.patch rename to patches/api/0014-Lagging-threshold.patch diff --git a/patches/api/0013-PlayerSetSpawnerTypeWithEggEvent.patch b/patches/api/0015-PlayerSetSpawnerTypeWithEggEvent.patch similarity index 100% rename from patches/api/0013-PlayerSetSpawnerTypeWithEggEvent.patch rename to patches/api/0015-PlayerSetSpawnerTypeWithEggEvent.patch diff --git a/patches/api/0014-EMC-MonsterEggSpawnEvent.patch b/patches/api/0016-EMC-MonsterEggSpawnEvent.patch similarity index 100% rename from patches/api/0014-EMC-MonsterEggSpawnEvent.patch rename to patches/api/0016-EMC-MonsterEggSpawnEvent.patch diff --git a/patches/api/0015-Player-invulnerabilities.patch b/patches/api/0017-Player-invulnerabilities.patch similarity index 100% rename from patches/api/0015-Player-invulnerabilities.patch rename to patches/api/0017-Player-invulnerabilities.patch diff --git a/patches/api/0016-Anvil-API.patch b/patches/api/0018-Anvil-API.patch similarity index 100% rename from patches/api/0016-Anvil-API.patch rename to patches/api/0018-Anvil-API.patch diff --git a/patches/api/0017-ItemStack-convenience-methods.patch b/patches/api/0019-ItemStack-convenience-methods.patch similarity index 100% rename from patches/api/0017-ItemStack-convenience-methods.patch rename to patches/api/0019-ItemStack-convenience-methods.patch diff --git a/patches/api/0018-Phantoms-attracted-to-crystals-and-crystals-shoot-ph.patch b/patches/api/0020-Phantoms-attracted-to-crystals-and-crystals-shoot-ph.patch similarity index 100% rename from patches/api/0018-Phantoms-attracted-to-crystals-and-crystals-shoot-ph.patch rename to patches/api/0020-Phantoms-attracted-to-crystals-and-crystals-shoot-ph.patch diff --git a/patches/api/0019-ChatColor-conveniences.patch b/patches/api/0021-ChatColor-conveniences.patch similarity index 100% rename from patches/api/0019-ChatColor-conveniences.patch rename to patches/api/0021-ChatColor-conveniences.patch diff --git a/patches/api/0020-LivingEntity-broadcastItemBreak.patch b/patches/api/0022-LivingEntity-broadcastItemBreak.patch similarity index 100% rename from patches/api/0020-LivingEntity-broadcastItemBreak.patch rename to patches/api/0022-LivingEntity-broadcastItemBreak.patch diff --git a/patches/api/0021-Item-entity-immunities.patch b/patches/api/0023-Item-entity-immunities.patch similarity index 100% rename from patches/api/0021-Item-entity-immunities.patch rename to patches/api/0023-Item-entity-immunities.patch diff --git a/patches/api/0022-Spigot-Improve-output-of-plugins-command.patch b/patches/api/0024-Spigot-Improve-output-of-plugins-command.patch similarity index 100% rename from patches/api/0022-Spigot-Improve-output-of-plugins-command.patch rename to patches/api/0024-Spigot-Improve-output-of-plugins-command.patch diff --git a/patches/api/0023-Add-option-to-disable-zombie-aggressiveness-towards-.patch b/patches/api/0025-Add-option-to-disable-zombie-aggressiveness-towards-.patch similarity index 100% rename from patches/api/0023-Add-option-to-disable-zombie-aggressiveness-towards-.patch rename to patches/api/0025-Add-option-to-disable-zombie-aggressiveness-towards-.patch diff --git a/patches/api/0024-Add-predicate-to-recipe-s-ExactChoice-ingredient.patch b/patches/api/0026-Add-predicate-to-recipe-s-ExactChoice-ingredient.patch similarity index 100% rename from patches/api/0024-Add-predicate-to-recipe-s-ExactChoice-ingredient.patch rename to patches/api/0026-Add-predicate-to-recipe-s-ExactChoice-ingredient.patch diff --git a/patches/api/0025-Alphabetize-in-game-plugins-list.patch b/patches/api/0027-Alphabetize-in-game-plugins-list.patch similarity index 100% rename from patches/api/0025-Alphabetize-in-game-plugins-list.patch rename to patches/api/0027-Alphabetize-in-game-plugins-list.patch diff --git a/patches/api/0026-Rabid-Wolf-API.patch b/patches/api/0028-Rabid-Wolf-API.patch similarity index 100% rename from patches/api/0026-Rabid-Wolf-API.patch rename to patches/api/0028-Rabid-Wolf-API.patch diff --git a/patches/api/0027-Fix-javadoc-warnings-missing-param-and-return.patch b/patches/api/0029-Fix-javadoc-warnings-missing-param-and-return.patch similarity index 100% rename from patches/api/0027-Fix-javadoc-warnings-missing-param-and-return.patch rename to patches/api/0029-Fix-javadoc-warnings-missing-param-and-return.patch diff --git a/patches/api/0028-PlayerBookTooLargeEvent.patch b/patches/api/0030-PlayerBookTooLargeEvent.patch similarity index 100% rename from patches/api/0028-PlayerBookTooLargeEvent.patch rename to patches/api/0030-PlayerBookTooLargeEvent.patch diff --git a/patches/api/0029-Full-netherite-armor-grants-fire-resistance.patch b/patches/api/0031-Full-netherite-armor-grants-fire-resistance.patch similarity index 100% rename from patches/api/0029-Full-netherite-armor-grants-fire-resistance.patch rename to patches/api/0031-Full-netherite-armor-grants-fire-resistance.patch diff --git a/patches/api/0030-Add-EntityTeleportHinderedEvent.patch b/patches/api/0032-Add-EntityTeleportHinderedEvent.patch similarity index 100% rename from patches/api/0030-Add-EntityTeleportHinderedEvent.patch rename to patches/api/0032-Add-EntityTeleportHinderedEvent.patch diff --git a/patches/api/0031-Conflict-on-change-for-adventure-deprecations.patch b/patches/api/0033-Conflict-on-change-for-adventure-deprecations.patch similarity index 100% rename from patches/api/0031-Conflict-on-change-for-adventure-deprecations.patch rename to patches/api/0033-Conflict-on-change-for-adventure-deprecations.patch diff --git a/patches/api/0032-Add-enchantment-target-for-bows-and-crossbows.patch b/patches/api/0034-Add-enchantment-target-for-bows-and-crossbows.patch similarity index 100% rename from patches/api/0032-Add-enchantment-target-for-bows-and-crossbows.patch rename to patches/api/0034-Add-enchantment-target-for-bows-and-crossbows.patch diff --git a/patches/api/0033-Iron-golem-poppy-calms-anger.patch b/patches/api/0035-Iron-golem-poppy-calms-anger.patch similarity index 100% rename from patches/api/0033-Iron-golem-poppy-calms-anger.patch rename to patches/api/0035-Iron-golem-poppy-calms-anger.patch diff --git a/patches/api/0034-API-for-any-mob-to-burn-daylight.patch b/patches/api/0036-API-for-any-mob-to-burn-daylight.patch similarity index 100% rename from patches/api/0034-API-for-any-mob-to-burn-daylight.patch rename to patches/api/0036-API-for-any-mob-to-burn-daylight.patch diff --git a/patches/api/0035-Flying-Fall-Damage-API.patch b/patches/api/0037-Flying-Fall-Damage-API.patch similarity index 100% rename from patches/api/0035-Flying-Fall-Damage-API.patch rename to patches/api/0037-Flying-Fall-Damage-API.patch diff --git a/patches/api/0036-Add-back-player-spawned-endermite-API.patch b/patches/api/0038-Add-back-player-spawned-endermite-API.patch similarity index 100% rename from patches/api/0036-Add-back-player-spawned-endermite-API.patch rename to patches/api/0038-Add-back-player-spawned-endermite-API.patch diff --git a/patches/api/0037-Fix-default-permission-system.patch b/patches/api/0039-Fix-default-permission-system.patch similarity index 100% rename from patches/api/0037-Fix-default-permission-system.patch rename to patches/api/0039-Fix-default-permission-system.patch diff --git a/patches/api/0038-Summoner-API.patch b/patches/api/0040-Summoner-API.patch similarity index 100% rename from patches/api/0038-Summoner-API.patch rename to patches/api/0040-Summoner-API.patch diff --git a/patches/api/0039-Clean-up-version-command-output-for-console.patch b/patches/api/0041-Clean-up-version-command-output-for-console.patch similarity index 100% rename from patches/api/0039-Clean-up-version-command-output-for-console.patch rename to patches/api/0041-Clean-up-version-command-output-for-console.patch diff --git a/patches/api/0040-Extended-OfflinePlayer-API.patch b/patches/api/0042-Extended-OfflinePlayer-API.patch similarity index 100% rename from patches/api/0040-Extended-OfflinePlayer-API.patch rename to patches/api/0042-Extended-OfflinePlayer-API.patch diff --git a/patches/api/0041-Added-the-ability-to-add-combustible-items.patch b/patches/api/0043-Added-the-ability-to-add-combustible-items.patch similarity index 100% rename from patches/api/0041-Added-the-ability-to-add-combustible-items.patch rename to patches/api/0043-Added-the-ability-to-add-combustible-items.patch diff --git a/patches/api/0042-Potion-NamespacedKey.patch b/patches/api/0044-Potion-NamespacedKey.patch similarity index 100% rename from patches/api/0042-Potion-NamespacedKey.patch rename to patches/api/0044-Potion-NamespacedKey.patch diff --git a/patches/api/0043-Grindstone-API.patch b/patches/api/0045-Grindstone-API.patch similarity index 100% rename from patches/api/0043-Grindstone-API.patch rename to patches/api/0045-Grindstone-API.patch diff --git a/patches/api/0044-Shears-can-have-looting-enchantment.patch b/patches/api/0046-Shears-can-have-looting-enchantment.patch similarity index 100% rename from patches/api/0044-Shears-can-have-looting-enchantment.patch rename to patches/api/0046-Shears-can-have-looting-enchantment.patch diff --git a/patches/server/0001-Decompile-Fixes.patch b/patches/server/0001-Decompile-Fixes.patch deleted file mode 100644 index f2136797a..000000000 --- a/patches/server/0001-Decompile-Fixes.patch +++ /dev/null @@ -1,19 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Ben Kerllenevich -Date: Tue, 23 Nov 2021 08:38:18 -0500 -Subject: [PATCH] Decompile Fixes - - -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 c5b9c0c650df5f4b7e3d2a431dc900e210104dea..212e2c5b4b917c0c327d4b0612fecaea81c0ad87 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 -@@ -123,7 +123,7 @@ public class Hoglin extends Animal implements Enemy, HoglinBase { - - @Override - public Brain getBrain() { -- return super.getBrain(); -+ return (Brain) super.getBrain(); // Purpur - decompile fix - } - - @Override diff --git a/patches/server/0001-Pufferfish-Server-Changes.patch b/patches/server/0001-Pufferfish-Server-Changes.patch new file mode 100644 index 000000000..b5effc48c --- /dev/null +++ b/patches/server/0001-Pufferfish-Server-Changes.patch @@ -0,0 +1,4280 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Initial Source +Date: Fri, 16 Jul 2021 17:11:36 -0500 +Subject: [PATCH] Pufferfish Server Changes + +Pufferfish +Copyright (C) 2022 pufferfish-gg + +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 028f6a1795ceb99d1760c73b0980238677b4b8bc..0f194168f5fca8eb768bca3ce953f28097fed578 100644 +--- a/build.gradle.kts ++++ b/build.gradle.kts +@@ -18,8 +18,12 @@ repositories { + } + + dependencies { +- implementation(project(":paper-api")) +- implementation(project(":paper-mojangapi")) ++ implementation(project(":pufferfish-api")) // Pufferfish // Paper ++ // Pufferfish start ++ implementation("io.papermc.paper:paper-mojangapi:1.18.1-R0.1-SNAPSHOT") { ++ exclude("io.papermc.paper", "paper-api") ++ } ++ // Pufferfish end + // Paper start + implementation("org.jline:jline-terminal-jansi:3.21.0") + implementation("net.minecrell:terminalconsoleappender:1.3.0") +@@ -51,6 +55,14 @@ dependencies { + } + // Paper end + ++ // Pufferfish start ++ implementation("org.yaml:snakeyaml:1.28") ++ implementation ("me.carleslc.Simple-YAML:Simple-Yaml:ed30ff3e6b") { ++ exclude(group="org.yaml", module="snakeyaml") ++ } ++ // Pufferfish end ++ implementation("com.github.technove:Flare:2c4a2114a0") // Pufferfish - flare ++ + testImplementation("io.github.classgraph:classgraph:4.8.47") // Paper - mob goal test + testImplementation("junit:junit:4.13.2") + testImplementation("org.hamcrest:hamcrest-library:1.3") +@@ -68,7 +80,7 @@ tasks.jar { + attributes( + "Main-Class" to "org.bukkit.craftbukkit.Main", + "Implementation-Title" to "CraftBukkit", +- "Implementation-Version" to "git-Paper-$implementationVersion", ++ "Implementation-Version" to "git-Pufferfish-$implementationVersion", // Pufferfish + "Implementation-Vendor" to date, // Paper + "Specification-Title" to "Bukkit", + "Specification-Version" to project.version, +diff --git a/src/main/java/co/aikar/timings/TimingsExport.java b/src/main/java/co/aikar/timings/TimingsExport.java +index 5e3b7fb2e0b7608610555cd23e7ad25a05883181..26bde1c22363ddbdbe4127d14bae54e17b0d7393 100644 +--- a/src/main/java/co/aikar/timings/TimingsExport.java ++++ b/src/main/java/co/aikar/timings/TimingsExport.java +@@ -227,7 +227,8 @@ public class TimingsExport extends Thread { + parent.put("config", createObject( + pair("spigot", mapAsJSON(Bukkit.spigot().getSpigotConfig(), null)), + pair("bukkit", mapAsJSON(Bukkit.spigot().getBukkitConfig(), null)), +- pair("paper", mapAsJSON(Bukkit.spigot().getPaperConfig(), null)) ++ pair("paper", mapAsJSON(Bukkit.spigot().getPaperConfig(), null)), // Pufferfish ++ pair("pufferfish", mapAsJSON(gg.pufferfish.pufferfish.PufferfishConfig.getConfigCopy(), null)) // Pufferfish + )); + + new TimingsExport(listeners, parent, history).start(); +diff --git a/src/main/java/com/destroystokyo/paper/Metrics.java b/src/main/java/com/destroystokyo/paper/Metrics.java +index 218f5bafeed8551b55b91c7fccaf6935c8b631ca..f2fe6ea3719ff8b2913b7a3a939d7a5b75cb8b28 100644 +--- a/src/main/java/com/destroystokyo/paper/Metrics.java ++++ b/src/main/java/com/destroystokyo/paper/Metrics.java +@@ -593,7 +593,7 @@ public class Metrics { + boolean logFailedRequests = config.getBoolean("logFailedRequests", false); + // Only start Metrics, if it's enabled in the config + if (config.getBoolean("enabled", true)) { +- Metrics metrics = new Metrics("Paper", serverUUID, logFailedRequests, Bukkit.getLogger()); ++ Metrics metrics = new Metrics("Pufferfish", serverUUID, logFailedRequests, Bukkit.getLogger()); // Pufferfish + + metrics.addCustomChart(new Metrics.SimplePie("minecraft_version", () -> { + String minecraftVersion = Bukkit.getVersion(); +@@ -603,7 +603,7 @@ public class Metrics { + + metrics.addCustomChart(new Metrics.SingleLineChart("players", () -> Bukkit.getOnlinePlayers().size())); + metrics.addCustomChart(new Metrics.SimplePie("online_mode", () -> Bukkit.getOnlineMode() ? "online" : "offline")); +- metrics.addCustomChart(new Metrics.SimplePie("paper_version", () -> (Metrics.class.getPackage().getImplementationVersion() != null) ? Metrics.class.getPackage().getImplementationVersion() : "unknown")); ++ metrics.addCustomChart(new Metrics.SimplePie("pufferfish_version", () -> (Metrics.class.getPackage().getImplementationVersion() != null) ? Metrics.class.getPackage().getImplementationVersion() : "unknown")); + + metrics.addCustomChart(new Metrics.DrilldownPie("java_version", () -> { + Map> map = new HashMap<>(); +diff --git a/src/main/java/com/destroystokyo/paper/PaperConfig.java b/src/main/java/com/destroystokyo/paper/PaperConfig.java +index 1d3cc8836d2ccbec4a8660f86501be35c76e8b0b..2a19037d3ad734d1cf746d02b72926dd702fd23a 100644 +--- a/src/main/java/com/destroystokyo/paper/PaperConfig.java ++++ b/src/main/java/com/destroystokyo/paper/PaperConfig.java +@@ -232,6 +232,15 @@ public class PaperConfig { + public static String timingsServerName; + private static void timings() { + boolean timings = getBoolean("timings.enabled", true); ++ // Pufferfish start ++ boolean reallyEnableTimings = getBoolean("timings.really-enabled", false); ++ if (timings && !reallyEnableTimings) { ++ Bukkit.getLogger().log(Level.WARNING, "[Pufferfish] To improve performance, timings have been disabled by default"); ++ Bukkit.getLogger().log(Level.WARNING, "[Pufferfish] You can still use timings by using /timings on, but they will not start on server startup unless you set timings.really-enabled to true in paper.yml"); ++ Bukkit.getLogger().log(Level.WARNING, "[Pufferfish] If you would like to disable this message, either set timings.really-enabled to true or timings.enabled to false."); ++ } ++ timings = reallyEnableTimings; ++ // Pufferfish end + boolean verboseTimings = getBoolean("timings.verbose", true); + TimingsManager.url = getString("timings.url", "https://timings.aikar.co/"); + if (!TimingsManager.url.endsWith("/")) { +diff --git a/src/main/java/com/destroystokyo/paper/util/misc/AreaMap.java b/src/main/java/com/destroystokyo/paper/util/misc/AreaMap.java +index c89f6986eda5a132a948732ea1b6923370685317..a69c13e20040c1561d9c2d4d89ec7d4e635134fc 100644 +--- a/src/main/java/com/destroystokyo/paper/util/misc/AreaMap.java ++++ b/src/main/java/com/destroystokyo/paper/util/misc/AreaMap.java +@@ -26,7 +26,7 @@ public abstract class AreaMap { + + // we use linked for better iteration. + // map of: coordinate to set of objects in coordinate +- protected final Long2ObjectOpenHashMap> areaMap = new Long2ObjectOpenHashMap<>(1024, 0.7f); ++ protected Long2ObjectOpenHashMap> areaMap = new Long2ObjectOpenHashMap<>(1024, 0.7f); // Pufferfish - not actually final + protected final PooledLinkedHashSets pooledHashSets; + + protected final ChangeCallback addCallback; +@@ -160,7 +160,8 @@ public abstract class AreaMap { + protected abstract PooledLinkedHashSets.PooledObjectLinkedOpenHashSet getEmptySetFor(final E object); + + // expensive op, only for debug +- protected void validate(final E object, final int viewDistance) { ++ protected void validate0(final E object, final int viewDistance) { // Pufferfish - rename this thing just in case it gets used I'd rather a compile time error. ++ if (true) throw new UnsupportedOperationException(); // Pufferfish - not going to put in the effort to fix this if it doesn't ever get used. + int entiesGot = 0; + int expectedEntries = (2 * viewDistance + 1); + expectedEntries *= expectedEntries; +diff --git a/src/main/java/com/destroystokyo/paper/util/misc/PlayerAreaMap.java b/src/main/java/com/destroystokyo/paper/util/misc/PlayerAreaMap.java +index 46954db7ecd35ac4018fdf476df7c8020d7ce6c8..1ad890a244bdf6df48a8db68cb43450e08c788a6 100644 +--- a/src/main/java/com/destroystokyo/paper/util/misc/PlayerAreaMap.java ++++ b/src/main/java/com/destroystokyo/paper/util/misc/PlayerAreaMap.java +@@ -5,7 +5,7 @@ import net.minecraft.server.level.ServerPlayer; + /** + * @author Spottedleaf + */ +-public final class PlayerAreaMap extends AreaMap { ++public class PlayerAreaMap extends AreaMap { // Pufferfish - not actually final + + public PlayerAreaMap() { + super(); +diff --git a/src/main/java/gg/airplane/structs/FluidDirectionCache.java b/src/main/java/gg/airplane/structs/FluidDirectionCache.java +new file mode 100644 +index 0000000000000000000000000000000000000000..aa8467b9dda1f7707e41f50ac7b3e9d7343723ec +--- /dev/null ++++ b/src/main/java/gg/airplane/structs/FluidDirectionCache.java +@@ -0,0 +1,136 @@ ++package gg.airplane.structs; ++ ++import it.unimi.dsi.fastutil.HashCommon; ++ ++/** ++ * This is a replacement for the cache used in FluidTypeFlowing. ++ * The requirements for the previous cache were: ++ * - Store 200 entries ++ * - Look for the flag in the cache ++ * - If it exists, move to front of cache ++ * - If it doesn't exist, remove last entry in cache and insert in front ++ * ++ * This class accomplishes something similar, however has a few different ++ * requirements put into place to make this more optimize: ++ * ++ * - maxDistance is the most amount of entries to be checked, instead ++ * of having to check the entire list. ++ * - In combination with that, entries are all tracked by age and how ++ * frequently they're used. This enables us to remove old entries, ++ * without constantly shifting any around. ++ * ++ * Usage of the previous map would have to reset the head every single usage, ++ * shifting the entire map. Here, nothing happens except an increment when ++ * the cache is hit, and when it needs to replace an old element only a single ++ * element is modified. ++ */ ++public class FluidDirectionCache { ++ ++ private static class FluidDirectionEntry { ++ private final T data; ++ private final boolean flag; ++ private int uses = 0; ++ private int age = 0; ++ ++ private FluidDirectionEntry(T data, boolean flag) { ++ this.data = data; ++ this.flag = flag; ++ } ++ ++ public int getValue() { ++ return this.uses - (this.age >> 1); // age isn't as important as uses ++ } ++ ++ public void incrementUses() { ++ this.uses = this.uses + 1 & Integer.MAX_VALUE; ++ } ++ ++ public void incrementAge() { ++ this.age = this.age + 1 & Integer.MAX_VALUE; ++ } ++ } ++ ++ private final FluidDirectionEntry[] entries; ++ private final int mask; ++ private final int maxDistance; // the most amount of entries to check for a value ++ ++ public FluidDirectionCache(int size) { ++ int arraySize = HashCommon.nextPowerOfTwo(size); ++ this.entries = new FluidDirectionEntry[arraySize]; ++ this.mask = arraySize - 1; ++ this.maxDistance = Math.min(arraySize, 4); ++ } ++ ++ public Boolean getValue(T data) { ++ FluidDirectionEntry curr; ++ int pos; ++ ++ if ((curr = this.entries[pos = HashCommon.mix(data.hashCode()) & this.mask]) == null) { ++ return null; ++ } else if (data.equals(curr.data)) { ++ curr.incrementUses(); ++ return curr.flag; ++ } ++ ++ int checked = 1; // start at 1 because we already checked the first spot above ++ ++ while ((curr = this.entries[pos = (pos + 1) & this.mask]) != null) { ++ if (data.equals(curr.data)) { ++ curr.incrementUses(); ++ return curr.flag; ++ } else if (++checked >= this.maxDistance) { ++ break; ++ } ++ } ++ ++ return null; ++ } ++ ++ public void putValue(T data, boolean flag) { ++ FluidDirectionEntry curr; ++ int pos; ++ ++ if ((curr = this.entries[pos = HashCommon.mix(data.hashCode()) & this.mask]) == null) { ++ this.entries[pos] = new FluidDirectionEntry<>(data, flag); // add ++ return; ++ } else if (data.equals(curr.data)) { ++ curr.incrementUses(); ++ return; ++ } ++ ++ int checked = 1; // start at 1 because we already checked the first spot above ++ ++ while ((curr = this.entries[pos = (pos + 1) & this.mask]) != null) { ++ if (data.equals(curr.data)) { ++ curr.incrementUses(); ++ return; ++ } else if (++checked >= this.maxDistance) { ++ this.forceAdd(data, flag); ++ return; ++ } ++ } ++ ++ this.entries[pos] = new FluidDirectionEntry<>(data, flag); // add ++ } ++ ++ private void forceAdd(T data, boolean flag) { ++ int expectedPos = HashCommon.mix(data.hashCode()) & this.mask; ++ ++ int toRemovePos = expectedPos; ++ FluidDirectionEntry entryToRemove = this.entries[toRemovePos]; ++ ++ for (int i = expectedPos + 1; i < expectedPos + this.maxDistance; i++) { ++ int pos = i & this.mask; ++ FluidDirectionEntry entry = this.entries[pos]; ++ if (entry.getValue() < entryToRemove.getValue()) { ++ toRemovePos = pos; ++ entryToRemove = entry; ++ } ++ ++ entry.incrementAge(); // use this as a mechanism to age the other entries ++ } ++ ++ // remove the least used/oldest entry ++ this.entries[toRemovePos] = new FluidDirectionEntry(data, flag); ++ } ++} +diff --git a/src/main/java/gg/airplane/structs/ItemListWithBitset.java b/src/main/java/gg/airplane/structs/ItemListWithBitset.java +new file mode 100644 +index 0000000000000000000000000000000000000000..1b7a4ee47f4445d7f2ac91d3a73ae113edbdddb2 +--- /dev/null ++++ b/src/main/java/gg/airplane/structs/ItemListWithBitset.java +@@ -0,0 +1,114 @@ ++package gg.airplane.structs; ++ ++import net.minecraft.core.NonNullList; ++import net.minecraft.world.item.ItemStack; ++import org.apache.commons.lang.Validate; ++import org.jetbrains.annotations.NotNull; ++import org.jetbrains.annotations.Nullable; ++ ++import java.util.AbstractList; ++import java.util.Arrays; ++import java.util.List; ++ ++public class ItemListWithBitset extends AbstractList { ++ public static ItemListWithBitset fromList(List list) { ++ if (list instanceof ItemListWithBitset ours) { ++ return ours; ++ } ++ return new ItemListWithBitset(list); ++ } ++ ++ private static ItemStack[] createArray(int size) { ++ ItemStack[] array = new ItemStack[size]; ++ Arrays.fill(array, ItemStack.EMPTY); ++ return array; ++ } ++ ++ private final ItemStack[] items; ++ ++ private long bitSet = 0; ++ private final long allBits; ++ ++ private static class OurNonNullList extends NonNullList { ++ protected OurNonNullList(List delegate) { ++ super(delegate, ItemStack.EMPTY); ++ } ++ } ++ ++ public final NonNullList nonNullList = new OurNonNullList(this); ++ ++ private ItemListWithBitset(List list) { ++ this(list.size()); ++ ++ for (int i = 0; i < list.size(); i++) { ++ this.set(i, list.get(i)); ++ } ++ } ++ ++ public ItemListWithBitset(int size) { ++ Validate.isTrue(size < Long.BYTES * 8, "size is too large"); ++ ++ this.items = createArray(size); ++ this.allBits = ((1L << size) - 1); ++ } ++ ++ public boolean isCompletelyEmpty() { ++ return this.bitSet == 0; ++ } ++ ++ public boolean hasFullStacks() { ++ return (this.bitSet & this.allBits) == allBits; ++ } ++ ++ @Override ++ public ItemStack set(int index, @NotNull ItemStack itemStack) { ++ ItemStack existing = this.items[index]; ++ ++ this.items[index] = itemStack; ++ ++ if (itemStack == ItemStack.EMPTY) { ++ this.bitSet &= ~(1L << index); ++ } else { ++ this.bitSet |= 1L << index; ++ } ++ ++ return existing; ++ } ++ ++ @NotNull ++ @Override ++ public ItemStack get(int var0) { ++ return this.items[var0]; ++ } ++ ++ @Override ++ public int size() { ++ return this.items.length; ++ } ++ ++ @Override ++ public void clear() { ++ Arrays.fill(this.items, ItemStack.EMPTY); ++ } ++ ++ // these are unsupported for block inventories which have a static size ++ @Override ++ public void add(int var0, ItemStack var1) { ++ throw new UnsupportedOperationException(); ++ } ++ ++ @Override ++ public ItemStack remove(int var0) { ++ throw new UnsupportedOperationException(); ++ } ++ ++ @Override ++ public String toString() { ++ return "ItemListWithBitset{" + ++ "items=" + Arrays.toString(items) + ++ ", bitSet=" + Long.toString(bitSet, 2) + ++ ", allBits=" + Long.toString(allBits, 2) + ++ ", size=" + this.items.length + ++ '}'; ++ } ++} +diff --git a/src/main/java/gg/airplane/structs/Long2FloatAgingCache.java b/src/main/java/gg/airplane/structs/Long2FloatAgingCache.java +new file mode 100644 +index 0000000000000000000000000000000000000000..a7f297ebb569f7c1f205e967ca485be70013a714 +--- /dev/null ++++ b/src/main/java/gg/airplane/structs/Long2FloatAgingCache.java +@@ -0,0 +1,119 @@ ++package gg.airplane.structs; ++ ++import it.unimi.dsi.fastutil.HashCommon; ++ ++/** ++ * A replacement for the cache used in Biome. ++ */ ++public class Long2FloatAgingCache { ++ ++ private static class AgingEntry { ++ private long data; ++ private float value; ++ private int uses = 0; ++ private int age = 0; ++ ++ private AgingEntry(long data, float value) { ++ this.data = data; ++ this.value = value; ++ } ++ ++ public void replace(long data, float flag) { ++ this.data = data; ++ this.value = flag; ++ } ++ ++ public int getValue() { ++ return this.uses - (this.age >> 1); // age isn't as important as uses ++ } ++ ++ public void incrementUses() { ++ this.uses = this.uses + 1 & Integer.MAX_VALUE; ++ } ++ ++ public void incrementAge() { ++ this.age = this.age + 1 & Integer.MAX_VALUE; ++ } ++ } ++ ++ private final AgingEntry[] entries; ++ private final int mask; ++ private final int maxDistance; // the most amount of entries to check for a value ++ ++ public Long2FloatAgingCache(int size) { ++ int arraySize = HashCommon.nextPowerOfTwo(size); ++ this.entries = new AgingEntry[arraySize]; ++ this.mask = arraySize - 1; ++ this.maxDistance = Math.min(arraySize, 4); ++ } ++ ++ public float getValue(long data) { ++ AgingEntry curr; ++ int pos; ++ ++ if ((curr = this.entries[pos = HashCommon.mix(HashCommon.long2int(data)) & this.mask]) == null) { ++ return Float.NaN; ++ } else if (data == curr.data) { ++ curr.incrementUses(); ++ return curr.value; ++ } ++ ++ int checked = 1; // start at 1 because we already checked the first spot above ++ ++ while ((curr = this.entries[pos = (pos + 1) & this.mask]) != null) { ++ if (data == curr.data) { ++ curr.incrementUses(); ++ return curr.value; ++ } else if (++checked >= this.maxDistance) { ++ break; ++ } ++ } ++ ++ return Float.NaN; ++ } ++ ++ public void putValue(long data, float value) { ++ AgingEntry curr; ++ int pos; ++ ++ if ((curr = this.entries[pos = HashCommon.mix(HashCommon.long2int(data)) & this.mask]) == null) { ++ this.entries[pos] = new AgingEntry(data, value); // add ++ return; ++ } else if (data == curr.data) { ++ curr.incrementUses(); ++ return; ++ } ++ ++ int checked = 1; // start at 1 because we already checked the first spot above ++ ++ while ((curr = this.entries[pos = (pos + 1) & this.mask]) != null) { ++ if (data == curr.data) { ++ curr.incrementUses(); ++ return; ++ } else if (++checked >= this.maxDistance) { ++ this.forceAdd(data, value); ++ return; ++ } ++ } ++ ++ this.entries[pos] = new AgingEntry(data, value); // add ++ } ++ ++ private void forceAdd(long data, float value) { ++ int expectedPos = HashCommon.mix(HashCommon.long2int(data)) & this.mask; ++ AgingEntry entryToRemove = this.entries[expectedPos]; ++ ++ for (int i = expectedPos + 1; i < expectedPos + this.maxDistance; i++) { ++ int pos = i & this.mask; ++ AgingEntry entry = this.entries[pos]; ++ if (entry.getValue() < entryToRemove.getValue()) { ++ entryToRemove = entry; ++ } ++ ++ entry.incrementAge(); // use this as a mechanism to age the other entries ++ } ++ ++ // remove the least used/oldest entry ++ entryToRemove.replace(data, value); ++ } ++} +diff --git a/src/main/java/gg/pufferfish/pufferfish/PufferfishCommand.java b/src/main/java/gg/pufferfish/pufferfish/PufferfishCommand.java +new file mode 100644 +index 0000000000000000000000000000000000000000..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..c32f284b5ce200c0f4dc7e22b986c10edee5a66f +--- /dev/null ++++ b/src/main/java/gg/pufferfish/pufferfish/PufferfishConfig.java +@@ -0,0 +1,266 @@ ++package gg.pufferfish.pufferfish; ++ ++import java.io.File; ++import java.io.IOException; ++import java.util.Collections; ++import net.minecraft.server.MinecraftServer; ++import org.apache.logging.log4j.Level; ++import org.bukkit.configuration.ConfigurationSection; ++import net.minecraft.core.Registry; ++import net.minecraft.world.entity.EntityType; ++import java.lang.reflect.Method; ++import java.lang.reflect.Modifier; ++import java.util.List; ++import gg.pufferfish.pufferfish.flare.FlareCommand; ++import net.minecraft.server.MinecraftServer; ++import org.apache.logging.log4j.Level; ++import org.bukkit.configuration.ConfigurationSection; ++import org.bukkit.configuration.MemoryConfiguration; ++import org.jetbrains.annotations.Nullable; ++import org.simpleyaml.configuration.comments.CommentType; ++import org.simpleyaml.configuration.file.YamlFile; ++import org.simpleyaml.exceptions.InvalidConfigurationException; ++import org.bukkit.command.SimpleCommandMap; ++ ++import java.lang.reflect.Method; ++import java.lang.reflect.Modifier; ++import java.util.List; ++import java.net.URI; ++import java.util.Collections; ++ ++public class PufferfishConfig { ++ ++ private static final YamlFile config = new YamlFile(); ++ private static int updates = 0; ++ ++ private static ConfigurationSection convertToBukkit(org.simpleyaml.configuration.ConfigurationSection section) { ++ ConfigurationSection newSection = new MemoryConfiguration(); ++ for (String key : section.getKeys(false)) { ++ if (section.isConfigurationSection(key)) { ++ newSection.set(key, convertToBukkit(section.getConfigurationSection(key))); ++ } else { ++ newSection.set(key, section.get(key)); ++ } ++ } ++ return newSection; ++ } ++ ++ public static ConfigurationSection getConfigCopy() { ++ return convertToBukkit(config); ++ } ++ ++ public static int getUpdates() { ++ return updates; ++ } ++ ++ public static void load() throws IOException { ++ File configFile = new File("pufferfish.yml"); ++ ++ if (configFile.exists()) { ++ try { ++ config.load(configFile); ++ } catch (InvalidConfigurationException e) { ++ throw new IOException(e); ++ } ++ } ++ ++ getString("info.version", "1.0"); ++ setComment("info", ++ "Pufferfish Configuration", ++ "Check out Pufferfish Host for maximum performance server hosting: https://pufferfish.host", ++ "Join our Discord for support: https://discord.gg/reZw4vQV9H", ++ "Download new builds at https://ci.pufferfish.host/job/Pufferfish"); ++ ++ for (Method method : PufferfishConfig.class.getDeclaredMethods()) { ++ if (Modifier.isStatic(method.getModifiers()) && Modifier.isPrivate(method.getModifiers()) && method.getParameterCount() == 0 && ++ method.getReturnType() == Void.TYPE && !method.getName().startsWith("lambda")) { ++ method.setAccessible(true); ++ try { ++ method.invoke(null); ++ } catch (Throwable t) { ++ MinecraftServer.LOGGER.log(Level.WARN, "Failed to load configuration option from " + method.getName(), t); ++ } ++ } ++ } ++ ++ updates++; ++ ++ config.save(configFile); ++ } ++ ++ 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 enableAsyncMobSpawning; ++ public static boolean asyncMobSpawningInitialized; ++ private static void asyncMobSpawning() { ++ boolean temp = getBoolean("enable-async-mob-spawning", true, ++ "Whether or not asynchronous mob spawning should be enabled.", ++ "On servers with many entities, this can improve performance by up to 15%. You must have", ++ "paper's per-player-mob-spawns setting set to true for this to work.", ++ "One quick note - this does not actually spawn mobs async (that would be very unsafe).", ++ "This just offloads some expensive calculations that are required for mob spawning."); ++ ++ // This prevents us from changing the value during a reload. ++ if (!asyncMobSpawningInitialized) { ++ asyncMobSpawningInitialized = true; ++ enableAsyncMobSpawning = temp; ++ } ++ } ++ ++ public static int maxProjectileLoadsPerTick; ++ public static int maxProjectileLoadsPerProjectile; ++ private static void projectileLoading() { ++ maxProjectileLoadsPerTick = getInt("projectile.max-loads-per-tick", 10, "Controls how many chunks are allowed", "to be sync loaded by projectiles in a tick."); ++ maxProjectileLoadsPerProjectile = getInt("projectile.max-loads-per-projectile", 10, "Controls how many chunks a projectile", "can load in its lifetime before it gets", "automatically removed."); ++ ++ setComment("projectile", "Optimizes projectile settings"); ++ } ++ ++ ++ public static boolean dearEnabled; ++ public static int startDistance; ++ public static int startDistanceSquared; ++ public static int maximumActivationPrio; ++ public static int activationDistanceMod; ++ ++ private static void dynamicActivationOfBrains() throws IOException { ++ dearEnabled = getBoolean("dab.enabled", "activation-range.enabled", true); ++ startDistance = getInt("dab.start-distance", "activation-range.start-distance", 12, ++ "This value determines how far away an entity has to be", ++ "from the player to start being effected by DEAR."); ++ startDistanceSquared = startDistance * startDistance; ++ maximumActivationPrio = getInt("dab.max-tick-freq", "activation-range.max-tick-freq", 20, ++ "This value defines how often in ticks, the furthest entity", ++ "will get their pathfinders and behaviors ticked. 20 = 1s"); ++ activationDistanceMod = getInt("dab.activation-dist-mod", "activation-range.activation-dist-mod", 8, ++ "This value defines how much distance modifies an entity's", ++ "tick frequency. freq = (distanceToPlayer^2) / (2^value)", ++ "If you want further away entities to tick less often, use 7.", ++ "If you want further away entities to tick more often, try 9."); ++ ++ for (EntityType entityType : Registry.ENTITY_TYPE) { ++ entityType.dabEnabled = true; // reset all, before setting the ones to true ++ } ++ getStringList("dab.blacklisted-entities", "activation-range.blacklisted-entities", Collections.emptyList(), "A list of entities to ignore for activation") ++ .forEach(name -> EntityType.byString(name).ifPresentOrElse(entityType -> { ++ entityType.dabEnabled = false; ++ }, () -> MinecraftServer.LOGGER.log(Level.WARN, "Unknown entity \"" + name + "\""))); ++ ++ setComment("dab", "Optimizes entity brains when", "they're far away from the player"); ++ } ++ ++ public static boolean disableInactiveGoalSelectorTick; ++ private static void inactiveGoalSelectorDisable() { ++ getBoolean("inactive-goal-selector-disable", true, ++ "Disables the AI goal selector in entity inactive ticks.", ++ "This can improve performance by a few percent, but has minor gameplay implications."); ++ } ++ ++ public static URI profileWebUrl; ++ private static void profilerOptions() { ++ profileWebUrl = URI.create(getString("flare.url", "https://flare.airplane.gg", "Sets the server to use for profiles.")); ++ ++ setComment("flare", "Configures Flare, the built-in profiler"); ++ } ++ ++ ++ public static String accessToken; ++ private static void airplaneWebServices() { ++ accessToken = getString("web-services.token", ""); ++ // todo lookup token (off-thread) and let users know if their token is valid ++ if (accessToken.length() > 0) { ++ gg.pufferfish.pufferfish.flare.FlareSetup.init(); // Pufferfish ++ SimpleCommandMap commandMap = MinecraftServer.getServer().server.getCommandMap(); ++ if (commandMap.getCommand("flare") == null) { ++ commandMap.register("flare", "Pufferfish", new FlareCommand()); ++ } ++ } ++ ++ setComment("web-services", "Options for connecting to Pufferfish/Airplane's online utilities"); ++ ++ } ++ ++ ++ public static boolean disableMethodProfiler; ++ private static void miscSettings() { ++ disableMethodProfiler = config.getBoolean("misc.disable-method-profiler", true); ++ config.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..adafc4fd661cf080b004b86c3eaed231a0133101 +--- /dev/null ++++ b/src/main/java/gg/pufferfish/pufferfish/PufferfishVersionFetcher.java +@@ -0,0 +1,136 @@ ++package gg.pufferfish.pufferfish; ++ ++import static net.kyori.adventure.text.Component.text; ++import static net.kyori.adventure.text.format.NamedTextColor.GREEN; ++import static net.kyori.adventure.text.format.NamedTextColor.RED; ++ ++import com.destroystokyo.paper.VersionHistoryManager; ++import com.destroystokyo.paper.util.VersionFetcher; ++import com.google.gson.Gson; ++import com.google.gson.JsonObject; ++import java.io.IOException; ++import java.net.URI; ++import java.net.http.HttpClient; ++import java.net.http.HttpRequest; ++import java.net.http.HttpResponse; ++import java.nio.charset.StandardCharsets; ++import java.util.concurrent.TimeUnit; ++import java.util.logging.Level; ++import java.util.logging.Logger; ++import net.kyori.adventure.text.Component; ++import net.kyori.adventure.text.JoinConfiguration; ++import net.kyori.adventure.text.format.NamedTextColor; ++import net.kyori.adventure.text.format.TextDecoration; ++import org.bukkit.craftbukkit.CraftServer; ++import org.jetbrains.annotations.NotNull; ++import org.jetbrains.annotations.Nullable; ++ ++public class PufferfishVersionFetcher implements VersionFetcher { ++ ++ private static final Logger LOGGER = Logger.getLogger("PufferfishVersionFetcher"); ++ private static final HttpClient client = HttpClient.newHttpClient(); ++ ++ private static final URI JENKINS_URI = URI.create("https://ci.pufferfish.host/job/Pufferfish-1.18/lastSuccessfulBuild/buildNumber"); ++ private static final String GITHUB_FORMAT = "https://api.github.com/repos/pufferfish-gg/Pufferfish/compare/ver/1.18...%s"; ++ ++ private static final HttpResponse.BodyHandler JSON_OBJECT_BODY_HANDLER = responseInfo -> HttpResponse.BodySubscribers ++ .mapping( ++ HttpResponse.BodySubscribers.ofString(StandardCharsets.UTF_8), ++ string -> new Gson().fromJson(string, JsonObject.class) ++ ); ++ ++ @Override ++ public long getCacheTime() { ++ return TimeUnit.MINUTES.toMillis(30); ++ } ++ ++ @Override ++ public @NotNull Component getVersionMessage(final @NotNull String serverVersion) { ++ final String[] parts = CraftServer.class.getPackage().getImplementationVersion().split("-"); ++ @NotNull Component component; ++ ++ if (parts.length != 3) { ++ component = text("Unknown server version.", RED); ++ } else { ++ final String versionString = parts[2]; ++ ++ try { ++ component = this.fetchJenkinsVersion(Integer.parseInt(versionString)); ++ } catch (NumberFormatException e) { ++ component = this.fetchGithubVersion(versionString.substring(1, versionString.length() - 1)); ++ } ++ } ++ ++ final @Nullable Component history = this.getHistory(); ++ return history != null ? Component ++ .join(JoinConfiguration.noSeparators(), component, Component.newline(), this.getHistory()) : component; ++ } ++ ++ private @NotNull Component fetchJenkinsVersion(final int versionNumber) { ++ final HttpRequest request = HttpRequest.newBuilder(JENKINS_URI).build(); ++ try { ++ final HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); ++ if (response.statusCode() != 200) { ++ return text("Received invalid status code (" + response.statusCode() + ") from server.", RED); ++ } ++ ++ int latestVersionNumber; ++ try { ++ latestVersionNumber = Integer.parseInt(response.body()); ++ } catch (NumberFormatException e) { ++ LOGGER.log(Level.WARNING, "Received invalid response from Jenkins \"" + response.body() + "\"."); ++ return text("Received invalid response from server.", RED); ++ } ++ ++ final int versionDiff = latestVersionNumber - versionNumber; ++ return this.getResponseMessage(versionDiff); ++ } catch (IOException | InterruptedException e) { ++ LOGGER.log(Level.WARNING, "Failed to look up version from Jenkins", e); ++ return text("Failed to retrieve version from server.", RED); ++ } ++ } ++ ++ // Based off code contributed by Techcable in Paper/GH-65 ++ private @NotNull Component fetchGithubVersion(final @NotNull String hash) { ++ final URI uri = URI.create(String.format(GITHUB_FORMAT, hash)); ++ final HttpRequest request = HttpRequest.newBuilder(uri).build(); ++ try { ++ final HttpResponse response = client.send(request, JSON_OBJECT_BODY_HANDLER); ++ if (response.statusCode() != 200) { ++ return text("Received invalid status code (" + response.statusCode() + ") from server.", RED); ++ } ++ ++ final JsonObject obj = response.body(); ++ final int versionDiff = obj.get("behind_by").getAsInt(); ++ ++ return this.getResponseMessage(versionDiff); ++ } catch (IOException | InterruptedException e) { ++ LOGGER.log(Level.WARNING, "Failed to look up version from GitHub", e); ++ return text("Failed to retrieve version from server.", RED); ++ } ++ } ++ ++ private @NotNull Component getResponseMessage(final int versionDiff) { ++ return switch (Math.max(-1, Math.min(1, versionDiff))) { ++ case -1 -> text("You are running an unsupported version of Pufferfish.", RED); ++ case 0 -> text("You are on the latest version!", GREEN); ++ default -> text("You are running " + versionDiff + " version" + (versionDiff == 1 ? "" : "s") + " beyond. " + ++ "Please update your server when possible to maintain stability, security, and receive the latest optimizations.", ++ RED); ++ }; ++ } ++ ++ private @Nullable Component getHistory() { ++ final VersionHistoryManager.VersionData data = VersionHistoryManager.INSTANCE.getVersionData(); ++ if (data == null) { ++ return null; ++ } ++ ++ final String oldVersion = data.getOldVersion(); ++ if (oldVersion == null) { ++ return null; ++ } ++ ++ return Component.text("Previous version: " + oldVersion, NamedTextColor.GRAY, TextDecoration.ITALIC); ++ } ++} +\ No newline at end of file +diff --git a/src/main/java/gg/pufferfish/pufferfish/compat/ServerConfigurations.java b/src/main/java/gg/pufferfish/pufferfish/compat/ServerConfigurations.java +new file mode 100644 +index 0000000000000000000000000000000000000000..f0ec1f3944c00d38537f6f3b3f13236b22562458 +--- /dev/null ++++ b/src/main/java/gg/pufferfish/pufferfish/compat/ServerConfigurations.java +@@ -0,0 +1,78 @@ ++package gg.pufferfish.pufferfish.compat; ++ ++import co.aikar.timings.TimingsManager; ++import com.google.common.io.Files; ++import org.bukkit.configuration.InvalidConfigurationException; ++import org.bukkit.configuration.file.YamlConfiguration; ++ ++import java.io.ByteArrayOutputStream; ++import java.io.File; ++import java.io.FileInputStream; ++import java.io.IOException; ++import java.nio.charset.StandardCharsets; ++import java.util.Arrays; ++import java.util.HashMap; ++import java.util.List; ++import java.util.Map; ++import java.util.Properties; ++import java.util.stream.Collectors; ++ ++public class ServerConfigurations { ++ ++ public static final String[] configurationFiles = new String[]{ ++ "server.properties", ++ "bukkit.yml", ++ "spigot.yml", ++ "paper.yml", ++ "pufferfish.yml" ++ }; ++ ++ public static Map getCleanCopies() throws IOException { ++ Map files = new HashMap<>(configurationFiles.length); ++ for (String file : configurationFiles) { ++ files.put(file, getCleanCopy(file)); ++ } ++ return files; ++ } ++ ++ public static String getCleanCopy(String configName) throws IOException { ++ File file = new File(configName); ++ List hiddenConfigs = TimingsManager.hiddenConfigs; ++ ++ switch (Files.getFileExtension(configName)) { ++ case "properties": { ++ Properties properties = new Properties(); ++ try (FileInputStream inputStream = new FileInputStream(file)) { ++ properties.load(inputStream); ++ } ++ for (String hiddenConfig : hiddenConfigs) { ++ properties.remove(hiddenConfig); ++ } ++ ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); ++ properties.store(outputStream, ""); ++ return Arrays.stream(outputStream.toString() ++ .split("\n")) ++ .filter(line -> !line.startsWith("#")) ++ .collect(Collectors.joining("\n")); ++ } ++ case "yml": { ++ YamlConfiguration configuration = new YamlConfiguration(); ++ try { ++ configuration.load(file); ++ } catch (InvalidConfigurationException e) { ++ throw new IOException(e); ++ } ++ configuration.options().header(null); ++ for (String key : configuration.getKeys(true)) { ++ if (hiddenConfigs.contains(key)) { ++ configuration.set(key, null); ++ } ++ } ++ return configuration.saveToString(); ++ } ++ default: ++ throw new IllegalArgumentException("Bad file type " + configName); ++ } ++ } ++ ++} +diff --git a/src/main/java/gg/pufferfish/pufferfish/flare/CustomCategories.java b/src/main/java/gg/pufferfish/pufferfish/flare/CustomCategories.java +new file mode 100644 +index 0000000000000000000000000000000000000000..401b42e29bccb5251684062f10b2e0f8b091bc95 +--- /dev/null ++++ b/src/main/java/gg/pufferfish/pufferfish/flare/CustomCategories.java +@@ -0,0 +1,8 @@ ++package gg.pufferfish.pufferfish.flare; ++ ++import co.technove.flare.live.category.GraphCategory; ++ ++public class CustomCategories { ++ public static final GraphCategory MC_PERF = new GraphCategory("MC Performance"); ++ public static final GraphCategory ENTITIES_AND_CHUNKS = new GraphCategory("Entities & Chunks"); ++} +diff --git a/src/main/java/gg/pufferfish/pufferfish/flare/FlareCommand.java b/src/main/java/gg/pufferfish/pufferfish/flare/FlareCommand.java +new file mode 100644 +index 0000000000000000000000000000000000000000..6401e508729407254abd8caea988f84d1eb7cb0f +--- /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.log(Level.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..0435f1dbec1cc9fc1751567128d9fff3f246c7b4 +--- /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.log(Level.WARN, "Flare warning: " + warning); ++ } ++ supported = true; ++ } catch (InitializationException e) { ++ MinecraftServer.LOGGER.log(Level.WARN, "Failed to enable Flare:", e); ++ } ++ } ++ ++ public static boolean isSupported() { ++ return supported; ++ } ++ ++} +diff --git a/src/main/java/gg/pufferfish/pufferfish/flare/PluginLookup.java b/src/main/java/gg/pufferfish/pufferfish/flare/PluginLookup.java +new file mode 100644 +index 0000000000000000000000000000000000000000..74aab5eb4b54ffbaf19b8976ffb8ca4a64584006 +--- /dev/null ++++ b/src/main/java/gg/pufferfish/pufferfish/flare/PluginLookup.java +@@ -0,0 +1,44 @@ ++package gg.pufferfish.pufferfish.flare; ++ ++import com.google.common.cache.Cache; ++import com.google.common.cache.CacheBuilder; ++import org.bukkit.Bukkit; ++import org.bukkit.plugin.Plugin; ++import org.bukkit.plugin.java.PluginClassLoader; ++ ++import java.util.Optional; ++import java.util.concurrent.TimeUnit; ++ ++public class PluginLookup { ++ private static final Cache pluginNameCache = CacheBuilder.newBuilder() ++ .expireAfterAccess(1, TimeUnit.MINUTES) ++ .maximumSize(1024) ++ .build(); ++ ++ public static Optional getPluginForClass(String name) { ++ if (name.startsWith("net.minecraft") || name.startsWith("java.") || name.startsWith("com.mojang") || ++ name.startsWith("com.google") || name.startsWith("it.unimi") || name.startsWith("sun")) { ++ return Optional.empty(); ++ } ++ ++ String existing = pluginNameCache.getIfPresent(name); ++ if (existing != null) { ++ return Optional.ofNullable(existing.isEmpty() ? null : existing); ++ } ++ ++ String newValue = ""; ++ ++ for (Plugin plugin : Bukkit.getPluginManager().getPlugins()) { ++ ClassLoader classLoader = plugin.getClass().getClassLoader(); ++ if (classLoader instanceof PluginClassLoader) { ++ if (((PluginClassLoader) classLoader)._airplane_hasClass(name)) { ++ newValue = plugin.getName(); ++ break; ++ } ++ } ++ } ++ ++ pluginNameCache.put(name, newValue); ++ return Optional.ofNullable(newValue.isEmpty() ? null : newValue); ++ } ++} +diff --git a/src/main/java/gg/pufferfish/pufferfish/flare/ProfilingManager.java b/src/main/java/gg/pufferfish/pufferfish/flare/ProfilingManager.java +new file mode 100644 +index 0000000000000000000000000000000000000000..e3f76eb11a261c3347f0cd89b5da309bc2dc82f9 +--- /dev/null ++++ b/src/main/java/gg/pufferfish/pufferfish/flare/ProfilingManager.java +@@ -0,0 +1,151 @@ ++package gg.pufferfish.pufferfish.flare; ++ ++import co.technove.flare.Flare; ++import co.technove.flare.FlareAuth; ++import co.technove.flare.FlareBuilder; ++import co.technove.flare.exceptions.UserReportableException; ++import co.technove.flare.internal.profiling.ProfileType; ++import gg.pufferfish.pufferfish.PufferfishConfig; ++import gg.pufferfish.pufferfish.PufferfishLogger; ++import gg.pufferfish.pufferfish.compat.ServerConfigurations; ++import gg.pufferfish.pufferfish.flare.collectors.GCEventCollector; ++import gg.pufferfish.pufferfish.flare.collectors.StatCollector; ++import gg.pufferfish.pufferfish.flare.collectors.TPSCollector; ++import gg.pufferfish.pufferfish.flare.collectors.WorldCountCollector; ++import org.bukkit.Bukkit; ++import org.bukkit.craftbukkit.scheduler.MinecraftInternalPlugin; ++import org.bukkit.scheduler.BukkitTask; ++import oshi.SystemInfo; ++import oshi.hardware.CentralProcessor; ++import oshi.hardware.GlobalMemory; ++import oshi.hardware.HardwareAbstractionLayer; ++import oshi.hardware.VirtualMemory; ++import oshi.software.os.OperatingSystem; ++ ++import java.io.IOException; ++import java.net.URI; ++import java.time.Duration; ++import java.util.Objects; ++import java.util.logging.Level; ++ ++public class ProfilingManager { ++ ++ private static Flare currentFlare; ++ private static BukkitTask currentTask = null; ++ ++ public static synchronized boolean isProfiling() { ++ return currentFlare != null && currentFlare.isRunning(); ++ } ++ ++ public static synchronized String getProfilingUri() { ++ return Objects.requireNonNull(currentFlare).getURI().map(URI::toString).orElse("Flare is not running"); ++ } ++ ++ public static Duration getTimeRan() { ++ Flare flare = currentFlare; // copy reference so no need to sync ++ if (flare == null) { ++ return Duration.ofMillis(0); ++ } ++ return flare.getCurrentDuration(); ++ } ++ ++ public static synchronized boolean start(ProfileType profileType) throws UserReportableException { ++ if (currentFlare != null && !currentFlare.isRunning()) { ++ currentFlare = null; // errored out ++ } ++ if (isProfiling()) { ++ return false; ++ } ++ if (Bukkit.isPrimaryThread()) { ++ throw new UserReportableException("Profiles should be started off-thread"); ++ } ++ ++ try { ++ OperatingSystem os = new SystemInfo().getOperatingSystem(); ++ ++ SystemInfo systemInfo = new SystemInfo(); ++ HardwareAbstractionLayer hardware = systemInfo.getHardware(); ++ ++ CentralProcessor processor = hardware.getProcessor(); ++ CentralProcessor.ProcessorIdentifier processorIdentifier = processor.getProcessorIdentifier(); ++ ++ GlobalMemory memory = hardware.getMemory(); ++ VirtualMemory virtualMemory = memory.getVirtualMemory(); ++ ++ FlareBuilder builder = new FlareBuilder() ++ .withProfileType(profileType) ++ .withMemoryProfiling(true) ++ .withAuth(FlareAuth.fromTokenAndUrl(PufferfishConfig.accessToken, PufferfishConfig.profileWebUrl)) ++ ++ .withFiles(ServerConfigurations.getCleanCopies()) ++ .withVersion("Primary Version", Bukkit.getVersion()) ++ .withVersion("Bukkit Version", Bukkit.getBukkitVersion()) ++ .withVersion("Minecraft Version", Bukkit.getMinecraftVersion()) ++ ++ .withGraphCategories(CustomCategories.ENTITIES_AND_CHUNKS, CustomCategories.MC_PERF) ++ .withCollectors(new TPSCollector(), new WorldCountCollector(), new GCEventCollector(), new StatCollector()) ++ .withClassIdentifier(PluginLookup::getPluginForClass) ++ ++ .withHardware(new FlareBuilder.HardwareBuilder() ++ .setCoreCount(processor.getPhysicalProcessorCount()) ++ .setThreadCount(processor.getLogicalProcessorCount()) ++ .setCpuModel(processorIdentifier.getName()) ++ .setCpuFrequency(processor.getMaxFreq()) ++ ++ .setTotalMemory(memory.getTotal()) ++ .setTotalSwap(virtualMemory.getSwapTotal()) ++ .setTotalVirtual(virtualMemory.getVirtualMax()) ++ ) ++ ++ .withOperatingSystem(new FlareBuilder.OperatingSystemBuilder() ++ .setManufacturer(os.getManufacturer()) ++ .setFamily(os.getFamily()) ++ .setVersion(os.getVersionInfo().toString()) ++ .setBitness(os.getBitness()) ++ ); ++ ++ currentFlare = builder.build(); ++ } catch (IOException e) { ++ PufferfishLogger.LOGGER.log(Level.WARNING, "Failed to read configuration files:", e); ++ throw new UserReportableException("Failed to load configuration files, check logs for further details."); ++ } ++ ++ try { ++ currentFlare.start(); ++ } catch (IllegalStateException e) { ++ PufferfishLogger.LOGGER.log(Level.WARNING, "Error starting Flare:", e); ++ throw new UserReportableException("Failed to start Flare, check logs for further details."); ++ } ++ ++ currentTask = Bukkit.getScheduler().runTaskLater(new MinecraftInternalPlugin(), ProfilingManager::stop, 20 * 60 * 15); ++ PufferfishLogger.LOGGER.log(Level.INFO, "Flare has been started: " + getProfilingUri()); ++ return true; ++ } ++ ++ public static synchronized boolean stop() { ++ if (!isProfiling()) { ++ return false; ++ } ++ if (!currentFlare.isRunning()) { ++ currentFlare = null; ++ return true; ++ } ++ PufferfishLogger.LOGGER.log(Level.INFO, "Flare has been stopped: " + getProfilingUri()); ++ try { ++ currentFlare.stop(); ++ } catch (IllegalStateException e) { ++ PufferfishLogger.LOGGER.log(Level.WARNING, "Error occurred stopping Flare", e); ++ } ++ currentFlare = null; ++ ++ try { ++ currentTask.cancel(); ++ } catch (Throwable t) { ++ PufferfishLogger.LOGGER.log(Level.WARNING, "Error occurred stopping Flare", t); ++ } ++ ++ currentTask = null; ++ return true; ++ } ++ ++} +diff --git a/src/main/java/gg/pufferfish/pufferfish/flare/collectors/GCEventCollector.java b/src/main/java/gg/pufferfish/pufferfish/flare/collectors/GCEventCollector.java +new file mode 100644 +index 0000000000000000000000000000000000000000..d426575c669020f369960107da1e2de2f11f082f +--- /dev/null ++++ b/src/main/java/gg/pufferfish/pufferfish/flare/collectors/GCEventCollector.java +@@ -0,0 +1,66 @@ ++package gg.pufferfish.pufferfish.flare.collectors; ++ ++import co.technove.flare.Flare; ++import co.technove.flare.internal.FlareInternal; ++import co.technove.flare.live.CollectorData; ++import co.technove.flare.live.EventCollector; ++import co.technove.flare.live.LiveEvent; ++import co.technove.flare.live.category.GraphCategory; ++import co.technove.flare.live.formatter.DataFormatter; ++import com.google.common.collect.ImmutableMap; ++import com.sun.management.GarbageCollectionNotificationInfo; ++ ++import javax.management.ListenerNotFoundException; ++import javax.management.Notification; ++import javax.management.NotificationEmitter; ++import javax.management.NotificationListener; ++import javax.management.openmbean.CompositeData; ++import java.lang.management.GarbageCollectorMXBean; ++import java.lang.management.ManagementFactory; ++ ++public class GCEventCollector extends EventCollector implements NotificationListener { ++ ++ private static final CollectorData MINOR_GC = new CollectorData("builtin:gc:minor", "Minor GC", "A small pause in the program to allow Garbage Collection to run.", DataFormatter.MILLISECONDS, GraphCategory.SYSTEM); ++ private static final CollectorData MAJOR_GC = new CollectorData("builtin:gc:major", "Major GC", "A large pause in the program to allow Garbage Collection to run.", DataFormatter.MILLISECONDS, GraphCategory.SYSTEM); ++ private static final CollectorData UNKNOWN_GC = new CollectorData("builtin:gc:generic", "Major GC", "A run of the Garbage Collection.", DataFormatter.MILLISECONDS, GraphCategory.SYSTEM); ++ ++ public GCEventCollector() { ++ super(MINOR_GC, MAJOR_GC, UNKNOWN_GC); ++ } ++ ++ private static CollectorData fromString(String string) { ++ if (string.endsWith("minor GC")) { ++ return MINOR_GC; ++ } else if (string.endsWith("major GC")) { ++ return MAJOR_GC; ++ } ++ return UNKNOWN_GC; ++ } ++ ++ @Override ++ public void start(Flare flare) { ++ for (GarbageCollectorMXBean garbageCollectorBean : ManagementFactory.getGarbageCollectorMXBeans()) { ++ NotificationEmitter notificationEmitter = (NotificationEmitter) garbageCollectorBean; ++ notificationEmitter.addNotificationListener(this, null, null); ++ } ++ } ++ ++ @Override ++ public void stop(Flare flare) { ++ for (GarbageCollectorMXBean garbageCollectorBean : ManagementFactory.getGarbageCollectorMXBeans()) { ++ NotificationEmitter notificationEmitter = (NotificationEmitter) garbageCollectorBean; ++ try { ++ notificationEmitter.removeNotificationListener(this); ++ } catch (ListenerNotFoundException e) { ++ } ++ } ++ } ++ ++ @Override ++ public void handleNotification(Notification notification, Object o) { ++ if (notification.getType().equals(GarbageCollectionNotificationInfo.GARBAGE_COLLECTION_NOTIFICATION)) { ++ GarbageCollectionNotificationInfo gcInfo = GarbageCollectionNotificationInfo.from((CompositeData) notification.getUserData()); ++ reportEvent(new LiveEvent(fromString(gcInfo.getGcAction()), System.currentTimeMillis(), (int) gcInfo.getGcInfo().getDuration(), ImmutableMap.of())); ++ } ++ } ++} +diff --git a/src/main/java/gg/pufferfish/pufferfish/flare/collectors/StatCollector.java b/src/main/java/gg/pufferfish/pufferfish/flare/collectors/StatCollector.java +new file mode 100644 +index 0000000000000000000000000000000000000000..a22c6dbae53667e4c72464fa27153aee30c7946e +--- /dev/null ++++ b/src/main/java/gg/pufferfish/pufferfish/flare/collectors/StatCollector.java +@@ -0,0 +1,41 @@ ++package gg.pufferfish.pufferfish.flare.collectors; ++ ++import co.technove.flare.live.CollectorData; ++import co.technove.flare.live.LiveCollector; ++import co.technove.flare.live.category.GraphCategory; ++import co.technove.flare.live.formatter.DataFormatter; ++import com.sun.management.OperatingSystemMXBean; ++import oshi.SystemInfo; ++import oshi.hardware.CentralProcessor; ++ ++import java.lang.management.ManagementFactory; ++import java.time.Duration; ++ ++public class StatCollector extends LiveCollector { ++ ++ private static final CollectorData CPU = new CollectorData("builtin:stat:cpu", "CPU Load", "The total amount of CPU usage across all cores.", DataFormatter.PERCENT, GraphCategory.SYSTEM); ++ private static final CollectorData CPU_PROCESS = new CollectorData("builtin:stat:cpu_process", "Process CPU", "The amount of CPU being used by this process.", DataFormatter.PERCENT, GraphCategory.SYSTEM); ++ private static final CollectorData MEMORY = new CollectorData("builtin:stat:memory_used", "Memory", "The amount of memory being used currently.", DataFormatter.BYTES, GraphCategory.SYSTEM); ++ private static final CollectorData MEMORY_TOTAL = new CollectorData("builtin:stat:memory_total", "Memory Total", "The total amount of memory allocated.", DataFormatter.BYTES, GraphCategory.SYSTEM); ++ ++ private final OperatingSystemMXBean bean; ++ private final CentralProcessor processor; ++ ++ public StatCollector() { ++ super(CPU, CPU_PROCESS, MEMORY, MEMORY_TOTAL); ++ this.interval = Duration.ofSeconds(5); ++ ++ this.bean = (OperatingSystemMXBean) ManagementFactory.getOperatingSystemMXBean(); ++ this.processor = new SystemInfo().getHardware().getProcessor(); ++ } ++ ++ @Override ++ public void run() { ++ Runtime runtime = Runtime.getRuntime(); ++ ++ this.report(CPU, this.processor.getSystemLoadAverage(1)[0] / 100); // percentage ++ this.report(CPU_PROCESS, this.bean.getProcessCpuLoad()); ++ this.report(MEMORY, runtime.totalMemory() - runtime.freeMemory()); ++ this.report(MEMORY_TOTAL, runtime.totalMemory()); ++ } ++} +diff --git a/src/main/java/gg/pufferfish/pufferfish/flare/collectors/TPSCollector.java b/src/main/java/gg/pufferfish/pufferfish/flare/collectors/TPSCollector.java +new file mode 100644 +index 0000000000000000000000000000000000000000..40447d00aefb5ffedb8a2ee87155a04088f0649f +--- /dev/null ++++ b/src/main/java/gg/pufferfish/pufferfish/flare/collectors/TPSCollector.java +@@ -0,0 +1,31 @@ ++package gg.pufferfish.pufferfish.flare.collectors; ++ ++import co.technove.flare.live.CollectorData; ++import co.technove.flare.live.LiveCollector; ++import co.technove.flare.live.formatter.SuffixFormatter; ++import gg.pufferfish.pufferfish.flare.CustomCategories; ++import net.minecraft.server.MinecraftServer; ++import org.bukkit.Bukkit; ++ ++import java.time.Duration; ++import java.util.Arrays; ++ ++public class TPSCollector extends LiveCollector { ++ private static final CollectorData TPS = new CollectorData("airplane:tps", "TPS", "Ticks per second, or how fast the server updates. For a smooth server this should be a constant 20TPS.", SuffixFormatter.of("TPS"), CustomCategories.MC_PERF); ++ private static final CollectorData MSPT = new CollectorData("airplane:mspt", "MSPT", "Milliseconds per tick, which can show how well your server is performing. This value should always be under 50mspt.", SuffixFormatter.of("mspt"), CustomCategories.MC_PERF); ++ ++ public TPSCollector() { ++ super(TPS, MSPT); ++ ++ this.interval = Duration.ofSeconds(5); ++ } ++ ++ @Override ++ public void run() { ++ long[] times = MinecraftServer.getServer().tickTimes5s.getTimes(); ++ double mspt = ((double) Arrays.stream(times).sum() / (double) times.length) * 1.0E-6D; ++ ++ this.report(TPS, Math.min(20D, Math.round(Bukkit.getServer().getTPS()[0] * 100d) / 100d)); ++ this.report(MSPT, (double) Math.round(mspt * 100d) / 100d); ++ } ++} +diff --git a/src/main/java/gg/pufferfish/pufferfish/flare/collectors/WorldCountCollector.java b/src/main/java/gg/pufferfish/pufferfish/flare/collectors/WorldCountCollector.java +new file mode 100644 +index 0000000000000000000000000000000000000000..db15d3fbe2b65fc8035573f5fdbea382055db9b2 +--- /dev/null ++++ b/src/main/java/gg/pufferfish/pufferfish/flare/collectors/WorldCountCollector.java +@@ -0,0 +1,45 @@ ++package gg.pufferfish.pufferfish.flare.collectors; ++ ++import co.technove.flare.live.CollectorData; ++import co.technove.flare.live.LiveCollector; ++import co.technove.flare.live.formatter.SuffixFormatter; ++import gg.pufferfish.pufferfish.flare.CustomCategories; ++import org.bukkit.Bukkit; ++import org.bukkit.World; ++import org.bukkit.craftbukkit.CraftWorld; ++ ++import java.time.Duration; ++ ++public class WorldCountCollector extends LiveCollector { ++ ++ private static final CollectorData PLAYER_COUNT = new CollectorData("airplane:world:playercount", "Player Count", "The number of players currently on the server.", new SuffixFormatter(" Player", " Players"), CustomCategories.ENTITIES_AND_CHUNKS); ++ private static final CollectorData ENTITY_COUNT = new CollectorData("airplane:world:entitycount", "Entity Count", "The number of entities in all worlds", new SuffixFormatter(" Entity", " Entities"), CustomCategories.ENTITIES_AND_CHUNKS); ++ private static final CollectorData CHUNK_COUNT = new CollectorData("airplane:world:chunkcount", "Chunk Count", "The number of chunks currently loaded.", new SuffixFormatter(" Chunk", " Chunks"), CustomCategories.ENTITIES_AND_CHUNKS); ++ private static final CollectorData TILE_ENTITY_COUNT = new CollectorData("airplane:world:blockentitycount", "Block Entity Count", "The number of block entities currently loaded.", new SuffixFormatter(" Block Entity", " Block Entities"), CustomCategories.ENTITIES_AND_CHUNKS); ++ ++ public WorldCountCollector() { ++ super(PLAYER_COUNT, ENTITY_COUNT, CHUNK_COUNT, TILE_ENTITY_COUNT); ++ ++ this.interval = Duration.ofSeconds(5); ++ } ++ ++ @Override ++ public void run() { ++ int entities = 0; ++ int chunkCount = 0; ++ int tileEntityCount = 0; ++ ++ if (!Bukkit.isStopping()) { ++ for (World world : Bukkit.getWorlds()) { ++ entities += ((CraftWorld) world).getHandle().entityManager.getEntityGetter().getCount(); ++ chunkCount += world.getChunkCount(); ++ tileEntityCount += world.getTileEntityCount(); ++ } ++ } ++ ++ this.report(PLAYER_COUNT, Bukkit.getOnlinePlayers().size()); ++ this.report(ENTITY_COUNT, entities); ++ this.report(CHUNK_COUNT, chunkCount); ++ this.report(TILE_ENTITY_COUNT, tileEntityCount); ++ } ++} +diff --git a/src/main/java/gg/pufferfish/pufferfish/sentry/PufferfishSentryAppender.java b/src/main/java/gg/pufferfish/pufferfish/sentry/PufferfishSentryAppender.java +new file mode 100644 +index 0000000000000000000000000000000000000000..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..9d6dc2c80945bec9bea74714c657c7a2e0bdde9e +--- /dev/null ++++ b/src/main/java/gg/pufferfish/pufferfish/util/AsyncExecutor.java +@@ -0,0 +1,51 @@ ++package gg.pufferfish.pufferfish.util; ++ ++import com.google.common.collect.Queues; ++import gg.pufferfish.pufferfish.PufferfishLogger; ++import java.util.Queue; ++import java.util.concurrent.locks.LockSupport; ++import java.util.function.BooleanSupplier; ++import java.util.logging.Level; ++ ++public class AsyncExecutor implements Runnable { ++ ++ private Queue jobs = Queues.newConcurrentLinkedQueue(); ++ private final Thread thread; ++ private final BooleanSupplier shouldRun; ++ private volatile boolean killswitch = false; ++ ++ public AsyncExecutor(String threadName, BooleanSupplier shouldRun) { ++ this.thread = new Thread(this, threadName); ++ this.shouldRun = shouldRun; ++ } ++ ++ public void start() { ++ thread.start(); ++ } ++ ++ public void kill() { ++ killswitch = true; ++ } ++ ++ public void submit(Runnable runnable) { ++ jobs.offer(runnable); ++ } ++ ++ @Override ++ public void run() { ++ while (!killswitch) { ++ if (shouldRun.getAsBoolean()) { ++ try { ++ Runnable runnable; ++ while ((runnable = jobs.poll()) != null) { ++ runnable.run(); ++ } ++ } catch (Exception e) { ++ PufferfishLogger.LOGGER.log(Level.SEVERE, e, () -> "Failed to execute async job for thread " + thread.getName()); ++ } ++ } ++ LockSupport.parkNanos("executing tasks", 1000L); ++ } ++ } ++ ++} +diff --git a/src/main/java/gg/pufferfish/pufferfish/util/AsyncPlayerAreaMap.java b/src/main/java/gg/pufferfish/pufferfish/util/AsyncPlayerAreaMap.java +new file mode 100644 +index 0000000000000000000000000000000000000000..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/net/minecraft/Util.java b/src/main/java/net/minecraft/Util.java +index 9c111d479bbcc101886c12950c97f10941125ae7..90018506da9e900d448a1fc43b3213ac513686e6 100644 +--- a/src/main/java/net/minecraft/Util.java ++++ b/src/main/java/net/minecraft/Util.java +@@ -391,6 +391,10 @@ public class Util { + } + + public static CompletableFuture> sequence(List> futures) { ++ // Pufferfish start - faster sequencing without all of.. _that_ ++ return CompletableFuture.allOf(futures.toArray(new CompletableFuture[futures.size()])) ++ .thenApply(unused -> futures.stream().map(CompletableFuture::join).collect(Collectors.toList())); ++ /* + return futures.stream().reduce(CompletableFuture.completedFuture(Lists.newArrayList()), (completableFuture, completableFuture2) -> { + return completableFuture2.thenCombine(completableFuture, (object, list) -> { + List list2 = Lists.newArrayListWithCapacity(list.size() + 1); +@@ -406,6 +410,8 @@ public class Util { + return list3; + }); + }); ++ */ ++ // Pufferfish end + } + + public static CompletableFuture> sequenceFailFast(List> futures) { +diff --git a/src/main/java/net/minecraft/server/MCUtil.java b/src/main/java/net/minecraft/server/MCUtil.java +index b61abf227a04b4565c2525e5f469db30c3a545a5..f99d189f461921d37581e2fc1382af60921b0660 100644 +--- a/src/main/java/net/minecraft/server/MCUtil.java ++++ b/src/main/java/net/minecraft/server/MCUtil.java +@@ -209,7 +209,7 @@ public final class MCUtil { + } + + public static long getCoordinateKey(final Entity entity) { +- return ((long)(MCUtil.fastFloor(entity.getZ()) >> 4) << 32) | ((MCUtil.fastFloor(entity.getX()) >> 4) & 0xFFFFFFFFL); ++ return ((long)(entity.blockPosition.getZ() >> 4) << 32) | ((entity.blockPosition.getX() >> 4) & 0xFFFFFFFFL); // Pufferfish - eliminate double->long cast in hotpath + } + + public static long getCoordinateKey(final ChunkPos pair) { +diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java +index 481a5dbad82f3f8dd5b1bf8ab207d82ec73d5bbd..1e7ae166a1d53fce28297e2eaebd11ec28a57a55 100644 +--- a/src/main/java/net/minecraft/server/MinecraftServer.java ++++ b/src/main/java/net/minecraft/server/MinecraftServer.java +@@ -310,6 +310,8 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop true); // Pufferfish - optimize mob spawning + + public static S spin(Function serverFactory) { + AtomicReference atomicreference = new AtomicReference(); +@@ -1709,7 +1711,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop // Spigot - Spigot > // CraftBukkit - cb > vanilla! ++ return "Pufferfish"; // Pufferfish - Pufferfish > // Paper - Paper > // Spigot - Spigot > // CraftBukkit - cb > vanilla! + } + + public SystemReport fillSystemReport(SystemReport details) { +@@ -2243,6 +2245,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop passengers = parent.getPassengers(); ++ ++ for (int i = 0, size = passengers.size(); i < size; i++) { ++ Entity entity = passengers.get(i); ++ int range = entity.getType().clientTrackingRange() * 16; ++ range = org.spigotmc.TrackingRange.getEntityTrackingRange(entity, range); // Paper ++ ++ if (range > highest) { // Paper - we need the lowest range thanks to the fact that our tracker doesn't account for passenger logic // Tuinity - not anymore! ++ highest = range; ++ } ++ ++ highest = getHighestRange(entity, highest); ++ } ++ ++ return highest; ++ } ++ + private int getEffectiveRange() { + int i = this.range; ++ // Pufferfish start - remove iterators and streams ++ /* + Iterator iterator = this.entity.getIndirectPassengers().iterator(); + + while (iterator.hasNext()) { +@@ -2369,6 +2389,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 9a07ccbd12675e501a9aebf89ab85adf6fb658ba..f51fcaba69c9ddc27130ab615526e05b1b4f0e07 100644 +--- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java ++++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java +@@ -77,6 +77,9 @@ public class ServerChunkCache extends ChunkSource { + final Long2ObjectOpenHashMap loadedChunkMap = new Long2ObjectOpenHashMap<>(8192, 0.5f); + + private final LevelChunk[] lastLoadedChunks = new LevelChunk[4 * 4]; ++ ++ public boolean firstRunSpawnCounts = true; // Pufferfish ++ public final java.util.concurrent.atomic.AtomicBoolean _pufferfish_spawnCountsReady = new java.util.concurrent.atomic.AtomicBoolean(false); // Pufferfish - optimize countmobs + + private static int getChunkCacheKey(int x, int z) { + return x & 3 | ((z & 3) << 2); +@@ -975,6 +978,7 @@ public class ServerChunkCache extends ChunkSource { + ProfilerFiller gameprofilerfiller = this.level.getProfiler(); + + gameprofilerfiller.push("pollingChunks"); ++ this.level.resetIceAndSnowTick(); // Pufferfish - reset ice & snow tick random + int k = this.level.getGameRules().getInt(GameRules.RULE_RANDOMTICKING); + boolean flag1 = level.ticksPerAnimalSpawns != 0L && worlddata.getGameTime() % level.ticksPerAnimalSpawns == 0L; // CraftBukkit + +@@ -984,18 +988,25 @@ public class ServerChunkCache extends ChunkSource { + // Paper start - per player mob spawning + NaturalSpawner.SpawnState spawnercreature_d; // moved down + if ((this.spawnFriendlies || this.spawnEnemies) && this.chunkMap.playerMobDistanceMap != null) { // don't count mobs when animals and monsters are disabled +- // re-set mob counts +- for (ServerPlayer player : this.level.players) { +- Arrays.fill(player.mobCounts, 0); ++ // Pufferfish start - moved down when async processing ++ if (!gg.pufferfish.pufferfish.PufferfishConfig.enableAsyncMobSpawning) { ++ // re-set mob counts ++ for (ServerPlayer player : this.level.players) { ++ Arrays.fill(player.mobCounts, 0); ++ } ++ lastSpawnState = NaturalSpawner.createState(l, this.level.getAllEntities(), this::getFullChunk, null, true); + } +- spawnercreature_d = NaturalSpawner.createState(l, this.level.getAllEntities(), this::getFullChunk, null, true); ++ // Pufferfish end + } else { +- spawnercreature_d = NaturalSpawner.createState(l, this.level.getAllEntities(), this::getFullChunk, this.chunkMap.playerMobDistanceMap == null ? new LocalMobCapCalculator(this.chunkMap) : null, false); ++ // Pufferfish start - this is only implemented for per-player mob spawning so this makes everything work if this setting is disabled. ++ lastSpawnState = NaturalSpawner.createState(l, this.level.getAllEntities(), this::getFullChunk, this.chunkMap.playerMobDistanceMap == null ? new LocalMobCapCalculator(this.chunkMap) : null, false); ++ _pufferfish_spawnCountsReady.set(true); ++ // Pufferfish end + } + // Paper end + this.level.timings.countNaturalMobs.stopTiming(); // Paper - timings + +- this.lastSpawnState = spawnercreature_d; ++ //this.lastSpawnState = spawnercreature_d; // Pufferfish - this is managed asynchronously + gameprofilerfiller.popPush("filteringLoadedChunks"); + // Paper - moved down + this.level.timings.chunkTicks.startTiming(); // Paper +@@ -1037,8 +1048,8 @@ public class ServerChunkCache extends ChunkSource { + + if ((true || this.level.isPositionEntityTicking(chunkcoordintpair)) && this.chunkMap.anyPlayerCloseEnoughForSpawning(holder, chunkcoordintpair, false)) { // Paper - optimise anyPlayerCloseEnoughForSpawning & optimise chunk tick iteration + chunk1.incrementInhabitedTime(j); +- if (flag2 && (this.spawnEnemies || this.spawnFriendlies) && this.level.getWorldBorder().isWithinBounds(chunkcoordintpair) && this.chunkMap.anyPlayerCloseEnoughForSpawning(holder, chunkcoordintpair, true)) { // Spigot // Paper - optimise anyPlayerCloseEnoughForSpawning & optimise chunk tick iteration +- NaturalSpawner.spawnForChunk(this.level, chunk1, spawnercreature_d, this.spawnFriendlies, this.spawnEnemies, flag1); ++ if (flag2 && (!gg.pufferfish.pufferfish.PufferfishConfig.enableAsyncMobSpawning || _pufferfish_spawnCountsReady.get()) && (this.spawnEnemies || this.spawnFriendlies) && this.level.getWorldBorder().isWithinBounds(chunkcoordintpair) && this.chunkMap.anyPlayerCloseEnoughForSpawning(holder, chunkcoordintpair, true)) { // Spigot // Paper - optimise anyPlayerCloseEnoughForSpawning & optimise chunk tick iteration ++ NaturalSpawner.spawnForChunk(this.level, chunk1, lastSpawnState, this.spawnFriendlies, this.spawnEnemies, flag1); // Pufferfish + } + + if (this.level.shouldTickBlocksAt(chunkcoordintpair.toLong())) { +@@ -1085,6 +1096,30 @@ public class ServerChunkCache extends ChunkSource { + } + // Paper end - controlled flush for entity tracker packets + } ++ ++ // Pufferfish start - optimize mob spawning ++ if (gg.pufferfish.pufferfish.PufferfishConfig.enableAsyncMobSpawning) { ++ for (ServerPlayer player : this.level.players) { ++ Arrays.fill(player.mobCounts, 0); ++ } ++ if (firstRunSpawnCounts) { ++ firstRunSpawnCounts = false; ++ _pufferfish_spawnCountsReady.set(true); ++ } ++ if (chunkMap.playerMobDistanceMap != null && _pufferfish_spawnCountsReady.getAndSet(false)) { ++ net.minecraft.server.MinecraftServer.getServer().mobSpawnExecutor.submit(() -> { ++ int mapped = distanceManager.getNaturalSpawnChunkCount(); ++ io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet.Iterator objectiterator = ++ level.entityTickList.entities.iterator(io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet.ITERATOR_FLAG_SEE_ADDITIONS); ++ gg.pufferfish.pufferfish.util.IterableWrapper wrappedIterator = ++ new gg.pufferfish.pufferfish.util.IterableWrapper<>(objectiterator); ++ lastSpawnState = NaturalSpawner.createState(mapped, wrappedIterator, this::getFullChunk, null, true); ++ objectiterator.finishedIterating(); ++ _pufferfish_spawnCountsReady.set(true); ++ }); ++ } ++ } ++ // Pufferfish end + } + + private void getFullChunk(long pos, Consumer chunkConsumer) { +diff --git a/src/main/java/net/minecraft/server/level/ServerEntity.java b/src/main/java/net/minecraft/server/level/ServerEntity.java +index 9a6c67b614944f841813ec2892381c3342bc365c..e80176708db486190dd527e3ade5fc690ceb39f7 100644 +--- a/src/main/java/net/minecraft/server/level/ServerEntity.java ++++ b/src/main/java/net/minecraft/server/level/ServerEntity.java +@@ -173,6 +173,7 @@ public class ServerEntity { + boolean flag4 = k < -32768L || k > 32767L || l < -32768L || l > 32767L || i1 < -32768L || i1 > 32767L; + + if (!flag4 && this.teleportDelay <= 400 && !this.wasRiding && this.wasOnGround == this.entity.isOnGround() && !(com.destroystokyo.paper.PaperConfig.sendFullPosForHardCollidingEntities && this.entity.hardCollides())) { // Paper - send full pos for hard colliding entities to prevent collision problems due to desync ++ if (flag2 || flag3 || this.entity instanceof AbstractArrow) { // Pufferfish + if ((!flag2 || !flag3) && !(this.entity instanceof AbstractArrow)) { + if (flag2) { + packet1 = new ClientboundMoveEntityPacket.Pos(this.entity.getId(), (short) ((int) k), (short) ((int) l), (short) ((int) i1), this.entity.isOnGround()); +@@ -182,6 +183,7 @@ public class ServerEntity { + } else { + packet1 = new ClientboundMoveEntityPacket.PosRot(this.entity.getId(), (short) ((int) k), (short) ((int) l), (short) ((int) i1), (byte) i, (byte) j, this.entity.isOnGround()); + } ++ } // Pufferfish + } else { + this.wasOnGround = this.entity.isOnGround(); + this.teleportDelay = 0; +diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java +index 28f605c3daa969c1a54745e552d55ecb874120a9..c7229e120d601619d2ea869d7aa506fb3b78cde4 100644 +--- a/src/main/java/net/minecraft/server/level/ServerLevel.java ++++ b/src/main/java/net/minecraft/server/level/ServerLevel.java +@@ -667,7 +667,20 @@ public class ServerLevel extends Level implements WorldGenLevel { + } + + gameprofilerfiller.push("tick"); +- this.guardEntityTick(this::tickNonPassenger, entity); ++ // Pufferfish start - copied from this.guardEntityTick ++ try { ++ this.tickNonPassenger(entity); // Pufferfish - changed ++ MinecraftServer.getServer().executeMidTickTasks(); // Tuinity - execute chunk tasks mid tick ++ } catch (Throwable throwable) { ++ if (throwable instanceof ThreadDeath) throw throwable; // Paper ++ // Paper start - Prevent tile entity and entity crashes ++ final String msg = String.format("Entity threw exception at %s:%s,%s,%s", entity.level.getWorld().getName(), entity.getX(), entity.getY(), entity.getZ()); ++ MinecraftServer.LOGGER.error(msg, throwable); ++ getCraftServer().getPluginManager().callEvent(new com.destroystokyo.paper.event.server.ServerExceptionEvent(new com.destroystokyo.paper.exception.ServerInternalException(msg, throwable))); ++ entity.discard(); ++ // Paper end ++ } ++ // Pufferfish end + gameprofilerfiller.pop(); + } + } +@@ -729,9 +742,11 @@ public class ServerLevel extends Level implements WorldGenLevel { + } + // Paper start - optimise random block ticking + private final BlockPos.MutableBlockPos chunkTickMutablePosition = new BlockPos.MutableBlockPos(); +- private final io.papermc.paper.util.math.ThreadUnsafeRandom randomTickRandom = new io.papermc.paper.util.math.ThreadUnsafeRandom(); ++ private final io.papermc.paper.util.math.ThreadUnsafeRandom randomTickRandom = new io.papermc.paper.util.math.ThreadUnsafeRandom(); public java.util.Random getThreadUnsafeRandom() { return this.randomTickRandom; } // Pufferfish - getter + // Paper end + ++ private int currentIceAndSnowTick = 0; protected void resetIceAndSnowTick() { this.currentIceAndSnowTick = this.randomTickRandom.nextInt(16); } // Pufferfish ++ + public void tickChunk(LevelChunk chunk, int randomTickSpeed) { + ChunkPos chunkcoordintpair = chunk.getPos(); + boolean flag = this.isRaining(); +@@ -742,7 +757,7 @@ public class ServerLevel extends Level implements WorldGenLevel { + gameprofilerfiller.push("thunder"); + final BlockPos.MutableBlockPos blockposition = this.chunkTickMutablePosition; // Paper - use mutable to reduce allocation rate, final to force compile fail on change + +- if (!this.paperConfig.disableThunder && flag && this.isThundering() && this.spigotConfig.thunderChance > 0 && this.random.nextInt(this.spigotConfig.thunderChance) == 0) { // Spigot // Paper - disable thunder ++ if (!this.paperConfig.disableThunder && flag && this.isThundering() && this.spigotConfig.thunderChance > 0 && this.random.nextInt(this.spigotConfig.thunderChance) == 0 && chunk.shouldDoLightning(this.random)) { // Spigot // Paper - disable thunder // Pufferfish - replace random with shouldDoLightning + blockposition.set(this.findLightningTargetAround(this.getBlockRandomPos(j, 0, k, 15))); // Paper + if (this.isRainingAt(blockposition)) { + DifficultyInstance difficultydamagescaler = this.getCurrentDifficultyAt(blockposition); +@@ -766,7 +781,7 @@ public class ServerLevel extends Level implements WorldGenLevel { + } + + gameprofilerfiller.popPush("iceandsnow"); +- if (!this.paperConfig.disableIceAndSnow && this.randomTickRandom.nextInt(16) == 0) { // Paper - Disable ice and snow // Paper - optimise random ticking ++ if (!this.paperConfig.disableIceAndSnow && (this.currentIceAndSnowTick++ & 15) == 0) { // Paper - Disable ice and snow // Paper - optimise random ticking // Pufferfish - optimize further random ticking + // Paper start - optimise chunk ticking + this.getRandomBlockPosition(j, 0, k, 15, blockposition); + int normalY = chunk.getHeight(Heightmap.Types.MOTION_BLOCKING, blockposition.getX() & 15, blockposition.getZ() & 15) + 1; +diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java +index 926d0a80cbb55184955ac6720948d2e86683cc57..889a4804eaa128e2f79a4ea48b8aa796336576d1 100644 +--- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java ++++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java +@@ -1110,6 +1110,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Ser + + @Override + public void handleEditBook(ServerboundEditBookPacket packet) { ++ if (!gg.pufferfish.pufferfish.PufferfishConfig.enableBooks && !this.player.getBukkitEntity().hasPermission("pufferfish.usebooks")) return; // Pufferfish + // Paper start + if (!this.cserver.isPrimaryThread()) { + List pageList = packet.getPages(); +diff --git a/src/main/java/net/minecraft/world/CompoundContainer.java b/src/main/java/net/minecraft/world/CompoundContainer.java +index 241fec02e6869c638d3a160819b32173a081467b..6a8f9e8f5bf108674c47018def28906e2d0a729c 100644 +--- a/src/main/java/net/minecraft/world/CompoundContainer.java ++++ b/src/main/java/net/minecraft/world/CompoundContainer.java +@@ -1,5 +1,6 @@ + package net.minecraft.world; + ++import net.minecraft.core.Direction; // Pufferfish + import net.minecraft.world.entity.player.Player; + import net.minecraft.world.item.ItemStack; + +@@ -64,6 +65,23 @@ public class CompoundContainer implements Container { + this.container2 = second; + } + ++ // Pufferfish start ++ @Override ++ public boolean hasEmptySlot(Direction enumdirection) { ++ return this.container1.hasEmptySlot(null) || this.container2.hasEmptySlot(null); ++ } ++ ++ @Override ++ public boolean isCompletelyFull(Direction enumdirection) { ++ return this.container1.isCompletelyFull(null) && this.container2.isCompletelyFull(null); ++ } ++ ++ @Override ++ public boolean isCompletelyEmpty(Direction enumdirection) { ++ return this.container1.isCompletelyEmpty(null) && this.container2.isCompletelyEmpty(null); ++ } ++ // Pufferfish end ++ + @Override + public int getContainerSize() { + return this.container1.getContainerSize() + this.container2.getContainerSize(); +diff --git a/src/main/java/net/minecraft/world/Container.java b/src/main/java/net/minecraft/world/Container.java +index 7437f01ca8f416e2c9150250e324af4725a4efb6..7ac51dbfce18a2bc52faa7a915abeccc6068909d 100644 +--- a/src/main/java/net/minecraft/world/Container.java ++++ b/src/main/java/net/minecraft/world/Container.java +@@ -1,6 +1,8 @@ + package net.minecraft.world; + + import java.util.Set; ++ ++import net.minecraft.core.Direction; // Pufferfish + import net.minecraft.world.entity.player.Player; + import net.minecraft.world.item.Item; + import net.minecraft.world.item.ItemStack; +@@ -9,6 +11,63 @@ import org.bukkit.craftbukkit.entity.CraftHumanEntity; + // CraftBukkit end + + public interface Container extends Clearable { ++ // Pufferfish start - allow the inventory to override and optimize these frequent calls ++ default boolean hasEmptySlot(@org.jetbrains.annotations.Nullable Direction enumdirection) { // there is a slot with 0 items in it ++ if (this instanceof WorldlyContainer worldlyContainer) { ++ for (int i : worldlyContainer.getSlotsForFace(enumdirection)) { ++ if (this.getItem(i).isEmpty()) { ++ return true; ++ } ++ } ++ } else { ++ int size = this.getContainerSize(); ++ for (int i = 0; i < size; i++) { ++ if (this.getItem(i).isEmpty()) { ++ return true; ++ } ++ } ++ } ++ return false; ++ } ++ ++ default boolean isCompletelyFull(@org.jetbrains.annotations.Nullable Direction enumdirection) { // every stack is maxed ++ if (this instanceof WorldlyContainer worldlyContainer) { ++ for (int i : worldlyContainer.getSlotsForFace(enumdirection)) { ++ ItemStack itemStack = this.getItem(i); ++ if (itemStack.getCount() < itemStack.getMaxStackSize()) { ++ return false; ++ } ++ } ++ } else { ++ int size = this.getContainerSize(); ++ for (int i = 0; i < size; i++) { ++ ItemStack itemStack = this.getItem(i); ++ if (itemStack.getCount() < itemStack.getMaxStackSize()) { ++ return false; ++ } ++ } ++ } ++ return true; ++ } ++ ++ default boolean isCompletelyEmpty(@org.jetbrains.annotations.Nullable Direction enumdirection) { ++ if (this instanceof WorldlyContainer worldlyContainer) { ++ for (int i : worldlyContainer.getSlotsForFace(enumdirection)) { ++ if (!this.getItem(i).isEmpty()) { ++ return false; ++ } ++ } ++ } else { ++ int size = this.getContainerSize(); ++ for (int i = 0; i < size; i++) { ++ if (!this.getItem(i).isEmpty()) { ++ return false; ++ } ++ } ++ } ++ return true; ++ } ++ // Pufferfish end + + int LARGE_MAX_STACK_SIZE = 64; + +diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java +index 8ea81f6ac7503c68f0aea34802843bc545f46db0..335ac54bef892db6b9fe66d0a7c3966abda84350 100644 +--- a/src/main/java/net/minecraft/world/entity/Entity.java ++++ b/src/main/java/net/minecraft/world/entity/Entity.java +@@ -222,7 +222,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, i + public double yo; + public double zo; + private Vec3 position; +- private BlockPos blockPosition; ++ public BlockPos blockPosition; // Pufferfish - private->public + private ChunkPos chunkPosition; + private Vec3 deltaMovement; + public float yRot; // Paper - private->public +@@ -342,6 +342,11 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, i + return this.originWorld; + } + // Paper end ++ // Pufferfish start ++ public int activatedPriority = gg.pufferfish.pufferfish.PufferfishConfig.maximumActivationPrio; // golf score ++ public final BlockPos.MutableBlockPos cachedBlockPos = new BlockPos.MutableBlockPos(); // used where needed ++ // Pufferfish end ++ + public float getBukkitYaw() { + return this.yRot; + } +@@ -365,17 +370,36 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, i + this.isLegacyTrackingEntity = isLegacyTrackingEntity; + } + ++ private org.spigotmc.TrackingRange.TrackingRangeType getFurthestEntity(Entity entity, net.minecraft.server.level.ChunkMap chunkMap, org.spigotmc.TrackingRange.TrackingRangeType type, int range) { ++ List passengers = entity.getPassengers(); ++ for (int i = 0, size = passengers.size(); i < size; i++) { ++ Entity passenger = passengers.get(i); ++ org.spigotmc.TrackingRange.TrackingRangeType passengerType = passenger.trackingRangeType; ++ int passengerRange = chunkMap.getEntityTrackerRange(passengerType.ordinal()); ++ if (passengerRange > range) { ++ type = passengerType; ++ range = passengerRange; ++ } ++ ++ type = this.getFurthestEntity(passenger, chunkMap, type, range); ++ } ++ ++ return type; ++ } ++ + public final com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet getPlayersInTrackRange() { + // determine highest range of passengers + if (this.passengers.isEmpty()) { + return ((ServerLevel)this.level).getChunkSource().chunkMap.playerEntityTrackerTrackMaps[this.trackingRangeType.ordinal()] + .getObjectsInRange(MCUtil.getCoordinateKey(this)); + } +- Iterable passengers = this.getIndirectPassengers(); ++ //Iterable passengers = this.getIndirectPassengers(); // Pufferfish + net.minecraft.server.level.ChunkMap chunkMap = ((ServerLevel)this.level).getChunkSource().chunkMap; + org.spigotmc.TrackingRange.TrackingRangeType type = this.trackingRangeType; + int range = chunkMap.getEntityTrackerRange(type.ordinal()); + ++ // Pufferfish start - use getFurthestEntity to skip getIndirectPassengers ++ /* + for (Entity passenger : passengers) { + org.spigotmc.TrackingRange.TrackingRangeType passengerType = passenger.trackingRangeType; + int passengerRange = chunkMap.getEntityTrackerRange(passengerType.ordinal()); +@@ -384,6 +408,9 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, i + range = passengerRange; + } + } ++ */ ++ type = this.getFurthestEntity(this, chunkMap, type, range); ++ // Pufferfish end + + return chunkMap.playerEntityTrackerTrackMaps[type.ordinal()].getObjectsInRange(MCUtil.getCoordinateKey(this)); + } +@@ -3845,16 +3872,18 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, i + } + + public boolean updateFluidHeightAndDoFluidPushing(Tag 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; +@@ -3862,14 +3891,61 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, i + 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; +@@ -3891,9 +3967,12 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, i + // 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 419a7e9614af2328ed401fc954196056243a984c..9bc568a2b0df13f55ee8670839b9e6a6b4f38c2d 100644 +--- a/src/main/java/net/minecraft/world/entity/EntityType.java ++++ b/src/main/java/net/minecraft/world/entity/EntityType.java +@@ -293,6 +293,7 @@ public class EntityType implements EntityTypeTest { + return Registry.ENTITY_TYPE.getOptional(ResourceLocation.tryParse(id)); + } + ++ public boolean dabEnabled = false; // Pufferfish + // Paper start - add id + public final String id; + +diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java +index 43378561cf48f969f5bf1fd0db349415f4d1c866..2c988e05299d530d483e21ab452de0a03e5bdcbd 100644 +--- a/src/main/java/net/minecraft/world/entity/LivingEntity.java ++++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java +@@ -139,7 +139,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 { + +@@ -394,8 +393,7 @@ public abstract class LivingEntity extends Entity { + + if (this.isAlive()) { + boolean flag = this instanceof net.minecraft.world.entity.player.Player; +- +- if (this.isInWall()) { ++ if (tickCount % 20 == 0 && couldPossiblyBeHurt(1.0F) && this.isInWall()) { // Pufferfish - optimize suffocation + this.hurt(DamageSource.IN_WALL, 1.0F); + } else if (flag && !this.level.getWorldBorder().isWithinBounds(this.getBoundingBox())) { + double d0 = this.level.getWorldBorder().getDistanceToBorder(this) + this.level.getWorldBorder().getDamageSafeZone(); +@@ -1288,6 +1286,15 @@ public abstract class LivingEntity extends Entity { + return this.getHealth() <= 0.0F; + } + ++ // Pufferfish start - optimize suffocation ++ public boolean couldPossiblyBeHurt(float amount) { ++ if ((float) this.invulnerableTime > (float) this.invulnerableDuration / 2.0F && amount <= this.lastHurt) { ++ return false; ++ } ++ return true; ++ } ++ // Pufferfish end ++ + @Override + public boolean hurt(DamageSource source, float amount) { + if (this.isInvulnerableTo(source)) { +@@ -1841,6 +1848,20 @@ public abstract class LivingEntity extends Entity { + return this.lastClimbablePos; + } + ++ ++ // Pufferfish start ++ private boolean cachedOnClimable = false; ++ private BlockPos lastClimbingPosition = null; ++ ++ public boolean onClimableCached() { ++ if (!this.blockPosition().equals(this.lastClimbingPosition)) { ++ this.cachedOnClimable = this.onClimbable(); ++ this.lastClimbingPosition = this.blockPosition(); ++ } ++ return this.cachedOnClimable; ++ } ++ // Pufferfish end ++ + public boolean onClimbable() { + if (this.isSpectator()) { + return false; +@@ -3538,7 +3559,10 @@ public abstract class LivingEntity extends Entity { + Vec3 vec3d1 = new Vec3(entity.getX(), entity.getEyeY(), entity.getZ()); + + // Paper - diff on change - used in CraftLivingEntity#hasLineOfSight(Location) and CraftWorld#lineOfSightExists +- return vec3d1.distanceToSqr(vec3d) > 128D * 128D ? false : this.level.clip(new ClipContext(vec3d, vec3d1, ClipContext.Block.COLLIDER, ClipContext.Fluid.NONE, this)).getType() == HitResult.Type.MISS; // Paper - use distanceToSqr ++ // Pufferfish start ++ //return vec3d1.distanceToSqr(vec3d) > 128D * 128D ? false : this.level.clip(new ClipContext(vec3d, vec3d1, ClipContext.Block.COLLIDER, ClipContext.Fluid.NONE, this)).getType() == HitResult.Type.MISS; // Paper - use distanceToSqr ++ return vec3d1.distanceToSqr(vec3d) > 128D * 128D ? false : this.level.rayTraceDirect(vec3d, vec3d1, net.minecraft.world.phys.shapes.CollisionContext.of(this)) == net.minecraft.world.phys.BlockHitResult.Type.MISS; ++ // Pufferfish end + } + } + +diff --git a/src/main/java/net/minecraft/world/entity/Mob.java b/src/main/java/net/minecraft/world/entity/Mob.java +index 031660f7d0ea270f87e5174a4fe65ccad1f7a561..607e78c24edabb26fd7494855087f7f19cfdc576 100644 +--- a/src/main/java/net/minecraft/world/entity/Mob.java ++++ b/src/main/java/net/minecraft/world/entity/Mob.java +@@ -210,10 +210,10 @@ public abstract class Mob extends LivingEntity { + @Override + public void inactiveTick() { + super.inactiveTick(); +- if (this.goalSelector.inactiveTick()) { ++ if (this.goalSelector.inactiveTick(this.activatedPriority, true) && !gg.pufferfish.pufferfish.PufferfishConfig.disableInactiveGoalSelectorTick) { // Pufferfish - pass activated priroity // Pufferfish - disable 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(); + } + } +@@ -844,16 +844,20 @@ public abstract class Mob extends LivingEntity { + + if (i % 2 != 0 && this.tickCount > 1) { + this.level.getProfiler().push("targetSelector"); ++ if (this.targetSelector.inactiveTick(this.activatedPriority, false)) // Pufferfish - use this to alternate ticking + this.targetSelector.tickRunningGoals(false); + this.level.getProfiler().pop(); + this.level.getProfiler().push("goalSelector"); ++ if (this.goalSelector.inactiveTick(this.activatedPriority, false)) // Pufferfish - use this to alternate ticking + this.goalSelector.tickRunningGoals(false); + this.level.getProfiler().pop(); + } else { + this.level.getProfiler().push("targetSelector"); ++ if (this.targetSelector.inactiveTick(this.activatedPriority, false)) // Pufferfish - use this to alternate ticking + this.targetSelector.tick(); + this.level.getProfiler().pop(); + this.level.getProfiler().push("goalSelector"); ++ if (this.goalSelector.inactiveTick(this.activatedPriority, false)) // Pufferfish - use this to alternate ticking + this.goalSelector.tick(); + this.level.getProfiler().pop(); + } +diff --git a/src/main/java/net/minecraft/world/entity/ai/attributes/AttributeMap.java b/src/main/java/net/minecraft/world/entity/ai/attributes/AttributeMap.java +index 9cbfda029782385d1a7987f5be46d450bd8a758e..52ddf38d5a1894a248e142c6b9ebaff5cc921e5b 100644 +--- a/src/main/java/net/minecraft/world/entity/ai/attributes/AttributeMap.java ++++ b/src/main/java/net/minecraft/world/entity/ai/attributes/AttributeMap.java +@@ -22,9 +22,11 @@ public class AttributeMap { + private final Map attributes = Maps.newHashMap(); + private final Set dirtyAttributes = Sets.newHashSet(); + private final AttributeSupplier supplier; ++ private final java.util.function.Function createInstance; // Pufferfish + + public AttributeMap(AttributeSupplier defaultAttributes) { + this.supplier = defaultAttributes; ++ this.createInstance = attribute -> this.supplier.createInstance(this::onAttributeModified, attribute); // Pufferfish + } + + private void onAttributeModified(AttributeInstance instance) { +@@ -44,11 +46,10 @@ public class AttributeMap { + }).collect(Collectors.toList()); + } + ++ + @Nullable + public AttributeInstance getInstance(Attribute attribute) { +- return this.attributes.computeIfAbsent(attribute, (attributex) -> { +- return this.supplier.createInstance(this::onAttributeModified, attributex); +- }); ++ return this.attributes.computeIfAbsent(attribute, this.createInstance); // Pufferfish - cache lambda, as for some reason java allocates it anyways + } + + public boolean hasAttribute(Attribute attribute) { +diff --git a/src/main/java/net/minecraft/world/entity/ai/behavior/AcquirePoi.java b/src/main/java/net/minecraft/world/entity/ai/behavior/AcquirePoi.java +index afbb2acd27416c801af3d718850b82a170734cd3..aa6ea8a81421ff0046bd926dedcda50f9ee02d60 100644 +--- a/src/main/java/net/minecraft/world/entity/ai/behavior/AcquirePoi.java ++++ b/src/main/java/net/minecraft/world/entity/ai/behavior/AcquirePoi.java +@@ -68,6 +68,7 @@ public class AcquirePoi extends Behavior { + @Override + protected void start(ServerLevel world, PathfinderMob entity, long time) { + this.nextScheduledStart = time + 20L + (long)world.getRandom().nextInt(20); ++ if (entity.getNavigation().isStuck()) this.nextScheduledStart += 200L; // Pufferfish - wait an additional 10s to check again if they're stuck + PoiManager poiManager = world.getPoiManager(); + this.batchCache.long2ObjectEntrySet().removeIf((entry) -> { + return !entry.getValue().isStillValid(time); +diff --git a/src/main/java/net/minecraft/world/entity/ai/behavior/VillagerPanicTrigger.java b/src/main/java/net/minecraft/world/entity/ai/behavior/VillagerPanicTrigger.java +index 42d466f7f162943886078eba3db18f2dfc2d7bee..6c0dda1ce018ec6bb2ebb97147045fffae0a89d5 100644 +--- a/src/main/java/net/minecraft/world/entity/ai/behavior/VillagerPanicTrigger.java ++++ b/src/main/java/net/minecraft/world/entity/ai/behavior/VillagerPanicTrigger.java +@@ -37,7 +37,11 @@ public class VillagerPanicTrigger extends Behavior { + + @Override + protected void tick(ServerLevel serverLevel, Villager villager, long l) { +- if (l % 100L == 0L) { ++ // Pufferfish start ++ if (villager.nextGolemPanic < 0) villager.nextGolemPanic = l + 100; ++ if (--villager.nextGolemPanic < l) { ++ villager.nextGolemPanic = -1; ++ // Pufferfish end + villager.spawnGolemIfNeeded(serverLevel, l, 3); + } + +diff --git a/src/main/java/net/minecraft/world/entity/ai/goal/GoalSelector.java b/src/main/java/net/minecraft/world/entity/ai/goal/GoalSelector.java +index 7fdc1cbd04a5bba9648272985f51c849b07b8223..02f44b5682b99417f3cd6d6b25dc46cdc2a09093 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 +@@ -51,9 +51,12 @@ public class GoalSelector { + } + + // Paper start +- public boolean inactiveTick() { ++ public boolean inactiveTick(int tickRate, boolean inactive) { // Pufferfish start ++ if (inactive && !gg.pufferfish.pufferfish.PufferfishConfig.dearEnabled) tickRate = 4; // reset to Paper's ++ tickRate = Math.min(tickRate, this.newGoalRate); + this.curRate++; +- return this.curRate % this.newGoalRate == 0; ++ return this.curRate % tickRate == 0; ++ // Pufferfish end + } + public boolean hasTasks() { + for (WrappedGoal task : this.availableGoals) { +diff --git a/src/main/java/net/minecraft/world/entity/ai/goal/MoveToBlockGoal.java b/src/main/java/net/minecraft/world/entity/ai/goal/MoveToBlockGoal.java +index 7fc40bb5fb6265b283c7c611f63aae76302c0eaf..387d31e342cef73ae769d712ba62e6aabb364e98 100644 +--- a/src/main/java/net/minecraft/world/entity/ai/goal/MoveToBlockGoal.java ++++ b/src/main/java/net/minecraft/world/entity/ai/goal/MoveToBlockGoal.java +@@ -119,6 +119,7 @@ public abstract class MoveToBlockGoal extends Goal { + for(int m = 0; m <= l; m = m > 0 ? -m : 1 - m) { + for(int n = m < l && m > -l ? l : 0; n <= l; n = n > 0 ? -n : 1 - n) { + mutableBlockPos.setWithOffset(blockPos, m, k - 1, n); ++ if (!this.mob.level.hasChunkAt(mutableBlockPos)) continue; // Pufferfish - if this block isn't loaded, continue + if (this.mob.isWithinRestriction(mutableBlockPos) && this.isValidTarget(this.mob.level, mutableBlockPos)) { + this.blockPos = mutableBlockPos; + setTargetPosition(mutableBlockPos.immutable()); // Paper +diff --git a/src/main/java/net/minecraft/world/entity/ai/targeting/TargetingConditions.java b/src/main/java/net/minecraft/world/entity/ai/targeting/TargetingConditions.java +index a7575b5ef56af6f53448d391abb4956e130148ca..e752c83df50fb9b670ecea2abc95426c2a009b6f 100644 +--- a/src/main/java/net/minecraft/world/entity/ai/targeting/TargetingConditions.java ++++ b/src/main/java/net/minecraft/world/entity/ai/targeting/TargetingConditions.java +@@ -75,9 +75,18 @@ public class TargetingConditions { + } + + if (this.range > 0.0D) { +- double d = this.testInvisible ? targetEntity.getVisibilityPercent(baseEntity) : 1.0D; +- double e = Math.max((this.useFollowRange ? this.getFollowRange(baseEntity) : this.range) * d, 2.0D); // Paper ++ // Pufferfish start - check range before getting visibility ++ // d = invisibility percent, e = follow range adjusted for invisibility, f = distance + double f = baseEntity.distanceToSqr(targetEntity.getX(), targetEntity.getY(), targetEntity.getZ()); ++ double followRangeRaw = this.useFollowRange ? this.getFollowRange(baseEntity) : this.range; ++ ++ if (f > followRangeRaw * followRangeRaw) { // the actual follow range will always be this value or smaller, so if the distance is larger then it never will return true after getting invis ++ return false; ++ } ++ ++ double d = this.testInvisible ? targetEntity.getVisibilityPercent(baseEntity) : 1.0D; ++ double e = Math.max((followRangeRaw) * d, 2.0D); // Paper ++ // Pufferfish end + if (f > e * e) { + return false; + } +diff --git a/src/main/java/net/minecraft/world/entity/ambient/Bat.java b/src/main/java/net/minecraft/world/entity/ambient/Bat.java +index 29dfbcecfbb2560e6ecde997abd5224a16c08c94..a76b754305036bf9c6387e27b86ec1559b290774 100644 +--- a/src/main/java/net/minecraft/world/entity/ambient/Bat.java ++++ b/src/main/java/net/minecraft/world/entity/ambient/Bat.java +@@ -254,13 +254,22 @@ public class Bat extends AmbientCreature { + } + } + ++ // Pufferfish start - only check for spooky season once an hour ++ private static boolean isSpookySeason = false; ++ private static final int ONE_HOUR = 20 * 60 * 60; ++ private static int lastSpookyCheck = -ONE_HOUR; + private static boolean isHalloween() { ++ if (net.minecraft.server.MinecraftServer.currentTick - lastSpookyCheck > ONE_HOUR) { + LocalDate localdate = LocalDate.now(); + int i = localdate.get(ChronoField.DAY_OF_MONTH); + int j = localdate.get(ChronoField.MONTH_OF_YEAR); + +- return j == 10 && i >= 20 || j == 11 && i <= 3; ++ isSpookySeason = j == 10 && i >= 20 || j == 11 && i <= 3; ++ lastSpookyCheck = net.minecraft.server.MinecraftServer.currentTick; ++ } ++ return isSpookySeason; + } ++ // Pufferfish end + + @Override + protected float getStandingEyeHeight(Pose pose, EntityDimensions dimensions) { +diff --git a/src/main/java/net/minecraft/world/entity/animal/axolotl/Axolotl.java b/src/main/java/net/minecraft/world/entity/animal/axolotl/Axolotl.java +index 86acf89ce875e215da8469947b382f70e42314b0..0fe2d6190ebba24713a3da7ef5367ecb86814a66 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 +@@ -276,9 +276,11 @@ public class Axolotl extends Animal implements LerpingModel, Bucketable { + return true; + } + ++ private int behaviorTick = 0; // Pufferfish + @Override + protected void customServerAiStep() { + this.level.getProfiler().push("axolotlBrain"); ++ if (this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish + this.getBrain().tick((ServerLevel) this.level, this); + this.level.getProfiler().pop(); + this.level.getProfiler().push("axolotlActivityUpdate"); +diff --git a/src/main/java/net/minecraft/world/entity/animal/goat/Goat.java b/src/main/java/net/minecraft/world/entity/animal/goat/Goat.java +index cc5687f43f8ac99995667fdc53c5c0586f70f367..d5cd18c478bedaeed83de31d442d1a5b0f0f54cf 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 +@@ -146,9 +146,11 @@ public class Goat extends Animal { + return (Brain) super.getBrain(); // CraftBukkit - decompile error + } + ++ private int behaviorTick = 0; // Pufferfish + @Override + protected void customServerAiStep() { + this.level.getProfiler().push("goatBrain"); ++ if (this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish + this.getBrain().tick((ServerLevel) this.level, this); + this.level.getProfiler().pop(); + this.level.getProfiler().push("goatActivityUpdate"); +diff --git a/src/main/java/net/minecraft/world/entity/item/ItemEntity.java b/src/main/java/net/minecraft/world/entity/item/ItemEntity.java +index f09db211f6f789ced85f7bf716428cd04bb41378..3ad87457cc1279540b5b633216aa0161e6e9b1b2 100644 +--- a/src/main/java/net/minecraft/world/entity/item/ItemEntity.java ++++ b/src/main/java/net/minecraft/world/entity/item/ItemEntity.java +@@ -242,10 +242,16 @@ public class ItemEntity extends Entity { + if (entityitem.isMergable()) { + // Paper Start - Fix items merging through walls + if (this.level.paperConfig.fixItemsMergingThroughWalls) { ++ // Pufferfish start - skip the allocations ++ /* + net.minecraft.world.level.ClipContext rayTrace = new net.minecraft.world.level.ClipContext(this.position(), entityitem.position(), + net.minecraft.world.level.ClipContext.Block.COLLIDER, net.minecraft.world.level.ClipContext.Fluid.NONE, this); + net.minecraft.world.phys.BlockHitResult rayTraceResult = level.clip(rayTrace); + if (rayTraceResult.getType() == net.minecraft.world.phys.HitResult.Type.BLOCK) continue; ++ */ ++ if (level.rayTraceDirect(this.position(), entityitem.position(), net.minecraft.world.phys.shapes.CollisionContext.of(this)) == ++ net.minecraft.world.phys.HitResult.Type.BLOCK) continue; ++ // Pufferfish end + } + // Paper End + this.tryToMerge(entityitem); +diff --git a/src/main/java/net/minecraft/world/entity/monster/EnderMan.java b/src/main/java/net/minecraft/world/entity/monster/EnderMan.java +index d47002d45dabd66f38d25d398d8943f4b911cdc5..5e207a3ea30ead2749c1121e2df8da53c8ebfdd7 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).getMaterial().blocksMotion()) { ++ // Pufferfish start - single chunk lookup ++ net.minecraft.world.level.chunk.LevelChunk chunk = this.level.getChunkIfLoaded(blockposition_mutableblockposition); ++ if (chunk == null) { ++ return false; ++ } ++ // Pufferfish end ++ while (blockposition_mutableblockposition.getY() > this.level.getMinBuildHeight() && !chunk.getBlockState(blockposition_mutableblockposition).getMaterial().blocksMotion()) { // Pufferfish + blockposition_mutableblockposition.move(Direction.DOWN); + } + +- BlockState iblockdata = this.level.getBlockState(blockposition_mutableblockposition); ++ BlockState iblockdata = chunk.getBlockState(blockposition_mutableblockposition); // Pufferfish + boolean flag = iblockdata.getMaterial().blocksMotion(); + boolean flag1 = iblockdata.getFluidState().is((Tag) 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 c5b9c0c650df5f4b7e3d2a431dc900e210104dea..17b4185704abfd47832941d02d60500707e8a530 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 +@@ -123,12 +123,14 @@ public class Hoglin extends Animal implements Enemy, HoglinBase { + + @Override + public Brain getBrain() { +- return super.getBrain(); ++ return (Brain) super.getBrain(); // Airplane - decompile fix + } + ++ private int behaviorTick; // Pufferfish + @Override + protected void customServerAiStep() { + this.level.getProfiler().push("hoglinBrain"); ++ if (this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish + this.getBrain().tick((ServerLevel)this.level, this); + this.level.getProfiler().pop(); + HoglinAi.updateActivity(this); +diff --git a/src/main/java/net/minecraft/world/entity/monster/piglin/Piglin.java b/src/main/java/net/minecraft/world/entity/monster/piglin/Piglin.java +index 8a2c2b4bd603aae37055abd058feb7ee759078ce..d850f1f57fc7560738f7bd82b37ec888e8e9301c 100644 +--- a/src/main/java/net/minecraft/world/entity/monster/piglin/Piglin.java ++++ b/src/main/java/net/minecraft/world/entity/monster/piglin/Piglin.java +@@ -288,9 +288,11 @@ public class Piglin extends AbstractPiglin implements CrossbowAttackMob, Invento + return !this.cannotHunt; + } + ++ private int behaviorTick; // Pufferfish + @Override + protected void customServerAiStep() { + this.level.getProfiler().push("piglinBrain"); ++ if (this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish + this.getBrain().tick((ServerLevel) this.level, this); + this.level.getProfiler().pop(); + PiglinAi.updateActivity(this); +diff --git a/src/main/java/net/minecraft/world/entity/npc/Villager.java b/src/main/java/net/minecraft/world/entity/npc/Villager.java +index ea612b3c3ed7305b504d65eb6d38ed99547fd488..7d6bc3c253a77adfc7c35383caadb37154dc5c0f 100644 +--- a/src/main/java/net/minecraft/world/entity/npc/Villager.java ++++ b/src/main/java/net/minecraft/world/entity/npc/Villager.java +@@ -138,6 +138,8 @@ public class Villager extends AbstractVillager implements ReputationEventHandler + return villageplacetype == PoiType.MEETING; + }); + ++ public long nextGolemPanic = -1; // Pufferfish ++ + public Villager(EntityType entityType, Level world) { + this(entityType, world, VillagerType.PLAINS); + } +@@ -241,11 +243,17 @@ public class Villager extends AbstractVillager implements ReputationEventHandler + } + // Spigot End + ++ private int behaviorTick = 0; // Pufferfish + @Override + protected void customServerAiStep() { mobTick(false); } + protected void mobTick(boolean inactive) { + this.level.getProfiler().push("villagerBrain"); +- if (!inactive) this.getBrain().tick((ServerLevel) this.level, this); // Paper ++ // Pufferfish start ++ if (!inactive) { ++ if (this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish ++ this.getBrain().tick((ServerLevel) this.level, this); // Paper ++ } ++ // Pufferfish end + this.level.getProfiler().pop(); + if (this.assignProfessionWhenSpawned) { + this.assignProfessionWhenSpawned = false; +diff --git a/src/main/java/net/minecraft/world/entity/player/Inventory.java b/src/main/java/net/minecraft/world/entity/player/Inventory.java +index c7e16e96633e17b951f0681599c5b3efc3ce1e6c..15dc5e9f426f78af0f3f92435a641a89ef2f134c 100644 +--- a/src/main/java/net/minecraft/world/entity/player/Inventory.java ++++ b/src/main/java/net/minecraft/world/entity/player/Inventory.java +@@ -688,6 +688,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()) { +@@ -702,6 +704,18 @@ public class Inventory implements Container, Nameable { + } + } + } ++ */ ++ for (int i = 0; i < this.compartments.size(); i++) { ++ List list = this.compartments.get(i); ++ for (int j = 0; j < list.size(); j++) { ++ ItemStack itemstack1 = list.get(j); ++ ++ if (!itemstack1.isEmpty() && itemstack1.sameItem(stack)) { ++ return true; ++ } ++ } ++ } ++ // Pufferfish end + + return false; + } +diff --git a/src/main/java/net/minecraft/world/entity/projectile/Projectile.java b/src/main/java/net/minecraft/world/entity/projectile/Projectile.java +index 15744949537430d8d8ae71ea72481126c9aff7bd..4759a6b3d3cd7973744562adf97d110d359ebce3 100644 +--- a/src/main/java/net/minecraft/world/entity/projectile/Projectile.java ++++ b/src/main/java/net/minecraft/world/entity/projectile/Projectile.java +@@ -42,6 +42,36 @@ public abstract class Projectile extends Entity { + super(type, world); + } + ++ // Pufferfish start ++ private static int loadedThisTick = 0; ++ private static int loadedTick; ++ ++ private int loadedLifetime = 0; ++ @Override ++ public void setPos(double x, double y, double z) { ++ int currentTick = net.minecraft.server.MinecraftServer.currentTick; ++ if (loadedTick != currentTick) { ++ loadedTick = currentTick; ++ loadedThisTick = 0; ++ } ++ int previousX = Mth.floor(this.getX()) >> 4, previousZ = Mth.floor(this.getZ()) >> 4; ++ int newX = Mth.floor(x) >> 4, newZ = Mth.floor(z) >> 4; ++ if (previousX != newX || previousZ != newZ) { ++ boolean isLoaded = ((net.minecraft.server.level.ServerChunkCache) this.level.getChunkSource()).getChunkAtIfLoadedMainThread(newX, newZ) != null; ++ if (!isLoaded) { ++ if (Projectile.loadedThisTick > gg.pufferfish.pufferfish.PufferfishConfig.maxProjectileLoadsPerTick) { ++ if (++this.loadedLifetime > gg.pufferfish.pufferfish.PufferfishConfig.maxProjectileLoadsPerProjectile) { ++ this.discard(); ++ } ++ return; ++ } ++ Projectile.loadedThisTick++; ++ } ++ } ++ super.setPos(x, y, z); ++ } ++ // Pufferfish start ++ + public void setOwner(@Nullable Entity entity) { + if (entity != null) { + this.ownerUUID = entity.getUUID(); +diff --git a/src/main/java/net/minecraft/world/entity/vehicle/AbstractMinecartContainer.java b/src/main/java/net/minecraft/world/entity/vehicle/AbstractMinecartContainer.java +index 8a07d5d25086d7544757bb86181fbe2b5e743d29..9be1ce7f5b956f0c82151ea02e555e7dd4a57241 100644 +--- a/src/main/java/net/minecraft/world/entity/vehicle/AbstractMinecartContainer.java ++++ b/src/main/java/net/minecraft/world/entity/vehicle/AbstractMinecartContainer.java +@@ -40,7 +40,10 @@ import org.bukkit.inventory.InventoryHolder; + + public abstract class AbstractMinecartContainer extends AbstractMinecart implements Container, MenuProvider { + ++ // Pufferfish start + private NonNullList itemStacks; ++ private gg.airplane.structs.ItemListWithBitset itemStacksOptimized; ++ // Pufferfish end + @Nullable + public ResourceLocation lootTable; + public long lootTableSeed; +@@ -89,12 +92,18 @@ public abstract class AbstractMinecartContainer extends AbstractMinecart impleme + + protected AbstractMinecartContainer(EntityType type, Level world) { + super(type, world); +- this.itemStacks = NonNullList.withSize(this.getContainerSize(), ItemStack.EMPTY); // CraftBukkit - SPIGOT-3513 ++ // Pufferfish start ++ this.itemStacksOptimized = new gg.airplane.structs.ItemListWithBitset(this.getContainerSize()); // CraftBukkit - SPIGOT-3513 ++ this.itemStacks = this.itemStacksOptimized.nonNullList; ++ // Pufferfish end + } + + protected AbstractMinecartContainer(EntityType type, double x, double y, double z, Level world) { + super(type, world, x, y, z); +- this.itemStacks = NonNullList.withSize(this.getContainerSize(), ItemStack.EMPTY); // CraftBukkit - SPIGOT-3513 ++ // Pufferfish start ++ this.itemStacksOptimized = new gg.airplane.structs.ItemListWithBitset(this.getContainerSize()); // CraftBukkit - SPIGOT-3513 ++ this.itemStacks = this.itemStacksOptimized.nonNullList; ++ // Pufferfish end + } + + @Override +@@ -217,7 +226,10 @@ public abstract class AbstractMinecartContainer extends AbstractMinecart impleme + protected void readAdditionalSaveData(CompoundTag nbt) { + super.readAdditionalSaveData(nbt); + this.lootableData.loadNbt(nbt); // Paper +- this.itemStacks = NonNullList.withSize(this.getContainerSize(), ItemStack.EMPTY); ++ // Pufferfish start ++ this.itemStacksOptimized = new gg.airplane.structs.ItemListWithBitset(this.getContainerSize()); ++ this.itemStacks = this.itemStacksOptimized.nonNullList; ++ // Pufferfish end + if (nbt.contains("LootTable", 8)) { + this.lootTable = new ResourceLocation(nbt.getString("LootTable")); + this.lootTableSeed = nbt.getLong("LootTableSeed"); +diff --git a/src/main/java/net/minecraft/world/item/crafting/ShapelessRecipe.java b/src/main/java/net/minecraft/world/item/crafting/ShapelessRecipe.java +index ffe5476d8ed15ee4384b679c341688787205ce59..9051559e78851257a56a998b4b882ebbcc394639 100644 +--- a/src/main/java/net/minecraft/world/item/crafting/ShapelessRecipe.java ++++ b/src/main/java/net/minecraft/world/item/crafting/ShapelessRecipe.java +@@ -25,8 +25,13 @@ public class ShapelessRecipe implements CraftingRecipe { + final String group; + final ItemStack result; + final NonNullList ingredients; ++ private final boolean isBukkit; // Pufferfish + ++ // Pufferfish start + public ShapelessRecipe(ResourceLocation id, String group, ItemStack output, NonNullList input) { ++ this(id, group, output, input, false); ++ } ++ public ShapelessRecipe(ResourceLocation id, String group, ItemStack output, NonNullList input, boolean isBukkit) { this.isBukkit = isBukkit; // Pufferfish end + this.id = id; + this.group = group; + this.result = output; +@@ -73,6 +78,28 @@ public class ShapelessRecipe implements CraftingRecipe { + } + + public boolean matches(CraftingContainer inventory, Level world) { ++ // Pufferfish start ++ if (!this.isBukkit) { ++ java.util.List ingredients = com.google.common.collect.Lists.newArrayList(this.ingredients.toArray(new Ingredient[0])); ++ ++ inventory: for (int index = 0; index < inventory.getContainerSize(); index++) { ++ ItemStack itemStack = inventory.getItem(index); ++ ++ if (!itemStack.isEmpty()) { ++ for (int i = 0; i < ingredients.size(); i++) { ++ if (ingredients.get(i).test(itemStack)) { ++ ingredients.remove(i); ++ continue inventory; ++ } ++ } ++ return false; ++ } ++ } ++ ++ return ingredients.isEmpty(); ++ } ++ // Pufferfish end ++ + StackedContents autorecipestackmanager = new StackedContents(); + int i = 0; + +diff --git a/src/main/java/net/minecraft/world/level/BlockGetter.java b/src/main/java/net/minecraft/world/level/BlockGetter.java +index d1eefa6ef3e9abfe7af4d8310aa64465fa2d5463..0f4aa330e5b179bb706a31917c671f165e22b9cd 100644 +--- a/src/main/java/net/minecraft/world/level/BlockGetter.java ++++ b/src/main/java/net/minecraft/world/level/BlockGetter.java +@@ -73,6 +73,16 @@ public interface BlockGetter extends LevelHeightAccessor { + }); + } + ++ // Pufferfish start - broken down variant of below rayTraceBlock, used by World#rayTraceDirect ++ default net.minecraft.world.phys.BlockHitResult.Type rayTraceBlockDirect(Vec3 vec3d, Vec3 vec3d1, BlockPos blockposition, BlockState iblockdata, net.minecraft.world.phys.shapes.CollisionContext voxelshapecoll) { ++ if (iblockdata.isAir()) return null; // Tuinity - optimise air cases ++ VoxelShape voxelshape = ClipContext.Block.COLLIDER.get(iblockdata, this, blockposition, voxelshapecoll); ++ net.minecraft.world.phys.BlockHitResult movingobjectpositionblock = this.clipWithInteractionOverride(vec3d, vec3d1, blockposition, voxelshape, iblockdata); ++ ++ return movingobjectpositionblock == null ? null : movingobjectpositionblock.getType(); ++ } ++ // Pufferfish end ++ + // CraftBukkit start - moved block handling into separate method for use by Block#rayTrace + default BlockHitResult clip(ClipContext raytrace1, BlockPos blockposition) { + // Paper start - Prevent raytrace from loading chunks +diff --git a/src/main/java/net/minecraft/world/level/GameRules.java b/src/main/java/net/minecraft/world/level/GameRules.java +index e7ca5d6fb8922e7e8065864f736b06056be080a0..6c9e574851b518242dbbee9bce954b44dbaeecb6 100644 +--- a/src/main/java/net/minecraft/world/level/GameRules.java ++++ b/src/main/java/net/minecraft/world/level/GameRules.java +@@ -90,6 +90,7 @@ public class GameRules { + public static final GameRules.Key RULE_UNIVERSAL_ANGER = GameRules.register("universalAnger", GameRules.Category.MOBS, GameRules.BooleanValue.create(false)); + public static final GameRules.Key RULE_PLAYERS_SLEEPING_PERCENTAGE = GameRules.register("playersSleepingPercentage", GameRules.Category.PLAYER, GameRules.IntegerValue.create(100)); + private final Map, GameRules.Value> rules; ++ private final GameRules.Value[] gameruleArray; + + private static > GameRules.Key register(String name, GameRules.Category category, GameRules.Type type) { + GameRules.Key gamerules_gamerulekey = new GameRules.Key<>(name, category); +@@ -108,17 +109,33 @@ public class GameRules { + } + + public GameRules() { +- this.rules = (Map) GameRules.GAME_RULE_TYPES.entrySet().stream().collect(ImmutableMap.toImmutableMap(Entry::getKey, (entry) -> { ++ // Pufferfish start - use this to ensure gameruleArray is initialized ++ this((Map) GameRules.GAME_RULE_TYPES.entrySet().stream().collect(ImmutableMap.toImmutableMap(Entry::getKey, (entry) -> { + return ((GameRules.Type) entry.getValue()).createRule(); +- })); ++ }))); ++ // Pufferfish end + } + + private GameRules(Map, GameRules.Value> rules) { + this.rules = rules; ++ ++ // Pufferfish start ++ int arraySize = rules.keySet().stream().mapToInt(key -> key.gameRuleIndex).max().orElse(-1) + 1; ++ GameRules.Value[] values = new GameRules.Value[arraySize]; ++ ++ for (Entry, GameRules.Value> entry : rules.entrySet()) { ++ values[entry.getKey().gameRuleIndex] = entry.getValue(); ++ } ++ ++ this.gameruleArray = values; ++ // Pufferfish end + } + + public > T getRule(GameRules.Key key) { +- return (T) this.rules.get(key); // CraftBukkit - decompile error ++ // Pufferfish start ++ return key == null ? null : (T) this.gameruleArray[key.gameRuleIndex]; ++ //return (T) this.rules.get(key); // CraftBukkit - decompile error ++ // Pufferfish end + } + + public CompoundTag createTag() { +@@ -177,6 +194,10 @@ public class GameRules { + } + + public static final class Key> { ++ // Pufferfish start ++ private static int lastGameRuleIndex = 0; ++ public final int gameRuleIndex = lastGameRuleIndex++; ++ // Pufferfish end + + final String id; + private final GameRules.Category category; +diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java +index 4247dcb003626535dbb997f48ad9f61380bd17e9..90aa1d75b5c23e5ee27ceae9f6ef90de913a6601 100644 +--- a/src/main/java/net/minecraft/world/level/Level.java ++++ b/src/main/java/net/minecraft/world/level/Level.java +@@ -308,6 +308,15 @@ public abstract class Level implements LevelAccessor, AutoCloseable { + } + // Paper end - optimise checkDespawn + ++ // Pufferfish start - ensure these get inlined ++ private final int minBuildHeight, minSection, height, maxBuildHeight, maxSection; ++ @Override public final int getMaxBuildHeight() { return this.maxBuildHeight; } ++ @Override public final int getMinSection() { return this.minSection; } ++ @Override public final int getMaxSection() { return this.maxSection; } ++ @Override public final int getMinBuildHeight() { return this.minBuildHeight; } ++ @Override public final int getHeight() { return this.height; } ++ // Pufferfish end ++ + protected Level(WritableLevelData worlddatamutable, ResourceKey resourcekey, final DimensionType dimensionmanager, Supplier supplier, boolean flag, boolean flag1, long i, org.bukkit.generator.ChunkGenerator gen, org.bukkit.generator.BiomeProvider biomeProvider, org.bukkit.World.Environment env, java.util.concurrent.Executor executor) { // Paper - Async-Anti-Xray - Pass executor + this.spigotConfig = new org.spigotmc.SpigotWorldConfig(((net.minecraft.world.level.storage.PrimaryLevelData) worlddatamutable).getLevelName()); // Spigot + this.paperConfig = new com.destroystokyo.paper.PaperWorldConfig(((net.minecraft.world.level.storage.PrimaryLevelData) worlddatamutable).getLevelName(), this.spigotConfig); // Paper +@@ -323,6 +332,13 @@ public abstract class Level implements LevelAccessor, AutoCloseable { + this.profiler = supplier; + this.levelData = worlddatamutable; + this.dimensionType = dimensionmanager; ++ // Pufferfish start ++ this.minBuildHeight = dimensionmanager.minY(); ++ this.minSection = SectionPos.blockToSectionCoord(this.minBuildHeight); ++ this.height = dimensionmanager.height(); ++ this.maxBuildHeight = this.minBuildHeight + this.height; ++ this.maxSection = SectionPos.blockToSectionCoord(this.maxBuildHeight - 1) + 1; ++ // Pufferfish end + this.dimension = resourcekey; + this.isClientSide = flag; + if (dimensionmanager.coordinateScale() != 1.0D) { +@@ -438,6 +454,91 @@ public abstract class Level implements LevelAccessor, AutoCloseable { + return null; + } + ++ // Pufferfish start - broken down method of raytracing for EntityLiving#hasLineOfSight, replaces IBlockAccess#rayTrace(RayTrace) ++ public net.minecraft.world.phys.BlockHitResult.Type rayTraceDirect(net.minecraft.world.phys.Vec3 vec3d, net.minecraft.world.phys.Vec3 vec3d1, net.minecraft.world.phys.shapes.CollisionContext voxelshapecoll) { ++ // most of this code comes from IBlockAccess#a(RayTrace, BiFunction, Function), but removes the needless functions ++ if (vec3d.equals(vec3d1)) { ++ return net.minecraft.world.phys.BlockHitResult.Type.MISS; ++ } ++ ++ double endX = Mth.lerp(-1.0E-7D, vec3d1.x, vec3d.x); ++ double endY = Mth.lerp(-1.0E-7D, vec3d1.y, vec3d.y); ++ double endZ = Mth.lerp(-1.0E-7D, vec3d1.z, vec3d.z); ++ ++ double startX = Mth.lerp(-1.0E-7D, vec3d.x, vec3d1.x); ++ double startY = Mth.lerp(-1.0E-7D, vec3d.y, vec3d1.y); ++ double startZ = Mth.lerp(-1.0E-7D, vec3d.z, vec3d1.z); ++ ++ int currentX = Mth.floor(startX); ++ int currentY = Mth.floor(startY); ++ int currentZ = Mth.floor(startZ); ++ ++ BlockPos.MutableBlockPos currentBlock = new BlockPos.MutableBlockPos(currentX, currentY, currentZ); ++ ++ LevelChunk chunk = this.getChunkIfLoaded(currentBlock); ++ if (chunk == null) { ++ return net.minecraft.world.phys.BlockHitResult.Type.MISS; ++ } ++ ++ net.minecraft.world.phys.BlockHitResult.Type initialCheck = this.rayTraceBlockDirect(vec3d, vec3d1, currentBlock, chunk.getBlockState(currentBlock), voxelshapecoll); ++ ++ if (initialCheck != null) { ++ return initialCheck; ++ } ++ ++ double diffX = endX - startX; ++ double diffY = endY - startY; ++ double diffZ = endZ - startZ; ++ ++ int xDirection = Mth.sign(diffX); ++ int yDirection = Mth.sign(diffY); ++ int zDirection = Mth.sign(diffZ); ++ ++ double normalizedX = xDirection == 0 ? Double.MAX_VALUE : (double) xDirection / diffX; ++ double normalizedY = yDirection == 0 ? Double.MAX_VALUE : (double) yDirection / diffY; ++ double normalizedZ = zDirection == 0 ? Double.MAX_VALUE : (double) zDirection / diffZ; ++ ++ double normalizedXDirection = normalizedX * (xDirection > 0 ? 1.0D - Mth.frac(startX) : Mth.frac(startX)); ++ double normalizedYDirection = normalizedY * (yDirection > 0 ? 1.0D - Mth.frac(startY) : Mth.frac(startY)); ++ double normalizedZDirection = normalizedZ * (zDirection > 0 ? 1.0D - Mth.frac(startZ) : Mth.frac(startZ)); ++ ++ net.minecraft.world.phys.BlockHitResult.Type result; ++ ++ do { ++ if (normalizedXDirection > 1.0D && normalizedYDirection > 1.0D && normalizedZDirection > 1.0D) { ++ return net.minecraft.world.phys.BlockHitResult.Type.MISS; ++ } ++ ++ if (normalizedXDirection < normalizedYDirection) { ++ if (normalizedXDirection < normalizedZDirection) { ++ currentX += xDirection; ++ normalizedXDirection += normalizedX; ++ } else { ++ currentZ += zDirection; ++ normalizedZDirection += normalizedZ; ++ } ++ } else if (normalizedYDirection < normalizedZDirection) { ++ currentY += yDirection; ++ normalizedYDirection += normalizedY; ++ } else { ++ currentZ += zDirection; ++ normalizedZDirection += normalizedZ; ++ } ++ ++ currentBlock.set(currentX, currentY, currentZ); ++ if (chunk.getPos().x != currentBlock.getX() >> 4 || chunk.getPos().z != currentBlock.getZ() >> 4) { ++ chunk = this.getChunkIfLoaded(currentBlock); ++ if (chunk == null) { ++ return net.minecraft.world.phys.BlockHitResult.Type.MISS; ++ } ++ } ++ result = this.rayTraceBlockDirect(vec3d, vec3d1, currentBlock, chunk.getBlockState(currentBlock), voxelshapecoll); ++ } while (result == null); ++ ++ return result; ++ } ++ // Pufferfish end ++ + public boolean isInWorldBounds(BlockPos pos) { + return pos.isInsideBuildHeightAndWorldBoundsHorizontal(this); // Paper - use better/optimized check + } +@@ -969,13 +1070,13 @@ public abstract class Level implements LevelAccessor, AutoCloseable { + try { + tickConsumer.accept(entity); + MinecraftServer.getServer().executeMidTickTasks(); // Paper - execute chunk tasks mid tick +- } catch (Throwable throwable) { ++ } catch (Throwable throwable) { // Pufferfish - diff on change ServerLevel.tick + if (throwable instanceof ThreadDeath) throw throwable; // Paper + // Paper start - Prevent tile entity and entity crashes + final String msg = String.format("Entity threw exception at %s:%s,%s,%s", entity.level.getWorld().getName(), entity.getX(), entity.getY(), entity.getZ()); + MinecraftServer.LOGGER.error(msg, throwable); + getCraftServer().getPluginManager().callEvent(new ServerExceptionEvent(new ServerInternalException(msg, throwable))); +- entity.discard(); ++ entity.discard(); // Pufferfish - diff on change ServerLevel.tick + // Paper end + } + } +@@ -1437,6 +1538,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 302803aa25b713cb087bdb2991cb0803dfe6005b..434bdc40ddd92700211076965d97a473706e7351 100644 +--- a/src/main/java/net/minecraft/world/level/NaturalSpawner.java ++++ b/src/main/java/net/minecraft/world/level/NaturalSpawner.java +@@ -170,7 +170,7 @@ public final class NaturalSpawner { + int difference = k1 - currEntityCount; + + if (world.paperConfig.perPlayerMobSpawns) { +- int minDiff = Integer.MAX_VALUE; ++ int minDiff = gg.pufferfish.pufferfish.PufferfishConfig.enableAsyncMobSpawning ? difference : Integer.MAX_VALUE; // Pufferfish - optimize mob spawning + final com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet inRange = world.getChunkSource().chunkMap.playerMobDistanceMap.getObjectsInRange(chunk.getPos()); + if (inRange != null) { + final Object[] backingSet = inRange.getBackingSet(); +@@ -431,12 +431,12 @@ public final class NaturalSpawner { + return spawnGroup == MobCategory.MONSTER && world.getBlockState(pos.below()).is(Blocks.NETHER_BRICKS) && structureAccessor.getStructureAt(pos, StructureFeature.NETHER_BRIDGE).isValid(); + } + +- 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 fa4544541b9c5f163b633596e73a5197cd081eb8..0e993320f56fa08fa346833620ad0b3bc2fc772b 100644 +--- a/src/main/java/net/minecraft/world/level/biome/Biome.java ++++ b/src/main/java/net/minecraft/world/level/biome/Biome.java +@@ -80,14 +80,20 @@ public final class Biome { + private final MobSpawnSettings mobSettings; + private final Biome.BiomeCategory biomeCategory; + private final BiomeSpecialEffects specialEffects; +- private final ThreadLocal temperatureCache = ThreadLocal.withInitial(() -> { ++ // Pufferfish start - use our cache ++ private final ThreadLocal temperatureCache = ThreadLocal.withInitial(() -> { + return Util.make(() -> { ++ /* + Long2FloatLinkedOpenHashMap long2FloatLinkedOpenHashMap = new Long2FloatLinkedOpenHashMap(1024, 0.25F) { + protected void rehash(int i) { + } + }; + long2FloatLinkedOpenHashMap.defaultReturnValue(Float.NaN); + return long2FloatLinkedOpenHashMap; ++ ++ */ ++ return new gg.airplane.structs.Long2FloatAgingCache(TEMPERATURE_CACHE_SIZE); ++ // Pufferfish end + }); + }); + +@@ -129,17 +135,15 @@ public final class Biome { + @Deprecated + public float getTemperature(BlockPos blockPos) { + long l = blockPos.asLong(); +- Long2FloatLinkedOpenHashMap long2FloatLinkedOpenHashMap = this.temperatureCache.get(); +- float f = long2FloatLinkedOpenHashMap.get(l); ++ // Pufferfish start ++ gg.airplane.structs.Long2FloatAgingCache cache = this.temperatureCache.get(); ++ float f = cache.getValue(l); + if (!Float.isNaN(f)) { + return f; + } else { + float g = this.getHeightAdjustedTemperature(blockPos); +- if (long2FloatLinkedOpenHashMap.size() == 1024) { +- long2FloatLinkedOpenHashMap.removeFirstFloat(); +- } +- +- long2FloatLinkedOpenHashMap.put(l, g); ++ cache.putValue(l, g); ++ // Pufferfish end + return g; + } + } +diff --git a/src/main/java/net/minecraft/world/level/block/entity/ChestBlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/ChestBlockEntity.java +index e56f7d76b501dab7d549efd2fafd514a9625c24e..066f43deb5df6f79428eb0f325c2abfb31ea9254 100644 +--- a/src/main/java/net/minecraft/world/level/block/entity/ChestBlockEntity.java ++++ b/src/main/java/net/minecraft/world/level/block/entity/ChestBlockEntity.java +@@ -32,7 +32,10 @@ import org.bukkit.entity.HumanEntity; + public class ChestBlockEntity extends RandomizableContainerBlockEntity implements LidBlockEntity { + + private static final int EVENT_SET_OPEN_COUNT = 1; ++ // Pufferfish start + private NonNullList items; ++ private gg.airplane.structs.ItemListWithBitset optimizedItems; ++ // Pufferfish end + public final ContainerOpenersCounter openersCounter; + private final ChestLidController chestLidController; + +@@ -66,9 +69,13 @@ public class ChestBlockEntity extends RandomizableContainerBlockEntity implement + } + // CraftBukkit end + ++ private final boolean isNative = getClass().equals(ChestBlockEntity.class); // Pufferfish + protected ChestBlockEntity(BlockEntityType type, BlockPos pos, BlockState state) { + super(type, pos, state); +- this.items = NonNullList.withSize(27, ItemStack.EMPTY); ++ // Pufferfish start ++ this.optimizedItems = new gg.airplane.structs.ItemListWithBitset(27); ++ this.items = this.optimizedItems.nonNullList; ++ // Pufferfish end + this.openersCounter = new ContainerOpenersCounter() { + @Override + protected void onOpen(Level world, BlockPos pos, BlockState state) { +@@ -99,6 +106,23 @@ public class ChestBlockEntity extends RandomizableContainerBlockEntity implement + this.chestLidController = new ChestLidController(); + } + ++ // Pufferfish start ++ @Override ++ public boolean hasEmptySlot(Direction enumdirection) { ++ return isNative ? !this.optimizedItems.hasFullStacks() : super.hasEmptySlot(enumdirection); ++ } ++ ++ @Override ++ public boolean isCompletelyFull(Direction enumdirection) { ++ return isNative ? this.optimizedItems.hasFullStacks() && super.isCompletelyFull(enumdirection) : super.isCompletelyFull(enumdirection); ++ } ++ ++ @Override ++ public boolean isCompletelyEmpty(Direction enumdirection) { ++ return isNative && this.optimizedItems.isCompletelyEmpty() || super.isCompletelyEmpty(enumdirection); ++ } ++ // Pufferfish end ++ + public ChestBlockEntity(BlockPos pos, BlockState state) { + this(BlockEntityType.CHEST, pos, state); + } +@@ -116,7 +140,10 @@ public class ChestBlockEntity extends RandomizableContainerBlockEntity implement + @Override + public void load(CompoundTag nbt) { + super.load(nbt); +- this.items = NonNullList.withSize(this.getContainerSize(), ItemStack.EMPTY); ++ // Pufferfish start ++ this.optimizedItems = new gg.airplane.structs.ItemListWithBitset(this.getContainerSize()); ++ this.items = this.optimizedItems.nonNullList; ++ // Pufferfish end + if (!this.tryLoadLootTable(nbt)) { + ContainerHelper.loadAllItems(nbt, this.items); + } +@@ -188,7 +215,10 @@ public class ChestBlockEntity extends RandomizableContainerBlockEntity implement + + @Override + protected void setItems(NonNullList list) { +- this.items = list; ++ // Pufferfish start ++ this.optimizedItems = gg.airplane.structs.ItemListWithBitset.fromList(list); ++ this.items = this.optimizedItems.nonNullList; ++ // Pufferfish end + } + + @Override +diff --git a/src/main/java/net/minecraft/world/level/block/entity/HopperBlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/HopperBlockEntity.java +index a19642740f42ee8c683ed7a86f6edd2bc887b6dd..14d5c4c62e5b9cac1c16af8e259f6a8ff3d8bbbf 100644 +--- a/src/main/java/net/minecraft/world/level/block/entity/HopperBlockEntity.java ++++ b/src/main/java/net/minecraft/world/level/block/entity/HopperBlockEntity.java +@@ -44,7 +44,10 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen + + public static final int MOVE_ITEM_SPEED = 8; + public static final int HOPPER_CONTAINER_SIZE = 5; ++ // Pufferfish start + private NonNullList items; ++ private gg.airplane.structs.ItemListWithBitset optimizedItems; // Pufferfish ++ // Pufferfish end + private int cooldownTime; + private long tickedGameTime; + +@@ -80,14 +83,37 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen + + public HopperBlockEntity(BlockPos pos, BlockState state) { + super(BlockEntityType.HOPPER, pos, state); +- this.items = NonNullList.withSize(5, ItemStack.EMPTY); ++ // Pufferfish start ++ this.optimizedItems = new gg.airplane.structs.ItemListWithBitset(5); ++ this.items = this.optimizedItems.nonNullList; ++ // Pufferfish end + this.cooldownTime = -1; + } + ++ // Pufferfish start ++ @Override ++ public boolean hasEmptySlot(Direction enumdirection) { ++ return !this.optimizedItems.hasFullStacks(); ++ } ++ ++ @Override ++ public boolean isCompletelyFull(Direction enumdirection) { ++ return this.optimizedItems.hasFullStacks() && super.isCompletelyFull(enumdirection); ++ } ++ ++ @Override ++ public boolean isCompletelyEmpty(Direction enumdirection) { ++ return this.optimizedItems.isCompletelyEmpty() || super.isCompletelyEmpty(enumdirection); ++ } ++ // Pufferfish end ++ + @Override + public void load(CompoundTag nbt) { + super.load(nbt); +- this.items = NonNullList.withSize(this.getContainerSize(), ItemStack.EMPTY); ++ // Pufferfish start ++ this.optimizedItems = new gg.airplane.structs.ItemListWithBitset(this.getContainerSize()); ++ this.items = this.optimizedItems.nonNullList; ++ // Pufferfish end + if (!this.tryLoadLootTable(nbt)) { + ContainerHelper.loadAllItems(nbt, this.items); + } +@@ -159,7 +185,7 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen + flag = HopperBlockEntity.ejectItems(world, pos, state, (Container) blockEntity, blockEntity); // CraftBukkit + } + +- if (!blockEntity.inventoryFull()) { ++ if (!blockEntity.optimizedItems.hasFullStacks() || !blockEntity.inventoryFull()) { // Pufferfish - use bitset first + flag |= booleansupplier.getAsBoolean(); + } + +@@ -198,7 +224,7 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen + skipPushModeEventFire = skipHopperEvents; + boolean foundItem = false; + for (int i = 0; i < hopper.getContainerSize(); ++i) { +- ItemStack item = hopper.getItem(i); ++ ItemStack item = hopper.getItem(i); // Pufferfish + if (!item.isEmpty()) { + foundItem = true; + ItemStack origItemStack = item; +@@ -401,12 +427,18 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen + } + + private static boolean isFullContainer(Container inventory, Direction direction) { +- return allMatch(inventory, direction, STACK_SIZE_TEST); // Paper - no streams ++ // Pufferfish start - use bitsets ++ //return allMatch(inventory, direction, STACK_SIZE_TEST); // Paper - no streams ++ return inventory.isCompletelyFull(direction); ++ // Pufferfish end + } + + private static boolean isEmptyContainer(Container inv, Direction facing) { + // Paper start +- return allMatch(inv, facing, IS_EMPTY_TEST); ++ // Pufferfish start - use bitsets ++ //return allMatch(inv, facing, IS_EMPTY_TEST); ++ return inv.isCompletelyEmpty(facing); ++ // Pufferfish end + } + private static boolean allMatch(Container iinventory, Direction enumdirection, java.util.function.BiPredicate test) { + if (iinventory instanceof WorldlyContainer) { +@@ -583,7 +615,7 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen + + if (HopperBlockEntity.canPlaceItemInContainer(to, stack, slot, side)) { + boolean flag = false; +- boolean flag1 = to.isEmpty(); ++ boolean flag1 = to.isCompletelyEmpty(side); // Pufferfish + + if (itemstack1.isEmpty()) { + // Spigot start - SPIGOT-6693, InventorySubcontainer#setItem +@@ -731,7 +763,10 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen + + @Override + protected void setItems(NonNullList list) { +- this.items = list; ++ // Pufferfish start ++ this.optimizedItems = gg.airplane.structs.ItemListWithBitset.fromList(list); ++ this.items = this.optimizedItems.nonNullList; ++ // Pufferfish end + } + + public static void entityInside(Level world, BlockPos pos, BlockState state, Entity entity, HopperBlockEntity blockEntity) { +diff --git a/src/main/java/net/minecraft/world/level/block/entity/RandomizableContainerBlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/RandomizableContainerBlockEntity.java +index ed3518fe7c841d9e1a9c97626acaa3d765a6d76f..da00f75ccc9f8c2e2174d4450479202a50844245 100644 +--- a/src/main/java/net/minecraft/world/level/block/entity/RandomizableContainerBlockEntity.java ++++ b/src/main/java/net/minecraft/world/level/block/entity/RandomizableContainerBlockEntity.java +@@ -96,13 +96,8 @@ public abstract class RandomizableContainerBlockEntity extends BaseContainerBloc + public boolean isEmpty() { + this.unpackLootTable((Player)null); + // Paper start +- for (ItemStack itemStack : this.getItems()) { +- if (!itemStack.isEmpty()) { +- return false; +- } +- } ++ return this.isCompletelyEmpty(null); // Pufferfish - use super + // Paper end +- return true; + } + + @Override +diff --git a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java +index 7567a8bf848c82b27383f084056cb43c41df6d0c..2b697f0d0b59b5af5aa59850d5e501cde9acf577 100644 +--- a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java ++++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java +@@ -94,6 +94,18 @@ public class LevelChunk extends ChunkAccess { + } + // Paper end + ++ // Pufferfish start - instead of using a random every time the chunk is ticked, define when lightning strikes preemptively ++ private int lightningTick; ++ // shouldDoLightning compiles down to 29 bytes, which with the default of 35 byte inlining should guarantee an inline ++ public final boolean shouldDoLightning(java.util.Random random) { ++ if (this.lightningTick-- <= 0) { ++ this.lightningTick = random.nextInt(100000) << 1; ++ return true; ++ } ++ return false; ++ } ++ // Pufferfish end ++ + public LevelChunk(Level world, ChunkPos pos) { + this(world, pos, UpgradeData.EMPTY, new LevelChunkTicks<>(), new LevelChunkTicks<>(), 0L, (LevelChunkSection[]) null, (LevelChunk.PostLoadProcessor) null, (BlendingData) null); + } +@@ -124,6 +136,7 @@ public class LevelChunk extends ChunkAccess { + this.fluidTicks = fluidTickScheduler; + // CraftBukkit start + this.bukkitChunk = new org.bukkit.craftbukkit.CraftChunk(this); ++ this.lightningTick = this.level.random.nextInt(100000) << 1; // Pufferfish - initialize lightning tick + } + + public org.bukkit.Chunk bukkitChunk; +diff --git a/src/main/java/net/minecraft/world/level/chunk/LevelChunkSection.java b/src/main/java/net/minecraft/world/level/chunk/LevelChunkSection.java +index d5ceebee36885c6470917bc1d0952733e983f030..ed71c57fe629f01b21694b8875b8defd89a9fee6 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,9 @@ public class LevelChunkSection { + private short tickingFluidCount; + public final PalettedContainer states; + private final PalettedContainer biomes; ++ // Pufferfish start ++ public short fluidStateCount; ++ // Pufferfish end + public final com.destroystokyo.paper.util.maplist.IBlockDataList tickingList = new com.destroystokyo.paper.util.maplist.IBlockDataList(); // Paper + + public LevelChunkSection(int chunkPos, PalettedContainer blockStateContainer, PalettedContainer biomeContainer) { +@@ -91,6 +94,7 @@ public class LevelChunkSection { + + if (!fluid.isEmpty()) { + --this.tickingFluidCount; ++ --this.fluidStateCount; // Pufferfish + } + + if (!state.isAir()) { +@@ -105,6 +109,7 @@ public class LevelChunkSection { + + if (!fluid1.isEmpty()) { + ++this.tickingFluidCount; ++ ++this.fluidStateCount; // Pufferfish + } + + return iblockdata1; +@@ -155,6 +160,7 @@ public class LevelChunkSection { + if (fluid.isRandomlyTicking()) { + this.tickingFluidCount = (short) (this.tickingFluidCount + 1); // Paper + } ++ this.fluidStateCount++; // Pufferfish + } + + }); +diff --git a/src/main/java/net/minecraft/world/level/entity/EntityTickList.java b/src/main/java/net/minecraft/world/level/entity/EntityTickList.java +index 4814e719e0b898464692075170889fdb2729a26a..ffcd18fa852af3f24bdf5398f481b839d2dcfe6c 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/entity/LevelEntityGetter.java b/src/main/java/net/minecraft/world/level/entity/LevelEntityGetter.java +index da1ad0b2679e392ed81b50c15f012c63cb5c939e..9c6ab057dccd6331e9c577e0c6192c0d84718906 100644 +--- a/src/main/java/net/minecraft/world/level/entity/LevelEntityGetter.java ++++ b/src/main/java/net/minecraft/world/level/entity/LevelEntityGetter.java +@@ -6,6 +6,8 @@ import javax.annotation.Nullable; + import net.minecraft.world.phys.AABB; + + public interface LevelEntityGetter { ++ int getCount(); // Pufferfish ++ + @Nullable + T get(int id); + +diff --git a/src/main/java/net/minecraft/world/level/entity/LevelEntityGetterAdapter.java b/src/main/java/net/minecraft/world/level/entity/LevelEntityGetterAdapter.java +index 3b13f6ea36a3bfecabe09221eb5c48dddab119db..563b6c47df0c5ae9efcb91fd53a065b1da1cdb94 100644 +--- a/src/main/java/net/minecraft/world/level/entity/LevelEntityGetterAdapter.java ++++ b/src/main/java/net/minecraft/world/level/entity/LevelEntityGetterAdapter.java +@@ -14,6 +14,8 @@ public class LevelEntityGetterAdapter implements LevelEn + this.sectionStorage = cache; + } + ++ @Override public int getCount() { return this.visibleEntities.count(); } // Pufferfish ++ + @Nullable + @Override + public T get(int id) { +diff --git a/src/main/java/net/minecraft/world/level/material/FlowingFluid.java b/src/main/java/net/minecraft/world/level/material/FlowingFluid.java +index 6f5f9d8a38bf023969c883b3e3a230c3cdc62104..a02a5313db545a8694feec0e2504465426c39a6a 100644 +--- a/src/main/java/net/minecraft/world/level/material/FlowingFluid.java ++++ b/src/main/java/net/minecraft/world/level/material/FlowingFluid.java +@@ -44,6 +44,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) {} +@@ -52,6 +54,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() {} +@@ -240,6 +250,8 @@ public abstract class FlowingFluid extends Fluid { + } + + private boolean canPassThroughWall(Direction face, BlockGetter world, BlockPos pos, BlockState state, BlockPos fromPos, BlockState fromState) { ++ // Pufferfish start - modify to use our cache ++ /* + Object2ByteLinkedOpenHashMap object2bytelinkedopenhashmap; + + if (!state.getBlock().hasDynamicShape() && !fromState.getBlock().hasDynamicShape()) { +@@ -247,9 +259,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); +@@ -260,11 +279,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(); +@@ -272,6 +302,11 @@ public abstract class FlowingFluid extends Fluid { + + object2bytelinkedopenhashmap.putAndMoveToFirst(block_a, (byte) (flag ? 1 : 0)); + } ++ */ ++ if (cache != null) { ++ cache.putValue(block_a, flag); ++ } ++ // Pufferfish end + + return flag; + } +diff --git a/src/main/java/net/minecraft/world/level/storage/loot/LootContext.java b/src/main/java/net/minecraft/world/level/storage/loot/LootContext.java +index 05b64f2730bfe836bd1d72dcfccd9f536908a099..d33af84300db18ea2b71dba2c9ed43390a293500 100644 +--- a/src/main/java/net/minecraft/world/level/storage/loot/LootContext.java ++++ b/src/main/java/net/minecraft/world/level/storage/loot/LootContext.java +@@ -41,8 +41,10 @@ public class LootContext { + this.level = world; + this.lootTables = tableGetter; + this.conditions = conditionGetter; +- this.params = ImmutableMap.copyOf(parameters); +- this.dynamicDrops = ImmutableMap.copyOf(drops); ++ // Pufferfish start - use unmodifiable maps instead of immutable ones to skip the copy ++ this.params = java.util.Collections.unmodifiableMap(parameters); ++ this.dynamicDrops = java.util.Collections.unmodifiableMap(drops); ++ // Pufferfish end + } + + public boolean hasParam(LootContextParam parameter) { +diff --git a/src/main/java/net/minecraft/world/phys/shapes/EntityCollisionContext.java b/src/main/java/net/minecraft/world/phys/shapes/EntityCollisionContext.java +index e387de8adc480eac27b58b6f3f0d331ffc4382f1..6dba9d6bb7b81fe176cc0fabbb60b554282a9180 100644 +--- a/src/main/java/net/minecraft/world/phys/shapes/EntityCollisionContext.java ++++ b/src/main/java/net/minecraft/world/phys/shapes/EntityCollisionContext.java +@@ -21,47 +21,68 @@ public class EntityCollisionContext implements CollisionContext { + return defaultValue; + } + }; ++ // 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; ++ */ + @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; ++ */ + this.entity = entity; + } + + /** @deprecated */ + @Deprecated + protected EntityCollisionContext(Entity entity) { ++ /* + this(entity.isDescending(), entity.getY(), entity instanceof LivingEntity ? ((LivingEntity)entity).getMainHandItem() : ItemStack.EMPTY, entity instanceof LivingEntity ? ((LivingEntity)entity)::canStandOnFluid : (fluid) -> { + return false; + }, entity); ++ */ ++ this.entity = entity; + } ++ // Pufferfish end + + @Override + public boolean isHoldingItem(Item item) { +- return this.heldItem.is(item); ++ // Pufferfish start ++ Entity entity = this.entity; ++ if (entity instanceof LivingEntity livingEntity) { ++ return livingEntity.getMainHandItem().is(item); ++ } ++ return ItemStack.EMPTY.is(item); ++ // Pufferfish end + } + + @Override + public boolean canStandOnFluid(FluidState state, FlowingFluid fluid) { +- return this.canStandOnFluid.test(fluid) && !state.getType().isSame(fluid); ++ // Pufferfish start ++ Entity entity = this.entity; ++ if (entity instanceof LivingEntity livingEntity) { ++ return livingEntity.canStandOnFluid(fluid) && !state.getType().isSame(fluid); ++ } ++ return false; ++ // Pufferfish end + } + + @Override + public boolean isDescending() { +- return this.descending; ++ return this.entity != null && this.entity.isDescending(); // Pufferfish + } + + @Override + public boolean isAbove(VoxelShape shape, BlockPos pos, boolean defaultValue) { +- return this.entityBottom > (double)pos.getY() + shape.max(Direction.Axis.Y) - (double)1.0E-5F; ++ return (this.entity == null ? -Double.MAX_VALUE : entity.getY()) > (double)pos.getY() + shape.max(Direction.Axis.Y) - (double)1.0E-5F; // Pufferfish + } + + @Nullable +diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java +index ba7023e7ca5d29375ff53c2951892138d155f69f..041ebfefca8f944f457a269f0e0b450b21949e73 100644 +--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java ++++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java +@@ -254,7 +254,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"); +@@ -1063,6 +1063,11 @@ public final class CraftServer implements Server { + plugin.getDescription().getName(), + "This plugin is not properly shutting down its async tasks when it is being shut down. This task may throw errors during the final shutdown logs and might not complete before process dies." + )); ++ getLogger().log(Level.SEVERE, String.format("%s Stacktrace", worker.getThread().getName())); ++ StackTraceElement[] stackTrace = worker.getThread().getStackTrace(); ++ for (StackTraceElement element : stackTrace) { ++ getLogger().log(Level.SEVERE, " " + element.toString()); ++ } + } + } + // Paper end +diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftShapelessRecipe.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftShapelessRecipe.java +index 0b3b46348ac9195bff1492ffc11fcbff7d3f5c6f..4010052c53f3a2831b4d5aa1c041d85897856acb 100644 +--- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftShapelessRecipe.java ++++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftShapelessRecipe.java +@@ -43,6 +43,6 @@ public class CraftShapelessRecipe extends ShapelessRecipe implements CraftRecipe + data.set(i, toNMS(ingred.get(i), true)); + } + +- MinecraftServer.getServer().getRecipeManager().addRecipe(new net.minecraft.world.item.crafting.ShapelessRecipe(CraftNamespacedKey.toMinecraft(this.getKey()), this.getGroup(), CraftItemStack.asNMSCopy(this.getResult()), data)); ++ MinecraftServer.getServer().getRecipeManager().addRecipe(new net.minecraft.world.item.crafting.ShapelessRecipe(CraftNamespacedKey.toMinecraft(this.getKey()), this.getGroup(), CraftItemStack.asNMSCopy(this.getResult()), data, true)); + } + } +diff --git a/src/main/java/org/bukkit/craftbukkit/scheduler/MinecraftInternalPlugin.java b/src/main/java/org/bukkit/craftbukkit/scheduler/MinecraftInternalPlugin.java +index 909b2c98e7a9117d2f737245e4661792ffafb744..0d9e2b3728f9ab500bd5e44702718535d338e9a5 100644 +--- a/src/main/java/org/bukkit/craftbukkit/scheduler/MinecraftInternalPlugin.java ++++ b/src/main/java/org/bukkit/craftbukkit/scheduler/MinecraftInternalPlugin.java +@@ -22,7 +22,8 @@ public class MinecraftInternalPlugin extends PluginBase { + private boolean enabled = true; + + private final String pluginName; +- private PluginDescriptionFile pdf; ++ private org.bukkit.plugin.PluginLogger logger; ++ private PluginDescriptionFile pdf; // Pufferfish + + public MinecraftInternalPlugin() { + this.pluginName = "Minecraft"; +@@ -75,7 +76,12 @@ public class MinecraftInternalPlugin extends PluginBase { + + @Override + public PluginLogger getLogger() { +- throw new UnsupportedOperationException("Not supported."); ++ // Pufferfish start ++ if (this.logger == null) { ++ this.logger = new org.bukkit.plugin.PluginLogger(this); // Pufferfish ++ } ++ return this.logger; ++ // Pufferfish end + } + + @Override +@@ -85,7 +91,7 @@ public class MinecraftInternalPlugin extends PluginBase { + + @Override + public Server getServer() { +- throw new UnsupportedOperationException("Not supported."); ++ return org.bukkit.Bukkit.getServer(); // Pufferfish - impl + } + + @Override +diff --git a/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java b/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java +index 4be7b18b4e794734439d1e8bec4304d88328c91e..98d396658bd7547e6b6f850529cab216e83c6c62 100644 +--- a/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java ++++ b/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java +@@ -421,7 +421,7 @@ public final class CraftMagicNumbers implements UnsafeValues { + + @Override + public com.destroystokyo.paper.util.VersionFetcher getVersionFetcher() { +- return new com.destroystokyo.paper.PaperVersionFetcher(); ++ return new gg.pufferfish.pufferfish.PufferfishVersionFetcher(); // Pufferfish + } + + @Override +diff --git a/src/main/java/org/bukkit/craftbukkit/util/ServerShutdownThread.java b/src/main/java/org/bukkit/craftbukkit/util/ServerShutdownThread.java +index d752720f2f234b9dbd2117333fee1bfad663ec02..f1be8a98c49a63d09c838a85eb58041733f71776 100644 +--- a/src/main/java/org/bukkit/craftbukkit/util/ServerShutdownThread.java ++++ b/src/main/java/org/bukkit/craftbukkit/util/ServerShutdownThread.java +@@ -11,6 +11,7 @@ public class ServerShutdownThread extends Thread { + + @Override + public void run() { ++ try { gg.pufferfish.pufferfish.flare.ProfilingManager.stop(); } catch (Throwable t) {} // Pufferfish - shut down Flare if it's running + try { + // Paper start - try to shutdown on main + server.safeShutdown(false, false); +diff --git a/src/main/java/org/bukkit/craftbukkit/util/Versioning.java b/src/main/java/org/bukkit/craftbukkit/util/Versioning.java +index 774556a62eb240da42e84db4502e2ed43495be17..80553face9c70c2a3d897681e7761df85b22d464 100644 +--- a/src/main/java/org/bukkit/craftbukkit/util/Versioning.java ++++ b/src/main/java/org/bukkit/craftbukkit/util/Versioning.java +@@ -11,7 +11,7 @@ public final class Versioning { + public static String getBukkitVersion() { + String result = "Unknown-Version"; + +- InputStream stream = Bukkit.class.getClassLoader().getResourceAsStream("META-INF/maven/io.papermc.paper/paper-api/pom.properties"); ++ InputStream stream = Bukkit.class.getClassLoader().getResourceAsStream("META-INF/maven/gg.pufferfish.pufferfish/pufferfish-api/pom.properties"); // Pufferfish + Properties properties = new Properties(); + + if (stream != null) { +diff --git a/src/main/java/org/spigotmc/ActivationRange.java b/src/main/java/org/spigotmc/ActivationRange.java +index 1732e753ea7748a66abecafb2136f93383f5ec4e..2eb9bc34afdc26bed21bc9d3e97f636cfc97f166 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 + { +@@ -211,6 +215,21 @@ public class ActivationRange + for (int i = 0; i < entities.size(); i++) { + Entity entity = entities.get(i); + ActivationRange.activateEntity(entity); ++ ++ // Pufferfish start ++ if (gg.pufferfish.pufferfish.PufferfishConfig.dearEnabled && entity.getType().dabEnabled) { ++ Vec3 playerVec = player.position(); ++ Vec3 entityVec = entity.position(); ++ double diffX = playerVec.x - entityVec.x, diffY = playerVec.y - entityVec.y, diffZ = playerVec.z - entityVec.z; ++ int squaredDistance = (int) (diffX * diffX + diffY * diffY + diffZ * diffZ); ++ entity.activatedPriority = squaredDistance > gg.pufferfish.pufferfish.PufferfishConfig.startDistanceSquared ? ++ Math.max(1, Math.min(squaredDistance >> gg.pufferfish.pufferfish.PufferfishConfig.activationDistanceMod, gg.pufferfish.pufferfish.PufferfishConfig.maximumActivationPrio)) : ++ 1; ++ } else { ++ entity.activatedPriority = 1; ++ } ++ // Pufferfish end ++ + } + // Paper end + } +@@ -227,12 +246,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; + } + } +@@ -280,7 +299,7 @@ public class ActivationRange + if ( entity instanceof LivingEntity ) + { + LivingEntity living = (LivingEntity) entity; +- if ( living.onClimbable() || living.jumping || living.hurtTime > 0 || living.activeEffects.size() > 0 ) // Paper ++ if ( living.onClimableCached() || living.jumping || living.hurtTime > 0 || living.activeEffects.size() > 0 ) // Paper // Pufferfish - use cached + { + return 1; // Paper + } diff --git a/patches/server/0002-Rebrand.patch b/patches/server/0002-Rebrand.patch index c51d117d1..fac1eddb2 100644 --- a/patches/server/0002-Rebrand.patch +++ b/patches/server/0002-Rebrand.patch @@ -5,43 +5,37 @@ Subject: [PATCH] Rebrand diff --git a/build.gradle.kts b/build.gradle.kts -index da31e84cb558e6fad9cab015cfae753ce7be7db0..07f572b9aa249e154bc08ecf8517c3953392b74f 100644 +index 0f194168f5fca8eb768bca3ce953f28097fed578..1bdcca4d1a25f4292ab9421d44fa3018af263341 100644 --- a/build.gradle.kts +++ b/build.gradle.kts -@@ -18,8 +18,12 @@ repositories { +@@ -18,7 +18,7 @@ repositories { } dependencies { -- implementation(project(":paper-api")) -- implementation(project(":paper-mojangapi")) -+ // Purpur start -+ implementation(project(":purpur-api")) -+ implementation("io.papermc.paper:paper-mojangapi:1.18.1-R0.1-SNAPSHOT") { -+ exclude("io.papermc.paper", "paper-api") -+ } -+ // Purpur end - // Paper start - implementation("org.jline:jline-terminal-jansi:3.21.0") - implementation("net.minecrell:terminalconsoleappender:1.3.0") -@@ -50,6 +54,8 @@ dependencies { +- implementation(project(":pufferfish-api")) // Pufferfish // Paper ++ implementation(project(":purpur-api")) // Pufferfish // Paper // Purpur + // Pufferfish start + implementation("io.papermc.paper:paper-mojangapi:1.18.1-R0.1-SNAPSHOT") { + exclude("io.papermc.paper", "paper-api") +@@ -54,6 +54,8 @@ dependencies { isTransitive = false } // Paper end + implementation("cat.inspiracio:rhino-js-engine:1.7.7.1") // Purpur + implementation("dev.omega24:upnp4j:1.0") // Purpur - testImplementation("io.github.classgraph:classgraph:4.8.47") // Paper - mob goal test - testImplementation("junit:junit:4.13.2") -@@ -68,7 +74,7 @@ tasks.jar { + // Pufferfish start + implementation("org.yaml:snakeyaml:1.28") +@@ -80,7 +82,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-Purpur-$implementationVersion", // Purpur +- "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, -@@ -156,7 +162,7 @@ fun TaskContainer.registerRunTask( +@@ -168,7 +170,7 @@ fun TaskContainer.registerRunTask( name: String, block: JavaExec.() -> Unit ): TaskProvider = register(name) { @@ -116,23 +110,23 @@ index e0b1f0671d16ddddcb6725acd25a1d1d69e42701..8c3c68465197fafc14849dc38a572e30 .completer(new ConsoleCommandCompleter(this.server)) .option(LineReader.Option.COMPLETE_IN_WORD, true); diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java -index 481a5dbad82f3f8dd5b1bf8ab207d82ec73d5bbd..7ad6b7b3094156477570b738026d88cb0f0f4f9e 100644 +index 1e7ae166a1d53fce28297e2eaebd11ec28a57a55..f0e7f8d4eb632d7c7effcd3d85d67baf1bf9eb41 100644 --- a/src/main/java/net/minecraft/server/MinecraftServer.java +++ b/src/main/java/net/minecraft/server/MinecraftServer.java -@@ -1709,7 +1709,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop // Spigot - Spigot > // CraftBukkit - cb > vanilla! -+ return "Purpur"; // Purpur - Purpur > // Paper - Paper > // Spigot - Spigot > // CraftBukkit - cb > vanilla! +- return "Pufferfish"; // Pufferfish - Pufferfish > // Paper - Paper > // Spigot - Spigot > // CraftBukkit - cb > vanilla! ++ return "Purpur"; // Purpur - Purpur > // Pufferfish - Pufferfish > // Paper - Paper > // Spigot - Spigot > // CraftBukkit - cb > vanilla! } public SystemReport fillSystemReport(SystemReport details) { diff --git a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java -index 38a0fb9a7c4ade9cacfd30dffabfea7e6b773981..fd3d3e22613511a42f7fc343079b081c72ff3f61 100644 +index aae2dadabedb075fd1bc712f0226882b85f4551d..9c31e5d653f2027cf5b028f9fb9268f1469cb1e0 100644 --- a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java +++ b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java -@@ -290,11 +290,12 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface +@@ -292,11 +292,12 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface DedicatedServer.LOGGER.warn("**** SERVER IS RUNNING IN OFFLINE/INSECURE MODE!"); DedicatedServer.LOGGER.warn("The server will make no attempt to authenticate usernames. Beware."); // Spigot start @@ -148,28 +142,41 @@ index 38a0fb9a7c4ade9cacfd30dffabfea7e6b773981..fd3d3e22613511a42f7fc343079b081c // Spigot end DedicatedServer.LOGGER.warn("To change this, set \"online-mode\" to \"true\" in the server.properties file."); diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java -index ba7023e7ca5d29375ff53c2951892138d155f69f..c94053599f30f61a67c20d61b41963ce1326db07 100644 +index 041ebfefca8f944f457a269f0e0b450b21949e73..3ccb381b11cec8dcd8c23de3efaa3a27902a5ac5 100644 --- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java +++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java @@ -254,7 +254,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 = "Purpur"; // Paper // Purpur +- 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(); private final Logger logger = Logger.getLogger("Minecraft"); +diff --git a/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java b/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java +index 98d396658bd7547e6b6f850529cab216e83c6c62..5687dbbb3f1df46a453841c24a343773c801bf73 100644 +--- a/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java ++++ b/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java +@@ -421,7 +421,7 @@ public final class CraftMagicNumbers implements UnsafeValues { + + @Override + public com.destroystokyo.paper.util.VersionFetcher getVersionFetcher() { +- 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..fb87620c742ff7912f5e8ccd2a7930dd605576d9 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/org.purpurmc.purpur/purpur-api/pom.properties"); // Purpur +- 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(); if (stream != null) { diff --git a/patches/server/0003-Purpur-config-files.patch b/patches/server/0003-Purpur-config-files.patch index 2d864f790..5e7355715 100644 --- a/patches/server/0003-Purpur-config-files.patch +++ b/patches/server/0003-Purpur-config-files.patch @@ -5,25 +5,24 @@ Subject: [PATCH] Purpur config files diff --git a/src/main/java/com/destroystokyo/paper/Metrics.java b/src/main/java/com/destroystokyo/paper/Metrics.java -index 218f5bafeed8551b55b91c7fccaf6935c8b631ca..7bc497bcae6a6a752e3c432178cb1e3c633e0bec 100644 +index f2fe6ea3719ff8b2913b7a3a939d7a5b75cb8b28..ed13b7b3f9d2a0b644d17d1019d42cbc4bbe72f7 100644 --- a/src/main/java/com/destroystokyo/paper/Metrics.java +++ b/src/main/java/com/destroystokyo/paper/Metrics.java -@@ -593,8 +593,7 @@ public class Metrics { +@@ -593,7 +593,7 @@ public class Metrics { boolean logFailedRequests = config.getBoolean("logFailedRequests", false); // Only start Metrics, if it's enabled in the config if (config.getBoolean("enabled", true)) { -- Metrics metrics = new Metrics("Paper", serverUUID, logFailedRequests, Bukkit.getLogger()); -- -+ Metrics metrics = new Metrics("Purpur", serverUUID, logFailedRequests, Bukkit.getLogger()); // Purpur +- 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", () -> { String minecraftVersion = Bukkit.getVersion(); - minecraftVersion = minecraftVersion.substring(minecraftVersion.indexOf("MC: ") + 4, minecraftVersion.length() - 1); -@@ -602,8 +601,8 @@ public class Metrics { +@@ -602,8 +602,8 @@ public class Metrics { })); metrics.addCustomChart(new Metrics.SingleLineChart("players", () -> Bukkit.getOnlinePlayers().size())); - metrics.addCustomChart(new Metrics.SimplePie("online_mode", () -> Bukkit.getOnlineMode() ? "online" : "offline")); -- metrics.addCustomChart(new Metrics.SimplePie("paper_version", () -> (Metrics.class.getPackage().getImplementationVersion() != null) ? Metrics.class.getPackage().getImplementationVersion() : "unknown")); +- metrics.addCustomChart(new Metrics.SimplePie("pufferfish_version", () -> (Metrics.class.getPackage().getImplementationVersion() != null) ? Metrics.class.getPackage().getImplementationVersion() : "unknown")); + metrics.addCustomChart(new Metrics.SimplePie("online_mode", () -> Bukkit.getOnlineMode() ? "online" : (PaperConfig.isProxyOnlineMode() ? "bungee" : "offline"))); // Purpur + metrics.addCustomChart(new Metrics.SimplePie("purpur_version", () -> (Metrics.class.getPackage().getImplementationVersion() != null) ? Metrics.class.getPackage().getImplementationVersion() : "unknown")); // Purpur @@ -65,7 +64,7 @@ index 098182d2426a25cef0bc285356bc346db0af8172..c26435a6b5cdc61ae5d123dacea711a4 if (this.source.acceptsSuccess() && !this.silent) { this.source.sendMessage(message, Util.NIL_UUID); diff --git a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java -index fd3d3e22613511a42f7fc343079b081c72ff3f61..8bf98d8f8e9f0baaf203a9e8d18683236856eab7 100644 +index 9c31e5d653f2027cf5b028f9fb9268f1469cb1e0..d58e5357f9cb45d9b5ddeaed4ec76aa388fc34d1 100644 --- a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java +++ b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java @@ -228,6 +228,15 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface @@ -85,7 +84,7 @@ index fd3d3e22613511a42f7fc343079b081c72ff3f61..8bf98d8f8e9f0baaf203a9e8d1868323 io.papermc.paper.util.ObfHelper.INSTANCE.getClass(); // load mappings for stacktrace deobf and etc. io.papermc.paper.brigadier.PaperBrigadierProviderImpl.INSTANCE.getClass(); // init PaperBrigadierProvider diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java -index 4247dcb003626535dbb997f48ad9f61380bd17e9..76faf838e27813595850ebb8c9a028f1c68325ad 100644 +index 90aa1d75b5c23e5ee27ceae9f6ef90de913a6601..37884e05689288d56ef0efe92296b80ef6093b3c 100644 --- a/src/main/java/net/minecraft/world/level/Level.java +++ b/src/main/java/net/minecraft/world/level/Level.java @@ -169,6 +169,8 @@ public abstract class Level implements LevelAccessor, AutoCloseable { @@ -97,7 +96,7 @@ index 4247dcb003626535dbb997f48ad9f61380bd17e9..76faf838e27813595850ebb8c9a028f1 public final co.aikar.timings.WorldTimingsHandler timings; // Paper public static BlockPos lastPhysicsProblem; // Spigot private org.spigotmc.TickLimiter entityLimiter; -@@ -311,6 +313,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable { +@@ -320,6 +322,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable { protected Level(WritableLevelData worlddatamutable, ResourceKey resourcekey, final DimensionType dimensionmanager, Supplier supplier, boolean flag, boolean flag1, long i, org.bukkit.generator.ChunkGenerator gen, org.bukkit.generator.BiomeProvider biomeProvider, org.bukkit.World.Environment env, java.util.concurrent.Executor executor) { // Paper - Async-Anti-Xray - Pass executor this.spigotConfig = new org.spigotmc.SpigotWorldConfig(((net.minecraft.world.level.storage.PrimaryLevelData) worlddatamutable).getLevelName()); // Spigot this.paperConfig = new com.destroystokyo.paper.PaperWorldConfig(((net.minecraft.world.level.storage.PrimaryLevelData) worlddatamutable).getLevelName(), this.spigotConfig); // Paper @@ -106,7 +105,7 @@ index 4247dcb003626535dbb997f48ad9f61380bd17e9..76faf838e27813595850ebb8c9a028f1 this.world = new CraftWorld((ServerLevel) this, gen, biomeProvider, env); this.ticksPerAnimalSpawns = this.getCraftServer().getTicksPerAnimalSpawns(); // CraftBukkit diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java -index c94053599f30f61a67c20d61b41963ce1326db07..df6eec75dc80f18b7804d1b5a181b4d5fd573abb 100644 +index 3ccb381b11cec8dcd8c23de3efaa3a27902a5ac5..f69516d01a634a08b75744bfe26e5d9f6831c8f3 100644 --- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java +++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java @@ -952,6 +952,7 @@ public final class CraftServer implements Server { @@ -133,7 +132,7 @@ index c94053599f30f61a67c20d61b41963ce1326db07..df6eec75dc80f18b7804d1b5a181b4d5 this.overrideAllCommandBlockCommands = this.commandsConfiguration.getStringList("command-block-overrides").contains("*"); this.ignoreVanillaPermissions = this.commandsConfiguration.getBoolean("ignore-vanilla-permissions"); -@@ -2662,6 +2665,18 @@ public final class CraftServer implements Server { +@@ -2667,6 +2670,18 @@ public final class CraftServer implements Server { return com.destroystokyo.paper.PaperConfig.config; } diff --git a/patches/server/0004-Purpur-client-support.patch b/patches/server/0004-Purpur-client-support.patch index 302510350..a2a95193c 100644 --- a/patches/server/0004-Purpur-client-support.patch +++ b/patches/server/0004-Purpur-client-support.patch @@ -17,10 +17,10 @@ index 7b23535a680d2a8534dcb8dd87770f66fb982c13..4b842e78d3e8fbca90f5f3d975bee654 public double lastEntitySpawnRadiusSquared; // Paper - optimise isOutsideRange, this field is in blocks public final com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet cachedSingleHashSet; // Paper diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -index 926d0a80cbb55184955ac6720948d2e86683cc57..7842e8098f044f20bee462a3c1c9ca8553b77e6a 100644 +index 889a4804eaa128e2f79a4ea48b8aa796336576d1..f020ec3974c9d8de8afd42bb33065350365c4687 100644 --- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java +++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -@@ -3142,6 +3142,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Ser +@@ -3143,6 +3143,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Ser private static final ResourceLocation CUSTOM_UNREGISTER = new ResourceLocation("unregister"); private static final ResourceLocation MINECRAFT_BRAND = new ResourceLocation("brand"); // Paper - Brand support @@ -28,7 +28,7 @@ index 926d0a80cbb55184955ac6720948d2e86683cc57..7842e8098f044f20bee462a3c1c9ca85 @Override public void handleCustomPayload(ServerboundCustomPayloadPacket packet) { -@@ -3166,6 +3167,13 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Ser +@@ -3167,6 +3168,13 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Ser ServerGamePacketListenerImpl.LOGGER.error("Couldn\'t unregister custom payload", ex); this.disconnect("Invalid payload UNREGISTER!", org.bukkit.event.player.PlayerKickEvent.Cause.INVALID_PAYLOAD); // Paper - kick event cause } diff --git a/patches/server/0005-Component-related-conveniences.patch b/patches/server/0005-Component-related-conveniences.patch index 717ea0d08..7cda4ed81 100644 --- a/patches/server/0005-Component-related-conveniences.patch +++ b/patches/server/0005-Component-related-conveniences.patch @@ -36,7 +36,7 @@ index 4b842e78d3e8fbca90f5f3d975bee654ec87d91d..c516ad43cac2419bcf63e11eaa183785 public void displayClientMessage(Component message, boolean actionBar) { this.sendMessage(message, actionBar ? ChatType.GAME_INFO : ChatType.CHAT, Util.NIL_UUID); diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java -index 042be2cf60a9d01698808d84f2e537a5eb952079..6ec5a8a52ec06be917f2ca682f5920ed5ae1a82c 100644 +index f55f8c0b2c748a5442199c0a7f772b02ed533753..e07dd4a0d0918d306512487ee7e989c8b39a7222 100644 --- a/src/main/java/net/minecraft/server/players/PlayerList.java +++ b/src/main/java/net/minecraft/server/players/PlayerList.java @@ -1373,6 +1373,62 @@ public abstract class PlayerList { @@ -123,10 +123,10 @@ index a828cad27fcd39f8bfbaefa97052a2a3b6650ee7..19d6165f266fcc39bc3533042109ef1b return this.isFireSource; } diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java -index 8ea81f6ac7503c68f0aea34802843bc545f46db0..605a0e9cf68a4f698ac03b3477585861aeb354c7 100644 +index 335ac54bef892db6b9fe66d0a7c3966abda84350..957407d5fa39445c63d621bd9eafb526bcbf6bce 100644 --- a/src/main/java/net/minecraft/world/entity/Entity.java +++ b/src/main/java/net/minecraft/world/entity/Entity.java -@@ -3608,6 +3608,34 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, i +@@ -3635,6 +3635,34 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, i return SlotAccess.NULL; } diff --git a/patches/server/0006-Ridables.patch b/patches/server/0006-Ridables.patch index 1ab788542..d69d5a494 100644 --- a/patches/server/0006-Ridables.patch +++ b/patches/server/0006-Ridables.patch @@ -22,10 +22,10 @@ index 14610e6144ec144ebbec6fb0945c67bb0ea86795..6833eb5dc7aa64bef1b3b38de5e282bd super(x, y, z); } diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java -index 7ad6b7b3094156477570b738026d88cb0f0f4f9e..973ea150c2f8ac136736b4ae9f505491e2fc80c2 100644 +index f0e7f8d4eb632d7c7effcd3d85d67baf1bf9eb41..a8a711c96a38c3d1c9d9b86418d389d5fa81ca29 100644 --- a/src/main/java/net/minecraft/server/MinecraftServer.java +++ b/src/main/java/net/minecraft/server/MinecraftServer.java -@@ -1587,6 +1587,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop 0; // Paper worldserver.hasEntityMoveEvent = io.papermc.paper.event.entity.EntityMoveEvent.getHandlerList().getRegisteredListeners().length > 0; // Paper @@ -34,7 +34,7 @@ index 7ad6b7b3094156477570b738026d88cb0f0f4f9e..973ea150c2f8ac136736b4ae9f505491 this.profiler.push(() -> { diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java -index 28f605c3daa969c1a54745e552d55ecb874120a9..f7cfc6d5ad13dba64c2350fe3fac7f31471e8642 100644 +index c7229e120d601619d2ea869d7aa506fb3b78cde4..41c9033b3e4fc9574793f670df3bb90c395ac991 100644 --- a/src/main/java/net/minecraft/server/level/ServerLevel.java +++ b/src/main/java/net/minecraft/server/level/ServerLevel.java @@ -211,6 +211,7 @@ public class ServerLevel extends Level implements WorldGenLevel { @@ -73,10 +73,10 @@ index c516ad43cac2419bcf63e11eaa183785b462de5c..618595add09eef5381307ba2fe154adf + } diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -index 7842e8098f044f20bee462a3c1c9ca8553b77e6a..1b50ee29ab8721807745e052e19c67e7db6cfd18 100644 +index f020ec3974c9d8de8afd42bb33065350365c4687..2d1897d84f7f1f6265a5c31ec2666af7019010c9 100644 --- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java +++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -@@ -2453,6 +2453,8 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Ser +@@ -2454,6 +2454,8 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Ser ServerGamePacketListenerImpl.this.cserver.getPluginManager().callEvent(event); @@ -86,7 +86,7 @@ index 7842e8098f044f20bee462a3c1c9ca8553b77e6a..1b50ee29ab8721807745e052e19c67e7 if ((entity instanceof Bucketable && entity instanceof LivingEntity && origItem != null && origItem.asItem() == Items.WATER_BUCKET) && (event.isCancelled() || ServerGamePacketListenerImpl.this.player.getInventory().getSelected() == null || ServerGamePacketListenerImpl.this.player.getInventory().getSelected().getItem() != origItem)) { ServerGamePacketListenerImpl.this.send(new ClientboundAddMobPacket((LivingEntity) entity)); diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java -index 605a0e9cf68a4f698ac03b3477585861aeb354c7..43697061ddbbdf160453d3ab189097a6a56e6246 100644 +index 957407d5fa39445c63d621bd9eafb526bcbf6bce..905e54d53f7a209f538da1500df0d3dfa6e8c7ed 100644 --- a/src/main/java/net/minecraft/world/entity/Entity.java +++ b/src/main/java/net/minecraft/world/entity/Entity.java @@ -292,7 +292,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, i @@ -98,7 +98,7 @@ index 605a0e9cf68a4f698ac03b3477585861aeb354c7..43697061ddbbdf160453d3ab189097a6 private float eyeHeight; public boolean isInPowderSnow; public boolean wasInPowderSnow; -@@ -2616,6 +2616,12 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, i +@@ -2643,6 +2643,12 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, i this.passengers = ImmutableList.copyOf(list); } @@ -111,7 +111,7 @@ index 605a0e9cf68a4f698ac03b3477585861aeb354c7..43697061ddbbdf160453d3ab189097a6 } return true; // CraftBukkit } -@@ -2656,6 +2662,14 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, i +@@ -2683,6 +2689,14 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, i return false; } // Spigot end @@ -126,7 +126,7 @@ index 605a0e9cf68a4f698ac03b3477585861aeb354c7..43697061ddbbdf160453d3ab189097a6 if (this.passengers.size() == 1 && this.passengers.get(0) == entity) { this.passengers = ImmutableList.of(); } else { -@@ -4288,4 +4302,41 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, i +@@ -4367,4 +4381,41 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, i return ((ServerChunkCache) level.getChunkSource()).isPositionTicking(this); } // Paper end @@ -192,10 +192,10 @@ index 645c1dc9bd09b135a641759c76ce8d957b9bd488..03adc3b746e05bb4b0514ba4a66c101b 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 43378561cf48f969f5bf1fd0db349415f4d1c866..b43623d0d0dce7ca09b83e9bc20fb75c4a971263 100644 +index 2c988e05299d530d483e21ab452de0a03e5bdcbd..46134547b26de59bb69c584df676bf09e756420e 100644 --- a/src/main/java/net/minecraft/world/entity/LivingEntity.java +++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java -@@ -217,9 +217,9 @@ public abstract class LivingEntity extends Entity { +@@ -216,9 +216,9 @@ public abstract class LivingEntity extends Entity { protected int deathScore; public float lastHurt; public boolean jumping; @@ -208,7 +208,7 @@ index 43378561cf48f969f5bf1fd0db349415f4d1c866..b43623d0d0dce7ca09b83e9bc20fb75c protected int lerpSteps; protected double lerpX; protected double lerpY; -@@ -284,7 +284,7 @@ public abstract class LivingEntity extends Entity { +@@ -283,7 +283,7 @@ public abstract class LivingEntity extends Entity { this.effectsDirty = true; this.useItem = ItemStack.EMPTY; this.lastClimbablePos = Optional.empty(); @@ -217,7 +217,7 @@ index 43378561cf48f969f5bf1fd0db349415f4d1c866..b43623d0d0dce7ca09b83e9bc20fb75c 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()); -@@ -335,6 +335,7 @@ public abstract class LivingEntity extends Entity { +@@ -334,6 +334,7 @@ public abstract class LivingEntity extends Entity { 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); } @@ -225,7 +225,7 @@ index 43378561cf48f969f5bf1fd0db349415f4d1c866..b43623d0d0dce7ca09b83e9bc20fb75c @Override protected void checkFallDamage(double heightDifference, boolean onGround, BlockState landedState, BlockPos landedPosition) { -@@ -2585,7 +2586,7 @@ public abstract class LivingEntity extends Entity { +@@ -2606,7 +2607,7 @@ public abstract class LivingEntity extends Entity { } protected long lastJumpTime = 0L; // Paper @@ -234,7 +234,7 @@ index 43378561cf48f969f5bf1fd0db349415f4d1c866..b43623d0d0dce7ca09b83e9bc20fb75c double d0 = (double) this.getJumpPower() + this.getJumpBoostPower(); Vec3 vec3d = this.getDeltaMovement(); // Paper start -@@ -3327,8 +3328,10 @@ public abstract class LivingEntity extends Entity { +@@ -3348,8 +3349,10 @@ public abstract class LivingEntity extends Entity { this.pushEntities(); this.level.getProfiler().pop(); // Paper start @@ -247,7 +247,7 @@ index 43378561cf48f969f5bf1fd0db349415f4d1c866..b43623d0d0dce7ca09b83e9bc20fb75c 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()); -@@ -3338,6 +3341,21 @@ public abstract class LivingEntity extends Entity { +@@ -3359,6 +3362,21 @@ public abstract class LivingEntity extends Entity { absMoveTo(event.getTo().getX(), event.getTo().getY(), event.getTo().getZ(), event.getTo().getYaw(), event.getTo().getPitch()); } } @@ -270,7 +270,7 @@ index 43378561cf48f969f5bf1fd0db349415f4d1c866..b43623d0d0dce7ca09b83e9bc20fb75c // Paper end if (!this.level.isClientSide && this.isSensitiveToWater() && this.isInWaterRainOrBubble()) { diff --git a/src/main/java/net/minecraft/world/entity/Mob.java b/src/main/java/net/minecraft/world/entity/Mob.java -index 031660f7d0ea270f87e5174a4fe65ccad1f7a561..2266fc8c5c0f39de9122727d43dd12eef199cb70 100644 +index 607e78c24edabb26fd7494855087f7f19cfdc576..65e7ce5cb473733b6a4ec89998f4b14053c62c42 100644 --- a/src/main/java/net/minecraft/world/entity/Mob.java +++ b/src/main/java/net/minecraft/world/entity/Mob.java @@ -143,6 +143,8 @@ public abstract class Mob extends LivingEntity { @@ -282,7 +282,7 @@ index 031660f7d0ea270f87e5174a4fe65ccad1f7a561..2266fc8c5c0f39de9122727d43dd12ee this.jumpControl = new JumpControl(this); this.bodyRotationControl = this.createBodyControl(); this.navigation = this.createNavigation(world); -@@ -1300,7 +1302,7 @@ public abstract class Mob extends LivingEntity { +@@ -1304,7 +1306,7 @@ public abstract class Mob extends LivingEntity { protected void onOffspringSpawnedFromEgg(Player player, Mob child) {} protected InteractionResult mobInteract(Player player, InteractionHand hand) { @@ -291,7 +291,7 @@ index 031660f7d0ea270f87e5174a4fe65ccad1f7a561..2266fc8c5c0f39de9122727d43dd12ee } public boolean isWithinRestriction() { -@@ -1661,4 +1663,52 @@ public abstract class Mob extends LivingEntity { +@@ -1665,4 +1667,52 @@ public abstract class Mob extends LivingEntity { return itemmonsteregg == null ? null : new ItemStack(itemmonsteregg); } @@ -345,13 +345,13 @@ index 031660f7d0ea270f87e5174a4fe65ccad1f7a561..2266fc8c5c0f39de9122727d43dd12ee + // Purpur end } diff --git a/src/main/java/net/minecraft/world/entity/ai/attributes/AttributeMap.java b/src/main/java/net/minecraft/world/entity/ai/attributes/AttributeMap.java -index 9cbfda029782385d1a7987f5be46d450bd8a758e..718b3fbf5b92d9979de7775e1d40420bb7cf439b 100644 +index 52ddf38d5a1894a248e142c6b9ebaff5cc921e5b..4fa1e8dd7ebc839527eed7f7db5af2c56c38f230 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 attributes = Maps.newHashMap(); +@@ -23,14 +23,21 @@ public class AttributeMap { private final Set dirtyAttributes = Sets.newHashSet(); private final AttributeSupplier supplier; + private final java.util.function.Function createInstance; // Pufferfish + private final net.minecraft.world.entity.LivingEntity entity; // Purpur public AttributeMap(AttributeSupplier defaultAttributes) { @@ -362,6 +362,7 @@ index 9cbfda029782385d1a7987f5be46d450bd8a758e..718b3fbf5b92d9979de7775e1d40420b + this.entity = entity; + // Purpur end this.supplier = defaultAttributes; + this.createInstance = attribute -> this.supplier.createInstance(this::onAttributeModified, attribute); // Pufferfish } private void onAttributeModified(AttributeInstance instance) { @@ -370,7 +371,7 @@ index 9cbfda029782385d1a7987f5be46d450bd8a758e..718b3fbf5b92d9979de7775e1d40420b this.dirtyAttributes.add(instance); } -@@ -40,7 +47,7 @@ public class AttributeMap { +@@ -42,7 +49,7 @@ public class AttributeMap { public Collection getSyncableAttributes() { return this.attributes.values().stream().filter((attribute) -> { @@ -531,7 +532,7 @@ index 7df56705a4a0de2dc4ff7ab133fc26612c219162..60d21d6171b9af20a4c6fcc0d564a31a } 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 29dfbcecfbb2560e6ecde997abd5224a16c08c94..8a6608310caf1a9046fba4e51ca0eff92c8177fc 100644 +index a76b754305036bf9c6387e27b86ec1559b290774..76de99e019c784609dfcafb35b9c4c9d21f8d6cf 100644 --- a/src/main/java/net/minecraft/world/entity/ambient/Bat.java +++ b/src/main/java/net/minecraft/world/entity/ambient/Bat.java @@ -19,6 +19,7 @@ import net.minecraft.world.entity.EntityDimensions; @@ -2058,7 +2059,7 @@ index 4797c34872f8fa2fba00172357c4f45e45cfe27f..4187d8fdb950781de8b414ed6928a3df this.targetSelector.addGoal(2, new OwnerHurtTargetGoal(this)); this.targetSelector.addGoal(3, (new HurtByTargetGoal(this, new Class[0])).setAlertOthers()); 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 86acf89ce875e215da8469947b382f70e42314b0..09a3ed5cd02e38eab3a66ada03b2651b8cbe1751 100644 +index 0fe2d6190ebba24713a3da7ef5367ecb86814a66..7f535683dcc2ad9908400d98328174baf1ea5a91 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 @@ -91,6 +91,23 @@ public class Axolotl extends Animal implements LerpingModel, Bucketable { @@ -2085,7 +2086,7 @@ index 86acf89ce875e215da8469947b382f70e42314b0..09a3ed5cd02e38eab3a66ada03b2651b @Override public Map getModelRotationValues() { return this.modelRotationValues; -@@ -514,14 +531,22 @@ public class Axolotl extends Animal implements LerpingModel, Bucketable { +@@ -516,14 +533,22 @@ public class Axolotl extends Animal implements LerpingModel, Bucketable { private static class AxolotlMoveControl extends SmoothSwimmingMoveControl { private final Axolotl axolotl; @@ -2108,7 +2109,7 @@ index 86acf89ce875e215da8469947b382f70e42314b0..09a3ed5cd02e38eab3a66ada03b2651b if (!this.axolotl.isPlayingDead()) { super.tick(); } -@@ -536,9 +561,9 @@ public class Axolotl extends Animal implements LerpingModel, Bucketable { +@@ -538,9 +563,9 @@ public class Axolotl extends Animal implements LerpingModel, Bucketable { } @Override @@ -2121,7 +2122,7 @@ index 86acf89ce875e215da8469947b382f70e42314b0..09a3ed5cd02e38eab3a66ada03b2651b } 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 cc5687f43f8ac99995667fdc53c5c0586f70f367..563ead054aa41c8420d9b3f8c335dce20c229ac6 100644 +index d5cd18c478bedaeed83de31d442d1a5b0f0f54cf..898a82d5f6404b736e13c14d9c15ea27a4f938b4 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 @@ -74,6 +74,18 @@ public class Goat extends Animal { @@ -2143,11 +2144,12 @@ index cc5687f43f8ac99995667fdc53c5c0586f70f367..563ead054aa41c8420d9b3f8c335dce2 @Override protected Brain.Provider brainProvider() { return Brain.provider(Goat.MEMORY_TYPES, Goat.SENSOR_TYPES); -@@ -149,6 +161,7 @@ public class Goat extends Animal { +@@ -150,7 +162,7 @@ public class Goat extends Animal { @Override protected void customServerAiStep() { this.level.getProfiler().push("goatBrain"); -+ if (getRider() == null) // Purpur - only use brain if no rider +- if (this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish ++ if (getRider() == null && 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"); @@ -3035,7 +3037,7 @@ index ee9194ffb3cc6d660d4f99a3914ede7e4a3643fe..1f789b1d481234cafaa6e75c36a272bb 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 d47002d45dabd66f38d25d398d8943f4b911cdc5..3e14ad3c66ff2e14e9f746a0d03bc34e7a48c1c8 100644 +index 5e207a3ea30ead2749c1121e2df8da53c8ebfdd7..753a6cee043ecb9c8af10ed0a1dfc9040bdf47b7 100644 --- a/src/main/java/net/minecraft/world/entity/monster/EnderMan.java +++ b/src/main/java/net/minecraft/world/entity/monster/EnderMan.java @@ -88,9 +88,22 @@ public class EnderMan extends Monster implements NeutralMob { @@ -3078,7 +3080,7 @@ index d47002d45dabd66f38d25d398d8943f4b911cdc5..3e14ad3c66ff2e14e9f746a0d03bc34e float f = this.getBrightness(); 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 -@@ -379,6 +393,7 @@ public class EnderMan extends Monster implements NeutralMob { +@@ -385,6 +399,7 @@ public class EnderMan extends Monster implements NeutralMob { public boolean hurt(DamageSource source, float amount) { if (this.isInvulnerableTo(source)) { return false; @@ -4326,7 +4328,7 @@ index c1e0cf7d86d7b45cc4ca342d80f0dc3fe43b0bfd..2dec8be9b199e9bcf0970774afbd19a4 public void setPersistentAngerTarget(@Nullable UUID uuid) { this.persistentAngerTarget = uuid; 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 212e2c5b4b917c0c327d4b0612fecaea81c0ad87..1412ab46fe66773ee8fbb717921e71934840fe9d 100644 +index 17b4185704abfd47832941d02d60500707e8a530..84969aa36add3b261b377c6b139313b153af4c46 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 @@ -67,6 +67,18 @@ public class Hoglin extends Animal implements Enemy, HoglinBase { @@ -4348,16 +4350,17 @@ index 212e2c5b4b917c0c327d4b0612fecaea81c0ad87..1412ab46fe66773ee8fbb717921e7193 @Override public boolean canBeLeashed(Player player) { return !this.isLeashed(); -@@ -129,6 +141,7 @@ public class Hoglin extends Animal implements Enemy, HoglinBase { +@@ -130,7 +142,7 @@ public class Hoglin extends Animal implements Enemy, HoglinBase { @Override protected void customServerAiStep() { this.level.getProfiler().push("hoglinBrain"); -+ if (getRider() == null) // Purpur - only use brain if no rider +- if (this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish ++ if (getRider() == null && this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish // Purpur - only use brain if no rider 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 8a2c2b4bd603aae37055abd058feb7ee759078ce..0edd303a5425af88b34ad6dbee5c39a5f1b73022 100644 +index d850f1f57fc7560738f7bd82b37ec888e8e9301c..6d27e775cff2e70889ef3fc4646d46edb184fb71 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 @@ -96,6 +96,18 @@ public class Piglin extends AbstractPiglin implements CrossbowAttackMob, Invento @@ -4379,11 +4382,12 @@ index 8a2c2b4bd603aae37055abd058feb7ee759078ce..0edd303a5425af88b34ad6dbee5c39a5 @Override public void addAdditionalSaveData(CompoundTag nbt) { super.addAdditionalSaveData(nbt); -@@ -291,6 +303,7 @@ public class Piglin extends AbstractPiglin implements CrossbowAttackMob, Invento +@@ -292,7 +304,7 @@ public class Piglin extends AbstractPiglin implements CrossbowAttackMob, Invento @Override protected void customServerAiStep() { this.level.getProfiler().push("piglinBrain"); -+ if (getRider() == null) // Purpur - only use brain if no rider +- if (this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish ++ if (getRider() == null && this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish // Purpur - only use brain if no rider this.getBrain().tick((ServerLevel) this.level, this); this.level.getProfiler().pop(); PiglinAi.updateActivity(this); @@ -4428,10 +4432,10 @@ index ea0040a3494709efb4819c7530dbcc37aa62e86a..5025d4571a34fe9e0e58ada6b81b0647 this.level.getProfiler().pop(); PiglinBruteAi.updateActivity(this); diff --git a/src/main/java/net/minecraft/world/entity/npc/Villager.java b/src/main/java/net/minecraft/world/entity/npc/Villager.java -index ea612b3c3ed7305b504d65eb6d38ed99547fd488..ed56e4f4e35d0425546a37455af3776c65d45f66 100644 +index 7d6bc3c253a77adfc7c35383caadb37154dc5c0f..d951bc1d709f64871a01edc81f091c8f4c032d34 100644 --- a/src/main/java/net/minecraft/world/entity/npc/Villager.java +++ b/src/main/java/net/minecraft/world/entity/npc/Villager.java -@@ -151,6 +151,23 @@ public class Villager extends AbstractVillager implements ReputationEventHandler +@@ -153,6 +153,23 @@ public class Villager extends AbstractVillager implements ReputationEventHandler this.setVillagerData(this.getVillagerData().setType(type).setProfession(VillagerProfession.NONE)); } @@ -4455,7 +4459,16 @@ index ea612b3c3ed7305b504d65eb6d38ed99547fd488..ed56e4f4e35d0425546a37455af3776c @Override public Brain getBrain() { return (Brain) super.getBrain(); // CraftBukkit - decompile error -@@ -302,7 +319,7 @@ public class Villager extends AbstractVillager implements ReputationEventHandler +@@ -250,7 +267,7 @@ public class Villager extends AbstractVillager implements ReputationEventHandler + this.level.getProfiler().push("villagerBrain"); + // Pufferfish start + if (!inactive) { +- if (this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish ++ if (getRider() == null && this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish // Purpur - only use brain if no rider + this.getBrain().tick((ServerLevel) this.level, this); // Paper + } + // Pufferfish end +@@ -310,7 +327,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(); @@ -4464,7 +4477,7 @@ index ea612b3c3ed7305b504d65eb6d38ed99547fd488..ed56e4f4e35d0425546a37455af3776c } else { boolean flag = this.getOffers().isEmpty(); -@@ -315,8 +332,9 @@ public class Villager extends AbstractVillager implements ReputationEventHandler +@@ -323,8 +340,9 @@ public class Villager extends AbstractVillager implements ReputationEventHandler } if (flag) { @@ -4551,7 +4564,7 @@ index 4132c1113f5437a776e5e3c1cb306904775aed88..1a945a32c3d3705a318ebca72a365931 public void tick() { super.tick(); 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 15744949537430d8d8ae71ea72481126c9aff7bd..36417466ae30e9f7f3e953fd4ff98ed3f3b71725 100644 +index 4759a6b3d3cd7973744562adf97d110d359ebce3..cfb43f277c013680a0e51fb93796e38f88b506db 100644 --- a/src/main/java/net/minecraft/world/entity/projectile/Projectile.java +++ b/src/main/java/net/minecraft/world/entity/projectile/Projectile.java @@ -35,7 +35,7 @@ public abstract class Projectile extends Entity { diff --git a/patches/server/0007-Configurable-entity-base-attributes.patch b/patches/server/0007-Configurable-entity-base-attributes.patch index b5d98b43c..f46da6fb4 100644 --- a/patches/server/0007-Configurable-entity-base-attributes.patch +++ b/patches/server/0007-Configurable-entity-base-attributes.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Configurable entity base attributes diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java -index 43697061ddbbdf160453d3ab189097a6a56e6246..d1e937e7294d360e4f81dc54730b3f4ef1fe3c27 100644 +index 905e54d53f7a209f538da1500df0d3dfa6e8c7ed..146b3d064829d38901c34e639d01d8d02b05f495 100644 --- a/src/main/java/net/minecraft/world/entity/Entity.java +++ b/src/main/java/net/minecraft/world/entity/Entity.java @@ -149,7 +149,7 @@ import org.bukkit.plugin.PluginManager; @@ -34,10 +34,10 @@ index 03adc3b746e05bb4b0514ba4a66c101b9742ceed..ec261673ac444fd5de9c8556cde5d788 @Override diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java -index 5d9e460c54068e0e05f975fa057a099c160a3d0d..f1df05c21fe3cce9270506bfb1882bd75d57471d 100644 +index 46134547b26de59bb69c584df676bf09e756420e..dac5e52f2d86a9f4874cbaa2fdd6775d4b0566c1 100644 --- a/src/main/java/net/minecraft/world/entity/LivingEntity.java +++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java -@@ -285,6 +285,7 @@ public abstract class LivingEntity extends Entity { +@@ -284,6 +284,7 @@ public abstract class LivingEntity extends Entity { this.useItem = ItemStack.EMPTY; this.lastClimbablePos = Optional.empty(); this.attributes = new AttributeMap(DefaultAttributes.getSupplier(type), this); // Purpur @@ -45,7 +45,7 @@ index 5d9e460c54068e0e05f975fa057a099c160a3d0d..f1df05c21fe3cce9270506bfb1882bd7 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()); -@@ -300,6 +301,8 @@ public abstract class LivingEntity extends Entity { +@@ -299,6 +300,8 @@ public abstract class LivingEntity extends Entity { this.brain = this.makeBrain(new Dynamic(dynamicopsnbt, (net.minecraft.nbt.Tag) dynamicopsnbt.createMap((Map) ImmutableMap.of(dynamicopsnbt.createString("memories"), (net.minecraft.nbt.Tag) dynamicopsnbt.emptyMap())))); } @@ -55,7 +55,7 @@ index 5d9e460c54068e0e05f975fa057a099c160a3d0d..f1df05c21fe3cce9270506bfb1882bd7 return this.brain; } 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 8a6608310caf1a9046fba4e51ca0eff92c8177fc..743b5d483a230d56207a8864b5abab06d7c68e16 100644 +index 76de99e019c784609dfcafb35b9c4c9d21f8d6cf..e82592e3e43f38a0f09803c7b111d73a90361f88 100644 --- a/src/main/java/net/minecraft/world/entity/ambient/Bat.java +++ b/src/main/java/net/minecraft/world/entity/ambient/Bat.java @@ -86,6 +86,18 @@ public class Bat extends AmbientCreature { @@ -459,7 +459,7 @@ index 4187d8fdb950781de8b414ed6928a3df4a89ba67..277637692e504cca04bfca7683c05229 @Override 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 09a3ed5cd02e38eab3a66ada03b2651b8cbe1751..a2b2d3f670cd200d7fba40f8227964ef95c5b455 100644 +index 7f535683dcc2ad9908400d98328174baf1ea5a91..1b66aea5871ccf83ea39d3f52f0f6da72cd785b6 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 @@ -106,6 +106,11 @@ public class Axolotl extends Animal implements LerpingModel, Bucketable { @@ -475,7 +475,7 @@ index 09a3ed5cd02e38eab3a66ada03b2651b8cbe1751..a2b2d3f670cd200d7fba40f8227964ef @Override 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 563ead054aa41c8420d9b3f8c335dce20c229ac6..21aca411c458a1e80cdd6e97961f9a5c572854a9 100644 +index 898a82d5f6404b736e13c14d9c15ea27a4f938b4..39a7ea2854090d8505a9bc888313d84645595e23 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 @@ -84,6 +84,11 @@ public class Goat extends Animal { @@ -845,7 +845,7 @@ index 1f789b1d481234cafaa6e75c36a272bbd064482e..e25374b06e3babd93cd5bbdd58ea401a public static AttributeSupplier.Builder createAttributes() { 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 3e14ad3c66ff2e14e9f746a0d03bc34e7a48c1c8..e5ddb64d1a212266c05170359e27927a5f9e243e 100644 +index 753a6cee043ecb9c8af10ed0a1dfc9040bdf47b7..1cec7bb3efa1e3377f72489c090d3234123d3e26 100644 --- a/src/main/java/net/minecraft/world/entity/monster/EnderMan.java +++ b/src/main/java/net/minecraft/world/entity/monster/EnderMan.java @@ -98,6 +98,11 @@ public class EnderMan extends Monster implements NeutralMob { @@ -1342,7 +1342,7 @@ index 2dec8be9b199e9bcf0970774afbd19a4861d802f..f30b36b3f1153e1cbd9b8242909d9ba8 @Nullable 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 1412ab46fe66773ee8fbb717921e71934840fe9d..3ec9b8343cf80ceea8503ad78863a4b539c6ef6b 100644 +index 84969aa36add3b261b377c6b139313b153af4c46..e46a6bba628417c1b0795c477e649cfeca015647 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 @@ -77,6 +77,11 @@ public class Hoglin extends Animal implements Enemy, HoglinBase { @@ -1358,7 +1358,7 @@ index 1412ab46fe66773ee8fbb717921e71934840fe9d..3ec9b8343cf80ceea8503ad78863a4b5 @Override 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 0edd303a5425af88b34ad6dbee5c39a5f1b73022..86a9800276648c094c688d6dbedd51657e642df5 100644 +index 6d27e775cff2e70889ef3fc4646d46edb184fb71..862534112722a482cd5f69b346b69f3d46086359 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 @@ -106,6 +106,11 @@ public class Piglin extends AbstractPiglin implements CrossbowAttackMob, Invento @@ -1390,10 +1390,10 @@ index 5025d4571a34fe9e0e58ada6b81b064716b8f672..f6734f2eb44af7b2389de5079831e0e4 public static AttributeSupplier.Builder createAttributes() { 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 ed56e4f4e35d0425546a37455af3776c65d45f66..6dcc96a2c939ebd0ca5e1934e69d61dfc2b5fa21 100644 +index d951bc1d709f64871a01edc81f091c8f4c032d34..0f466c9301c38e3747a22f116b98d47d1d912601 100644 --- a/src/main/java/net/minecraft/world/entity/npc/Villager.java +++ b/src/main/java/net/minecraft/world/entity/npc/Villager.java -@@ -166,6 +166,11 @@ public class Villager extends AbstractVillager implements ReputationEventHandler +@@ -168,6 +168,11 @@ public class Villager extends AbstractVillager implements ReputationEventHandler protected void registerGoals() { this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); } diff --git a/patches/server/0008-Timings-stuff.patch b/patches/server/0008-Timings-stuff.patch index 21e99b387..4ace542c5 100644 --- a/patches/server/0008-Timings-stuff.patch +++ b/patches/server/0008-Timings-stuff.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Timings stuff diff --git a/src/main/java/co/aikar/timings/TimingsExport.java b/src/main/java/co/aikar/timings/TimingsExport.java -index 5e3b7fb2e0b7608610555cd23e7ad25a05883181..52197656e46fd159240a936fe13bfb27f2b4e9a7 100644 +index 26bde1c22363ddbdbe4127d14bae54e17b0d7393..0f4b41fd7037f380f759a93f9ef70b4860816ad1 100644 --- a/src/main/java/co/aikar/timings/TimingsExport.java +++ b/src/main/java/co/aikar/timings/TimingsExport.java -@@ -225,9 +225,14 @@ public class TimingsExport extends Thread { +@@ -225,10 +225,14 @@ public class TimingsExport extends Thread { // Information on the users Config parent.put("config", createObject( @@ -16,16 +16,17 @@ index 5e3b7fb2e0b7608610555cd23e7ad25a05883181..52197656e46fd159240a936fe13bfb27 + // Purpur start + pair("server.properties", mapAsJSON(Bukkit.spigot().getServerProperties())), pair("bukkit", mapAsJSON(Bukkit.spigot().getBukkitConfig(), null)), -- pair("paper", mapAsJSON(Bukkit.spigot().getPaperConfig(), 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 + pair("spigot", mapAsJSON(Bukkit.spigot().getSpigotConfig(), null)), + pair("paper", mapAsJSON(Bukkit.spigot().getPaperConfig(), null)), ++ pair("pufferfish", mapAsJSON(gg.pufferfish.pufferfish.PufferfishConfig.getConfigCopy(), null)), // Pufferfish + pair("purpur", mapAsJSON(Bukkit.spigot().getPurpurConfig(), null)) + // Purpur end )); new TimingsExport(listeners, parent, history).start(); -@@ -268,6 +273,19 @@ public class TimingsExport extends Thread { +@@ -269,6 +273,19 @@ public class TimingsExport extends Thread { return timingsCost; } @@ -46,11 +47,11 @@ index 5e3b7fb2e0b7608610555cd23e7ad25a05883181..52197656e46fd159240a936fe13bfb27 JSONObject object = new JSONObject(); diff --git a/src/main/java/com/destroystokyo/paper/PaperConfig.java b/src/main/java/com/destroystokyo/paper/PaperConfig.java -index cd918cec00d8202252af0d20b1a8891371c538e3..6c1b67fbd4524aac075e93ed454f7139002c997a 100644 +index 2a19037d3ad734d1cf746d02b72926dd702fd23a..cbae8e7a2ee67e5a8f07f8d9b9bf4be30e760928 100644 --- a/src/main/java/com/destroystokyo/paper/PaperConfig.java +++ b/src/main/java/com/destroystokyo/paper/PaperConfig.java -@@ -234,6 +234,12 @@ public class PaperConfig { - boolean timings = getBoolean("timings.enabled", true); +@@ -243,6 +243,12 @@ public class PaperConfig { + // Pufferfish end boolean verboseTimings = getBoolean("timings.verbose", true); TimingsManager.url = getString("timings.url", "https://timings.aikar.co/"); + // Purpur start diff --git a/patches/server/0011-AFK-API.patch b/patches/server/0011-AFK-API.patch index 21d196331..352ad71ea 100644 --- a/patches/server/0011-AFK-API.patch +++ b/patches/server/0011-AFK-API.patch @@ -68,7 +68,7 @@ index 618595add09eef5381307ba2fe154adfc97b2a0e..100d01814aca6cbb26c721f55851df27 return this.stats; } diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -index 1b50ee29ab8721807745e052e19c67e7db6cfd18..5ad2118d07c190eae3e76a7c8cb6e216d12c915e 100644 +index 2d1897d84f7f1f6265a5c31ec2666af7019010c9..fa14b693257811fa762eeb209042ff1a4b3a65ec 100644 --- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java +++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java @@ -295,6 +295,20 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Ser @@ -114,7 +114,7 @@ index 1b50ee29ab8721807745e052e19c67e7db6cfd18..5ad2118d07c190eae3e76a7c8cb6e216 // Skip the first time we do this if (true) { // Spigot - don't skip any move events Location oldTo = to.clone(); -@@ -1439,7 +1461,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Ser +@@ -1440,7 +1462,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Ser if (!this.player.isChangingDimension() && d11 > org.spigotmc.SpigotConfig.movedWronglyThreshold && !this.player.isSleeping() && !this.player.gameMode.isCreative() && this.player.gameMode.getGameModeForPlayer() != GameType.SPECTATOR) { // Spigot flag1 = true; // Paper - diff on change, this should be moved wrongly @@ -123,7 +123,7 @@ index 1b50ee29ab8721807745e052e19c67e7db6cfd18..5ad2118d07c190eae3e76a7c8cb6e216 } this.player.absMoveTo(d0, d1, d2, f, f1); -@@ -1489,6 +1511,8 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Ser +@@ -1490,6 +1512,8 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Ser this.lastYaw = to.getYaw(); this.lastPitch = to.getPitch(); @@ -167,7 +167,7 @@ index e39965c2e50bc8ee424ea07819346e0611398e28..212ea98eeaaf4b20ba0896dab03cd092 private EntitySelector() {} // Paper start diff --git a/src/main/java/net/minecraft/world/entity/ai/targeting/TargetingConditions.java b/src/main/java/net/minecraft/world/entity/ai/targeting/TargetingConditions.java -index a7575b5ef56af6f53448d391abb4956e130148ca..0a9e4dc5d6d567605c587df9bcbb57d379b62877 100644 +index e752c83df50fb9b670ecea2abc95426c2a009b6f..baa4f9026d31de92210300ecb8ee8c1b6d575435 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 { @@ -305,10 +305,10 @@ index 373bdce6590564bd9de4a571a91bbf05e287ee08..27d70e8e29902fe4275200cf75082980 public boolean untamedTamablesAreRidable = true; public boolean useNightVisionWhenRiding = false; diff --git a/src/main/java/org/spigotmc/ActivationRange.java b/src/main/java/org/spigotmc/ActivationRange.java -index 1732e753ea7748a66abecafb2136f93383f5ec4e..068238bc911d8458a321a920c3638d368449ac95 100644 +index 2eb9bc34afdc26bed21bc9d3e97f636cfc97f166..e12ba58f3795c17694defa308ced50a19d2a37b9 100644 --- a/src/main/java/org/spigotmc/ActivationRange.java +++ b/src/main/java/org/spigotmc/ActivationRange.java -@@ -195,6 +195,7 @@ public class ActivationRange +@@ -199,6 +199,7 @@ public class ActivationRange { player.activatedTick = MinecraftServer.currentTick; diff --git a/patches/server/0012-Bring-back-server-name.patch b/patches/server/0012-Bring-back-server-name.patch index fba254437..b0be76926 100644 --- a/patches/server/0012-Bring-back-server-name.patch +++ b/patches/server/0012-Bring-back-server-name.patch @@ -17,10 +17,10 @@ index f944e6beafc7876ed9c6923a22f58d82967b77cb..e1c7b7a659e56fa5b3a1f52cb2ccc99b public final boolean spawnNpcs = this.get("spawn-npcs", true); public final boolean pvp = this.get("pvp", true); diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java -index df6eec75dc80f18b7804d1b5a181b4d5fd573abb..484111518d60c4f764f4c59b45cff96b86a6dbbe 100644 +index f69516d01a634a08b75744bfe26e5d9f6831c8f3..2551044b41483dd719e5944e5536888acc3a1968 100644 --- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java +++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java -@@ -2831,4 +2831,11 @@ public final class CraftServer implements Server { +@@ -2836,4 +2836,11 @@ public final class CraftServer implements Server { } // Paper end diff --git a/patches/server/0013-Configurable-server-mod-name.patch b/patches/server/0013-Configurable-server-mod-name.patch index 094c30618..1a9a1f2fa 100644 --- a/patches/server/0013-Configurable-server-mod-name.patch +++ b/patches/server/0013-Configurable-server-mod-name.patch @@ -5,15 +5,15 @@ Subject: [PATCH] Configurable server mod name diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java -index 973ea150c2f8ac136736b4ae9f505491e2fc80c2..1cb6c989a0b21df7dff72062550daa09721d7687 100644 +index a8a711c96a38c3d1c9d9b86418d389d5fa81ca29..11f96558f195cc60b4376f8554a4bc5cd93014b9 100644 --- a/src/main/java/net/minecraft/server/MinecraftServer.java +++ b/src/main/java/net/minecraft/server/MinecraftServer.java -@@ -1710,7 +1710,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop // Paper - Paper > // Spigot - Spigot > // CraftBukkit - cb > vanilla! -+ return org.purpurmc.purpur.PurpurConfig.serverModName; // Purpur - Purpur > // Paper - Paper > // Spigot - Spigot > // CraftBukkit - cb > vanilla! +- return "Purpur"; // Purpur - Purpur > // Pufferfish - Pufferfish > // Paper - Paper > // Spigot - Spigot > // CraftBukkit - cb > vanilla! ++ return org.purpurmc.purpur.PurpurConfig.serverModName; // Purpur - Purpur > // Pufferfish - Pufferfish > // Paper - Paper > // Spigot - Spigot > // CraftBukkit - cb > vanilla! } public SystemReport fillSystemReport(SystemReport details) { diff --git a/patches/server/0014-LivingEntity-safeFallDistance.patch b/patches/server/0014-LivingEntity-safeFallDistance.patch index 9032f9dd6..21c93a3db 100644 --- a/patches/server/0014-LivingEntity-safeFallDistance.patch +++ b/patches/server/0014-LivingEntity-safeFallDistance.patch @@ -5,10 +5,10 @@ Subject: [PATCH] LivingEntity safeFallDistance diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java -index f1df05c21fe3cce9270506bfb1882bd75d57471d..ab868741cdb9ad20c81d4ed3561d498d25fcbf12 100644 +index dac5e52f2d86a9f4874cbaa2fdd6775d4b0566c1..6418809533a1847967e5cd18e70d3586bc79d979 100644 --- a/src/main/java/net/minecraft/world/entity/LivingEntity.java +++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java -@@ -252,6 +252,7 @@ public abstract class LivingEntity extends Entity { +@@ -251,6 +251,7 @@ public abstract class LivingEntity extends Entity { // CraftBukkit start public int expToDrop; public int maxAirTicks = 300; @@ -16,7 +16,7 @@ index f1df05c21fe3cce9270506bfb1882bd75d57471d..ab868741cdb9ad20c81d4ed3561d498d public boolean forceDrops; public ArrayList drops = new ArrayList(); public final org.bukkit.craftbukkit.attribute.CraftAttributeMap craftAttributes; -@@ -351,8 +352,8 @@ public abstract class LivingEntity extends Entity { +@@ -350,8 +351,8 @@ public abstract class LivingEntity extends Entity { this.tryAddSoulSpeed(); } @@ -27,7 +27,7 @@ index f1df05c21fe3cce9270506bfb1882bd75d57471d..ab868741cdb9ad20c81d4ed3561d498d if (!landedState.isAir()) { double d1 = Math.min((double) (0.2F + f / 15.0F), 2.5D); -@@ -1905,7 +1906,7 @@ public abstract class LivingEntity extends Entity { +@@ -1926,7 +1927,7 @@ public abstract class LivingEntity extends Entity { MobEffectInstance mobeffect = this.getEffect(MobEffects.JUMP); float f2 = mobeffect == null ? 0.0F : (float) (mobeffect.getAmplifier() + 1); diff --git a/patches/server/0015-Lagging-threshold.patch b/patches/server/0015-Lagging-threshold.patch index ff964d27a..9fe9ca989 100644 --- a/patches/server/0015-Lagging-threshold.patch +++ b/patches/server/0015-Lagging-threshold.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Lagging threshold diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java -index 1cb6c989a0b21df7dff72062550daa09721d7687..4d89c9de4b4aae4452750fb941a2e610b8834039 100644 +index 11f96558f195cc60b4376f8554a4bc5cd93014b9..bb4b6194583cf65a98aaf099d0c37acc47570b38 100644 --- a/src/main/java/net/minecraft/server/MinecraftServer.java +++ b/src/main/java/net/minecraft/server/MinecraftServer.java @@ -304,6 +304,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop implements EntityTypeTest { @@ -25,7 +25,7 @@ index 419a7e9614af2328ed401fc954196056243a984c..3177e4f0af6ec4dd22c7e809d23f3711 public static ResourceLocation getKey(EntityType type) { return Registry.ENTITY_TYPE.getKey(type); } -@@ -450,6 +460,16 @@ public class EntityType implements EntityTypeTest { +@@ -451,6 +461,16 @@ public class EntityType implements EntityTypeTest { return this.category; } diff --git a/patches/server/0017-EMC-MonsterEggSpawnEvent.patch b/patches/server/0017-EMC-MonsterEggSpawnEvent.patch index 29082d663..4b7ce280f 100644 --- a/patches/server/0017-EMC-MonsterEggSpawnEvent.patch +++ b/patches/server/0017-EMC-MonsterEggSpawnEvent.patch @@ -5,10 +5,10 @@ Subject: [PATCH] EMC - MonsterEggSpawnEvent diff --git a/src/main/java/net/minecraft/world/entity/EntityType.java b/src/main/java/net/minecraft/world/entity/EntityType.java -index 3177e4f0af6ec4dd22c7e809d23f3711a8dfda54..4236c9dfa0be6fa8ad2f3e32d7f65f1b722c4e60 100644 +index ac29cea738d013b69c263dd4708966e59a7e5741..4833e545545d9114a15f0bcc0d0a2a214183000f 100644 --- a/src/main/java/net/minecraft/world/entity/EntityType.java +++ b/src/main/java/net/minecraft/world/entity/EntityType.java -@@ -333,13 +333,20 @@ public class EntityType implements EntityTypeTest { +@@ -334,13 +334,20 @@ public class EntityType implements EntityTypeTest { @Nullable public Entity spawn(ServerLevel world, @Nullable ItemStack stack, @Nullable Player player, BlockPos pos, MobSpawnType spawnReason, boolean alignPosition, boolean invertY) { @@ -31,7 +31,7 @@ index 3177e4f0af6ec4dd22c7e809d23f3711a8dfda54..4236c9dfa0be6fa8ad2f3e32d7f65f1b } @Nullable -@@ -364,9 +371,29 @@ public class EntityType implements EntityTypeTest { +@@ -365,9 +372,29 @@ public class EntityType implements EntityTypeTest { } } // Paper end diff --git a/patches/server/0018-Player-invulnerabilities.patch b/patches/server/0018-Player-invulnerabilities.patch index b1070ccc0..7b5c1b6e2 100644 --- a/patches/server/0018-Player-invulnerabilities.patch +++ b/patches/server/0018-Player-invulnerabilities.patch @@ -82,10 +82,10 @@ index 100d01814aca6cbb26c721f55851df27dc654880..7d8853191eadf5f5d909dcc0269740d6 public Scoreboard getScoreboard() { return this.getBukkitEntity().getScoreboard().getHandle(); diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -index 5ad2118d07c190eae3e76a7c8cb6e216d12c915e..728d6829d9fdf20176e7c8568b8acbf43fdf4d1e 100644 +index fa14b693257811fa762eeb209042ff1a4b3a65ec..5e2769dac191d725a1e38e61a871baa644d29b44 100644 --- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java +++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -@@ -1963,12 +1963,21 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Ser +@@ -1964,12 +1964,21 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Ser @Override public void handleResourcePackResponse(ServerboundResourcePackPacket packet) { PacketUtils.ensureRunningOnSameThread(packet, this, this.player.getLevel()); diff --git a/patches/server/0020-Configurable-villager-brain-ticks.patch b/patches/server/0020-Configurable-villager-brain-ticks.patch index e8aae763f..b2f5913b9 100644 --- a/patches/server/0020-Configurable-villager-brain-ticks.patch +++ b/patches/server/0020-Configurable-villager-brain-ticks.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Configurable villager brain ticks 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 9a8e08e9a44900ad3899a63992948941abe7c5d8..17eb573debec8e67dab2f5790ba9fc0a1f09a208 100644 +index 0f466c9301c38e3747a22f116b98d47d1d912601..783cdc4949549725c884cdc0edff485936c77de3 100644 --- a/src/main/java/net/minecraft/world/entity/npc/Villager.java +++ b/src/main/java/net/minecraft/world/entity/npc/Villager.java @@ -137,6 +137,7 @@ public class Villager extends AbstractVillager implements ReputationEventHandler @@ -14,9 +14,9 @@ index 9a8e08e9a44900ad3899a63992948941abe7c5d8..17eb573debec8e67dab2f5790ba9fc0a }); + private final int brainTickOffset; // Purpur - public Villager(EntityType entityType, Level world) { - this(entityType, world, VillagerType.PLAINS); -@@ -149,6 +150,7 @@ public class Villager extends AbstractVillager implements ReputationEventHandler + public long nextGolemPanic = -1; // Pufferfish + +@@ -151,6 +152,7 @@ public class Villager extends AbstractVillager implements ReputationEventHandler this.getNavigation().setCanFloat(true); this.setCanPickUpLoot(true); this.setVillagerData(this.getVillagerData().setType(type).setProfession(VillagerProfession.NONE)); @@ -24,17 +24,17 @@ index 9a8e08e9a44900ad3899a63992948941abe7c5d8..17eb573debec8e67dab2f5790ba9fc0a } // Purpur start -@@ -267,6 +269,10 @@ public class Villager extends AbstractVillager implements ReputationEventHandler - protected void customServerAiStep() { mobTick(false); } - protected void mobTick(boolean inactive) { +@@ -272,6 +274,10 @@ public class Villager extends AbstractVillager implements ReputationEventHandler this.level.getProfiler().push("villagerBrain"); -+ // Purpur start -+ boolean tick = (level.getGameTime() + brainTickOffset) % level.purpurConfig.villagerBrainTicks == 0; -+ if (((ServerLevel) level).getServer().lagging ? tick : level.purpurConfig.villagerUseBrainTicksOnlyWhenLagging || tick) -+ // Purpur end - if (!inactive) this.getBrain().tick((ServerLevel) this.level, this); // Paper - this.level.getProfiler().pop(); - if (this.assignProfessionWhenSpawned) { + // Pufferfish start + if (!inactive) { ++ // Purpur start ++ boolean tick = (level.getGameTime() + brainTickOffset) % level.purpurConfig.villagerBrainTicks == 0; ++ if (((ServerLevel) level).getServer().lagging ? tick : level.purpurConfig.villagerUseBrainTicksOnlyWhenLagging || tick) ++ // Purpur end + if (getRider() == null && this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish // Purpur - only use brain if no rider + this.getBrain().tick((ServerLevel) this.level, this); // Paper + } diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java index 29de4c4316285b7ddf77c508d098584d2cb365e9..ccd3597d78a12d8e6a3514c0f9cfa2aa31b688f8 100644 --- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java diff --git a/patches/server/0021-Alternative-Keepalive-Handling.patch b/patches/server/0021-Alternative-Keepalive-Handling.patch index e718cbbbf..c5a48806a 100644 --- a/patches/server/0021-Alternative-Keepalive-Handling.patch +++ b/patches/server/0021-Alternative-Keepalive-Handling.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Alternative Keepalive Handling diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -index 728d6829d9fdf20176e7c8568b8acbf43fdf4d1e..727f75ec771f68268904ea93b9da30427b8579fa 100644 +index 5e2769dac191d725a1e38e61a871baa644d29b44..a1079a9f524e9efcc0b29b1c1b5f4e5aefa9ec85 100644 --- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java +++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java @@ -228,6 +228,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Ser @@ -38,7 +38,7 @@ index 728d6829d9fdf20176e7c8568b8acbf43fdf4d1e..727f75ec771f68268904ea93b9da3042 if (this.keepAlivePending) { if (!this.processedDisconnect && elapsedTime >= KEEPALIVE_LIMIT) { // check keepalive limit, don't fire if already disconnected ServerGamePacketListenerImpl.LOGGER.warn("{} was kicked due to keepalive timeout!", this.player.getScoreboardName()); // more info -@@ -3134,6 +3150,16 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Ser +@@ -3135,6 +3151,16 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Ser @Override public void handleKeepAlive(ServerboundKeepAlivePacket packet) { diff --git a/patches/server/0028-Zombie-horse-naturally-spawn.patch b/patches/server/0028-Zombie-horse-naturally-spawn.patch index 445bd19c0..44d3cd409 100644 --- a/patches/server/0028-Zombie-horse-naturally-spawn.patch +++ b/patches/server/0028-Zombie-horse-naturally-spawn.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Zombie horse naturally spawn diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java -index f7cfc6d5ad13dba64c2350fe3fac7f31471e8642..ba095685ec775cb5a7ac9a5b7b0a91797f87b087 100644 +index 41c9033b3e4fc9574793f670df3bb90c395ac991..99eb79174cb45ac4d94990afef410dba0daf632c 100644 --- a/src/main/java/net/minecraft/server/level/ServerLevel.java +++ b/src/main/java/net/minecraft/server/level/ServerLevel.java -@@ -750,9 +750,15 @@ public class ServerLevel extends Level implements WorldGenLevel { +@@ -765,9 +765,15 @@ public class ServerLevel extends Level implements WorldGenLevel { boolean flag1 = this.getGameRules().getBoolean(GameRules.RULE_DOMOBSPAWNING) && this.random.nextDouble() < (double) difficultydamagescaler.getEffectiveDifficulty() * paperConfig.skeleHorseSpawnChance && !this.getBlockState(blockposition.below()).is(Blocks.LIGHTNING_ROD); // Paper if (flag1) { diff --git a/patches/server/0046-Disable-loot-drops-on-death-by-cramming.patch b/patches/server/0046-Disable-loot-drops-on-death-by-cramming.patch index 0a948d640..8a7a62a67 100644 --- a/patches/server/0046-Disable-loot-drops-on-death-by-cramming.patch +++ b/patches/server/0046-Disable-loot-drops-on-death-by-cramming.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Disable loot drops on death by cramming diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java -index ab868741cdb9ad20c81d4ed3561d498d25fcbf12..d2944ac5002f77dee2ae111b4f529471ab265c59 100644 +index 6418809533a1847967e5cd18e70d3586bc79d979..1988ffd4778d00d62fe334479de50cd4ef094f31 100644 --- a/src/main/java/net/minecraft/world/entity/LivingEntity.java +++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java -@@ -1712,10 +1712,12 @@ public abstract class LivingEntity extends Entity { +@@ -1719,10 +1719,12 @@ public abstract class LivingEntity extends Entity { this.dropEquipment(); // CraftBukkit - from below if (this.shouldDropLoot() && this.level.getGameRules().getBoolean(GameRules.RULE_DOMOBLOOT)) { diff --git a/patches/server/0049-Fix-the-dead-lagging-the-server.patch b/patches/server/0049-Fix-the-dead-lagging-the-server.patch index ba4501eab..0cda26bc4 100644 --- a/patches/server/0049-Fix-the-dead-lagging-the-server.patch +++ b/patches/server/0049-Fix-the-dead-lagging-the-server.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Fix the dead lagging the server diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java -index 7af5b443983d557199100c0ab5044e96ebf52d9b..36569048c8ba952270b8ca1e492292cab5f13ae6 100644 +index 146b3d064829d38901c34e639d01d8d02b05f495..bc1139194a3cb087eb8fc92e85491ae44ee461c3 100644 --- a/src/main/java/net/minecraft/world/entity/Entity.java +++ b/src/main/java/net/minecraft/world/entity/Entity.java -@@ -1736,6 +1736,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, i +@@ -1763,6 +1763,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, i this.yRotO = this.getYRot(); this.xRotO = this.getXRot(); this.setYHeadRot(yaw); // Paper - Update head rotation @@ -17,10 +17,10 @@ index 7af5b443983d557199100c0ab5044e96ebf52d9b..36569048c8ba952270b8ca1e492292ca public void absMoveTo(double x, double y, double z) { diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java -index d2944ac5002f77dee2ae111b4f529471ab265c59..eec8fe7566a7911d1250286ecf525c851baecc8e 100644 +index 1988ffd4778d00d62fe334479de50cd4ef094f31..3e472507d03ad86001fbd19a089a14bc7abd3cf7 100644 --- a/src/main/java/net/minecraft/world/entity/LivingEntity.java +++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java -@@ -2911,7 +2911,7 @@ public abstract class LivingEntity extends Entity { +@@ -2932,7 +2932,7 @@ public abstract class LivingEntity extends Entity { } } diff --git a/patches/server/0052-Configurable-TPS-Catchup.patch b/patches/server/0052-Configurable-TPS-Catchup.patch index 0092abbd0..6e4069fd6 100644 --- a/patches/server/0052-Configurable-TPS-Catchup.patch +++ b/patches/server/0052-Configurable-TPS-Catchup.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Configurable TPS Catchup diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java -index 4d89c9de4b4aae4452750fb941a2e610b8834039..55067e47a9907f038bf77f5dda2f395773472ceb 100644 +index bb4b6194583cf65a98aaf099d0c37acc47570b38..ec2edb4b62f692aa39652845e81daf5de2798607 100644 --- a/src/main/java/net/minecraft/server/MinecraftServer.java +++ b/src/main/java/net/minecraft/server/MinecraftServer.java -@@ -1266,7 +1266,13 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop= this.level.paperConfig.netherVoidTopDamageHeight)) { // Paper end diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java -index eec8fe7566a7911d1250286ecf525c851baecc8e..d5d315122326e923dd47ec25c59d81d413839829 100644 +index 3e472507d03ad86001fbd19a089a14bc7abd3cf7..b4a5fdba6e47ae482a445deb5f90dbde2a8f5dec 100644 --- a/src/main/java/net/minecraft/world/entity/LivingEntity.java +++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java -@@ -2403,7 +2403,7 @@ public abstract class LivingEntity extends Entity { +@@ -2424,7 +2424,7 @@ public abstract class LivingEntity extends Entity { @Override protected void outOfWorld() { diff --git a/patches/server/0062-Add-canSaveToDisk-to-Entity.patch b/patches/server/0062-Add-canSaveToDisk-to-Entity.patch index 5a6c73116..a04e88f63 100644 --- a/patches/server/0062-Add-canSaveToDisk-to-Entity.patch +++ b/patches/server/0062-Add-canSaveToDisk-to-Entity.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Add canSaveToDisk to Entity diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java -index 359c70f3618ebd8138daaaadae88db98ee733a30..3417d7aaf858ae4759fd6b2b22f67e951769c33c 100644 +index 347a30897d82b6b6c789e8f207ed1a98c09f5ed3..0ff2b5c23c904826098af23f17e95aba790ef3de 100644 --- a/src/main/java/net/minecraft/world/entity/Entity.java +++ b/src/main/java/net/minecraft/world/entity/Entity.java -@@ -4339,5 +4339,9 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, i +@@ -4418,5 +4418,9 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, i public boolean processClick(InteractionHand hand) { return false; } diff --git a/patches/server/0063-Dispenser-curse-of-binding-protection.patch b/patches/server/0063-Dispenser-curse-of-binding-protection.patch index 09b65d5bd..de918516a 100644 --- a/patches/server/0063-Dispenser-curse-of-binding-protection.patch +++ b/patches/server/0063-Dispenser-curse-of-binding-protection.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Dispenser curse of binding protection diff --git a/src/main/java/net/minecraft/world/entity/Mob.java b/src/main/java/net/minecraft/world/entity/Mob.java -index b0e2f5c98ab1bea6962312fa05cd6d61fc81cb98..3249ea9a4b7dc05a1b53373955c267e7a4c7903b 100644 +index a9f4f86fc2a3c6017acda35c90ae1b53937256c6..033bf60ff92d26981ace3e3ccab91a52c7b4e241 100644 --- a/src/main/java/net/minecraft/world/entity/Mob.java +++ b/src/main/java/net/minecraft/world/entity/Mob.java @@ -63,6 +63,7 @@ import net.minecraft.world.item.ProjectileWeaponItem; @@ -16,7 +16,7 @@ index b0e2f5c98ab1bea6962312fa05cd6d61fc81cb98..3249ea9a4b7dc05a1b53373955c267e7 import net.minecraft.world.level.GameRules; import net.minecraft.world.level.ItemLike; import net.minecraft.world.level.Level; -@@ -1092,6 +1093,12 @@ public abstract class Mob extends LivingEntity { +@@ -1096,6 +1097,12 @@ public abstract class Mob extends LivingEntity { } diff --git a/patches/server/0066-Add-5-second-tps-average-in-tps.patch b/patches/server/0066-Add-5-second-tps-average-in-tps.patch index 0c410e54d..4ceffce5e 100644 --- a/patches/server/0066-Add-5-second-tps-average-in-tps.patch +++ b/patches/server/0066-Add-5-second-tps-average-in-tps.patch @@ -27,7 +27,7 @@ index fa56cd09102a89692b42f1d14257990508c5c720..f9251183df72ddc56662fd3f02acf216 setListData(vector); } diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java -index 55067e47a9907f038bf77f5dda2f395773472ceb..70dfa4d3b7a2c9b2a47000bf26100f174ab67d46 100644 +index ec2edb4b62f692aa39652845e81daf5de2798607..6a1863e815d3330d4619309630e447f601ea0136 100644 --- a/src/main/java/net/minecraft/server/MinecraftServer.java +++ b/src/main/java/net/minecraft/server/MinecraftServer.java @@ -303,7 +303,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop> consumer, Set trackedPlayers) { this.trackedPlayers = trackedPlayers; diff --git a/src/main/java/net/minecraft/world/entity/item/ItemEntity.java b/src/main/java/net/minecraft/world/entity/item/ItemEntity.java -index f09db211f6f789ced85f7bf716428cd04bb41378..67b6803b986236d20b5309cab3c7f9c95ddf5f0b 100644 +index 3ad87457cc1279540b5b633216aa0161e6e9b1b2..cc5396761d67a7d9e9fcc5959caf1f9330d8a6af 100644 --- a/src/main/java/net/minecraft/world/entity/item/ItemEntity.java +++ b/src/main/java/net/minecraft/world/entity/item/ItemEntity.java @@ -54,6 +54,12 @@ public class ItemEntity extends Entity { @@ -47,7 +47,7 @@ index f09db211f6f789ced85f7bf716428cd04bb41378..67b6803b986236d20b5309cab3c7f9c9 public ItemEntity(EntityType type, Level world) { super(type, world); -@@ -321,6 +327,16 @@ public class ItemEntity extends Entity { +@@ -327,6 +333,16 @@ public class ItemEntity extends Entity { return false; } else if (!this.getItem().getItem().canBeHurtBy(source)) { return false; @@ -64,7 +64,7 @@ index f09db211f6f789ced85f7bf716428cd04bb41378..67b6803b986236d20b5309cab3c7f9c9 } else { // CraftBukkit start if (org.bukkit.craftbukkit.event.CraftEventFactory.handleNonLivingEntityDamageEvent(this, source, amount)) { -@@ -503,6 +519,12 @@ public class ItemEntity extends Entity { +@@ -509,6 +525,12 @@ public class ItemEntity extends Entity { com.google.common.base.Preconditions.checkArgument(!stack.isEmpty(), "Cannot drop air"); // CraftBukkit this.getEntityData().set(ItemEntity.DATA_ITEM, stack); this.getEntityData().markDirty(ItemEntity.DATA_ITEM); // CraftBukkit - SPIGOT-4591, must mark dirty @@ -77,7 +77,7 @@ index f09db211f6f789ced85f7bf716428cd04bb41378..67b6803b986236d20b5309cab3c7f9c9 } @Override -@@ -593,4 +615,17 @@ public class ItemEntity extends Entity { +@@ -599,4 +621,17 @@ public class ItemEntity extends Entity { public SoundSource getSoundSource() { return SoundSource.AMBIENT; } diff --git a/patches/server/0077-Add-allow-water-in-end-world-option.patch b/patches/server/0077-Add-allow-water-in-end-world-option.patch index b6c63c151..69b93983b 100644 --- a/patches/server/0077-Add-allow-water-in-end-world-option.patch +++ b/patches/server/0077-Add-allow-water-in-end-world-option.patch @@ -27,10 +27,10 @@ index b5a5c56fbb66c17dd2e2d1f4d69d2b1826cd4951..9805132a1b11d62c3491c178fbed8b6a return true; diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java -index 03a4c5fa746033825c26a031fbf79f721be6fa84..70d87c16734a5e1ac14cc494f95554137ba9f06e 100644 +index 37884e05689288d56ef0efe92296b80ef6093b3c..e5e4fa1865cdd903a4c543b79b4d30a2f067742d 100644 --- a/src/main/java/net/minecraft/world/level/Level.java +++ b/src/main/java/net/minecraft/world/level/Level.java -@@ -1496,4 +1496,14 @@ public abstract class Level implements LevelAccessor, AutoCloseable { +@@ -1598,4 +1598,14 @@ public abstract class Level implements LevelAccessor, AutoCloseable { public boolean shouldDelayFallingBlockEntityRemoval(Entity.RemovalReason reason) { return false; } diff --git a/patches/server/0078-Allow-color-codes-in-books.patch b/patches/server/0078-Allow-color-codes-in-books.patch index 1323b247e..ef63bc0c3 100644 --- a/patches/server/0078-Allow-color-codes-in-books.patch +++ b/patches/server/0078-Allow-color-codes-in-books.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Allow color codes in books diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -index 727f75ec771f68268904ea93b9da30427b8579fa..36b2015e9639441744fec5babcf46475d14ed13a 100644 +index a1079a9f524e9efcc0b29b1c1b5f4e5aefa9ec85..8b16168b7c670edd251057712b1f6075133ceb8b 100644 --- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java +++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -@@ -1233,13 +1233,16 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Ser +@@ -1234,13 +1234,16 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Ser itemstack1.setTag(nbttagcompound.copy()); } @@ -28,7 +28,7 @@ index 727f75ec771f68268904ea93b9da30427b8579fa..36b2015e9639441744fec5babcf46475 this.updateBookPages(pages, (s) -> { return Component.Serializer.toJson(new TextComponent(s)); -@@ -1251,10 +1254,13 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Ser +@@ -1252,10 +1255,13 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Ser private void updateBookPages(List list, UnaryOperator unaryoperator, ItemStack itemstack, int slot, ItemStack handItem) { // CraftBukkit ListTag nbttaglist = new ListTag(); @@ -44,7 +44,7 @@ index 727f75ec771f68268904ea93b9da30427b8579fa..36b2015e9639441744fec5babcf46475 Objects.requireNonNull(nbttaglist); stream.forEach(nbttaglist::add); -@@ -1264,10 +1270,10 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Ser +@@ -1265,10 +1271,10 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Ser for (int j = list.size(); i < j; ++i) { TextFilter.FilteredText itextfilter_a = (TextFilter.FilteredText) list.get(i); @@ -57,7 +57,7 @@ index 727f75ec771f68268904ea93b9da30427b8579fa..36b2015e9639441744fec5babcf46475 if (!s.equals(s1)) { nbttagcompound.putString(String.valueOf(i), (String) unaryoperator.apply(s1)); -@@ -1283,6 +1289,16 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Ser +@@ -1284,6 +1290,16 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Ser this.player.getInventory().setItem(slot, CraftEventFactory.handleEditBookEvent(player, slot, handItem, itemstack)); // CraftBukkit // Paper - Don't ignore result (see other callsite for handleEditBookEvent) } diff --git a/patches/server/0079-Entity-lifespan.patch b/patches/server/0079-Entity-lifespan.patch index 1e7dbd9e6..f0912c041 100644 --- a/patches/server/0079-Entity-lifespan.patch +++ b/patches/server/0079-Entity-lifespan.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Entity lifespan diff --git a/src/main/java/net/minecraft/world/entity/Mob.java b/src/main/java/net/minecraft/world/entity/Mob.java -index 3249ea9a4b7dc05a1b53373955c267e7a4c7903b..78f7022c6584e634b000cc0c079112fc6cc8e869 100644 +index 033bf60ff92d26981ace3e3ccab91a52c7b4e241..359b47daf27712bbd5f2b8fee5a2edf609184878 100644 --- a/src/main/java/net/minecraft/world/entity/Mob.java +++ b/src/main/java/net/minecraft/world/entity/Mob.java @@ -129,6 +129,7 @@ public abstract class Mob extends LivingEntity { @@ -80,7 +80,7 @@ index 3249ea9a4b7dc05a1b53373955c267e7a4c7903b..78f7022c6584e634b000cc0c079112fc } @Override -@@ -1607,6 +1641,7 @@ public abstract class Mob extends LivingEntity { +@@ -1611,6 +1645,7 @@ public abstract class Mob extends LivingEntity { this.setLastHurtMob(target); } diff --git a/patches/server/0080-Add-option-to-teleport-to-spawn-if-outside-world-bor.patch b/patches/server/0080-Add-option-to-teleport-to-spawn-if-outside-world-bor.patch index 56c10c75f..03ad2bc7d 100644 --- a/patches/server/0080-Add-option-to-teleport-to-spawn-if-outside-world-bor.patch +++ b/patches/server/0080-Add-option-to-teleport-to-spawn-if-outside-world-bor.patch @@ -35,7 +35,7 @@ index ae40f50a33f676059738625773127ff9ae181be0..295658d4b479e8a3e825f0b5ce5dfc2c + // Purpur end } diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java -index 3756da97a8404ea573a66f3f160cfbfd857b12e5..e2d1c4f0084f981d2541313fb8cdb0b9edd71f71 100644 +index 38c9aa1e1f717cd9b596db85e9234d2651ac0de0..2950a9556fd474b659d66c1fc4d5e6e451ad9fda 100644 --- a/src/main/java/net/minecraft/world/entity/LivingEntity.java +++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java @@ -43,6 +43,7 @@ import net.minecraft.network.syncher.EntityDataAccessor; @@ -46,7 +46,7 @@ index 3756da97a8404ea573a66f3f160cfbfd857b12e5..e2d1c4f0084f981d2541313fb8cdb0b9 import net.minecraft.server.level.ServerChunkCache; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; -@@ -409,6 +410,7 @@ public abstract class LivingEntity extends Entity { +@@ -407,6 +408,7 @@ public abstract class LivingEntity extends Entity { double d1 = this.level.getWorldBorder().getDamagePerBlock(); if (d1 > 0.0D) { diff --git a/patches/server/0081-Squid-EAR-immunity.patch b/patches/server/0081-Squid-EAR-immunity.patch index d5d0b73e5..575c3051b 100644 --- a/patches/server/0081-Squid-EAR-immunity.patch +++ b/patches/server/0081-Squid-EAR-immunity.patch @@ -25,7 +25,7 @@ index b5d48e3979361e1b6407a1dcf0f5271542273974..71eef6cc623db385db98d3fe98ea6574 public boolean spiderRidable = false; diff --git a/src/main/java/org/spigotmc/ActivationRange.java b/src/main/java/org/spigotmc/ActivationRange.java -index 068238bc911d8458a321a920c3638d368449ac95..8ab99ce9dd678d45928c262b68741f10fd38f9a3 100644 +index e12ba58f3795c17694defa308ced50a19d2a37b9..49b9c0db6693c95bae8f63e97f50720580dc59da 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; @@ -36,7 +36,7 @@ index 068238bc911d8458a321a920c3638d368449ac95..8ab99ce9dd678d45928c262b68741f10 import net.minecraft.world.entity.animal.WaterAnimal; import net.minecraft.world.entity.animal.horse.Llama; import net.minecraft.world.entity.boss.EnderDragonPart; -@@ -362,6 +363,7 @@ public class ActivationRange +@@ -381,6 +382,7 @@ public class ActivationRange */ public static boolean checkIfActive(Entity entity) { diff --git a/patches/server/0083-Configurable-villager-breeding.patch b/patches/server/0083-Configurable-villager-breeding.patch index abe5ff0b0..6ecb4e3c2 100644 --- a/patches/server/0083-Configurable-villager-breeding.patch +++ b/patches/server/0083-Configurable-villager-breeding.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Configurable villager breeding 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 ed97980cc89e086099ff5c343a99a12d8f3e469d..854efbee2b67ed805ab10882a1be0a845fbbd825 100644 +index fbed51cacb1963b10946056a42fa6878e88a31a9..3d755f2c6253e83d0d9fb1348f066f4f7fe1d4be 100644 --- a/src/main/java/net/minecraft/world/entity/npc/Villager.java +++ b/src/main/java/net/minecraft/world/entity/npc/Villager.java -@@ -759,7 +759,7 @@ public class Villager extends AbstractVillager implements ReputationEventHandler +@@ -767,7 +767,7 @@ public class Villager extends AbstractVillager implements ReputationEventHandler @Override public boolean canBreed() { diff --git a/patches/server/0085-Totems-work-in-inventory.patch b/patches/server/0085-Totems-work-in-inventory.patch index 914f10c0e..2c3b6e436 100644 --- a/patches/server/0085-Totems-work-in-inventory.patch +++ b/patches/server/0085-Totems-work-in-inventory.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Totems work in inventory diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java -index e2d1c4f0084f981d2541313fb8cdb0b9edd71f71..527fd1662b8ff007fbc383b0061f70b2bbd5cafe 100644 +index 2950a9556fd474b659d66c1fc4d5e6e451ad9fda..1bc7b7f016b0f58bad34dfd58e506967d61099e5 100644 --- a/src/main/java/net/minecraft/world/entity/LivingEntity.java +++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java -@@ -1513,6 +1513,19 @@ public abstract class LivingEntity extends Entity { +@@ -1520,6 +1520,19 @@ public abstract class LivingEntity extends Entity { } } diff --git a/patches/server/0091-Short-enderman-height.patch b/patches/server/0091-Short-enderman-height.patch index ac4e40115..817f285e7 100644 --- a/patches/server/0091-Short-enderman-height.patch +++ b/patches/server/0091-Short-enderman-height.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Short enderman height diff --git a/src/main/java/net/minecraft/world/entity/EntityType.java b/src/main/java/net/minecraft/world/entity/EntityType.java -index 4236c9dfa0be6fa8ad2f3e32d7f65f1b722c4e60..7307b3208f1b4ffe31fc4e6f73ac7264eb95849a 100644 +index 4833e545545d9114a15f0bcc0d0a2a214183000f..fce7a9c1c1477d1b02a4adbe0bc5a5c97b8c93c9 100644 --- a/src/main/java/net/minecraft/world/entity/EntityType.java +++ b/src/main/java/net/minecraft/world/entity/EntityType.java @@ -279,7 +279,8 @@ public class EntityType implements EntityTypeTest { @@ -19,10 +19,10 @@ index 4236c9dfa0be6fa8ad2f3e32d7f65f1b722c4e60..7307b3208f1b4ffe31fc4e6f73ac7264 private static EntityType register(String id, EntityType.Builder type) { // CraftBukkit - decompile error return (EntityType) Registry.register(Registry.ENTITY_TYPE, id, (EntityType) type.build(id)); // CraftBukkit - decompile error 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 2e831d7901747d7957becddc2daf453c4f30bdcc..c72d5151d4d8eb784550022321578ca820c84948 100644 +index 434e51325cbfe4539470f9ec5164e8ebdd482748..37fe7d448ae40e67cc8c7c1856df3c8ac276f0be 100644 --- a/src/main/java/net/minecraft/world/entity/monster/EnderMan.java +++ b/src/main/java/net/minecraft/world/entity/monster/EnderMan.java -@@ -399,6 +399,7 @@ public class EnderMan extends Monster implements NeutralMob { +@@ -405,6 +405,7 @@ public class EnderMan extends Monster implements NeutralMob { if (this.isInvulnerableTo(source)) { return false; } else if (getRider() != null) { return super.hurt(source, amount); // Purpur - no teleporting on damage diff --git a/patches/server/0092-Stop-squids-floating-on-top-of-water.patch b/patches/server/0092-Stop-squids-floating-on-top-of-water.patch index 0067b88ce..01db7c591 100644 --- a/patches/server/0092-Stop-squids-floating-on-top-of-water.patch +++ b/patches/server/0092-Stop-squids-floating-on-top-of-water.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Stop squids floating on top of water diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java -index 3417d7aaf858ae4759fd6b2b22f67e951769c33c..55635a8abf1ee69e01ba8a7d6119e6c7fdd7ac0d 100644 +index 0ff2b5c23c904826098af23f17e95aba790ef3de..4fad4e9a15be08154c3eb7af1653a61dae9a581f 100644 --- a/src/main/java/net/minecraft/world/entity/Entity.java +++ b/src/main/java/net/minecraft/world/entity/Entity.java -@@ -3887,6 +3887,12 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, i +@@ -3914,6 +3914,12 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, i this.yRotO = this.getYRot(); } @@ -19,7 +19,7 @@ index 3417d7aaf858ae4759fd6b2b22f67e951769c33c..55635a8abf1ee69e01ba8a7d6119e6c7 + // Purpur end + public boolean updateFluidHeightAndDoFluidPushing(Tag 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; diff --git a/src/main/java/net/minecraft/world/entity/animal/Squid.java b/src/main/java/net/minecraft/world/entity/animal/Squid.java index 501b082e36b30140eef9540b8b217b9d45309dff..55f94d28a536f5ceda00831e1b22c8db31c07c2f 100644 diff --git a/patches/server/0094-Entities-can-use-portals-configuration.patch b/patches/server/0094-Entities-can-use-portals-configuration.patch index fb83640d1..573c61a83 100644 --- a/patches/server/0094-Entities-can-use-portals-configuration.patch +++ b/patches/server/0094-Entities-can-use-portals-configuration.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Entities can use portals configuration diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java -index 55635a8abf1ee69e01ba8a7d6119e6c7fdd7ac0d..0198b89d3ac768c826d5be22cde88004f3444d48 100644 +index 4fad4e9a15be08154c3eb7af1653a61dae9a581f..4d9ba5bfd3632c5402badde37996b6ae8c4dea5b 100644 --- a/src/main/java/net/minecraft/world/entity/Entity.java +++ b/src/main/java/net/minecraft/world/entity/Entity.java -@@ -2716,7 +2716,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, i +@@ -2743,7 +2743,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, i public void handleInsidePortal(BlockPos pos) { if (this.isOnPortalCooldown()) { this.setPortalCooldown(); @@ -17,7 +17,7 @@ index 55635a8abf1ee69e01ba8a7d6119e6c7fdd7ac0d..0198b89d3ac768c826d5be22cde88004 if (!this.level.isClientSide && !pos.equals(this.portalEntrancePos)) { this.portalEntrancePos = pos.immutable(); } -@@ -3376,7 +3376,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, i +@@ -3403,7 +3403,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, i } public boolean canChangeDimensions() { diff --git a/patches/server/0099-Despawn-rate-config-options-per-projectile-type.patch b/patches/server/0099-Despawn-rate-config-options-per-projectile-type.patch index a7e8b8e9b..6efcd7c2a 100644 --- a/patches/server/0099-Despawn-rate-config-options-per-projectile-type.patch +++ b/patches/server/0099-Despawn-rate-config-options-per-projectile-type.patch @@ -79,7 +79,7 @@ index fe502e148e218ae404e0049c0251d3e3ca08c825..11befaed9c2845262607cd9e1e38a178 + // Purpur end } diff --git a/src/main/java/net/minecraft/world/entity/projectile/FishingHook.java b/src/main/java/net/minecraft/world/entity/projectile/FishingHook.java -index 1037d0a0cdd4fd7aa99a958ee969759c5883fdc0..c1267592dd5fcc2b528eac22aaa245189df5a436 100644 +index 2015223c1703935faef52a8b88263ab3f1fbe92a..3b36a5f79b830dcf0b3eb4ff278c82134a58da96 100644 --- a/src/main/java/net/minecraft/world/entity/projectile/FishingHook.java +++ b/src/main/java/net/minecraft/world/entity/projectile/FishingHook.java @@ -663,4 +663,11 @@ public class FishingHook extends Projectile { @@ -127,7 +127,7 @@ index 1a945a32c3d3705a318ebca72a365931a8c001b7..c7fdcfae1ba823046fdfe78aa97b4a7a + // Purpur end } 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 36417466ae30e9f7f3e953fd4ff98ed3f3b71725..edc4fc597d60e6125d44023085e993db2e96c4ef 100644 +index cfb43f277c013680a0e51fb93796e38f88b506db..97609bedb82dee6a200ff8b825622c427d7dcf73 100644 --- a/src/main/java/net/minecraft/world/entity/projectile/Projectile.java +++ b/src/main/java/net/minecraft/world/entity/projectile/Projectile.java @@ -33,6 +33,7 @@ public abstract class Projectile extends Entity { @@ -138,9 +138,9 @@ index 36417466ae30e9f7f3e953fd4ff98ed3f3b71725..edc4fc597d60e6125d44023085e993db // CraftBukkit start public boolean hitCancelled = false; // Purpur - private -> public -@@ -42,6 +43,19 @@ public abstract class Projectile extends Entity { - super(type, world); +@@ -72,6 +73,19 @@ public abstract class Projectile extends Entity { } + // Pufferfish start + // Purpur start + protected final void tickDespawnCounter() { diff --git a/patches/server/0104-Stonecutter-damage.patch b/patches/server/0104-Stonecutter-damage.patch index a1f60ac90..18c0659b1 100644 --- a/patches/server/0104-Stonecutter-damage.patch +++ b/patches/server/0104-Stonecutter-damage.patch @@ -24,10 +24,10 @@ index 19d6165f266fcc39bc3533042109ef1b725ecb77..32bf8264fecb8873f3ffa0e57a4bb48b private boolean bypassArmor; private boolean bypassInvul; diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java -index 0198b89d3ac768c826d5be22cde88004f3444d48..cd25a1e6671276ecb465a60ee969122fa5648ded 100644 +index 4d9ba5bfd3632c5402badde37996b6ae8c4dea5b..02a2496539813b1d34b9e5ccac0590e6b58c17b5 100644 --- a/src/main/java/net/minecraft/world/entity/Entity.java +++ b/src/main/java/net/minecraft/world/entity/Entity.java -@@ -1024,7 +1024,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, i +@@ -1051,7 +1051,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, i } // CraftBukkit end diff --git a/patches/server/0105-Configurable-daylight-cycle.patch b/patches/server/0105-Configurable-daylight-cycle.patch index 09b29634b..dcd37b2fc 100644 --- a/patches/server/0105-Configurable-daylight-cycle.patch +++ b/patches/server/0105-Configurable-daylight-cycle.patch @@ -18,10 +18,10 @@ index 689ad22925b2561f7c8db961743eb1f821dbb25f..fa3c960992cc240161817e54659d83fe public ClientboundSetTimePacket(long time, long timeOfDay, boolean doDaylightCycle) { this.gameTime = time % 192000; // Paper - fix guardian beam diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java -index 70dfa4d3b7a2c9b2a47000bf26100f174ab67d46..9e62e7ba26648d3ffa393f47a1de1d14182ceb36 100644 +index 6a1863e815d3330d4619309630e447f601ea0136..dcb496f6af114ad5a41bfa3f3072ba48d9f2e8e9 100644 --- a/src/main/java/net/minecraft/server/MinecraftServer.java +++ b/src/main/java/net/minecraft/server/MinecraftServer.java -@@ -1583,7 +1583,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop this.disconnect("Book too large!", org.bukkit.event.player.PlayerKickEvent.Cause.ILLEGAL_ACTION)); // Paper - kick event cause return; } -@@ -1182,6 +1184,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Ser +@@ -1183,6 +1185,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Ser 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()); diff --git a/patches/server/0132-Add-mobGriefing-bypass-to-everything-affected.patch b/patches/server/0132-Add-mobGriefing-bypass-to-everything-affected.patch index 5cac8f4b0..ab2fcec60 100644 --- a/patches/server/0132-Add-mobGriefing-bypass-to-everything-affected.patch +++ b/patches/server/0132-Add-mobGriefing-bypass-to-everything-affected.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Add mobGriefing bypass to everything affected diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java -index 527fd1662b8ff007fbc383b0061f70b2bbd5cafe..d0fbbaaa48d7e199ab22bc0b089f2ad45fe816ee 100644 +index 1bc7b7f016b0f58bad34dfd58e506967d61099e5..f86e65530182685517e12ea609a4bdcd8c493161 100644 --- a/src/main/java/net/minecraft/world/entity/LivingEntity.java +++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java -@@ -1684,7 +1684,7 @@ public abstract class LivingEntity extends Entity { +@@ -1691,7 +1691,7 @@ public abstract class LivingEntity extends Entity { boolean flag = false; if (this.dead && adversary instanceof WitherBoss) { // Paper @@ -18,7 +18,7 @@ index 527fd1662b8ff007fbc383b0061f70b2bbd5cafe..d0fbbaaa48d7e199ab22bc0b089f2ad4 BlockState iblockdata = Blocks.WITHER_ROSE.defaultBlockState(); diff --git a/src/main/java/net/minecraft/world/entity/Mob.java b/src/main/java/net/minecraft/world/entity/Mob.java -index 2f75ebe8088d70f73a17557fe525ae119a1e19e9..861dd2603a82b26cb0f003b5defc34446feb6fdf 100644 +index 163ca04792388a9f27e5dc22def40ce5b9efbf11..4d2ed6947d661d0f40c9a40824cbd957806c5a3a 100644 --- a/src/main/java/net/minecraft/world/entity/Mob.java +++ b/src/main/java/net/minecraft/world/entity/Mob.java @@ -664,7 +664,7 @@ public abstract class Mob extends LivingEntity { @@ -179,10 +179,10 @@ index bb32bc0989ebbdd7522fe5f70ae3e6414f909388..8ac278f8e55ad7a090ad332db2da1e1e // CraftBukkit start 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 a15be819a3c73cf3bdd5da148d6f2d56dd5db404..bd58dbee7f2922d341a6c201b762a52e78366106 100644 +index 3945038b07393cd4692b340f32a988404117fe6f..8486b37d37fda4cc380a81edb1675e469e1a4e91 100644 --- a/src/main/java/net/minecraft/world/entity/monster/EnderMan.java +++ b/src/main/java/net/minecraft/world/entity/monster/EnderMan.java -@@ -501,7 +501,7 @@ public class EnderMan extends Monster implements NeutralMob { +@@ -507,7 +507,7 @@ public class EnderMan extends Monster implements NeutralMob { @Override public boolean canUse() { if (!enderman.level.purpurConfig.endermanAllowGriefing) return false; // Purpur @@ -191,7 +191,7 @@ index a15be819a3c73cf3bdd5da148d6f2d56dd5db404..bd58dbee7f2922d341a6c201b762a52e } @Override -@@ -549,7 +549,7 @@ public class EnderMan extends Monster implements NeutralMob { +@@ -555,7 +555,7 @@ public class EnderMan extends Monster implements NeutralMob { @Override public boolean canUse() { if (!enderman.level.purpurConfig.endermanAllowGriefing) return false; // Purpur @@ -249,10 +249,10 @@ index cd26c22664fb23457f99fe04607146c8c6545307..5057b9b6ad2c47a11ca11b281393ccf6 BlockPos blockposition = (new BlockPos(this.mob.getX(), this.mob.getY() + 0.5D, this.mob.getZ())).relative(this.selectedDirection); BlockState iblockdata = this.mob.level.getBlockState(blockposition); 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 86a9800276648c094c688d6dbedd51657e642df5..6beb798df62223fe653afa0f34e4a43d6cff11b7 100644 +index 862534112722a482cd5f69b346b69f3d46086359..badf1fc1a5f5ca0086b45876171a18cad00896b7 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 -@@ -406,7 +406,7 @@ public class Piglin extends AbstractPiglin implements CrossbowAttackMob, Invento +@@ -407,7 +407,7 @@ public class Piglin extends AbstractPiglin implements CrossbowAttackMob, Invento @Override public boolean wantsToPickUp(ItemStack stack) { @@ -290,10 +290,10 @@ index e69213b43c8aa5a7c04add7a87482d531fbf52d2..f51ea103238b4a50439f5162a248cd9a // CraftBukkit start - fire ExplosionPrimeEvent ExplosionPrimeEvent event = new ExplosionPrimeEvent((org.bukkit.entity.Explosive) this.getBukkitEntity()); 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 edc4fc597d60e6125d44023085e993db2e96c4ef..2771e600d46195e8170b2bde57c523c2bcbadf5a 100644 +index 97609bedb82dee6a200ff8b825622c427d7dcf73..39422e7fb1e04c007ec136ea709af22a558ff066 100644 --- a/src/main/java/net/minecraft/world/entity/projectile/Projectile.java +++ b/src/main/java/net/minecraft/world/entity/projectile/Projectile.java -@@ -290,6 +290,6 @@ public abstract class Projectile extends Entity { +@@ -320,6 +320,6 @@ public abstract class Projectile extends Entity { public boolean mayInteract(Level world, BlockPos pos) { Entity entity = this.getOwner(); @@ -328,7 +328,7 @@ index 8613008090a9d7cf5cd7c2a598f2c725b5b0cdc6..a66e9826b8283366cec5adb54f79efee // CraftBukkit start // this.level.createExplosion(this, this.locX(), this.locY(), this.locZ(), 1.0F, false, explosion_effect); diff --git a/src/main/java/net/minecraft/world/entity/raid/Raider.java b/src/main/java/net/minecraft/world/entity/raid/Raider.java -index 039544609aa4fd2f5ab5075792a2e51ef315dc37..eeeef593d0dc075ded76d7d30b76fcfc8ffcb294 100644 +index 74a0866513ea04f635089badfd5cde132d2d1355..a66e456582743f11a458ff22ad8b0467a7e40813 100644 --- a/src/main/java/net/minecraft/world/entity/raid/Raider.java +++ b/src/main/java/net/minecraft/world/entity/raid/Raider.java @@ -313,7 +313,7 @@ public abstract class Raider extends PatrollingMonster { diff --git a/patches/server/0136-Movement-options-for-armor-stands.patch b/patches/server/0136-Movement-options-for-armor-stands.patch index c21e45202..5e4439361 100644 --- a/patches/server/0136-Movement-options-for-armor-stands.patch +++ b/patches/server/0136-Movement-options-for-armor-stands.patch @@ -17,10 +17,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/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java -index cd25a1e6671276ecb465a60ee969122fa5648ded..1747b075d86585f795cde85f390a0a95b80ad3d7 100644 +index 02a2496539813b1d34b9e5ccac0590e6b58c17b5..fcb640e531704b127efe820bb269e7bf9b95c605 100644 --- a/src/main/java/net/minecraft/world/entity/Entity.java +++ b/src/main/java/net/minecraft/world/entity/Entity.java -@@ -1588,7 +1588,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, i +@@ -1615,7 +1615,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, i return this.isInWater() || flag; } diff --git a/patches/server/0137-Fix-stuck-in-portals.patch b/patches/server/0137-Fix-stuck-in-portals.patch index 50b19210d..5770cbee9 100644 --- a/patches/server/0137-Fix-stuck-in-portals.patch +++ b/patches/server/0137-Fix-stuck-in-portals.patch @@ -17,10 +17,10 @@ index 951e45f7b17f6f903c99634177395464a1889c0f..d31ae3a1df1a457d7ae55bc82e9c870c // CraftBukkit end this.setLevel(worldserver); diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java -index 1747b075d86585f795cde85f390a0a95b80ad3d7..d046f2f85efd54c3fe91ca5020bbc7b326005275 100644 +index fcb640e531704b127efe820bb269e7bf9b95c605..430a95752aa590d40a54fb96d7484b55a781a1b6 100644 --- a/src/main/java/net/minecraft/world/entity/Entity.java +++ b/src/main/java/net/minecraft/world/entity/Entity.java -@@ -2713,12 +2713,15 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, i +@@ -2740,12 +2740,15 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, i return Vec3.directionFromRotation(this.getRotationVector()); } diff --git a/patches/server/0138-Toggle-for-water-sensitive-mob-damage.patch b/patches/server/0138-Toggle-for-water-sensitive-mob-damage.patch index 8b40e0dd4..c3aec6afa 100644 --- a/patches/server/0138-Toggle-for-water-sensitive-mob-damage.patch +++ b/patches/server/0138-Toggle-for-water-sensitive-mob-damage.patch @@ -21,7 +21,7 @@ index ca2bf36120ba09ed27ad7b1064431f9feffa3420..2feec97ffe62cfe063085e3767937e8a @Override 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 743b5d483a230d56207a8864b5abab06d7c68e16..ef5cd71dcbaa4f86977fe9ae76042c49c144da9c 100644 +index e82592e3e43f38a0f09803c7b111d73a90361f88..c1a2bfb521ba72e4d1b29aa3e0c72e0c28a652be 100644 --- a/src/main/java/net/minecraft/world/entity/ambient/Bat.java +++ b/src/main/java/net/minecraft/world/entity/ambient/Bat.java @@ -98,6 +98,11 @@ public class Bat extends AmbientCreature { @@ -402,7 +402,7 @@ index 20e0e74d7e43f436172ca7b4cb8b613ba116f9ec..663683a50f7a1125f406b6ec8bf7a041 @Override 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 4020f631f4f02834550a60630d5d429004d71269..d0edbad76a749163f77723547001e59352654e12 100644 +index 1ac7aa1f49a9740b5e20a5452410a6557ce98eda..8e6c2aadd7f69dc6bd585242bf4e23540e75ceeb 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 @@ -116,6 +116,11 @@ public class Axolotl extends Animal implements LerpingModel, Bucketable { @@ -418,7 +418,7 @@ index 4020f631f4f02834550a60630d5d429004d71269..d0edbad76a749163f77723547001e593 @Override 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 b72756afc34afa3f4f8ad838eba3a24faf7e46d0..d8bd81f1c8069f81983ccf0aee8200bd67c913de 100644 +index 33e7abf0b02695d191a9b827dc82f432c3381167..7b5782c3a7026b24aea2bc8be5d23b5d16b86fd4 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 @@ -94,6 +94,11 @@ public class Goat extends Animal { @@ -655,7 +655,7 @@ index e25374b06e3babd93cd5bbdd58ea401a92dbb001..0aa4d8e506462cfe6fb69414a30cc5eb public static AttributeSupplier.Builder createAttributes() { 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 bd58dbee7f2922d341a6c201b762a52e78366106..1ab913281012305cba379ba7301a76501f7d3ed5 100644 +index 8486b37d37fda4cc380a81edb1675e469e1a4e91..5932a5f4f641846637f0afe4c3496910bfdd5570 100644 --- a/src/main/java/net/minecraft/world/entity/monster/EnderMan.java +++ b/src/main/java/net/minecraft/world/entity/monster/EnderMan.java @@ -293,7 +293,7 @@ public class EnderMan extends Monster implements NeutralMob { @@ -1081,7 +1081,7 @@ index f8b742ee8e637edd8e8b05aff2fcc7a75afe959a..fd2828469badbc7936ea68c0bdd8d689 @Override 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 c536b0f0d0ebe5b6b6409972eb711f84fc42afb7..866665186e7dd20a00a635d4675da2e7ac57ae91 100644 +index 0cc680cdcbda6b5b5dfb08df8a2e52510cd5b854..6a41212b1fa2fe61177901dd64c77a92d7d502a4 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 @@ -87,6 +87,11 @@ public class Hoglin extends Animal implements Enemy, HoglinBase { @@ -1097,7 +1097,7 @@ index c536b0f0d0ebe5b6b6409972eb711f84fc42afb7..866665186e7dd20a00a635d4675da2e7 @Override 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 6beb798df62223fe653afa0f34e4a43d6cff11b7..9410c57b4999915ccff9588895fd9bec307499bd 100644 +index badf1fc1a5f5ca0086b45876171a18cad00896b7..89e4364c6c55b5fa0e34e90c87fc622583277c73 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 @@ -111,6 +111,11 @@ public class Piglin extends AbstractPiglin implements CrossbowAttackMob, Invento @@ -1129,10 +1129,10 @@ index f6734f2eb44af7b2389de5079831e0e4f8a1c742..d3a298734e4434d29b66e94fc6c299d3 public static AttributeSupplier.Builder createAttributes() { 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 a78b5416a850d6533b71e3d288224141d5077a77..4420cc8f05f98a2f69507386f7f2893602ef3a59 100644 +index 1d711f67709b8af89d4ada3191fdce716e92047c..56df45d9ff5d4ea608c970f26486795f9776d714 100644 --- a/src/main/java/net/minecraft/world/entity/npc/Villager.java +++ b/src/main/java/net/minecraft/world/entity/npc/Villager.java -@@ -179,6 +179,11 @@ public class Villager extends AbstractVillager implements ReputationEventHandler +@@ -181,6 +181,11 @@ public class Villager extends AbstractVillager implements ReputationEventHandler public boolean canBeLeashed(Player player) { return level.purpurConfig.villagerCanBeLeashed && !this.isLeashed(); } diff --git a/patches/server/0142-Dont-run-with-scissors.patch b/patches/server/0142-Dont-run-with-scissors.patch index 15e2c4847..43c6c6c5c 100644 --- a/patches/server/0142-Dont-run-with-scissors.patch +++ b/patches/server/0142-Dont-run-with-scissors.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Dont run with scissors! diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -index 93f8d5b707811b73fb15cedc4bac83d825ba9e15..6ce4f59b25d8b546bc7c9c1f544b49ba529ee45c 100644 +index d069a2816eab77223ba355449a149b3b1e465b60..fb9fbd708ab8aa9690f8c61182a1590c56b3ee6d 100644 --- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java +++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -@@ -1588,6 +1588,13 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Ser +@@ -1589,6 +1589,13 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Ser this.player.resetFallDistance(); } @@ -22,7 +22,7 @@ index 93f8d5b707811b73fb15cedc4bac83d825ba9e15..6ce4f59b25d8b546bc7c9c1f544b49ba 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(); -@@ -1621,6 +1628,12 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Ser +@@ -1622,6 +1629,12 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Ser } // Paper end - optimise out extra getCubes diff --git a/patches/server/0143-One-Punch-Man.patch b/patches/server/0143-One-Punch-Man.patch index 7cb0e3240..cfeb20c03 100644 --- a/patches/server/0143-One-Punch-Man.patch +++ b/patches/server/0143-One-Punch-Man.patch @@ -5,10 +5,10 @@ Subject: [PATCH] One Punch Man! diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java -index d0fbbaaa48d7e199ab22bc0b089f2ad45fe816ee..24b2d3b768f0b3fd0bd2238419aec6e06bcb1a70 100644 +index f86e65530182685517e12ea609a4bdcd8c493161..2290038b18ccd2757e305870d0a4a39cf674c713 100644 --- a/src/main/java/net/minecraft/world/entity/LivingEntity.java +++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java -@@ -2138,6 +2138,20 @@ public abstract class LivingEntity extends Entity { +@@ -2159,6 +2159,20 @@ public abstract class LivingEntity extends Entity { ((ServerPlayer) damagesource.getEntity()).awardStat(Stats.DAMAGE_DEALT_ABSORBED, Math.round(f2 * 10.0F)); } diff --git a/patches/server/0156-Add-config-for-villager-trading.patch b/patches/server/0156-Add-config-for-villager-trading.patch index 8c946ea15..989c47b7f 100644 --- a/patches/server/0156-Add-config-for-villager-trading.patch +++ b/patches/server/0156-Add-config-for-villager-trading.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Add config for villager trading 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 f82d4019aba5fd89e0f8d41a4dc6e42abd228510..fae4a7c79baac15a06b737b6081956a55b024c8d 100644 +index 56df45d9ff5d4ea608c970f26486795f9776d714..31ca5ddf748313f7d6cdee906d13e8dcb616875d 100644 --- a/src/main/java/net/minecraft/world/entity/npc/Villager.java +++ b/src/main/java/net/minecraft/world/entity/npc/Villager.java -@@ -357,7 +357,7 @@ public class Villager extends AbstractVillager implements ReputationEventHandler +@@ -365,7 +365,7 @@ public class Villager extends AbstractVillager implements ReputationEventHandler return tryRide(player, hand); // Purpur } else { if (level.purpurConfig.villagerRidable && itemstack.isEmpty()) return tryRide(player, hand); // Purpur diff --git a/patches/server/0158-Drowning-Settings.patch b/patches/server/0158-Drowning-Settings.patch index 73baf7730..d25d5885b 100644 --- a/patches/server/0158-Drowning-Settings.patch +++ b/patches/server/0158-Drowning-Settings.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Drowning Settings diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java -index d046f2f85efd54c3fe91ca5020bbc7b326005275..1dbd9be6b72b3ab85243cc6d02af0977b7ae75ad 100644 +index 430a95752aa590d40a54fb96d7484b55a781a1b6..5477f0294d27391edb1ea8fd36dbdc011392950a 100644 --- a/src/main/java/net/minecraft/world/entity/Entity.java +++ b/src/main/java/net/minecraft/world/entity/Entity.java -@@ -502,7 +502,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, i +@@ -529,7 +529,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, i // Spigot end this.entityData = new SynchedEntityData(this); this.entityData.define(Entity.DATA_SHARED_FLAGS_ID, (byte) 0); @@ -17,7 +17,7 @@ index d046f2f85efd54c3fe91ca5020bbc7b326005275..1dbd9be6b72b3ab85243cc6d02af0977 this.entityData.define(Entity.DATA_CUSTOM_NAME_VISIBLE, false); this.entityData.define(Entity.DATA_CUSTOM_NAME, Optional.empty()); this.entityData.define(Entity.DATA_SILENT, false); -@@ -2947,7 +2947,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, i +@@ -2974,7 +2974,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, i } public int getMaxAirSupply() { @@ -27,10 +27,10 @@ index d046f2f85efd54c3fe91ca5020bbc7b326005275..1dbd9be6b72b3ab85243cc6d02af0977 public int getAirSupply() { diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java -index 24b2d3b768f0b3fd0bd2238419aec6e06bcb1a70..a46e6d55731c7877fd50329b1ad37951e1f02784 100644 +index 2290038b18ccd2757e305870d0a4a39cf674c713..19b71de5039f1c5affe9abadf3fdf38ce75f01f7 100644 --- a/src/main/java/net/minecraft/world/entity/LivingEntity.java +++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java -@@ -421,7 +421,7 @@ public abstract class LivingEntity extends Entity { +@@ -419,7 +419,7 @@ public abstract class LivingEntity extends Entity { if (flag1) { this.setAirSupply(this.decreaseAirSupply(this.getAirSupply())); @@ -39,7 +39,7 @@ index 24b2d3b768f0b3fd0bd2238419aec6e06bcb1a70..a46e6d55731c7877fd50329b1ad37951 this.setAirSupply(0); Vec3 vec3d = this.getDeltaMovement(); -@@ -433,7 +433,7 @@ public abstract class LivingEntity extends Entity { +@@ -431,7 +431,7 @@ public abstract class LivingEntity extends Entity { this.level.addParticle(ParticleTypes.BUBBLE, this.getX() + d2, this.getY() + d3, this.getZ() + d4, vec3d.x, vec3d.y, vec3d.z); } diff --git a/patches/server/0169-Configurable-damage-settings-for-magma-blocks.patch b/patches/server/0169-Configurable-damage-settings-for-magma-blocks.patch index 8b4881711..41d549f60 100644 --- a/patches/server/0169-Configurable-damage-settings-for-magma-blocks.patch +++ b/patches/server/0169-Configurable-damage-settings-for-magma-blocks.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Configurable damage settings for magma blocks diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java -index 1dbd9be6b72b3ab85243cc6d02af0977b7ae75ad..f15cce18d4cfb56d8b54bb8f50af206ad48f8cd1 100644 +index 5477f0294d27391edb1ea8fd36dbdc011392950a..ffe4024267f432c9eba37a6712e444e55c69799d 100644 --- a/src/main/java/net/minecraft/world/entity/Entity.java +++ b/src/main/java/net/minecraft/world/entity/Entity.java -@@ -1024,7 +1024,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, i +@@ -1051,7 +1051,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, i } // CraftBukkit end diff --git a/patches/server/0175-Configurable-mob-blindness.patch b/patches/server/0175-Configurable-mob-blindness.patch index 1207ebecb..dd6f36bf2 100644 --- a/patches/server/0175-Configurable-mob-blindness.patch +++ b/patches/server/0175-Configurable-mob-blindness.patch @@ -6,10 +6,10 @@ Subject: [PATCH] Configurable mob blindness Ported from https://github.com/raltsmc/mobblindness diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java -index a46e6d55731c7877fd50329b1ad37951e1f02784..9da2d74f38110abfeaa42ac15b4523dfe7db7e07 100644 +index 19b71de5039f1c5affe9abadf3fdf38ce75f01f7..b7d580fc174b69c23dbbc18e114f3d3119a8e9ea 100644 --- a/src/main/java/net/minecraft/world/entity/LivingEntity.java +++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java -@@ -988,6 +988,17 @@ public abstract class LivingEntity extends Entity { +@@ -986,6 +986,17 @@ public abstract class LivingEntity extends Entity { if (entitytypes == EntityType.SKELETON && itemstack.is(Items.SKELETON_SKULL) || entitytypes == EntityType.ZOMBIE && itemstack.is(Items.ZOMBIE_HEAD) || entitytypes == EntityType.CREEPER && itemstack.is(Items.CREEPER_HEAD)) { d0 *= 0.5D; } diff --git a/patches/server/0187-API-for-any-mob-to-burn-daylight.patch b/patches/server/0187-API-for-any-mob-to-burn-daylight.patch index adf5b15fa..107c9f1e4 100644 --- a/patches/server/0187-API-for-any-mob-to-burn-daylight.patch +++ b/patches/server/0187-API-for-any-mob-to-burn-daylight.patch @@ -6,10 +6,10 @@ Subject: [PATCH] API for any mob to burn daylight Co-authored by: Encode42 diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java -index f15cce18d4cfb56d8b54bb8f50af206ad48f8cd1..2673c49e46e0acbac413feec146000d535721404 100644 +index ffe4024267f432c9eba37a6712e444e55c69799d..1e4d6c36576ebfc9ff643d87d73ee6a908a06876 100644 --- a/src/main/java/net/minecraft/world/entity/Entity.java +++ b/src/main/java/net/minecraft/world/entity/Entity.java -@@ -4352,5 +4352,18 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, i +@@ -4431,5 +4431,18 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, i public boolean canSaveToDisk() { return true; } @@ -29,10 +29,10 @@ index f15cce18d4cfb56d8b54bb8f50af206ad48f8cd1..2673c49e46e0acbac413feec146000d5 // Purpur end } diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java -index a8702eafb732944d189e386646ed219113c1d811..ea66ac3bbf619feec50e3e73c60f25c14356abbb 100644 +index b7d580fc174b69c23dbbc18e114f3d3119a8e9ea..bf01f496e2b89f6fbd44d845a12cdfaed41e5f13 100644 --- a/src/main/java/net/minecraft/world/entity/LivingEntity.java +++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java -@@ -262,6 +262,7 @@ public abstract class LivingEntity extends Entity { +@@ -261,6 +261,7 @@ public abstract class LivingEntity extends Entity { public boolean bukkitPickUpLoot; 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 @@ -40,7 +40,7 @@ index a8702eafb732944d189e386646ed219113c1d811..ea66ac3bbf619feec50e3e73c60f25c1 @Override public float getBukkitYaw() { -@@ -765,6 +766,7 @@ public abstract class LivingEntity extends Entity { +@@ -763,6 +764,7 @@ public abstract class LivingEntity extends Entity { dataresult.resultOrPartial(logger::error).ifPresent((nbtbase) -> { nbt.put("Brain", nbtbase); }); @@ -48,7 +48,7 @@ index a8702eafb732944d189e386646ed219113c1d811..ea66ac3bbf619feec50e3e73c60f25c1 } @Override -@@ -840,6 +842,11 @@ public abstract class LivingEntity extends Entity { +@@ -838,6 +840,11 @@ public abstract class LivingEntity extends Entity { this.brain = this.makeBrain(new Dynamic(NbtOps.INSTANCE, nbt.get("Brain"))); } @@ -60,7 +60,7 @@ index a8702eafb732944d189e386646ed219113c1d811..ea66ac3bbf619feec50e3e73c60f25c1 } // CraftBukkit start -@@ -3408,6 +3415,27 @@ public abstract class LivingEntity extends Entity { +@@ -3429,6 +3436,27 @@ public abstract class LivingEntity extends Entity { this.hurt(DamageSource.DROWN, 1.0F); } @@ -89,10 +89,10 @@ index a8702eafb732944d189e386646ed219113c1d811..ea66ac3bbf619feec50e3e73c60f25c1 public boolean isSensitiveToWater() { diff --git a/src/main/java/net/minecraft/world/entity/Mob.java b/src/main/java/net/minecraft/world/entity/Mob.java -index afb05e183908f720546ace65ddaab45cc2d75525..1d1492f2b705f6d0138b747206a381c8d038c3fa 100644 +index 4d2ed6947d661d0f40c9a40824cbd957806c5a3a..fd8c372169ebbca95ffdf23dc1a7d61bee420ed2 100644 --- a/src/main/java/net/minecraft/world/entity/Mob.java +++ b/src/main/java/net/minecraft/world/entity/Mob.java -@@ -1658,17 +1658,7 @@ public abstract class Mob extends LivingEntity { +@@ -1662,17 +1662,7 @@ public abstract class Mob extends LivingEntity { } public boolean isSunBurnTick() { diff --git a/patches/server/0191-Add-toggle-for-end-portal-safe-teleporting.patch b/patches/server/0191-Add-toggle-for-end-portal-safe-teleporting.patch index 173954462..a7dad6c95 100644 --- a/patches/server/0191-Add-toggle-for-end-portal-safe-teleporting.patch +++ b/patches/server/0191-Add-toggle-for-end-portal-safe-teleporting.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Add toggle for end portal safe teleporting diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java -index 2673c49e46e0acbac413feec146000d535721404..8fb0b23f7b643c6bc321f3a3b4acfab78646721b 100644 +index 1e4d6c36576ebfc9ff643d87d73ee6a908a06876..2ed6964132513fa7b38fe4733aea4afd0153a735 100644 --- a/src/main/java/net/minecraft/world/entity/Entity.java +++ b/src/main/java/net/minecraft/world/entity/Entity.java -@@ -2764,7 +2764,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, i +@@ -2791,7 +2791,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, i } this.processPortalCooldown(); diff --git a/patches/server/0193-Make-lightning-rod-range-configurable.patch b/patches/server/0193-Make-lightning-rod-range-configurable.patch index 21787829d..f5423f895 100644 --- a/patches/server/0193-Make-lightning-rod-range-configurable.patch +++ b/patches/server/0193-Make-lightning-rod-range-configurable.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Make lightning rod range configurable diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java -index fca42bb14e41e7e853af83a2f4b37d586b68c6fa..474bb004678d9482a3fc5c00b2bcf3f3a293f217 100644 +index 4b0250d4c51c4f9c346671fba63abfbe137a1873..543cdbd0785aa83eaf79ae52f0e2e5d3c343f6d0 100644 --- a/src/main/java/net/minecraft/server/level/ServerLevel.java +++ b/src/main/java/net/minecraft/server/level/ServerLevel.java -@@ -900,7 +900,7 @@ public class ServerLevel extends Level implements WorldGenLevel { +@@ -915,7 +915,7 @@ public class ServerLevel extends Level implements WorldGenLevel { return villageplacetype == PoiType.LIGHTNING_ROD; }, (blockposition1) -> { return blockposition1.getY() == this.getLevel().getHeight(Heightmap.Types.WORLD_SURFACE, blockposition1.getX(), blockposition1.getZ()) - 1; diff --git a/patches/server/0208-Customizable-sleeping-actionbar-messages.patch b/patches/server/0208-Customizable-sleeping-actionbar-messages.patch index 89878b8c0..24db833c3 100644 --- a/patches/server/0208-Customizable-sleeping-actionbar-messages.patch +++ b/patches/server/0208-Customizable-sleeping-actionbar-messages.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Customizable sleeping actionbar messages diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java -index 474bb004678d9482a3fc5c00b2bcf3f3a293f217..5a42fde11c219a1572bf31663487ff1fff0d1312 100644 +index 543cdbd0785aa83eaf79ae52f0e2e5d3c343f6d0..5dc9bbc5fe3de52a8a680b1864aeb6fc778c66f7 100644 --- a/src/main/java/net/minecraft/server/level/ServerLevel.java +++ b/src/main/java/net/minecraft/server/level/ServerLevel.java -@@ -949,11 +949,29 @@ public class ServerLevel extends Level implements WorldGenLevel { +@@ -964,11 +964,29 @@ 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); diff --git a/patches/server/0213-Config-to-disable-Enderman-teleport-on-projectile-hi.patch b/patches/server/0213-Config-to-disable-Enderman-teleport-on-projectile-hi.patch index a28f44c9e..5e7abf8e2 100644 --- a/patches/server/0213-Config-to-disable-Enderman-teleport-on-projectile-hi.patch +++ b/patches/server/0213-Config-to-disable-Enderman-teleport-on-projectile-hi.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Config to disable Enderman teleport on projectile hit 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 bba5ce10a4dc06272936ca26d1ac0e5a98c2da1f..8923274fc04282cf97398b997603f699d92e6699 100644 +index fa2d6b478ed420e36622a0bde48131661d483d8b..a20b888ba856b8c588af5cad5c2d72874f3aeaea 100644 --- a/src/main/java/net/minecraft/world/entity/monster/EnderMan.java +++ b/src/main/java/net/minecraft/world/entity/monster/EnderMan.java -@@ -400,7 +400,7 @@ public class EnderMan extends Monster implements NeutralMob { +@@ -406,7 +406,7 @@ public class EnderMan extends Monster implements NeutralMob { return false; } else if (getRider() != null) { return super.hurt(source, amount); // Purpur - no teleporting on damage } else if (org.purpurmc.purpur.PurpurConfig.endermanShortHeight && source == DamageSource.IN_WALL) { return false; // Purpur - no suffocation damage if short height diff --git a/patches/server/0216-Toggle-for-kinetic-damage.patch b/patches/server/0216-Toggle-for-kinetic-damage.patch index 49957dc41..8989ef608 100644 --- a/patches/server/0216-Toggle-for-kinetic-damage.patch +++ b/patches/server/0216-Toggle-for-kinetic-damage.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Toggle for kinetic damage diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java -index 953c27e0ea5bc5d38f24cf69d40bb80dcb33da91..b6022415925d365931a1110f727d5a22a04b9cee 100644 +index bf01f496e2b89f6fbd44d845a12cdfaed41e5f13..dcadf213c929c693ee60666993288ef09988e62c 100644 --- a/src/main/java/net/minecraft/world/entity/LivingEntity.java +++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java -@@ -2797,7 +2797,11 @@ public abstract class LivingEntity extends Entity { +@@ -2818,7 +2818,11 @@ public abstract class LivingEntity extends Entity { if (f4 > 0.0F) { this.playSound(this.getFallDamageSound((int) f4), 1.0F, 1.0F); diff --git a/patches/server/0228-Added-the-ability-to-add-combustible-items.patch b/patches/server/0228-Added-the-ability-to-add-combustible-items.patch index 27d7ea525..379c467a1 100644 --- a/patches/server/0228-Added-the-ability-to-add-combustible-items.patch +++ b/patches/server/0228-Added-the-ability-to-add-combustible-items.patch @@ -51,10 +51,10 @@ index 0d9b592ebfd22dc679ba7dbfd339db4d6bc5ad44..f5fd6e0fd67c1b760000c2e75ce09d1c private int maxStack = MAX_STACK; public List transaction = new java.util.ArrayList(); diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java -index 71ba48fee88aad08883ae65e0b6eb4a674343f32..1b7a3180ff88b0db2aab6e61b286f9b4d0da6862 100644 +index f69fc2e41a5a319e7e790d6ba0f7c85604787ab5..9b474cebe9999068384e810b5f14d6b3a049a564 100644 --- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java +++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java -@@ -1439,6 +1439,19 @@ public final class CraftServer implements Server { +@@ -1444,6 +1444,19 @@ public final class CraftServer implements Server { return true; } diff --git a/patches/server/0229-Option-for-if-rain-and-thunder-should-stop-on-sleep.patch b/patches/server/0229-Option-for-if-rain-and-thunder-should-stop-on-sleep.patch index 6cf38bfa4..3bded6fef 100644 --- a/patches/server/0229-Option-for-if-rain-and-thunder-should-stop-on-sleep.patch +++ b/patches/server/0229-Option-for-if-rain-and-thunder-should-stop-on-sleep.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Option for if rain and thunder should stop on sleep diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java -index 5a42fde11c219a1572bf31663487ff1fff0d1312..8994844b6519082244fe95c1ba72dc81ac9f7f64 100644 +index 5dc9bbc5fe3de52a8a680b1864aeb6fc778c66f7..c062c169ea03a3e945729f0727caf1f4c4adbd07 100644 --- a/src/main/java/net/minecraft/server/level/ServerLevel.java +++ b/src/main/java/net/minecraft/server/level/ServerLevel.java -@@ -1110,6 +1110,7 @@ public class ServerLevel extends Level implements WorldGenLevel { +@@ -1125,6 +1125,7 @@ public class ServerLevel extends Level implements WorldGenLevel { private void resetWeatherCycle() { // CraftBukkit start @@ -16,7 +16,7 @@ index 5a42fde11c219a1572bf31663487ff1fff0d1312..8994844b6519082244fe95c1ba72dc81 this.serverLevelData.setRaining(false, org.bukkit.event.weather.WeatherChangeEvent.Cause.SLEEP); // Paper - when passing the night // 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.... -@@ -1117,6 +1118,7 @@ public class ServerLevel extends Level implements WorldGenLevel { +@@ -1132,6 +1133,7 @@ public class ServerLevel extends Level implements WorldGenLevel { this.serverLevelData.setRainTime(0); } // CraftBukkit end diff --git a/patches/server/0231-Shift-right-click-to-use-exp-for-mending.patch b/patches/server/0231-Shift-right-click-to-use-exp-for-mending.patch index c794375a4..f3400d515 100644 --- a/patches/server/0231-Shift-right-click-to-use-exp-for-mending.patch +++ b/patches/server/0231-Shift-right-click-to-use-exp-for-mending.patch @@ -36,10 +36,10 @@ index 61d0fcaf3c96a2b241b35b2b21104b9fbf403160..c5c7fd414953fb432549e46918fe7e32 + // Purpur end } diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -index 6ce4f59b25d8b546bc7c9c1f544b49ba529ee45c..9a57f6ad29798adbd0b1d55deb47745c353fdac4 100644 +index fb9fbd708ab8aa9690f8c61182a1590c56b3ee6d..b7499782b5968c33ad530b1557f03ed3a7d0451a 100644 --- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java +++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -@@ -1959,6 +1959,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Ser +@@ -1960,6 +1960,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Ser boolean cancelled; if (movingobjectposition == null || movingobjectposition.getType() != HitResult.Type.BLOCK) { diff --git a/patches/server/0234-Option-for-beds-to-explode-on-villager-sleep.patch b/patches/server/0234-Option-for-beds-to-explode-on-villager-sleep.patch index e8337d3f4..363ca1d16 100644 --- a/patches/server/0234-Option-for-beds-to-explode-on-villager-sleep.patch +++ b/patches/server/0234-Option-for-beds-to-explode-on-villager-sleep.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Option for beds to explode on villager sleep 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 9688755b7d5e8125d411404ea23a06f38c6a7a0c..5b058a8c4ecb7a8b8f5600d761b6df234c35f574 100644 +index 31ca5ddf748313f7d6cdee906d13e8dcb616875d..5e9d901d5fd545b94dd04267b90b3f32a3dd881c 100644 --- a/src/main/java/net/minecraft/world/entity/npc/Villager.java +++ b/src/main/java/net/minecraft/world/entity/npc/Villager.java -@@ -1161,6 +1161,12 @@ public class Villager extends AbstractVillager implements ReputationEventHandler +@@ -1169,6 +1169,12 @@ public class Villager extends AbstractVillager implements ReputationEventHandler @Override public void startSleeping(BlockPos pos) { diff --git a/patches/server/0235-Halloween-options-and-optimizations.patch b/patches/server/0235-Halloween-options-and-optimizations.patch index bf00059ac..c4b5bcac4 100644 --- a/patches/server/0235-Halloween-options-and-optimizations.patch +++ b/patches/server/0235-Halloween-options-and-optimizations.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Halloween options and optimizations 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 ef5cd71dcbaa4f86977fe9ae76042c49c144da9c..df62c7a24560f305832e925afa7ce7b182bb1b35 100644 +index c1a2bfb521ba72e4d1b29aa3e0c72e0c28a652be..3babc327941ba56549fa0e44f020c932efbfc6bd 100644 --- a/src/main/java/net/minecraft/world/entity/ambient/Bat.java +++ b/src/main/java/net/minecraft/world/entity/ambient/Bat.java @@ -312,7 +312,7 @@ public class Bat extends AmbientCreature { @@ -17,14 +17,14 @@ index ef5cd71dcbaa4f86977fe9ae76042c49c144da9c..df62c7a24560f305832e925afa7ce7b1 b0 = 7; } else if (random.nextBoolean()) { return false; -@@ -322,6 +322,7 @@ public class Bat extends AmbientCreature { - } - } - +@@ -326,6 +326,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/monster/AbstractSkeleton.java b/src/main/java/net/minecraft/world/entity/monster/AbstractSkeleton.java index 193622015c6d3dbff787061e13fa098a287bce0c..1c48ac2f0b6cfee952ce5e76d7d886034ef871c0 100644 --- a/src/main/java/net/minecraft/world/entity/monster/AbstractSkeleton.java diff --git a/patches/server/0237-UPnP-Port-Forwarding.patch b/patches/server/0237-UPnP-Port-Forwarding.patch index 93d3cf88c..bab2dedb5 100644 --- a/patches/server/0237-UPnP-Port-Forwarding.patch +++ b/patches/server/0237-UPnP-Port-Forwarding.patch @@ -5,7 +5,7 @@ Subject: [PATCH] UPnP Port Forwarding diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java -index 022cf286342abe9f5349b70e096373a0db2a810d..58143bf03bd3314a4b7b40acb72e203aebce7fa6 100644 +index 80322847f36a255a9e6fa01223f10771531fa15c..ceac9acc88d7d6c012c5b985f4cb9ae2fdcbf98c 100644 --- a/src/main/java/net/minecraft/server/MinecraftServer.java +++ b/src/main/java/net/minecraft/server/MinecraftServer.java @@ -309,6 +309,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop entitytypes = entity.getType(); diff --git a/patches/server/0255-Give-bee-counts-in-beehives-to-Purpur-clients.patch b/patches/server/0255-Give-bee-counts-in-beehives-to-Purpur-clients.patch index f96a32918..13eb20cae 100644 --- a/patches/server/0255-Give-bee-counts-in-beehives-to-Purpur-clients.patch +++ b/patches/server/0255-Give-bee-counts-in-beehives-to-Purpur-clients.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Give bee counts in beehives to Purpur clients diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java -index 58143bf03bd3314a4b7b40acb72e203aebce7fa6..149621028a3bc28d65cbd4c53baff36bb58df6e8 100644 +index ceac9acc88d7d6c012c5b985f4cb9ae2fdcbf98c..42c8fdf6ed4f6ab233964e4f64fb7ec4580e53fa 100644 --- a/src/main/java/net/minecraft/server/MinecraftServer.java +++ b/src/main/java/net/minecraft/server/MinecraftServer.java -@@ -1135,6 +1135,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop