LoaderV2
基本信息
| 属性 | 值 |
|---|---|
| 类 | com.mitchej123.jarjar.fml.common.LoaderV2 |
| 父类 | cpw.mods.fml.common.Loader |
| MOD_COMPARATOR | nullsFirst(comparing(getId, CASE_INSENSITIVE_ORDER).thenComparing(getVersion).thenComparing(getNestLevel)) |
LoaderV2 替代 FML 原版 Loader,把 mod 装载流程改为:
- 使用
ParallellModDiscoverer替代串行ModDiscoverer - 用
ModCandidateV2Sorter进行 API 拓扑排序 + 重复去重 - 把
JarJar的metadataCollection路径与 jar 关联写回ASMDataTableV2
loadMods 流程
public void loadMods() {
progressBar = ProgressManager.push("Loading", 7);
progressBar.step("Constructing Mods");
initializeLoader();
mods = Lists.newArrayList();
namedMods = Maps.newHashMap();
this.modController = super.modController = new LoadController(this);
modController.transition(LoaderState.LOADING, false);
long startTime = System.nanoTime();
discoverer = identifyMods();
long endTime = System.nanoTime();
FMLLog.fine("Mod identification took %.2f seconds", (endTime - startTime) / 1e9);
ModAPIManager.INSTANCE.manageAPI(modClassLoader, discoverer);
disableRequestedMods();
modController.distributeStateMessage(FMLLoadEvent.class);
sortModList();
ModAPIManager.INSTANCE.cleanupAPIContainers(modController.getActiveModList());
ModAPIManager.INSTANCE.cleanupAPIContainers(mods);
mods = ImmutableList.copyOf(mods);
for (File nonMod : discoverer.getNonModLibs()) { ... addFile ... }
modController.transition(LoaderState.CONSTRUCTING, false);
modController.distributeStateMessage(LoaderState.CONSTRUCTING, modClassLoader, discoverer.getASMTable(), reverseDependencies);
List<ModContainer> modsCopy = Lists.newArrayList();
modsCopy.addAll(getActiveModList());
modsCopy.sort(Comparator.comparing(ModContainer::getModId));
// FMLLog mod signature data
progressBar.step("Initializing mods Phase 1");
modController.transition(LoaderState.PREINITIALIZATION, false);
}
identifyMods
public ModDiscoverer identifyMods() {
mods.add(new InjectedModContainer(mcp, new File("minecraft.jar")));
for (String cont : injectedContainers) {
final ModContainer mc = (ModContainer) Class.forName(cont, true, modClassLoader).getConstructor().newInstance();
mods.add(new InjectedModContainer(mc, mc.getSource()));
}
ParallellModDiscoverer discoverer = new ParallellModDiscoverer(canonicalModsDir, modClassLoader);
discoverer.discoverMods();
final ASMDataTable dataTable = discoverer.getASMTable();
final List<ModCandidateV2> modCandidates = discoverer.getModCandidates();
final List<File> nonModLibs = discoverer.getNonModLibs();
modCandidates.sort(Comparator.comparing(c -> c.getModContainer().getName(), String.CASE_INSENSITIVE_ORDER));
final List<SortableCandidate> sortableCandidates = Lists.newArrayList();
for (ModCandidateV2 candidate : modCandidates) {
final List<ModContainerWrapper> mods = candidate.getWrappedMods();
if (mods.isEmpty() && !candidate.isClasspath() && !candidate.containsAPIAnnotations()) {
nonModLibs.add(candidate.getModContainer());
} else {
if (mods.isEmpty() && candidate.containsAPIAnnotations()) {
sortableCandidates.add(new APIOnlyWrapper(candidate));
} else {
sortableCandidates.addAll(mods);
}
}
}
final Optional<List<SortableCandidate>> resolvedCandidates = new ModCandidateV2Sorter<>(sortableCandidates, MOD_COMPARATOR).resolve();
if (!resolvedCandidates.isPresent()) throw new RuntimeException("critical error during mod resolution");
final Set<ModCandidateV2> uniqueCandidates = new LinkedHashSet<>();
for (SortableCandidate sortable : resolvedCandidates.get()) {
if (sortable instanceof ModContainerWrapper wrapper) {
final ModContainer mod = wrapper.mod();
uniqueCandidates.add(wrapper.candidate());
dataTable.addContainer(mod);
mods.add(mod);
} else if (sortable instanceof APIOnlyWrapper apiWrapper) {
final ModCandidateV2 candidate = apiWrapper.candidate();
uniqueCandidates.add(candidate);
modClassLoader.addFile(candidate.getModContainer());
}
}
for (ModCandidateV2 candidate : uniqueCandidates) {
candidate.sendToTable(dataTable);
}
modCandidates.forEach(ModCandidateV2::releaseParsedData);
identifyDuplicates(mods);
namedMods = Maps.uniqueIndex(mods, new ModIdFunction());
FMLLog.info("Forge Mod Loader has identified %d mod%s to load", mods.size(), mods.size() != 1 ? "s" : "");
DiscoveryPool.shutdown();
return discoverer;
}
关键差异
| 流程 | 原版 | V2 |
|---|---|---|
| 发现 | new ModDiscoverer().discoverMods() 串行 |
new ParallellModDiscoverer(canonicalModsDir, modClassLoader).discoverMods() 并行 |
| 排序 | sortModList() |
ModCandidateV2Sorter(sortableCandidates, MOD_COMPARATOR).resolve() |
| ASMDataTable | new ASMDataTable() |
new ASMDataTableV2()(V2ConstructionReplacerTransformer 改写) |
| 候选 | List<ModCandidate> |
List<ModCandidateV2> 带 API/嵌套/tweaker 信息 |
| API-only jar | 视为 non-mod lib | 走 APIOnlyWrapper 加入 sort + classpath |
| 释放 | 立即 GC | releaseParsedData 批量释放 |
| DiscoveryPool | 不存在 | 末段 DiscoveryPool.shutdown() |
嵌套处理
sendToTable 通过 ModCandidateV2.sendToTable(table) 把每个 ASMModParserV2 的 @Mod 与 interface ASMData 注入 ASMDataTableV2,并通过 dataTable.addContainer(mod) 建立 ModContainer → ASMData 索引。
相关条目
- RFB 插件入口 - 整个 V2 链路触发处
- V2ConstructionReplacerTransformer - 替换
Loader.instance()为 V2 - CoreModManagerV2 - coremod 阶段的兄弟
- 并行 mod 发现 - 替代原版 discoverMods
- ModCandidateV2Sorter - 排序阶段
- DiscoveryPool 共享线程池 - 关闭时机