优化 mod 列表加载速度 (#1985)

* Share ZipFileSystem

* fix memory leak
This commit is contained in:
Glavo 2023-01-08 18:15:55 +08:00 committed by GitHub
parent bf822d7cf7
commit 1cc429c7ca
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 80 additions and 88 deletions

View File

@ -21,7 +21,6 @@ import com.google.gson.*;
import com.google.gson.annotations.JsonAdapter; import com.google.gson.annotations.JsonAdapter;
import org.jackhuang.hmcl.util.Immutable; import org.jackhuang.hmcl.util.Immutable;
import org.jackhuang.hmcl.util.gson.JsonUtils; import org.jackhuang.hmcl.util.gson.JsonUtils;
import org.jackhuang.hmcl.util.io.CompressingUtils;
import org.jackhuang.hmcl.util.io.FileUtils; import org.jackhuang.hmcl.util.io.FileUtils;
import java.io.IOException; import java.io.IOException;
@ -58,16 +57,14 @@ public final class FabricModMetadata {
this.contact = contact; this.contact = contact;
} }
public static LocalModFile fromFile(ModManager modManager, Path modFile) throws IOException, JsonParseException { public static LocalModFile fromFile(ModManager modManager, Path modFile, FileSystem fs) throws IOException, JsonParseException {
try (FileSystem fs = CompressingUtils.createReadOnlyZipFileSystem(modFile)) { 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 LocalModFile(modManager, modManager.getLocalMod(metadata.id, ModLoaderType.FABRIC), modFile, metadata.name, new LocalModFile.Description(metadata.description),
return new LocalModFile(modManager, modManager.getLocalMod(metadata.id, ModLoaderType.FABRIC), modFile, metadata.name, new LocalModFile.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);
}
} }
@JsonAdapter(FabricModAuthorSerializer.class) @JsonAdapter(FabricModAuthorSerializer.class)

View File

@ -3,10 +3,10 @@ package org.jackhuang.hmcl.mod;
import com.google.gson.JsonParseException; import com.google.gson.JsonParseException;
import com.moandjiezana.toml.Toml; import com.moandjiezana.toml.Toml;
import org.jackhuang.hmcl.util.Immutable; import org.jackhuang.hmcl.util.Immutable;
import org.jackhuang.hmcl.util.io.CompressingUtils;
import org.jackhuang.hmcl.util.io.FileUtils; import org.jackhuang.hmcl.util.io.FileUtils;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
import java.nio.file.FileSystem; import java.nio.file.FileSystem;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
@ -115,29 +115,27 @@ public final class ForgeNewModMetadata {
} }
} }
public static LocalModFile fromFile(ModManager modManager, Path modFile) throws IOException, JsonParseException { public static LocalModFile fromFile(ModManager modManager, Path modFile, FileSystem fs) throws IOException, JsonParseException {
try (FileSystem fs = CompressingUtils.createReadOnlyZipFileSystem(modFile)) { Path modstoml = fs.getPath("META-INF/mods.toml");
Path modstoml = fs.getPath("META-INF/mods.toml"); if (Files.notExists(modstoml))
if (Files.notExists(modstoml)) throw new IOException("File " + modFile + " is not a Forge 1.13+ mod.");
throw new IOException("File " + modFile + " is not a Forge 1.13+ mod."); ForgeNewModMetadata metadata = new Toml().read(FileUtils.readText(modstoml)).to(ForgeNewModMetadata.class);
ForgeNewModMetadata metadata = new Toml().read(FileUtils.readText(modstoml)).to(ForgeNewModMetadata.class); if (metadata == null || metadata.getMods().isEmpty())
if (metadata == null || metadata.getMods().isEmpty()) throw new IOException("Mod " + modFile + " `mods.toml` is malformed..");
throw new IOException("Mod " + modFile + " `mods.toml` is malformed.."); Mod mod = metadata.getMods().get(0);
Mod mod = metadata.getMods().get(0); Path manifestMF = fs.getPath("META-INF/MANIFEST.MF");
Path manifestMF = fs.getPath("META-INF/MANIFEST.MF"); String jarVersion = "";
String jarVersion = ""; if (Files.exists(manifestMF)) {
if (Files.exists(manifestMF)) { try (InputStream is = Files.newInputStream(manifestMF)) {
try { Manifest manifest = new Manifest(is);
Manifest manifest = new Manifest(Files.newInputStream(manifestMF)); jarVersion = manifest.getMainAttributes().getValue(Attributes.Name.IMPLEMENTATION_VERSION);
jarVersion = manifest.getMainAttributes().getValue(Attributes.Name.IMPLEMENTATION_VERSION); } catch (IOException e) {
} catch (IOException e) { LOG.log(Level.WARNING, "Failed to parse MANIFEST.MF in file " + modFile);
LOG.log(Level.WARNING, "Failed to parse MANIFEST.MF in file " + modFile);
}
} }
return new LocalModFile(modManager, modManager.getLocalMod(mod.getModId(), ModLoaderType.FORGE), modFile, mod.getDisplayName(), new LocalModFile.Description(mod.getDescription()),
mod.getAuthors(), mod.getVersion().replace("${file.jarVersion}", jarVersion), "",
mod.getDisplayURL(),
metadata.getLogoFile());
} }
return new LocalModFile(modManager, modManager.getLocalMod(mod.getModId(), ModLoaderType.FORGE), modFile, mod.getDisplayName(), new LocalModFile.Description(mod.getDescription()),
mod.getAuthors(), mod.getVersion().replace("${file.jarVersion}", jarVersion), "",
mod.getDisplayURL(),
metadata.getLogoFile());
} }
} }

View File

@ -23,7 +23,6 @@ import com.google.gson.reflect.TypeToken;
import org.jackhuang.hmcl.util.Immutable; import org.jackhuang.hmcl.util.Immutable;
import org.jackhuang.hmcl.util.StringUtils; import org.jackhuang.hmcl.util.StringUtils;
import org.jackhuang.hmcl.util.gson.JsonUtils; import org.jackhuang.hmcl.util.gson.JsonUtils;
import org.jackhuang.hmcl.util.io.CompressingUtils;
import org.jackhuang.hmcl.util.io.FileUtils; import org.jackhuang.hmcl.util.io.FileUtils;
import java.io.IOException; import java.io.IOException;
@ -120,28 +119,26 @@ public final class ForgeOldModMetadata {
return authors; return authors;
} }
public static LocalModFile fromFile(ModManager modManager, Path modFile) throws IOException, JsonParseException { public static LocalModFile fromFile(ModManager modManager, Path modFile, FileSystem fs) throws IOException, JsonParseException {
try (FileSystem fs = CompressingUtils.createReadOnlyZipFileSystem(modFile)) { Path mcmod = fs.getPath("mcmod.info");
Path mcmod = fs.getPath("mcmod.info"); if (Files.notExists(mcmod))
if (Files.notExists(mcmod)) throw new IOException("File " + modFile + " is not a Forge mod.");
throw new IOException("File " + modFile + " is not a Forge mod."); List<ForgeOldModMetadata> modList = JsonUtils.GSON.fromJson(FileUtils.readText(mcmod),
List<ForgeOldModMetadata> modList = JsonUtils.GSON.fromJson(FileUtils.readText(mcmod), new TypeToken<List<ForgeOldModMetadata>>() {
new TypeToken<List<ForgeOldModMetadata>>() { }.getType());
}.getType()); if (modList == null || modList.isEmpty())
if (modList == null || modList.isEmpty()) throw new IOException("Mod " + modFile + " `mcmod.info` is malformed..");
throw new IOException("Mod " + modFile + " `mcmod.info` is malformed.."); ForgeOldModMetadata metadata = modList.get(0);
ForgeOldModMetadata metadata = modList.get(0); String authors = metadata.getAuthor();
String authors = metadata.getAuthor(); if (StringUtils.isBlank(authors) && metadata.getAuthors().length > 0)
if (StringUtils.isBlank(authors) && metadata.getAuthors().length > 0) authors = String.join(", ", metadata.getAuthors());
authors = String.join(", ", metadata.getAuthors()); if (StringUtils.isBlank(authors) && metadata.getAuthorList().length > 0)
if (StringUtils.isBlank(authors) && metadata.getAuthorList().length > 0) 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 LocalModFile(modManager, modManager.getLocalMod(metadata.getModId(), ModLoaderType.FORGE), modFile, metadata.getName(), new LocalModFile.Description(metadata.getDescription()),
return new LocalModFile(modManager, modManager.getLocalMod(metadata.getModId(), ModLoaderType.FORGE), modFile, metadata.getName(), new LocalModFile.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

@ -73,24 +73,27 @@ public final class ModManager {
String fileName = StringUtils.removeSuffix(FileUtils.getName(modFile), DISABLED_EXTENSION, OLD_EXTENSION); String fileName = StringUtils.removeSuffix(FileUtils.getName(modFile), DISABLED_EXTENSION, OLD_EXTENSION);
String description; String description;
if (fileName.endsWith(".zip") || fileName.endsWith(".jar")) { if (fileName.endsWith(".zip") || fileName.endsWith(".jar")) {
try { try (FileSystem fs = CompressingUtils.createReadOnlyZipFileSystem(modFile)) {
return ForgeOldModMetadata.fromFile(this, modFile); try {
} catch (Exception ignore) { return ForgeOldModMetadata.fromFile(this, modFile, fs);
} } catch (Exception ignore) {
}
try { try {
return ForgeNewModMetadata.fromFile(this, modFile); return ForgeNewModMetadata.fromFile(this, modFile, fs);
} catch (Exception ignore) { } catch (Exception ignore) {
} }
try { try {
return FabricModMetadata.fromFile(this, modFile); return FabricModMetadata.fromFile(this, modFile, fs);
} catch (Exception ignore) { } catch (Exception ignore) {
} }
try { try {
return PackMcMeta.fromFile(this, modFile); return PackMcMeta.fromFile(this, modFile, fs);
} catch (Exception ignore) { } catch (Exception ignore) {
}
} catch (Exception ignored) {
} }
description = ""; description = "";

View File

@ -23,7 +23,6 @@ import com.google.gson.annotations.SerializedName;
import org.jackhuang.hmcl.util.Immutable; import org.jackhuang.hmcl.util.Immutable;
import org.jackhuang.hmcl.util.gson.JsonUtils; import org.jackhuang.hmcl.util.gson.JsonUtils;
import org.jackhuang.hmcl.util.gson.Validation; import org.jackhuang.hmcl.util.gson.Validation;
import org.jackhuang.hmcl.util.io.CompressingUtils;
import org.jackhuang.hmcl.util.io.FileUtils; import org.jackhuang.hmcl.util.io.FileUtils;
import java.io.IOException; import java.io.IOException;
@ -143,19 +142,17 @@ public class PackMcMeta implements Validation {
} }
} }
public static LocalModFile fromFile(ModManager modManager, Path modFile) throws IOException, JsonParseException { public static LocalModFile fromFile(ModManager modManager, Path modFile, FileSystem fs) throws IOException, JsonParseException {
try (FileSystem fs = CompressingUtils.createReadOnlyZipFileSystem(modFile)) { 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 LocalModFile(
return new LocalModFile( modManager,
modManager, modManager.getLocalMod(FileUtils.getNameWithoutExtension(modFile), ModLoaderType.PACK),
modManager.getLocalMod(FileUtils.getNameWithoutExtension(modFile), ModLoaderType.PACK), modFile,
modFile, FileUtils.getNameWithoutExtension(modFile),
FileUtils.getNameWithoutExtension(modFile), metadata.pack.description,
metadata.pack.description, "", "", "", "", "");
"", "", "", "", "");
}
} }
} }