共享状态与版本仲裁
MCLib 的核心设计:同一个库可能被 shade 进十几个 mod。当多个副本同时存在时,全场只有一个真正执行逻辑,其余副本的调用被重定向到它。状态本身存在 LaunchWrapper 的全局 blackboard 上,因此跨 jar 可见。
基本信息
| 属性 | 值 |
|---|---|
| 类型 | 运行时机制 / cglib 代理 |
| 触发条件 | MCLib 构造时自动 register,共享模块类加载时 shareifyClass |
| 全局键前缀 | mclib.sharedstate. |
SharedReference
包路径 makamys.mclib.core.sharedstate。泛型帮助类,把对象存进 Launch.blackboard。
| 方法 | 说明 |
|---|---|
get(String namespace, String name, Class<?> clazz) |
用 clazz.newInstance() 构造初值 |
get(String namespace, String name, Supplier<T> constructor) |
用 supplier 构造初值 |
get(String namespace, String name, Class<?> clazz, Supplier<T> constructor)(私有) |
实际实现 |
键名拼装为 "mclib.sharedstate." + namespace + "." + name,查不到就先 put 再返回。
本 mod 实际使用的键:
| namespace | name | 类型 | 使用者 |
|---|---|---|---|
"TaskQueue" |
"queuedTasks" |
Map |
TaskQueue.queuedTasks |
"mclib" |
"existingLibPackages" |
ArrayList |
SharedLibHelper.existingLibPackages |
"mclib" |
"newestLibPackage" |
MutableObject<String> |
SharedLibHelper.newestLibPackage |
SloppyDepLoader.NS("SloppyDepLoader") |
"modDeps" |
HashMap |
SloppyDepLoaderAPI.modDeps |
AssetDirector.NS("AssetDirector") |
"jsons" |
HashMap |
AssetDirectorAPI.jsons |
MutableObject 来自 commons-lang3 的可变包装,充当跨副本的 lazy 单例槽位。
SharedLibHelper
包路径 makamys.mclib.core.sharedstate。
| 方法 | 说明 |
|---|---|
register(MCLib mcLib) |
取 MCLib 类的 canonical name 去掉最后 2 段作为本副本包名,加入 existingLibPackages |
getNewestLibPackage() |
遍历所有已注册包名,逐一 Class.forName 读其 VERSION 字段,用 ComparableVersion 取最大值。若版本串以 @ 开头则按 Integer.MAX_VALUE 处理(开发版构建)。必须在 pre-init 之后调用,否则抛 IllegalStateException |
isNewestLib(MCLib) |
本副本是否就是最新版本 |
shareifyClass(Class<?> clazz) |
转发到最新副本的 shareifyClassImpl |
shareifyClassImpl(Class<?> clazz) |
遍历 clazz.getFields(),对每个 public static 字段用 cglib Enhancer 生成同类型代理并写回该字段 |
toRelativeClassName(String className) |
剥掉所属 mclib 包前缀,转成相对类名 |
findNewestLibClass(Class<?> clazz) |
拼出最新副本中的对应类并加载 |
选出的最新包名会 LOGGER.info("Latest version of MCLib present: ...") 打印一次。
注意:shareifyClassImpl 要求字段类型有无参构造,代理才能在最新副本中实例化真实对象。
SharedModuleMethodRedirector
包路径 makamys.mclib.core.sharedstate,实现 cglib 的 MethodInterceptor。
- 构造时记录字段,并判断
field.getDeclaringClass().getName().startsWith(SharedLibHelper.getNewestLibPackage())得到isNewest。 isNewest = true:懒加载真实对象(field.getType().getConstructor().newInstance()),把调用proxy.invoke给它。isNewest = false:到最新副本的同名字段上取对象,再按方法名和参数类型反射调用同构方法。
"等价"的定义写在类注释里:字段名相同,且外层类相对 mclib 根包的 canonical name 相同。
@SharedField
包路径 makamys.mclib.core.sharedstate,@Retention(RUNTIME) 的空标记注解。当前 shareifyClassImpl 里对它的检查是被注释掉的——源码里保留着 //if(f.isAnnotationPresent(SharedField.class)) { 与对应的 }// 配对,注解目前不参与任何逻辑,加了也不会被特殊对待。
相关条目
- MCLib 核心与任务队列 -
TaskQueue与两个模块持有者 - 反射访问点 - 本类用到的
Class.forName与ReflectionHelper路径