From 94b6d7596ec9e2cc3040a387d956d19f784b0701 Mon Sep 17 00:00:00 2001 From: yushijinhun Date: Sun, 9 Aug 2020 17:02:42 +0800 Subject: [PATCH] Redesign authlib-injector updater The process of searching for authlib-injector.jar has been changed to: 1. use ~/.hmcl/authlib-injector.jar if present 2. use ./authlib-injector.jar if present 3. download if none of above is present Instead of checking for updates when logging in, we now do it at startup. We only check for updates if there is at least one authlib-injector server configured. This changed is expected to imporve launching speed. --- .../org/jackhuang/hmcl/setting/Accounts.java | 34 +++++++++- .../AuthlibInjectorDownloader.java | 62 +++++++++++-------- 2 files changed, 68 insertions(+), 28 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/setting/Accounts.java b/HMCL/src/main/java/org/jackhuang/hmcl/setting/Accounts.java index 4427fb687..34a680130 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/setting/Accounts.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/setting/Accounts.java @@ -29,6 +29,7 @@ import org.jackhuang.hmcl.auth.AccountFactory; import org.jackhuang.hmcl.auth.AuthenticationException; import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorAccount; import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorAccountFactory; +import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorArtifactInfo; import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorArtifactProvider; import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorDownloader; import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorServer; @@ -61,9 +62,22 @@ import static org.jackhuang.hmcl.util.i18n.I18n.i18n; public final class Accounts { private Accounts() {} + private static final AuthlibInjectorArtifactProvider AUTHLIB_INJECTOR_DOWNLOADER = createAuthlibInjectorArtifactProvider(); + private static void triggerAuthlibInjectorUpdateCheck() { + if (AUTHLIB_INJECTOR_DOWNLOADER instanceof AuthlibInjectorDownloader) { + Schedulers.io().execute(() -> { + try { + ((AuthlibInjectorDownloader) AUTHLIB_INJECTOR_DOWNLOADER).checkUpdate(); + } catch (IOException e) { + LOG.log(Level.WARNING, "Failed to check update for authlib-injector", e); + } + }); + } + } + public static final OfflineAccountFactory FACTORY_OFFLINE = OfflineAccountFactory.INSTANCE; public static final YggdrasilAccountFactory FACTORY_MOJANG = YggdrasilAccountFactory.MOJANG; - public static final AuthlibInjectorAccountFactory FACTORY_AUTHLIB_INJECTOR = new AuthlibInjectorAccountFactory(createAuthlibInjectorArtifactProvider(), Accounts::getOrCreateAuthlibInjectorServer); + public static final AuthlibInjectorAccountFactory FACTORY_AUTHLIB_INJECTOR = new AuthlibInjectorAccountFactory(AUTHLIB_INJECTOR_DOWNLOADER, Accounts::getOrCreateAuthlibInjectorServer); // ==== login type / account factory mapping ==== private static final Map> type2factory = new HashMap<>(); @@ -206,6 +220,10 @@ public final class Accounts { }); } + if (!config().getAuthlibInjectorServers().isEmpty()) { + triggerAuthlibInjectorUpdateCheck(); + } + for (AuthlibInjectorServer server : config().getAuthlibInjectorServers()) { if (selected instanceof AuthlibInjectorAccount && ((AuthlibInjectorAccount) selected).getServer() == server) continue; @@ -243,7 +261,19 @@ public final class Accounts { private static AuthlibInjectorArtifactProvider createAuthlibInjectorArtifactProvider() { String authlibinjectorLocation = System.getProperty("hmcl.authlibinjector.location"); if (authlibinjectorLocation == null) { - return new AuthlibInjectorDownloader(Metadata.HMCL_DIRECTORY, DownloadProviders::getDownloadProvider); + return new AuthlibInjectorDownloader( + Metadata.HMCL_DIRECTORY.resolve("authlib-injector.jar"), + DownloadProviders::getDownloadProvider) { + @Override + public Optional getArtifactInfoImmediately() { + Optional local = super.getArtifactInfoImmediately(); + if (local.isPresent()) { + return local; + } + // search authlib-injector.jar in current directory, it's used as a fallback + return parseArtifact(Paths.get("authlib-injector.jar")); + } + }; } else { LOG.info("Using specified authlib-injector: " + authlibinjectorLocation); return new SimpleAuthlibInjectorArtifactProvider(Paths.get(authlibinjectorLocation)); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/authlibinjector/AuthlibInjectorDownloader.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/authlibinjector/AuthlibInjectorDownloader.java index 82953f662..4961ab716 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/authlibinjector/AuthlibInjectorDownloader.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/authlibinjector/AuthlibInjectorDownloader.java @@ -31,6 +31,7 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.Map; import java.util.Optional; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Supplier; import java.util.logging.Level; @@ -43,38 +44,31 @@ public class AuthlibInjectorDownloader implements AuthlibInjectorArtifactProvide private final Path artifactLocation; private final Supplier downloadProvider; - /** - * The flag will be reset after application restart. - */ - private boolean updateChecked = false; - /** * @param artifactsDirectory where to save authlib-injector artifacts */ - public AuthlibInjectorDownloader(Path artifactsDirectory, Supplier downloadProvider) { - this.artifactLocation = artifactsDirectory.resolve("authlib-injector.jar"); + public AuthlibInjectorDownloader(Path artifactLocation, Supplier downloadProvider) { + this.artifactLocation = artifactLocation; this.downloadProvider = downloadProvider; } @Override public AuthlibInjectorArtifactInfo getArtifactInfo() throws IOException { - synchronized (artifactLocation) { + Optional cached = getArtifactInfoImmediately(); + if (cached.isPresent()) { + return cached.get(); + } + + synchronized (this) { Optional local = getLocalArtifact(); - - if (!local.isPresent() || !updateChecked) { - try { - update(local); - updateChecked = true; - } catch (IOException e) { - LOG.log(Level.WARNING, "Failed to download authlib-injector", e); - if (!local.isPresent()) { - throw e; - } - LOG.warning("Fallback to use cached artifact: " + local.get()); - } + if (local.isPresent()) { + return local.get(); } - - return getLocalArtifact().orElseThrow(() -> new IOException("The updated authlib-inejector cannot be recognized")); + LOG.info("No local authlib-injector found, downloading"); + updateChecked.set(true); + update(); + local = getLocalArtifact(); + return local.orElseThrow(() -> new IOException("The downloaded authlib-inejector cannot be recognized")); } } @@ -83,10 +77,22 @@ public class AuthlibInjectorDownloader implements AuthlibInjectorArtifactProvide return getLocalArtifact(); } - private void update(Optional local) throws IOException { - LOG.info("Checking update of authlib-injector"); + private final AtomicBoolean updateChecked = new AtomicBoolean(false); + + public void checkUpdate() throws IOException { + // this method runs only once + if (updateChecked.compareAndSet(false, true)) { + synchronized (this) { + LOG.info("Checking update of authlib-injector"); + update(); + } + } + } + + private void update() throws IOException { AuthlibInjectorVersionInfo latest = getLatestArtifactInfo(); + Optional local = getLocalArtifact(); if (local.isPresent() && local.get().getBuildNumber() >= latest.buildNumber) { return; } @@ -116,11 +122,15 @@ public class AuthlibInjectorDownloader implements AuthlibInjectorArtifactProvide } private Optional getLocalArtifact() { - if (!Files.isRegularFile(artifactLocation)) { + return parseArtifact(artifactLocation); + } + + protected static Optional parseArtifact(Path path) { + if (!Files.isRegularFile(path)) { return Optional.empty(); } try { - return Optional.of(AuthlibInjectorArtifactInfo.from(artifactLocation)); + return Optional.of(AuthlibInjectorArtifactInfo.from(path)); } catch (IOException e) { LOG.log(Level.WARNING, "Bad authlib-injector artifact", e); return Optional.empty();