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 装载流程改为:

  1. 使用 ParallellModDiscoverer 替代串行 ModDiscoverer
  2. 用 ModCandidateV2Sorter 进行 API 拓扑排序 + 重复去重
  3. 把 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 索引。

相关条目