|
| 1 | +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 |
| 2 | +From: adabugra <57899270+adabugra@users.noreply.github.com> |
| 3 | +Date: Mon, 6 Jan 2025 19:24:06 +0300 |
| 4 | +Subject: [PATCH] Pufferfish: Sentry |
| 5 | + |
| 6 | + |
| 7 | +diff --git a/build.gradle.kts b/build.gradle.kts |
| 8 | +index 1aa76451f9129b578d6887b1239966eb67f5ee1b..2ddad10cf12dd07fd227ddda13336fdd61cd0197 100644 |
| 9 | +--- a/build.gradle.kts |
| 10 | ++++ b/build.gradle.kts |
| 11 | +@@ -66,6 +66,7 @@ dependencies { |
| 12 | + apiAndDocs("net.kyori:adventure-text-logger-slf4j") |
| 13 | + api("org.apache.logging.log4j:log4j-api:$log4jVersion") |
| 14 | + api("org.slf4j:slf4j-api:$slf4jVersion") |
| 15 | ++ api("io.sentry:sentry:8.0.0-rc.2") // Pufferfish |
| 16 | + |
| 17 | + implementation("org.ow2.asm:asm:9.7.1") |
| 18 | + implementation("org.ow2.asm:asm-commons:9.7.1") |
| 19 | +diff --git a/src/main/java/gg/pufferfish/pufferfish/sentry/SentryContext.java b/src/main/java/gg/pufferfish/pufferfish/sentry/SentryContext.java |
| 20 | +new file mode 100644 |
| 21 | +index 0000000000000000000000000000000000000000..c7772aac00f6db664f7a5673bc2585fa025e6aad |
| 22 | +--- /dev/null |
| 23 | ++++ b/src/main/java/gg/pufferfish/pufferfish/sentry/SentryContext.java |
| 24 | +@@ -0,0 +1,165 @@ |
| 25 | ++package gg.pufferfish.pufferfish.sentry; |
| 26 | ++ |
| 27 | ++import com.google.gson.Gson; |
| 28 | ++ |
| 29 | ++import java.lang.reflect.Field; |
| 30 | ++import java.lang.reflect.Modifier; |
| 31 | ++import java.util.Map; |
| 32 | ++import java.util.TreeMap; |
| 33 | ++ |
| 34 | ++import org.apache.logging.log4j.ThreadContext; |
| 35 | ++import org.bukkit.command.Command; |
| 36 | ++import org.bukkit.command.CommandSender; |
| 37 | ++import org.bukkit.entity.Player; |
| 38 | ++import org.bukkit.event.Event; |
| 39 | ++import org.bukkit.event.player.PlayerEvent; |
| 40 | ++import org.bukkit.plugin.Plugin; |
| 41 | ++import org.bukkit.plugin.RegisteredListener; |
| 42 | ++import org.jetbrains.annotations.Nullable; |
| 43 | ++ |
| 44 | ++public class SentryContext { |
| 45 | ++ |
| 46 | ++ private static final Gson GSON = new Gson(); |
| 47 | ++ |
| 48 | ++ public static void setPluginContext(@Nullable Plugin plugin) { |
| 49 | ++ if (plugin != null) { |
| 50 | ++ ThreadContext.put("pufferfishsentry_pluginname", plugin.getName()); |
| 51 | ++ ThreadContext.put("pufferfishsentry_pluginversion", plugin.getPluginMeta().getVersion()); |
| 52 | ++ } |
| 53 | ++ } |
| 54 | ++ |
| 55 | ++ public static void removePluginContext() { |
| 56 | ++ ThreadContext.remove("pufferfishsentry_pluginname"); |
| 57 | ++ ThreadContext.remove("pufferfishsentry_pluginversion"); |
| 58 | ++ } |
| 59 | ++ |
| 60 | ++ public static void setSenderContext(@Nullable CommandSender sender) { |
| 61 | ++ if (sender != null) { |
| 62 | ++ ThreadContext.put("pufferfishsentry_playername", sender.getName()); |
| 63 | ++ if (sender instanceof Player player) { |
| 64 | ++ ThreadContext.put("pufferfishsentry_playerid", player.getUniqueId().toString()); |
| 65 | ++ } |
| 66 | ++ } |
| 67 | ++ } |
| 68 | ++ |
| 69 | ++ public static void removeSenderContext() { |
| 70 | ++ ThreadContext.remove("pufferfishsentry_playername"); |
| 71 | ++ ThreadContext.remove("pufferfishsentry_playerid"); |
| 72 | ++ } |
| 73 | ++ |
| 74 | ++ public static void setEventContext(Event event, RegisteredListener registration) { |
| 75 | ++ setPluginContext(registration.getPlugin()); |
| 76 | ++ |
| 77 | ++ try { |
| 78 | ++ // Find the player that was involved with this event |
| 79 | ++ Player player = null; |
| 80 | ++ if (event instanceof PlayerEvent) { |
| 81 | ++ player = ((PlayerEvent) event).getPlayer(); |
| 82 | ++ } else { |
| 83 | ++ Class<? extends Event> eventClass = event.getClass(); |
| 84 | ++ |
| 85 | ++ Field playerField = null; |
| 86 | ++ |
| 87 | ++ for (Field field : eventClass.getDeclaredFields()) { |
| 88 | ++ if (field.getType().equals(Player.class)) { |
| 89 | ++ playerField = field; |
| 90 | ++ break; |
| 91 | ++ } |
| 92 | ++ } |
| 93 | ++ |
| 94 | ++ if (playerField != null) { |
| 95 | ++ playerField.setAccessible(true); |
| 96 | ++ player = (Player) playerField.get(event); |
| 97 | ++ } |
| 98 | ++ } |
| 99 | ++ |
| 100 | ++ if (player != null) { |
| 101 | ++ setSenderContext(player); |
| 102 | ++ } |
| 103 | ++ } catch (Exception ignored) { |
| 104 | ++ } // We can't really safely log exceptions. |
| 105 | ++ |
| 106 | ++ ThreadContext.put("pufferfishsentry_eventdata", GSON.toJson(serializeFields(event))); |
| 107 | ++ } |
| 108 | ++ |
| 109 | ++ public static void removeEventContext() { |
| 110 | ++ removePluginContext(); |
| 111 | ++ removeSenderContext(); |
| 112 | ++ ThreadContext.remove("pufferfishsentry_eventdata"); |
| 113 | ++ } |
| 114 | ++ |
| 115 | ++ private static Map<String, String> serializeFields(Object object) { |
| 116 | ++ Map<String, String> fields = new TreeMap<>(); |
| 117 | ++ fields.put("_class", object.getClass().getName()); |
| 118 | ++ for (Field declaredField : object.getClass().getDeclaredFields()) { |
| 119 | ++ try { |
| 120 | ++ if (Modifier.isStatic(declaredField.getModifiers())) { |
| 121 | ++ continue; |
| 122 | ++ } |
| 123 | ++ |
| 124 | ++ String fieldName = declaredField.getName(); |
| 125 | ++ if (fieldName.equals("handlers")) { |
| 126 | ++ continue; |
| 127 | ++ } |
| 128 | ++ declaredField.setAccessible(true); |
| 129 | ++ Object value = declaredField.get(object); |
| 130 | ++ if (value != null) { |
| 131 | ++ fields.put(fieldName, value.toString()); |
| 132 | ++ } else { |
| 133 | ++ fields.put(fieldName, "<null>"); |
| 134 | ++ } |
| 135 | ++ } catch (Exception ignored) { |
| 136 | ++ } // We can't really safely log exceptions. |
| 137 | ++ } |
| 138 | ++ return fields; |
| 139 | ++ } |
| 140 | ++ |
| 141 | ++ public static class State { |
| 142 | ++ |
| 143 | ++ private Plugin plugin; |
| 144 | ++ private Command command; |
| 145 | ++ private String commandLine; |
| 146 | ++ private Event event; |
| 147 | ++ private RegisteredListener registeredListener; |
| 148 | ++ |
| 149 | ++ public Plugin getPlugin() { |
| 150 | ++ return plugin; |
| 151 | ++ } |
| 152 | ++ |
| 153 | ++ public void setPlugin(Plugin plugin) { |
| 154 | ++ this.plugin = plugin; |
| 155 | ++ } |
| 156 | ++ |
| 157 | ++ public Command getCommand() { |
| 158 | ++ return command; |
| 159 | ++ } |
| 160 | ++ |
| 161 | ++ public void setCommand(Command command) { |
| 162 | ++ this.command = command; |
| 163 | ++ } |
| 164 | ++ |
| 165 | ++ public String getCommandLine() { |
| 166 | ++ return commandLine; |
| 167 | ++ } |
| 168 | ++ |
| 169 | ++ public void setCommandLine(String commandLine) { |
| 170 | ++ this.commandLine = commandLine; |
| 171 | ++ } |
| 172 | ++ |
| 173 | ++ public Event getEvent() { |
| 174 | ++ return event; |
| 175 | ++ } |
| 176 | ++ |
| 177 | ++ public void setEvent(Event event) { |
| 178 | ++ this.event = event; |
| 179 | ++ } |
| 180 | ++ |
| 181 | ++ public RegisteredListener getRegisteredListener() { |
| 182 | ++ return registeredListener; |
| 183 | ++ } |
| 184 | ++ |
| 185 | ++ public void setRegisteredListener(RegisteredListener registeredListener) { |
| 186 | ++ this.registeredListener = registeredListener; |
| 187 | ++ } |
| 188 | ++ } |
| 189 | ++} |
| 190 | +diff --git a/src/main/java/org/bukkit/plugin/SimplePluginManager.java b/src/main/java/org/bukkit/plugin/SimplePluginManager.java |
| 191 | +index ab36e3aaff57e2f27b5aed06b4bdfe277f86a35e..96da9f1082ab134d197b3a6069f2fcdf38585efe 100644 |
| 192 | +--- a/src/main/java/org/bukkit/plugin/SimplePluginManager.java |
| 193 | ++++ b/src/main/java/org/bukkit/plugin/SimplePluginManager.java |
| 194 | +@@ -597,7 +597,9 @@ public final class SimplePluginManager implements PluginManager { |
| 195 | + |
| 196 | + // Paper start |
| 197 | + private void handlePluginException(String msg, Throwable ex, Plugin plugin) { |
| 198 | ++ gg.pufferfish.pufferfish.sentry.SentryContext.setPluginContext(plugin); // Pufferfish |
| 199 | + server.getLogger().log(Level.SEVERE, msg, ex); |
| 200 | ++ gg.pufferfish.pufferfish.sentry.SentryContext.removePluginContext(); // Pufferfish |
| 201 | + callEvent(new com.destroystokyo.paper.event.server.ServerExceptionEvent(new com.destroystokyo.paper.exception.ServerPluginEnableDisableException(msg, ex, plugin))); |
| 202 | + } |
| 203 | + // Paper end |
| 204 | +@@ -667,9 +669,11 @@ public final class SimplePluginManager implements PluginManager { |
| 205 | + )); |
| 206 | + } |
| 207 | + } catch (Throwable ex) { |
| 208 | ++ gg.pufferfish.pufferfish.sentry.SentryContext.setEventContext(event, registration); // Pufferfish |
| 209 | + // Paper start - error reporting |
| 210 | + String msg = "Could not pass event " + event.getEventName() + " to " + registration.getPlugin().getDescription().getFullName(); |
| 211 | + server.getLogger().log(Level.SEVERE, msg, ex); |
| 212 | ++ gg.pufferfish.pufferfish.sentry.SentryContext.removeEventContext(); // Pufferfish |
| 213 | + if (!(event instanceof com.destroystokyo.paper.event.server.ServerExceptionEvent)) { // We don't want to cause an endless event loop |
| 214 | + callEvent(new com.destroystokyo.paper.event.server.ServerExceptionEvent(new com.destroystokyo.paper.exception.ServerEventException(msg, ex, registration.getPlugin(), registration.getListener(), event))); |
| 215 | + } |
| 216 | +diff --git a/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java b/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java |
| 217 | +index b412aaf08901d169ac9fc89b36f9d6ccb95c53d3..45a9ca8969f635d20cc44c062fda85bbccd8f8ff 100644 |
| 218 | +--- a/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java |
| 219 | ++++ b/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java |
| 220 | +@@ -336,7 +336,13 @@ public final class JavaPluginLoader implements PluginLoader { |
| 221 | + try { |
| 222 | + jPlugin.setEnabled(true); |
| 223 | + } catch (Throwable ex) { |
| 224 | ++ gg.pufferfish.pufferfish.sentry.SentryContext.setPluginContext(plugin); // Pufferfish |
| 225 | + server.getLogger().log(Level.SEVERE, "Error occurred while enabling " + plugin.getDescription().getFullName() + " (Is it up to date?)", ex); |
| 226 | ++ gg.pufferfish.pufferfish.sentry.SentryContext.removePluginContext(); // Pufferfish |
| 227 | ++ // Paper start - Disable plugins that fail to load |
| 228 | ++ this.server.getPluginManager().disablePlugin(jPlugin); |
| 229 | ++ return; |
| 230 | ++ // Paper end |
| 231 | + } |
| 232 | + |
| 233 | + // Perhaps abort here, rather than continue going, but as it stands, |
| 234 | +@@ -361,7 +367,9 @@ public final class JavaPluginLoader implements PluginLoader { |
| 235 | + try { |
| 236 | + jPlugin.setEnabled(false); |
| 237 | + } catch (Throwable ex) { |
| 238 | ++ gg.pufferfish.pufferfish.sentry.SentryContext.setPluginContext(plugin); // Pufferfish |
| 239 | + server.getLogger().log(Level.SEVERE, "Error occurred while disabling " + plugin.getDescription().getFullName() + " (Is it up to date?)", ex); |
| 240 | ++ gg.pufferfish.pufferfish.sentry.SentryContext.removePluginContext(); // Pufferfish |
| 241 | + } |
| 242 | + |
| 243 | + if (cloader instanceof PluginClassLoader) { |
0 commit comments