🧩 feat: 支持从线程调用栈进行强力搜索

This commit is contained in:
TheWhiteDog9487
2026-09-15 11:31:32 +08:00
parent e0af7c05eb
commit 863bcfd0e2
9 changed files with 111 additions and 6 deletions
+10
View File
@@ -52,6 +52,16 @@ dependencies {
implementation("maven.modrinth:fuuu3xnx:bvFby61J") implementation("maven.modrinth:fuuu3xnx:bvFby61J")
// https://modrinth.com/mod/controlling/versions?l=fabric // https://modrinth.com/mod/controlling/versions?l=fabric
implementation("maven.modrinth:xv94TkTM:rAp2jljs") implementation("maven.modrinth:xv94TkTM:rAp2jljs")
// https://modrinth.com/mod/notenoughcrashes/versions?l=fabric
implementation("maven.modrinth:yM94ont6:3oHxOFPT")
// https://modrinth.com/mod/scoreboard-overhaul/versions
implementation("maven.modrinth:2YIQkF2v:HI1qkixZ")
// https://modrinth.com/plugin/plasmo-voice/versions?l=fabric
implementation("maven.modrinth:1bZhdhsH:I0T9OQcy")
// https://modrinth.com/mod/first-person-model/versions?l=fabric
implementation("maven.modrinth:H5XMjpHi:6sgz2HEq")
// https://modrinth.com/mod/entityculling/versions?l=fabric
implementation("maven.modrinth:NNAgCjsB:iiF6U3Ne")
} }
tasks.processResources { tasks.processResources {
+1 -1
View File
@@ -14,7 +14,7 @@ loom_version=1.17-SNAPSHOT
fabric_kotlin_version=1.13.13+kotlin.2.4.10 fabric_kotlin_version=1.13.13+kotlin.2.4.10
# Mod Properties # Mod Properties
version=1.0.0 version=1.1.0
group=xyz.thewhitedog9487 group=xyz.thewhitedog9487
OutputFileBasename=WhoProvideThisKeybind OutputFileBasename=WhoProvideThisKeybind
@@ -0,0 +1,57 @@
package xyz.thewhitedog9487.client.mixin;
import com.mojang.blaze3d.platform.InputConstants;
import net.fabricmc.loader.api.FabricLoader;
import net.fabricmc.loader.api.ModContainer;
import net.minecraft.client.KeyMapping;
import org.jetbrains.annotations.NotNull;
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.callback.CallbackInfo;
import org.spongepowered.include.com.google.common.io.Files;
import xyz.thewhitedog9487.client.MiscellaneousKt;
import xyz.thewhitedog9487.client.ModInfo;
import xyz.thewhitedog9487.client.WhoProvideThisKeybindModClient;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.Optional;
import java.util.function.Function;
@Mixin(KeyMapping.class)
public class KeyMappingMixin {
@Inject(method = "<init>(Ljava/lang/String;Lcom/mojang/blaze3d/platform/InputConstants$Type;ILnet/minecraft/client/KeyMapping$Category;I)V",
at = @At("TAIL"))
void DiscoverStackTrace(String name, InputConstants.Type type, int value, KeyMapping.Category category, int order, CallbackInfo ci) throws IOException, ClassNotFoundException {
var StackTraceCLassNames = Arrays.stream(Thread.currentThread().getStackTrace())
.map(StackTraceElement::getClassName)
.toList();
for (String ClassName : StackTraceCLassNames) {
if (IsFrameworkClass(ClassName)) continue;
for (String PartOfClassName : ClassName.split("\\.")) {
Optional<ModContainer> CandidateModContainer = FabricLoader.getInstance().getModContainer(PartOfClassName);
if (CandidateModContainer.isPresent() && CandidateModContainer.get() != MiscellaneousKt.getVanillaMinecraftContainer()) {
var ModInfo = new ModInfo(CandidateModContainer.get());
MiscellaneousKt.getKeyMappingToMod().put((KeyMapping) (Object) this, ModInfo);
return; } } } }
@Unique
boolean IsFrameworkClass(@NotNull String ClassName) {
return ClassName.startsWith("java.")
|| ClassName.startsWith("jdk.")
|| ClassName.startsWith("sun.")
|| ClassName.startsWith("com.sun.")
|| ClassName.startsWith("kotlin.")
|| ClassName.startsWith("net.minecraft")
|| ClassName.startsWith("net.fabricmc")
|| ClassName.startsWith("org.spongepowered.")
// ↓ 第三方Mod兼容条目
|| ClassName.startsWith("dev.tr7zw.transition"); } }
@@ -7,17 +7,22 @@ import net.minecraft.client.KeyMapping
import net.minecraft.client.gui.components.Button import net.minecraft.client.gui.components.Button
import net.minecraft.client.gui.components.Tooltip import net.minecraft.client.gui.components.Tooltip
import net.minecraft.network.chat.Component import net.minecraft.network.chat.Component
import kotlin.io.path.pathString
val VanillaMinecraftContainer: ModContainer = FabricLoader val VanillaMinecraftContainer: ModContainer = FabricLoader
.getInstance() .getInstance()
.getModContainer("minecraft") .getModContainer("minecraft")
.get() .get()
val KeyMappingToMod: HashMap<KeyMapping, ModInfo> = HashMap()
val KeyMapping.Provider: ModInfo get() { val KeyMapping.Provider: ModInfo get() {
if (SettingsInstance.SearchInStackTrace) {
KeyMappingToMod[this]?.let { return it } }
val KeyBindTranslationKey = name val KeyBindTranslationKey = name
val CandidateModIds = KeyBindTranslationKey.split(".") val CandidateModIds = KeyBindTranslationKey.split(".")
if (CandidateModIds.size == 1){ if (CandidateModIds.size == 1){
WhoProvideThisKeybindModClient.ClientLogger.warn("这是个什么东西,按键绑定ID只有一个部分,ID: $KeyBindTranslationKey")
return ModInfo(null) } return ModInfo(null) }
for (CandidateModId in CandidateModIds) { for (CandidateModId in CandidateModIds) {
val ModContainer = FabricLoader.getInstance().getModContainer(CandidateModId) val ModContainer = FabricLoader.getInstance().getModContainer(CandidateModId)
@@ -44,6 +44,13 @@ class ModMenuEntryPoint: ModMenuApi {
.binding( false, { SettingsInstance.ShowDebugInfo }, { SettingsInstance.ShowDebugInfo = it } ) .binding( false, { SettingsInstance.ShowDebugInfo }, { SettingsInstance.ShowDebugInfo = it } )
.controller(BooleanControllerBuilder::create ) .controller(BooleanControllerBuilder::create )
.build() } .build() }
.option {
Option.createBuilder<Boolean>()
.name(Component.translatable("option.SearchInStackTrace"))
.description { OptionDescription.of(Component.translatable("option.SearchInStackTrace.description") ) }
.binding( false, { SettingsInstance.SearchInStackTrace }, { SettingsInstance.SearchInStackTrace = it } )
.controller(BooleanControllerBuilder::create )
.build() }
.build() } .build() }
.build() .build()
.generateScreen(ParentScreen) } } .generateScreen(ParentScreen) } }
@@ -57,7 +57,26 @@ class Settings {
取值范围:true false 取值范围:true false
默认值:false 默认值:false
""") """)
var ShowDebugInfo = false } var ShowDebugInfo = false
@SerialEntry(comment =
"""
是否注入KeyMapping构造函数,在每次注册按键绑定时查询线程的调用栈信息来进行强力匹配
警告:在部分情况下可能产生更多误报,请按需选择
类型:布尔值
取值范围:true false
默认值:false
""")
var SearchInStackTrace = false
@SerialEntry(comment =
"""
是否在悬停信息中显示Mod的文件路径
类型:布尔值
取值范围:true false
默认值:false
""")
var ShowFilePath = false }
var SettingsHandler: ConfigClassHandler<Settings>? = null var SettingsHandler: ConfigClassHandler<Settings>? = null
@@ -3,7 +3,8 @@
"package": "xyz.thewhitedog9487.client.mixin", "package": "xyz.thewhitedog9487.client.mixin",
"compatibilityLevel": "JAVA_25", "compatibilityLevel": "JAVA_25",
"client": [ "client": [
"KeyBindsListKeyEntryMixin" "KeyBindsListKeyEntryMixin",
"KeyMappingMixin"
], ],
"injectors": { "injectors": {
"defaultRequire": 1 "defaultRequire": 1
@@ -13,8 +13,11 @@
"option.ShowKeybindID.description": "Display the keybind's own ID in the tooltip", "option.ShowKeybindID.description": "Display the keybind's own ID in the tooltip",
"option.ShowDebugInfo": "Show Debug Info", "option.ShowDebugInfo": "Show Debug Info",
"option.ShowDebugInfo.description": "This information is used to troubleshoot issues related to this mod; it generally does not need to be enabled", "option.ShowDebugInfo.description": "This information is used to troubleshoot issues related to this mod; it generally does not need to be enabled",
"option.SearchInStackTrace": "Enable aggressive search via stack trace lookup",
"option.SearchInStackTrace.description": "Injects into the KeyMapping class constructor and queries the current thread's call stack each time a keybind is registered for aggressive matching\nWarning: In some cases, this may produce more false positives; enable as needed",
"tooltip.provided_by": "Provided by: %s", "tooltip.provided_by": "Provided by: %s",
"tooltip.provider_mod_id": "Provider Mod ID: %s", "tooltip.provider_mod_id": "Provider Mod ID: %s",
"tooltip.key_id": "Keybind ID: %s", "tooltip.key_id": "Keybind ID: %s",
"special_provider_UNKNOWN": "Unknown" "tooltip.mod_file_path": "Mod File Path: %s",
"special_string_UNKNOWN": "Unknown"
} }
@@ -13,8 +13,11 @@
"option.ShowKeybindID.description": "在提示信息中显示该按键本身的ID", "option.ShowKeybindID.description": "在提示信息中显示该按键本身的ID",
"option.ShowDebugInfo": "是否显示调试信息", "option.ShowDebugInfo": "是否显示调试信息",
"option.ShowDebugInfo.description": "这些信息用于出现和本Mod相关的问题时进行排查,一般情况下不需要开启", "option.ShowDebugInfo.description": "这些信息用于出现和本Mod相关的问题时进行排查,一般情况下不需要开启",
"option.SearchInStackTrace": "是否要通过查询堆栈信息来进行强力搜索",
"option.SearchInStackTrace.description": "注入KeyMapping类的构造函数,在每次注册按键绑定时查询线程的调用栈信息来进行强力匹配\n警告:部分情况下可能产生更多误报,请按需选择",
"tooltip.provided_by": "提供者:%s", "tooltip.provided_by": "提供者:%s",
"tooltip.provider_mod_id": "提供者ID%s", "tooltip.provider_mod_id": "提供者ID%s",
"tooltip.key_id": "键位ID%s", "tooltip.key_id": "键位ID%s",
"special_provider_UNKNOWN": "未知" "tooltip.mod_file_path": "Mod文件路径:%s",
"special_string_UNKNOWN": "未知"
} }