可撤销动作基类
基本信息
| 属性 | 值 |
|---|---|
| 包 | modtweaker2.utils |
| 文件数 | 10 个 Base*.java(共 620 行) |
| 实现接口 | minetweaker.IUndoableAction |
| 根类 | BaseUndoable(60 行) |
| 提交入口 | MineTweakerAPI.apply(IUndoableAction) |
功能
ModTweaker 的每个 @ZenMethod 在参数校验通过后都不会直接改动游戏注册表,而是构造一个 IUndoableAction 并交给 MineTweakerAPI.apply(...) 提交。这让整包脚本可以被 MineTweaker 统一重载与回滚。modtweaker2/utils/ 下的 10 个抽象基类把「往 List / Map / 合成表里增删」这四种重复模式各抽了一层。
类族
| 类 | 行数 | 继承 | 用途 |
|---|---|---|---|
BaseUndoable |
60 | IUndoableAction |
根类,持有 name 与 success |
BaseListModification<T> |
114 | BaseUndoable |
持有 list / recipes / successful 三个 LinkedList |
BaseListAddition<T> |
66 | BaseListModification<T> |
逐条 list.add(),成功项记入 successful |
BaseListRemoval<T> |
73 | BaseListModification<T> |
逐条 list.remove(),撤销时 list.add() 还原 |
BaseMapModification<K,V> |
52 | BaseUndoable |
同上,容器换成 Map |
BaseMapAddition<K,V> |
83 | BaseMapModification<K,V> |
map.put(),被覆盖的旧值存入 overwritten |
BaseMapRemoval<K,V> |
61 | BaseMapModification<K,V> |
map.remove() 并在撤销时回填 |
BaseCraftingAddition |
23 | BaseListAddition<IRecipe> |
固定为 IRecipe 列表 |
BaseCraftingRemoval |
39 | BaseListRemoval<IRecipe> |
固定为 IRecipe 列表 |
BaseMultiModification |
49 | BaseUndoable |
把多个 IUndoableAction 串成一个原子动作 |
撤销语义
BaseUndoable.canUndo() 直接返回 success 字段(BaseUndoable.java:23-26),而 BaseListModification.canUndo() 收紧为「recipes 与 successful 都非空」(BaseListModification.java:17-21)。也就是说只有真正改动了至少一条注册表记录、且该改动成功落地的动作才可回滚;纯失败的动作不占用回滚槽位。
BaseMapAddition 额外维护 overwritten(BaseMapAddition.java:14),在 apply() 里若 map.put() 返回了非空旧值,就记一条 logWarning 并把旧值存进 overwritten(BaseMapAddition.java:33-45),撤销时再写回——这使得「覆盖已有配方」也能被正确还原。
BaseMultiModification 是唯一的组合器:apply() / undo() 都只做一层 for 循环转发(BaseMultiModification.java:15-19、:42-46),canUndo() 要求每个子动作都可撤销(BaseMultiModification.java:21-28)。
失败不抛异常
apply() 遇到单条配方失败时走 LogHelper.logError(...) 而不中断循环。以 BaseListAddition.apply() 为例(BaseListAddition.java:24-40):list.add() 返回 false 就记一条 Error adding %s Recipe for %s;配方对象为 null 则记 Error adding %s Recipe: null object。undo() 侧对称(BaseListAddition.java:42-58)。因此一次 addRecipe 可以部分成功,脚本不会因个别配方冲突而整体回滚。
描述文本
所有基类的 describe() / describeUndo() 都用 String.format 产出以 [ModTweaker2] 开头的英文串(BaseUndoable.java:28-35、BaseListAddition.java:60-67)。这与 反射与日志助手 中 LogHelper.logError 统一追加的 [ModTweaker2] 前缀一致。
身份与去重
BaseUndoable.equals() 只比较 name 字段,hashCode() 返回 name.hashCode()(BaseUndoable.java:37-55)。getOverrideKey() 恒返回 null(BaseUndoable.java:57-60)。多数具体动作(如 Chisel 的 AddVariation)另行覆盖 getOverrideKey()。
一处非缺陷的命名
BaseListAddition 与 BaseListRemoval 的三参构造函数把参数拼写为 recipies(BaseListAddition.java:13),而方法体里往 recipes 字段 addAll。二者不是同一标识符:recipes 是 BaseListModification 声明的 protected final LinkedList<T> recipes(BaseListModification.java:9),recipies 是形参。方法体 recipes.addAll(recipies) 因此成立,行为正确——按代码原样记录,不作为缺陷。
相关条目
- 插件加载器 - 决定哪些脚本类会进入注册表
- 反射与日志助手 -
LogHelper.logError与ReflectionHelper的实现 - Chisel 脚本类 - 覆盖
getOverrideKey()的具体动作示例