diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameRepository.java b/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameRepository.java index bc6ca59a1..94b0e6829 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameRepository.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameRepository.java @@ -328,7 +328,7 @@ public class HMCLGameRepository extends DefaultGameRepository { vs.setUsesGlobal(true); } - public LaunchOptions getLaunchOptions(String version, JavaVersion javaVersion, File gameDir) { + public LaunchOptions getLaunchOptions(String version, JavaVersion javaVersion, File gameDir, boolean makeLaunchScript) { VersionSetting vs = getVersionSetting(version); LaunchOptions.Builder builder = new LaunchOptions.Builder() @@ -358,7 +358,8 @@ public class HMCLGameRepository extends DefaultGameRepository { .setNativesDir(vs.getNativesDir()) .setProcessPriority(vs.getProcessPriority()) .setUseNativeGLFW(vs.isUseNativeGLFW()) - .setUseNativeOpenAL(vs.isUseNativeOpenAL()); + .setUseNativeOpenAL(vs.isUseNativeOpenAL()) + .setDaemon(!makeLaunchScript && vs.getLauncherVisibility().isDaemon()); if (config().hasProxy()) { builder.setProxy(ProxyManager.getProxy()); if (config().hasProxyAuth()) { diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/game/LauncherHelper.java b/HMCL/src/main/java/org/jackhuang/hmcl/game/LauncherHelper.java index f39c21568..d3fb71abc 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/game/LauncherHelper.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/game/LauncherHelper.java @@ -172,7 +172,7 @@ public final class LauncherHelper { } }).withStage("launch.state.logging_in")) .thenComposeAsync(authInfo -> Task.supplyAsync(() -> { - LaunchOptions launchOptions = repository.getLaunchOptions(selectedVersion, javaVersionRef.get(), profile.getGameDir()); + LaunchOptions launchOptions = repository.getLaunchOptions(selectedVersion, javaVersionRef.get(), profile.getGameDir(), scriptFile != null); return new HMCLGameLauncher( repository, version, diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/setting/LauncherVisibility.java b/HMCL/src/main/java/org/jackhuang/hmcl/setting/LauncherVisibility.java index 556cbedb5..f3ca33937 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/setting/LauncherVisibility.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/setting/LauncherVisibility.java @@ -43,5 +43,9 @@ public enum LauncherVisibility { /** * Hide the launcher and reopen it when game closes. */ - HIDE_AND_REOPEN + HIDE_AND_REOPEN; + + public boolean isDaemon() { + return this != CLOSE; + } } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/AuthInfo.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/AuthInfo.java index 73e130a08..aabbe7ebd 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/AuthInfo.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/AuthInfo.java @@ -18,8 +18,10 @@ package org.jackhuang.hmcl.auth; import org.jackhuang.hmcl.game.Arguments; +import org.jackhuang.hmcl.game.LaunchOptions; import org.jackhuang.hmcl.util.Immutable; +import java.io.IOException; import java.util.UUID; /** @@ -27,26 +29,18 @@ import java.util.UUID; * @author huangyuhui */ @Immutable -public final class AuthInfo implements AutoCloseable { +public class AuthInfo implements AutoCloseable { private final String username; private final UUID uuid; private final String accessToken; private final String userProperties; - private final Arguments arguments; - private final AutoCloseable closeable; public AuthInfo(String username, UUID uuid, String accessToken, String userProperties) { - this(username, uuid, accessToken, userProperties, null, null); - } - - public AuthInfo(String username, UUID uuid, String accessToken, String userProperties, Arguments arguments, AutoCloseable closeable) { this.username = username; this.uuid = uuid; this.accessToken = accessToken; this.userProperties = userProperties; - this.arguments = arguments; - this.closeable = closeable; } public String getUsername() { @@ -72,24 +66,14 @@ public final class AuthInfo implements AutoCloseable { } /** + * Called when launching game. * @return null if no argument is specified */ - public Arguments getArguments() { - return arguments; - } - - public AuthInfo withArguments(Arguments arguments) { - return new AuthInfo(username, uuid, accessToken, userProperties, arguments, closeable); - } - - public AuthInfo withCloseable(AutoCloseable closeable) { - return new AuthInfo(username, uuid, accessToken, userProperties, arguments, closeable); + public Arguments getLaunchArguments(LaunchOptions options) throws IOException { + return null; } @Override public void close() throws Exception { - if (closeable != null) { - closeable.close(); - } } } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/authlibinjector/AuthlibInjectorAccount.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/authlibinjector/AuthlibInjectorAccount.java index 163bcf221..054dd51fe 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/authlibinjector/AuthlibInjectorAccount.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/authlibinjector/AuthlibInjectorAccount.java @@ -26,6 +26,7 @@ import org.jackhuang.hmcl.auth.yggdrasil.TextureType; import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilAccount; import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilSession; import org.jackhuang.hmcl.game.Arguments; +import org.jackhuang.hmcl.game.LaunchOptions; import org.jackhuang.hmcl.util.ToStringBuilder; import org.jackhuang.hmcl.util.function.ExceptionalSupplier; import java.io.IOException; @@ -71,7 +72,7 @@ public class AuthlibInjectorAccount extends YggdrasilAccount { Optional prefetchedMeta = server.getMetadataResponse(); if (auth.isPresent() && artifact.isPresent() && prefetchedMeta.isPresent()) { - return Optional.of(auth.get().withArguments(generateArguments(artifact.get(), server, prefetchedMeta.get()))); + return Optional.of(new AuthlibInjectorAuthInfo(auth.get(), artifact.get(), server, prefetchedMeta.get())); } else { return Optional.empty(); } @@ -112,14 +113,30 @@ public class AuthlibInjectorAccount extends YggdrasilAccount { } } - return auth.withArguments(generateArguments(artifact, server, prefetchedMeta)); + return new AuthlibInjectorAuthInfo(auth, artifact, server, prefetchedMeta); } - private static Arguments generateArguments(AuthlibInjectorArtifactInfo artifact, AuthlibInjectorServer server, String prefetchedMeta) { - return new Arguments().addJVMArguments( - "-javaagent:" + artifact.getLocation().toString() + "=" + server.getUrl(), - "-Dauthlibinjector.side=client", - "-Dauthlibinjector.yggdrasil.prefetched=" + Base64.getEncoder().encodeToString(prefetchedMeta.getBytes(UTF_8))); + private static class AuthlibInjectorAuthInfo extends AuthInfo { + + private final AuthlibInjectorArtifactInfo artifact; + private final AuthlibInjectorServer server; + private final String prefetchedMeta; + + public AuthlibInjectorAuthInfo(AuthInfo authInfo, AuthlibInjectorArtifactInfo artifact, AuthlibInjectorServer server, String prefetchedMeta) { + super(authInfo.getUsername(), authInfo.getUUID(), authInfo.getAccessToken(), authInfo.getUserProperties()); + + this.artifact = artifact; + this.server = server; + this.prefetchedMeta = prefetchedMeta; + } + + @Override + public Arguments getLaunchArguments(LaunchOptions options) { + return new Arguments().addJVMArguments( + "-javaagent:" + artifact.getLocation().toString() + "=" + server.getUrl(), + "-Dauthlibinjector.side=client", + "-Dauthlibinjector.yggdrasil.prefetched=" + Base64.getEncoder().encodeToString(prefetchedMeta.getBytes(UTF_8))); + } } @Override diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/offline/OfflineAccount.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/offline/OfflineAccount.java index e4bffa492..154350402 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/offline/OfflineAccount.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/offline/OfflineAccount.java @@ -28,6 +28,7 @@ import org.jackhuang.hmcl.auth.yggdrasil.Texture; import org.jackhuang.hmcl.auth.yggdrasil.TextureModel; import org.jackhuang.hmcl.auth.yggdrasil.TextureType; import org.jackhuang.hmcl.game.Arguments; +import org.jackhuang.hmcl.game.LaunchOptions; import org.jackhuang.hmcl.util.StringUtils; import org.jackhuang.hmcl.util.ToStringBuilder; import org.jackhuang.hmcl.util.gson.UUIDTypeAdapter; @@ -129,15 +130,7 @@ public class OfflineAccount extends Account { } try { - YggdrasilServer server = new YggdrasilServer(0); - server.start(); - server.addCharacter(new YggdrasilServer.Character(uuid, username, skin.load(username).run())); - - return authInfo.withArguments(new Arguments().addJVMArguments( - "-javaagent:" + artifact.getLocation().toString() + "=" + "http://localhost:" + server.getListeningPort(), - "-Dauthlibinjector.side=client" - )) - .withCloseable(server::stop); + return new OfflineAuthInfo(authInfo, artifact); } catch (Exception e) { throw new AuthenticationException(e); } @@ -146,6 +139,46 @@ public class OfflineAccount extends Account { } } + private class OfflineAuthInfo extends AuthInfo { + private final AuthlibInjectorArtifactInfo artifact; + private YggdrasilServer server; + + public OfflineAuthInfo(AuthInfo authInfo, AuthlibInjectorArtifactInfo artifact) { + super(authInfo.getUsername(), authInfo.getUUID(), authInfo.getAccessToken(), authInfo.getUserProperties()); + + this.artifact = artifact; + } + + @Override + public Arguments getLaunchArguments(LaunchOptions options) throws IOException { + if (!options.isDaemon()) return null; + + server = new YggdrasilServer(0); + server.start(); + + try { + server.addCharacter(new YggdrasilServer.Character(uuid, username, skin.load(username).run())); + } catch (IOException e) { + throw e; + } catch (Exception e) { + throw new IOException(e); + } + + return new Arguments().addJVMArguments( + "-javaagent:" + artifact.getLocation().toString() + "=" + "http://localhost:" + server.getListeningPort(), + "-Dauthlibinjector.side=client" + ); + } + + @Override + public void close() throws Exception { + super.close(); + + if (server != null) + server.stop(); + } + } + @Override public Optional playOffline() throws AuthenticationException { return Optional.of(logIn()); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/LaunchOptions.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/LaunchOptions.java index 76a50c34c..88f5e6fb4 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/LaunchOptions.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/LaunchOptions.java @@ -59,6 +59,7 @@ public class LaunchOptions implements Serializable { private ProcessPriority processPriority = ProcessPriority.NORMAL; private boolean useNativeGLFW; private boolean useNativeOpenAL; + private boolean daemon; /** * The game directory @@ -243,6 +244,13 @@ public class LaunchOptions implements Serializable { return useNativeOpenAL; } + /** + * Will launcher keeps alive after game launched or not. + */ + public boolean isDaemon() { + return daemon; + } + public static class Builder { private final LaunchOptions options = new LaunchOptions(); @@ -411,6 +419,10 @@ public class LaunchOptions implements Serializable { return options.useNativeOpenAL; } + public boolean isDaemon() { + return options.daemon; + } + public Builder setGameDir(File gameDir) { options.gameDir = gameDir; return this; @@ -543,5 +555,10 @@ public class LaunchOptions implements Serializable { return this; } + public Builder setDaemon(boolean daemon) { + options.daemon = daemon; + return this; + } + } } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/launch/DefaultLauncher.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/launch/DefaultLauncher.java index 48b3a321d..8682197a1 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/launch/DefaultLauncher.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/launch/DefaultLauncher.java @@ -255,8 +255,9 @@ public class DefaultLauncher extends Launcher { configuration.put("${natives_directory}", nativeFolderPath); res.addAll(Arguments.parseArguments(version.getArguments().map(Arguments::getJvm).orElseGet(this::getDefaultJVMArguments), configuration)); - if (authInfo.getArguments() != null && authInfo.getArguments().getJvm() != null && !authInfo.getArguments().getJvm().isEmpty()) - res.addAll(Arguments.parseArguments(authInfo.getArguments().getJvm(), configuration)); + Arguments argumentsFromAuthInfo = authInfo.getLaunchArguments(options); + if (argumentsFromAuthInfo != null && argumentsFromAuthInfo.getJvm() != null && !argumentsFromAuthInfo.getJvm().isEmpty()) + res.addAll(Arguments.parseArguments(argumentsFromAuthInfo.getJvm(), configuration)); res.add(version.getMainClass()); @@ -267,8 +268,8 @@ public class DefaultLauncher extends Launcher { if (version.getMinecraftArguments().isPresent()) { res.addAll(Arguments.parseArguments(this.getDefaultGameArguments(), configuration, features)); } - if (authInfo.getArguments() != null && authInfo.getArguments().getGame() != null && !authInfo.getArguments().getGame().isEmpty()) - res.addAll(Arguments.parseArguments(authInfo.getArguments().getGame(), configuration, features)); + if (argumentsFromAuthInfo != null && argumentsFromAuthInfo.getGame() != null && !argumentsFromAuthInfo.getGame().isEmpty()) + res.addAll(Arguments.parseArguments(argumentsFromAuthInfo.getGame(), configuration, features)); if (StringUtils.isNotBlank(options.getServerIp())) { String[] args = options.getServerIp().split(":");