MCLib 核心与任务队列

[!IMPORTANT] MCLib 不是一个 Forge mod,而是一个被 shade 进其他 mod 的 Java 库。源码里没有任何可发布的 @Mod 类(唯一三处 @Mod 注解在 test/ 包中,project.gradle 默认把它们从 jar 里排除)。宿主 mod 必须在构造期调用 MCLib.init() 来启动它。

基本信息

属性 值
类型 库入口 / 生命周期引导
触发条件 宿主 mod 的 FMLConstructionEvent(@EventHandler)中调用 MCLib.init()
Gradle modid makamys.mclib(仅用于 archivesBaseName 和 srgExtra 重定位,不是 Forge 注册的 modid)
Minecraft 1.7.10 / Forge 10.13.4.1614-1.7.10
发布版本 0.3.7.7(publish/version.txt)

MCLib 主类

包路径 makamys.mclib.core。

成员 签名 / 值 说明
版本号 public static final String VERSION = "@VERSION@" 构建期由 publish/get_version.py 替换;运行期若仍为 @...@ 会被当作 Integer.MAX_VALUE 参与版本仲裁(见 共享状态与版本仲裁)
资源版本 public static final String RESOURCES_VERSION = "v0_3_7" 决定从 classpath 读取哪个版本的证书/HTML 模板目录
单例 public static MCLib instance 由 init() 赋值
面向宿主 mod 的日志 public static Logger LOGGER,初始化为 "mclib(<宿主modid>)" 构造时改名为 mclib(...),方便在多 mod 环境下分辨日志来源
库自身日志 public static final Logger GLOGGER = LogManager.getLogger("mclib") 固定名
FML 母总线 public static EventBus FML_MASTER 从 LoadController.masterChannel 反射取出(见 反射访问点)
方法 行为
MCLib.init() 首次调用时转发到 init(true)
MCLib.init(boolean subscribe) 无条件新建 MCLib 实例并覆盖 instance
MCLib(boolean subscribe) 注册到 SharedLibHelper;subscribe = true 时反射拿到 LoadController.masterChannel 并把自己注册进去,以便接收 FML 生命周期事件
onPreInit(FMLPreInitializationEvent) @Subscribe(Guava EventBus)。若本副本是全场最新版本则调用 InternalModules.sloppyDepLoader.preInit(),随后 TaskQueue.consume(LoaderState.PREINITIALIZATION, instance)

subscribe = false 的用途:库不需要自动接收 FML 事件时(例如仅使用静态工具类),可以不窃取 masterChannel。此时 onPreInit 必须由宿主 mod 手动调用,否则依赖 TaskQueue 的 AssetDirector 预初始化任务不会执行。

反射失败时 LOGGER.error 提示:“状态变更事件处理器将必须由你的 mod 手动调用”。

TaskQueue

包路径 makamys.mclib.core。让宿主 mod 把工作推迟到某个 LoaderState 执行,且多个 mod 提交同名任务时由版本更高的一方胜出。

存储结构为 LoaderState -> (taskName -> (version, owner, task)),底层 map 通过 共享状态 机制放在 Launch.blackboard 的 "TaskQueue" / "queuedTasks" 键下。

方法 说明
enqueueTask(LoaderState state, String taskName, Runnable task, ComparableVersion version) 若已存在同名任务,只有当新版本 compareTo 更大时才覆盖;owner 记为 MCLib.instance
enqueueTask(LoaderState state, String taskName, Runnable runnable) 便捷重载,版本取 new ComparableVersion(MCLib.VERSION)
consume(LoaderState state, Object owner)(包私有) 只运行 owner 为当前 MCLib.instance 的任务,并从 map 中移除。owner 判等用引用相等

AssetDirectorAPI 的静态初始化块会往 LoaderState.PREINITIALIZATION 排一个名为 "AssetDirectorPreinit" 的任务。

模块持有者

类 可见性 成员
MCLibModules public public static UpdateCheckAPI updateCheckAPI — 唯一面向外部 mod 的共享模块入口
InternalModules 包私有 public static SloppyDepLoader sloppyDepLoader — 库内部用

两者都在 static { SharedLibHelper.shareifyClass(...); } 中把静态字段替换成 cglib 代理,因此旧版本副本的调用会被重定向到最新副本。

初始化契约

宿主 mod 侧的两步(对应仓库内 updatecheck/test/UCLTest.java 的写法):

  1. 构造事件里 MCLib.init();
  2. 预初始化事件里通过 MCLibModules.updateCheckAPI 提交任务。

静态工具类(SloppyDepLoader 等)无需初始化即可直接调静态方法。

相关条目