diff --git a/patches/server/0001-Pufferfish-Server-Changes.patch b/patches/server/0001-Pufferfish-Server-Changes.patch
index 829c88b..7bc5a95 100644
--- a/patches/server/0001-Pufferfish-Server-Changes.patch
+++ b/patches/server/0001-Pufferfish-Server-Changes.patch
@@ -19,7 +19,7 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see .
diff --git a/build.gradle.kts b/build.gradle.kts
-index 2374cc9bab5039d0a0dc11d4b2ec573ab75778a7..0485d126f799789b5d5abcaf96da96df25a12dfd 100644
+index 44e0f265432487fe5c3c00c5245041298ade16a0..7b74ec475b1e953080d5aa8b0a10d1680e7c46f1 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -9,8 +9,12 @@ plugins {
@@ -37,7 +37,7 @@ index 2374cc9bab5039d0a0dc11d4b2ec573ab75778a7..0485d126f799789b5d5abcaf96da96df
// Paper start
implementation("org.jline:jline-terminal-jansi:3.21.0")
implementation("net.minecrell:terminalconsoleappender:1.3.0")
-@@ -44,6 +48,13 @@ dependencies {
+@@ -44,6 +48,14 @@ dependencies {
runtimeOnly("org.apache.maven.resolver:maven-resolver-connector-basic:1.7.3")
runtimeOnly("org.apache.maven.resolver:maven-resolver-transport-http:1.7.3")
@@ -47,11 +47,12 @@ index 2374cc9bab5039d0a0dc11d4b2ec573ab75778a7..0485d126f799789b5d5abcaf96da96df
+ exclude(group="org.yaml", module="snakeyaml")
+ }
+ // Pufferfish end
++ implementation("com.github.technove:Flare:34637f3f87") // Pufferfish - flare
+
testImplementation("io.github.classgraph:classgraph:4.8.47") // Paper - mob goal test
testImplementation("junit:junit:4.13.2")
testImplementation("org.hamcrest:hamcrest-library:1.3")
-@@ -52,6 +63,14 @@ dependencies {
+@@ -52,6 +64,14 @@ dependencies {
}
val craftbukkitPackageVersion = "1_19_R1" // Paper
@@ -66,7 +67,7 @@ index 2374cc9bab5039d0a0dc11d4b2ec573ab75778a7..0485d126f799789b5d5abcaf96da96df
tasks.jar {
archiveClassifier.set("dev")
-@@ -64,7 +83,7 @@ tasks.jar {
+@@ -64,7 +84,7 @@ tasks.jar {
attributes(
"Main-Class" to "org.bukkit.craftbukkit.Main",
"Implementation-Title" to "CraftBukkit",
@@ -610,10 +611,10 @@ index 0000000000000000000000000000000000000000..020368da69b9a492155f6de6297f7473
+}
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..ed9c8e882739c02d0d04129d251e4c726b422c07
+index 0000000000000000000000000000000000000000..5d75693abb78f1a6cb3595a4af6d1ca0795c1025
--- /dev/null
+++ b/src/main/java/gg/pufferfish/pufferfish/PufferfishConfig.java
-@@ -0,0 +1,283 @@
+@@ -0,0 +1,315 @@
+package gg.pufferfish.pufferfish;
+
+import gg.pufferfish.pufferfish.simd.SIMDDetection;
@@ -631,6 +632,7 @@ index 0000000000000000000000000000000000000000..ed9c8e882739c02d0d04129d251e4c72
+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;
@@ -639,6 +641,13 @@ index 0000000000000000000000000000000000000000..ed9c8e882739c02d0d04129d251e4c72
+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 {
+
@@ -862,6 +871,30 @@ index 0000000000000000000000000000000000000000..ed9c8e882739c02d0d04129d251e4c72
+
+ setComment("dab", "Optimizes entity brains when", "they're far away from the player");
+ }
++
++ 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 Map projectileTimeouts;
+ private static void projectileTimeouts() {
@@ -1062,6 +1095,699 @@ index 0000000000000000000000000000000000000000..e877921370f6009a4bd204d9b17d2d58
+ }
+}
\ No newline at end of file
+diff --git a/src/main/java/gg/pufferfish/pufferfish/compat/ServerConfigurations.java b/src/main/java/gg/pufferfish/pufferfish/compat/ServerConfigurations.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..4ad189d52b27560424ddb311d0817a334637dc95
+--- /dev/null
++++ b/src/main/java/gg/pufferfish/pufferfish/compat/ServerConfigurations.java
+@@ -0,0 +1,78 @@
++package gg.pufferfish.pufferfish.compat;
++
++import co.aikar.timings.TimingsManager;
++import com.google.common.io.Files;
++import org.bukkit.configuration.InvalidConfigurationException;
++import org.bukkit.configuration.file.YamlConfiguration;
++
++import java.io.ByteArrayOutputStream;
++import java.io.File;
++import java.io.FileInputStream;
++import java.io.IOException;
++import java.nio.charset.StandardCharsets;
++import java.util.Arrays;
++import java.util.HashMap;
++import java.util.List;
++import java.util.Map;
++import java.util.Properties;
++import java.util.stream.Collectors;
++
++public class ServerConfigurations {
++
++ public static final String[] configurationFiles = new String[]{
++ "server.properties",
++ "bukkit.yml",
++ "spigot.yml",
++ // "paper.yml", // TODO: Figure out what to do with this.
++ "pufferfish.yml"
++ };
++
++ public static Map getCleanCopies() throws IOException {
++ Map files = new HashMap<>(configurationFiles.length);
++ for (String file : configurationFiles) {
++ files.put(file, getCleanCopy(file));
++ }
++ return files;
++ }
++
++ public static String getCleanCopy(String configName) throws IOException {
++ File file = new File(configName);
++ List hiddenConfigs = TimingsManager.hiddenConfigs;
++
++ switch (Files.getFileExtension(configName)) {
++ case "properties": {
++ Properties properties = new Properties();
++ try (FileInputStream inputStream = new FileInputStream(file)) {
++ properties.load(inputStream);
++ }
++ for (String hiddenConfig : hiddenConfigs) {
++ properties.remove(hiddenConfig);
++ }
++ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
++ properties.store(outputStream, "");
++ return Arrays.stream(outputStream.toString()
++ .split("\n"))
++ .filter(line -> !line.startsWith("#"))
++ .collect(Collectors.joining("\n"));
++ }
++ case "yml": {
++ YamlConfiguration configuration = new YamlConfiguration();
++ try {
++ configuration.load(file);
++ } catch (InvalidConfigurationException e) {
++ throw new IOException(e);
++ }
++ configuration.options().header(null);
++ for (String key : configuration.getKeys(true)) {
++ if (hiddenConfigs.contains(key)) {
++ configuration.set(key, null);
++ }
++ }
++ return configuration.saveToString();
++ }
++ default:
++ throw new IllegalArgumentException("Bad file type " + configName);
++ }
++ }
++
++}
+diff --git a/src/main/java/gg/pufferfish/pufferfish/flare/CustomCategories.java b/src/main/java/gg/pufferfish/pufferfish/flare/CustomCategories.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..401b42e29bccb5251684062f10b2e0f8b091bc95
+--- /dev/null
++++ b/src/main/java/gg/pufferfish/pufferfish/flare/CustomCategories.java
+@@ -0,0 +1,8 @@
++package gg.pufferfish.pufferfish.flare;
++
++import co.technove.flare.live.category.GraphCategory;
++
++public class CustomCategories {
++ public static final GraphCategory MC_PERF = new GraphCategory("MC Performance");
++ public static final GraphCategory ENTITIES_AND_CHUNKS = new GraphCategory("Entities & Chunks");
++}
+diff --git a/src/main/java/gg/pufferfish/pufferfish/flare/FlareCommand.java b/src/main/java/gg/pufferfish/pufferfish/flare/FlareCommand.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..3785d1512eb650f91d58903672c059e7449598fc
+--- /dev/null
++++ b/src/main/java/gg/pufferfish/pufferfish/flare/FlareCommand.java
+@@ -0,0 +1,136 @@
++package gg.pufferfish.pufferfish.flare;
++
++import co.technove.flare.exceptions.UserReportableException;
++import co.technove.flare.internal.profiling.ProfileType;
++import gg.pufferfish.pufferfish.PufferfishConfig;
++import net.kyori.adventure.text.Component;
++import net.kyori.adventure.text.event.ClickEvent;
++import net.kyori.adventure.text.format.NamedTextColor;
++import net.kyori.adventure.text.format.TextColor;
++import net.kyori.adventure.text.format.TextDecoration;
++import net.minecraft.server.MinecraftServer;
++import org.apache.logging.log4j.Level;
++import org.bukkit.Bukkit;
++import org.bukkit.command.Command;
++import org.bukkit.command.CommandSender;
++import org.bukkit.command.ConsoleCommandSender;
++import org.bukkit.craftbukkit.scheduler.MinecraftInternalPlugin;
++import org.bukkit.util.StringUtil;
++import org.jetbrains.annotations.NotNull;
++
++import java.time.Duration;
++import java.util.ArrayList;
++import java.util.Collections;
++import java.util.List;
++
++public class FlareCommand extends Command {
++
++ private static final String BASE_URL = "https://blog.airplane.gg/flare-tutorial/#setting-the-access-token";
++ private static final TextColor HEX = TextColor.fromHexString("#e3eaea");
++ private static final Component PREFIX = Component.text()
++ .append(Component.text("Flare ✈")
++ .color(TextColor.fromHexString("#6a7eda"))
++ .decoration(TextDecoration.BOLD, true)
++ .append(Component.text(" ", HEX)
++ .decoration(TextDecoration.BOLD, false)))
++ .asComponent();
++
++ public FlareCommand() {
++ super("flare", "Profile your server with Flare", "/flare", Collections.singletonList("profile"));
++ this.setPermission("airplane.flare");
++ }
++
++ @Override
++ public boolean execute(@NotNull CommandSender sender, @NotNull String commandLabel, String @NotNull [] args) {
++ if (!testPermission(sender)) return true;
++ if (PufferfishConfig.accessToken.length() == 0) {
++ Component clickable = Component.text(BASE_URL, HEX, TextDecoration.UNDERLINED).clickEvent(ClickEvent.clickEvent(ClickEvent.Action.OPEN_URL, BASE_URL));
++
++ sender.sendMessage(PREFIX.append(Component.text("Flare currently requires an access token to use. To learn more, visit ").color(HEX).append(clickable)));
++ return true;
++ }
++
++ if (!FlareSetup.isSupported()) {
++ sender.sendMessage(PREFIX.append(
++ Component.text("Profiling is not supported in this environment, check your startup logs for the error.", NamedTextColor.RED)));
++ return true;
++ }
++ if (ProfilingManager.isProfiling()) {
++ if (args.length == 1 && args[0].equalsIgnoreCase("status")) {
++ sender.sendMessage(PREFIX.append(Component.text("Current profile has been ran for " + ProfilingManager.getTimeRan().toString(), HEX)));
++ return true;
++ }
++ if (ProfilingManager.stop()) {
++ if (!(sender instanceof ConsoleCommandSender)) {
++ sender.sendMessage(PREFIX.append(Component.text("Profiling has been stopped.", HEX)));
++ }
++ } else {
++ sender.sendMessage(PREFIX.append(Component.text("Profiling has already been stopped.", HEX)));
++ }
++ } else {
++ ProfileType profileType = ProfileType.ITIMER;
++ if (args.length > 0) {
++ try {
++ profileType = ProfileType.valueOf(args[0].toUpperCase());
++ } catch (Exception e) {
++ sender.sendMessage(PREFIX.append(Component
++ .text("Invalid profile type ", HEX)
++ .append(Component.text(args[0], HEX, TextDecoration.BOLD)
++ .append(Component.text("!", HEX)))
++ ));
++ }
++ }
++ ProfileType finalProfileType = profileType;
++ Bukkit.getScheduler().runTaskAsynchronously(new MinecraftInternalPlugin(), () -> {
++ try {
++ if (ProfilingManager.start(finalProfileType)) {
++ if (!(sender instanceof ConsoleCommandSender)) {
++ sender.sendMessage(PREFIX.append(Component
++ .text("Flare has been started: " + ProfilingManager.getProfilingUri(), HEX)
++ .clickEvent(ClickEvent.openUrl(ProfilingManager.getProfilingUri()))
++ ));
++ sender.sendMessage(PREFIX.append(Component.text(" Run /" + commandLabel + " to stop the Flare.", HEX)));
++ }
++ } else {
++ sender.sendMessage(PREFIX.append(Component
++ .text("Flare has already been started: " + ProfilingManager.getProfilingUri(), HEX)
++ .clickEvent(ClickEvent.openUrl(ProfilingManager.getProfilingUri()))
++ ));
++ }
++ } catch (UserReportableException e) {
++ sender.sendMessage(Component.text("Flare failed to start: " + e.getUserError(), NamedTextColor.RED));
++ if (e.getCause() != null) {
++ MinecraftServer.LOGGER.warn("Flare failed to start", e);
++ }
++ }
++ });
++ }
++ return true;
++ }
++
++ @Override
++ public @NotNull List tabComplete(@NotNull CommandSender sender, @NotNull String alias, String @NotNull [] args) throws IllegalArgumentException {
++ List list = new ArrayList<>();
++ if (ProfilingManager.isProfiling()) {
++ if (args.length == 1) {
++ String lastWord = args[0];
++ if (StringUtil.startsWithIgnoreCase("status", lastWord)) {
++ list.add("status");
++ }
++ if (StringUtil.startsWithIgnoreCase("stop", lastWord)) {
++ list.add("stop");
++ }
++ }
++ } else {
++ if (args.length <= 1) {
++ String lastWord = args.length == 0 ? "" : args[0];
++ for (ProfileType value : ProfileType.values()) {
++ if (StringUtil.startsWithIgnoreCase(value.getInternalName(), lastWord)) {
++ list.add(value.name().toLowerCase());
++ }
++ }
++ }
++ }
++ return list;
++ }
++}
+diff --git a/src/main/java/gg/pufferfish/pufferfish/flare/FlareSetup.java b/src/main/java/gg/pufferfish/pufferfish/flare/FlareSetup.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..cd22e4dcc8b7b57b10a95ef084637249a98e524f
+--- /dev/null
++++ b/src/main/java/gg/pufferfish/pufferfish/flare/FlareSetup.java
+@@ -0,0 +1,33 @@
++package gg.pufferfish.pufferfish.flare;
++
++import co.technove.flare.FlareInitializer;
++import co.technove.flare.internal.profiling.InitializationException;
++import net.minecraft.server.MinecraftServer;
++import org.apache.logging.log4j.Level;
++
++public class FlareSetup {
++
++ private static boolean initialized = false;
++ private static boolean supported = false;
++
++ public static void init() {
++ if (initialized) {
++ return;
++ }
++
++ initialized = true;
++ try {
++ for (String warning : FlareInitializer.initialize()) {
++ MinecraftServer.LOGGER.warn("Flare warning: " + warning);
++ }
++ supported = true;
++ } catch (InitializationException e) {
++ MinecraftServer.LOGGER.warn("Failed to enable Flare:", e);
++ }
++ }
++
++ public static boolean isSupported() {
++ return supported;
++ }
++
++}
+diff --git a/src/main/java/gg/pufferfish/pufferfish/flare/PluginLookup.java b/src/main/java/gg/pufferfish/pufferfish/flare/PluginLookup.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..74aab5eb4b54ffbaf19b8976ffb8ca4a64584006
+--- /dev/null
++++ b/src/main/java/gg/pufferfish/pufferfish/flare/PluginLookup.java
+@@ -0,0 +1,44 @@
++package gg.pufferfish.pufferfish.flare;
++
++import com.google.common.cache.Cache;
++import com.google.common.cache.CacheBuilder;
++import org.bukkit.Bukkit;
++import org.bukkit.plugin.Plugin;
++import org.bukkit.plugin.java.PluginClassLoader;
++
++import java.util.Optional;
++import java.util.concurrent.TimeUnit;
++
++public class PluginLookup {
++ private static final Cache pluginNameCache = CacheBuilder.newBuilder()
++ .expireAfterAccess(1, TimeUnit.MINUTES)
++ .maximumSize(1024)
++ .build();
++
++ public static Optional getPluginForClass(String name) {
++ if (name.startsWith("net.minecraft") || name.startsWith("java.") || name.startsWith("com.mojang") ||
++ name.startsWith("com.google") || name.startsWith("it.unimi") || name.startsWith("sun")) {
++ return Optional.empty();
++ }
++
++ String existing = pluginNameCache.getIfPresent(name);
++ if (existing != null) {
++ return Optional.ofNullable(existing.isEmpty() ? null : existing);
++ }
++
++ String newValue = "";
++
++ for (Plugin plugin : Bukkit.getPluginManager().getPlugins()) {
++ ClassLoader classLoader = plugin.getClass().getClassLoader();
++ if (classLoader instanceof PluginClassLoader) {
++ if (((PluginClassLoader) classLoader)._airplane_hasClass(name)) {
++ newValue = plugin.getName();
++ break;
++ }
++ }
++ }
++
++ pluginNameCache.put(name, newValue);
++ return Optional.ofNullable(newValue.isEmpty() ? null : newValue);
++ }
++}
+diff --git a/src/main/java/gg/pufferfish/pufferfish/flare/ProfilingManager.java b/src/main/java/gg/pufferfish/pufferfish/flare/ProfilingManager.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..e3f76eb11a261c3347f0cd89b5da309bc2dc82f9
+--- /dev/null
++++ b/src/main/java/gg/pufferfish/pufferfish/flare/ProfilingManager.java
+@@ -0,0 +1,151 @@
++package gg.pufferfish.pufferfish.flare;
++
++import co.technove.flare.Flare;
++import co.technove.flare.FlareAuth;
++import co.technove.flare.FlareBuilder;
++import co.technove.flare.exceptions.UserReportableException;
++import co.technove.flare.internal.profiling.ProfileType;
++import gg.pufferfish.pufferfish.PufferfishConfig;
++import gg.pufferfish.pufferfish.PufferfishLogger;
++import gg.pufferfish.pufferfish.compat.ServerConfigurations;
++import gg.pufferfish.pufferfish.flare.collectors.GCEventCollector;
++import gg.pufferfish.pufferfish.flare.collectors.StatCollector;
++import gg.pufferfish.pufferfish.flare.collectors.TPSCollector;
++import gg.pufferfish.pufferfish.flare.collectors.WorldCountCollector;
++import org.bukkit.Bukkit;
++import org.bukkit.craftbukkit.scheduler.MinecraftInternalPlugin;
++import org.bukkit.scheduler.BukkitTask;
++import oshi.SystemInfo;
++import oshi.hardware.CentralProcessor;
++import oshi.hardware.GlobalMemory;
++import oshi.hardware.HardwareAbstractionLayer;
++import oshi.hardware.VirtualMemory;
++import oshi.software.os.OperatingSystem;
++
++import java.io.IOException;
++import java.net.URI;
++import java.time.Duration;
++import java.util.Objects;
++import java.util.logging.Level;
++
++public class ProfilingManager {
++
++ private static Flare currentFlare;
++ private static BukkitTask currentTask = null;
++
++ public static synchronized boolean isProfiling() {
++ return currentFlare != null && currentFlare.isRunning();
++ }
++
++ public static synchronized String getProfilingUri() {
++ return Objects.requireNonNull(currentFlare).getURI().map(URI::toString).orElse("Flare is not running");
++ }
++
++ public static Duration getTimeRan() {
++ Flare flare = currentFlare; // copy reference so no need to sync
++ if (flare == null) {
++ return Duration.ofMillis(0);
++ }
++ return flare.getCurrentDuration();
++ }
++
++ public static synchronized boolean start(ProfileType profileType) throws UserReportableException {
++ if (currentFlare != null && !currentFlare.isRunning()) {
++ currentFlare = null; // errored out
++ }
++ if (isProfiling()) {
++ return false;
++ }
++ if (Bukkit.isPrimaryThread()) {
++ throw new UserReportableException("Profiles should be started off-thread");
++ }
++
++ try {
++ OperatingSystem os = new SystemInfo().getOperatingSystem();
++
++ SystemInfo systemInfo = new SystemInfo();
++ HardwareAbstractionLayer hardware = systemInfo.getHardware();
++
++ CentralProcessor processor = hardware.getProcessor();
++ CentralProcessor.ProcessorIdentifier processorIdentifier = processor.getProcessorIdentifier();
++
++ GlobalMemory memory = hardware.getMemory();
++ VirtualMemory virtualMemory = memory.getVirtualMemory();
++
++ FlareBuilder builder = new FlareBuilder()
++ .withProfileType(profileType)
++ .withMemoryProfiling(true)
++ .withAuth(FlareAuth.fromTokenAndUrl(PufferfishConfig.accessToken, PufferfishConfig.profileWebUrl))
++
++ .withFiles(ServerConfigurations.getCleanCopies())
++ .withVersion("Primary Version", Bukkit.getVersion())
++ .withVersion("Bukkit Version", Bukkit.getBukkitVersion())
++ .withVersion("Minecraft Version", Bukkit.getMinecraftVersion())
++
++ .withGraphCategories(CustomCategories.ENTITIES_AND_CHUNKS, CustomCategories.MC_PERF)
++ .withCollectors(new TPSCollector(), new WorldCountCollector(), new GCEventCollector(), new StatCollector())
++ .withClassIdentifier(PluginLookup::getPluginForClass)
++
++ .withHardware(new FlareBuilder.HardwareBuilder()
++ .setCoreCount(processor.getPhysicalProcessorCount())
++ .setThreadCount(processor.getLogicalProcessorCount())
++ .setCpuModel(processorIdentifier.getName())
++ .setCpuFrequency(processor.getMaxFreq())
++
++ .setTotalMemory(memory.getTotal())
++ .setTotalSwap(virtualMemory.getSwapTotal())
++ .setTotalVirtual(virtualMemory.getVirtualMax())
++ )
++
++ .withOperatingSystem(new FlareBuilder.OperatingSystemBuilder()
++ .setManufacturer(os.getManufacturer())
++ .setFamily(os.getFamily())
++ .setVersion(os.getVersionInfo().toString())
++ .setBitness(os.getBitness())
++ );
++
++ currentFlare = builder.build();
++ } catch (IOException e) {
++ PufferfishLogger.LOGGER.log(Level.WARNING, "Failed to read configuration files:", e);
++ throw new UserReportableException("Failed to load configuration files, check logs for further details.");
++ }
++
++ try {
++ currentFlare.start();
++ } catch (IllegalStateException e) {
++ PufferfishLogger.LOGGER.log(Level.WARNING, "Error starting Flare:", e);
++ throw new UserReportableException("Failed to start Flare, check logs for further details.");
++ }
++
++ currentTask = Bukkit.getScheduler().runTaskLater(new MinecraftInternalPlugin(), ProfilingManager::stop, 20 * 60 * 15);
++ PufferfishLogger.LOGGER.log(Level.INFO, "Flare has been started: " + getProfilingUri());
++ return true;
++ }
++
++ public static synchronized boolean stop() {
++ if (!isProfiling()) {
++ return false;
++ }
++ if (!currentFlare.isRunning()) {
++ currentFlare = null;
++ return true;
++ }
++ PufferfishLogger.LOGGER.log(Level.INFO, "Flare has been stopped: " + getProfilingUri());
++ try {
++ currentFlare.stop();
++ } catch (IllegalStateException e) {
++ PufferfishLogger.LOGGER.log(Level.WARNING, "Error occurred stopping Flare", e);
++ }
++ currentFlare = null;
++
++ try {
++ currentTask.cancel();
++ } catch (Throwable t) {
++ PufferfishLogger.LOGGER.log(Level.WARNING, "Error occurred stopping Flare", t);
++ }
++
++ currentTask = null;
++ return true;
++ }
++
++}
+diff --git a/src/main/java/gg/pufferfish/pufferfish/flare/collectors/GCEventCollector.java b/src/main/java/gg/pufferfish/pufferfish/flare/collectors/GCEventCollector.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..d426575c669020f369960107da1e2de2f11f082f
+--- /dev/null
++++ b/src/main/java/gg/pufferfish/pufferfish/flare/collectors/GCEventCollector.java
+@@ -0,0 +1,66 @@
++package gg.pufferfish.pufferfish.flare.collectors;
++
++import co.technove.flare.Flare;
++import co.technove.flare.internal.FlareInternal;
++import co.technove.flare.live.CollectorData;
++import co.technove.flare.live.EventCollector;
++import co.technove.flare.live.LiveEvent;
++import co.technove.flare.live.category.GraphCategory;
++import co.technove.flare.live.formatter.DataFormatter;
++import com.google.common.collect.ImmutableMap;
++import com.sun.management.GarbageCollectionNotificationInfo;
++
++import javax.management.ListenerNotFoundException;
++import javax.management.Notification;
++import javax.management.NotificationEmitter;
++import javax.management.NotificationListener;
++import javax.management.openmbean.CompositeData;
++import java.lang.management.GarbageCollectorMXBean;
++import java.lang.management.ManagementFactory;
++
++public class GCEventCollector extends EventCollector implements NotificationListener {
++
++ private static final CollectorData MINOR_GC = new CollectorData("builtin:gc:minor", "Minor GC", "A small pause in the program to allow Garbage Collection to run.", DataFormatter.MILLISECONDS, GraphCategory.SYSTEM);
++ private static final CollectorData MAJOR_GC = new CollectorData("builtin:gc:major", "Major GC", "A large pause in the program to allow Garbage Collection to run.", DataFormatter.MILLISECONDS, GraphCategory.SYSTEM);
++ private static final CollectorData UNKNOWN_GC = new CollectorData("builtin:gc:generic", "Major GC", "A run of the Garbage Collection.", DataFormatter.MILLISECONDS, GraphCategory.SYSTEM);
++
++ public GCEventCollector() {
++ super(MINOR_GC, MAJOR_GC, UNKNOWN_GC);
++ }
++
++ private static CollectorData fromString(String string) {
++ if (string.endsWith("minor GC")) {
++ return MINOR_GC;
++ } else if (string.endsWith("major GC")) {
++ return MAJOR_GC;
++ }
++ return UNKNOWN_GC;
++ }
++
++ @Override
++ public void start(Flare flare) {
++ for (GarbageCollectorMXBean garbageCollectorBean : ManagementFactory.getGarbageCollectorMXBeans()) {
++ NotificationEmitter notificationEmitter = (NotificationEmitter) garbageCollectorBean;
++ notificationEmitter.addNotificationListener(this, null, null);
++ }
++ }
++
++ @Override
++ public void stop(Flare flare) {
++ for (GarbageCollectorMXBean garbageCollectorBean : ManagementFactory.getGarbageCollectorMXBeans()) {
++ NotificationEmitter notificationEmitter = (NotificationEmitter) garbageCollectorBean;
++ try {
++ notificationEmitter.removeNotificationListener(this);
++ } catch (ListenerNotFoundException e) {
++ }
++ }
++ }
++
++ @Override
++ public void handleNotification(Notification notification, Object o) {
++ if (notification.getType().equals(GarbageCollectionNotificationInfo.GARBAGE_COLLECTION_NOTIFICATION)) {
++ GarbageCollectionNotificationInfo gcInfo = GarbageCollectionNotificationInfo.from((CompositeData) notification.getUserData());
++ reportEvent(new LiveEvent(fromString(gcInfo.getGcAction()), System.currentTimeMillis(), (int) gcInfo.getGcInfo().getDuration(), ImmutableMap.of()));
++ }
++ }
++}
+diff --git a/src/main/java/gg/pufferfish/pufferfish/flare/collectors/StatCollector.java b/src/main/java/gg/pufferfish/pufferfish/flare/collectors/StatCollector.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..a22c6dbae53667e4c72464fa27153aee30c7946e
+--- /dev/null
++++ b/src/main/java/gg/pufferfish/pufferfish/flare/collectors/StatCollector.java
+@@ -0,0 +1,41 @@
++package gg.pufferfish.pufferfish.flare.collectors;
++
++import co.technove.flare.live.CollectorData;
++import co.technove.flare.live.LiveCollector;
++import co.technove.flare.live.category.GraphCategory;
++import co.technove.flare.live.formatter.DataFormatter;
++import com.sun.management.OperatingSystemMXBean;
++import oshi.SystemInfo;
++import oshi.hardware.CentralProcessor;
++
++import java.lang.management.ManagementFactory;
++import java.time.Duration;
++
++public class StatCollector extends LiveCollector {
++
++ private static final CollectorData CPU = new CollectorData("builtin:stat:cpu", "CPU Load", "The total amount of CPU usage across all cores.", DataFormatter.PERCENT, GraphCategory.SYSTEM);
++ private static final CollectorData CPU_PROCESS = new CollectorData("builtin:stat:cpu_process", "Process CPU", "The amount of CPU being used by this process.", DataFormatter.PERCENT, GraphCategory.SYSTEM);
++ private static final CollectorData MEMORY = new CollectorData("builtin:stat:memory_used", "Memory", "The amount of memory being used currently.", DataFormatter.BYTES, GraphCategory.SYSTEM);
++ private static final CollectorData MEMORY_TOTAL = new CollectorData("builtin:stat:memory_total", "Memory Total", "The total amount of memory allocated.", DataFormatter.BYTES, GraphCategory.SYSTEM);
++
++ private final OperatingSystemMXBean bean;
++ private final CentralProcessor processor;
++
++ public StatCollector() {
++ super(CPU, CPU_PROCESS, MEMORY, MEMORY_TOTAL);
++ this.interval = Duration.ofSeconds(5);
++
++ this.bean = (OperatingSystemMXBean) ManagementFactory.getOperatingSystemMXBean();
++ this.processor = new SystemInfo().getHardware().getProcessor();
++ }
++
++ @Override
++ public void run() {
++ Runtime runtime = Runtime.getRuntime();
++
++ this.report(CPU, this.processor.getSystemLoadAverage(1)[0] / 100); // percentage
++ this.report(CPU_PROCESS, this.bean.getProcessCpuLoad());
++ this.report(MEMORY, runtime.totalMemory() - runtime.freeMemory());
++ this.report(MEMORY_TOTAL, runtime.totalMemory());
++ }
++}
+diff --git a/src/main/java/gg/pufferfish/pufferfish/flare/collectors/TPSCollector.java b/src/main/java/gg/pufferfish/pufferfish/flare/collectors/TPSCollector.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..40447d00aefb5ffedb8a2ee87155a04088f0649f
+--- /dev/null
++++ b/src/main/java/gg/pufferfish/pufferfish/flare/collectors/TPSCollector.java
+@@ -0,0 +1,31 @@
++package gg.pufferfish.pufferfish.flare.collectors;
++
++import co.technove.flare.live.CollectorData;
++import co.technove.flare.live.LiveCollector;
++import co.technove.flare.live.formatter.SuffixFormatter;
++import gg.pufferfish.pufferfish.flare.CustomCategories;
++import net.minecraft.server.MinecraftServer;
++import org.bukkit.Bukkit;
++
++import java.time.Duration;
++import java.util.Arrays;
++
++public class TPSCollector extends LiveCollector {
++ private static final CollectorData TPS = new CollectorData("airplane:tps", "TPS", "Ticks per second, or how fast the server updates. For a smooth server this should be a constant 20TPS.", SuffixFormatter.of("TPS"), CustomCategories.MC_PERF);
++ private static final CollectorData MSPT = new CollectorData("airplane:mspt", "MSPT", "Milliseconds per tick, which can show how well your server is performing. This value should always be under 50mspt.", SuffixFormatter.of("mspt"), CustomCategories.MC_PERF);
++
++ public TPSCollector() {
++ super(TPS, MSPT);
++
++ this.interval = Duration.ofSeconds(5);
++ }
++
++ @Override
++ public void run() {
++ long[] times = MinecraftServer.getServer().tickTimes5s.getTimes();
++ double mspt = ((double) Arrays.stream(times).sum() / (double) times.length) * 1.0E-6D;
++
++ this.report(TPS, Math.min(20D, Math.round(Bukkit.getServer().getTPS()[0] * 100d) / 100d));
++ this.report(MSPT, (double) Math.round(mspt * 100d) / 100d);
++ }
++}
+diff --git a/src/main/java/gg/pufferfish/pufferfish/flare/collectors/WorldCountCollector.java b/src/main/java/gg/pufferfish/pufferfish/flare/collectors/WorldCountCollector.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..337a18cad9d328762f8431d1b3f2f8f822485064
+--- /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().getEntityLookup().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
@@ -1415,6 +2141,18 @@ index 0000000000000000000000000000000000000000..facd55463d44cb7e3d2ca6892982f549
+ return backingMap.size();
+ }
+}
+diff --git a/src/main/java/io/papermc/paper/chunk/system/entity/EntityLookup.java b/src/main/java/io/papermc/paper/chunk/system/entity/EntityLookup.java
+index 7d7540cb917564c497eb3a5ef98fb068944694ea..eb22acd39de0c57a9089b452136c39bdf186f316 100644
+--- a/src/main/java/io/papermc/paper/chunk/system/entity/EntityLookup.java
++++ b/src/main/java/io/papermc/paper/chunk/system/entity/EntityLookup.java
+@@ -36,6 +36,7 @@ import java.util.function.Consumer;
+ import java.util.function.Predicate;
+
+ public final class EntityLookup implements LevelEntityGetter {
++ @Override public int getCount() { return this.accessibleEntities.size(); } // Pufferfish // Prismarine - Flare
+
+ private static final Logger LOGGER = LogUtils.getLogger();
+
diff --git a/src/main/java/io/papermc/paper/configuration/GlobalConfiguration.java b/src/main/java/io/papermc/paper/configuration/GlobalConfiguration.java
index 4a8286c78a9a5e305b19cc5d316bc73a78e49b4d..54bca103347e89f116fb7fbf37449a32ac094286 100644
--- a/src/main/java/io/papermc/paper/configuration/GlobalConfiguration.java
@@ -3312,6 +4050,32 @@ index 4cdfc433df67afcd455422e9baf56f167dd712ae..57fcf3910f45ce371ac2e237b277b103
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 9723a0ad61548c8c6c4c5ef20a150d5b17d80afd..bdc430feebba71077405d1a33fca0dd3efabd5de 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 d5129c12c79eb6fe6b7e5f8eed4d24226423f5fd..a9e19ae9b0ad0779dc046c38894beb4dc58042a2 100644
+--- a/src/main/java/net/minecraft/world/level/entity/LevelEntityGetterAdapter.java
++++ b/src/main/java/net/minecraft/world/level/entity/LevelEntityGetterAdapter.java
+@@ -14,6 +14,8 @@ public class LevelEntityGetterAdapter implements LevelEn
+ this.sectionStorage = cache;
+ }
+
++ @Override public int getCount() { return this.visibleEntities.count(); } // Pufferfish
++
+ @Nullable
+ @Override
+ public T get(int id) {
diff --git a/src/main/java/net/minecraft/world/level/material/FlowingFluid.java b/src/main/java/net/minecraft/world/level/material/FlowingFluid.java
index ff40fe323964f173561a6838fb443e79abf9df38..c2c3ed6ba79f9f41497e042571f699a0fc6e9335 100644
--- a/src/main/java/net/minecraft/world/level/material/FlowingFluid.java
@@ -3541,6 +4305,43 @@ index 0b3b46348ac9195bff1492ffc11fcbff7d3f5c6f..4010052c53f3a2831b4d5aa1c041d858
+ 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 1628913b1e9b91e68dcd942a38da4aed95b12d4a..05cc8f9cdcd7e920bf9503f68efb16cd74a359a2 100644
--- a/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java
@@ -3554,6 +4355,18 @@ index 1628913b1e9b91e68dcd942a38da4aed95b12d4a..05cc8f9cdcd7e920bf9503f68efb16cd
}
@Override
+diff --git a/src/main/java/org/bukkit/craftbukkit/util/ServerShutdownThread.java b/src/main/java/org/bukkit/craftbukkit/util/ServerShutdownThread.java
+index e948ec5a573b22645664eb53bc3e9932246544e4..e3845dc3357bbb74885ae3a1a08525adde581235 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
diff --git a/patches/server/0002-Purpur-Server-Changes.patch b/patches/server/0002-Purpur-Server-Changes.patch
index fe35885..70004d5 100644
--- a/patches/server/0002-Purpur-Server-Changes.patch
+++ b/patches/server/0002-Purpur-Server-Changes.patch
@@ -25,7 +25,7 @@ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/build.gradle.kts b/build.gradle.kts
-index 9746ce97d05cace4104e6992126fb983beadc4cf..a61a0f4f1ce89681336d2ed246e6b616aa6fe73d 100644
+index 7b74ec475b1e953080d5aa8b0a10d1680e7c46f1..e2ee97d7a98e6eac3db4f2a06eaa35f1dde45db1 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -9,7 +9,7 @@ plugins {
@@ -56,7 +56,7 @@ index 9746ce97d05cace4104e6992126fb983beadc4cf..a61a0f4f1ce89681336d2ed246e6b616
exclude(group="org.yaml", module="snakeyaml")
}
// Pufferfish end
-@@ -83,7 +86,7 @@ tasks.jar {
+@@ -84,7 +87,7 @@ tasks.jar {
attributes(
"Main-Class" to "org.bukkit.craftbukkit.Main",
"Implementation-Title" to "CraftBukkit",
@@ -65,7 +65,7 @@ index 9746ce97d05cace4104e6992126fb983beadc4cf..a61a0f4f1ce89681336d2ed246e6b616
"Implementation-Vendor" to date, // Paper
"Specification-Title" to "Bukkit",
"Specification-Version" to project.version,
-@@ -182,7 +185,7 @@ fun TaskContainer.registerRunTask(
+@@ -183,7 +186,7 @@ fun TaskContainer.registerRunTask(
name: String,
block: JavaExec.() -> Unit
): TaskProvider = register(name) {
@@ -273,10 +273,10 @@ index 46cab7a8c7b87ab01b26074b04f5a02b3907cfc4..f7449f6ec04356175a0349001a6daa27
}
}
diff --git a/src/main/java/gg/pufferfish/pufferfish/PufferfishConfig.java b/src/main/java/gg/pufferfish/pufferfish/PufferfishConfig.java
-index ed9c8e882739c02d0d04129d251e4c726b422c07..c9ae76401278057f87ebef13619a114722e81a1d 100644
+index 5d75693abb78f1a6cb3595a4af6d1ca0795c1025..7cda3da7f9eb68e06570765ef8e0fa0b92ffb1ab 100644
--- a/src/main/java/gg/pufferfish/pufferfish/PufferfishConfig.java
+++ b/src/main/java/gg/pufferfish/pufferfish/PufferfishConfig.java
-@@ -28,6 +28,7 @@ public class PufferfishConfig {
+@@ -36,6 +36,7 @@ public class PufferfishConfig {
private static final YamlFile config = new YamlFile();
private static int updates = 0;
@@ -284,7 +284,7 @@ index ed9c8e882739c02d0d04129d251e4c726b422c07..c9ae76401278057f87ebef13619a1147
private static ConfigurationSection convertToBukkit(org.simpleyaml.configuration.ConfigurationSection section) {
ConfigurationSection newSection = new MemoryConfiguration();
-@@ -50,7 +51,7 @@ public class PufferfishConfig {
+@@ -58,7 +59,7 @@ public class PufferfishConfig {
}
public static void load() throws IOException {
@@ -293,7 +293,7 @@ index ed9c8e882739c02d0d04129d251e4c726b422c07..c9ae76401278057f87ebef13619a1147
if (configFile.exists()) {
try {
-@@ -113,6 +114,22 @@ public class PufferfishConfig {
+@@ -121,6 +122,22 @@ public class PufferfishConfig {
config.setComment(key, String.join("\n", comment), CommentType.BLOCK);
}
}
@@ -316,7 +316,7 @@ index ed9c8e882739c02d0d04129d251e4c726b422c07..c9ae76401278057f87ebef13619a1147
private static boolean getBoolean(String key, boolean defaultValue, String... comment) {
return getBoolean(key, null, defaultValue, comment);
-@@ -222,7 +239,7 @@ public class PufferfishConfig {
+@@ -230,7 +247,7 @@ public class PufferfishConfig {
public static int activationDistanceMod;
private static void dynamicActivationOfBrains() throws IOException {
@@ -325,7 +325,7 @@ index ed9c8e882739c02d0d04129d251e4c726b422c07..c9ae76401278057f87ebef13619a1147
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.");
-@@ -266,7 +283,7 @@ public class PufferfishConfig {
+@@ -298,7 +315,7 @@ public class PufferfishConfig {
public static boolean throttleInactiveGoalSelectorTick;
private static void inactiveGoalSelectorThrottle() {
diff --git a/patches/server/0005-Rebrand.patch b/patches/server/0005-Rebrand.patch
index 7088904..fdca6b2 100644
--- a/patches/server/0005-Rebrand.patch
+++ b/patches/server/0005-Rebrand.patch
@@ -5,7 +5,7 @@ Subject: [PATCH] Rebrand
diff --git a/build.gradle.kts b/build.gradle.kts
-index 4e4bc7e55a81dc44d7f6cfc6a8761141d0d3bb6c..86b5ec9cc5cf3dbfdaef3ad33d547d245d261ea1 100644
+index e2ee97d7a98e6eac3db4f2a06eaa35f1dde45db1..762d7405ddb661ce2913dff6963e076767297a52 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -9,7 +9,7 @@ plugins {
@@ -17,7 +17,7 @@ index 4e4bc7e55a81dc44d7f6cfc6a8761141d0d3bb6c..86b5ec9cc5cf3dbfdaef3ad33d547d24
// Pufferfish start
implementation("io.papermc.paper:paper-mojangapi:1.19.2-R0.1-SNAPSHOT") {
exclude("io.papermc.paper", "paper-api")
-@@ -86,7 +86,7 @@ tasks.jar {
+@@ -87,7 +87,7 @@ tasks.jar {
attributes(
"Main-Class" to "org.bukkit.craftbukkit.Main",
"Implementation-Title" to "CraftBukkit",
diff --git a/patches/server/0026-Revert-Purpur-commit-disable-pufferfish-mechanical-c.patch b/patches/server/0026-Revert-Purpur-commit-disable-pufferfish-mechanical-c.patch
index 35aae4d..3c3388a 100644
--- a/patches/server/0026-Revert-Purpur-commit-disable-pufferfish-mechanical-c.patch
+++ b/patches/server/0026-Revert-Purpur-commit-disable-pufferfish-mechanical-c.patch
@@ -6,10 +6,10 @@ Subject: [PATCH] Revert Purpur commit "disable pufferfish mechanical changes
diff --git a/src/main/java/gg/pufferfish/pufferfish/PufferfishConfig.java b/src/main/java/gg/pufferfish/pufferfish/PufferfishConfig.java
-index c9ae76401278057f87ebef13619a114722e81a1d..e7266845645982dfbc7708e702ed39135ac5f732 100644
+index 7cda3da7f9eb68e06570765ef8e0fa0b92ffb1ab..86cdfabc754c55f68f6d8328e4a91473db254ddd 100644
--- a/src/main/java/gg/pufferfish/pufferfish/PufferfishConfig.java
+++ b/src/main/java/gg/pufferfish/pufferfish/PufferfishConfig.java
-@@ -239,7 +239,7 @@ public class PufferfishConfig {
+@@ -247,7 +247,7 @@ public class PufferfishConfig {
public static int activationDistanceMod;
private static void dynamicActivationOfBrains() throws IOException {
@@ -18,7 +18,7 @@ index c9ae76401278057f87ebef13619a114722e81a1d..e7266845645982dfbc7708e702ed3913
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.");
-@@ -283,7 +283,7 @@ public class PufferfishConfig {
+@@ -315,7 +315,7 @@ public class PufferfishConfig {
public static boolean throttleInactiveGoalSelectorTick;
private static void inactiveGoalSelectorThrottle() {