Authlib Injector support
This commit is contained in:
@@ -57,6 +57,12 @@ public abstract class Account {
|
||||
*/
|
||||
public abstract AuthInfo logIn(MultiCharacterSelector selector, Proxy proxy) throws AuthenticationException;
|
||||
|
||||
public AuthInfo logInWithPassword(MultiCharacterSelector selector, String password) throws AuthenticationException {
|
||||
return logInWithPassword(selector, password, Proxy.NO_PROXY);
|
||||
}
|
||||
|
||||
public abstract AuthInfo logInWithPassword(MultiCharacterSelector selector, String password, Proxy proxy) throws AuthenticationException;
|
||||
|
||||
public abstract boolean canPlayOffline();
|
||||
|
||||
/**
|
||||
|
||||
@@ -64,6 +64,11 @@ public class OfflineAccount extends Account {
|
||||
return new AuthInfo(username, uuid, uuid);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuthInfo logInWithPassword(MultiCharacterSelector selector, String password, Proxy proxy) throws AuthenticationException {
|
||||
return logIn(selector, proxy);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void logOut() {
|
||||
// Offline account need not log out.
|
||||
|
||||
@@ -32,7 +32,7 @@ public class OfflineAccountFactory extends AccountFactory<OfflineAccount> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public OfflineAccount fromUsername(String username, String password) {
|
||||
public OfflineAccount fromUsername(String username, String password, Object additionalData) {
|
||||
return new OfflineAccount(username, getUUIDFromUserName(username));
|
||||
}
|
||||
|
||||
|
||||
@@ -29,13 +29,7 @@ public class AuthlibInjectorAccount extends YggdrasilAccount {
|
||||
// Authlib Injector recommends launchers to pre-fetch the server basic information before launched the game to save time.
|
||||
GetTask getTask = new GetTask(NetworkUtils.toURL(serverBaseURL));
|
||||
AtomicBoolean flag = new AtomicBoolean(true);
|
||||
Thread thread = Lang.thread(() -> {
|
||||
try {
|
||||
getTask.run();
|
||||
} catch (Exception ignore) {
|
||||
flag.set(false);
|
||||
}
|
||||
});
|
||||
Thread thread = Lang.thread(() -> flag.set(getTask.test()));
|
||||
|
||||
AuthInfo info = super.logIn(selector, proxy);
|
||||
try {
|
||||
@@ -53,6 +47,29 @@ public class AuthlibInjectorAccount extends YggdrasilAccount {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuthInfo logInWithPassword(MultiCharacterSelector selector, String password, Proxy proxy) throws AuthenticationException {
|
||||
// Authlib Injector recommends launchers to pre-fetch the server basic information before launched the game to save time.
|
||||
GetTask getTask = new GetTask(NetworkUtils.toURL(serverBaseURL));
|
||||
AtomicBoolean flag = new AtomicBoolean(true);
|
||||
Thread thread = Lang.thread(() -> flag.set(getTask.test()));
|
||||
|
||||
AuthInfo info = super.logInWithPassword(selector, password, proxy);
|
||||
try {
|
||||
thread.join();
|
||||
|
||||
String arg = "-javaagent:" + injectorJarPath.get() + "=" + serverBaseURL;
|
||||
Arguments arguments = Arguments.addJVMArguments(null, arg);
|
||||
|
||||
//if (flag.get())
|
||||
// arguments = Arguments.addJVMArguments(arguments, "-Dorg.to2mbn.authlibinjector.config.prefetched=" + getTask.getResult());
|
||||
|
||||
return info.setArguments(arguments);
|
||||
} catch (Exception e) {
|
||||
throw new AuthenticationException("Unable to get authlib injector jar path", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<Object, Object> toStorageImpl() {
|
||||
Map<Object, Object> map = super.toStorageImpl();
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
package org.jackhuang.hmcl.auth.yggdrasil;
|
||||
|
||||
public class AuthlibInjectorServerInfo {
|
||||
private final String serverIp;
|
||||
private final String serverName;
|
||||
|
||||
public AuthlibInjectorServerInfo(String serverIp, String serverName) {
|
||||
this.serverIp = serverIp;
|
||||
this.serverName = serverName;
|
||||
}
|
||||
|
||||
public String getServerIp() {
|
||||
return serverIp;
|
||||
}
|
||||
|
||||
public String getServerName() {
|
||||
return serverName;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package org.jackhuang.hmcl.auth.yggdrasil;
|
||||
|
||||
public class AuthlibInjectorServerResponse {
|
||||
|
||||
private final Meta meta;
|
||||
|
||||
public AuthlibInjectorServerResponse() {
|
||||
this(new Meta());
|
||||
}
|
||||
|
||||
public AuthlibInjectorServerResponse(Meta meta) {
|
||||
this.meta = meta;
|
||||
}
|
||||
|
||||
public Meta getMeta() {
|
||||
return meta;
|
||||
}
|
||||
|
||||
public static class Meta {
|
||||
private final String serverName;
|
||||
|
||||
public Meta() {
|
||||
this("");
|
||||
}
|
||||
|
||||
public Meta(String serverName) {
|
||||
this.serverName = serverName;
|
||||
}
|
||||
|
||||
public String getServerName() {
|
||||
return serverName;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -132,24 +132,47 @@ public class YggdrasilAccount extends Account {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuthInfo logInWithPassword(MultiCharacterSelector selector, String password, Proxy proxy) throws AuthenticationException {
|
||||
logInWithPassword0(password, proxy);
|
||||
if (!isLoggedIn())
|
||||
throw new AuthenticationException("Wrong password for account " + username);
|
||||
|
||||
if (selectedProfile == null) {
|
||||
if (profiles == null || profiles.length <= 0)
|
||||
throw new NoCharacterException(this);
|
||||
|
||||
selectedProfile = selector.select(this, Arrays.asList(profiles));
|
||||
}
|
||||
return new AuthInfo(selectedProfile, accessToken, userType, GSON.toJson(userProperties));
|
||||
}
|
||||
|
||||
private void logIn0(Proxy proxy) throws AuthenticationException {
|
||||
if (StringUtils.isNotBlank(accessToken)) {
|
||||
if (StringUtils.isBlank(userId))
|
||||
if (StringUtils.isNotBlank(username))
|
||||
userId = username;
|
||||
else
|
||||
throw new AuthenticationException("Invalid uuid and username");
|
||||
if (checkTokenValidity(proxy)) {
|
||||
isOnline = true;
|
||||
return;
|
||||
}
|
||||
logIn1(routeRefresh, new RefreshRequest(accessToken, clientToken), proxy);
|
||||
logInWithToken(proxy);
|
||||
} else if (StringUtils.isNotBlank(password))
|
||||
logIn1(routeAuthenticate, new AuthenticationRequest(username, password, clientToken), proxy);
|
||||
logInWithPassword0(password, proxy);
|
||||
else
|
||||
throw new AuthenticationException("Password cannot be blank");
|
||||
}
|
||||
|
||||
private void logInWithToken(Proxy proxy) throws AuthenticationException {
|
||||
if (StringUtils.isBlank(userId))
|
||||
if (StringUtils.isNotBlank(username))
|
||||
userId = username;
|
||||
else
|
||||
throw new AuthenticationException("Invalid uuid and username");
|
||||
if (checkTokenValidity(proxy)) {
|
||||
isOnline = true;
|
||||
return;
|
||||
}
|
||||
logIn1(routeRefresh, new RefreshRequest(accessToken, clientToken), proxy);
|
||||
}
|
||||
|
||||
public void logInWithPassword0(String password, Proxy proxy) throws AuthenticationException {
|
||||
logIn1(routeAuthenticate, new AuthenticationRequest(username, password, clientToken), proxy);
|
||||
}
|
||||
|
||||
private void logIn1(URL url, Object input, Proxy proxy) throws AuthenticationException {
|
||||
AuthenticationResponse response = makeRequest(url, input, proxy)
|
||||
.orElseThrow(() -> new AuthenticationException("Server response empty"));
|
||||
|
||||
@@ -138,10 +138,16 @@ public class DefaultLauncher extends Launcher {
|
||||
configuration.put("${assets_root}", gameAssets.getAbsolutePath());
|
||||
|
||||
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));
|
||||
|
||||
res.add(version.getMainClass());
|
||||
|
||||
Map<String, Boolean> features = getFeatures();
|
||||
res.addAll(Arguments.parseArguments(version.getArguments().map(Arguments::getGame).orElseGet(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));
|
||||
|
||||
res.addAll(Arguments.parseStringArguments(version.getMinecraftArguments().map(StringUtils::tokenize).orElseGet(LinkedList::new), configuration));
|
||||
|
||||
// Optional Minecraft arguments
|
||||
|
||||
@@ -192,6 +192,7 @@ public final class TaskExecutor {
|
||||
// do nothing
|
||||
} catch (Exception e) {
|
||||
lastException = e;
|
||||
variables.set("lastException", e);
|
||||
Logging.LOG.log(Level.FINE, "Task failed: " + task.getName(), e);
|
||||
task.onDone().fireEvent(new TaskEvent(this, task, true));
|
||||
taskListeners.forEach(it -> it.onFailed(task, e));
|
||||
|
||||
@@ -35,15 +35,15 @@ public final class AutoTypingMap<K> {
|
||||
this.impl = impl;
|
||||
}
|
||||
|
||||
public <V> V get(K key) {
|
||||
public synchronized <V> V get(K key) {
|
||||
return (V) impl.get(key);
|
||||
}
|
||||
|
||||
public <V> Optional<V> getOptional(K key) {
|
||||
public synchronized <V> Optional<V> getOptional(K key) {
|
||||
return Optional.ofNullable(get(key));
|
||||
}
|
||||
|
||||
public void set(K key, Object value) {
|
||||
public synchronized void set(K key, Object value) {
|
||||
if (value != null)
|
||||
impl.put(key, value);
|
||||
}
|
||||
|
||||
@@ -93,7 +93,7 @@ public final class NetworkUtils {
|
||||
|
||||
public static HttpURLConnection createConnection(URL url, Proxy proxy) throws IOException {
|
||||
initHttps();
|
||||
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
|
||||
HttpURLConnection connection = (HttpURLConnection) url.openConnection(proxy);
|
||||
connection.setDoInput(true);
|
||||
connection.setUseCaches(false);
|
||||
connection.setConnectTimeout(15000);
|
||||
|
||||
Reference in New Issue
Block a user