Skip to content

Commit e6e33b3

Browse files
authored
Merge pull request #41 from adabugra/feature/sentry
Feature: Add Sentry
2 parents b9ca4c0 + bebfe1e commit e6e33b3

File tree

2 files changed

+492
-0
lines changed

2 files changed

+492
-0
lines changed
Lines changed: 243 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,243 @@
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

Comments
 (0)