From 0988dc53cd347cb31cd83b0234168bf12928f5db Mon Sep 17 00:00:00 2001 From: Glavo Date: Tue, 7 Oct 2025 16:26:26 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=A8=A1=E7=BB=84=E5=8A=A0?= =?UTF-8?q?=E8=BD=BD=E5=99=A8=E6=A3=80=E6=B5=8B=E6=9C=BA=E5=88=B6=20(#4622?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../jackhuang/hmcl/game/LauncherHelper.java | 2 +- .../hmcl/game/DefaultGameRepository.java | 5 + .../jackhuang/hmcl/game/GameRepository.java | 6 + .../org/jackhuang/hmcl/mod/ModManager.java | 124 ++++++++++++------ .../hmcl/mod/curse/CurseCompletionTask.java | 2 +- .../mod/mcbbs/McbbsModpackCompletionTask.java | 2 +- 6 files changed, 98 insertions(+), 43 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/game/LauncherHelper.java b/HMCL/src/main/java/org/jackhuang/hmcl/game/LauncherHelper.java index ad1e6cd4e..54a547f83 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/game/LauncherHelper.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/game/LauncherHelper.java @@ -191,7 +191,7 @@ public final class LauncherHelper { LaunchOptions launchOptions = repository.getLaunchOptions( 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( repository, diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameRepository.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameRepository.java index f87f32799..df8f45326 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameRepository.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameRepository.java @@ -149,6 +149,11 @@ public class DefaultGameRepository implements GameRepository { return getVersionRoot(id).resolve("natives-" + platform); } + @Override + public Path getModsDirectory(String id) { + return getRunDirectory(id).resolve("mods"); + } + @Override public Path getVersionRoot(String id) { return getBaseDirectory().resolve("versions/" + id); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameRepository.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameRepository.java index 52a7cc1ef..e9049123a 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameRepository.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameRepository.java @@ -131,6 +131,12 @@ public interface GameRepository extends VersionProvider { */ 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 * diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/ModManager.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/ModManager.java index 987b4808b..beb9311ec 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/ModManager.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/ModManager.java @@ -18,6 +18,7 @@ package org.jackhuang.hmcl.mod; import com.google.gson.JsonParseException; +import org.jackhuang.hmcl.download.LibraryAnalyzer; import org.jackhuang.hmcl.game.GameRepository; import org.jackhuang.hmcl.mod.modinfo.*; import org.jackhuang.hmcl.util.Pair; @@ -30,34 +31,39 @@ import java.io.IOException; import java.nio.file.*; import java.util.*; +import static org.jackhuang.hmcl.util.Pair.pair; +import static org.jackhuang.hmcl.util.logging.Logger.LOG; + public final class ModManager { @FunctionalInterface private interface ModMetadataReader { LocalModFile fromFile(ModManager modManager, Path modFile, FileSystem fs) throws IOException, JsonParseException; } - private static final Map> READERS; + private static final Map>>> READERS; static { - TreeMap> readers = new TreeMap<>(); - readers.put("zip", Pair.pair(new ModMetadataReader[]{ - ForgeOldModMetadata::fromFile, - ForgeNewModMetadata::fromFile, - FabricModMetadata::fromFile, - QuiltModMetadata::fromFile, - PackMcMeta::fromFile, - }, "")); - readers.put("jar", readers.get("zip")); - readers.put("litemod", Pair.pair(new ModMetadataReader[]{ - LiteModMetadata::fromFile - }, "LiteLoader Mod")); - READERS = Collections.unmodifiableMap(readers); + var map = new HashMap>>>(); + var zipReaders = List.>>of( + pair(ForgeNewModMetadata::fromFile, EnumSet.of(ModLoaderType.FORGE, ModLoaderType.NEO_FORGED)), + pair(ForgeOldModMetadata::fromFile, EnumSet.of(ModLoaderType.FORGE)), + pair(FabricModMetadata::fromFile, EnumSet.of(ModLoaderType.FABRIC)), + pair(QuiltModMetadata::fromFile, EnumSet.of(ModLoaderType.QUILT)), + pair(PackMcMeta::fromFile, EnumSet.of(ModLoaderType.PACK)) + ); + + map.put("zip", zipReaders); + map.put("jar", zipReaders); + map.put("litemod", List.of(pair(LiteModMetadata::fromFile, EnumSet.of(ModLoaderType.LITE_LOADER)))); + + READERS = map; } private final GameRepository repository; private final String id; private final TreeSet localModFiles = new TreeSet<>(); private final HashMap localMods = new HashMap<>(); + private LibraryAnalyzer analyzer; private boolean loaded = false; @@ -70,12 +76,12 @@ public final class ModManager { return repository; } - public String getVersion() { + public String getInstanceId() { return id; } public Path getModsDirectory() { - return repository.getRunDirectory(id).resolve("mods"); + return repository.getModsDirectory(id); } public LocalMod getLocalMod(String id, ModLoaderType modLoaderType) { @@ -83,44 +89,82 @@ public final class ModManager { } private void addModInfo(Path file) { - try { - 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 fileName = StringUtils.removeSuffix(FileUtils.getName(file), DISABLED_EXTENSION, OLD_EXTENSION); String extension = fileName.substring(fileName.lastIndexOf(".") + 1); - Pair currentReader = READERS.get(extension); - if (currentReader == null) { - throw new IllegalArgumentException("File " + modFile + " is not a mod file."); + + List>> readersMap = READERS.get(extension); + if (readersMap == null) { + // Is not a mod file. + return; } - try (FileSystem fs = CompressingUtils.createReadOnlyZipFileSystem(modFile)) { - for (ModMetadataReader reader : currentReader.getKey()) { + Set modLoaderTypes = analyzer.getModLoaders(); + + var supportedReaders = new ArrayList(); + var unsupportedReaders = new ArrayList(); + + for (Pair> 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 { - return reader.fromFile(this, modFile, fs); + modInfo = reader.fromFile(this, file, fs); + break; } 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, - getLocalMod(FileUtils.getNameWithoutExtension(modFile), ModLoaderType.UNKNOWN), - modFile, - FileUtils.getNameWithoutExtension(modFile), - new LocalModFile.Description(currentReader.getValue()) - ); + if (modInfo == null) { + String fileNameWithoutExtension = FileUtils.getNameWithoutExtension(file); + + modInfo = new LocalModFile(this, + 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 { localModFiles.clear(); localMods.clear(); + + analyzer = LibraryAnalyzer.analyze(getRepository().getResolvedPreservingPatchesVersion(id), null); + if (Files.isDirectory(getModsDirectory())) { try (DirectoryStream modsDirectoryStream = Files.newDirectoryStream(getModsDirectory())) { for (Path subitem : modsDirectoryStream) { diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/curse/CurseCompletionTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/curse/CurseCompletionTask.java index 19c2306f0..c091a1c93 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/curse/CurseCompletionTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/curse/CurseCompletionTask.java @@ -138,7 +138,7 @@ public final class CurseCompletionTask extends Task { .collect(Collectors.toList())); 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 shaderPacksRoot = versionRoot.resolve("shaderpacks"); finished.set(0); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/mcbbs/McbbsModpackCompletionTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/mcbbs/McbbsModpackCompletionTask.java index 31cdcdd74..3ca07c225 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/mcbbs/McbbsModpackCompletionTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/mcbbs/McbbsModpackCompletionTask.java @@ -273,7 +273,7 @@ public class McbbsModpackCompletionTask extends CompletableFutureTask { @Nullable private Path getFilePath(McbbsModpackManifest.File file) { 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) { String fileName = ((McbbsModpackManifest.CurseFile) file).getFileName(); if (fileName == null) return null;