From 980c455c2d14207623cd676bc4a6a4a6b7bf3f41 Mon Sep 17 00:00:00 2001 From: Alexander01998 Date: Mon, 19 Oct 2020 19:25:46 +0200 Subject: [PATCH] Add Sentry integration Unfortunately this is incompatible with Minecraft's default Java version (1.8.0_51), so none of this will actually work in production until (a) Minecraft updates to a newer Java version, or (b) Sentry changes their SSL certificate to something that Java 1.8.0_51 can understand. See also: https://github.com/getsentry/sentry-java/issues/1000 I found out about this issue AFTER I spent nearly a week setting everything up just the way I like, so now I don't want to just delete it all. I will leave this here until the issue is fixed or I can figure out something else to make it work. --- build.gradle | 4 + .../java/net/wurstclient/WurstClient.java | 91 ++++++++++++++ .../net/wurstclient/command/CmdProcessor.java | 3 + src/main/java/net/wurstclient/hack/Hack.java | 11 ++ .../keybinds/KeybindProcessor.java | 15 ++- .../mixin/MinecraftClientMixin.java | 116 ++++++++++++++++++ 6 files changed, 237 insertions(+), 3 deletions(-) diff --git a/build.gradle b/build.gradle index 214ee911..befb6c91 100644 --- a/build.gradle +++ b/build.gradle @@ -22,6 +22,10 @@ dependencies { // Fabric API. This is technically optional, but you probably want it anyway. modCompile "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}" + // Sentry. Automatically reports crashes so you don't have to. + modCompile 'io.sentry:sentry:3.1.0' + include 'io.sentry:sentry:3.1.0' + // net.wurstclient.ai.PathPos extends net.minecraft.util.math.BlockPos, // which uses javax.annotation.concurrent.Immutable, which is part of // the JSR305 library. diff --git a/src/main/java/net/wurstclient/WurstClient.java b/src/main/java/net/wurstclient/WurstClient.java index 2c3db63d..6761d153 100644 --- a/src/main/java/net/wurstclient/WurstClient.java +++ b/src/main/java/net/wurstclient/WurstClient.java @@ -13,12 +13,17 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.StandardOpenOption; import java.util.ArrayList; +import java.util.HashMap; import java.util.stream.Collectors; import java.util.stream.Stream; import org.lwjgl.glfw.GLFW; +import io.sentry.Sentry; import net.fabricmc.fabric.api.client.keybinding.v1.KeyBindingHelper; +import net.fabricmc.loader.api.FabricLoader; +import net.fabricmc.loader.api.metadata.ModMetadata; +import net.minecraft.SharedConstants; import net.minecraft.client.MinecraftClient; import net.minecraft.client.options.KeyBinding; import net.minecraft.client.util.InputUtil; @@ -86,6 +91,7 @@ public enum WurstClient { System.out.println("Starting Wurst Client..."); + setupSentry(); wurstFolder = createWurstFolder(); String trackingID = "UA-52838431-5"; @@ -150,6 +156,91 @@ public enum WurstClient "Wurst " + VERSION + " MC" + MC_VERSION); } + private void setupSentry() + { + FabricLoader fabricLoader = FabricLoader.getInstance(); + + Sentry.init(options -> { + + options.setDsn( + "https://c01aef15a7cb466da7824ec5dac0d009@o302674.ingest.sentry.io/5464583"); + options.setDebug(true); + + String modVersion = fabricLoader.getModContainer("wurst").get() + .getMetadata().getVersion().getFriendlyString(); + + if(modVersion != null && !modVersion.equals("${version}")) + options.setRelease(modVersion); + else + options.setRelease("v" + VERSION + "-MC" + MC_VERSION); + }); + + Sentry.configureScope(scope -> { + scope.setTag("wurst.version", VERSION); + }); + + Sentry.configureScope(scope -> { + scope.setTag("mc.version", + SharedConstants.getGameVersion().getName()); + }); + + Sentry.configureScope(scope -> { + scope.setTag("fabric.api_version", + fabricLoader.getModContainer("fabric").get().getMetadata() + .getVersion().getFriendlyString()); + }); + + Sentry.configureScope(scope -> { + scope.setTag("fabric.loader_version", + fabricLoader.getModContainer("fabricloader").get().getMetadata() + .getVersion().getFriendlyString()); + }); + + Sentry.configureScope(scope -> { + boolean dev = fabricLoader.isDevelopmentEnvironment(); + scope.setTag("environment", dev ? "dev" : "prod"); + }); + + Sentry.configureScope(scope -> { + + HashMap map = new HashMap<>(); + map.put("name", System.getProperty("os.name")); + scope.setContexts("os", map); + + scope.setTag("os.arch", System.getProperty("os.arch")); + }); + + Sentry.configureScope(scope -> { + + HashMap map = new HashMap<>(); + map.put("runtime", System.getProperty("java.runtime.name")); + map.put("version", System.getProperty("java.runtime.version")); + map.put("vendor", System.getProperty("java.vendor")); + map.put("vm", System.getProperty("java.vm.name") + " (" + + System.getProperty("java.vm.info") + ")"); + scope.setContexts("java", map); + + scope.setTag("java.version", System.getProperty("java.version")); + scope.setTag("java.vendor", System.getProperty("java.vendor")); + scope.setTag("java.vm", System.getProperty("java.vm.name")); + }); + + Sentry.configureScope(scope -> { + + ArrayList mods = + fabricLoader.getAllMods().stream().map(mod -> mod.getMetadata()) + .filter(mod -> !mod.getId().startsWith("fabric-")) + .collect(Collectors.toCollection(() -> new ArrayList<>())); + + HashMap map = new HashMap<>(); + for(ModMetadata mod : mods) + map.put(mod.getId(), mod.getVersion().getFriendlyString()); + scope.setContexts("mods", map); + + scope.setTag("other_mods", "" + (map.size() - 4)); + }); + } + private Path createWurstFolder() { Path dotMinecraftFolder = MC.runDirectory.toPath().normalize(); diff --git a/src/main/java/net/wurstclient/command/CmdProcessor.java b/src/main/java/net/wurstclient/command/CmdProcessor.java index 42b8214d..7eb7689f 100644 --- a/src/main/java/net/wurstclient/command/CmdProcessor.java +++ b/src/main/java/net/wurstclient/command/CmdProcessor.java @@ -9,6 +9,7 @@ package net.wurstclient.command; import java.util.Arrays; +import io.sentry.Sentry; import net.minecraft.util.crash.CrashException; import net.minecraft.util.crash.CrashReport; import net.minecraft.util.crash.CrashReportSection; @@ -75,6 +76,8 @@ public final class CmdProcessor implements ChatOutputListener private void runCmd(Command cmd, String input) { + Sentry.addBreadcrumb("." + input, "command.run"); + String[] args = input.split(" "); args = Arrays.copyOfRange(args, 1, args.length); diff --git a/src/main/java/net/wurstclient/hack/Hack.java b/src/main/java/net/wurstclient/hack/Hack.java index 26e65597..4dbd44bc 100644 --- a/src/main/java/net/wurstclient/hack/Hack.java +++ b/src/main/java/net/wurstclient/hack/Hack.java @@ -7,12 +7,16 @@ */ package net.wurstclient.hack; +import java.util.Map.Entry; import java.util.Objects; +import io.sentry.Breadcrumb; +import io.sentry.Sentry; import net.wurstclient.Category; import net.wurstclient.Feature; import net.wurstclient.hacks.NavigatorHack; import net.wurstclient.hacks.TooManyHaxHack; +import net.wurstclient.settings.Setting; public abstract class Hack extends Feature { @@ -74,6 +78,13 @@ public abstract class Hack extends Feature if(enabled && tooManyHax.isEnabled() && tooManyHax.isBlocked(this)) return; + Breadcrumb breadcrumb = new Breadcrumb(name); + breadcrumb.setCategory("hack." + (enabled ? "enable" : "disable")); + for(Entry e : getSettings().entrySet()) + breadcrumb.setData(e.getValue().getName(), + e.getValue().toJson().toString()); + Sentry.addBreadcrumb(breadcrumb); + this.enabled = enabled; if(!(this instanceof NavigatorHack)) diff --git a/src/main/java/net/wurstclient/keybinds/KeybindProcessor.java b/src/main/java/net/wurstclient/keybinds/KeybindProcessor.java index 4d3e67f1..22080218 100644 --- a/src/main/java/net/wurstclient/keybinds/KeybindProcessor.java +++ b/src/main/java/net/wurstclient/keybinds/KeybindProcessor.java @@ -9,6 +9,8 @@ package net.wurstclient.keybinds; import org.lwjgl.glfw.GLFW; +import io.sentry.Breadcrumb; +import io.sentry.Sentry; import net.minecraft.client.gui.screen.Screen; import net.minecraft.client.util.InputUtil; import net.wurstclient.WurstClient; @@ -44,7 +46,16 @@ public final class KeybindProcessor implements KeyPressListener return; String keyName = getKeyName(event); + String cmds = keybinds.getCommands(keyName); + if(cmds == null) + return; + + Breadcrumb breadcrumb = new Breadcrumb(cmds); + breadcrumb.setCategory("keybind.trigger"); + breadcrumb.setData("key", keyName); + Sentry.addBreadcrumb(breadcrumb); + processCmds(cmds); } @@ -57,10 +68,8 @@ public final class KeybindProcessor implements KeyPressListener private void processCmds(String cmds) { - if(cmds == null) - return; - cmds = cmds.replace(";", "\u00a7").replace("\u00a7\u00a7", ";"); + for(String cmd : cmds.split("\u00a7")) processCmd(cmd.trim()); } diff --git a/src/main/java/net/wurstclient/mixin/MinecraftClientMixin.java b/src/main/java/net/wurstclient/mixin/MinecraftClientMixin.java index f088ba30..82728520 100644 --- a/src/main/java/net/wurstclient/mixin/MinecraftClientMixin.java +++ b/src/main/java/net/wurstclient/mixin/MinecraftClientMixin.java @@ -7,6 +7,12 @@ */ package net.wurstclient.mixin; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.stream.Collectors; + +import org.jetbrains.annotations.Nullable; import org.objectweb.asm.Opcodes; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; @@ -16,22 +22,32 @@ import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import com.mojang.blaze3d.platform.GlDebugInfo; + +import io.sentry.Breadcrumb; +import io.sentry.Sentry; import net.minecraft.client.MinecraftClient; import net.minecraft.client.WindowEventHandler; +import net.minecraft.client.gui.screen.Screen; import net.minecraft.client.network.ClientPlayerEntity; import net.minecraft.client.network.ClientPlayerInteractionManager; import net.minecraft.client.util.Session; +import net.minecraft.client.util.Window; import net.minecraft.entity.Entity; +import net.minecraft.util.crash.CrashReport; import net.minecraft.util.hit.EntityHitResult; import net.minecraft.util.hit.HitResult; import net.minecraft.util.snooper.SnooperListener; import net.minecraft.util.thread.ReentrantThreadExecutor; +import net.wurstclient.Feature; import net.wurstclient.WurstClient; import net.wurstclient.events.LeftClickListener.LeftClickEvent; import net.wurstclient.events.RightClickListener.RightClickEvent; +import net.wurstclient.hack.Hack; import net.wurstclient.mixinterface.IClientPlayerEntity; import net.wurstclient.mixinterface.IClientPlayerInteractionManager; import net.wurstclient.mixinterface.IMinecraftClient; +import net.wurstclient.settings.Setting; @Mixin(MinecraftClient.class) public abstract class MinecraftClientMixin @@ -117,6 +133,106 @@ public abstract class MinecraftClientMixin return session; } + @Inject(at = {@At("HEAD")}, + method = { + "addDetailsToCrashReport(Lnet/minecraft/util/crash/CrashReport;)Lnet/minecraft/util/crash/CrashReport;"}) + private void onAddDetailsToCrashReport(CrashReport report, + CallbackInfoReturnable cir) + { + Sentry.configureScope(scope -> { + HashMap map = new HashMap<>(); + map.put("name", GlDebugInfo.getCpuInfo()); + scope.setContexts("cpu", map); + }); + + Sentry.configureScope(scope -> { + + HashMap map = new HashMap<>(); + + map.put("name", GlDebugInfo.getRenderer()); + map.put("version", GlDebugInfo.getVersion()); + map.put("vendor_name", GlDebugInfo.getVendor()); + + Window window = WurstClient.MC.getWindow(); + map.put("framebuffer", window.getFramebufferWidth() + "x" + + window.getFramebufferHeight()); + + scope.setContexts("gpu", map); + }); + + Sentry.configureScope(scope -> { + + scope.setTag("mc.lang", + WurstClient.MC.getLanguageManager().getLanguage().getCode()); + + scope.setTag("mc.font", + WurstClient.MC.forcesUnicodeFont() ? "unicode" : "default"); + + Screen cs = WurstClient.MC.currentScreen; + String screen = + cs == null ? "none" : cs.getClass().getCanonicalName(); + scope.setTag("mc.screen", screen); + }); + + Sentry.configureScope(scope -> { + + HashMap map = new HashMap<>(); + + ArrayList enabledHax = WurstClient.INSTANCE.getHax() + .getAllHax().stream().filter(Hack::isEnabled).map(Hack::getName) + .collect(Collectors.toCollection(() -> new ArrayList<>())); + + map.put("enabled_hacks", enabledHax); + + ArrayList features = new ArrayList<>(); + features.addAll(WurstClient.INSTANCE.getHax().getAllHax()); + features.addAll(WurstClient.INSTANCE.getCmds().getAllCmds()); + features.addAll(WurstClient.INSTANCE.getOtfs().getAllOtfs()); + + HashMap> map2 = new HashMap<>(); + for(Feature feature : features) + { + Collection settings = feature.getSettings().values(); + if(settings.isEmpty()) + continue; + + HashMap map3 = new HashMap<>(); + for(Setting setting : settings) + map3.put(setting.getName(), setting.toJson().toString()); + map2.put(feature.getName(), map3); + } + map.put("settings", map2); + + scope.setContexts("wurst", map); + }); + } + + @Inject(at = {@At("HEAD")}, + method = {"printCrashReport(Lnet/minecraft/util/crash/CrashReport;)V"}) + private static void onPrintCrashReport(CrashReport report, CallbackInfo ci) + { + Sentry.captureException(report.getCause()); + } + + @Inject(at = {@At("HEAD")}, + method = {"openScreen(Lnet/minecraft/client/gui/screen/Screen;)V"}) + private void onOpenScreen(@Nullable Screen screen, CallbackInfo ci) + { + Breadcrumb breadcrumb = new Breadcrumb(); + breadcrumb.setType("navigation"); + breadcrumb.setCategory("screen.change"); + + Screen cs = WurstClient.MC.currentScreen; + String from = cs == null ? "none" : cs.getClass().getCanonicalName(); + breadcrumb.setData("from", from); + + String to = + screen == null ? "none" : screen.getClass().getCanonicalName(); + breadcrumb.setData("to", to); + + Sentry.addBreadcrumb(breadcrumb); + } + @Override public void rightClick() {