feat: show more friendly message if installing mod in InstallerListPage. Closes #996.

This commit is contained in:
huanghongxun
2021-09-20 21:55:07 +08:00
parent ae8600c882
commit b95119bb0e
10 changed files with 63 additions and 36 deletions

View File

@@ -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=Failed to install due to some files not downloaded successfully
install.failed.downloading.detail=Failed to download file: %s install.failed.downloading.detail=Failed to download file: %s
install.failed.downloading.timeout=Timed out while downloading the 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.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.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. install.failed.version_mismatch=The library requires the game version %s, but the actual version is %s.

View File

@@ -346,7 +346,7 @@ install.failed=安裝失敗
install.failed.downloading=安裝失敗,部分檔案未能完成下載 install.failed.downloading=安裝失敗,部分檔案未能完成下載
install.failed.downloading.detail=未能下載檔案: %s install.failed.downloading.detail=未能下載檔案: %s
install.failed.downloading.timeout=下載逾時: %s install.failed.downloading.timeout=下載逾時: %s
install.failed.install_online=無法識別要安裝的軟體 install.failed.install_online=無法識別要安裝的軟體。如果你要安裝 Mod你需要在模組管理頁面安裝模組。
install.failed.malformed=剛才下載的檔案格式損壞。您可以切換到其他下載來源以解決此問題。 install.failed.malformed=剛才下載的檔案格式損壞。您可以切換到其他下載來源以解決此問題。
install.failed.optifine_conflict=暫不支援 OptiFine 與 Forge 同時安裝在 Minecraft 1.13 上 install.failed.optifine_conflict=暫不支援 OptiFine 與 Forge 同時安裝在 Minecraft 1.13 上
install.failed.version_mismatch=該軟體需要的遊戲版本為 %s但實際的遊戲版本為 %s。 install.failed.version_mismatch=該軟體需要的遊戲版本為 %s但實際的遊戲版本為 %s。

View File

@@ -346,7 +346,7 @@ install.failed=安装失败
install.failed.downloading=安装失败,部分文件未能完成下载 install.failed.downloading=安装失败,部分文件未能完成下载
install.failed.downloading.detail=未能下载文件:%s install.failed.downloading.detail=未能下载文件:%s
install.failed.downloading.timeout=下载超时:%s install.failed.downloading.timeout=下载超时:%s
install.failed.install_online=无法识别要安装的软件 install.failed.install_online=无法识别要安装的软件。如果你要安装 Mod你需要在模组管理页面安装模组。
install.failed.malformed=下载的文件格式损坏。您可以切换到其他下载源来解决此问题。 install.failed.malformed=下载的文件格式损坏。您可以切换到其他下载源来解决此问题。
install.failed.optifine_conflict=暂不支持 OptiFine, Fabric 与 Forge 同时安装在 Minecraft 1.13 及以上版本 install.failed.optifine_conflict=暂不支持 OptiFine, Fabric 与 Forge 同时安装在 Minecraft 1.13 及以上版本
install.failed.version_mismatch=该软件需要的游戏版本为 %s但实际的游戏版本为 %s。 install.failed.version_mismatch=该软件需要的游戏版本为 %s但实际的游戏版本为 %s。

View File

@@ -64,14 +64,14 @@ public final class FabricModMetadata {
this.contact = contact; 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())) { try (FileSystem fs = CompressingUtils.createReadOnlyZipFileSystem(modFile.toPath())) {
Path mcmod = fs.getPath("fabric.mod.json"); Path mcmod = fs.getPath("fabric.mod.json");
if (Files.notExists(mcmod)) if (Files.notExists(mcmod))
throw new IOException("File " + modFile + " is not a Fabric mod."); throw new IOException("File " + modFile + " is not a Fabric mod.");
FabricModMetadata metadata = JsonUtils.fromNonNullJson(FileUtils.readText(mcmod), FabricModMetadata.class); FabricModMetadata metadata = JsonUtils.fromNonNullJson(FileUtils.readText(mcmod), FabricModMetadata.class);
String authors = metadata.authors == null ? "" : metadata.authors.stream().map(author -> author.name).collect(Collectors.joining(", ")); 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); authors, metadata.version, "", metadata.contact != null ? metadata.contact.getOrDefault("homepage", "") : "", metadata.icon);
} }
} }

View File

@@ -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())) { try (FileSystem fs = CompressingUtils.createReadOnlyZipFileSystem(modFile.toPath())) {
Path modstoml = fs.getPath("META-INF/mods.toml"); Path modstoml = fs.getPath("META-INF/mods.toml");
if (Files.notExists(modstoml)) 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()); 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.getAuthors(), mod.getVersion().replace("${file.jarVersion}", jarVersion), "",
mod.getDisplayURL(), mod.getDisplayURL(),
metadata.getLogoFile()); metadata.getLogoFile());

View File

@@ -120,7 +120,7 @@ public final class ForgeOldModMetadata {
return authors; 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())) { try (FileSystem fs = CompressingUtils.createReadOnlyZipFileSystem(modFile.toPath())) {
Path mcmod = fs.getPath("mcmod.info"); Path mcmod = fs.getPath("mcmod.info");
if (Files.notExists(mcmod)) if (Files.notExists(mcmod))
@@ -138,7 +138,7 @@ public final class ForgeOldModMetadata {
authors = String.join(", ", metadata.getAuthorList()); authors = String.join(", ", metadata.getAuthorList());
if (StringUtils.isBlank(authors)) if (StringUtils.isBlank(authors))
authors = metadata.getCredits(); 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(), authors, metadata.getVersion(), metadata.getGameVersion(),
StringUtils.isBlank(metadata.getUrl()) ? metadata.getUpdateUrl() : metadata.url, StringUtils.isBlank(metadata.getUrl()) ? metadata.getUpdateUrl() : metadata.url,
metadata.getLogoFile()); metadata.getLogoFile());

View File

@@ -108,7 +108,7 @@ public final class LiteModMetadata {
return updateURI; 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)) { try (ZipFile zipFile = new ZipFile(modFile)) {
ZipEntry entry = zipFile.getEntry("litemod.json"); ZipEntry entry = zipFile.getEntry("litemod.json");
if (entry == null) if (entry == null)
@@ -116,7 +116,7 @@ public final class LiteModMetadata {
LiteModMetadata metadata = JsonUtils.GSON.fromJson(IOUtils.readFullyAsString(zipFile.getInputStream(entry)), LiteModMetadata.class); LiteModMetadata metadata = JsonUtils.GSON.fromJson(IOUtils.readFullyAsString(zipFile.getInputStream(entry)), LiteModMetadata.class);
if (metadata == null) if (metadata == null)
throw new IOException("Mod " + modFile + " `litemod.json` is malformed."); 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(), ""); metadata.getVersion(), metadata.getGameVersion(), metadata.getUpdateURI(), "");
} }
} }

View File

@@ -49,11 +49,11 @@ public final class ModInfo implements Comparable<ModInfo> {
private final String logoPath; private final String logoPath;
private final BooleanProperty activeProperty; private final BooleanProperty activeProperty;
public ModInfo(ModManager modManager, File file, String id, String name, Description description) { public ModInfo(File file, String id, String name, Description description) {
this(modManager, file, id, name, 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.file = file.toPath();
this.id = id; this.id = id;
this.name = name; this.name = name;
@@ -64,16 +64,16 @@ public final class ModInfo implements Comparable<ModInfo> {
this.url = url; this.url = url;
this.logoPath = logoPath; this.logoPath = logoPath;
activeProperty = new SimpleBooleanProperty(this, "active", !modManager.isDisabled(file)) { activeProperty = new SimpleBooleanProperty(this, "active", !ModManager.isDisabled(file)) {
@Override @Override
protected void invalidated() { protected void invalidated() {
Path path = ModInfo.this.file.toAbsolutePath(); Path path = ModInfo.this.file.toAbsolutePath();
try { try {
if (get()) if (get())
ModInfo.this.file = modManager.enableMod(path); ModInfo.this.file = ModManager.enableMod(path);
else else
ModInfo.this.file = modManager.disableMod(path); ModInfo.this.file = ModManager.disableMod(path);
} catch (IOException e) { } catch (IOException e) {
Logging.LOG.log(Level.SEVERE, "Unable to invert state of mod file " + path, e); Logging.LOG.log(Level.SEVERE, "Unable to invert state of mod file " + path, e);
} }

View File

@@ -19,16 +19,15 @@ package org.jackhuang.hmcl.mod;
import org.jackhuang.hmcl.game.GameRepository; import org.jackhuang.hmcl.game.GameRepository;
import org.jackhuang.hmcl.util.StringUtils; 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.io.FileUtils;
import org.jackhuang.hmcl.util.versioning.VersionNumber; import org.jackhuang.hmcl.util.versioning.VersionNumber;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.nio.file.DirectoryStream; import java.nio.file.*;
import java.nio.file.Files; import java.util.Collection;
import java.nio.file.Path; import java.util.TreeSet;
import java.nio.file.StandardCopyOption;
import java.util.*;
public final class ModManager { public final class ModManager {
private final GameRepository repository; 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; File file = isDisabled(modFile) ? new File(modFile.getAbsoluteFile().getParentFile(), FileUtils.getNameWithoutExtension(modFile)) : modFile;
String description, extension = FileUtils.getExtension(file); String description, extension = FileUtils.getExtension(file);
switch (extension) { switch (extension) {
case "zip": case "zip":
case "jar": case "jar":
try { try {
return ForgeOldModMetadata.fromFile(this, modFile); return ForgeOldModMetadata.fromFile(modFile);
} catch (Exception ignore) { } catch (Exception ignore) {
} }
try { try {
return ForgeNewModMetadata.fromFile(this, modFile); return ForgeNewModMetadata.fromFile(modFile);
} catch (Exception ignore) { } catch (Exception ignore) {
} }
try { try {
return FabricModMetadata.fromFile(this, modFile); return FabricModMetadata.fromFile(modFile);
} catch (Exception ignore) { } catch (Exception ignore) {
} }
try { try {
return PackMcMeta.fromFile(this, modFile); return PackMcMeta.fromFile(modFile);
} catch (Exception ignore) { } catch (Exception ignore) {
} }
@@ -91,7 +90,7 @@ public final class ModManager {
break; break;
case "litemod": case "litemod":
try { try {
return LiteModMetadata.fromFile(this, modFile); return LiteModMetadata.fromFile(modFile);
} catch (Exception ignore) { } catch (Exception ignore) {
description = "LiteLoader Mod"; description = "LiteLoader Mod";
} }
@@ -99,7 +98,7 @@ public final class ModManager {
default: default:
throw new IllegalArgumentException("File " + modFile + " is not a mod file."); 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 { public void refreshMods() throws IOException {
@@ -130,7 +129,7 @@ public final class ModManager {
} }
public void addMod(File file) throws IOException { public void addMod(File file) throws IOException {
if (!isFileMod(file)) if (!isFileNameMod(file))
throw new IllegalArgumentException("File " + file + " is not a valid mod file."); throw new IllegalArgumentException("File " + file + " is not a valid mod file.");
if (!loaded) 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)); Path disabled = file.getParent().resolve(StringUtils.addSuffix(FileUtils.getName(file), DISABLED_EXTENSION));
if (Files.exists(file)) if (Files.exists(file))
Files.move(file, disabled, StandardCopyOption.REPLACE_EXISTING); Files.move(file, disabled, StandardCopyOption.REPLACE_EXISTING);
return disabled; 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)); Path enabled = file.getParent().resolve(StringUtils.removeSuffix(FileUtils.getName(file), DISABLED_EXTENSION));
if (Files.exists(file)) if (Files.exists(file))
Files.move(file, enabled, StandardCopyOption.REPLACE_EXISTING); Files.move(file, enabled, StandardCopyOption.REPLACE_EXISTING);
return enabled; return enabled;
} }
public boolean isDisabled(File file) { public static boolean isDisabled(File file) {
return file.getPath().endsWith(DISABLED_EXTENSION); return file.getPath().endsWith(DISABLED_EXTENSION);
} }
public boolean isFileMod(File file) { public static boolean isFileNameMod(File file) {
String name = file.getName(); String name = file.getName();
if (isDisabled(file)) if (isDisabled(file))
name = FileUtils.getNameWithoutExtension(file); name = FileUtils.getNameWithoutExtension(file);
return name.endsWith(".zip") || name.endsWith(".jar") || name.endsWith(".litemod"); 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 * Check if "mods" directory has mod file named "fileName" no matter the mod is disabled or not
* *

View File

@@ -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())) { try (FileSystem fs = CompressingUtils.createReadOnlyZipFileSystem(modFile.toPath())) {
Path mcmod = fs.getPath("pack.mcmeta"); Path mcmod = fs.getPath("pack.mcmeta");
if (Files.notExists(mcmod)) if (Files.notExists(mcmod))
throw new IOException("File " + modFile + " is not a resource pack."); throw new IOException("File " + modFile + " is not a resource pack.");
PackMcMeta metadata = JsonUtils.fromNonNullJson(FileUtils.readText(mcmod), PackMcMeta.class); 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, "", "", "", "", "");
} }
} }
} }