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();