diff --git a/gradle.properties b/gradle.properties index 363bc5c..1ff5872 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,7 +1,7 @@ group = org.plazmamc.plazma version = 1.19.4-R0.1-SNAPSHOT -paperCommit = d5abb94e69ceed6e73116c13e7634b57b5ad84cf +paperCommit = fbf74ba0ac016c408bbec28e7da317b68a81e2e1 org.gradle.caching = true org.gradle.parallel = true diff --git a/patches/api/0001-Pufferfish-API-Changes.patch b/patches/api/0001-Pufferfish-API-Changes.patch index 0a7dd13..cdc55e1 100644 --- a/patches/api/0001-Pufferfish-API-Changes.patch +++ b/patches/api/0001-Pufferfish-API-Changes.patch @@ -1,6 +1,6 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: AlphaKR93 -Date: Thu, 23 Mar 2023 10:28:40 +0900 +Date: Mon, 27 Mar 2023 02:50:22 +0000 Subject: [PATCH] Pufferfish API Changes Original: Kevin Raneri @@ -47,7 +47,7 @@ index cad12a2632b9ebb569280441c42869685db1f31a..b83e2c5a0a094002d12aee55ec0cf8d1 into("META-INF/maven/${project.group}/${project.name}") diff --git a/src/main/java/gg/pufferfish/pufferfish/sentry/SentryContext.java b/src/main/java/gg/pufferfish/pufferfish/sentry/SentryContext.java new file mode 100644 -index 0000000000000000000000000000000000000000..10310fdd53de28efb8a8250f6d3b0c8eb08fb68a +index 0000000000000000000000000000000000000000..6fbaf2a232745db0a41394b1c2cc0cc90cefc4ee --- /dev/null +++ b/src/main/java/gg/pufferfish/pufferfish/sentry/SentryContext.java @@ -0,0 +1,161 @@ @@ -69,21 +69,21 @@ index 0000000000000000000000000000000000000000..10310fdd53de28efb8a8250f6d3b0c8e +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()); @@ -92,15 +92,15 @@ index 0000000000000000000000000000000000000000..10310fdd53de28efb8a8250f6d3b0c8e + } + } + } -+ ++ + 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; @@ -108,36 +108,36 @@ index 0000000000000000000000000000000000000000..10310fdd53de28efb8a8250f6d3b0c8e + 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()); @@ -146,7 +146,7 @@ index 0000000000000000000000000000000000000000..10310fdd53de28efb8a8250f6d3b0c8e + if (Modifier.isStatic(declaredField.getModifiers())) { + continue; + } -+ ++ + String fieldName = declaredField.getName(); + if (fieldName.equals("handlers")) { + continue; @@ -162,51 +162,51 @@ index 0000000000000000000000000000000000000000..10310fdd53de28efb8a8250f6d3b0c8e + } + 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; + } @@ -214,7 +214,7 @@ index 0000000000000000000000000000000000000000..10310fdd53de28efb8a8250f6d3b0c8e +} diff --git a/src/main/java/gg/pufferfish/pufferfish/simd/SIMDChecker.java b/src/main/java/gg/pufferfish/pufferfish/simd/SIMDChecker.java new file mode 100644 -index 0000000000000000000000000000000000000000..ab5fea0b03224bf249352ce340e94704ff713345 +index 0000000000000000000000000000000000000000..6d266ebf5e52745ad13e90e5754b524383fa9b29 --- /dev/null +++ b/src/main/java/gg/pufferfish/pufferfish/simd/SIMDChecker.java @@ -0,0 +1,40 @@ @@ -231,7 +231,7 @@ index 0000000000000000000000000000000000000000..ab5fea0b03224bf249352ce340e94704 + */ +@Deprecated +public class SIMDChecker { -+ ++ + @Deprecated + public static boolean canEnable(Logger logger) { + try { @@ -239,13 +239,13 @@ index 0000000000000000000000000000000000000000..ab5fea0b03224bf249352ce340e94704 + return false; + } else { + SIMDDetection.testRun = true; -+ ++ + VectorSpecies ISPEC = IntVector.SPECIES_PREFERRED; + VectorSpecies FSPEC = FloatVector.SPECIES_PREFERRED; -+ ++ + logger.log(Level.INFO, "Max SIMD vector size on this system is " + ISPEC.vectorBitSize() + " bits (int)"); + logger.log(Level.INFO, "Max SIMD vector size on this system is " + FSPEC.vectorBitSize() + " bits (float)"); -+ ++ + if (ISPEC.elementSize() < 2 || FSPEC.elementSize() < 2) { + logger.log(Level.WARNING, "SIMD is not properly supported on this system!"); + return false; @@ -256,11 +256,11 @@ index 0000000000000000000000000000000000000000..ab5fea0b03224bf249352ce340e94704 + } catch (NoClassDefFoundError | Exception ignored) {} // Basically, we don't do anything. This lets us detect if it's not functional and disable it. + return false; + } -+ ++ +} diff --git a/src/main/java/gg/pufferfish/pufferfish/simd/SIMDDetection.java b/src/main/java/gg/pufferfish/pufferfish/simd/SIMDDetection.java new file mode 100644 -index 0000000000000000000000000000000000000000..a84889d3e9cfc4d7ab5f867820a6484c6070711b +index 0000000000000000000000000000000000000000..fd708554d6dab2ddcd24c3024330b8ebf9462111 --- /dev/null +++ b/src/main/java/gg/pufferfish/pufferfish/simd/SIMDDetection.java @@ -0,0 +1,35 @@ @@ -270,11 +270,11 @@ index 0000000000000000000000000000000000000000..a84889d3e9cfc4d7ab5f867820a6484c + +@Deprecated +public class SIMDDetection { -+ ++ + public static boolean isEnabled = false; + public static boolean versionLimited = false; + public static boolean testRun = false; -+ ++ + @Deprecated + public static boolean canEnable(Logger logger) { + try { @@ -283,7 +283,7 @@ index 0000000000000000000000000000000000000000..a84889d3e9cfc4d7ab5f867820a6484c + return false; + } + } -+ ++ + @Deprecated + public static int getJavaVersion() { + // https://stackoverflow.com/a/2591122 @@ -297,11 +297,11 @@ index 0000000000000000000000000000000000000000..a84889d3e9cfc4d7ab5f867820a6484c + version = version.split("-")[0]; // Azul is stupid + return Integer.parseInt(version); + } -+ ++ +} diff --git a/src/main/java/gg/pufferfish/pufferfish/simd/VectorMapPalette.java b/src/main/java/gg/pufferfish/pufferfish/simd/VectorMapPalette.java new file mode 100644 -index 0000000000000000000000000000000000000000..ae2464920c9412ac90b819a540ee58be0741465f +index 0000000000000000000000000000000000000000..20ec3b29b0cb4061cc89d635b3929ffe71008e22 --- /dev/null +++ b/src/main/java/gg/pufferfish/pufferfish/simd/VectorMapPalette.java @@ -0,0 +1,83 @@ @@ -316,10 +316,10 @@ index 0000000000000000000000000000000000000000..ae2464920c9412ac90b819a540ee58be + +@Deprecated +public class VectorMapPalette { -+ ++ + private static final VectorSpecies I_SPEC = IntVector.SPECIES_PREFERRED; + private static final VectorSpecies F_SPEC = FloatVector.SPECIES_PREFERRED; -+ ++ + @Deprecated + public static void matchColorVectorized(int[] in, byte[] out) { + int speciesLength = I_SPEC.length(); @@ -329,64 +329,64 @@ index 0000000000000000000000000000000000000000..ae2464920c9412ac90b819a540ee58be + float[] bluesArr = new float[speciesLength]; + float[] greensArr = new float[speciesLength]; + int[] alphasArr = new int[speciesLength]; -+ ++ + for (int j = 0; j < speciesLength; j++) { + alphasArr[j] = (in[i + j] >> 24) & 0xFF; + redsArr[j] = (in[i + j] >> 16) & 0xFF; + greensArr[j] = (in[i + j] >> 8) & 0xFF; + bluesArr[j] = (in[i + j] >> 0) & 0xFF; + } -+ ++ + IntVector alphas = IntVector.fromArray(I_SPEC, alphasArr, 0); + FloatVector reds = FloatVector.fromArray(F_SPEC, redsArr, 0); + FloatVector greens = FloatVector.fromArray(F_SPEC, greensArr, 0); + FloatVector blues = FloatVector.fromArray(F_SPEC, bluesArr, 0); + IntVector resultIndex = IntVector.zero(I_SPEC); + VectorMask modificationMask = VectorMask.fromLong(I_SPEC, 0xffffffff); -+ ++ + modificationMask = modificationMask.and(alphas.lt(128).not()); + FloatVector bestDistances = FloatVector.broadcast(F_SPEC, Float.MAX_VALUE); -+ ++ + for (int c = 4; c < MapPalette.colors.length; c++) { + // We're using 32-bit floats here because it's 2x faster and nobody will know the difference. + // For correctness, the original algorithm uses 64-bit floats instead. Completely unnecessary. + FloatVector compReds = FloatVector.broadcast(F_SPEC, MapPalette.colors[c].getRed()); + FloatVector compGreens = FloatVector.broadcast(F_SPEC, MapPalette.colors[c].getGreen()); + FloatVector compBlues = FloatVector.broadcast(F_SPEC, MapPalette.colors[c].getBlue()); -+ ++ + FloatVector rMean = reds.add(compReds).div(2.0f); + FloatVector rDiff = reds.sub(compReds); + FloatVector gDiff = greens.sub(compGreens); + FloatVector bDiff = blues.sub(compBlues); -+ ++ + FloatVector weightR = rMean.div(256.0f).add(2); + FloatVector weightG = FloatVector.broadcast(F_SPEC, 4.0f); + FloatVector weightB = FloatVector.broadcast(F_SPEC, 255.0f).sub(rMean).div(256.0f).add(2.0f); -+ ++ + FloatVector distance = weightR.mul(rDiff).mul(rDiff).add(weightG.mul(gDiff).mul(gDiff)).add(weightB.mul(bDiff).mul(bDiff)); -+ ++ + // Now we compare to the best distance we've found. + // This mask contains a "1" if better, and a "0" otherwise. + VectorMask bestDistanceMask = distance.lt(bestDistances); + bestDistances = bestDistances.blend(distance, bestDistanceMask); // Update the best distances -+ ++ + // Update the result array + // We also AND with the modification mask because we don't want to interfere if the alpha value isn't large enough. + resultIndex = resultIndex.blend(c, bestDistanceMask.cast(I_SPEC).and(modificationMask)); // Update the results + } -+ ++ + for (int j = 0; j < speciesLength; j++) { + int index = resultIndex.lane(j); + out[i + j] = (byte) (index < 128 ? index : -129 + (index - 127)); + } + } -+ ++ + // For the final ones, fall back to the regular method + for (; i < in.length; i++) { + out[i] = MapPalette.matchColor(new Color(in[i], true)); + } + } -+ ++ +} diff --git a/src/main/java/org/bukkit/map/MapPalette.java b/src/main/java/org/bukkit/map/MapPalette.java index 3a9aaca2e76411a9c27f9f5e0f22d060d5a66d06..9584e245144b561b4f6745b2f26a4f69a6f92891 100644 diff --git a/patches/api/0002-Purpur-API-Changes.patch b/patches/api/0002-Purpur-API-Changes.patch index a9cddc7..43e51a8 100644 --- a/patches/api/0002-Purpur-API-Changes.patch +++ b/patches/api/0002-Purpur-API-Changes.patch @@ -1,6 +1,6 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: AlphaKR93 -Date: Thu, 23 Mar 2023 10:35:54 +0900 +Date: Mon, 27 Mar 2023 02:52:12 +0000 Subject: [PATCH] Purpur API Changes Original: PurpurMC diff --git a/patches/server/0001-Pufferfish-Server-Changes.patch b/patches/server/0001-Pufferfish-Server-Changes.patch index 1609a1e..b8864d5 100644 --- a/patches/server/0001-Pufferfish-Server-Changes.patch +++ b/patches/server/0001-Pufferfish-Server-Changes.patch @@ -1,6 +1,6 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: AlphaKR93 -Date: Thu, 23 Mar 2023 10:28:40 +0900 +Date: Mon, 27 Mar 2023 02:50:22 +0000 Subject: [PATCH] Pufferfish Server Changes Original: Kevin Raneri @@ -20,7 +20,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . diff --git a/build.gradle.kts b/build.gradle.kts -index 9cf389defdaeb887e9cad4f0fed3f3b95667b238..3c8293f002f11b430083502362fdc801f44aa138 100644 +index 9cf389defdaeb887e9cad4f0fed3f3b95667b238..b41b186397d013c19436c345be98b785d4bd0295 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -7,8 +7,12 @@ plugins { @@ -38,7 +38,7 @@ index 9cf389defdaeb887e9cad4f0fed3f3b95667b238..3c8293f002f11b430083502362fdc801 // Paper start implementation("org.jline:jline-terminal-jansi:3.21.0") implementation("net.minecrell:terminalconsoleappender:1.3.0") -@@ -42,6 +46,14 @@ dependencies { +@@ -42,6 +46,13 @@ 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") @@ -48,12 +48,11 @@ index 9cf389defdaeb887e9cad4f0fed3f3b95667b238..3c8293f002f11b430083502362fdc801 + 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") -@@ -50,6 +62,14 @@ dependencies { +@@ -50,6 +61,14 @@ dependencies { } val craftbukkitPackageVersion = "1_19_R3" // Paper @@ -68,7 +67,7 @@ index 9cf389defdaeb887e9cad4f0fed3f3b95667b238..3c8293f002f11b430083502362fdc801 tasks.jar { archiveClassifier.set("dev") -@@ -62,7 +82,7 @@ tasks.jar { +@@ -62,7 +81,7 @@ tasks.jar { attributes( "Main-Class" to "org.bukkit.craftbukkit.Main", "Implementation-Title" to "CraftBukkit", @@ -543,7 +542,7 @@ index 0000000000000000000000000000000000000000..a7f297ebb569f7c1f205e967ca485be7 +} 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 +index 0000000000000000000000000000000000000000..e164237e749bcc43466d4ed7aeada5ab9fddf8a6 --- /dev/null +++ b/src/main/java/gg/pufferfish/pufferfish/PufferfishCommand.java @@ -0,0 +1,68 @@ @@ -571,7 +570,7 @@ index 0000000000000000000000000000000000000000..020368da69b9a492155f6de6297f7473 + this.usageMessage = "/pufferfish [reload | version]"; + this.setPermission("bukkit.command.pufferfish"); + } -+ ++ + public static void init() { + MinecraftServer.getServer().server.getCommandMap().register("pufferfish", "Pufferfish", new PufferfishCommand()); + } @@ -617,10 +616,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..e0076044d0a29e33ee297fe6180a041436075297 +index 0000000000000000000000000000000000000000..a8cead500186142115d4dc029c942fdfc68f7fe5 --- /dev/null +++ b/src/main/java/gg/pufferfish/pufferfish/PufferfishConfig.java -@@ -0,0 +1,317 @@ +@@ -0,0 +1,285 @@ +package gg.pufferfish.pufferfish; + +import gg.pufferfish.pufferfish.simd.SIMDDetection; @@ -638,7 +637,6 @@ index 0000000000000000000000000000000000000000..e0076044d0a29e33ee297fe6180a0414 +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; @@ -647,19 +645,12 @@ index 0000000000000000000000000000000000000000..e0076044d0a29e33ee297fe6180a0414 +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)) { @@ -671,18 +662,18 @@ index 0000000000000000000000000000000000000000..e0076044d0a29e33ee297fe6180a0414 + } + 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); @@ -690,14 +681,14 @@ index 0000000000000000000000000000000000000000..e0076044d0a29e33ee297fe6180a0414 + 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")) { @@ -709,11 +700,11 @@ index 0000000000000000000000000000000000000000..e0076044d0a29e33ee297fe6180a0414 + } + } + } -+ ++ + updates++; -+ ++ + config.save(configFile); -+ ++ + // Attempt to detect vectorization + try { + SIMDDetection.isEnabled = SIMDDetection.canEnable(PufferfishLogger.LOGGER); @@ -721,7 +712,7 @@ index 0000000000000000000000000000000000000000..e0076044d0a29e33ee297fe6180a0414 + } catch (NoClassDefFoundError | Exception ignored) { + ignored.printStackTrace(); + } -+ ++ + if (SIMDDetection.isEnabled) { + PufferfishLogger.LOGGER.info("SIMD operations detected as functional. Will replace some operations with faster versions."); + } else if (SIMDDetection.versionLimited) { @@ -733,76 +724,76 @@ index 0000000000000000000000000000000000000000..e0076044d0a29e33ee297fe6180a0414 + PufferfishLogger.LOGGER.warning("Debug: Java: " + System.getProperty("java.version") + ", test run: " + SIMDDetection.testRun); + } + } -+ ++ + private static void setComment(String key, String... comment) { + if (config.contains(key)) { + config.setComment(key, String.join("\n", comment), CommentType.BLOCK); + } + } -+ ++ + private static void ensureDefault(String key, Object defaultValue, String... comment) { + if (!config.contains(key)) { + config.set(key, defaultValue); + config.setComment(key, String.join("\n", comment), CommentType.BLOCK); + } + } -+ ++ + private static boolean getBoolean(String key, boolean defaultValue, String... comment) { + return getBoolean(key, null, defaultValue, comment); + } -+ ++ + private static boolean getBoolean(String key, @Nullable String oldKey, boolean defaultValue, String... comment) { + ensureDefault(key, defaultValue, comment); + return config.getBoolean(key, defaultValue); + } -+ ++ + private static int getInt(String key, int defaultValue, String... comment) { + return getInt(key, null, defaultValue, comment); + } -+ ++ + private static int getInt(String key, @Nullable String oldKey, int defaultValue, String... comment) { + ensureDefault(key, defaultValue, comment); + return config.getInt(key, defaultValue); + } -+ ++ + private static double getDouble(String key, double defaultValue, String... comment) { + return getDouble(key, null, defaultValue, comment); + } -+ ++ + private static double getDouble(String key, @Nullable String oldKey, double defaultValue, String... comment) { + ensureDefault(key, defaultValue, comment); + return config.getDouble(key, defaultValue); + } -+ ++ + private static String getString(String key, String defaultValue, String... comment) { + return getOldString(key, null, defaultValue, comment); + } -+ ++ + private static String getOldString(String key, @Nullable String oldKey, String defaultValue, String... comment) { + ensureDefault(key, defaultValue, comment); + return config.getString(key, defaultValue); + } -+ ++ + private static List getStringList(String key, List defaultValue, String... comment) { + return getStringList(key, null, defaultValue, comment); + } -+ ++ + private static List getStringList(String key, @Nullable String oldKey, List defaultValue, String... comment) { + ensureDefault(key, defaultValue, comment); + return config.getStringList(key); + } -+ ++ + public static String sentryDsn; + private static void sentry() { + String sentryEnvironment = System.getenv("SENTRY_DSN"); + String sentryConfig = getString("sentry-dsn", "", "Sentry DSN for improved error logging, leave blank to disable", "Obtain from https://sentry.io/"); -+ ++ + sentryDsn = sentryEnvironment == null ? sentryConfig : sentryEnvironment; + if (sentryDsn != null && !sentryDsn.isBlank()) { + gg.pufferfish.pufferfish.sentry.SentryManager.init(); + } + } -+ ++ + public static boolean enableBooks; + private static void books() { + enableBooks = getBoolean("enable-books", true, @@ -811,7 +802,7 @@ index 0000000000000000000000000000000000000000..e0076044d0a29e33ee297fe6180a0414 + "disabling this option.", + "This can be overridden per-player with the permission pufferfish.usebooks"); + } -+ ++ + public static boolean enableSuffocationOptimization; + private static void suffocationOptimization() { + enableSuffocationOptimization = getBoolean("enable-suffocation-optimization", true, @@ -820,7 +811,7 @@ index 0000000000000000000000000000000000000000..e0076044d0a29e33ee297fe6180a0414 + "be left enabled on most servers, but is provided as a", + "configuration option if the vanilla deviation is undesirable."); + } -+ ++ + public static boolean enableAsyncMobSpawning; + public static boolean asyncMobSpawningInitialized; + private static void asyncMobSpawning() { @@ -830,14 +821,14 @@ index 0000000000000000000000000000000000000000..e0076044d0a29e33ee297fe6180a0414 + "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() { @@ -879,7 +870,7 @@ index 0000000000000000000000000000000000000000..e0076044d0a29e33ee297fe6180a0414 + + setComment("dab", "Optimizes entity brains when", "they're far away from the player"); + } -+ ++ + public static Map projectileTimeouts; + private static void projectileTimeouts() { + // Set some defaults @@ -890,13 +881,13 @@ index 0000000000000000000000000000000000000000..e0076044d0a29e33ee297fe6180a0414 + "entity is in this list and it has survived for longer than", + "that number of ticks, then it will be removed. Setting a value to", + "-1 disables this feature."); -+ ++ + for (EntityType entityType : BuiltInRegistries.ENTITY_TYPE) { + String type = EntityType.getKey(entityType).getPath().toUpperCase(Locale.ROOT); + entityType.ttl = config.getInt("entity_timeouts." + type, -1); + } + } -+ ++ + public static boolean throttleInactiveGoalSelectorTick; + private static void inactiveGoalSelectorThrottle() { + getBoolean("inactive-goal-selector-throttle", "inactive-goal-selector-disable", true, @@ -904,30 +895,6 @@ index 0000000000000000000000000000000000000000..e0076044d0a29e33ee297fe6180a0414 + "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; + public static boolean disableOutOfOrderChat; @@ -962,7 +929,7 @@ index 0000000000000000000000000000000000000000..53f2df00c6809618a9ee3d2ea72e85e8 +} 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..e877921370f6009a4bd204d9b17d2d58834b8822 +index 0000000000000000000000000000000000000000..1adc3de7cdb5b5f309df45f463e4697d6ec2c245 --- /dev/null +++ b/src/main/java/gg/pufferfish/pufferfish/PufferfishVersionFetcher.java @@ -0,0 +1,136 @@ @@ -994,46 +961,46 @@ index 0000000000000000000000000000000000000000..e877921370f6009a4bd204d9b17d2d58 +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.19/lastSuccessfulBuild/buildNumber"); + private static final String GITHUB_FORMAT = "https://api.github.com/repos/pufferfish-gg/Pufferfish/compare/ver/1.19...%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 { @@ -1041,7 +1008,7 @@ index 0000000000000000000000000000000000000000..e877921370f6009a4bd204d9b17d2d58 + if (response.statusCode() != 200) { + return text("Received invalid status code (" + response.statusCode() + ") from server.", RED); + } -+ ++ + int latestVersionNumber; + try { + latestVersionNumber = Integer.parseInt(response.body()); @@ -1049,7 +1016,7 @@ index 0000000000000000000000000000000000000000..e877921370f6009a4bd204d9b17d2d58 + 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) { @@ -1057,7 +1024,7 @@ index 0000000000000000000000000000000000000000..e877921370f6009a4bd204d9b17d2d58 + 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)); @@ -1067,17 +1034,17 @@ index 0000000000000000000000000000000000000000..e877921370f6009a4bd204d9b17d2d58 + 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); @@ -1087,718 +1054,25 @@ index 0000000000000000000000000000000000000000..e877921370f6009a4bd204d9b17d2d58 + RED); + }; + } -+ ++ + private @Nullable Component getHistory() { + final VersionHistoryManager.VersionData data = VersionHistoryManager.INSTANCE.getVersionData(); + if (data == null) { + return null; + } -+ ++ + final String oldVersion = data.getOldVersion(); + if (oldVersion == null) { + return null; + } -+ ++ + return Component.text("Previous version: " + oldVersion, NamedTextColor.GRAY, TextDecoration.ITALIC); + } +} \ No newline at end of file -diff --git a/src/main/java/gg/pufferfish/pufferfish/compat/ServerConfigurations.java b/src/main/java/gg/pufferfish/pufferfish/compat/ServerConfigurations.java -new file mode 100644 -index 0000000000000000000000000000000000000000..4ad189d52b27560424ddb311d0817a334637dc95 ---- /dev/null -+++ b/src/main/java/gg/pufferfish/pufferfish/compat/ServerConfigurations.java -@@ -0,0 +1,78 @@ -+package gg.pufferfish.pufferfish.compat; -+ -+import co.aikar.timings.TimingsManager; -+import com.google.common.io.Files; -+import org.bukkit.configuration.InvalidConfigurationException; -+import org.bukkit.configuration.file.YamlConfiguration; -+ -+import java.io.ByteArrayOutputStream; -+import java.io.File; -+import java.io.FileInputStream; -+import java.io.IOException; -+import java.nio.charset.StandardCharsets; -+import java.util.Arrays; -+import java.util.HashMap; -+import java.util.List; -+import java.util.Map; -+import java.util.Properties; -+import java.util.stream.Collectors; -+ -+public class ServerConfigurations { -+ -+ public static final String[] configurationFiles = new String[]{ -+ "server.properties", -+ "bukkit.yml", -+ "spigot.yml", -+ // "paper.yml", // TODO: Figure out what to do with this. -+ "pufferfish.yml" -+ }; -+ -+ public static Map getCleanCopies() throws IOException { -+ Map files = new HashMap<>(configurationFiles.length); -+ for (String file : configurationFiles) { -+ files.put(file, getCleanCopy(file)); -+ } -+ return files; -+ } -+ -+ public static String getCleanCopy(String configName) throws IOException { -+ File file = new File(configName); -+ List hiddenConfigs = TimingsManager.hiddenConfigs; -+ -+ switch (Files.getFileExtension(configName)) { -+ case "properties": { -+ Properties properties = new Properties(); -+ try (FileInputStream inputStream = new FileInputStream(file)) { -+ properties.load(inputStream); -+ } -+ for (String hiddenConfig : hiddenConfigs) { -+ properties.remove(hiddenConfig); -+ } -+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); -+ properties.store(outputStream, ""); -+ return Arrays.stream(outputStream.toString() -+ .split("\n")) -+ .filter(line -> !line.startsWith("#")) -+ .collect(Collectors.joining("\n")); -+ } -+ case "yml": { -+ YamlConfiguration configuration = new YamlConfiguration(); -+ try { -+ configuration.load(file); -+ } catch (InvalidConfigurationException e) { -+ throw new IOException(e); -+ } -+ configuration.options().header(null); -+ for (String key : configuration.getKeys(true)) { -+ if (hiddenConfigs.contains(key)) { -+ configuration.set(key, null); -+ } -+ } -+ return configuration.saveToString(); -+ } -+ default: -+ throw new IllegalArgumentException("Bad file type " + configName); -+ } -+ } -+ -+} -diff --git a/src/main/java/gg/pufferfish/pufferfish/flare/CustomCategories.java b/src/main/java/gg/pufferfish/pufferfish/flare/CustomCategories.java -new file mode 100644 -index 0000000000000000000000000000000000000000..401b42e29bccb5251684062f10b2e0f8b091bc95 ---- /dev/null -+++ b/src/main/java/gg/pufferfish/pufferfish/flare/CustomCategories.java -@@ -0,0 +1,8 @@ -+package gg.pufferfish.pufferfish.flare; -+ -+import co.technove.flare.live.category.GraphCategory; -+ -+public class CustomCategories { -+ public static final GraphCategory MC_PERF = new GraphCategory("MC Performance"); -+ public static final GraphCategory ENTITIES_AND_CHUNKS = new GraphCategory("Entities & Chunks"); -+} -diff --git a/src/main/java/gg/pufferfish/pufferfish/flare/FlareCommand.java b/src/main/java/gg/pufferfish/pufferfish/flare/FlareCommand.java -new file mode 100644 -index 0000000000000000000000000000000000000000..3785d1512eb650f91d58903672c059e7449598fc ---- /dev/null -+++ b/src/main/java/gg/pufferfish/pufferfish/flare/FlareCommand.java -@@ -0,0 +1,136 @@ -+package gg.pufferfish.pufferfish.flare; -+ -+import co.technove.flare.exceptions.UserReportableException; -+import co.technove.flare.internal.profiling.ProfileType; -+import gg.pufferfish.pufferfish.PufferfishConfig; -+import net.kyori.adventure.text.Component; -+import net.kyori.adventure.text.event.ClickEvent; -+import net.kyori.adventure.text.format.NamedTextColor; -+import net.kyori.adventure.text.format.TextColor; -+import net.kyori.adventure.text.format.TextDecoration; -+import net.minecraft.server.MinecraftServer; -+import org.apache.logging.log4j.Level; -+import org.bukkit.Bukkit; -+import org.bukkit.command.Command; -+import org.bukkit.command.CommandSender; -+import org.bukkit.command.ConsoleCommandSender; -+import org.bukkit.craftbukkit.scheduler.MinecraftInternalPlugin; -+import org.bukkit.util.StringUtil; -+import org.jetbrains.annotations.NotNull; -+ -+import java.time.Duration; -+import java.util.ArrayList; -+import java.util.Collections; -+import java.util.List; -+ -+public class FlareCommand extends Command { -+ -+ private static final String BASE_URL = "https://blog.airplane.gg/flare-tutorial/#setting-the-access-token"; -+ private static final TextColor HEX = TextColor.fromHexString("#e3eaea"); -+ private static final Component PREFIX = Component.text() -+ .append(Component.text("Flare ✈") -+ .color(TextColor.fromHexString("#6a7eda")) -+ .decoration(TextDecoration.BOLD, true) -+ .append(Component.text(" ", HEX) -+ .decoration(TextDecoration.BOLD, false))) -+ .asComponent(); -+ -+ public FlareCommand() { -+ super("flare", "Profile your server with Flare", "/flare", Collections.singletonList("profile")); -+ this.setPermission("airplane.flare"); -+ } -+ -+ @Override -+ public boolean execute(@NotNull CommandSender sender, @NotNull String commandLabel, String @NotNull [] args) { -+ if (!testPermission(sender)) return true; -+ if (PufferfishConfig.accessToken.length() == 0) { -+ Component clickable = Component.text(BASE_URL, HEX, TextDecoration.UNDERLINED).clickEvent(ClickEvent.clickEvent(ClickEvent.Action.OPEN_URL, BASE_URL)); -+ -+ sender.sendMessage(PREFIX.append(Component.text("Flare currently requires an access token to use. To learn more, visit ").color(HEX).append(clickable))); -+ return true; -+ } -+ -+ if (!FlareSetup.isSupported()) { -+ sender.sendMessage(PREFIX.append( -+ Component.text("Profiling is not supported in this environment, check your startup logs for the error.", NamedTextColor.RED))); -+ return true; -+ } -+ if (ProfilingManager.isProfiling()) { -+ if (args.length == 1 && args[0].equalsIgnoreCase("status")) { -+ sender.sendMessage(PREFIX.append(Component.text("Current profile has been ran for " + ProfilingManager.getTimeRan().toString(), HEX))); -+ return true; -+ } -+ if (ProfilingManager.stop()) { -+ if (!(sender instanceof ConsoleCommandSender)) { -+ sender.sendMessage(PREFIX.append(Component.text("Profiling has been stopped.", HEX))); -+ } -+ } else { -+ sender.sendMessage(PREFIX.append(Component.text("Profiling has already been stopped.", HEX))); -+ } -+ } else { -+ ProfileType profileType = ProfileType.ITIMER; -+ if (args.length > 0) { -+ try { -+ profileType = ProfileType.valueOf(args[0].toUpperCase()); -+ } catch (Exception e) { -+ sender.sendMessage(PREFIX.append(Component -+ .text("Invalid profile type ", HEX) -+ .append(Component.text(args[0], HEX, TextDecoration.BOLD) -+ .append(Component.text("!", HEX))) -+ )); -+ } -+ } -+ ProfileType finalProfileType = profileType; -+ Bukkit.getScheduler().runTaskAsynchronously(new MinecraftInternalPlugin(), () -> { -+ try { -+ if (ProfilingManager.start(finalProfileType)) { -+ if (!(sender instanceof ConsoleCommandSender)) { -+ sender.sendMessage(PREFIX.append(Component -+ .text("Flare has been started: " + ProfilingManager.getProfilingUri(), HEX) -+ .clickEvent(ClickEvent.openUrl(ProfilingManager.getProfilingUri())) -+ )); -+ sender.sendMessage(PREFIX.append(Component.text(" Run /" + commandLabel + " to stop the Flare.", HEX))); -+ } -+ } else { -+ sender.sendMessage(PREFIX.append(Component -+ .text("Flare has already been started: " + ProfilingManager.getProfilingUri(), HEX) -+ .clickEvent(ClickEvent.openUrl(ProfilingManager.getProfilingUri())) -+ )); -+ } -+ } catch (UserReportableException e) { -+ sender.sendMessage(Component.text("Flare failed to start: " + e.getUserError(), NamedTextColor.RED)); -+ if (e.getCause() != null) { -+ MinecraftServer.LOGGER.warn("Flare failed to start", e); -+ } -+ } -+ }); -+ } -+ return true; -+ } -+ -+ @Override -+ public @NotNull List tabComplete(@NotNull CommandSender sender, @NotNull String alias, String @NotNull [] args) throws IllegalArgumentException { -+ List list = new ArrayList<>(); -+ if (ProfilingManager.isProfiling()) { -+ if (args.length == 1) { -+ String lastWord = args[0]; -+ if (StringUtil.startsWithIgnoreCase("status", lastWord)) { -+ list.add("status"); -+ } -+ if (StringUtil.startsWithIgnoreCase("stop", lastWord)) { -+ list.add("stop"); -+ } -+ } -+ } else { -+ if (args.length <= 1) { -+ String lastWord = args.length == 0 ? "" : args[0]; -+ for (ProfileType value : ProfileType.values()) { -+ if (StringUtil.startsWithIgnoreCase(value.getInternalName(), lastWord)) { -+ list.add(value.name().toLowerCase()); -+ } -+ } -+ } -+ } -+ return list; -+ } -+} -diff --git a/src/main/java/gg/pufferfish/pufferfish/flare/FlareSetup.java b/src/main/java/gg/pufferfish/pufferfish/flare/FlareSetup.java -new file mode 100644 -index 0000000000000000000000000000000000000000..cd22e4dcc8b7b57b10a95ef084637249a98e524f ---- /dev/null -+++ b/src/main/java/gg/pufferfish/pufferfish/flare/FlareSetup.java -@@ -0,0 +1,33 @@ -+package gg.pufferfish.pufferfish.flare; -+ -+import co.technove.flare.FlareInitializer; -+import co.technove.flare.internal.profiling.InitializationException; -+import net.minecraft.server.MinecraftServer; -+import org.apache.logging.log4j.Level; -+ -+public class FlareSetup { -+ -+ private static boolean initialized = false; -+ private static boolean supported = false; -+ -+ public static void init() { -+ if (initialized) { -+ return; -+ } -+ -+ initialized = true; -+ try { -+ for (String warning : FlareInitializer.initialize()) { -+ MinecraftServer.LOGGER.warn("Flare warning: " + warning); -+ } -+ supported = true; -+ } catch (InitializationException e) { -+ MinecraftServer.LOGGER.warn("Failed to enable Flare:", e); -+ } -+ } -+ -+ public static boolean isSupported() { -+ return supported; -+ } -+ -+} -diff --git a/src/main/java/gg/pufferfish/pufferfish/flare/PluginLookup.java b/src/main/java/gg/pufferfish/pufferfish/flare/PluginLookup.java -new file mode 100644 -index 0000000000000000000000000000000000000000..74aab5eb4b54ffbaf19b8976ffb8ca4a64584006 ---- /dev/null -+++ b/src/main/java/gg/pufferfish/pufferfish/flare/PluginLookup.java -@@ -0,0 +1,44 @@ -+package gg.pufferfish.pufferfish.flare; -+ -+import com.google.common.cache.Cache; -+import com.google.common.cache.CacheBuilder; -+import org.bukkit.Bukkit; -+import org.bukkit.plugin.Plugin; -+import org.bukkit.plugin.java.PluginClassLoader; -+ -+import java.util.Optional; -+import java.util.concurrent.TimeUnit; -+ -+public class PluginLookup { -+ private static final Cache pluginNameCache = CacheBuilder.newBuilder() -+ .expireAfterAccess(1, TimeUnit.MINUTES) -+ .maximumSize(1024) -+ .build(); -+ -+ public static Optional getPluginForClass(String name) { -+ if (name.startsWith("net.minecraft") || name.startsWith("java.") || name.startsWith("com.mojang") || -+ name.startsWith("com.google") || name.startsWith("it.unimi") || name.startsWith("sun")) { -+ return Optional.empty(); -+ } -+ -+ String existing = pluginNameCache.getIfPresent(name); -+ if (existing != null) { -+ return Optional.ofNullable(existing.isEmpty() ? null : existing); -+ } -+ -+ String newValue = ""; -+ -+ for (Plugin plugin : Bukkit.getPluginManager().getPlugins()) { -+ ClassLoader classLoader = plugin.getClass().getClassLoader(); -+ if (classLoader instanceof PluginClassLoader) { -+ if (((PluginClassLoader) classLoader)._airplane_hasClass(name)) { -+ newValue = plugin.getName(); -+ break; -+ } -+ } -+ } -+ -+ pluginNameCache.put(name, newValue); -+ return Optional.ofNullable(newValue.isEmpty() ? null : newValue); -+ } -+} -diff --git a/src/main/java/gg/pufferfish/pufferfish/flare/ProfilingManager.java b/src/main/java/gg/pufferfish/pufferfish/flare/ProfilingManager.java -new file mode 100644 -index 0000000000000000000000000000000000000000..e3f76eb11a261c3347f0cd89b5da309bc2dc82f9 ---- /dev/null -+++ b/src/main/java/gg/pufferfish/pufferfish/flare/ProfilingManager.java -@@ -0,0 +1,151 @@ -+package gg.pufferfish.pufferfish.flare; -+ -+import co.technove.flare.Flare; -+import co.technove.flare.FlareAuth; -+import co.technove.flare.FlareBuilder; -+import co.technove.flare.exceptions.UserReportableException; -+import co.technove.flare.internal.profiling.ProfileType; -+import gg.pufferfish.pufferfish.PufferfishConfig; -+import gg.pufferfish.pufferfish.PufferfishLogger; -+import gg.pufferfish.pufferfish.compat.ServerConfigurations; -+import gg.pufferfish.pufferfish.flare.collectors.GCEventCollector; -+import gg.pufferfish.pufferfish.flare.collectors.StatCollector; -+import gg.pufferfish.pufferfish.flare.collectors.TPSCollector; -+import gg.pufferfish.pufferfish.flare.collectors.WorldCountCollector; -+import org.bukkit.Bukkit; -+import org.bukkit.craftbukkit.scheduler.MinecraftInternalPlugin; -+import org.bukkit.scheduler.BukkitTask; -+import oshi.SystemInfo; -+import oshi.hardware.CentralProcessor; -+import oshi.hardware.GlobalMemory; -+import oshi.hardware.HardwareAbstractionLayer; -+import oshi.hardware.VirtualMemory; -+import oshi.software.os.OperatingSystem; -+ -+import java.io.IOException; -+import java.net.URI; -+import java.time.Duration; -+import java.util.Objects; -+import java.util.logging.Level; -+ -+public class ProfilingManager { -+ -+ private static Flare currentFlare; -+ private static BukkitTask currentTask = null; -+ -+ public static synchronized boolean isProfiling() { -+ return currentFlare != null && currentFlare.isRunning(); -+ } -+ -+ public static synchronized String getProfilingUri() { -+ return Objects.requireNonNull(currentFlare).getURI().map(URI::toString).orElse("Flare is not running"); -+ } -+ -+ public static Duration getTimeRan() { -+ Flare flare = currentFlare; // copy reference so no need to sync -+ if (flare == null) { -+ return Duration.ofMillis(0); -+ } -+ return flare.getCurrentDuration(); -+ } -+ -+ public static synchronized boolean start(ProfileType profileType) throws UserReportableException { -+ if (currentFlare != null && !currentFlare.isRunning()) { -+ currentFlare = null; // errored out -+ } -+ if (isProfiling()) { -+ return false; -+ } -+ if (Bukkit.isPrimaryThread()) { -+ throw new UserReportableException("Profiles should be started off-thread"); -+ } -+ -+ try { -+ OperatingSystem os = new SystemInfo().getOperatingSystem(); -+ -+ SystemInfo systemInfo = new SystemInfo(); -+ HardwareAbstractionLayer hardware = systemInfo.getHardware(); -+ -+ CentralProcessor processor = hardware.getProcessor(); -+ CentralProcessor.ProcessorIdentifier processorIdentifier = processor.getProcessorIdentifier(); -+ -+ GlobalMemory memory = hardware.getMemory(); -+ VirtualMemory virtualMemory = memory.getVirtualMemory(); -+ -+ FlareBuilder builder = new FlareBuilder() -+ .withProfileType(profileType) -+ .withMemoryProfiling(true) -+ .withAuth(FlareAuth.fromTokenAndUrl(PufferfishConfig.accessToken, PufferfishConfig.profileWebUrl)) -+ -+ .withFiles(ServerConfigurations.getCleanCopies()) -+ .withVersion("Primary Version", Bukkit.getVersion()) -+ .withVersion("Bukkit Version", Bukkit.getBukkitVersion()) -+ .withVersion("Minecraft Version", Bukkit.getMinecraftVersion()) -+ -+ .withGraphCategories(CustomCategories.ENTITIES_AND_CHUNKS, CustomCategories.MC_PERF) -+ .withCollectors(new TPSCollector(), new WorldCountCollector(), new GCEventCollector(), new StatCollector()) -+ .withClassIdentifier(PluginLookup::getPluginForClass) -+ -+ .withHardware(new FlareBuilder.HardwareBuilder() -+ .setCoreCount(processor.getPhysicalProcessorCount()) -+ .setThreadCount(processor.getLogicalProcessorCount()) -+ .setCpuModel(processorIdentifier.getName()) -+ .setCpuFrequency(processor.getMaxFreq()) -+ -+ .setTotalMemory(memory.getTotal()) -+ .setTotalSwap(virtualMemory.getSwapTotal()) -+ .setTotalVirtual(virtualMemory.getVirtualMax()) -+ ) -+ -+ .withOperatingSystem(new FlareBuilder.OperatingSystemBuilder() -+ .setManufacturer(os.getManufacturer()) -+ .setFamily(os.getFamily()) -+ .setVersion(os.getVersionInfo().toString()) -+ .setBitness(os.getBitness()) -+ ); -+ -+ currentFlare = builder.build(); -+ } catch (IOException e) { -+ PufferfishLogger.LOGGER.log(Level.WARNING, "Failed to read configuration files:", e); -+ throw new UserReportableException("Failed to load configuration files, check logs for further details."); -+ } -+ -+ try { -+ currentFlare.start(); -+ } catch (IllegalStateException e) { -+ PufferfishLogger.LOGGER.log(Level.WARNING, "Error starting Flare:", e); -+ throw new UserReportableException("Failed to start Flare, check logs for further details."); -+ } -+ -+ currentTask = Bukkit.getScheduler().runTaskLater(new MinecraftInternalPlugin(), ProfilingManager::stop, 20 * 60 * 15); -+ PufferfishLogger.LOGGER.log(Level.INFO, "Flare has been started: " + getProfilingUri()); -+ return true; -+ } -+ -+ public static synchronized boolean stop() { -+ if (!isProfiling()) { -+ return false; -+ } -+ if (!currentFlare.isRunning()) { -+ currentFlare = null; -+ return true; -+ } -+ PufferfishLogger.LOGGER.log(Level.INFO, "Flare has been stopped: " + getProfilingUri()); -+ try { -+ currentFlare.stop(); -+ } catch (IllegalStateException e) { -+ PufferfishLogger.LOGGER.log(Level.WARNING, "Error occurred stopping Flare", e); -+ } -+ currentFlare = null; -+ -+ try { -+ currentTask.cancel(); -+ } catch (Throwable t) { -+ PufferfishLogger.LOGGER.log(Level.WARNING, "Error occurred stopping Flare", t); -+ } -+ -+ currentTask = null; -+ return true; -+ } -+ -+} -diff --git a/src/main/java/gg/pufferfish/pufferfish/flare/collectors/GCEventCollector.java b/src/main/java/gg/pufferfish/pufferfish/flare/collectors/GCEventCollector.java -new file mode 100644 -index 0000000000000000000000000000000000000000..d426575c669020f369960107da1e2de2f11f082f ---- /dev/null -+++ b/src/main/java/gg/pufferfish/pufferfish/flare/collectors/GCEventCollector.java -@@ -0,0 +1,66 @@ -+package gg.pufferfish.pufferfish.flare.collectors; -+ -+import co.technove.flare.Flare; -+import co.technove.flare.internal.FlareInternal; -+import co.technove.flare.live.CollectorData; -+import co.technove.flare.live.EventCollector; -+import co.technove.flare.live.LiveEvent; -+import co.technove.flare.live.category.GraphCategory; -+import co.technove.flare.live.formatter.DataFormatter; -+import com.google.common.collect.ImmutableMap; -+import com.sun.management.GarbageCollectionNotificationInfo; -+ -+import javax.management.ListenerNotFoundException; -+import javax.management.Notification; -+import javax.management.NotificationEmitter; -+import javax.management.NotificationListener; -+import javax.management.openmbean.CompositeData; -+import java.lang.management.GarbageCollectorMXBean; -+import java.lang.management.ManagementFactory; -+ -+public class GCEventCollector extends EventCollector implements NotificationListener { -+ -+ private static final CollectorData MINOR_GC = new CollectorData("builtin:gc:minor", "Minor GC", "A small pause in the program to allow Garbage Collection to run.", DataFormatter.MILLISECONDS, GraphCategory.SYSTEM); -+ private static final CollectorData MAJOR_GC = new CollectorData("builtin:gc:major", "Major GC", "A large pause in the program to allow Garbage Collection to run.", DataFormatter.MILLISECONDS, GraphCategory.SYSTEM); -+ private static final CollectorData UNKNOWN_GC = new CollectorData("builtin:gc:generic", "Major GC", "A run of the Garbage Collection.", DataFormatter.MILLISECONDS, GraphCategory.SYSTEM); -+ -+ public GCEventCollector() { -+ super(MINOR_GC, MAJOR_GC, UNKNOWN_GC); -+ } -+ -+ private static CollectorData fromString(String string) { -+ if (string.endsWith("minor GC")) { -+ return MINOR_GC; -+ } else if (string.endsWith("major GC")) { -+ return MAJOR_GC; -+ } -+ return UNKNOWN_GC; -+ } -+ -+ @Override -+ public void start(Flare flare) { -+ for (GarbageCollectorMXBean garbageCollectorBean : ManagementFactory.getGarbageCollectorMXBeans()) { -+ NotificationEmitter notificationEmitter = (NotificationEmitter) garbageCollectorBean; -+ notificationEmitter.addNotificationListener(this, null, null); -+ } -+ } -+ -+ @Override -+ public void stop(Flare flare) { -+ for (GarbageCollectorMXBean garbageCollectorBean : ManagementFactory.getGarbageCollectorMXBeans()) { -+ NotificationEmitter notificationEmitter = (NotificationEmitter) garbageCollectorBean; -+ try { -+ notificationEmitter.removeNotificationListener(this); -+ } catch (ListenerNotFoundException e) { -+ } -+ } -+ } -+ -+ @Override -+ public void handleNotification(Notification notification, Object o) { -+ if (notification.getType().equals(GarbageCollectionNotificationInfo.GARBAGE_COLLECTION_NOTIFICATION)) { -+ GarbageCollectionNotificationInfo gcInfo = GarbageCollectionNotificationInfo.from((CompositeData) notification.getUserData()); -+ reportEvent(new LiveEvent(fromString(gcInfo.getGcAction()), System.currentTimeMillis(), (int) gcInfo.getGcInfo().getDuration(), ImmutableMap.of())); -+ } -+ } -+} -diff --git a/src/main/java/gg/pufferfish/pufferfish/flare/collectors/StatCollector.java b/src/main/java/gg/pufferfish/pufferfish/flare/collectors/StatCollector.java -new file mode 100644 -index 0000000000000000000000000000000000000000..a22c6dbae53667e4c72464fa27153aee30c7946e ---- /dev/null -+++ b/src/main/java/gg/pufferfish/pufferfish/flare/collectors/StatCollector.java -@@ -0,0 +1,41 @@ -+package gg.pufferfish.pufferfish.flare.collectors; -+ -+import co.technove.flare.live.CollectorData; -+import co.technove.flare.live.LiveCollector; -+import co.technove.flare.live.category.GraphCategory; -+import co.technove.flare.live.formatter.DataFormatter; -+import com.sun.management.OperatingSystemMXBean; -+import oshi.SystemInfo; -+import oshi.hardware.CentralProcessor; -+ -+import java.lang.management.ManagementFactory; -+import java.time.Duration; -+ -+public class StatCollector extends LiveCollector { -+ -+ private static final CollectorData CPU = new CollectorData("builtin:stat:cpu", "CPU Load", "The total amount of CPU usage across all cores.", DataFormatter.PERCENT, GraphCategory.SYSTEM); -+ private static final CollectorData CPU_PROCESS = new CollectorData("builtin:stat:cpu_process", "Process CPU", "The amount of CPU being used by this process.", DataFormatter.PERCENT, GraphCategory.SYSTEM); -+ private static final CollectorData MEMORY = new CollectorData("builtin:stat:memory_used", "Memory", "The amount of memory being used currently.", DataFormatter.BYTES, GraphCategory.SYSTEM); -+ private static final CollectorData MEMORY_TOTAL = new CollectorData("builtin:stat:memory_total", "Memory Total", "The total amount of memory allocated.", DataFormatter.BYTES, GraphCategory.SYSTEM); -+ -+ private final OperatingSystemMXBean bean; -+ private final CentralProcessor processor; -+ -+ public StatCollector() { -+ super(CPU, CPU_PROCESS, MEMORY, MEMORY_TOTAL); -+ this.interval = Duration.ofSeconds(5); -+ -+ this.bean = (OperatingSystemMXBean) ManagementFactory.getOperatingSystemMXBean(); -+ this.processor = new SystemInfo().getHardware().getProcessor(); -+ } -+ -+ @Override -+ public void run() { -+ Runtime runtime = Runtime.getRuntime(); -+ -+ this.report(CPU, this.processor.getSystemLoadAverage(1)[0] / 100); // percentage -+ this.report(CPU_PROCESS, this.bean.getProcessCpuLoad()); -+ this.report(MEMORY, runtime.totalMemory() - runtime.freeMemory()); -+ this.report(MEMORY_TOTAL, runtime.totalMemory()); -+ } -+} -diff --git a/src/main/java/gg/pufferfish/pufferfish/flare/collectors/TPSCollector.java b/src/main/java/gg/pufferfish/pufferfish/flare/collectors/TPSCollector.java -new file mode 100644 -index 0000000000000000000000000000000000000000..40447d00aefb5ffedb8a2ee87155a04088f0649f ---- /dev/null -+++ b/src/main/java/gg/pufferfish/pufferfish/flare/collectors/TPSCollector.java -@@ -0,0 +1,31 @@ -+package gg.pufferfish.pufferfish.flare.collectors; -+ -+import co.technove.flare.live.CollectorData; -+import co.technove.flare.live.LiveCollector; -+import co.technove.flare.live.formatter.SuffixFormatter; -+import gg.pufferfish.pufferfish.flare.CustomCategories; -+import net.minecraft.server.MinecraftServer; -+import org.bukkit.Bukkit; -+ -+import java.time.Duration; -+import java.util.Arrays; -+ -+public class TPSCollector extends LiveCollector { -+ private static final CollectorData TPS = new CollectorData("airplane:tps", "TPS", "Ticks per second, or how fast the server updates. For a smooth server this should be a constant 20TPS.", SuffixFormatter.of("TPS"), CustomCategories.MC_PERF); -+ private static final CollectorData MSPT = new CollectorData("airplane:mspt", "MSPT", "Milliseconds per tick, which can show how well your server is performing. This value should always be under 50mspt.", SuffixFormatter.of("mspt"), CustomCategories.MC_PERF); -+ -+ public TPSCollector() { -+ super(TPS, MSPT); -+ -+ this.interval = Duration.ofSeconds(5); -+ } -+ -+ @Override -+ public void run() { -+ long[] times = MinecraftServer.getServer().tickTimes5s.getTimes(); -+ double mspt = ((double) Arrays.stream(times).sum() / (double) times.length) * 1.0E-6D; -+ -+ this.report(TPS, Math.min(20D, Math.round(Bukkit.getServer().getTPS()[0] * 100d) / 100d)); -+ this.report(MSPT, (double) Math.round(mspt * 100d) / 100d); -+ } -+} -diff --git a/src/main/java/gg/pufferfish/pufferfish/flare/collectors/WorldCountCollector.java b/src/main/java/gg/pufferfish/pufferfish/flare/collectors/WorldCountCollector.java -new file mode 100644 -index 0000000000000000000000000000000000000000..029d840e28d67d26d3c0dd6785e25dbf15f9226c ---- /dev/null -+++ b/src/main/java/gg/pufferfish/pufferfish/flare/collectors/WorldCountCollector.java -@@ -0,0 +1,45 @@ -+package gg.pufferfish.pufferfish.flare.collectors; -+ -+import co.technove.flare.live.CollectorData; -+import co.technove.flare.live.LiveCollector; -+import co.technove.flare.live.formatter.SuffixFormatter; -+import gg.pufferfish.pufferfish.flare.CustomCategories; -+import org.bukkit.Bukkit; -+import org.bukkit.World; -+ -+import java.time.Duration; -+ -+public class WorldCountCollector extends LiveCollector { -+ -+ private static final CollectorData PLAYER_COUNT = new CollectorData("airplane:world:playercount", "Player Count", "The number of players currently on the server.", new SuffixFormatter(" Player", " Players"), CustomCategories.ENTITIES_AND_CHUNKS); -+ private static final CollectorData ENTITY_COUNT = new CollectorData("airplane:world:entitycount", "Entity Count", "The number of entities in all worlds", new SuffixFormatter(" Entity", " Entities"), CustomCategories.ENTITIES_AND_CHUNKS); -+ private static final CollectorData CHUNK_COUNT = new CollectorData("airplane:world:chunkcount", "Chunk Count", "The number of chunks currently loaded.", new SuffixFormatter(" Chunk", " Chunks"), CustomCategories.ENTITIES_AND_CHUNKS); -+ private static final CollectorData TILE_ENTITY_COUNT = new CollectorData("airplane:world:blockentitycount", "Block Entity Count", "The number of block entities currently loaded.", new SuffixFormatter(" Block Entity", " Block Entities"), CustomCategories.ENTITIES_AND_CHUNKS); -+ -+ public WorldCountCollector() { -+ super(PLAYER_COUNT, ENTITY_COUNT, CHUNK_COUNT, TILE_ENTITY_COUNT); -+ -+ this.interval = Duration.ofSeconds(5); -+ } -+ -+ @Override -+ public void run() { -+ if (true) return; // This doesn't work, and it's not worth fixing at the moment. -+ int entities = 0; -+ int chunkCount = 0; -+ int tileEntityCount = 0; -+ -+ if (!Bukkit.isStopping()) { -+ for (World world : Bukkit.getWorlds()) { -+ world.getEntityCount(); -+ chunkCount += world.getChunkCount(); -+ tileEntityCount += world.getTileEntityCount(); -+ } -+ } -+ -+ this.report(PLAYER_COUNT, Bukkit.getOnlinePlayers().size()); -+ this.report(ENTITY_COUNT, entities); -+ this.report(CHUNK_COUNT, chunkCount); -+ this.report(TILE_ENTITY_COUNT, tileEntityCount); -+ } -+} diff --git a/src/main/java/gg/pufferfish/pufferfish/sentry/PufferfishSentryAppender.java b/src/main/java/gg/pufferfish/pufferfish/sentry/PufferfishSentryAppender.java new file mode 100644 -index 0000000000000000000000000000000000000000..731ef11c7a025ae95ed8a757b530d834733d0621 +index 0000000000000000000000000000000000000000..d04a8a4336566dbe6e1b9ec0d574cff43e003fa8 --- /dev/null +++ b/src/main/java/gg/pufferfish/pufferfish/sentry/PufferfishSentryAppender.java @@ -0,0 +1,135 @@ @@ -1822,14 +1096,14 @@ index 0000000000000000000000000000000000000000..731ef11c7a025ae95ed8a757b530d834 +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)) { @@ -1846,55 +1120,55 @@ index 0000000000000000000000000000000000000000..731ef11c7a025ae95ed8a757b530d834 + } + } + } -+ ++ + 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: @@ -1911,35 +1185,35 @@ index 0000000000000000000000000000000000000000..731ef11c7a025ae95ed8a757b530d834 + 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 +index 0000000000000000000000000000000000000000..b011abbeb80b42de6be3785e47c7ba3c0f6dc161 --- /dev/null +++ b/src/main/java/gg/pufferfish/pufferfish/sentry/SentryManager.java @@ -0,0 +1,40 @@ @@ -1951,27 +1225,27 @@ index 0000000000000000000000000000000000000000..1b29210ad0bbb4ada150f23357f0c80d +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); @@ -1981,11 +1255,11 @@ index 0000000000000000000000000000000000000000..1b29210ad0bbb4ada150f23357f0c80d + initialized = false; + } + } -+ ++ +} diff --git a/src/main/java/gg/pufferfish/pufferfish/util/AsyncExecutor.java b/src/main/java/gg/pufferfish/pufferfish/util/AsyncExecutor.java new file mode 100644 -index 0000000000000000000000000000000000000000..8e5323d5d9af25c8a85c4b34a6be76cfc54384cf +index 0000000000000000000000000000000000000000..0fe6243ea01f39fc43c4ca8897a70feddb7fb11d --- /dev/null +++ b/src/main/java/gg/pufferfish/pufferfish/util/AsyncExecutor.java @@ -0,0 +1,73 @@ @@ -2000,26 +1274,26 @@ index 0000000000000000000000000000000000000000..8e5323d5d9af25c8a85c4b34a6be76cf +import java.util.logging.Level; + +public class AsyncExecutor implements Runnable { -+ ++ + private final Queue jobs = Queues.newArrayDeque(); + private final Lock mutex = new ReentrantLock(); + private final Condition cond = mutex.newCondition(); + private final Thread thread; + private volatile boolean killswitch = false; -+ ++ + public AsyncExecutor(String threadName) { + this.thread = new Thread(this, threadName); + } -+ ++ + public void start() { + thread.start(); + } -+ ++ + public void kill() { + killswitch = true; + cond.signalAll(); + } -+ ++ + public void submit(Runnable runnable) { + mutex.lock(); + try { @@ -2029,7 +1303,7 @@ index 0000000000000000000000000000000000000000..8e5323d5d9af25c8a85c4b34a6be76cf + mutex.unlock(); + } + } -+ ++ + @Override + public void run() { + while (!killswitch) { @@ -2045,26 +1319,26 @@ index 0000000000000000000000000000000000000000..8e5323d5d9af25c8a85c4b34a6be76cf + } + } + } -+ ++ + private Runnable takeRunnable() throws InterruptedException { + mutex.lock(); + try { + while (jobs.isEmpty() && !killswitch) { + cond.await(); + } -+ ++ + if (jobs.isEmpty()) return null; // We've set killswitch -+ ++ + return jobs.remove(); + } finally { + mutex.unlock(); + } + } -+ ++ +} diff --git a/src/main/java/gg/pufferfish/pufferfish/util/AsyncPlayerAreaMap.java b/src/main/java/gg/pufferfish/pufferfish/util/AsyncPlayerAreaMap.java new file mode 100644 -index 0000000000000000000000000000000000000000..fdcb62d12164024a5f354d60cc863821a18d1b2a +index 0000000000000000000000000000000000000000..a93ee99c2399def1e221260547a3e6bce2d621fa --- /dev/null +++ b/src/main/java/gg/pufferfish/pufferfish/util/AsyncPlayerAreaMap.java @@ -0,0 +1,31 @@ @@ -2076,22 +1350,22 @@ index 0000000000000000000000000000000000000000..fdcb62d12164024a5f354d60cc863821 +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); @@ -2127,7 +1401,7 @@ index 0000000000000000000000000000000000000000..c1929840254a3e6d721816f4a20415be +} 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 +index 0000000000000000000000000000000000000000..42cdc43d6b739973a0944f502089757247ee6c61 --- /dev/null +++ b/src/main/java/gg/pufferfish/pufferfish/util/Long2ObjectOpenHashMapWrapper.java @@ -0,0 +1,40 @@ @@ -2138,34 +1412,34 @@ index 0000000000000000000000000000000000000000..facd55463d44cb7e3d2ca6892982f549 +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(); @@ -2228,18 +1502,18 @@ index 6efb8b10f17c70b05128039376d254e6beda3841..57e8c6673c7cfe447a75f15506e80000 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 4450d825a5474da211f0e83f845d8c7129fa08aa..6d5e9400892b86562519a893349ca55e566bfb24 100644 +index 4450d825a5474da211f0e83f845d8c7129fa08aa..a4c64b1cc11955fb279b8ed0fb7d2668a7b90d2b 100644 --- a/src/main/java/net/minecraft/server/MinecraftServer.java +++ b/src/main/java/net/minecraft/server/MinecraftServer.java -@@ -312,6 +312,8 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop S spin(Function serverFactory) { AtomicReference atomicreference = new AtomicReference(); + Thread thread = new io.papermc.paper.util.TickThread(() -> { // Paper - rewrite chunk system @@ -1681,7 +1683,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop loadedChunkMap = new it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap<>(8192, 0.5f); +@@ -77,6 +77,9 @@ public class ServerChunkCache extends ChunkSource { private final LevelChunk[] lastLoadedChunks = new LevelChunk[4 * 4]; -+ + + public boolean firstRunSpawnCounts = true; // Pufferfish + public final java.util.concurrent.atomic.AtomicBoolean _pufferfish_spawnCountsReady = new java.util.concurrent.atomic.AtomicBoolean(false); // Pufferfish - optimize countmobs - ++ private static int getChunkCacheKey(int x, int z) { return x & 3 | ((z & 3) << 2); + } @@ -703,6 +706,7 @@ public class ServerChunkCache extends ChunkSource { ProfilerFiller gameprofilerfiller = this.level.getProfiler(); @@ -2399,7 +1673,7 @@ index ca84eddbdb1e198b899750e5f6b3eafd25ce970f..c6f5d6756fa0e068a462d9c0ded12e07 } // 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) { @@ -2644,7 +1918,7 @@ index 04b1531572e8fff1e46fe1c94e7fc863841e0f66..47ddc42f2b63d9d3fae5ae6ea93d4183 int LARGE_MAX_STACK_SIZE = 64; int DEFAULT_DISTANCE_LIMIT = 8; diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java -index 4705d7066207250c03a5f98eef61554c901f2e35..e25be74ef0a88541884ad62a4b84219400d5a142 100644 +index 4705d7066207250c03a5f98eef61554c901f2e35..548133e399b5abc4aa83045af87c135a3455b722 100644 --- a/src/main/java/net/minecraft/world/entity/Entity.java +++ b/src/main/java/net/minecraft/world/entity/Entity.java @@ -291,7 +291,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { @@ -2665,7 +1939,7 @@ index 4705d7066207250c03a5f98eef61554c901f2e35..e25be74ef0a88541884ad62a4b842194 + 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; } @@ -4295,43 +3569,6 @@ index f7ea77dd82d978ad307f99c743efacfb34478b3d..009ab06182359862b8f543030ec4fe4e + MinecraftServer.getServer().getRecipeManager().addRecipe(new net.minecraft.world.item.crafting.ShapelessRecipe(CraftNamespacedKey.toMinecraft(this.getKey()), this.getGroup(), CraftRecipe.getCategory(this.getCategory()), 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 d96399e9bf1a58db5a4a22e58abb99e7660e0694..eb19f679ee498e51d02fe9a961cf02699cf75848 100644 ---- a/src/main/java/org/bukkit/craftbukkit/scheduler/MinecraftInternalPlugin.java -+++ b/src/main/java/org/bukkit/craftbukkit/scheduler/MinecraftInternalPlugin.java -@@ -22,7 +22,8 @@ public class MinecraftInternalPlugin extends PluginBase { - private boolean enabled = true; - - private final String pluginName; -- private PluginDescriptionFile pdf; -+ private org.bukkit.plugin.PluginLogger logger; -+ private PluginDescriptionFile pdf; // Pufferfish - - public MinecraftInternalPlugin() { - this.pluginName = "Minecraft"; -@@ -81,7 +82,12 @@ public class MinecraftInternalPlugin extends PluginBase { - - @Override - public PluginLogger getLogger() { -- throw new UnsupportedOperationException("Not supported."); -+ // Pufferfish start -+ if (this.logger == null) { -+ this.logger = new org.bukkit.plugin.PluginLogger(this); // Pufferfish -+ } -+ return this.logger; -+ // Pufferfish end - } - - @Override -@@ -91,7 +97,7 @@ public class MinecraftInternalPlugin extends PluginBase { - - @Override - public Server getServer() { -- throw new UnsupportedOperationException("Not supported."); -+ return org.bukkit.Bukkit.getServer(); // Pufferfish - impl - } - - @Override diff --git a/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java b/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java index 64c50c52c11214740de7903e5592b8b6b2c170b3..d4f62940504e3ef7a70e13b1f4a7726f23b4c637 100644 --- a/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java @@ -4345,18 +3582,6 @@ index 64c50c52c11214740de7903e5592b8b6b2c170b3..d4f62940504e3ef7a70e13b1f4a7726f } @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 59478cd..0d71552 100644 --- a/patches/server/0002-Purpur-Server-Changes.patch +++ b/patches/server/0002-Purpur-Server-Changes.patch @@ -1,6 +1,6 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: AlphaKR93 -Date: Thu, 23 Mar 2023 10:35:36 +0900 +Date: Mon, 27 Mar 2023 02:52:12 +0000 Subject: [PATCH] Purpur Server Changes Original: PurpurMC @@ -25,7 +25,7 @@ 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 3c8293f002f11b430083502362fdc801f44aa138..6c5b3f6a2c4f7b20e3388b63c36b7c4bc4cf179f 100644 +index b41b186397d013c19436c345be98b785d4bd0295..22470f0ab8354a9f31a0f195f3fe80f2f5ee2f0e 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -7,12 +7,8 @@ plugins { @@ -54,7 +54,7 @@ index 3c8293f002f11b430083502362fdc801f44aa138..6c5b3f6a2c4f7b20e3388b63c36b7c4b runtimeOnly("org.apache.maven:maven-resolver-provider:3.8.5") runtimeOnly("org.apache.maven.resolver:maven-resolver-connector-basic:1.7.3") runtimeOnly("org.apache.maven.resolver:maven-resolver-transport-http:1.7.3") -@@ -82,7 +82,7 @@ tasks.jar { +@@ -81,7 +81,7 @@ tasks.jar { attributes( "Main-Class" to "org.bukkit.craftbukkit.Main", "Implementation-Title" to "CraftBukkit", @@ -63,7 +63,7 @@ index 3c8293f002f11b430083502362fdc801f44aa138..6c5b3f6a2c4f7b20e3388b63c36b7c4b "Implementation-Vendor" to date, // Paper "Specification-Title" to "Bukkit", "Specification-Version" to project.version, -@@ -154,7 +154,7 @@ fun TaskContainer.registerRunTask( +@@ -153,7 +153,7 @@ fun TaskContainer.registerRunTask( name: String, block: JavaExec.() -> Unit ): TaskProvider = register(name) { @@ -261,27 +261,27 @@ index 39844531b03eb8a6c70700b4ecbf0ff1a557424d..632ae75cb3bbc7a3955872d14ad0fbc2 public void removeCommand(String name) { this.children.remove(name); diff --git a/src/main/java/gg/pufferfish/pufferfish/PufferfishConfig.java b/src/main/java/gg/pufferfish/pufferfish/PufferfishConfig.java -index e0076044d0a29e33ee297fe6180a041436075297..c0f44f0593aab16d5ceab493f4075772f454732e 100644 +index a8cead500186142115d4dc029c942fdfc68f7fe5..62b2a3a44929b80b813bc24a33cd1f5049fecbb2 100644 --- a/src/main/java/gg/pufferfish/pufferfish/PufferfishConfig.java +++ b/src/main/java/gg/pufferfish/pufferfish/PufferfishConfig.java -@@ -36,6 +36,7 @@ public class PufferfishConfig { - +@@ -28,6 +28,7 @@ public class PufferfishConfig { + private static final YamlFile config = new YamlFile(); private static int updates = 0; + public static File pufferfishFile; // Purpur - + private static ConfigurationSection convertToBukkit(org.simpleyaml.configuration.ConfigurationSection section) { ConfigurationSection newSection = new MemoryConfiguration(); -@@ -58,7 +59,7 @@ public class PufferfishConfig { +@@ -50,7 +51,7 @@ public class PufferfishConfig { } - + public static void load() throws IOException { - File configFile = new File("pufferfish.yml"); + File configFile = pufferfishFile; // Purpur - + if (configFile.exists()) { try { -@@ -232,7 +233,7 @@ public class PufferfishConfig { +@@ -224,7 +225,7 @@ public class PufferfishConfig { public static int activationDistanceMod; private static void dynamicActivationOfBrains() throws IOException { @@ -290,8 +290,8 @@ index e0076044d0a29e33ee297fe6180a041436075297..c0f44f0593aab16d5ceab493f4075772 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."); -@@ -276,7 +277,7 @@ public class PufferfishConfig { - +@@ -268,7 +269,7 @@ public class PufferfishConfig { + public static boolean throttleInactiveGoalSelectorTick; private static void inactiveGoalSelectorThrottle() { - getBoolean("inactive-goal-selector-throttle", "inactive-goal-selector-disable", true, @@ -1049,7 +1049,7 @@ index 9ec6145fe04ec64bbee8ec6a837719caebdbc6f5..358d610ad020cada1bb83e393deeeaae public ClientboundSetTimePacket(long time, long timeOfDay, boolean doDaylightCycle) { this.gameTime = time; diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java -index 6d5e9400892b86562519a893349ca55e566bfb24..5416b64c3000c9b17a78991218e068bf5ef33db7 100644 +index a4c64b1cc11955fb279b8ed0fb7d2668a7b90d2b..2cc20cc768bbbd386972c426d3a131af33612294 100644 --- a/src/main/java/net/minecraft/server/MinecraftServer.java +++ b/src/main/java/net/minecraft/server/MinecraftServer.java @@ -249,7 +249,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop cachedSingleHashSet; // Paper public PlayerNaturallySpawnCreaturesEvent playerNaturallySpawnedEvent; // Paper public org.bukkit.event.player.PlayerQuitEvent.QuitReason quitReason = null; // Paper - there are a lot of changes to do if we change all methods leading to the event @@ -2642,7 +2642,7 @@ index a399de32d5f2f932d41fbb552780979950844a05..e5cf1bf76094b9b4e2ee9608276e0361 public ServerPlayer(MinecraftServer server, ServerLevel world, GameProfile profile) { super(world, world.getSharedSpawnPos(), world.getSharedSpawnAngle(), profile); -@@ -373,6 +378,7 @@ public class ServerPlayer extends Player { +@@ -374,6 +379,7 @@ public class ServerPlayer extends Player { this.bukkitPickUpLoot = true; this.maxHealthCache = this.getMaxHealth(); this.cachedSingleMobDistanceMap = new com.destroystokyo.paper.util.PooledHashSets.PooledObjectLinkedOpenHashSet<>(this); // Paper @@ -2650,7 +2650,7 @@ index a399de32d5f2f932d41fbb552780979950844a05..e5cf1bf76094b9b4e2ee9608276e0361 } // Yes, this doesn't match Vanilla, but it's the best we can do for now. -@@ -512,6 +518,9 @@ public class ServerPlayer extends Player { +@@ -513,6 +519,9 @@ public class ServerPlayer extends Player { } } @@ -2660,7 +2660,7 @@ index a399de32d5f2f932d41fbb552780979950844a05..e5cf1bf76094b9b4e2ee9608276e0361 } @Override -@@ -578,6 +587,9 @@ public class ServerPlayer extends Player { +@@ -579,6 +588,9 @@ public class ServerPlayer extends Player { } this.getBukkitEntity().setExtraData(nbt); // CraftBukkit @@ -2670,7 +2670,7 @@ index a399de32d5f2f932d41fbb552780979950844a05..e5cf1bf76094b9b4e2ee9608276e0361 } // CraftBukkit start - World fallback code, either respawn location or global spawn -@@ -706,6 +718,15 @@ public class ServerPlayer extends Player { +@@ -707,6 +719,15 @@ public class ServerPlayer extends Player { this.trackStartFallingPosition(); this.trackEnteredOrExitedLavaOnVehicle(); this.advancements.flushDirty(this); @@ -2686,7 +2686,7 @@ index a399de32d5f2f932d41fbb552780979950844a05..e5cf1bf76094b9b4e2ee9608276e0361 } public void doTick() { -@@ -944,6 +965,7 @@ public class ServerPlayer extends Player { +@@ -945,6 +966,7 @@ public class ServerPlayer extends Player { })); Team scoreboardteambase = this.getTeam(); @@ -2694,7 +2694,7 @@ index a399de32d5f2f932d41fbb552780979950844a05..e5cf1bf76094b9b4e2ee9608276e0361 if (scoreboardteambase != null && scoreboardteambase.getDeathMessageVisibility() != Team.Visibility.ALWAYS) { if (scoreboardteambase.getDeathMessageVisibility() == Team.Visibility.HIDE_FOR_OTHER_TEAMS) { this.server.getPlayerList().broadcastSystemToTeam(this, ichatbasecomponent); -@@ -1045,14 +1067,30 @@ public class ServerPlayer extends Player { +@@ -1046,14 +1068,30 @@ public class ServerPlayer extends Player { } @@ -2726,7 +2726,7 @@ index a399de32d5f2f932d41fbb552780979950844a05..e5cf1bf76094b9b4e2ee9608276e0361 return false; } else { Entity entity = source.getEntity(); -@@ -1161,7 +1199,7 @@ public class ServerPlayer extends Player { +@@ -1162,7 +1200,7 @@ public class ServerPlayer extends Player { PortalInfo shapedetectorshape = this.findDimensionEntryPoint(worldserver); if (shapedetectorshape != null) { @@ -2735,7 +2735,7 @@ index a399de32d5f2f932d41fbb552780979950844a05..e5cf1bf76094b9b4e2ee9608276e0361 worldserver = shapedetectorshape.world; // CraftBukkit if (worldserver == null) { } else // CraftBukkit - empty to fall through to null to event if (resourcekey == LevelStem.OVERWORLD && worldserver.getTypeKey() == LevelStem.NETHER) { // CraftBukkit -@@ -1184,8 +1222,8 @@ public class ServerPlayer extends Player { +@@ -1185,8 +1223,8 @@ public class ServerPlayer extends Player { worldserver = ((CraftWorld) exit.getWorld()).getHandle(); // CraftBukkit end @@ -2746,7 +2746,7 @@ index a399de32d5f2f932d41fbb552780979950844a05..e5cf1bf76094b9b4e2ee9608276e0361 if (true) { // CraftBukkit this.isChangingDimension = true; // CraftBukkit - Set teleport invulnerability only if player changing worlds -@@ -1196,6 +1234,7 @@ public class ServerPlayer extends Player { +@@ -1197,6 +1235,7 @@ public class ServerPlayer extends Player { playerlist.sendPlayerPermissionLevel(this); worldserver1.removePlayerImmediately(this, Entity.RemovalReason.CHANGED_DIMENSION); this.unsetRemoved(); @@ -2754,7 +2754,7 @@ index a399de32d5f2f932d41fbb552780979950844a05..e5cf1bf76094b9b4e2ee9608276e0361 // CraftBukkit end this.setLevel(worldserver); -@@ -1203,7 +1242,7 @@ public class ServerPlayer extends Player { +@@ -1204,7 +1243,7 @@ public class ServerPlayer extends Player { this.connection.teleport(exit); // CraftBukkit - use internal teleport without event this.connection.resetPosition(); worldserver.addDuringPortalTeleport(this); @@ -2763,7 +2763,7 @@ index a399de32d5f2f932d41fbb552780979950844a05..e5cf1bf76094b9b4e2ee9608276e0361 this.triggerDimensionChangeTriggers(worldserver1); this.connection.send(new ClientboundPlayerAbilitiesPacket(this.getAbilities())); playerlist.sendLevelInfo(this, worldserver); -@@ -1232,6 +1271,7 @@ public class ServerPlayer extends Player { +@@ -1233,6 +1272,7 @@ public class ServerPlayer extends Player { } // Paper end @@ -2771,7 +2771,7 @@ index a399de32d5f2f932d41fbb552780979950844a05..e5cf1bf76094b9b4e2ee9608276e0361 return this; } } -@@ -1353,7 +1393,7 @@ public class ServerPlayer extends Player { +@@ -1354,7 +1394,7 @@ public class ServerPlayer extends Player { return entitymonster.isPreventingPlayerRest(this); }); @@ -2780,7 +2780,7 @@ index a399de32d5f2f932d41fbb552780979950844a05..e5cf1bf76094b9b4e2ee9608276e0361 return Either.left(Player.BedSleepingProblem.NOT_SAFE); } } -@@ -1489,6 +1529,7 @@ public class ServerPlayer extends Player { +@@ -1490,6 +1530,7 @@ public class ServerPlayer extends Player { @Override public void openTextEdit(SignBlockEntity sign) { @@ -2788,7 +2788,7 @@ index a399de32d5f2f932d41fbb552780979950844a05..e5cf1bf76094b9b4e2ee9608276e0361 sign.setAllowedPlayerEditor(this.getUUID()); this.connection.send(new ClientboundBlockUpdatePacket(this.level, sign.getBlockPos())); this.connection.send(new ClientboundOpenSignEditorPacket(sign.getBlockPos())); -@@ -1715,6 +1756,26 @@ public class ServerPlayer extends Player { +@@ -1726,6 +1767,26 @@ public class ServerPlayer extends Player { this.lastSentExp = -1; // CraftBukkit - Added to reset } @@ -2815,7 +2815,7 @@ index a399de32d5f2f932d41fbb552780979950844a05..e5cf1bf76094b9b4e2ee9608276e0361 @Override public void displayClientMessage(Component message, boolean overlay) { this.sendSystemMessage(message, overlay); -@@ -2014,6 +2075,7 @@ public class ServerPlayer extends Player { +@@ -2025,6 +2086,7 @@ public class ServerPlayer extends Player { } public void sendTexturePack(String url, String hash, boolean required, @Nullable Component resourcePackPrompt) { @@ -2823,7 +2823,7 @@ index a399de32d5f2f932d41fbb552780979950844a05..e5cf1bf76094b9b4e2ee9608276e0361 this.connection.send(new ClientboundResourcePackPacket(url, hash, required, resourcePackPrompt)); } -@@ -2028,8 +2090,63 @@ public class ServerPlayer extends Player { +@@ -2039,8 +2101,63 @@ public class ServerPlayer extends Player { public void resetLastActionTime() { this.lastActionTime = Util.getMillis(); @@ -2887,7 +2887,7 @@ index a399de32d5f2f932d41fbb552780979950844a05..e5cf1bf76094b9b4e2ee9608276e0361 public ServerStatsCounter getStats() { return this.stats; } -@@ -2490,8 +2607,16 @@ public class ServerPlayer extends Player { +@@ -2501,8 +2618,16 @@ public class ServerPlayer extends Player { @Override public boolean isImmobile() { @@ -2905,7 +2905,7 @@ index a399de32d5f2f932d41fbb552780979950844a05..e5cf1bf76094b9b4e2ee9608276e0361 @Override public Scoreboard getScoreboard() { -@@ -2540,4 +2665,50 @@ public class ServerPlayer extends Player { +@@ -2551,4 +2676,50 @@ public class ServerPlayer extends Player { return (CraftPlayer) super.getBukkitEntity(); } // CraftBukkit end @@ -4049,7 +4049,7 @@ index 14fab63346d56c72cd7534a04760efd10eef4295..745e792482f61c571e2efbd4200dd1bd @Override diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java -index e25be74ef0a88541884ad62a4b84219400d5a142..b2ee73a2ef6042694ca84c27d592e5fb13b4a0fe 100644 +index 548133e399b5abc4aa83045af87c135a3455b722..3c10c719f6172161a2dcc6592a0a1492e9b3d7c1 100644 --- a/src/main/java/net/minecraft/world/entity/Entity.java +++ b/src/main/java/net/minecraft/world/entity/Entity.java @@ -155,7 +155,7 @@ import org.bukkit.plugin.PluginManager; @@ -5071,7 +5071,7 @@ index 791f672b30f2a4d3b329e2ce0f4fb9c2ca627b01..8d7c33e16f9eaec2120c5aad75172ff6 }); } diff --git a/src/main/java/net/minecraft/world/entity/Mob.java b/src/main/java/net/minecraft/world/entity/Mob.java -index 636e601b004a412d02e5be86e97d489b52c28e1b..141b25060905f598208cb1a39f4c2b2e9f3ef766 100644 +index 636e601b004a412d02e5be86e97d489b52c28e1b..8e2274f7dce34e0997356205cf96e46f8d41cca1 100644 --- a/src/main/java/net/minecraft/world/entity/Mob.java +++ b/src/main/java/net/minecraft/world/entity/Mob.java @@ -65,6 +65,7 @@ import net.minecraft.world.item.ProjectileWeaponItem; @@ -5212,7 +5212,8 @@ index 636e601b004a412d02e5be86e97d489b52c28e1b..141b25060905f598208cb1a39f4c2b2e - this.level.getProfiler().pop(); + //this.level.getProfiler().pop(); // Purpur } else { - this.level.getProfiler().push("targetSelector"); +- this.level.getProfiler().push("targetSelector"); ++ //this.level.getProfiler().push("targetSelector"); // Purpur if (this.targetSelector.inactiveTick(this.activatedPriority, false)) // Pufferfish - use this to alternate ticking this.targetSelector.tick(); - this.level.getProfiler().pop(); @@ -15118,7 +15119,7 @@ index 5d199fe497bd852827d3d18fb7566a09e70331a3..6cd8a50289a6404441e9e5e08d82d2eb if (NaturalSpawner.isSpawnPositionOk(SpawnPlacements.Type.ON_GROUND, world, blockposition2, EntityType.WANDERING_TRADER)) { blockposition1 = blockposition2; diff --git a/src/main/java/net/minecraft/world/entity/player/Player.java b/src/main/java/net/minecraft/world/entity/player/Player.java -index 2b02800666b358159c8ecb63208a14855f90657b..f96524c4a5d3c51b1ab6d990b30055ad5d8bfaac 100644 +index 0629c471d38a77c44fc1c86ccdfcb0690f61ca17..d055cfc257cc7282170ba67c848af3e6c5f2ec1f 100644 --- a/src/main/java/net/minecraft/world/entity/player/Player.java +++ b/src/main/java/net/minecraft/world/entity/player/Player.java @@ -186,6 +186,8 @@ public abstract class Player extends LivingEntity { @@ -15198,7 +15199,7 @@ index 2b02800666b358159c8ecb63208a14855f90657b..f96524c4a5d3c51b1ab6d990b30055ad } @Override -@@ -584,7 +624,7 @@ public abstract class Player extends LivingEntity { +@@ -590,7 +630,7 @@ public abstract class Player extends LivingEntity { for (int i = 0; i < list.size(); ++i) { Entity entity = (Entity) list.get(i); @@ -15207,7 +15208,7 @@ index 2b02800666b358159c8ecb63208a14855f90657b..f96524c4a5d3c51b1ab6d990b30055ad list1.add(entity); } else if (!entity.isRemoved()) { this.touch(entity); -@@ -1275,7 +1315,7 @@ public abstract class Player extends LivingEntity { +@@ -1281,7 +1321,7 @@ public abstract class Player extends LivingEntity { flag2 = flag2 && !level.paperConfig().entities.behavior.disablePlayerCrits; // Paper flag2 = flag2 && !this.isSprinting(); if (flag2) { @@ -15216,7 +15217,7 @@ index 2b02800666b358159c8ecb63208a14855f90657b..f96524c4a5d3c51b1ab6d990b30055ad } f += f1; -@@ -1944,9 +1984,19 @@ public abstract class Player extends LivingEntity { +@@ -1950,9 +1990,19 @@ public abstract class Player extends LivingEntity { @Override public int getExperienceReward() { if (!this.level.getGameRules().getBoolean(GameRules.RULE_KEEPINVENTORY) && !this.isSpectator()) { @@ -15239,7 +15240,7 @@ index 2b02800666b358159c8ecb63208a14855f90657b..f96524c4a5d3c51b1ab6d990b30055ad } else { return 0; } -@@ -2022,6 +2072,11 @@ public abstract class Player extends LivingEntity { +@@ -2028,6 +2078,11 @@ public abstract class Player extends LivingEntity { return this.inventory.armor; } @@ -15251,7 +15252,7 @@ index 2b02800666b358159c8ecb63208a14855f90657b..f96524c4a5d3c51b1ab6d990b30055ad public boolean setEntityOnShoulder(CompoundTag entityNbt) { if (!this.isPassenger() && this.onGround && !this.isInWater() && !this.isInPowderSnow) { if (this.getShoulderEntityLeft().isEmpty()) { -@@ -2305,7 +2360,7 @@ public abstract class Player extends LivingEntity { +@@ -2311,7 +2366,7 @@ public abstract class Player extends LivingEntity { public ItemStack eat(Level world, ItemStack stack) { this.getFoodData().eat(stack.getItem(), stack); this.awardStat(Stats.ITEM_USED.get(stack.getItem())); @@ -16659,7 +16660,7 @@ index 180aec596110309aade13d2080f8824d152b07cb..c4aec1e5135a79837918b692e75a7b55 return InteractionResult.PASS; } diff --git a/src/main/java/net/minecraft/world/item/ItemStack.java b/src/main/java/net/minecraft/world/item/ItemStack.java -index a6253272205337b3b855679b3057c2519a807a4c..234d992206f34febc7aff24b78cb3e526254e35f 100644 +index d690b8d0c1da1f56b226376df8c76c34375e3c73..0c0a0639cd9c85d4332a6fa85e3143510f3c7448 100644 --- a/src/main/java/net/minecraft/world/item/ItemStack.java +++ b/src/main/java/net/minecraft/world/item/ItemStack.java @@ -109,6 +109,7 @@ import org.bukkit.event.world.StructureGrowEvent; @@ -16670,7 +16671,7 @@ index a6253272205337b3b855679b3057c2519a807a4c..234d992206f34febc7aff24b78cb3e52 public static final Codec CODEC = RecordCodecBuilder.create((instance) -> { return instance.group(BuiltInRegistries.ITEM.byNameCodec().fieldOf("id").forGetter((itemstack) -> { return itemstack.item; -@@ -414,6 +415,7 @@ public final class ItemStack { +@@ -415,6 +416,7 @@ public final class ItemStack { world.preventPoiUpdated = true; // CraftBukkit - SPIGOT-5710 for (BlockState blockstate : blocks) { blockstate.update(true, false); @@ -16678,7 +16679,7 @@ index a6253272205337b3b855679b3057c2519a807a4c..234d992206f34febc7aff24b78cb3e52 } world.preventPoiUpdated = false; -@@ -443,6 +445,7 @@ public final class ItemStack { +@@ -444,6 +446,7 @@ public final class ItemStack { if (!(block.getBlock() instanceof BaseEntityBlock)) { // Containers get placed automatically block.getBlock().onPlace(block, world, newblockposition, oldBlock, true, itemactioncontext); // Paper - pass itemactioncontext } @@ -16686,7 +16687,7 @@ index a6253272205337b3b855679b3057c2519a807a4c..234d992206f34febc7aff24b78cb3e52 world.notifyAndUpdatePhysics(newblockposition, null, oldBlock, block, world.getBlockState(newblockposition), updateFlag, 512); // send null chunk as chunk.k() returns false by this point } -@@ -558,6 +561,16 @@ public final class ItemStack { +@@ -559,6 +562,16 @@ public final class ItemStack { return this.isDamageableItem() && this.getDamageValue() > 0; } @@ -16703,7 +16704,7 @@ index a6253272205337b3b855679b3057c2519a807a4c..234d992206f34febc7aff24b78cb3e52 public int getDamageValue() { return this.tag == null ? 0 : this.tag.getInt("Damage"); } -@@ -577,7 +590,7 @@ public final class ItemStack { +@@ -578,7 +591,7 @@ public final class ItemStack { int j; if (amount > 0) { @@ -16712,7 +16713,7 @@ index a6253272205337b3b855679b3057c2519a807a4c..234d992206f34febc7aff24b78cb3e52 int k = 0; for (int l = 0; j > 0 && l < amount; ++l) { -@@ -632,6 +645,12 @@ public final class ItemStack { +@@ -633,6 +646,12 @@ public final class ItemStack { if (this.hurt(amount, entity.getRandom(), entity /*instanceof ServerPlayer ? (ServerPlayer) entity : null*/)) { // Paper - pass LivingEntity for EntityItemDamageEvent breakCallback.accept(entity); Item item = this.getItem(); @@ -16725,7 +16726,7 @@ index a6253272205337b3b855679b3057c2519a807a4c..234d992206f34febc7aff24b78cb3e52 // CraftBukkit start - Check for item breaking if (this.count == 1 && entity instanceof net.minecraft.world.entity.player.Player) { org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerItemBreakEvent((net.minecraft.world.entity.player.Player) entity, this); -@@ -1164,7 +1183,7 @@ public final class ItemStack { +@@ -1165,7 +1184,7 @@ public final class ItemStack { ListTag nbttaglist = this.tag.getList("Enchantments", 10); @@ -16734,7 +16735,7 @@ index a6253272205337b3b855679b3057c2519a807a4c..234d992206f34febc7aff24b78cb3e52 processEnchantOrder(this.tag); // Paper } -@@ -1172,6 +1191,12 @@ public final class ItemStack { +@@ -1173,6 +1192,12 @@ public final class ItemStack { return this.tag != null && this.tag.contains("Enchantments", 9) ? !this.tag.getList("Enchantments", 10).isEmpty() : false; } @@ -17513,7 +17514,7 @@ index 3d2b34c5a7c9b00c1164b4f89c2cbff81fc460eb..b5505e926e5cdb447de68e8eb8e46c97 return true; } else { diff --git a/src/main/java/net/minecraft/world/level/block/BedBlock.java b/src/main/java/net/minecraft/world/level/block/BedBlock.java -index 96434f14525a2159f335b94aad95081f488fadf3..d56bbd43b127a1d663a398b1da7090ff64ab6a6c 100644 +index 3aa79a441ac4bd6b4d87d19bdb3011455210fd41..4de8e77ef7574a0febf9c89258e4aeb26f4af6fc 100644 --- a/src/main/java/net/minecraft/world/level/block/BedBlock.java +++ b/src/main/java/net/minecraft/world/level/block/BedBlock.java @@ -97,7 +97,7 @@ public class BedBlock extends HorizontalDirectionalBlock implements EntityBlock @@ -20681,7 +20682,7 @@ index 4d7a2c4c1001aefe9fcd4be8dbcb414f721bfff9..2c7716a9d65ebda209a144b82c2126b6 + // Purpur end } diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java -index 1bada55af5d16437da4d16f9ded55f88a6121eb4..2b647aeb75e2207186bf9506fba0d92a745cfae8 100644 +index 0ae1fce0c1a2e3bfbbab756a088fc76545e263fa..f81b320ef330d03b68cf8b4af04b7c991ce9636b 100644 --- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java @@ -525,10 +525,15 @@ public class CraftPlayer extends CraftHumanEntity implements Player { @@ -20712,7 +20713,7 @@ index 1bada55af5d16437da4d16f9ded55f88a6121eb4..2b647aeb75e2207186bf9506fba0d92a return false; } -@@ -2356,6 +2365,28 @@ public class CraftPlayer extends CraftHumanEntity implements Player { +@@ -2366,6 +2375,28 @@ public class CraftPlayer extends CraftHumanEntity implements Player { return this.getHandle().getAbilities().walkingSpeed * 2f; } @@ -20741,7 +20742,7 @@ index 1bada55af5d16437da4d16f9ded55f88a6121eb4..2b647aeb75e2207186bf9506fba0d92a private void validateSpeed(float value) { if (value < 0) { if (value < -1f) { -@@ -3148,4 +3179,97 @@ public class CraftPlayer extends CraftHumanEntity implements Player { +@@ -3158,4 +3189,97 @@ public class CraftPlayer extends CraftHumanEntity implements Player { return this.spigot; } // Spigot end @@ -21921,7 +21922,7 @@ index 0000000000000000000000000000000000000000..0bcbe1f07ff8e552d2abd6e432af5710 +} diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java new file mode 100644 -index 0000000000000000000000000000000000000000..fe903b1e1bd211651e3808becd34a2d28dc57f34 +index 0000000000000000000000000000000000000000..d3f2002759ac4788feca1e62c90c2e64596eb2f2 --- /dev/null +++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java @@ -0,0 +1,3189 @@ @@ -22050,7 +22051,7 @@ index 0000000000000000000000000000000000000000..fe903b1e1bd211651e3808becd34a2d2 + public boolean useBetterMending = false; + public boolean alwaysTameInCreative = false; + public boolean boatEjectPlayersOnLand = false; -+ public boolean boatsDoFallDamage = true; ++ public boolean boatsDoFallDamage = false; + public boolean disableDropsOnCrammingDeath = false; + public boolean entitiesCanUsePortals = true; + public boolean entitiesPickUpLootBypassMobGriefing = false; diff --git a/patches/server/0003-Build-Fix.patch b/patches/server/0003-Build-Fix.patch deleted file mode 100644 index 00a7d6a..0000000 --- a/patches/server/0003-Build-Fix.patch +++ /dev/null @@ -1,19 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: AlphaKR93 -Date: Thu, 23 Mar 2023 12:08:57 +0900 -Subject: [PATCH] Build Fix - - -diff --git a/src/main/java/net/minecraft/world/entity/Mob.java b/src/main/java/net/minecraft/world/entity/Mob.java -index 141b25060905f598208cb1a39f4c2b2e9f3ef766..d67bc2abe9590b1b0b86e450c9fabe5e2237be6c 100644 ---- a/src/main/java/net/minecraft/world/entity/Mob.java -+++ b/src/main/java/net/minecraft/world/entity/Mob.java -@@ -946,7 +946,7 @@ public abstract class Mob extends LivingEntity implements Targeting { - this.goalSelector.tickRunningGoals(false); - //this.level.getProfiler().pop(); // Purpur - } else { -- this.level.getProfiler().push("targetSelector"); -+ //this.level.getProfiler().push("targetSelector"); // Plazma - build fix - if (this.targetSelector.inactiveTick(this.activatedPriority, false)) // Pufferfish - use this to alternate ticking - this.targetSelector.tick(); - //this.level.getProfiler().pop(); // Purpur diff --git a/patches/server/0004-Rebrand.patch b/patches/server/0003-Rebrand.patch similarity index 99% rename from patches/server/0004-Rebrand.patch rename to patches/server/0003-Rebrand.patch index e58eec1..942196c 100644 --- a/patches/server/0004-Rebrand.patch +++ b/patches/server/0003-Rebrand.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Rebrand diff --git a/build.gradle.kts b/build.gradle.kts -index 6c5b3f6a2c4f7b20e3388b63c36b7c4bc4cf179f..e4de94641d33b3deadc44bbb5f23f666d79737ea 100644 +index 22470f0ab8354a9f31a0f195f3fe80f2f5ee2f0e..dc89b3a8956af69fa71a2bfa17c22aee6c5e9edf 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -7,7 +7,7 @@ plugins { @@ -17,7 +17,7 @@ index 6c5b3f6a2c4f7b20e3388b63c36b7c4bc4cf179f..e4de94641d33b3deadc44bbb5f23f666 implementation("io.papermc.paper:paper-mojangapi:1.19.4-R0.1-SNAPSHOT") // Purpur // Paper start implementation("org.jline:jline-terminal-jansi:3.21.0") -@@ -82,7 +82,7 @@ tasks.jar { +@@ -81,7 +81,7 @@ tasks.jar { attributes( "Main-Class" to "org.bukkit.craftbukkit.Main", "Implementation-Title" to "CraftBukkit", @@ -175,7 +175,7 @@ index c6fa6bcd66d61359124a8426b919493c6ec43f06..e0eaa847526431ac58d00f18f0fca6b1 /* CraftBukkit start - Replace everything OptionParser optionparser = new OptionParser(); diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java -index 5416b64c3000c9b17a78991218e068bf5ef33db7..180ea5edb002856f975c074e319998b68b67a7af 100644 +index 2cc20cc768bbbd386972c426d3a131af33612294..b81f8207348992761eac2aca8bae370c9b39f0f3 100644 --- a/src/main/java/net/minecraft/server/MinecraftServer.java +++ b/src/main/java/net/minecraft/server/MinecraftServer.java @@ -929,7 +929,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop 2147483647L ? Integer.MAX_VALUE : (int) l; int j1 = this.getCoprime(i1); @@ -30,7 +30,7 @@ index c79e2b5160c41ce77ebd5355aebcefb3cb9151ca..735655847f75584a985c896637f47c24 for (int l1 = 0; l1 < i1; ++l1) { int i2 = (k1 + j1 * l1) % i1; -@@ -439,7 +439,7 @@ public class ServerPlayer extends Player { +@@ -440,7 +440,7 @@ public class ServerPlayer extends Player { long l = k * k; int i1 = l > 2147483647L ? Integer.MAX_VALUE : (int) l; int j1 = this.getCoprime(i1); diff --git a/patches/server/0021-Entity-Configuration.patch b/patches/server/0020-Entity-Configuration.patch similarity index 100% rename from patches/server/0021-Entity-Configuration.patch rename to patches/server/0020-Entity-Configuration.patch diff --git a/patches/server/0022-Various-Optimizations.patch b/patches/server/0021-Various-Optimizations.patch similarity index 98% rename from patches/server/0022-Various-Optimizations.patch rename to patches/server/0021-Various-Optimizations.patch index ed13352..f2e5cf5 100644 --- a/patches/server/0022-Various-Optimizations.patch +++ b/patches/server/0021-Various-Optimizations.patch @@ -10,7 +10,7 @@ Subject: [PATCH] Various Optimizations 0011 - Swaps the predicate order of collision (Akarin) diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java -index b2ee73a2ef6042694ca84c27d592e5fb13b4a0fe..d6e20ebb1e07da767f6571bbaf0dc882cdf4503f 100644 +index 3c10c719f6172161a2dcc6592a0a1492e9b3d7c1..d64f188257c135e2a4af07e28ff8f912c80eac03 100644 --- a/src/main/java/net/minecraft/world/entity/Entity.java +++ b/src/main/java/net/minecraft/world/entity/Entity.java @@ -1991,8 +1991,9 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { diff --git a/patches/server/0023-Add-configuration-to-disable-moved-to-quickly-check-.patch b/patches/server/0022-Add-configuration-to-disable-moved-to-quickly-check-.patch similarity index 100% rename from patches/server/0023-Add-configuration-to-disable-moved-to-quickly-check-.patch rename to patches/server/0022-Add-configuration-to-disable-moved-to-quickly-check-.patch diff --git a/patches/server/0024-Apply-faster-random.patch b/patches/server/0023-Apply-faster-random.patch similarity index 99% rename from patches/server/0024-Apply-faster-random.patch rename to patches/server/0023-Apply-faster-random.patch index b8e177b..37107cb 100644 --- a/patches/server/0024-Apply-faster-random.patch +++ b/patches/server/0023-Apply-faster-random.patch @@ -18,7 +18,7 @@ index e5ea9f27a1936ed9e329e74317c91c5df89b9fbd..7e7a4d872983cd2efdc575bc33353f94 private long lastFill = -1; private long nextRefill = -1; diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java -index 6d2d1d99977d33fdd8c9a34b65bbca189852d1c0..6e2713a2f3c30a09f54bab4d0746d84dad66be2e 100644 +index 9ba8bf1cc91494651a1855d12415f4ac3586cf9d..17cd928af104d74704d61ca26084bd60014af800 100644 --- a/src/main/java/net/minecraft/server/MinecraftServer.java +++ b/src/main/java/net/minecraft/server/MinecraftServer.java @@ -693,7 +693,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop