From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: AlphaKR93 Date: Sun, 26 May 2024 19:50:35 +0900 Subject: [PATCH] Pufferfish API Changes diff --git a/build.gradle.kts b/build.gradle.kts index fd39ed209b20c927054b8482c400beeeeab460a3..1336685fde70c4a88a1fb591cdfcd781a8f06d0b 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -52,6 +52,7 @@ dependencies { apiAndDocs("net.kyori:adventure-text-logger-slf4j") api("org.apache.logging.log4j:log4j-api:$log4jVersion") api("org.slf4j:slf4j-api:$slf4jVersion") + api("io.sentry:sentry:5.4.0") // Pufferfish implementation("org.ow2.asm:asm:9.7") implementation("org.ow2.asm:asm-commons:9.7") @@ -130,6 +131,13 @@ val generateApiVersioningFile by tasks.registering { } } +// Pufferfish Start +tasks.withType { + val compilerArgs = options.compilerArgs + compilerArgs.add("--add-modules=jdk.incubator.vector") +} +// Pufferfish End + tasks.jar { from(generateApiVersioningFile.map { it.outputs.files.singleFile }) { into("META-INF/maven/${project.group}/${project.name}") diff --git a/src/main/java/gg/pufferfish/pufferfish/sentry/SentryContext.java b/src/main/java/gg/pufferfish/pufferfish/sentry/SentryContext.java new file mode 100644 index 0000000000000000000000000000000000000000..10310fdd53de28efb8a8250f6d3b0c8eb08fb68a --- /dev/null +++ b/src/main/java/gg/pufferfish/pufferfish/sentry/SentryContext.java @@ -0,0 +1,161 @@ +package gg.pufferfish.pufferfish.sentry; + +import com.google.gson.Gson; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.Map; +import java.util.TreeMap; +import org.apache.logging.log4j.ThreadContext; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.event.Event; +import org.bukkit.event.player.PlayerEvent; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.RegisteredListener; +import org.jetbrains.annotations.Nullable; + +public class SentryContext { + + private static final Gson GSON = new Gson(); + + public static void setPluginContext(@Nullable Plugin plugin) { + if (plugin != null) { + ThreadContext.put("pufferfishsentry_pluginname", plugin.getName()); + ThreadContext.put("pufferfishsentry_pluginversion", plugin.getDescription().getVersion()); + } + } + + public static void removePluginContext() { + ThreadContext.remove("pufferfishsentry_pluginname"); + ThreadContext.remove("pufferfishsentry_pluginversion"); + } + + public static void setSenderContext(@Nullable CommandSender sender) { + if (sender != null) { + ThreadContext.put("pufferfishsentry_playername", sender.getName()); + if (sender instanceof Player player) { + ThreadContext.put("pufferfishsentry_playerid", player.getUniqueId().toString()); + } + } + } + + public static void removeSenderContext() { + ThreadContext.remove("pufferfishsentry_playername"); + ThreadContext.remove("pufferfishsentry_playerid"); + } + + public static void setEventContext(Event event, RegisteredListener registration) { + setPluginContext(registration.getPlugin()); + + try { + // Find the player that was involved with this event + Player player = null; + if (event instanceof PlayerEvent) { + player = ((PlayerEvent) event).getPlayer(); + } else { + Class eventClass = event.getClass(); + + Field playerField = null; + + for (Field field : eventClass.getDeclaredFields()) { + if (field.getType().equals(Player.class)) { + playerField = field; + break; + } + } + + if (playerField != null) { + playerField.setAccessible(true); + player = (Player) playerField.get(event); + } + } + + if (player != null) { + setSenderContext(player); + } + } catch (Exception e) {} // We can't really safely log exceptions. + + ThreadContext.put("pufferfishsentry_eventdata", GSON.toJson(serializeFields(event))); + } + + public static void removeEventContext() { + removePluginContext(); + removeSenderContext(); + ThreadContext.remove("pufferfishsentry_eventdata"); + } + + private static Map serializeFields(Object object) { + Map fields = new TreeMap<>(); + fields.put("_class", object.getClass().getName()); + for (Field declaredField : object.getClass().getDeclaredFields()) { + try { + if (Modifier.isStatic(declaredField.getModifiers())) { + continue; + } + + String fieldName = declaredField.getName(); + if (fieldName.equals("handlers")) { + continue; + } + declaredField.setAccessible(true); + Object value = declaredField.get(object); + if (value != null) { + fields.put(fieldName, value.toString()); + } else { + fields.put(fieldName, ""); + } + } catch (Exception e) {} // We can't really safely log exceptions. + } + return fields; + } + + public static class State { + + private Plugin plugin; + private Command command; + private String commandLine; + private Event event; + private RegisteredListener registeredListener; + + public Plugin getPlugin() { + return plugin; + } + + public void setPlugin(Plugin plugin) { + this.plugin = plugin; + } + + public Command getCommand() { + return command; + } + + public void setCommand(Command command) { + this.command = command; + } + + public String getCommandLine() { + return commandLine; + } + + public void setCommandLine(String commandLine) { + this.commandLine = commandLine; + } + + public Event getEvent() { + return event; + } + + public void setEvent(Event event) { + this.event = event; + } + + public RegisteredListener getRegisteredListener() { + return registeredListener; + } + + public void setRegisteredListener(RegisteredListener registeredListener) { + this.registeredListener = registeredListener; + } + } +} diff --git a/src/main/java/gg/pufferfish/pufferfish/simd/SIMDChecker.java b/src/main/java/gg/pufferfish/pufferfish/simd/SIMDChecker.java new file mode 100644 index 0000000000000000000000000000000000000000..ab5fea0b03224bf249352ce340e94704ff713345 --- /dev/null +++ b/src/main/java/gg/pufferfish/pufferfish/simd/SIMDChecker.java @@ -0,0 +1,40 @@ +package gg.pufferfish.pufferfish.simd; + +import java.util.logging.Level; +import java.util.logging.Logger; +import jdk.incubator.vector.FloatVector; +import jdk.incubator.vector.IntVector; +import jdk.incubator.vector.VectorSpecies; + +/** + * Basically, java is annoying and we have to push this out to its own class. + */ +@Deprecated +public class SIMDChecker { + + @Deprecated + public static boolean canEnable(Logger logger) { + try { + if (SIMDDetection.getJavaVersion() != 17 && SIMDDetection.getJavaVersion() != 18 && SIMDDetection.getJavaVersion() != 19) { + return false; + } else { + SIMDDetection.testRun = true; + + VectorSpecies ISPEC = IntVector.SPECIES_PREFERRED; + VectorSpecies FSPEC = FloatVector.SPECIES_PREFERRED; + + logger.log(Level.INFO, "Max SIMD vector size on this system is " + ISPEC.vectorBitSize() + " bits (int)"); + logger.log(Level.INFO, "Max SIMD vector size on this system is " + FSPEC.vectorBitSize() + " bits (float)"); + + if (ISPEC.elementSize() < 2 || FSPEC.elementSize() < 2) { + logger.log(Level.WARNING, "SIMD is not properly supported on this system!"); + return false; + } + + return true; + } + } catch (NoClassDefFoundError | Exception ignored) {} // Basically, we don't do anything. This lets us detect if it's not functional and disable it. + return false; + } + +} diff --git a/src/main/java/gg/pufferfish/pufferfish/simd/SIMDDetection.java b/src/main/java/gg/pufferfish/pufferfish/simd/SIMDDetection.java new file mode 100644 index 0000000000000000000000000000000000000000..a84889d3e9cfc4d7ab5f867820a6484c6070711b --- /dev/null +++ b/src/main/java/gg/pufferfish/pufferfish/simd/SIMDDetection.java @@ -0,0 +1,35 @@ +package gg.pufferfish.pufferfish.simd; + +import java.util.logging.Logger; + +@Deprecated +public class SIMDDetection { + + public static boolean isEnabled = false; + public static boolean versionLimited = false; + public static boolean testRun = false; + + @Deprecated + public static boolean canEnable(Logger logger) { + try { + return SIMDChecker.canEnable(logger); + } catch (NoClassDefFoundError | Exception ignored) { + return false; + } + } + + @Deprecated + public static int getJavaVersion() { + // https://stackoverflow.com/a/2591122 + String version = System.getProperty("java.version"); + if(version.startsWith("1.")) { + version = version.substring(2, 3); + } else { + int dot = version.indexOf("."); + if(dot != -1) { version = version.substring(0, dot); } + } + version = version.split("-")[0]; // Azul is stupid + return Integer.parseInt(version); + } + +} diff --git a/src/main/java/gg/pufferfish/pufferfish/simd/VectorMapPalette.java b/src/main/java/gg/pufferfish/pufferfish/simd/VectorMapPalette.java new file mode 100644 index 0000000000000000000000000000000000000000..ae2464920c9412ac90b819a540ee58be0741465f --- /dev/null +++ b/src/main/java/gg/pufferfish/pufferfish/simd/VectorMapPalette.java @@ -0,0 +1,83 @@ +package gg.pufferfish.pufferfish.simd; + +import java.awt.Color; +import jdk.incubator.vector.FloatVector; +import jdk.incubator.vector.IntVector; +import jdk.incubator.vector.VectorMask; +import jdk.incubator.vector.VectorSpecies; +import org.bukkit.map.MapPalette; + +@Deprecated +public class VectorMapPalette { + + private static final VectorSpecies I_SPEC = IntVector.SPECIES_PREFERRED; + private static final VectorSpecies F_SPEC = FloatVector.SPECIES_PREFERRED; + + @Deprecated + public static void matchColorVectorized(int[] in, byte[] out) { + int speciesLength = I_SPEC.length(); + int i; + for (i = 0; i < in.length - speciesLength; i += speciesLength) { + float[] redsArr = new float[speciesLength]; + float[] bluesArr = new float[speciesLength]; + float[] greensArr = new float[speciesLength]; + int[] alphasArr = new int[speciesLength]; + + for (int j = 0; j < speciesLength; j++) { + alphasArr[j] = (in[i + j] >> 24) & 0xFF; + redsArr[j] = (in[i + j] >> 16) & 0xFF; + greensArr[j] = (in[i + j] >> 8) & 0xFF; + bluesArr[j] = (in[i + j] >> 0) & 0xFF; + } + + IntVector alphas = IntVector.fromArray(I_SPEC, alphasArr, 0); + FloatVector reds = FloatVector.fromArray(F_SPEC, redsArr, 0); + FloatVector greens = FloatVector.fromArray(F_SPEC, greensArr, 0); + FloatVector blues = FloatVector.fromArray(F_SPEC, bluesArr, 0); + IntVector resultIndex = IntVector.zero(I_SPEC); + VectorMask modificationMask = VectorMask.fromLong(I_SPEC, 0xffffffff); + + modificationMask = modificationMask.and(alphas.lt(128).not()); + FloatVector bestDistances = FloatVector.broadcast(F_SPEC, Float.MAX_VALUE); + + for (int c = 4; c < MapPalette.colors.length; c++) { + // We're using 32-bit floats here because it's 2x faster and nobody will know the difference. + // For correctness, the original algorithm uses 64-bit floats instead. Completely unnecessary. + FloatVector compReds = FloatVector.broadcast(F_SPEC, MapPalette.colors[c].getRed()); + FloatVector compGreens = FloatVector.broadcast(F_SPEC, MapPalette.colors[c].getGreen()); + FloatVector compBlues = FloatVector.broadcast(F_SPEC, MapPalette.colors[c].getBlue()); + + FloatVector rMean = reds.add(compReds).div(2.0f); + FloatVector rDiff = reds.sub(compReds); + FloatVector gDiff = greens.sub(compGreens); + FloatVector bDiff = blues.sub(compBlues); + + FloatVector weightR = rMean.div(256.0f).add(2); + FloatVector weightG = FloatVector.broadcast(F_SPEC, 4.0f); + FloatVector weightB = FloatVector.broadcast(F_SPEC, 255.0f).sub(rMean).div(256.0f).add(2.0f); + + FloatVector distance = weightR.mul(rDiff).mul(rDiff).add(weightG.mul(gDiff).mul(gDiff)).add(weightB.mul(bDiff).mul(bDiff)); + + // Now we compare to the best distance we've found. + // This mask contains a "1" if better, and a "0" otherwise. + VectorMask bestDistanceMask = distance.lt(bestDistances); + bestDistances = bestDistances.blend(distance, bestDistanceMask); // Update the best distances + + // Update the result array + // We also AND with the modification mask because we don't want to interfere if the alpha value isn't large enough. + resultIndex = resultIndex.blend(c, bestDistanceMask.cast(I_SPEC).and(modificationMask)); // Update the results + } + + for (int j = 0; j < speciesLength; j++) { + int index = resultIndex.lane(j); + out[i + j] = (byte) (index < 128 ? index : -129 + (index - 127)); + } + } + + // For the final ones, fall back to the regular method + for (; i < in.length; i++) { + out[i] = MapPalette.matchColor(new Color(in[i], true)); + } + } + +} diff --git a/src/main/java/org/bukkit/map/MapPalette.java b/src/main/java/org/bukkit/map/MapPalette.java index c80faa079eca1564847070f0338fc98024639829..e632d51d3487eb4807243b6705999ad124466bf5 100644 --- a/src/main/java/org/bukkit/map/MapPalette.java +++ b/src/main/java/org/bukkit/map/MapPalette.java @@ -1,6 +1,7 @@ package org.bukkit.map; import com.google.common.base.Preconditions; +import gg.pufferfish.pufferfish.simd.SIMDDetection; // Pufferfish import java.awt.Color; import java.awt.Graphics2D; import java.awt.Image; @@ -40,7 +41,7 @@ public final class MapPalette { } @NotNull - static final Color[] colors = { + public static final Color[] colors = { // Pufferfish - public access c(0, 0, 0, 0), c(0, 0, 0, 0), c(0, 0, 0, 0), c(0, 0, 0, 0), c(89, 125, 39), c(109, 153, 48), c(127, 178, 56), c(67, 94, 29), c(174, 164, 115), c(213, 201, 140), c(247, 233, 163), c(130, 123, 86), @@ -211,9 +212,15 @@ public final class MapPalette { temp.getRGB(0, 0, temp.getWidth(), temp.getHeight(), pixels, 0, temp.getWidth()); byte[] result = new byte[temp.getWidth() * temp.getHeight()]; + // Pufferfish start + if (!SIMDDetection.isEnabled) { for (int i = 0; i < pixels.length; i++) { result[i] = matchColor(new Color(pixels[i], true)); } + } else { + gg.pufferfish.pufferfish.simd.VectorMapPalette.matchColorVectorized(pixels, result); + } + // Pufferfish end return result; } diff --git a/src/main/java/org/bukkit/plugin/SimplePluginManager.java b/src/main/java/org/bukkit/plugin/SimplePluginManager.java index 07a9c9e254188c251165ca84c8e961fccda01175..5dc64d8c9aeae612fd31af0673f3530a9e777dfc 100644 --- a/src/main/java/org/bukkit/plugin/SimplePluginManager.java +++ b/src/main/java/org/bukkit/plugin/SimplePluginManager.java @@ -596,7 +596,9 @@ public final class SimplePluginManager implements PluginManager { // Paper start private void handlePluginException(String msg, Throwable ex, Plugin plugin) { + gg.pufferfish.pufferfish.sentry.SentryContext.setPluginContext(plugin); // Pufferfish server.getLogger().log(Level.SEVERE, msg, ex); + gg.pufferfish.pufferfish.sentry.SentryContext.removePluginContext(); // Pufferfish callEvent(new com.destroystokyo.paper.event.server.ServerExceptionEvent(new com.destroystokyo.paper.exception.ServerPluginEnableDisableException(msg, ex, plugin))); } // Paper end @@ -666,9 +668,11 @@ public final class SimplePluginManager implements PluginManager { )); } } catch (Throwable ex) { + gg.pufferfish.pufferfish.sentry.SentryContext.setEventContext(event, registration); // Pufferfish // Paper start - error reporting String msg = "Could not pass event " + event.getEventName() + " to " + registration.getPlugin().getDescription().getFullName(); server.getLogger().log(Level.SEVERE, msg, ex); + gg.pufferfish.pufferfish.sentry.SentryContext.removeEventContext(); // Pufferfish if (!(event instanceof com.destroystokyo.paper.event.server.ServerExceptionEvent)) { // We don't want to cause an endless event loop callEvent(new com.destroystokyo.paper.event.server.ServerExceptionEvent(new com.destroystokyo.paper.exception.ServerEventException(msg, ex, registration.getPlugin(), registration.getListener(), event))); } diff --git a/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java b/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java index eaefbb00e9993d54906cc8cf35cf753c0d6c7707..301e82369603f3dd6e6c1bd380da4bacacd7ef6c 100644 --- a/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java +++ b/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java @@ -336,7 +336,13 @@ public final class JavaPluginLoader implements PluginLoader { try { jPlugin.setEnabled(true); } catch (Throwable ex) { + gg.pufferfish.pufferfish.sentry.SentryContext.setPluginContext(plugin); // Pufferfish server.getLogger().log(Level.SEVERE, "Error occurred while enabling " + plugin.getDescription().getFullName() + " (Is it up to date?)", ex); + gg.pufferfish.pufferfish.sentry.SentryContext.removePluginContext(); // Pufferfish + // Paper start - Disable plugins that fail to load + this.server.getPluginManager().disablePlugin(jPlugin); + return; + // Paper end } // Perhaps abort here, rather than continue going, but as it stands, @@ -361,7 +367,9 @@ public final class JavaPluginLoader implements PluginLoader { try { jPlugin.setEnabled(false); } catch (Throwable ex) { + gg.pufferfish.pufferfish.sentry.SentryContext.setPluginContext(plugin); // Pufferfish server.getLogger().log(Level.SEVERE, "Error occurred while disabling " + plugin.getDescription().getFullName() + " (Is it up to date?)", ex); + gg.pufferfish.pufferfish.sentry.SentryContext.removePluginContext(); // Pufferfish } if (cloader instanceof PluginClassLoader) { diff --git a/src/main/java/org/bukkit/plugin/java/PluginClassLoader.java b/src/main/java/org/bukkit/plugin/java/PluginClassLoader.java index 7e4f7cb2afbc145e532285c793573ad107bc3033..12449e18180d604e9cbbc744da74a8b222a18e1f 100644 --- a/src/main/java/org/bukkit/plugin/java/PluginClassLoader.java +++ b/src/main/java/org/bukkit/plugin/java/PluginClassLoader.java @@ -50,6 +50,8 @@ public final class PluginClassLoader extends URLClassLoader implements io.paperm private io.papermc.paper.plugin.provider.classloader.PluginClassLoaderGroup classLoaderGroup; // Paper public io.papermc.paper.plugin.provider.entrypoint.DependencyContext dependencyContext; // Paper + private boolean closed = false; // Pufferfish + static { ClassLoader.registerAsParallelCapable(); } @@ -197,6 +199,7 @@ public final class PluginClassLoader extends URLClassLoader implements io.paperm throw new ClassNotFoundException(name); } + public boolean _airplane_hasClass(@NotNull String name) { return this.classes.containsKey(name); } // Pufferfish @Override protected Class findClass(String name) throws ClassNotFoundException { if (name.startsWith("org.bukkit.") || name.startsWith("net.minecraft.")) { @@ -204,7 +207,7 @@ public final class PluginClassLoader extends URLClassLoader implements io.paperm } Class result = classes.get(name); - if (result == null) { + if (result == null && !this.closed) { // Pufferfish String path = name.replace('.', '/').concat(".class"); JarEntry entry = jar.getJarEntry(path); @@ -251,6 +254,7 @@ public final class PluginClassLoader extends URLClassLoader implements io.paperm this.setClass(name, result); // Paper } + if (result == null) throw new ClassNotFoundException(name); // Pufferfish return result; } @@ -265,6 +269,7 @@ public final class PluginClassLoader extends URLClassLoader implements io.paperm // Paper end super.close(); } finally { + this.closed = true; // Pufferfish jar.close(); } }