├── .gitignore ├── LICENSE ├── README.md ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle └── src └── main ├── java └── link │ └── infra │ └── mixintrace │ ├── TraceUtils.java │ └── mixin │ ├── MixinCrashReport.java │ └── MixinCrashReportSection.java └── resources ├── assets └── mixintrace │ └── icon.png ├── fabric.mod.json └── mixintrace.mixins.json /.gitignore: -------------------------------------------------------------------------------- 1 | # User-specific stuff 2 | .idea/ 3 | 4 | *.iml 5 | *.ipr 6 | *.iws 7 | 8 | # IntelliJ 9 | out/ 10 | # mpeltonen/sbt-idea plugin 11 | .idea_modules/ 12 | 13 | # JIRA plugin 14 | atlassian-ide-plugin.xml 15 | 16 | # Compiled class file 17 | *.class 18 | 19 | # Log file 20 | *.log 21 | 22 | # BlueJ files 23 | *.ctxt 24 | 25 | # Package Files # 26 | *.jar 27 | *.war 28 | *.nar 29 | *.ear 30 | *.zip 31 | *.tar.gz 32 | *.rar 33 | 34 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 35 | hs_err_pid* 36 | 37 | *~ 38 | 39 | # temporary files which can be created if a process still has a handle open of a deleted file 40 | .fuse_hidden* 41 | 42 | # KDE directory preferences 43 | .directory 44 | 45 | # Linux trash folder which might appear on any partition or disk 46 | .Trash-* 47 | 48 | # .nfs files are created when an open file is removed but is still being accessed 49 | .nfs* 50 | 51 | # General 52 | .DS_Store 53 | .AppleDouble 54 | .LSOverride 55 | 56 | # Icon must end with two \r 57 | Icon 58 | 59 | # Thumbnails 60 | ._* 61 | 62 | # Files that might appear in the root of a volume 63 | .DocumentRevisions-V100 64 | .fseventsd 65 | .Spotlight-V100 66 | .TemporaryItems 67 | .Trashes 68 | .VolumeIcon.icns 69 | .com.apple.timemachine.donotpresent 70 | 71 | # Directories potentially created on remote AFP share 72 | .AppleDB 73 | .AppleDesktop 74 | Network Trash Folder 75 | Temporary Items 76 | .apdisk 77 | 78 | # Windows thumbnail cache files 79 | Thumbs.db 80 | Thumbs.db:encryptable 81 | ehthumbs.db 82 | ehthumbs_vista.db 83 | 84 | # Dump file 85 | *.stackdump 86 | 87 | # Folder config file 88 | [Dd]esktop.ini 89 | 90 | # Recycle Bin used on file shares 91 | $RECYCLE.BIN/ 92 | 93 | # Windows Installer files 94 | *.cab 95 | *.msi 96 | *.msix 97 | *.msm 98 | *.msp 99 | 100 | # Windows shortcuts 101 | *.lnk 102 | 103 | .gradle 104 | build/ 105 | 106 | # Ignore Gradle GUI config 107 | gradle-app.setting 108 | 109 | # Cache of project 110 | .gradletasknamecache 111 | 112 | **/build/ 113 | 114 | # Common working directory 115 | run/ 116 | 117 | # Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) 118 | !gradle-wrapper.jar 119 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2020 comp500 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MixinTrace 2 | This is a Fabric mod that adds a list of mixins (and Mixin configuration names) to classes in the stack trace to crash reports. 3 | 4 | Example output: 5 | 6 | ``` 7 | Mixins in Stacktrace: 8 | net.minecraft.class_465: 9 | dev.emi.bunchotrinkets.mixin.AbstractContainerScreenMixin (bunchotrinkets.mixins.json) 10 | net.backslot.mixin.HandledScreenMixin (backslot.mixins.json) 11 | dev.emi.trinkets.mixin.HandledScreenMixin (trinkets.mixins.json) 12 | net.minecraft.class_485: 13 | dev.emi.trinkets.mixin.AbstractInventoryScreenMixin (trinkets.mixins.json) 14 | net.minecraft.class_481: 15 | dev.emi.trinkets.mixin.CreativeInventoryScreenMixin (trinkets.mixins.json) 16 | net.fabricmc.fabric.mixin.item.group.client.MixinCreativePlayerInventoryGui (fabric-item-groups-v0.mixins.json) 17 | net.minecraft.class_757: 18 | com.jamieswhiteshirt.reachentityattributes.mixin.client.GameRendererMixin (mixins.reach-entity-attributes.json) 19 | net.minecraft.class_310: 20 | net.fabricmc.fabric.mixin.networking.MixinMinecraftClient (fabric-networking-v0.mixins.json) 21 | net.fabricmc.fabric.mixin.resource.loader.MixinMinecraftGame (fabric-resource-loader-v0.mixins.json) 22 | net.fabricmc.fabric.mixin.event.interaction.MixinMinecraftClient (fabric-events-interaction-v0.mixins.json) 23 | net.fabricmc.fabric.mixin.event.lifecycle.client.MinecraftClientMixin (fabric-lifecycle-events-v1.mixins.json) 24 | net.fabricmc.fabric.mixin.registry.sync.client.MixinMinecraftClient (fabric-registry-sync-v0.mixins.json) 25 | dev.emi.bunchotrinkets.mixin.MinecraftClientMixin (bunchotrinkets.mixins.json) 26 | ``` 27 | 28 | Tested on Minecraft 1.16.2 and 1.16.1, but is likely to work on many different versions. 29 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'fabric-loom' version '0.8-SNAPSHOT' 3 | id 'maven-publish' 4 | } 5 | 6 | sourceCompatibility = JavaVersion.VERSION_16 7 | targetCompatibility = JavaVersion.VERSION_16 8 | 9 | archivesBaseName = project.archives_base_name 10 | version = project.mod_version + "+" + project.minecraft_version 11 | group = project.maven_group 12 | 13 | repositories { 14 | // Add repositories to retrieve artifacts from in here. 15 | // You should only use this when depending on other mods because 16 | // Loom adds the essential maven repositories to download Minecraft and libraries from automatically. 17 | // See https://docs.gradle.org/current/userguide/declaring_repositories.html 18 | // for more information about repositories. 19 | } 20 | 21 | dependencies { 22 | // To change the versions see the gradle.properties file 23 | minecraft "com.mojang:minecraft:${project.minecraft_version}" 24 | mappings "net.fabricmc:yarn:${project.yarn_mappings}:v2" 25 | modImplementation "net.fabricmc:fabric-loader:${project.loader_version}" 26 | 27 | // PSA: Some older mods, compiled on Loom 0.2.1, might have outdated Maven POMs. 28 | // You may need to force-disable transitiveness on them. 29 | } 30 | 31 | processResources { 32 | inputs.property "version", project.version 33 | 34 | filesMatching("fabric.mod.json") { 35 | expand "version": project.version 36 | } 37 | } 38 | 39 | tasks.withType(JavaCompile).configureEach { 40 | // ensure that the encoding is set to UTF-8, no matter what the system default is 41 | // this fixes some edge cases with special characters not displaying correctly 42 | // see http://yodaconditions.net/blog/fix-for-java-file-encoding-problems-with-gradle.html 43 | // If Javadoc is generated, this must be specified in that task too. 44 | it.options.encoding = "UTF-8" 45 | 46 | // Minecraft 1.17 (21w19a) upwards uses Java 16. 47 | it.options.release = 16 48 | } 49 | 50 | java { 51 | // Loom will automatically attach sourcesJar to a RemapSourcesJar task and to the "build" task 52 | // if it is present. 53 | // If you remove this line, sources will not be generated. 54 | withSourcesJar() 55 | } 56 | 57 | jar { 58 | from("LICENSE") { 59 | rename { "${it}_${project.archivesBaseName}"} 60 | } 61 | } 62 | 63 | // configure the maven publication 64 | publishing { 65 | publications { 66 | mavenJava(MavenPublication) { 67 | // add all the jars that should be included when publishing to maven 68 | artifact(remapJar) { 69 | builtBy remapJar 70 | } 71 | artifact(sourcesJar) { 72 | builtBy remapSourcesJar 73 | } 74 | } 75 | } 76 | 77 | // See https://docs.gradle.org/current/userguide/publishing_maven.html for information on how to set up publishing. 78 | repositories { 79 | // Add repositories to publish to here. 80 | // Notice: This block does NOT have the same function as the block in the top level. 81 | // The repositories here will be used for publishing your artifact, not for 82 | // retrieving dependencies. 83 | } 84 | } -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Done to increase the memory available to gradle. 2 | org.gradle.jvmargs=-Xmx1G 3 | # Fabric Properties 4 | # check these on https://modmuss50.me/fabric.html 5 | minecraft_version=1.17 6 | yarn_mappings=1.17+build.7 7 | loader_version=0.14.7 8 | # Mod Properties 9 | mod_version=1.1.1 10 | maven_group=link.infra 11 | archives_base_name=mixintrace 12 | # Dependencies -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/comp500/mixintrace/517d04f79302208e564811a0429408c65286c3f2/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.2-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/comp500/mixintrace/517d04f79302208e564811a0429408c65286c3f2/gradlew -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | jcenter() 4 | maven { 5 | name = 'Fabric' 6 | url = 'https://maven.fabricmc.net/' 7 | } 8 | gradlePluginPortal() 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/link/infra/mixintrace/TraceUtils.java: -------------------------------------------------------------------------------- 1 | package link.infra.mixintrace; 2 | 3 | import org.spongepowered.asm.mixin.extensibility.IMixinInfo; 4 | import org.spongepowered.asm.mixin.transformer.ClassInfo; 5 | 6 | import java.lang.reflect.Method; 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | import java.util.Set; 10 | 11 | public class TraceUtils { 12 | public static void printTrace(StackTraceElement[] stackTrace, StringBuilder crashReportBuilder) { 13 | if (stackTrace != null && stackTrace.length > 0) { 14 | crashReportBuilder.append("\nMixins in Stacktrace:"); 15 | 16 | try { 17 | List classNames = new ArrayList<>(); 18 | for (StackTraceElement el : stackTrace) { 19 | if (!classNames.contains(el.getClassName())) { 20 | classNames.add(el.getClassName()); 21 | } 22 | } 23 | 24 | boolean found = false; 25 | for (String className : classNames) { 26 | ClassInfo classInfo = ClassInfo.fromCache(className); 27 | if (classInfo != null) { 28 | // Workaround for bug in Mixin, where it adds to the wrong thing :( 29 | Object mixinInfoSetObject; 30 | try { 31 | Method getMixins = ClassInfo.class.getDeclaredMethod("getMixins"); 32 | getMixins.setAccessible(true); 33 | mixinInfoSetObject = getMixins.invoke(classInfo); 34 | } catch (Exception e) { 35 | // Fabric loader >=0.12.0 proguards out this method; use the field instead 36 | var mixinsField = ClassInfo.class.getDeclaredField("mixins"); 37 | mixinsField.setAccessible(true); 38 | mixinInfoSetObject = mixinsField.get(classInfo); 39 | } 40 | 41 | @SuppressWarnings("unchecked") Set mixinInfoSet = (Set) mixinInfoSetObject; 42 | 43 | if (mixinInfoSet.size() > 0) { 44 | crashReportBuilder.append("\n\t"); 45 | crashReportBuilder.append(className); 46 | crashReportBuilder.append(":"); 47 | for (IMixinInfo info : mixinInfoSet) { 48 | crashReportBuilder.append("\n\t\t"); 49 | crashReportBuilder.append(info.getClassName()); 50 | crashReportBuilder.append(" ("); 51 | crashReportBuilder.append(info.getConfig().getName()); 52 | crashReportBuilder.append(")"); 53 | } 54 | found = true; 55 | } 56 | } 57 | } 58 | 59 | if (!found) { 60 | crashReportBuilder.append(" None found"); 61 | } 62 | } catch (Exception e) { 63 | crashReportBuilder.append(" Failed to find Mixin metadata: ").append(e); 64 | } 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/link/infra/mixintrace/mixin/MixinCrashReport.java: -------------------------------------------------------------------------------- 1 | package link.infra.mixintrace.mixin; 2 | 3 | import link.infra.mixintrace.TraceUtils; 4 | import net.minecraft.util.crash.CrashReport; 5 | import org.spongepowered.asm.mixin.Mixin; 6 | import org.spongepowered.asm.mixin.Shadow; 7 | import org.spongepowered.asm.mixin.injection.At; 8 | import org.spongepowered.asm.mixin.injection.Inject; 9 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 10 | 11 | @Mixin(value = CrashReport.class) 12 | public abstract class MixinCrashReport { 13 | @Shadow private StackTraceElement[] stackTrace; 14 | 15 | @Inject(method = "addStackTrace", at = @At(value = "FIELD", target = "Lnet/minecraft/util/crash/CrashReport;otherSections:Ljava/util/List;")) 16 | private void mixintrace_addTrace(StringBuilder crashReportBuilder, CallbackInfo ci) { 17 | int trailingNewlineCount = 0; 18 | // Remove trailing \n 19 | if (crashReportBuilder.charAt(crashReportBuilder.length() - 1) == '\n') { 20 | crashReportBuilder.deleteCharAt(crashReportBuilder.length() - 1); 21 | trailingNewlineCount++; 22 | } 23 | if (crashReportBuilder.charAt(crashReportBuilder.length() - 1) == '\n') { 24 | crashReportBuilder.deleteCharAt(crashReportBuilder.length() - 1); 25 | trailingNewlineCount++; 26 | } 27 | TraceUtils.printTrace(stackTrace, crashReportBuilder); 28 | crashReportBuilder.append("\n".repeat(trailingNewlineCount)); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/link/infra/mixintrace/mixin/MixinCrashReportSection.java: -------------------------------------------------------------------------------- 1 | package link.infra.mixintrace.mixin; 2 | 3 | import link.infra.mixintrace.TraceUtils; 4 | import net.minecraft.util.crash.CrashReportSection; 5 | import org.spongepowered.asm.mixin.Mixin; 6 | import org.spongepowered.asm.mixin.Shadow; 7 | import org.spongepowered.asm.mixin.injection.At; 8 | import org.spongepowered.asm.mixin.injection.Inject; 9 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 10 | 11 | @Mixin(value = CrashReportSection.class) 12 | public abstract class MixinCrashReportSection { 13 | @Shadow private StackTraceElement[] stackTrace; 14 | 15 | @Inject(method = "addStackTrace", at = @At("TAIL")) 16 | private void mixintrace_addTrace(StringBuilder crashReportBuilder, CallbackInfo ci) { 17 | TraceUtils.printTrace(stackTrace, crashReportBuilder); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/resources/assets/mixintrace/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/comp500/mixintrace/517d04f79302208e564811a0429408c65286c3f2/src/main/resources/assets/mixintrace/icon.png -------------------------------------------------------------------------------- /src/main/resources/fabric.mod.json: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": 1, 3 | "id": "mixintrace", 4 | "version": "${version}", 5 | "name": "MixinTrace", 6 | "description": "Adds a list of mixins in the stack trace to crash reports", 7 | "authors": [ 8 | "comp500" 9 | ], 10 | "contact": { 11 | "homepage": "https://modrinth.com/mod/mixintrace", 12 | "sources": "https://github.com/comp500/mixintrace", 13 | "issues": "https://github.com/comp500/mixintrace/issues" 14 | }, 15 | "license": "MIT", 16 | "icon": "assets/mixintrace/icon.png", 17 | "environment": "*", 18 | "entrypoints": {}, 19 | "mixins": [ 20 | "mixintrace.mixins.json" 21 | ], 22 | "depends": { 23 | "fabricloader": ">=0.12.0", 24 | "minecraft": "*" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/resources/mixintrace.mixins.json: -------------------------------------------------------------------------------- 1 | { 2 | "required": true, 3 | "minVersion": "0.8", 4 | "package": "link.infra.mixintrace.mixin", 5 | "compatibilityLevel": "JAVA_16", 6 | "mixins": [ 7 | "MixinCrashReport", 8 | "MixinCrashReportSection" 9 | ], 10 | "client": [ 11 | ], 12 | "injectors": { 13 | "defaultRequire": 1 14 | } 15 | } 16 | --------------------------------------------------------------------------------