优化模组加载器检测机制 (#4622)

This commit is contained in:
Glavo
2025-10-07 16:26:26 +08:00
committed by GitHub
parent f1c8791961
commit 0988dc53cd
6 changed files with 98 additions and 43 deletions

View File

@@ -191,7 +191,7 @@ public final class LauncherHelper {
LaunchOptions launchOptions = repository.getLaunchOptions( LaunchOptions launchOptions = repository.getLaunchOptions(
selectedVersion, javaVersionRef.get(), profile.getGameDir(), javaAgents, javaArguments, scriptFile != null); selectedVersion, javaVersionRef.get(), profile.getGameDir(), javaAgents, javaArguments, scriptFile != null);
LOG.info("Here's the structure of game mod directory:\n" + FileUtils.printFileStructure(repository.getModManager(selectedVersion).getModsDirectory(), 10)); LOG.info("Here's the structure of game mod directory:\n" + FileUtils.printFileStructure(repository.getModsDirectory(selectedVersion), 10));
return new HMCLGameLauncher( return new HMCLGameLauncher(
repository, repository,

View File

@@ -149,6 +149,11 @@ public class DefaultGameRepository implements GameRepository {
return getVersionRoot(id).resolve("natives-" + platform); return getVersionRoot(id).resolve("natives-" + platform);
} }
@Override
public Path getModsDirectory(String id) {
return getRunDirectory(id).resolve("mods");
}
@Override @Override
public Path getVersionRoot(String id) { public Path getVersionRoot(String id) {
return getBaseDirectory().resolve("versions/" + id); return getBaseDirectory().resolve("versions/" + id);

View File

@@ -131,6 +131,12 @@ public interface GameRepository extends VersionProvider {
*/ */
Path getNativeDirectory(String id, Platform platform); Path getNativeDirectory(String id, Platform platform);
/// Get the directory for placing mod files.
///
/// @param id instance id
/// @return the mods directory
Path getModsDirectory(String id);
/** /**
* Get minecraft jar * Get minecraft jar
* *

View File

@@ -18,6 +18,7 @@
package org.jackhuang.hmcl.mod; package org.jackhuang.hmcl.mod;
import com.google.gson.JsonParseException; import com.google.gson.JsonParseException;
import org.jackhuang.hmcl.download.LibraryAnalyzer;
import org.jackhuang.hmcl.game.GameRepository; import org.jackhuang.hmcl.game.GameRepository;
import org.jackhuang.hmcl.mod.modinfo.*; import org.jackhuang.hmcl.mod.modinfo.*;
import org.jackhuang.hmcl.util.Pair; import org.jackhuang.hmcl.util.Pair;
@@ -30,34 +31,39 @@ import java.io.IOException;
import java.nio.file.*; import java.nio.file.*;
import java.util.*; import java.util.*;
import static org.jackhuang.hmcl.util.Pair.pair;
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
public final class ModManager { public final class ModManager {
@FunctionalInterface @FunctionalInterface
private interface ModMetadataReader { private interface ModMetadataReader {
LocalModFile fromFile(ModManager modManager, Path modFile, FileSystem fs) throws IOException, JsonParseException; LocalModFile fromFile(ModManager modManager, Path modFile, FileSystem fs) throws IOException, JsonParseException;
} }
private static final Map<String, Pair<ModMetadataReader[], String>> READERS; private static final Map<String, List<Pair<ModMetadataReader, Set<ModLoaderType>>>> READERS;
static { static {
TreeMap<String, Pair<ModMetadataReader[], String>> readers = new TreeMap<>(); var map = new HashMap<String, List<Pair<ModMetadataReader, Set<ModLoaderType>>>>();
readers.put("zip", Pair.pair(new ModMetadataReader[]{ var zipReaders = List.<Pair<ModMetadataReader, Set<ModLoaderType>>>of(
ForgeOldModMetadata::fromFile, pair(ForgeNewModMetadata::fromFile, EnumSet.of(ModLoaderType.FORGE, ModLoaderType.NEO_FORGED)),
ForgeNewModMetadata::fromFile, pair(ForgeOldModMetadata::fromFile, EnumSet.of(ModLoaderType.FORGE)),
FabricModMetadata::fromFile, pair(FabricModMetadata::fromFile, EnumSet.of(ModLoaderType.FABRIC)),
QuiltModMetadata::fromFile, pair(QuiltModMetadata::fromFile, EnumSet.of(ModLoaderType.QUILT)),
PackMcMeta::fromFile, pair(PackMcMeta::fromFile, EnumSet.of(ModLoaderType.PACK))
}, "")); );
readers.put("jar", readers.get("zip"));
readers.put("litemod", Pair.pair(new ModMetadataReader[]{ map.put("zip", zipReaders);
LiteModMetadata::fromFile map.put("jar", zipReaders);
}, "LiteLoader Mod")); map.put("litemod", List.of(pair(LiteModMetadata::fromFile, EnumSet.of(ModLoaderType.LITE_LOADER))));
READERS = Collections.unmodifiableMap(readers);
READERS = map;
} }
private final GameRepository repository; private final GameRepository repository;
private final String id; private final String id;
private final TreeSet<LocalModFile> localModFiles = new TreeSet<>(); private final TreeSet<LocalModFile> localModFiles = new TreeSet<>();
private final HashMap<LocalMod, LocalMod> localMods = new HashMap<>(); private final HashMap<LocalMod, LocalMod> localMods = new HashMap<>();
private LibraryAnalyzer analyzer;
private boolean loaded = false; private boolean loaded = false;
@@ -70,12 +76,12 @@ public final class ModManager {
return repository; return repository;
} }
public String getVersion() { public String getInstanceId() {
return id; return id;
} }
public Path getModsDirectory() { public Path getModsDirectory() {
return repository.getRunDirectory(id).resolve("mods"); return repository.getModsDirectory(id);
} }
public LocalMod getLocalMod(String id, ModLoaderType modLoaderType) { public LocalMod getLocalMod(String id, ModLoaderType modLoaderType) {
@@ -83,44 +89,82 @@ public final class ModManager {
} }
private void addModInfo(Path file) { private void addModInfo(Path file) {
try { String fileName = StringUtils.removeSuffix(FileUtils.getName(file), DISABLED_EXTENSION, OLD_EXTENSION);
LocalModFile localModFile = getModInfo(file);
if (!localModFile.isOld()) {
localModFiles.add(localModFile);
}
} catch (IllegalArgumentException ignore) {
}
}
public LocalModFile getModInfo(Path modFile) {
String fileName = StringUtils.removeSuffix(FileUtils.getName(modFile), DISABLED_EXTENSION, OLD_EXTENSION);
String extension = fileName.substring(fileName.lastIndexOf(".") + 1); String extension = fileName.substring(fileName.lastIndexOf(".") + 1);
Pair<ModMetadataReader[], String> currentReader = READERS.get(extension);
if (currentReader == null) { List<Pair<ModMetadataReader, Set<ModLoaderType>>> readersMap = READERS.get(extension);
throw new IllegalArgumentException("File " + modFile + " is not a mod file."); if (readersMap == null) {
// Is not a mod file.
return;
} }
try (FileSystem fs = CompressingUtils.createReadOnlyZipFileSystem(modFile)) { Set<ModLoaderType> modLoaderTypes = analyzer.getModLoaders();
for (ModMetadataReader reader : currentReader.getKey()) {
var supportedReaders = new ArrayList<ModMetadataReader>();
var unsupportedReaders = new ArrayList<ModMetadataReader>();
for (Pair<ModMetadataReader, Set<ModLoaderType>> reader : readersMap) {
boolean supported = false;
for (ModLoaderType type : reader.getValue()) {
if (modLoaderTypes.contains(type)) {
supported = true;
break;
}
}
if (supported) {
supportedReaders.add(reader.getKey());
} else {
unsupportedReaders.add(reader.getKey());
}
}
LocalModFile modInfo = null;
try (FileSystem fs = CompressingUtils.createReadOnlyZipFileSystem(file)) {
for (ModMetadataReader reader : supportedReaders) {
try { try {
return reader.fromFile(this, modFile, fs); modInfo = reader.fromFile(this, file, fs);
break;
} catch (Exception ignore) { } catch (Exception ignore) {
} }
} }
} catch (Exception ignored) {
if (modInfo == null) {
for (ModMetadataReader reader : unsupportedReaders) {
try {
modInfo = reader.fromFile(this, file, fs);
break;
} catch (Exception ignore) {
}
}
}
} catch (Exception e) {
LOG.warning("Failed to open mod file " + file, e);
} }
return new LocalModFile(this, if (modInfo == null) {
getLocalMod(FileUtils.getNameWithoutExtension(modFile), ModLoaderType.UNKNOWN), String fileNameWithoutExtension = FileUtils.getNameWithoutExtension(file);
modFile,
FileUtils.getNameWithoutExtension(modFile), modInfo = new LocalModFile(this,
new LocalModFile.Description(currentReader.getValue()) getLocalMod(fileNameWithoutExtension, ModLoaderType.UNKNOWN),
); file,
fileNameWithoutExtension,
new LocalModFile.Description("litemod".equals(extension) ? "LiteLoader Mod" : "")
);
}
if (!modInfo.isOld()) {
localModFiles.add(modInfo);
}
} }
public void refreshMods() throws IOException { public void refreshMods() throws IOException {
localModFiles.clear(); localModFiles.clear();
localMods.clear(); localMods.clear();
analyzer = LibraryAnalyzer.analyze(getRepository().getResolvedPreservingPatchesVersion(id), null);
if (Files.isDirectory(getModsDirectory())) { if (Files.isDirectory(getModsDirectory())) {
try (DirectoryStream<Path> modsDirectoryStream = Files.newDirectoryStream(getModsDirectory())) { try (DirectoryStream<Path> modsDirectoryStream = Files.newDirectoryStream(getModsDirectory())) {
for (Path subitem : modsDirectoryStream) { for (Path subitem : modsDirectoryStream) {

View File

@@ -138,7 +138,7 @@ public final class CurseCompletionTask extends Task<Void> {
.collect(Collectors.toList())); .collect(Collectors.toList()));
JsonUtils.writeToJsonFile(root.resolve("manifest.json"), newManifest); JsonUtils.writeToJsonFile(root.resolve("manifest.json"), newManifest);
Path versionRoot = repository.getVersionRoot(modManager.getVersion()); Path versionRoot = repository.getVersionRoot(modManager.getInstanceId());
Path resourcePacksRoot = versionRoot.resolve("resourcepacks"); Path resourcePacksRoot = versionRoot.resolve("resourcepacks");
Path shaderPacksRoot = versionRoot.resolve("shaderpacks"); Path shaderPacksRoot = versionRoot.resolve("shaderpacks");
finished.set(0); finished.set(0);

View File

@@ -273,7 +273,7 @@ public class McbbsModpackCompletionTask extends CompletableFutureTask<Void> {
@Nullable @Nullable
private Path getFilePath(McbbsModpackManifest.File file) { private Path getFilePath(McbbsModpackManifest.File file) {
if (file instanceof McbbsModpackManifest.AddonFile) { if (file instanceof McbbsModpackManifest.AddonFile) {
return modManager.getRepository().getRunDirectory(modManager.getVersion()).resolve(((McbbsModpackManifest.AddonFile) file).getPath()); return modManager.getRepository().getRunDirectory(modManager.getInstanceId()).resolve(((McbbsModpackManifest.AddonFile) file).getPath());
} else if (file instanceof McbbsModpackManifest.CurseFile) { } else if (file instanceof McbbsModpackManifest.CurseFile) {
String fileName = ((McbbsModpackManifest.CurseFile) file).getFileName(); String fileName = ((McbbsModpackManifest.CurseFile) file).getFileName();
if (fileName == null) return null; if (fileName == null) return null;