From b95119bb0eb5d1118b7f8e79acb83633c5d78f11 Mon Sep 17 00:00:00 2001 From: huanghongxun Date: Mon, 20 Sep 2021 21:55:07 +0800 Subject: [PATCH] feat: show more friendly message if installing mod in InstallerListPage. Closes #996. --- .../resources/assets/lang/I18N.properties | 2 +- .../resources/assets/lang/I18N_zh.properties | 2 +- .../assets/lang/I18N_zh_CN.properties | 2 +- .../jackhuang/hmcl/mod/FabricModMetadata.java | 4 +- .../hmcl/mod/ForgeNewModMetadata.java | 4 +- .../hmcl/mod/ForgeOldModMetadata.java | 4 +- .../jackhuang/hmcl/mod/LiteModMetadata.java | 4 +- .../java/org/jackhuang/hmcl/mod/ModInfo.java | 12 ++-- .../org/jackhuang/hmcl/mod/ModManager.java | 61 +++++++++++++------ .../org/jackhuang/hmcl/mod/PackMcMeta.java | 4 +- 10 files changed, 63 insertions(+), 36 deletions(-) diff --git a/HMCL/src/main/resources/assets/lang/I18N.properties b/HMCL/src/main/resources/assets/lang/I18N.properties index c2f242f5c..52f0659d9 100644 --- a/HMCL/src/main/resources/assets/lang/I18N.properties +++ b/HMCL/src/main/resources/assets/lang/I18N.properties @@ -346,7 +346,7 @@ install.failed=Version failed to install install.failed.downloading=Failed to install due to some files not downloaded successfully install.failed.downloading.detail=Failed to download file: %s install.failed.downloading.timeout=Timed out while downloading the file: %s -install.failed.install_online=Unable to recognize the provided installer file +install.failed.install_online=Unable to recognize the provided installer file. If you are installing a mod, go to "Mods" page. install.failed.malformed=The files just downloaded a moment ago is malformed. You may switch to other download provider to resolve this problem. install.failed.optifine_conflict=Fabric, OptiFine and Forge are installed simultaneously on Minecraft 1.13 install.failed.version_mismatch=The library requires the game version %s, but the actual version is %s. diff --git a/HMCL/src/main/resources/assets/lang/I18N_zh.properties b/HMCL/src/main/resources/assets/lang/I18N_zh.properties index 49a9e2a53..a330be621 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh.properties @@ -346,7 +346,7 @@ install.failed=安裝失敗 install.failed.downloading=安裝失敗,部分檔案未能完成下載 install.failed.downloading.detail=未能下載檔案: %s install.failed.downloading.timeout=下載逾時: %s -install.failed.install_online=無法識別要安裝的軟體 +install.failed.install_online=無法識別要安裝的軟體。如果你要安裝 Mod,你需要在模組管理頁面安裝模組。 install.failed.malformed=剛才下載的檔案格式損壞。您可以切換到其他下載來源以解決此問題。 install.failed.optifine_conflict=暫不支援 OptiFine 與 Forge 同時安裝在 Minecraft 1.13 上 install.failed.version_mismatch=該軟體需要的遊戲版本為 %s,但實際的遊戲版本為 %s。 diff --git a/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties b/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties index 33295d5ea..4a2ecc30a 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties @@ -346,7 +346,7 @@ install.failed=安装失败 install.failed.downloading=安装失败,部分文件未能完成下载 install.failed.downloading.detail=未能下载文件:%s install.failed.downloading.timeout=下载超时:%s -install.failed.install_online=无法识别要安装的软件 +install.failed.install_online=无法识别要安装的软件。如果你要安装 Mod,你需要在模组管理页面安装模组。 install.failed.malformed=下载的文件格式损坏。您可以切换到其他下载源来解决此问题。 install.failed.optifine_conflict=暂不支持 OptiFine, Fabric 与 Forge 同时安装在 Minecraft 1.13 及以上版本 install.failed.version_mismatch=该软件需要的游戏版本为 %s,但实际的游戏版本为 %s。 diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/FabricModMetadata.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/FabricModMetadata.java index 16697bff4..2ad52b3a6 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/FabricModMetadata.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/FabricModMetadata.java @@ -64,14 +64,14 @@ public final class FabricModMetadata { this.contact = contact; } - public static ModInfo fromFile(ModManager modManager, File modFile) throws IOException, JsonParseException { + public static ModInfo fromFile(File modFile) throws IOException, JsonParseException { try (FileSystem fs = CompressingUtils.createReadOnlyZipFileSystem(modFile.toPath())) { Path mcmod = fs.getPath("fabric.mod.json"); if (Files.notExists(mcmod)) throw new IOException("File " + modFile + " is not a Fabric mod."); FabricModMetadata metadata = JsonUtils.fromNonNullJson(FileUtils.readText(mcmod), FabricModMetadata.class); String authors = metadata.authors == null ? "" : metadata.authors.stream().map(author -> author.name).collect(Collectors.joining(", ")); - return new ModInfo(modManager, modFile, metadata.id, metadata.name, new ModInfo.Description(metadata.description), + return new ModInfo(modFile, metadata.id, metadata.name, new ModInfo.Description(metadata.description), authors, metadata.version, "", metadata.contact != null ? metadata.contact.getOrDefault("homepage", "") : "", metadata.icon); } } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/ForgeNewModMetadata.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/ForgeNewModMetadata.java index e1651a26c..c11f1fce6 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/ForgeNewModMetadata.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/ForgeNewModMetadata.java @@ -116,7 +116,7 @@ public final class ForgeNewModMetadata { } } - public static ModInfo fromFile(ModManager modManager, File modFile) throws IOException, JsonParseException { + public static ModInfo fromFile(File modFile) throws IOException, JsonParseException { try (FileSystem fs = CompressingUtils.createReadOnlyZipFileSystem(modFile.toPath())) { Path modstoml = fs.getPath("META-INF/mods.toml"); if (Files.notExists(modstoml)) @@ -135,7 +135,7 @@ public final class ForgeNewModMetadata { LOG.log(Level.WARNING, "Failed to parse MANIFEST.MF in file " + modFile.getPath()); } } - return new ModInfo(modManager, modFile, mod.getModId(), mod.getDisplayName(), new ModInfo.Description(mod.getDescription()), + return new ModInfo(modFile, mod.getModId(), mod.getDisplayName(), new ModInfo.Description(mod.getDescription()), mod.getAuthors(), mod.getVersion().replace("${file.jarVersion}", jarVersion), "", mod.getDisplayURL(), metadata.getLogoFile()); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/ForgeOldModMetadata.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/ForgeOldModMetadata.java index 3ec883a1a..25d1930cd 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/ForgeOldModMetadata.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/ForgeOldModMetadata.java @@ -120,7 +120,7 @@ public final class ForgeOldModMetadata { return authors; } - public static ModInfo fromFile(ModManager modManager, File modFile) throws IOException, JsonParseException { + public static ModInfo fromFile(File modFile) throws IOException, JsonParseException { try (FileSystem fs = CompressingUtils.createReadOnlyZipFileSystem(modFile.toPath())) { Path mcmod = fs.getPath("mcmod.info"); if (Files.notExists(mcmod)) @@ -138,7 +138,7 @@ public final class ForgeOldModMetadata { authors = String.join(", ", metadata.getAuthorList()); if (StringUtils.isBlank(authors)) authors = metadata.getCredits(); - return new ModInfo(modManager, modFile, metadata.getModId(), metadata.getName(), new ModInfo.Description(metadata.getDescription()), + return new ModInfo(modFile, metadata.getModId(), metadata.getName(), new ModInfo.Description(metadata.getDescription()), authors, metadata.getVersion(), metadata.getGameVersion(), StringUtils.isBlank(metadata.getUrl()) ? metadata.getUpdateUrl() : metadata.url, metadata.getLogoFile()); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/LiteModMetadata.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/LiteModMetadata.java index 113cbc900..27a062050 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/LiteModMetadata.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/LiteModMetadata.java @@ -108,7 +108,7 @@ public final class LiteModMetadata { return updateURI; } - public static ModInfo fromFile(ModManager modManager, File modFile) throws IOException, JsonParseException { + public static ModInfo fromFile(File modFile) throws IOException, JsonParseException { try (ZipFile zipFile = new ZipFile(modFile)) { ZipEntry entry = zipFile.getEntry("litemod.json"); if (entry == null) @@ -116,7 +116,7 @@ public final class LiteModMetadata { LiteModMetadata metadata = JsonUtils.GSON.fromJson(IOUtils.readFullyAsString(zipFile.getInputStream(entry)), LiteModMetadata.class); if (metadata == null) throw new IOException("Mod " + modFile + " `litemod.json` is malformed."); - return new ModInfo(modManager, modFile, null, metadata.getName(), new ModInfo.Description(metadata.getDescription()), metadata.getAuthor(), + return new ModInfo(modFile, null, metadata.getName(), new ModInfo.Description(metadata.getDescription()), metadata.getAuthor(), metadata.getVersion(), metadata.getGameVersion(), metadata.getUpdateURI(), ""); } } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/ModInfo.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/ModInfo.java index ce4bb68ed..e07264a70 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/ModInfo.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/ModInfo.java @@ -49,11 +49,11 @@ public final class ModInfo implements Comparable { private final String logoPath; private final BooleanProperty activeProperty; - public ModInfo(ModManager modManager, File file, String id, String name, Description description) { - this(modManager, file, id, name, description, "", "", "", "", ""); + public ModInfo(File file, String id, String name, Description description) { + this(file, id, name, description, "", "", "", "", ""); } - public ModInfo(ModManager modManager, File file, String id, String name, Description description, String authors, String version, String gameVersion, String url, String logoPath) { + public ModInfo(File file, String id, String name, Description description, String authors, String version, String gameVersion, String url, String logoPath) { this.file = file.toPath(); this.id = id; this.name = name; @@ -64,16 +64,16 @@ public final class ModInfo implements Comparable { this.url = url; this.logoPath = logoPath; - activeProperty = new SimpleBooleanProperty(this, "active", !modManager.isDisabled(file)) { + activeProperty = new SimpleBooleanProperty(this, "active", !ModManager.isDisabled(file)) { @Override protected void invalidated() { Path path = ModInfo.this.file.toAbsolutePath(); try { if (get()) - ModInfo.this.file = modManager.enableMod(path); + ModInfo.this.file = ModManager.enableMod(path); else - ModInfo.this.file = modManager.disableMod(path); + ModInfo.this.file = ModManager.disableMod(path); } catch (IOException e) { Logging.LOG.log(Level.SEVERE, "Unable to invert state of mod file " + path, e); } 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 ea57739ab..e431b701f 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/ModManager.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/ModManager.java @@ -19,16 +19,15 @@ package org.jackhuang.hmcl.mod; import org.jackhuang.hmcl.game.GameRepository; import org.jackhuang.hmcl.util.StringUtils; +import org.jackhuang.hmcl.util.io.CompressingUtils; import org.jackhuang.hmcl.util.io.FileUtils; import org.jackhuang.hmcl.util.versioning.VersionNumber; import java.io.File; import java.io.IOException; -import java.nio.file.DirectoryStream; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.StandardCopyOption; -import java.util.*; +import java.nio.file.*; +import java.util.Collection; +import java.util.TreeSet; public final class ModManager { private final GameRepository repository; @@ -61,29 +60,29 @@ public final class ModManager { } } - public ModInfo getModInfo(File modFile) { + public static ModInfo getModInfo(File modFile) { File file = isDisabled(modFile) ? new File(modFile.getAbsoluteFile().getParentFile(), FileUtils.getNameWithoutExtension(modFile)) : modFile; String description, extension = FileUtils.getExtension(file); switch (extension) { case "zip": case "jar": try { - return ForgeOldModMetadata.fromFile(this, modFile); + return ForgeOldModMetadata.fromFile(modFile); } catch (Exception ignore) { } try { - return ForgeNewModMetadata.fromFile(this, modFile); + return ForgeNewModMetadata.fromFile(modFile); } catch (Exception ignore) { } try { - return FabricModMetadata.fromFile(this, modFile); + return FabricModMetadata.fromFile(modFile); } catch (Exception ignore) { } try { - return PackMcMeta.fromFile(this, modFile); + return PackMcMeta.fromFile(modFile); } catch (Exception ignore) { } @@ -91,7 +90,7 @@ public final class ModManager { break; case "litemod": try { - return LiteModMetadata.fromFile(this, modFile); + return LiteModMetadata.fromFile(modFile); } catch (Exception ignore) { description = "LiteLoader Mod"; } @@ -99,7 +98,7 @@ public final class ModManager { default: throw new IllegalArgumentException("File " + modFile + " is not a mod file."); } - return new ModInfo(this, modFile, null, FileUtils.getNameWithoutExtension(modFile), new ModInfo.Description(description)); + return new ModInfo(modFile, null, FileUtils.getNameWithoutExtension(modFile), new ModInfo.Description(description)); } public void refreshMods() throws IOException { @@ -130,7 +129,7 @@ public final class ModManager { } public void addMod(File file) throws IOException { - if (!isFileMod(file)) + if (!isFileNameMod(file)) throw new IllegalArgumentException("File " + file + " is not a valid mod file."); if (!loaded) @@ -152,31 +151,59 @@ public final class ModManager { } } - public Path disableMod(Path file) throws IOException { + public static Path disableMod(Path file) throws IOException { Path disabled = file.getParent().resolve(StringUtils.addSuffix(FileUtils.getName(file), DISABLED_EXTENSION)); if (Files.exists(file)) Files.move(file, disabled, StandardCopyOption.REPLACE_EXISTING); return disabled; } - public Path enableMod(Path file) throws IOException { + public static Path enableMod(Path file) throws IOException { Path enabled = file.getParent().resolve(StringUtils.removeSuffix(FileUtils.getName(file), DISABLED_EXTENSION)); if (Files.exists(file)) Files.move(file, enabled, StandardCopyOption.REPLACE_EXISTING); return enabled; } - public boolean isDisabled(File file) { + public static boolean isDisabled(File file) { return file.getPath().endsWith(DISABLED_EXTENSION); } - public boolean isFileMod(File file) { + public static boolean isFileNameMod(File file) { String name = file.getName(); if (isDisabled(file)) name = FileUtils.getNameWithoutExtension(file); return name.endsWith(".zip") || name.endsWith(".jar") || name.endsWith(".litemod"); } + public static boolean isFileMod(Path modFile) { + try (FileSystem fs = CompressingUtils.createReadOnlyZipFileSystem(modFile)) { + if (Files.exists(fs.getPath("mcmod.info")) || Files.exists(fs.getPath("META-INF/mods.toml"))) { + // Forge mod + return true; + } + + if (Files.exists(fs.getPath("fabric.mod.json"))) { + // Fabric mod + return true; + } + + if (Files.exists(fs.getPath("litemod.json"))) { + // Liteloader mod + return true; + } + + if (Files.exists(fs.getPath("pack.mcmeta"))) { + // resource pack, data pack + return true; + } + + return false; + } catch (IOException e) { + return false; + } + } + /** * Check if "mods" directory has mod file named "fileName" no matter the mod is disabled or not * diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/PackMcMeta.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/PackMcMeta.java index 7bf970049..44138751f 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/PackMcMeta.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/PackMcMeta.java @@ -144,13 +144,13 @@ public class PackMcMeta implements Validation { } } - public static ModInfo fromFile(ModManager modManager, File modFile) throws IOException, JsonParseException { + public static ModInfo fromFile(File modFile) throws IOException, JsonParseException { try (FileSystem fs = CompressingUtils.createReadOnlyZipFileSystem(modFile.toPath())) { Path mcmod = fs.getPath("pack.mcmeta"); if (Files.notExists(mcmod)) throw new IOException("File " + modFile + " is not a resource pack."); PackMcMeta metadata = JsonUtils.fromNonNullJson(FileUtils.readText(mcmod), PackMcMeta.class); - return new ModInfo(modManager, modFile, null, FileUtils.getNameWithoutExtension(modFile), metadata.pack.description, "", "", "", "", ""); + return new ModInfo(modFile, null, FileUtils.getNameWithoutExtension(modFile), metadata.pack.description, "", "", "", "", ""); } } }