优化 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 org.jackhuang.hmcl.util.Immutable;
import org.jackhuang.hmcl.util.gson.JsonUtils;
import org.jackhuang.hmcl.util.io.CompressingUtils;
import org.jackhuang.hmcl.util.io.FileUtils;
import java.io.IOException;
@ -58,16 +57,14 @@ public final class FabricModMetadata {
this.contact = contact;
}
public static LocalModFile fromFile(ModManager modManager, Path modFile) throws IOException, JsonParseException {
try (FileSystem fs = CompressingUtils.createReadOnlyZipFileSystem(modFile)) {
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 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);
}
public static LocalModFile fromFile(ModManager modManager, Path modFile, FileSystem fs) throws IOException, JsonParseException {
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 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);
}
@JsonAdapter(FabricModAuthorSerializer.class)

View File

@ -3,10 +3,10 @@ package org.jackhuang.hmcl.mod;
import com.google.gson.JsonParseException;
import com.moandjiezana.toml.Toml;
import org.jackhuang.hmcl.util.Immutable;
import org.jackhuang.hmcl.util.io.CompressingUtils;
import org.jackhuang.hmcl.util.io.FileUtils;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.FileSystem;
import java.nio.file.Files;
import java.nio.file.Path;
@ -115,29 +115,27 @@ public final class ForgeNewModMetadata {
}
}
public static LocalModFile fromFile(ModManager modManager, Path modFile) throws IOException, JsonParseException {
try (FileSystem fs = CompressingUtils.createReadOnlyZipFileSystem(modFile)) {
Path modstoml = fs.getPath("META-INF/mods.toml");
if (Files.notExists(modstoml))
throw new IOException("File " + modFile + " is not a Forge 1.13+ mod.");
ForgeNewModMetadata metadata = new Toml().read(FileUtils.readText(modstoml)).to(ForgeNewModMetadata.class);
if (metadata == null || metadata.getMods().isEmpty())
throw new IOException("Mod " + modFile + " `mods.toml` is malformed..");
Mod mod = metadata.getMods().get(0);
Path manifestMF = fs.getPath("META-INF/MANIFEST.MF");
String jarVersion = "";
if (Files.exists(manifestMF)) {
try {
Manifest manifest = new Manifest(Files.newInputStream(manifestMF));
jarVersion = manifest.getMainAttributes().getValue(Attributes.Name.IMPLEMENTATION_VERSION);
} catch (IOException e) {
LOG.log(Level.WARNING, "Failed to parse MANIFEST.MF in file " + modFile);
}
public static LocalModFile fromFile(ModManager modManager, Path modFile, FileSystem fs) throws IOException, JsonParseException {
Path modstoml = fs.getPath("META-INF/mods.toml");
if (Files.notExists(modstoml))
throw new IOException("File " + modFile + " is not a Forge 1.13+ mod.");
ForgeNewModMetadata metadata = new Toml().read(FileUtils.readText(modstoml)).to(ForgeNewModMetadata.class);
if (metadata == null || metadata.getMods().isEmpty())
throw new IOException("Mod " + modFile + " `mods.toml` is malformed..");
Mod mod = metadata.getMods().get(0);
Path manifestMF = fs.getPath("META-INF/MANIFEST.MF");
String jarVersion = "";
if (Files.exists(manifestMF)) {
try (InputStream is = Files.newInputStream(manifestMF)) {
Manifest manifest = new Manifest(is);
jarVersion = manifest.getMainAttributes().getValue(Attributes.Name.IMPLEMENTATION_VERSION);
} catch (IOException e) {
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.StringUtils;
import org.jackhuang.hmcl.util.gson.JsonUtils;
import org.jackhuang.hmcl.util.io.CompressingUtils;
import org.jackhuang.hmcl.util.io.FileUtils;
import java.io.IOException;
@ -120,28 +119,26 @@ public final class ForgeOldModMetadata {
return authors;
}
public static LocalModFile fromFile(ModManager modManager, Path modFile) throws IOException, JsonParseException {
try (FileSystem fs = CompressingUtils.createReadOnlyZipFileSystem(modFile)) {
Path mcmod = fs.getPath("mcmod.info");
if (Files.notExists(mcmod))
throw new IOException("File " + modFile + " is not a Forge mod.");
List<ForgeOldModMetadata> modList = JsonUtils.GSON.fromJson(FileUtils.readText(mcmod),
new TypeToken<List<ForgeOldModMetadata>>() {
}.getType());
if (modList == null || modList.isEmpty())
throw new IOException("Mod " + modFile + " `mcmod.info` is malformed..");
ForgeOldModMetadata metadata = modList.get(0);
String authors = metadata.getAuthor();
if (StringUtils.isBlank(authors) && metadata.getAuthors().length > 0)
authors = String.join(", ", metadata.getAuthors());
if (StringUtils.isBlank(authors) && metadata.getAuthorList().length > 0)
authors = String.join(", ", metadata.getAuthorList());
if (StringUtils.isBlank(authors))
authors = metadata.getCredits();
return new LocalModFile(modManager, modManager.getLocalMod(metadata.getModId(), ModLoaderType.FORGE), modFile, metadata.getName(), new LocalModFile.Description(metadata.getDescription()),
authors, metadata.getVersion(), metadata.getGameVersion(),
StringUtils.isBlank(metadata.getUrl()) ? metadata.getUpdateUrl() : metadata.url,
metadata.getLogoFile());
}
public static LocalModFile fromFile(ModManager modManager, Path modFile, FileSystem fs) throws IOException, JsonParseException {
Path mcmod = fs.getPath("mcmod.info");
if (Files.notExists(mcmod))
throw new IOException("File " + modFile + " is not a Forge mod.");
List<ForgeOldModMetadata> modList = JsonUtils.GSON.fromJson(FileUtils.readText(mcmod),
new TypeToken<List<ForgeOldModMetadata>>() {
}.getType());
if (modList == null || modList.isEmpty())
throw new IOException("Mod " + modFile + " `mcmod.info` is malformed..");
ForgeOldModMetadata metadata = modList.get(0);
String authors = metadata.getAuthor();
if (StringUtils.isBlank(authors) && metadata.getAuthors().length > 0)
authors = String.join(", ", metadata.getAuthors());
if (StringUtils.isBlank(authors) && metadata.getAuthorList().length > 0)
authors = String.join(", ", metadata.getAuthorList());
if (StringUtils.isBlank(authors))
authors = metadata.getCredits();
return new LocalModFile(modManager, modManager.getLocalMod(metadata.getModId(), ModLoaderType.FORGE), modFile, metadata.getName(), new LocalModFile.Description(metadata.getDescription()),
authors, metadata.getVersion(), metadata.getGameVersion(),
StringUtils.isBlank(metadata.getUrl()) ? metadata.getUpdateUrl() : metadata.url,
metadata.getLogoFile());
}
}

View File

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

View File

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