组合键绑定
基本信息
| 属性 | 值 |
|---|---|
| 所属维度 | keybinding |
| 核心接口 | com.blamejared.controlling.keybinding.ComboKeyBinding |
| 实际实现 | com.blamejared.controlling.mixins.early.MixinKeyBinding(Mixin 到原版 net.minecraft.client.settings.KeyBinding) |
| 侧别 | 纯客户端(Mixin 用 addClientMixins 注册) |
| 修饰键取值 | 见 修饰键枚举 |
功能
Controlling 自身不注册任何按键(源码中没有一处 new KeyBinding)。它通过 Mixin 在原版
KeyBinding 类上挂载 ComboKeyBinding 接口,使所有按键绑定——原版的以及其他 mod 注册的——
都自动获得一个额外的修饰键字段,从而支持组合键。
每个 KeyBinding 实例因此多出两个字段(MixinKeyBinding 中标注 @Unique):
| 字段 | 初始值 | 含义 |
|---|---|---|
controlling$keyModifier |
KeyModifier.NONE |
当前生效的修饰键 |
controlling$defaultKeyModifier |
KeyModifier.NONE |
默认修饰键("恢复默认"时回到的值) |
构造函数 <init> 的 TAIL 注入点会把这两个字段再次置为 KeyModifier.NONE,
保证新实例一定是干净的初始状态。
接口方法
ComboKeyBinding 共 12 个方法,全部以 controlling$ 前缀命名(mixin unique 命名空间约定,
避免与原版或其他 mod 的成员冲突):
| 方法 | 作用 |
|---|---|
controlling$getKeyModifier() |
读取当前修饰键 |
controlling$getDefaultKeyModifier() |
读取默认修饰键 |
controlling$setKeyModifier(KeyModifier) |
设置当前修饰键;传入 null 归一化为 NONE |
controlling$setDefaultKeyModifier(KeyModifier) |
设置默认修饰键;null 归一化为 NONE |
controlling$setKeyModifierAndCode(KeyModifier, int) |
同时设置修饰键与键码 |
controlling$getDisplayName() |
返回 GUI 显示名,如 Ctrl + G |
controlling$conflicts(KeyBinding) |
是否构成硬冲突 |
controlling$hasKeyCodeModifierConflict(KeyBinding) |
是否构成软冲突(同键码、不同修饰键) |
controlling$isSetToDefaultValue() |
键码与修饰键是否都等于默认值 |
controlling$setToDefault() |
恢复默认键码 + 默认修饰键 |
controlling$isModifierActive() |
当前所需修饰键是否按下 |
显示名规则
controlling$getDisplayName():
GameSettings.getKeyDisplayString(keyCode)作为基础键名;- 若修饰键为
NONE或keyCode == Keyboard.KEY_NONE,直接返回基础键名; - 否则返回
modifier.getDisplayName() + " + " + keyName,例如Ctrl + G、Shift + K。
硬冲突与软冲突
这是本 mod 区分冲突严重度的核心逻辑,也是列表里 [ ] 显示为金色或红色的依据。
controlling$conflicts(other) —— 硬冲突,两个条件都不满足即返回 false:
other == null→ 不冲突;this.keyCode != other.getKeyCode()或this.keyCode == Keyboard.KEY_NONE→ 不冲突;- 取
other的修饰键(非ComboKeyBinding实例一律按KeyModifier.NONE计), 返回this.keyModifier == otherModifier。
即:键码相同 且 修饰键相同才算冲突。G 与 Ctrl + G 不算冲突。
controlling$hasKeyCodeModifierConflict(other) —— 软冲突:
other不是ComboKeyBinding→false;!controlling$conflicts(other)→false;- 返回
this.keyModifier != combo.controlling$getKeyModifier()。
即:键码相同 但 修饰键不同。源码注释给的例子是 SHIFT 与 SHIFT + G 这类较轻度的冲突。
判定与重置
controlling$isSetToDefaultValue():keyCode == keyCodeDefault && keyModifier == defaultKeyModifier—— 键码和修饰键都回到默认才算"未改动"。这直接控制 GUI 里"恢复默认"按钮是否可点。controlling$setToDefault():keyCode = keyCodeDefault; keyModifier = defaultKeyModifier
持久化格式
MixinGameSettings(Mixin 到原版 GameSettings)在 options.txt 的按键行后追加第三个字段。
写入:saveOptions 把对 PrintWriter.println(String) 的调用重定向,凡是以 key_ 开头的行,
经 controlling$appendModifierToKeyLine 处理——按 ":" 分割且只取 2 段后重组:
| 情况 | 输出 |
|---|---|
该按键是 ComboKeyBinding 且修饰键不为 NONE |
key_<描述>:<键码>:<MODIFIER> |
修饰键为 NONE |
原样输出 key_<描述>:<键码>(不写第三段) |
找不到对应 KeyBinding |
原样输出 |
<MODIFIER> 用的是 KeyModifier.name(),即序列化名 CONTROL / SHIFT / ALT。
读取:loadOptions 在调用原版 KeyBinding.setKeyCode(int) 之前拦截,若该行字段数
astring.length > 2 且该按键是 ComboKeyBinding,则用 KeyModifier.fromSerializedName(astring[2])
设置修饰键。fromSerializedName 对任何无法识别的字符串都回落为 NONE。
因为第三段只在该按键真正有修饰键时才写入,所以原版写出的两字段格式与本 mod 完全兼容,
原版读取时也会忽略多出的第三段。匹配按键时用 "key_" + keyBinding.getKeyDescription() 精确比对。