fix(launch): should not patch offline account skin when generating launch script or stopping the launcher immediately after game launched.

This commit is contained in:
huanghongxun 2021-12-19 18:44:49 +08:00
parent 9514357391
commit e57cb0ce51
8 changed files with 103 additions and 46 deletions

View File

@ -328,7 +328,7 @@ public class HMCLGameRepository extends DefaultGameRepository {
vs.setUsesGlobal(true); 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); VersionSetting vs = getVersionSetting(version);
LaunchOptions.Builder builder = new LaunchOptions.Builder() LaunchOptions.Builder builder = new LaunchOptions.Builder()
@ -358,7 +358,8 @@ public class HMCLGameRepository extends DefaultGameRepository {
.setNativesDir(vs.getNativesDir()) .setNativesDir(vs.getNativesDir())
.setProcessPriority(vs.getProcessPriority()) .setProcessPriority(vs.getProcessPriority())
.setUseNativeGLFW(vs.isUseNativeGLFW()) .setUseNativeGLFW(vs.isUseNativeGLFW())
.setUseNativeOpenAL(vs.isUseNativeOpenAL()); .setUseNativeOpenAL(vs.isUseNativeOpenAL())
.setDaemon(!makeLaunchScript && vs.getLauncherVisibility().isDaemon());
if (config().hasProxy()) { if (config().hasProxy()) {
builder.setProxy(ProxyManager.getProxy()); builder.setProxy(ProxyManager.getProxy());
if (config().hasProxyAuth()) { if (config().hasProxyAuth()) {

View File

@ -172,7 +172,7 @@ public final class LauncherHelper {
} }
}).withStage("launch.state.logging_in")) }).withStage("launch.state.logging_in"))
.thenComposeAsync(authInfo -> Task.supplyAsync(() -> { .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( return new HMCLGameLauncher(
repository, repository,
version, version,

View File

@ -43,5 +43,9 @@ public enum LauncherVisibility {
/** /**
* Hide the launcher and reopen it when game closes. * Hide the launcher and reopen it when game closes.
*/ */
HIDE_AND_REOPEN HIDE_AND_REOPEN;
public boolean isDaemon() {
return this != CLOSE;
}
} }

View File

@ -18,8 +18,10 @@
package org.jackhuang.hmcl.auth; package org.jackhuang.hmcl.auth;
import org.jackhuang.hmcl.game.Arguments; import org.jackhuang.hmcl.game.Arguments;
import org.jackhuang.hmcl.game.LaunchOptions;
import org.jackhuang.hmcl.util.Immutable; import org.jackhuang.hmcl.util.Immutable;
import java.io.IOException;
import java.util.UUID; import java.util.UUID;
/** /**
@ -27,26 +29,18 @@ import java.util.UUID;
* @author huangyuhui * @author huangyuhui
*/ */
@Immutable @Immutable
public final class AuthInfo implements AutoCloseable { public class AuthInfo implements AutoCloseable {
private final String username; private final String username;
private final UUID uuid; private final UUID uuid;
private final String accessToken; private final String accessToken;
private final String userProperties; private final String userProperties;
private final Arguments arguments;
private final AutoCloseable closeable;
public AuthInfo(String username, UUID uuid, String accessToken, String userProperties) { 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.username = username;
this.uuid = uuid; this.uuid = uuid;
this.accessToken = accessToken; this.accessToken = accessToken;
this.userProperties = userProperties; this.userProperties = userProperties;
this.arguments = arguments;
this.closeable = closeable;
} }
public String getUsername() { public String getUsername() {
@ -72,24 +66,14 @@ public final class AuthInfo implements AutoCloseable {
} }
/** /**
* Called when launching game.
* @return null if no argument is specified * @return null if no argument is specified
*/ */
public Arguments getArguments() { public Arguments getLaunchArguments(LaunchOptions options) throws IOException {
return arguments; return null;
}
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);
} }
@Override @Override
public void close() throws Exception { public void close() throws Exception {
if (closeable != null) {
closeable.close();
}
} }
} }

View File

@ -26,6 +26,7 @@ import org.jackhuang.hmcl.auth.yggdrasil.TextureType;
import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilAccount; import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilAccount;
import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilSession; import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilSession;
import org.jackhuang.hmcl.game.Arguments; import org.jackhuang.hmcl.game.Arguments;
import org.jackhuang.hmcl.game.LaunchOptions;
import org.jackhuang.hmcl.util.ToStringBuilder; import org.jackhuang.hmcl.util.ToStringBuilder;
import org.jackhuang.hmcl.util.function.ExceptionalSupplier; import org.jackhuang.hmcl.util.function.ExceptionalSupplier;
import java.io.IOException; import java.io.IOException;
@ -71,7 +72,7 @@ public class AuthlibInjectorAccount extends YggdrasilAccount {
Optional<String> prefetchedMeta = server.getMetadataResponse(); Optional<String> prefetchedMeta = server.getMetadataResponse();
if (auth.isPresent() && artifact.isPresent() && prefetchedMeta.isPresent()) { 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 { } else {
return Optional.empty(); return Optional.empty();
} }
@ -112,15 +113,31 @@ 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) { 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( return new Arguments().addJVMArguments(
"-javaagent:" + artifact.getLocation().toString() + "=" + server.getUrl(), "-javaagent:" + artifact.getLocation().toString() + "=" + server.getUrl(),
"-Dauthlibinjector.side=client", "-Dauthlibinjector.side=client",
"-Dauthlibinjector.yggdrasil.prefetched=" + Base64.getEncoder().encodeToString(prefetchedMeta.getBytes(UTF_8))); "-Dauthlibinjector.yggdrasil.prefetched=" + Base64.getEncoder().encodeToString(prefetchedMeta.getBytes(UTF_8)));
} }
}
@Override @Override
public Map<Object, Object> toStorage() { public Map<Object, Object> toStorage() {

View File

@ -28,6 +28,7 @@ import org.jackhuang.hmcl.auth.yggdrasil.Texture;
import org.jackhuang.hmcl.auth.yggdrasil.TextureModel; import org.jackhuang.hmcl.auth.yggdrasil.TextureModel;
import org.jackhuang.hmcl.auth.yggdrasil.TextureType; import org.jackhuang.hmcl.auth.yggdrasil.TextureType;
import org.jackhuang.hmcl.game.Arguments; import org.jackhuang.hmcl.game.Arguments;
import org.jackhuang.hmcl.game.LaunchOptions;
import org.jackhuang.hmcl.util.StringUtils; import org.jackhuang.hmcl.util.StringUtils;
import org.jackhuang.hmcl.util.ToStringBuilder; import org.jackhuang.hmcl.util.ToStringBuilder;
import org.jackhuang.hmcl.util.gson.UUIDTypeAdapter; import org.jackhuang.hmcl.util.gson.UUIDTypeAdapter;
@ -129,15 +130,7 @@ public class OfflineAccount extends Account {
} }
try { try {
YggdrasilServer server = new YggdrasilServer(0); return new OfflineAuthInfo(authInfo, artifact);
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);
} catch (Exception e) { } catch (Exception e) {
throw new AuthenticationException(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 @Override
public Optional<AuthInfo> playOffline() throws AuthenticationException { public Optional<AuthInfo> playOffline() throws AuthenticationException {
return Optional.of(logIn()); return Optional.of(logIn());

View File

@ -59,6 +59,7 @@ public class LaunchOptions implements Serializable {
private ProcessPriority processPriority = ProcessPriority.NORMAL; private ProcessPriority processPriority = ProcessPriority.NORMAL;
private boolean useNativeGLFW; private boolean useNativeGLFW;
private boolean useNativeOpenAL; private boolean useNativeOpenAL;
private boolean daemon;
/** /**
* The game directory * The game directory
@ -243,6 +244,13 @@ public class LaunchOptions implements Serializable {
return useNativeOpenAL; return useNativeOpenAL;
} }
/**
* Will launcher keeps alive after game launched or not.
*/
public boolean isDaemon() {
return daemon;
}
public static class Builder { public static class Builder {
private final LaunchOptions options = new LaunchOptions(); private final LaunchOptions options = new LaunchOptions();
@ -411,6 +419,10 @@ public class LaunchOptions implements Serializable {
return options.useNativeOpenAL; return options.useNativeOpenAL;
} }
public boolean isDaemon() {
return options.daemon;
}
public Builder setGameDir(File gameDir) { public Builder setGameDir(File gameDir) {
options.gameDir = gameDir; options.gameDir = gameDir;
return this; return this;
@ -543,5 +555,10 @@ public class LaunchOptions implements Serializable {
return this; return this;
} }
public Builder setDaemon(boolean daemon) {
options.daemon = daemon;
return this;
}
} }
} }

View File

@ -255,8 +255,9 @@ public class DefaultLauncher extends Launcher {
configuration.put("${natives_directory}", nativeFolderPath); configuration.put("${natives_directory}", nativeFolderPath);
res.addAll(Arguments.parseArguments(version.getArguments().map(Arguments::getJvm).orElseGet(this::getDefaultJVMArguments), configuration)); 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()) Arguments argumentsFromAuthInfo = authInfo.getLaunchArguments(options);
res.addAll(Arguments.parseArguments(authInfo.getArguments().getJvm(), configuration)); if (argumentsFromAuthInfo != null && argumentsFromAuthInfo.getJvm() != null && !argumentsFromAuthInfo.getJvm().isEmpty())
res.addAll(Arguments.parseArguments(argumentsFromAuthInfo.getJvm(), configuration));
res.add(version.getMainClass()); res.add(version.getMainClass());
@ -267,8 +268,8 @@ public class DefaultLauncher extends Launcher {
if (version.getMinecraftArguments().isPresent()) { if (version.getMinecraftArguments().isPresent()) {
res.addAll(Arguments.parseArguments(this.getDefaultGameArguments(), configuration, features)); res.addAll(Arguments.parseArguments(this.getDefaultGameArguments(), configuration, features));
} }
if (authInfo.getArguments() != null && authInfo.getArguments().getGame() != null && !authInfo.getArguments().getGame().isEmpty()) if (argumentsFromAuthInfo != null && argumentsFromAuthInfo.getGame() != null && !argumentsFromAuthInfo.getGame().isEmpty())
res.addAll(Arguments.parseArguments(authInfo.getArguments().getGame(), configuration, features)); res.addAll(Arguments.parseArguments(argumentsFromAuthInfo.getGame(), configuration, features));
if (StringUtils.isNotBlank(options.getServerIp())) { if (StringUtils.isNotBlank(options.getServerIp())) {
String[] args = options.getServerIp().split(":"); String[] args = options.getServerIp().split(":");