feat: cache detected game version

This commit is contained in:
huanghongxun 2021-08-21 14:41:56 +08:00
parent ddd0f83936
commit 7da9d0f283
13 changed files with 62 additions and 28 deletions

View File

@ -133,7 +133,7 @@ public final class LauncherHelper {
HMCLGameRepository repository = profile.getRepository();
DefaultDependencyManager dependencyManager = profile.getDependency();
Version version = MaintainTask.maintain(repository, repository.getResolvedVersion(selectedVersion));
Optional<String> gameVersion = GameVersion.minecraftVersion(repository.getVersionJar(version));
Optional<String> gameVersion = repository.getGameVersion(version);
boolean integrityCheck = repository.unmarkVersionLaunchedAbnormally(selectedVersion);
CountDownLatch launchingLatch = new CountDownLatch(1);
@ -311,7 +311,7 @@ public final class LauncherHelper {
// Without onAccept called, the launching operation will be terminated.
VersionNumber gameVersion = VersionNumber.asVersion(GameVersion.minecraftVersion(profile.getRepository().getVersionJar(version)).orElse("Unknown"));
VersionNumber gameVersion = VersionNumber.asVersion(profile.getRepository().getGameVersion(version).orElse("Unknown"));
JavaVersion java = setting.getJavaVersion();
if (java == null) {
Controllers.dialog(i18n("launch.wrong_javadir"), i18n("message.warning"), MessageType.WARNING, onAccept);

View File

@ -26,7 +26,6 @@ import javafx.scene.control.Control;
import javafx.scene.control.Skin;
import javafx.scene.image.Image;
import org.jackhuang.hmcl.download.LibraryAnalyzer;
import org.jackhuang.hmcl.game.GameVersion;
import org.jackhuang.hmcl.mod.ModpackConfiguration;
import org.jackhuang.hmcl.setting.Profile;
import org.jackhuang.hmcl.util.i18n.I18n;
@ -41,8 +40,6 @@ import static org.jackhuang.hmcl.download.LibraryAnalyzer.LibraryType.MINECRAFT;
import static org.jackhuang.hmcl.util.Lang.handleUncaught;
import static org.jackhuang.hmcl.util.Lang.threadPool;
import static org.jackhuang.hmcl.util.Logging.LOG;
import static org.jackhuang.hmcl.util.StringUtils.removePrefix;
import static org.jackhuang.hmcl.util.StringUtils.removeSuffix;
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
public class GameItem extends Control {
@ -61,7 +58,7 @@ public class GameItem extends Control {
this.version = id;
// GameVersion.minecraftVersion() is a time-costing job (up to ~200 ms)
CompletableFuture.supplyAsync(() -> GameVersion.minecraftVersion(profile.getRepository().getVersionJar(id)).orElse(i18n("message.unknown")), POOL_VERSION_RESOLVE)
CompletableFuture.supplyAsync(() -> profile.getRepository().getGameVersion(id).orElse(i18n("message.unknown")), POOL_VERSION_RESOLVE)
.thenAcceptAsync(game -> {
StringBuilder libraries = new StringBuilder(game);
LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(profile.getRepository().getResolvedPreservingPatchesVersion(id));
@ -72,7 +69,7 @@ public class GameItem extends Control {
if (I18n.hasKey("install.installer." + libraryId)) {
libraries.append(", ").append(i18n("install.installer." + libraryId));
if (libraryVersion != null)
libraries.append(": ").append(modifyVersion("", libraryVersion.replaceAll("(?i)" + libraryId, "")));
libraries.append(": ").append(LibraryAnalyzer.modifyVersion(game, libraryVersion.replaceAll("(?i)" + libraryId, "")));
}
}
@ -123,8 +120,4 @@ public class GameItem extends Control {
public ObjectProperty<Image> imageProperty() {
return image;
}
private static String modifyVersion(String gameVersion, String version) {
return removeSuffix(removePrefix(removeSuffix(removePrefix(version.replace(gameVersion, "").trim(), "-"), "-"), "_"), "_");
}
}

View File

@ -22,7 +22,6 @@ import javafx.scene.Node;
import javafx.scene.control.Skin;
import javafx.stage.FileChooser;
import org.jackhuang.hmcl.download.LibraryAnalyzer;
import org.jackhuang.hmcl.game.GameVersion;
import org.jackhuang.hmcl.game.Version;
import org.jackhuang.hmcl.setting.Profile;
import org.jackhuang.hmcl.task.Schedulers;
@ -70,7 +69,7 @@ public class InstallerListPage extends ListPageBase<InstallerItem> implements Ve
this.gameVersion = null;
CompletableFuture.supplyAsync(() -> {
gameVersion = GameVersion.minecraftVersion(profile.getRepository().getVersionJar(version)).orElse(null);
gameVersion = profile.getRepository().getGameVersion(version).orElse(null);
return LibraryAnalyzer.analyze(profile.getRepository().getResolvedPreservingPatchesVersion(versionId));
}).thenAcceptAsync(analyzer -> {

View File

@ -23,7 +23,6 @@ import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.scene.Node;
import javafx.stage.FileChooser;
import org.jackhuang.hmcl.game.GameVersion;
import org.jackhuang.hmcl.game.World;
import org.jackhuang.hmcl.setting.Profile;
import org.jackhuang.hmcl.setting.Theme;
@ -87,7 +86,7 @@ public class WorldListPage extends ListPageBase<WorldListItem> implements Versio
setLoading(true);
return CompletableFuture
.runAsync(() -> gameVersion = GameVersion.minecraftVersion(profile.getRepository().getVersionJar(id)).orElse(null))
.runAsync(() -> gameVersion = profile.getRepository().getGameVersion(id).orElse(null))
.thenApplyAsync(unused -> {
try (Stream<World> stream = World.getWorlds(savesDir)) {
return stream.parallel().collect(Collectors.toList());

View File

@ -23,7 +23,6 @@ import org.jackhuang.hmcl.download.game.GameDownloadTask;
import org.jackhuang.hmcl.download.game.GameLibrariesTask;
import org.jackhuang.hmcl.download.optifine.OptiFineInstallTask;
import org.jackhuang.hmcl.game.DefaultGameRepository;
import org.jackhuang.hmcl.game.GameVersion;
import org.jackhuang.hmcl.game.Library;
import org.jackhuang.hmcl.game.Version;
import org.jackhuang.hmcl.task.Task;
@ -99,7 +98,7 @@ public class DefaultDependencyManager extends AbstractDependencyManager {
return Task.composeAsync(() -> {
List<Task<?>> tasks = new ArrayList<>();
Optional<String> gameVersion = GameVersion.minecraftVersion(repository.getVersionJar(version));
Optional<String> gameVersion = repository.getGameVersion(version);
if (!gameVersion.isPresent()) return null;
LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(version.resolvePreservingPatches(getGameRepository()));

View File

@ -29,6 +29,8 @@ import java.util.regex.Pattern;
import java.util.stream.Collectors;
import static org.jackhuang.hmcl.util.Pair.pair;
import static org.jackhuang.hmcl.util.StringUtils.removePrefix;
import static org.jackhuang.hmcl.util.StringUtils.removeSuffix;
public final class LibraryAnalyzer implements Iterable<LibraryAnalyzer.LibraryMark> {
private Version version;
@ -153,6 +155,10 @@ public final class LibraryAnalyzer implements Iterable<LibraryAnalyzer.LibraryMa
return mainClass != null && (LAUNCH_WRAPPER_MAIN.equals(mainClass) || mainClass.startsWith("net.fabricmc") || mainClass.startsWith("cpw.mods"));
}
public static String modifyVersion(String gameVersion, String version) {
return removeSuffix(removePrefix(removeSuffix(removePrefix(version.replace(gameVersion, "").trim(), "-"), "-"), "_"), "_");
}
public enum LibraryType {
MINECRAFT(true, "game", Pattern.compile("^$"), Pattern.compile("^$")),
FABRIC(true, "fabric", Pattern.compile("net\\.fabricmc"), Pattern.compile("fabric-loader")),

View File

@ -22,7 +22,6 @@ import org.jackhuang.hmcl.download.DependencyManager;
import org.jackhuang.hmcl.download.LibraryAnalyzer;
import org.jackhuang.hmcl.download.VersionMismatchException;
import org.jackhuang.hmcl.download.optifine.OptiFineInstallTask;
import org.jackhuang.hmcl.game.GameVersion;
import org.jackhuang.hmcl.game.Version;
import org.jackhuang.hmcl.task.FileDownloadTask;
import org.jackhuang.hmcl.task.Task;
@ -132,7 +131,7 @@ public final class ForgeInstallTask extends Task<Version> {
* @throws VersionMismatchException if required game version of installer does not match the actual one.
*/
public static boolean detectForgeInstallerType(DependencyManager dependencyManager, Version version, Path installer) throws IOException, VersionMismatchException {
Optional<String> gameVersion = GameVersion.minecraftVersion(dependencyManager.getGameRepository().getVersionJar(version));
Optional<String> gameVersion = dependencyManager.getGameRepository().getGameVersion(version);
if (!gameVersion.isPresent()) throw new IOException();
try (FileSystem fs = CompressingUtils.createReadOnlyZipFileSystem(installer)) {
String installProfileText = FileUtils.readText(fs.getPath("install_profile.json"));
@ -165,7 +164,7 @@ public final class ForgeInstallTask extends Task<Version> {
* @throws VersionMismatchException if required game version of installer does not match the actual one.
*/
public static Task<Version> install(DefaultDependencyManager dependencyManager, Version version, Path installer) throws IOException, VersionMismatchException {
Optional<String> gameVersion = GameVersion.minecraftVersion(dependencyManager.getGameRepository().getVersionJar(version));
Optional<String> gameVersion = dependencyManager.getGameRepository().getGameVersion(version);
if (!gameVersion.isPresent()) throw new IOException();
try (FileSystem fs = CompressingUtils.createReadOnlyZipFileSystem(installer)) {
String installProfileText = FileUtils.readText(fs.getPath("install_profile.json"));

View File

@ -208,8 +208,7 @@ public final class OptiFineInstallTask extends Task<Version> {
* @throws VersionMismatchException if required game version of installer does not match the actual one.
*/
public static Task<Version> install(DefaultDependencyManager dependencyManager, Version version, Path installer) throws IOException, VersionMismatchException {
File jar = dependencyManager.getGameRepository().getVersionJar(version);
Optional<String> gameVersion = GameVersion.minecraftVersion(jar);
Optional<String> gameVersion = dependencyManager.getGameRepository().getGameVersion(version);
if (!gameVersion.isPresent()) throw new IOException();
try (FileSystem fs = CompressingUtils.createReadOnlyZipFileSystem(installer)) {
Path configClass = fs.getPath("Config.class");

View File

@ -50,6 +50,7 @@ import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.TreeMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import java.util.stream.Stream;
@ -64,6 +65,7 @@ public class DefaultGameRepository implements GameRepository {
private File baseDirectory;
protected Map<String, Version> versions;
private ConcurrentHashMap<File, Optional<String>> gameVersions = new ConcurrentHashMap<>();
public DefaultGameRepository(File baseDirectory) {
this.baseDirectory = baseDirectory;
@ -136,6 +138,21 @@ public class DefaultGameRepository implements GameRepository {
return new File(getVersionRoot(id), id + ".jar");
}
@Override
public Optional<String> getGameVersion(Version version) {
// This implementation may cause multiple flows against the same version entering
// this function, which is accepted because GameVersion::minecraftVersion should
// be consistent.
File versionJar = getVersionJar(version);
if (gameVersions.containsKey(versionJar)) {
return gameVersions.get(versionJar);
} else {
Optional<String> gameVersion = GameVersion.minecraftVersion(versionJar);
gameVersions.put(versionJar, gameVersion);
return gameVersion;
}
}
@Override
public File getNativeDirectory(String id) {
return new File(getVersionRoot(id), "natives");
@ -321,6 +338,7 @@ public class DefaultGameRepository implements GameRepository {
}
}
this.gameVersions.clear();
this.versions = versions;
}

View File

@ -24,6 +24,7 @@ import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
/**
* Supports operations on versioning.
@ -134,6 +135,30 @@ public interface GameRepository extends VersionProvider {
*/
File getVersionJar(Version version);
/**
* Detect game version.
*
* This method is time-consuming, but the result will be cached.
* Consider running this job in IO scheduler.
*
* @param version version
* @return game version, or empty if an error occurred in detection.
*/
Optional<String> getGameVersion(Version version);
/**
* Detect game version.
*
* This method is time-consuming, but the result will be cached.
* Consider running this job in IO scheduler.
*
* @param versionId id of version
* @return game version, or empty if an error occurred in detection.
*/
default Optional<String> getGameVersion(String versionId) throws VersionNotFoundException {
return getGameVersion(getVersion(versionId));
}
/**
* Get minecraft jar
*

View File

@ -19,7 +19,6 @@ package org.jackhuang.hmcl.mod.mcbbs;
import org.jackhuang.hmcl.download.LibraryAnalyzer;
import org.jackhuang.hmcl.game.DefaultGameRepository;
import org.jackhuang.hmcl.game.GameVersion;
import org.jackhuang.hmcl.game.Library;
import org.jackhuang.hmcl.mod.ModAdviser;
import org.jackhuang.hmcl.mod.Modpack;
@ -81,7 +80,7 @@ public class McbbsModpackExportTask extends Task<Void> {
});
LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(repository.getResolvedPreservingPatchesVersion(version));
String gameVersion = GameVersion.minecraftVersion(repository.getVersionJar(version))
String gameVersion = repository.getGameVersion(version)
.orElseThrow(() -> new IOException("Cannot parse the version of " + version));
List<McbbsModpackManifest.Addon> addons = new ArrayList<>();

View File

@ -19,7 +19,6 @@ package org.jackhuang.hmcl.mod.multimc;
import org.jackhuang.hmcl.download.LibraryAnalyzer;
import org.jackhuang.hmcl.game.DefaultGameRepository;
import org.jackhuang.hmcl.game.GameVersion;
import org.jackhuang.hmcl.mod.ModAdviser;
import org.jackhuang.hmcl.mod.Modpack;
import org.jackhuang.hmcl.mod.ModpackExportInfo;
@ -72,7 +71,7 @@ public class MultiMCModpackExportTask extends Task<Void> {
zip.putDirectory(repository.getRunDirectory(versionId).toPath(), ".minecraft", path -> Modpack.acceptFile(path, blackList, whitelist));
LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(repository.getResolvedPreservingPatchesVersion(versionId));
String gameVersion = GameVersion.minecraftVersion(repository.getVersionJar(versionId))
String gameVersion = repository.getGameVersion(versionId)
.orElseThrow(() -> new IOException("Cannot parse the version of " + versionId));
List<MultiMCManifest.MultiMCManifestComponent> components = new ArrayList<>();
components.add(new MultiMCManifest.MultiMCManifestComponent(true, false, "net.minecraft", gameVersion));

View File

@ -19,7 +19,6 @@ package org.jackhuang.hmcl.mod.server;
import org.jackhuang.hmcl.download.LibraryAnalyzer;
import org.jackhuang.hmcl.game.DefaultGameRepository;
import org.jackhuang.hmcl.game.GameVersion;
import org.jackhuang.hmcl.mod.ModAdviser;
import org.jackhuang.hmcl.mod.Modpack;
import org.jackhuang.hmcl.mod.ModpackConfiguration;
@ -81,7 +80,7 @@ public class ServerModpackExportTask extends Task<Void> {
});
LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(repository.getResolvedPreservingPatchesVersion(versionId));
String gameVersion = GameVersion.minecraftVersion(repository.getVersionJar(versionId))
String gameVersion = repository.getGameVersion(versionId)
.orElseThrow(() -> new IOException("Cannot parse the version of " + versionId));
List<ServerModpackManifest.Addon> addons = new ArrayList<>();
addons.add(new ServerModpackManifest.Addon(MINECRAFT.getPatchId(), gameVersion));