|
| 1 | +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 |
| 2 | +From: Kevin Raneri <kevin.raneri@gmail.com> |
| 3 | +Date: Tue, 9 Nov 2021 14:01:56 -0500 |
| 4 | +Subject: [PATCH] Pufferfish API Changes |
| 5 | + |
| 6 | +Pufferfish |
| 7 | +Copyright (C) 2022 pufferfish-gg |
| 8 | + |
| 9 | +This program is free software: you can redistribute it and/or modify |
| 10 | +it under the terms of the GNU General Public License as published by |
| 11 | +the Free Software Foundation, either version 3 of the License, or |
| 12 | +(at your option) any later version. |
| 13 | + |
| 14 | +This program is distributed in the hope that it will be useful, |
| 15 | +but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 16 | +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 17 | +GNU General Public License for more details. |
| 18 | + |
| 19 | +You should have received a copy of the GNU General Public License |
| 20 | +along with this program. If not, see <http://www.gnu.org/licenses/>. |
| 21 | + |
| 22 | +diff --git a/build.gradle.kts b/build.gradle.kts |
| 23 | +index 001c2b963205012f340db0d539e4033c748124ce..554f5e35954f35aecaf454853a0a2999f15d19bc 100644 |
| 24 | +--- a/build.gradle.kts |
| 25 | ++++ b/build.gradle.kts |
| 26 | +@@ -40,6 +40,7 @@ dependencies { |
| 27 | + apiAndDocs("net.kyori:adventure-text-serializer-plain") |
| 28 | + api("org.apache.logging.log4j:log4j-api:2.17.1") |
| 29 | + api("org.slf4j:slf4j-api:1.8.0-beta4") |
| 30 | ++ api("io.sentry:sentry:5.4.0") // Pufferfish |
| 31 | + |
| 32 | + implementation("org.ow2.asm:asm:9.2") |
| 33 | + implementation("org.ow2.asm:asm-commons:9.2") |
| 34 | +diff --git a/src/main/java/gg/pufferfish/pufferfish/sentry/SentryContext.java b/src/main/java/gg/pufferfish/pufferfish/sentry/SentryContext.java |
| 35 | +new file mode 100644 |
| 36 | +index 0000000000000000000000000000000000000000..10310fdd53de28efb8a8250f6d3b0c8eb08fb68a |
| 37 | +--- /dev/null |
| 38 | ++++ b/src/main/java/gg/pufferfish/pufferfish/sentry/SentryContext.java |
| 39 | +@@ -0,0 +1,161 @@ |
| 40 | ++package gg.pufferfish.pufferfish.sentry; |
| 41 | ++ |
| 42 | ++import com.google.gson.Gson; |
| 43 | ++import java.lang.reflect.Field; |
| 44 | ++import java.lang.reflect.Modifier; |
| 45 | ++import java.util.Map; |
| 46 | ++import java.util.TreeMap; |
| 47 | ++import org.apache.logging.log4j.ThreadContext; |
| 48 | ++import org.bukkit.command.Command; |
| 49 | ++import org.bukkit.command.CommandSender; |
| 50 | ++import org.bukkit.entity.Player; |
| 51 | ++import org.bukkit.event.Event; |
| 52 | ++import org.bukkit.event.player.PlayerEvent; |
| 53 | ++import org.bukkit.plugin.Plugin; |
| 54 | ++import org.bukkit.plugin.RegisteredListener; |
| 55 | ++import org.jetbrains.annotations.Nullable; |
| 56 | ++ |
| 57 | ++public class SentryContext { |
| 58 | ++ |
| 59 | ++ private static final Gson GSON = new Gson(); |
| 60 | ++ |
| 61 | ++ public static void setPluginContext(@Nullable Plugin plugin) { |
| 62 | ++ if (plugin != null) { |
| 63 | ++ ThreadContext.put("pufferfishsentry_pluginname", plugin.getName()); |
| 64 | ++ ThreadContext.put("pufferfishsentry_pluginversion", plugin.getDescription().getVersion()); |
| 65 | ++ } |
| 66 | ++ } |
| 67 | ++ |
| 68 | ++ public static void removePluginContext() { |
| 69 | ++ ThreadContext.remove("pufferfishsentry_pluginname"); |
| 70 | ++ ThreadContext.remove("pufferfishsentry_pluginversion"); |
| 71 | ++ } |
| 72 | ++ |
| 73 | ++ public static void setSenderContext(@Nullable CommandSender sender) { |
| 74 | ++ if (sender != null) { |
| 75 | ++ ThreadContext.put("pufferfishsentry_playername", sender.getName()); |
| 76 | ++ if (sender instanceof Player player) { |
| 77 | ++ ThreadContext.put("pufferfishsentry_playerid", player.getUniqueId().toString()); |
| 78 | ++ } |
| 79 | ++ } |
| 80 | ++ } |
| 81 | ++ |
| 82 | ++ public static void removeSenderContext() { |
| 83 | ++ ThreadContext.remove("pufferfishsentry_playername"); |
| 84 | ++ ThreadContext.remove("pufferfishsentry_playerid"); |
| 85 | ++ } |
| 86 | ++ |
| 87 | ++ public static void setEventContext(Event event, RegisteredListener registration) { |
| 88 | ++ setPluginContext(registration.getPlugin()); |
| 89 | ++ |
| 90 | ++ try { |
| 91 | ++ // Find the player that was involved with this event |
| 92 | ++ Player player = null; |
| 93 | ++ if (event instanceof PlayerEvent) { |
| 94 | ++ player = ((PlayerEvent) event).getPlayer(); |
| 95 | ++ } else { |
| 96 | ++ Class<? extends Event> eventClass = event.getClass(); |
| 97 | ++ |
| 98 | ++ Field playerField = null; |
| 99 | ++ |
| 100 | ++ for (Field field : eventClass.getDeclaredFields()) { |
| 101 | ++ if (field.getType().equals(Player.class)) { |
| 102 | ++ playerField = field; |
| 103 | ++ break; |
| 104 | ++ } |
| 105 | ++ } |
| 106 | ++ |
| 107 | ++ if (playerField != null) { |
| 108 | ++ playerField.setAccessible(true); |
| 109 | ++ player = (Player) playerField.get(event); |
| 110 | ++ } |
| 111 | ++ } |
| 112 | ++ |
| 113 | ++ if (player != null) { |
| 114 | ++ setSenderContext(player); |
| 115 | ++ } |
| 116 | ++ } catch (Exception e) {} // We can't really safely log exceptions. |
| 117 | ++ |
| 118 | ++ ThreadContext.put("pufferfishsentry_eventdata", GSON.toJson(serializeFields(event))); |
| 119 | ++ } |
| 120 | ++ |
| 121 | ++ public static void removeEventContext() { |
| 122 | ++ removePluginContext(); |
| 123 | ++ removeSenderContext(); |
| 124 | ++ ThreadContext.remove("pufferfishsentry_eventdata"); |
| 125 | ++ } |
| 126 | ++ |
| 127 | ++ private static Map<String, String> serializeFields(Object object) { |
| 128 | ++ Map<String, String> fields = new TreeMap<>(); |
| 129 | ++ fields.put("_class", object.getClass().getName()); |
| 130 | ++ for (Field declaredField : object.getClass().getDeclaredFields()) { |
| 131 | ++ try { |
| 132 | ++ if (Modifier.isStatic(declaredField.getModifiers())) { |
| 133 | ++ continue; |
| 134 | ++ } |
| 135 | ++ |
| 136 | ++ String fieldName = declaredField.getName(); |
| 137 | ++ if (fieldName.equals("handlers")) { |
| 138 | ++ continue; |
| 139 | ++ } |
| 140 | ++ declaredField.setAccessible(true); |
| 141 | ++ Object value = declaredField.get(object); |
| 142 | ++ if (value != null) { |
| 143 | ++ fields.put(fieldName, value.toString()); |
| 144 | ++ } else { |
| 145 | ++ fields.put(fieldName, "<null>"); |
| 146 | ++ } |
| 147 | ++ } catch (Exception e) {} // We can't really safely log exceptions. |
| 148 | ++ } |
| 149 | ++ return fields; |
| 150 | ++ } |
| 151 | ++ |
| 152 | ++ public static class State { |
| 153 | ++ |
| 154 | ++ private Plugin plugin; |
| 155 | ++ private Command command; |
| 156 | ++ private String commandLine; |
| 157 | ++ private Event event; |
| 158 | ++ private RegisteredListener registeredListener; |
| 159 | ++ |
| 160 | ++ public Plugin getPlugin() { |
| 161 | ++ return plugin; |
| 162 | ++ } |
| 163 | ++ |
| 164 | ++ public void setPlugin(Plugin plugin) { |
| 165 | ++ this.plugin = plugin; |
| 166 | ++ } |
| 167 | ++ |
| 168 | ++ public Command getCommand() { |
| 169 | ++ return command; |
| 170 | ++ } |
| 171 | ++ |
| 172 | ++ public void setCommand(Command command) { |
| 173 | ++ this.command = command; |
| 174 | ++ } |
| 175 | ++ |
| 176 | ++ public String getCommandLine() { |
| 177 | ++ return commandLine; |
| 178 | ++ } |
| 179 | ++ |
| 180 | ++ public void setCommandLine(String commandLine) { |
| 181 | ++ this.commandLine = commandLine; |
| 182 | ++ } |
| 183 | ++ |
| 184 | ++ public Event getEvent() { |
| 185 | ++ return event; |
| 186 | ++ } |
| 187 | ++ |
| 188 | ++ public void setEvent(Event event) { |
| 189 | ++ this.event = event; |
| 190 | ++ } |
| 191 | ++ |
| 192 | ++ public RegisteredListener getRegisteredListener() { |
| 193 | ++ return registeredListener; |
| 194 | ++ } |
| 195 | ++ |
| 196 | ++ public void setRegisteredListener(RegisteredListener registeredListener) { |
| 197 | ++ this.registeredListener = registeredListener; |
| 198 | ++ } |
| 199 | ++ } |
| 200 | ++} |
| 201 | +diff --git a/src/main/java/org/bukkit/plugin/SimplePluginManager.java b/src/main/java/org/bukkit/plugin/SimplePluginManager.java |
| 202 | +index 1366496271c4c7f72d1e5f990e51775b1c371f99..05bb388407b5bd8a942478237580a38ffaa388c8 100644 |
| 203 | +--- a/src/main/java/org/bukkit/plugin/SimplePluginManager.java |
| 204 | ++++ b/src/main/java/org/bukkit/plugin/SimplePluginManager.java |
| 205 | +@@ -581,7 +581,9 @@ public final class SimplePluginManager implements PluginManager { |
| 206 | + |
| 207 | + // Paper start |
| 208 | + private void handlePluginException(String msg, Throwable ex, Plugin plugin) { |
| 209 | ++ gg.pufferfish.pufferfish.sentry.SentryContext.setPluginContext(plugin); // Pufferfish |
| 210 | + server.getLogger().log(Level.SEVERE, msg, ex); |
| 211 | ++ gg.pufferfish.pufferfish.sentry.SentryContext.removePluginContext(); // Pufferfish |
| 212 | + callEvent(new ServerExceptionEvent(new ServerPluginEnableDisableException(msg, ex, plugin))); |
| 213 | + } |
| 214 | + // Paper end |
| 215 | +@@ -640,14 +642,16 @@ public final class SimplePluginManager implements PluginManager { |
| 216 | + )); |
| 217 | + } |
| 218 | + } catch (Throwable ex) { |
| 219 | ++ gg.pufferfish.pufferfish.sentry.SentryContext.setEventContext(event, registration); // Pufferfish |
| 220 | + // Paper start - error reporting |
| 221 | + String msg = "Could not pass event " + event.getEventName() + " to " + registration.getPlugin().getDescription().getFullName(); |
| 222 | + server.getLogger().log(Level.SEVERE, msg, ex); |
| 223 | ++ gg.pufferfish.pufferfish.sentry.SentryContext.removeEventContext(); // Pufferfish |
| 224 | + if (!(event instanceof ServerExceptionEvent)) { // We don't want to cause an endless event loop |
| 225 | + callEvent(new ServerExceptionEvent(new ServerEventException(msg, ex, registration.getPlugin(), registration.getListener(), event))); |
| 226 | + } |
| 227 | + // Paper end |
| 228 | +- } |
| 229 | ++ } finally { gg.pufferfish.pufferfish.sentry.SentryContext.removeEventContext(); } // Pufferfish |
| 230 | + } |
| 231 | + } |
| 232 | + |
| 233 | +diff --git a/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java b/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java |
| 234 | +index c8b11793c6a3baabc1c9566e0463ab1d6e293827..2b9218ddd262e89180588c3014dad328317dd8db 100644 |
| 235 | +--- a/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java |
| 236 | ++++ b/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java |
| 237 | +@@ -369,7 +369,9 @@ public final class JavaPluginLoader implements PluginLoader { |
| 238 | + try { |
| 239 | + jPlugin.setEnabled(true); |
| 240 | + } catch (Throwable ex) { |
| 241 | ++ gg.pufferfish.pufferfish.sentry.SentryContext.setPluginContext(plugin); // Pufferfish |
| 242 | + server.getLogger().log(Level.SEVERE, "Error occurred while enabling " + plugin.getDescription().getFullName() + " (Is it up to date?)", ex); |
| 243 | ++ gg.pufferfish.pufferfish.sentry.SentryContext.removePluginContext(); // Pufferfish |
| 244 | + // Paper start - Disable plugins that fail to load |
| 245 | + this.server.getPluginManager().disablePlugin(jPlugin); |
| 246 | + return; |
| 247 | +@@ -398,7 +400,9 @@ public final class JavaPluginLoader implements PluginLoader { |
| 248 | + try { |
| 249 | + jPlugin.setEnabled(false); |
| 250 | + } catch (Throwable ex) { |
| 251 | ++ gg.pufferfish.pufferfish.sentry.SentryContext.setPluginContext(plugin); // Pufferfish |
| 252 | + server.getLogger().log(Level.SEVERE, "Error occurred while disabling " + plugin.getDescription().getFullName() + " (Is it up to date?)", ex); |
| 253 | ++ gg.pufferfish.pufferfish.sentry.SentryContext.removePluginContext(); // Pufferfish |
| 254 | + } |
| 255 | + |
| 256 | + if (cloader instanceof PluginClassLoader) { |
| 257 | +diff --git a/src/main/java/org/bukkit/plugin/java/PluginClassLoader.java b/src/main/java/org/bukkit/plugin/java/PluginClassLoader.java |
| 258 | +index 8a39c48fce819d72a94d5309db8dfc42930989af..c67e91316f35750b18e082746206a01e783f1740 100644 |
| 259 | +--- a/src/main/java/org/bukkit/plugin/java/PluginClassLoader.java |
| 260 | ++++ b/src/main/java/org/bukkit/plugin/java/PluginClassLoader.java |
| 261 | +@@ -46,6 +46,8 @@ public final class PluginClassLoader extends URLClassLoader { // Spigot |
| 262 | + private final Set<String> seenIllegalAccess = Collections.newSetFromMap(new ConcurrentHashMap<>()); |
| 263 | + private java.util.logging.Logger logger; // Paper - add field |
| 264 | + |
| 265 | ++ private boolean closed = false; // Pufferfish |
| 266 | ++ |
| 267 | + static { |
| 268 | + ClassLoader.registerAsParallelCapable(); |
| 269 | + } |
| 270 | +@@ -151,6 +153,7 @@ public final class PluginClassLoader extends URLClassLoader { // Spigot |
| 271 | + throw new ClassNotFoundException(name); |
| 272 | + } |
| 273 | + |
| 274 | ++ public boolean _airplane_hasClass(@NotNull String name) { return this.classes.containsKey(name); } // Pufferfish |
| 275 | + @Override |
| 276 | + protected Class<?> findClass(String name) throws ClassNotFoundException { |
| 277 | + if (name.startsWith("org.bukkit.") || name.startsWith("net.minecraft.")) { |
| 278 | +@@ -158,7 +161,7 @@ public final class PluginClassLoader extends URLClassLoader { // Spigot |
| 279 | + } |
| 280 | + Class<?> result = classes.get(name); |
| 281 | + |
| 282 | +- if (result == null) { |
| 283 | ++ if (result == null && !this.closed) { // Pufferfish |
| 284 | + String path = name.replace('.', '/').concat(".class"); |
| 285 | + JarEntry entry = jar.getJarEntry(path); |
| 286 | + |
| 287 | +@@ -213,6 +216,7 @@ public final class PluginClassLoader extends URLClassLoader { // Spigot |
| 288 | + try { |
| 289 | + super.close(); |
| 290 | + } finally { |
| 291 | ++ this.closed = true; // Pufferfish |
| 292 | + jar.close(); |
| 293 | + } |
| 294 | + } |
0 commit comments