SloppyDepLoader
可选依赖的自动下载器。与 CodeChickenCore 的 DepLoader 不同,依赖找不到时不会退出游戏——名字里的 “sloppy” 就是这个意思。类注释还写明它本身"非常不原创,主要抄自 CodeChickenCore,而后者又主要抄自 FML",致谢 ChickenBones 和 cpw。
基本信息
| 属性 | 值 |
|---|---|
| 类型 | 共享模块 / 依赖下载 |
| 命名空间常量 | SloppyDepLoader.NS = "SloppyDepLoader" |
| 日志名 | "SloppyDepLoader" |
| 配置文件 | 有:config/sloppydeploader.cfg,见 配置文件 |
| 触发 | pre-init,由 MCLib 核心 在"本副本最新"时调用 preInit() |
公共 API
| 成员 | 签名 | 说明 |
|---|---|---|
SloppyDepLoaderAPI.addDependenciesForMod |
public static void addDependenciesForMod(String modid, SloppyDependency... sloppyDependencies) |
把依赖序列化成 ; 分隔的字符串存进共享 map。可在 pre-init 之前的任意时候调用(通常放在 static 块里) |
SloppyDepLoader.preInit |
public void preInit() |
若无人使用直接返回;否则重载配置,enabled 为真时逐条解析并 load() |
preInit 内部按 , 再切 5 段构造 SloppyDependency(用 Arrays.copyOf(k.split(","), 5),不足自动补 null)。
SloppyDependency
包路径 makamys.mclib.sloppydeploader。五个参数全为包私有字段。
| 参数 | 字段 | 说明 |
|---|---|---|
| 仓库 URL | repo |
依赖 jar 的发布地址 |
| 文件名 | filename |
需要解析出版本号的文件名 |
| 探测类 | testClass |
存在则说明依赖已装,跳过下载 |
| 开发版文件名 | dev(Optional) |
非混淆环境(可读到 net.minecraft.world.World 的字节码)时替换 filename 使用 |
| 文件名正则 | pattern(Optional) |
缺省用 (\w+).*?([\d\.]+)[-\w]*\.[^\d]+ |
| 构造函数 / 方法 | 说明 |
|---|---|
SloppyDependency(String[] params) |
取 5 段 |
SloppyDependency(String repo, String filename, String testClass, String dev, String pattern) |
完整形式;空串转 Optional.empty() |
SloppyDependency(String repo, String filename, String testClass) |
dev/pattern 为 null |
serializeToString() |
String.join(",", {repo, filename, testClass, dev, pattern}),空 Optional 写成空串 |
toString() |
SloppyDependency{<filename> @ <repo>} |
版本号解析
SloppyDepLoader.VersionedFile 持有 public final Pattern pattern、public final String filename、public final ComparableVersion version、public final String name。构造时用正则匹配文件名,捕获组 1 为名字、组 2 为版本;匹配不上则 name 和 version 都为 null,matches() 返回 false。
文件名格式非法时 addSloppyDep 抛 RuntimeException("Invalid filename format for dependency: " + filename)。自定义正则语法错误时 LOGGER.error("Invalid filename pattern: " + ...) 并退回缺省正则。
版本合并规则
DepLoadInst.addDep 调 mergeNew,同名依赖取版本更高者,coreLib 标志取两者或。已存在更新版本时 LOGGER.trace("Not adding dependency ... because a newer version of it has been added already")。
checkExisting 扫描两个目录:mods/ 和 mods/<mcVer>/(后者在构造时 mkdirs)。
| 情况 | 行为 |
|---|---|
在 mods/ 找到但搬移失败 |
deleteMod(f) 删除后继续 |
目录版本 cmp < 0(比请求的旧) |
LOGGER.info("Deleted old version ..."),删除并触发下载 |
目录版本 cmp > 0(比请求的新) |
LOGGER.warn("Warning: version of <name>, <版本> is newer than request <版本>"),直接采用现有文件 |
| 相等 | 视为已满足 |
下载落到 mods/<mcVer>/ 下。
已有依赖的探测
addSloppyDep 在加入队列前先判断:
- 用
LaunchClassLoader.getClassBytes("net.minecraft.world.World")是否为 null 判断是否混淆环境;非混淆时才启用dev文件名。 - 用
SloppyDepLoader.class.getResource("/" + testClass.replace('.', '/') + ".class")探测依赖是否已存在;存在则LOGGER.trace("Skipping dependency ... because test class ... is present")直接返回。
下载
| 项 | 值 |
|---|---|
| 下载缓冲 | ByteBuffer.allocateDirect(1 << 23)(8 MiB,静态共享) |
| 超时 | 连接与读取各 5000 ms |
| User-Agent | 源码写成 "" + owner + " Downloader",owner 是常量 "Sloppy DepLoader",运行时拼成 Sloppy DepLoader Downloader |
| 大小上限 | 超过缓冲区容量直接抛异常并判定"下载无效" |
| 失败处理 | 删除半成品文件并 LOGGER.error("A download error occured downloading ..."),不抛异常、不弹窗、不退出 |
| 进度显示 | 用 IDownloadDisplay 接口;实际代码里恒用 DummyDownloader(全空实现),即静默下载 |
IDownloadDisplay 接口方法:resetProgress(int sizeGuess)、setPokeThread(Thread)、updateProgress(int fullLength)、shouldStopIt()、updateProgressString(String, Object...)、makeDialog()、showErrorDialog(String name, String url)。DummyDownloader 是其全空实现。
并发
SloppyDepDownloadManager(包路径同 sloppydeploader):
| 成员 | 值 |
|---|---|
| 线程池 | new ThreadPoolExecutor(0, 1, 60, TimeUnit.SECONDS, workQueue),核心 0、上限 1 |
| 队列 | LinkedBlockingQueue<Runnable> |
enqueueDownload(Supplier task) |
CompletableFuture.supplyAsync(task, executor) 并收集到 futures |
allDone() |
所有 future 都 isDone() |
getDownloadedList() |
未全部完成时返回空 list;否则逐个 get(),异常映射为 null 后过滤掉 |
下载在独立线程池异步跑,pre-init 不阻塞;重启提示在 主菜单打开时 补。
内部类
| 类型 | 说明 |
|---|---|
SloppyDepLoader.DepLoadInst |
依赖集合与目录扫描的主体,构造时 MCLib.FML_MASTER.register(this) |
SloppyDepLoader.Dependency |
字段 public String url、public VersionedFile file、public String existing、public boolean coreLib、public boolean downloaded |
SloppyDepLoader.VersionedFile |
见上文 |
SloppyDepLoader.IDownloadDisplay / DummyDownloader |
进度显示抽象 |
SloppyDepLoader.DepLoadInst.SloppyDepDownloadTask |
内部类,实现 Supplier<String>,返回下载成功的文件名 |
coreLib 标志在本 fork 中实际恒为 false(addSloppyDep 里写死 boolean coreLib = false;),其注释描述的"coremod 必需依赖需立即加入 classpath"路径未实现。
使用示例(源码内 sloppydeploader/test/SDLTest.java)
示例 mod 在 static 块里先 SloppyDepLoaderAPI.addDependenciesForMod(...) 声明两个依赖(一个真实可下载的 jar、一个故意不存在的),再调 MCLib.init()。顺序很关键:依赖声明必须早于 pre-init。
相关条目
- 配置文件 -
sloppydeploader.cfg的两个选项 - GUI 注入 - 下载完成后的重启动提示
- MCLib 核心与任务队列 -
preInit()的调用时机 - 反射访问点 -
deleteMod反射的三个 JDK 字段