diff --git a/build.gradle.kts b/build.gradle.kts index 713909d..2f29b56 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -15,7 +15,7 @@ plugins { toolkitLoomHelper { // Adds OneConfig to our project - useOneConfig(mcData, "commands", "config", "config-impl", "events", "internal", "ui") + useOneConfig(mcData, "commands", "config", "config-impl", "events", "internal", "ui", "utils") // Removes the server configs from IntelliJ IDEA, leaving only client runs. // If you're developing a server-side mod, you can remove this line. diff --git a/src/main/java/org/polyfrost/crashpatch/mixin/MixinCrashReport.java b/src/main/java/org/polyfrost/crashpatch/mixin/MixinCrashReport.java index 9335159..3b88a91 100644 --- a/src/main/java/org/polyfrost/crashpatch/mixin/MixinCrashReport.java +++ b/src/main/java/org/polyfrost/crashpatch/mixin/MixinCrashReport.java @@ -6,11 +6,12 @@ package org.polyfrost.crashpatch.mixin; -import org.polyfrost.crashpatch.crashes.ModIdentifier; +import org.polyfrost.crashpatch.identifier.ModIdentifier; import org.polyfrost.crashpatch.hooks.CrashReportHook; import org.polyfrost.crashpatch.hooks.StacktraceDeobfuscator; import net.minecraft.crash.CrashReport; import net.minecraftforge.fml.common.ModContainer; +import org.polyfrost.crashpatch.identifier.ModMetadata; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; @@ -32,7 +33,7 @@ public String getSuspectedCrashPatchMods() { @Inject(method = "populateEnvironment", at = @At("TAIL")) private void afterPopulateEnvironment(CallbackInfo ci) { - ModContainer susMod = ModIdentifier.INSTANCE.identifyFromStacktrace(cause); + ModMetadata susMod = ModIdentifier.INSTANCE.identifyFromStacktrace(cause); crashpatch$suspectedMod = (susMod == null ? "Unknown" : susMod.getName()); } diff --git a/src/main/java/org/polyfrost/crashpatch/mixin/MixinGuiConnecting.java b/src/main/java/org/polyfrost/crashpatch/mixin/MixinGuiConnecting.java index 19b1291..2102bca 100644 --- a/src/main/java/org/polyfrost/crashpatch/mixin/MixinGuiConnecting.java +++ b/src/main/java/org/polyfrost/crashpatch/mixin/MixinGuiConnecting.java @@ -41,10 +41,11 @@ protected void mouseClicked(int mouseX, int mouseY, int mouseButton) throws IOEx String[] list = crashpatch$wrapFormattedStringToWidth(crashpatch$getText(), width).split("\n"); int width = -1; for (String text : list) { - width = Math.max(width, fontRendererObj.getStringWidth(text)); + width = Math.max(width, this.fontRendererObj.getStringWidth(text)); } + int left = (this.width / 2) - width / 2; - if ((width == -1 || (left < mouseX && left + width > mouseX)) && (mouseY > 5 && mouseY < 15 + ((list.length - 1) * (fontRendererObj.FONT_HEIGHT + 2)))) { + if ((width == -1 || (left < mouseX && left + width > mouseX)) && (mouseY > 5 && mouseY < 15 + ((list.length - 1) * (this.fontRendererObj.FONT_HEIGHT + 2)))) { UDesktop.browse(URI.create("https://discord.gg/eh7tNFezct")); } } @@ -54,8 +55,8 @@ protected void mouseClicked(int mouseX, int mouseY, int mouseButton) throws IOEx @Unique public void crashpatch$drawSplitCenteredString(String text, int x, int y, int color) { for (String line : crashpatch$wrapFormattedStringToWidth(text, width).split("\n")) { - drawCenteredString(fontRendererObj, line, x, y, color); - y += fontRendererObj.FONT_HEIGHT + 2; + drawCenteredString(this.fontRendererObj, line, x, y, color); + y += this.fontRendererObj.FONT_HEIGHT + 2; } } @@ -91,7 +92,7 @@ protected void mouseClicked(int mouseX, int mouseY, int mouseButton) throws IOEx case ' ': l = k; default: - j += fontRendererObj.getCharWidth(c0); + j += this.fontRendererObj.getCharWidth(c0); if (flag) { ++j; diff --git a/src/main/java/org/polyfrost/crashpatch/mixin/MixinGuiDupesFound.java b/src/main/java/org/polyfrost/crashpatch/mixin/MixinGuiDupesFound.java index 68de486..0b09055 100644 --- a/src/main/java/org/polyfrost/crashpatch/mixin/MixinGuiDupesFound.java +++ b/src/main/java/org/polyfrost/crashpatch/mixin/MixinGuiDupesFound.java @@ -1,5 +1,6 @@ package org.polyfrost.crashpatch.mixin; +import org.polyfrost.crashpatch.CrashPatch; import org.polyfrost.universal.UDesktop; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.GuiButton; @@ -9,9 +10,9 @@ import net.minecraftforge.fml.common.DuplicateModsFoundException; import net.minecraftforge.fml.common.FMLCommonHandler; import net.minecraftforge.fml.common.ModContainer; -import org.polyfrost.crashpatch.CrashPatchOldKt; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; @@ -40,7 +41,7 @@ private void onInit(CallbackInfo ci) { protected void actionPerformed(GuiButton button) { switch (button.id) { case 0: - UDesktop.open(new File(CrashPatchOldKt.getMcDir(), "mods")); + UDesktop.open(new File(CrashPatch.getMcDir(), "mods")); break; case 1: FMLCommonHandler.instance().exitJava(0, false); @@ -53,16 +54,16 @@ private void onDrawScreen(int mouseX, int mouseY, float partialTicks, CallbackIn ci.cancel(); drawDefaultBackground(); int offset = 10; - offset += drawSplitString("There are duplicate mods in your mod folder!", width / 2, offset, width, Color.RED.getRGB()); + offset += crashpatch$drawSplitString("There are duplicate mods in your mod folder!", width / 2, offset, width, Color.RED.getRGB()); for (Map.Entry modContainerFileEntry : dupes.dupes.entries()) { offset += 10; - offset += drawSplitString(String.format("%s : %s", modContainerFileEntry.getKey().getModId(), modContainerFileEntry.getValue().getName()), width / 2, offset, width, Color.YELLOW.getRGB()); + offset += crashpatch$drawSplitString(String.format("%s : %s", modContainerFileEntry.getKey().getModId(), modContainerFileEntry.getValue().getName()), width / 2, offset, width, Color.YELLOW.getRGB()); } offset += 10; - drawSplitString(EnumChatFormatting.BOLD + "To fix this, go into your mods folder by clicking the button below or going to " + CrashPatchOldKt.getMcDir().getAbsolutePath() + " and deleting the duplicate mods.", width / 2, offset, width, Color.BLUE.getRGB()); + crashpatch$drawSplitString(EnumChatFormatting.BOLD + "To fix this, go into your mods folder by clicking the button below or going to " + CrashPatch.getMcDir().getAbsolutePath() + " and deleting the duplicate mods.", width / 2, offset, width, Color.BLUE.getRGB()); for (GuiButton guiButton : this.buttonList) { guiButton.drawButton(this.mc, mouseX, mouseY); @@ -72,8 +73,9 @@ private void onDrawScreen(int mouseX, int mouseY, float partialTicks, CallbackIn } } - private static int drawSplitString(String str, int x, int y, int wrapWidth, int textColor) { - str = trimStringNewline(str); + @Unique + private static int crashpatch$drawSplitString(String str, int x, int y, int wrapWidth, int textColor) { + str = crashpatch$trimStringNewline(str); int y2 = y; for (String s : Minecraft.getMinecraft().fontRendererObj.listFormattedStringToWidth(str, wrapWidth)) { Minecraft.getMinecraft().fontRendererObj.drawStringWithShadow(s, (float) (x - Minecraft.getMinecraft().fontRendererObj.getStringWidth(s) / 2), (float) y2, textColor); @@ -82,10 +84,12 @@ private static int drawSplitString(String str, int x, int y, int wrapWidth, int return y2 - y; } - private static String trimStringNewline(String text) { + @Unique + private static String crashpatch$trimStringNewline(String text) { while (text != null && text.endsWith("\n")) { text = text.substring(0, text.length() - 1); } return text; } + } diff --git a/src/main/java/org/polyfrost/crashpatch/mixin/MixinMinecraft.java b/src/main/java/org/polyfrost/crashpatch/mixin/MixinMinecraft.java index f768fdf..84427e4 100644 --- a/src/main/java/org/polyfrost/crashpatch/mixin/MixinMinecraft.java +++ b/src/main/java/org/polyfrost/crashpatch/mixin/MixinMinecraft.java @@ -33,7 +33,7 @@ import org.lwjgl.opengl.GL12; import org.lwjgl.opengl.GL14; import org.polyfrost.crashpatch.CrashPatch; -import org.polyfrost.crashpatch.config.CrashPatchConfig; +import org.polyfrost.crashpatch.CrashPatchConfig; import org.polyfrost.crashpatch.crashes.StateManager; import org.polyfrost.crashpatch.gui.CrashUI; import org.polyfrost.crashpatch.hooks.MinecraftHook; @@ -52,6 +52,7 @@ import java.util.Queue; import java.util.concurrent.FutureTask; +@SuppressWarnings("AccessStaticViaInstance") @Mixin(value = Minecraft.class, priority = -9000) public abstract class MixinMinecraft implements MinecraftHook { @@ -161,7 +162,7 @@ public void run(CallbackInfo ci) { } try { while (running) { - if (!hasCrashed || crashReporter == null) { + if (!this.hasCrashed || this.crashReporter == null) { try { if (CrashPatch.INSTANCE.getRequestedCrash()) { CrashPatch.INSTANCE.setRequestedCrash(false); @@ -174,7 +175,7 @@ public void run(CallbackInfo ci) { addGraphicsAndWorldToCrashReport(e.getCrashReport()); crashpatch$addInfoToCrash(e.getCrashReport()); crashpatch$resetGameState(); - logger.fatal("Reported exception thrown!", e); + this.logger.fatal("Reported exception thrown!", e); crashpatch$displayCrashScreen(e.getCrashReport()); } catch (Throwable e) { crashpatch$clientCrashCount++; @@ -182,16 +183,16 @@ public void run(CallbackInfo ci) { addGraphicsAndWorldToCrashReport(report); crashpatch$addInfoToCrash(report); crashpatch$resetGameState(); - logger.fatal("Unreported exception thrown!", e); + this.logger.fatal("Unreported exception thrown!", e); crashpatch$displayCrashScreen(report); } } else { crashpatch$serverCrashCount++; - crashpatch$addInfoToCrash(crashReporter); + crashpatch$addInfoToCrash(this.crashReporter); freeMemory(); - crashpatch$displayCrashScreen(crashReporter); - hasCrashed = false; - crashReporter = null; + crashpatch$displayCrashScreen(this.crashReporter); + this.hasCrashed = false; + this.crashReporter = null; } } } catch (MinecraftError ignored) { @@ -213,7 +214,7 @@ private void onGUIDisplay(GuiScreen i, CallbackInfo ci) { crashpatch$letDie = true; } if ((crashpatch$clientCrashCount >= CrashPatchConfig.INSTANCE.getCrashLimit() || crashpatch$serverCrashCount >= CrashPatchConfig.INSTANCE.getCrashLimit())) { - logger.error("Crash limit reached, exiting game"); + this.logger.error("Crash limit reached, exiting game"); crashpatch$letDie = true; } displayCrashReport(report); @@ -221,19 +222,19 @@ private void onGUIDisplay(GuiScreen i, CallbackInfo ci) { try { // Reset hasCrashed, debugCrashKeyPressTime, and crashIntegratedServerNextTick - hasCrashed = false; - debugCrashKeyPressTime = -1; + this.hasCrashed = false; + this.debugCrashKeyPressTime = -1; // Vanilla does this when switching to main menu but not our custom crash screen // nor the out of memory screen (see https://bugs.mojang.com/browse/MC-128953) - gameSettings.showDebugInfo = false; + this.gameSettings.showDebugInfo = false; // Display the crash screen // crashpatch$runGUILoop(new GuiCrashScreen(report)); displayGuiScreen(new CrashUI(report).create()); } catch (Throwable t) { // The crash screen has crashed. Report it normally instead. - logger.error("An uncaught exception occured while displaying the crash screen, making normal report instead", t); + this.logger.error("An uncaught exception occured while displaying the crash screen, making normal report instead", t); displayCrashReport(report); System.exit(report.getFile() != null ? -1 : -2); } @@ -252,9 +253,9 @@ private void onGUIDisplay(GuiScreen i, CallbackInfo ci) { // Free up memory such that this works properly in case of an OutOfMemoryError int originalMemoryReserveSize = -1; try { // In case another mod actually deletes the memoryReserve field - if (memoryReserve != null) { - originalMemoryReserveSize = memoryReserve.length; - memoryReserve = new byte[0]; + if (this.memoryReserve != null) { + originalMemoryReserveSize = this.memoryReserve.length; + this.memoryReserve = new byte[0]; } } catch (Throwable ignored) { } @@ -262,31 +263,31 @@ private void onGUIDisplay(GuiScreen i, CallbackInfo ci) { StateManager.INSTANCE.resetStates(); if (crashpatch$clientCrashCount >= CrashPatchConfig.INSTANCE.getLeaveLimit() || crashpatch$serverCrashCount >= CrashPatchConfig.INSTANCE.getLeaveLimit()) { - logger.error("Crash limit reached, exiting world"); + this.logger.error("Crash limit reached, exiting world"); CrashUI.Companion.setLeaveWorldCrash(true); if (getNetHandler() != null) { getNetHandler().getNetworkManager().closeChannel(new ChatComponentText("[CrashPatch] Client crashed")); } loadWorld(null); - if (entityRenderer.isShaderActive()) { - entityRenderer.stopUseShader(); + if (this.entityRenderer.isShaderActive()) { + this.entityRenderer.stopUseShader(); } - scheduledTasks.clear(); // TODO: Figure out why this isn't necessary for vanilla disconnect + this.scheduledTasks.clear(); // TODO: Figure out why this isn't necessary for vanilla disconnect } crashpatch$resetState(); if (originalMemoryReserveSize != -1) { try { - memoryReserve = new byte[originalMemoryReserveSize]; + this.memoryReserve = new byte[originalMemoryReserveSize]; } catch (Throwable ignored) { } } System.gc(); } catch (Throwable t) { - logger.error("Failed to reset state after a crash", t); + this.logger.error("Failed to reset state after a crash", t); try { StateManager.INSTANCE.resetStates(); crashpatch$resetState(); @@ -303,19 +304,19 @@ private void onGUIDisplay(GuiScreen i, CallbackInfo ci) { } displayCrashReport(report); try { - mcResourceManager = new SimpleReloadableResourceManager(metadataSerializer_); - renderEngine = new TextureManager(mcResourceManager); - mcResourceManager.registerReloadListener(renderEngine); + this.mcResourceManager = new SimpleReloadableResourceManager(this.metadataSerializer_); + this.renderEngine = new TextureManager(this.mcResourceManager); + this.mcResourceManager.registerReloadListener(this.renderEngine); - mcLanguageManager = new LanguageManager(metadataSerializer_, gameSettings.language); - mcResourceManager.registerReloadListener(mcLanguageManager); + this.mcLanguageManager = new LanguageManager(this.metadataSerializer_, this.gameSettings.language); + this.mcResourceManager.registerReloadListener(this.mcLanguageManager); refreshResources(); // TODO: Why is this necessary? - fontRendererObj = new FontRenderer(gameSettings, new ResourceLocation("textures/font/ascii.png"), renderEngine, false); - mcResourceManager.registerReloadListener(fontRendererObj); + this.fontRendererObj = new FontRenderer(this.gameSettings, new ResourceLocation("textures/font/ascii.png"), this.renderEngine, false); + this.mcResourceManager.registerReloadListener(this.fontRendererObj); - mcSoundHandler = new SoundHandler(mcResourceManager, gameSettings); - mcResourceManager.registerReloadListener(mcSoundHandler); + this.mcSoundHandler = new SoundHandler(this.mcResourceManager, this.gameSettings); + this.mcResourceManager.registerReloadListener(this.mcSoundHandler); //try { // this is necessary for some GUI stuff. if it works, cool, if not, it's not a big deal // //EventManager.INSTANCE.register(Notifications.INSTANCE); @@ -325,7 +326,7 @@ private void onGUIDisplay(GuiScreen i, CallbackInfo ci) { //} //todo do we need a polyui equivalent - running = true; + this.running = true; try { //noinspection deprecation SplashProgress.pause();// Disable the forge splash progress screen @@ -336,7 +337,7 @@ private void onGUIDisplay(GuiScreen i, CallbackInfo ci) { crashpatch$runGUILoop(new CrashUI(report, CrashUI.GuiType.INIT)); } catch (Throwable t) { if (!crashpatch$letDie) { - logger.error("An uncaught exception occured while displaying the init error screen, making normal report instead", t); + this.logger.error("An uncaught exception occured while displaying the init error screen, making normal report instead", t); crashpatch$letDie = true; } displayCrashReport(report); @@ -349,21 +350,21 @@ private void onGUIDisplay(GuiScreen i, CallbackInfo ci) { private void crashpatch$runGUILoop(CrashUI crashUI) throws Throwable { GuiScreen screen = crashUI.create(); displayGuiScreen(screen); - while (running && currentScreen != null) { + while (this.running && this.currentScreen != null) { if (Display.isCreated() && Display.isCloseRequested()) { System.exit(0); } //EventManager.INSTANCE.post(new RenderEvent.Start()); todo - leftClickCounter = 10000; - currentScreen.handleInput(); - currentScreen.updateScreen(); + this.leftClickCounter = 10000; + this.currentScreen.handleInput(); + this.currentScreen.updateScreen(); GlStateManager.pushMatrix(); GlStateManager.clear(16640); - framebufferMc.bindFramebuffer(true); + this.framebufferMc.bindFramebuffer(true); GlStateManager.enableTexture2D(); - GlStateManager.viewport(0, 0, displayWidth, displayHeight); + GlStateManager.viewport(0, 0, this.displayWidth, this.displayHeight); ScaledResolution scaledResolution = new ScaledResolution(((Minecraft) (Object) this)); GlStateManager.clear(256); @@ -377,20 +378,20 @@ private void onGUIDisplay(GuiScreen i, CallbackInfo ci) { int width = scaledResolution.getScaledWidth(); int height = scaledResolution.getScaledHeight(); - int mouseX = Mouse.getX() * width / displayWidth; - int mouseY = height - Mouse.getY() * height / displayHeight - 1; + int mouseX = Mouse.getX() * width / this.displayWidth; + int mouseY = height - Mouse.getY() * height / this.displayHeight - 1; Gui.drawRect(0, 0, width, height, Color.WHITE.getRGB()); // DO NOT REMOVE THIS! FOR SOME REASON NANOVG DOESN'T RENDER WITHOUT IT - currentScreen.drawScreen(mouseX, mouseY, 0); + this.currentScreen.drawScreen(mouseX, mouseY, 0); if (crashUI.getShouldCrash()) { crashpatch$letDie = true; throw Objects.requireNonNull(crashUI.getThrowable()); } - framebufferMc.unbindFramebuffer(); + this.framebufferMc.unbindFramebuffer(); GlStateManager.popMatrix(); GlStateManager.pushMatrix(); - framebufferMc.framebufferRender(displayWidth, displayHeight); + this.framebufferMc.framebufferRender(this.displayWidth, this.displayHeight); GlStateManager.popMatrix(); //EventManager.INSTANCE.post(new RenderEvent(Stage.END, 0)); todo diff --git a/src/main/java/org/polyfrost/crashpatch/mixin/MixinTileEntityRendererDispatcher.java b/src/main/java/org/polyfrost/crashpatch/mixin/MixinTileEntityRendererDispatcher.java index a2f91a0..df04c12 100644 --- a/src/main/java/org/polyfrost/crashpatch/mixin/MixinTileEntityRendererDispatcher.java +++ b/src/main/java/org/polyfrost/crashpatch/mixin/MixinTileEntityRendererDispatcher.java @@ -5,6 +5,7 @@ import net.minecraft.client.renderer.tileentity.TileEntityRendererDispatcher; import net.minecraft.tileentity.TileEntity; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Redirect; @@ -15,7 +16,9 @@ //TODO: this could be completely useless, check with smarter people @Mixin(TileEntityRendererDispatcher.class) public abstract class MixinTileEntityRendererDispatcher implements StateManager.IResettable { - private boolean drawingBatch = false; + + @Unique + private boolean crashpatch$drawingBatch = false; @Inject(method = "", at = @At(value = "RETURN")) public void onInit(CallbackInfo ci) { @@ -24,12 +27,12 @@ public void onInit(CallbackInfo ci) { @Override public void resetState() { - if (drawingBatch) drawingBatch = false; + if (crashpatch$drawingBatch) crashpatch$drawingBatch = false; } @Redirect(method = "renderTileEntity", at = @At(value = "INVOKE", target = "Lnet/minecraft/tileentity/TileEntity;hasFastRenderer()Z")) private boolean isNotFastRenderOrDrawing(TileEntity instance) { - if (!drawingBatch) { + if (!crashpatch$drawingBatch) { return false; } else { return instance.hasFastRenderer(); @@ -38,17 +41,18 @@ private boolean isNotFastRenderOrDrawing(TileEntity instance) { @Redirect(method = "renderTileEntityAt(Lnet/minecraft/tileentity/TileEntity;DDDFI)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/tileentity/TileEntity;hasFastRenderer()Z")) private boolean isFastRenderOrDrawing(TileEntity instance) { - return drawingBatch && instance.hasFastRenderer(); + return crashpatch$drawingBatch && instance.hasFastRenderer(); } @Inject(method = "preDrawBatch", at = @At("TAIL"), remap = false) private void setDrawingBatchTrue(CallbackInfo ci) { - drawingBatch = true; + crashpatch$drawingBatch = true; } @Inject(method = "drawBatch", at = @At("TAIL"), remap = false) private void setDrawingBatchFalse(int pass, CallbackInfo ci) { - drawingBatch = false; + crashpatch$drawingBatch = false; } + } //#endif diff --git a/src/main/java/org/polyfrost/crashpatch/mixin/MixinWorldRenderer.java b/src/main/java/org/polyfrost/crashpatch/mixin/MixinWorldRenderer.java index f0f363d..654a3cb 100644 --- a/src/main/java/org/polyfrost/crashpatch/mixin/MixinWorldRenderer.java +++ b/src/main/java/org/polyfrost/crashpatch/mixin/MixinWorldRenderer.java @@ -24,6 +24,8 @@ private void onInitEnd(int bufferSizeIn, CallbackInfo ci) { @Override public void resetState() { - if (isDrawing) finishDrawing(); + if (this.isDrawing) { + finishDrawing(); + } } } diff --git a/src/main/kotlin/org/polyfrost/crashpatch/CrashPatch.kt b/src/main/kotlin/org/polyfrost/crashpatch/CrashPatch.kt index 364089d..3801e49 100644 --- a/src/main/kotlin/org/polyfrost/crashpatch/CrashPatch.kt +++ b/src/main/kotlin/org/polyfrost/crashpatch/CrashPatch.kt @@ -4,12 +4,12 @@ package org.polyfrost.crashpatch import net.minecraftforge.fml.common.Mod import net.minecraftforge.fml.common.event.FMLInitializationEvent import net.minecraftforge.fml.common.event.FMLPreInitializationEvent -import org.apache.logging.log4j.LogManager //#else //$$ import net.fabricmc.api.ClientModInitializer //#endif import java.io.File +import org.apache.logging.log4j.LogManager import org.polyfrost.crashpatch.crashes.CrashScanStorage import org.polyfrost.crashpatch.crashes.DeobfuscatingRewritePolicy import org.polyfrost.oneconfig.api.commands.v1.CommandManager @@ -30,10 +30,12 @@ object CrashPatch private val logger = LogManager.getLogger() + @JvmStatic val mcDir by lazy(LazyThreadSafetyMode.PUBLICATION) { File(System.getProperty("user.dir")) } + @JvmStatic val gameDir by lazy(LazyThreadSafetyMode.PUBLICATION) { try { if (mcDir.parentFile?.name?.let { name -> diff --git a/src/main/kotlin/org/polyfrost/crashpatch/CrashPatchCommand.kt b/src/main/kotlin/org/polyfrost/crashpatch/CrashPatchCommand.kt index ccd0f0a..db6b9a1 100644 --- a/src/main/kotlin/org/polyfrost/crashpatch/CrashPatchCommand.kt +++ b/src/main/kotlin/org/polyfrost/crashpatch/CrashPatchCommand.kt @@ -3,9 +3,9 @@ package org.polyfrost.crashpatch import net.minecraft.util.ChatComponentText import org.polyfrost.crashpatch.crashes.CrashScanStorage import org.polyfrost.oneconfig.api.commands.v1.factories.annotated.Command +import org.polyfrost.oneconfig.utils.v1.dsl.openUI import org.polyfrost.universal.ChatColor import org.polyfrost.universal.UMinecraft -import org.polyfrost.utils.v1.dsl.openUI @Command(CrashPatch.ID) object CrashPatchCommand { @@ -18,7 +18,6 @@ object CrashPatchCommand { @Command fun reload() { if (CrashScanStorage.downloadJson()) { - CrashScanStorage.simpleCache.clear() UMinecraft.getMinecraft().thePlayer.addChatMessage(ChatComponentText("${ChatColor.RED}[CrashPatch] Successfully reloaded JSON file!")) } else { UMinecraft.getMinecraft().thePlayer.addChatMessage(ChatComponentText("${ChatColor.RED}[CrashPatch] Failed to reload the JSON file!")) diff --git a/src/main/kotlin/org/polyfrost/crashpatch/crashes/ModIdentifier.kt b/src/main/kotlin/org/polyfrost/crashpatch/crashes/ModIdentifier.kt deleted file mode 100644 index e37172b..0000000 --- a/src/main/kotlin/org/polyfrost/crashpatch/crashes/ModIdentifier.kt +++ /dev/null @@ -1,89 +0,0 @@ -package org.polyfrost.crashpatch.crashes - -import org.polyfrost.crashpatch.logger -import net.minecraft.launchwrapper.Launch -import net.minecraft.launchwrapper.LaunchClassLoader -import net.minecraftforge.fml.common.Loader -import net.minecraftforge.fml.common.ModContainer -import java.io.File -import java.io.IOException -import java.net.URISyntaxException -import java.net.URL - -object ModIdentifier { - - fun identifyFromStacktrace(e: Throwable?): ModContainer? { - val modMap = makeModMap() - - // Get the set of classes - val classes = LinkedHashSet() - e?.stackTrace?.forEachIndexed { index, stackTraceElement -> - if (index < 4) { // everything after the first 3 lines are basically useless and only leads to false detections - classes.add(stackTraceElement.className) - } - } - val mods = LinkedHashSet() - for (className in classes) { - val classMods = identifyFromClass(className, modMap) - if (classMods.isNotEmpty()) { - mods.addAll(classMods) - } - } - return mods.firstOrNull() - } - - private fun identifyFromClass(className: String, modMap: Map>): Set { - // Skip identification for Mixin, one's mod copy of the library is shared with all other mods - if (className.startsWith("org.spongepowered.asm.mixin.")) return emptySet() - - // Get the URL of the class - val untrasformedName = untransformName(Launch.classLoader, className) - var url = Launch.classLoader.getResource(untrasformedName.replace('.', '/') + ".class") - logger.debug("$className = $untrasformedName = $url") - if (url == null) { - logger.warn("Failed to identify $className (untransformed name: $untrasformedName)") - return emptySet() - } - - // Get the mod containing that class - return try { - if (url.protocol == "jar") url = URL(url.file.substring(0, url.file.indexOf('!'))) - modMap[File(url.toURI()).canonicalFile] ?: emptySet() - } catch (e: URISyntaxException) { - throw RuntimeException(e) - } catch (e: IOException) { - throw RuntimeException(e) - } - } - - private fun makeModMap(): Map> { - val modMap = HashMap>() - for (mod in Loader.instance().modList) { - val currentMods = modMap.getOrDefault(mod.source, HashSet()) - currentMods.add(mod) - try { - modMap[mod.source.canonicalFile] = currentMods - } catch (e: IOException) { - throw RuntimeException(e) - } - } - try { - modMap.remove(Loader.instance().minecraftModContainer.source) // Ignore minecraft jar (minecraft) - modMap.remove(Loader.instance().indexedModList["FML"]!!.source) // Ignore forge jar (FML, forge) - } catch (ignored: NullPointerException) { - // Workaround for https://github.com/MinecraftForge/MinecraftForge/issues/4919 - } - return modMap - } - - private fun untransformName(launchClassLoader: LaunchClassLoader, className: String): String { - return try { - val untransformNameMethod = - LaunchClassLoader::class.java.getDeclaredMethod("untransformName", String::class.java) - untransformNameMethod.isAccessible = true - untransformNameMethod.invoke(launchClassLoader, className) as String - } catch (e: ReflectiveOperationException) { - throw RuntimeException(e) - } - } -} diff --git a/src/main/kotlin/org/polyfrost/crashpatch/identifier/ModIdentifier.kt b/src/main/kotlin/org/polyfrost/crashpatch/identifier/ModIdentifier.kt new file mode 100644 index 0000000..cee2f73 --- /dev/null +++ b/src/main/kotlin/org/polyfrost/crashpatch/identifier/ModIdentifier.kt @@ -0,0 +1,192 @@ +package org.polyfrost.crashpatch.identifier + +//#if FORGE +import java.io.IOException +import java.net.URISyntaxException +import java.net.URL +import net.minecraft.launchwrapper.Launch +import net.minecraft.launchwrapper.LaunchClassLoader +import net.minecraftforge.fml.common.Loader +//#else +//$$ import net.fabricmc.loader.api.FabricLoader +//$$ import org.jetbrains.annotations.Nullable +//$$ import org.spongepowered.asm.mixin.extensibility.IMixinInfo +//$$ import org.spongepowered.asm.mixin.transformer.ClassInfo +//$$ import java.lang.reflect.Field +//$$ import java.nio.file.Path +//$$ import java.nio.file.Paths +//#endif + +import java.io.File +import org.apache.logging.log4j.LogManager + +typealias ModMap = Map> + +object ModIdentifier { + + private val logger = LogManager.getLogger() + + fun identifyFromStacktrace(e: Throwable?): ModMetadata? { + val modMap = makeModMap() + + // Get the set of classes + val classes = LinkedHashSet() + e?.stackTrace?.forEachIndexed { index, stackTraceElement -> + if (index < 4) { // everything after the first 3 lines are basically useless and only leads to false detections + classes.add(stackTraceElement.className) + } + } + + val mods = LinkedHashSet() + for (className in classes) { + val classMods = identifyFromClass(className, modMap) + if (classMods.isNotEmpty()) { + mods.addAll(classMods) + } + } + + return mods.firstOrNull() + } + + private fun identifyFromClass(className: String, modMap: ModMap): Set { + // Skip identification for Mixin, one's mod copy of the library is shared with all other mods + if (className.startsWith("org.spongepowered.asm.mixin.")) { + return emptySet() + } + + //#if FORGE + // Get the URL of the class + val untrasformedName = untransformName(Launch.classLoader, className) + var url = Launch.classLoader.getResource(untrasformedName.replace('.', '/') + ".class") + logger.debug("{} = {} = {}", className, untrasformedName, url) + if (url == null) { + logger.warn("Failed to identify $className (untransformed name: $untrasformedName)") + return emptySet() + } + + // Get the mod containing that class + return try { + if (url.protocol == "jar") url = URL(url.file.substring(0, url.file.indexOf('!'))) + modMap[File(url.toURI()).canonicalFile] ?: emptySet() + } catch (e: URISyntaxException) { + throw RuntimeException(e) + } catch (e: IOException) { + throw RuntimeException(e) + } + //#else + //$$ try { + //$$ val clz = Class.forName(className) + //$$ val codeSource = clz.protectionDomain.codeSource + //$$ if (codeSource == null) { + //$$ logger.debug("Failed to identify $className because of a null code source") + //$$ return emptySet() + //$$ } + //$$ + //$$ val url = codeSource.location + //$$ if (url == null) { + //$$ logger.debug("Failed to identify $className because of a null URL") + //$$ return emptySet() + //$$ } + //$$ + //$$ return getModsAt(Paths.get(url.toURI()), modMap) + //$$ } catch (e: Exception) { + //$$ logger.debug("Ignoring class $className for identification because of an error", e) + //$$ return emptySet() + //$$ } + //#endif + } + + //#if FABRIC + //$$ private fun getModsAt(path: Path, modMap: ModMap): MutableSet { + //$$ val mod: MutableSet? = modMap[path.toFile()] + //$$ if (mod != null) return mod + //$$ + //$$ else if (FabricLoader.getInstance().isDevelopmentEnvironment) { + //$$ // For some reason, in dev, the mod being tested has the 'resources' folder as the origin instead of the 'classes' folder. + //$$ + //$$ val resourcesPathString: String = + //$$ path.toString().replace("\\", "/") // Make it work with Architectury as well + //$$ .replace("common/build/classes/java/main", "fabric/build/resources/main") + //$$ .replace("common/build/classes/kotlin/main", "fabric/build/resources/main") + //$$ .replace("classes/java/main", "resources/main") + //$$ .replace("classes/kotlin/main", "resources/main") + //$$ val resourcesPath: Path = Paths.get(resourcesPathString) + //$$ return modMap.getOrElse(resourcesPath.toFile()) { emptySet() }.toMutableSet() + //$$ } else { + //$$ logger.debug("Mod at path '" + path.toAbsolutePath() + "' is at fault, but it could not be found in the map of mod paths: ") + //$$ return mutableSetOf() + //$$ } + //$$ } + //#endif + + private fun makeModMap(): ModMap { + val modMap = HashMap>() + + //#if FORGE + for (mod in Loader.instance().modList) { + val currentMods = modMap.getOrDefault(mod.source, HashSet()) + currentMods.add(ModMetadata(mod.modId, mod.name)) + + try { + modMap[mod.source.canonicalFile] = currentMods + } catch (e: IOException) { + throw RuntimeException(e) + } + } + + try { + modMap.remove(Loader.instance().minecraftModContainer.source) // Ignore minecraft jar (minecraft) + modMap.remove(Loader.instance().indexedModList["FML"]!!.source) // Ignore forge jar (FML, forge) + } catch (ignored: NullPointerException) { + // Workaround for https://github.com/MinecraftForge/MinecraftForge/issues/4919 + } + //#else + //$$ for (modContainer in FabricLoader.getInstance().allMods) { + //$$ val modMetadata = ModMetadata(modContainer.metadata.id, modContainer.metadata.name) + //$$ for (source in modContainer.origin.paths) { + //$$ modMap.computeIfAbsent(source.toFile()) { mutableSetOf() }.add(modMetadata) + //$$ } + //$$ } + //#endif + + return modMap + } + + //#if FORGE + private fun untransformName(launchClassLoader: LaunchClassLoader, className: String): String { + return try { + val untransformNameMethod = + LaunchClassLoader::class.java.getDeclaredMethod("untransformName", String::class.java) + untransformNameMethod.isAccessible = true + untransformNameMethod.invoke(launchClassLoader, className) as String + } catch (e: ReflectiveOperationException) { + throw RuntimeException(e) + } + } + //#endif + + //#if FABRIC + //$$ private object Reflection { + //$$ var classInfoMixin: Field? = null + //$$ + //$$ init { + //$$ try { + //$$ classInfoMixin = ClassInfo::class.java.getDeclaredField("mixin") + //$$ classInfoMixin!!.isAccessible = true + //$$ } catch (e: NoSuchFieldException) { + //$$ throw java.lang.RuntimeException(e) + //$$ } + //$$ } + //$$ + //$$ @Nullable + //$$ fun getMixinInfo(classInfo: ClassInfo?): IMixinInfo { + //$$ try { + //$$ return classInfoMixin!!.get(classInfo) as IMixinInfo + //$$ } catch (e: IllegalAccessException) { + //$$ throw java.lang.RuntimeException(e) + //$$ } + //$$ } + //$$ } + //#endif + +} diff --git a/src/main/kotlin/org/polyfrost/crashpatch/identifier/ModMetadata.kt b/src/main/kotlin/org/polyfrost/crashpatch/identifier/ModMetadata.kt new file mode 100644 index 0000000..6c694b6 --- /dev/null +++ b/src/main/kotlin/org/polyfrost/crashpatch/identifier/ModMetadata.kt @@ -0,0 +1,6 @@ +package org.polyfrost.crashpatch.identifier + +data class ModMetadata( + val id: String, + val name: String, +) diff --git a/src/main/kotlin/org/polyfrost/crashpatch/utils/GuiDisconnectedHook.kt b/src/main/kotlin/org/polyfrost/crashpatch/utils/GuiDisconnectedHook.kt index c89e5e9..3a6246a 100644 --- a/src/main/kotlin/org/polyfrost/crashpatch/utils/GuiDisconnectedHook.kt +++ b/src/main/kotlin/org/polyfrost/crashpatch/utils/GuiDisconnectedHook.kt @@ -7,7 +7,7 @@ import net.minecraft.client.gui.GuiScreen import org.polyfrost.crashpatch.CrashPatchConfig import org.spongepowered.asm.mixin.injection.callback.CallbackInfo import org.polyfrost.crashpatch.gui.CrashUI -import org.polyfrost.utils.v1.dsl.mc +import org.polyfrost.oneconfig.utils.v1.dsl.mc object GuiDisconnectedHook {