Merge pull request #342 from yushijinhun/refactor-skin
Yggdrasil 部分重构及 bug 修复
This commit is contained in:
@@ -17,9 +17,10 @@
|
|||||||
*/
|
*/
|
||||||
package org.jackhuang.hmcl.game;
|
package org.jackhuang.hmcl.game;
|
||||||
|
|
||||||
import com.jfoenix.concurrency.JFXUtilities;
|
import static org.jackhuang.hmcl.util.Lang.mapOf;
|
||||||
|
import static org.jackhuang.hmcl.util.Pair.pair;
|
||||||
|
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
import javafx.scene.layout.Region;
|
|
||||||
import org.jackhuang.hmcl.Launcher;
|
import org.jackhuang.hmcl.Launcher;
|
||||||
import org.jackhuang.hmcl.auth.Account;
|
import org.jackhuang.hmcl.auth.Account;
|
||||||
import org.jackhuang.hmcl.auth.AuthInfo;
|
import org.jackhuang.hmcl.auth.AuthInfo;
|
||||||
@@ -38,7 +39,6 @@ import org.jackhuang.hmcl.ui.Controllers;
|
|||||||
import org.jackhuang.hmcl.ui.DialogController;
|
import org.jackhuang.hmcl.ui.DialogController;
|
||||||
import org.jackhuang.hmcl.ui.LogWindow;
|
import org.jackhuang.hmcl.ui.LogWindow;
|
||||||
import org.jackhuang.hmcl.ui.construct.MessageBox;
|
import org.jackhuang.hmcl.ui.construct.MessageBox;
|
||||||
import org.jackhuang.hmcl.ui.construct.MessageDialogPane;
|
|
||||||
import org.jackhuang.hmcl.ui.construct.TaskExecutorDialogPane;
|
import org.jackhuang.hmcl.ui.construct.TaskExecutorDialogPane;
|
||||||
import org.jackhuang.hmcl.util.*;
|
import org.jackhuang.hmcl.util.*;
|
||||||
|
|
||||||
@@ -47,7 +47,6 @@ import java.util.*;
|
|||||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||||
import java.util.concurrent.CountDownLatch;
|
import java.util.concurrent.CountDownLatch;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
import java.util.function.Consumer;
|
|
||||||
|
|
||||||
public final class LauncherHelper {
|
public final class LauncherHelper {
|
||||||
public static final LauncherHelper INSTANCE = new LauncherHelper();
|
public static final LauncherHelper INSTANCE = new LauncherHelper();
|
||||||
@@ -301,10 +300,10 @@ public final class LauncherHelper {
|
|||||||
if (authInfo == null)
|
if (authInfo == null)
|
||||||
forbiddenTokens = Collections.emptyMap();
|
forbiddenTokens = Collections.emptyMap();
|
||||||
else
|
else
|
||||||
forbiddenTokens = Lang.mapOf(
|
forbiddenTokens = mapOf(
|
||||||
new Pair<>(authInfo.getAccessToken(), "<access token>"),
|
pair(authInfo.getAccessToken(), "<access token>"),
|
||||||
new Pair<>(authInfo.getUserId(), "<uuid>"),
|
pair(UUIDTypeAdapter.fromUUID(authInfo.getUUID()), "<uuid>"),
|
||||||
new Pair<>(authInfo.getUsername(), "<player>")
|
pair(authInfo.getUsername(), "<player>")
|
||||||
);
|
);
|
||||||
|
|
||||||
visibility = setting.getLauncherVisibility();
|
visibility = setting.getLauncherVisibility();
|
||||||
@@ -334,7 +333,7 @@ public final class LauncherHelper {
|
|||||||
else
|
else
|
||||||
System.out.print(log);
|
System.out.print(log);
|
||||||
|
|
||||||
logs.add(new Pair<>(log, level));
|
logs.add(pair(log, level));
|
||||||
if (logs.size() > Settings.INSTANCE.getLogLines())
|
if (logs.size() > Settings.INSTANCE.getLogLines())
|
||||||
logs.removeFirst();
|
logs.removeFirst();
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,9 @@
|
|||||||
*/
|
*/
|
||||||
package org.jackhuang.hmcl.setting;
|
package org.jackhuang.hmcl.setting;
|
||||||
|
|
||||||
|
import static org.jackhuang.hmcl.util.Lang.mapOf;
|
||||||
|
import static org.jackhuang.hmcl.util.Pair.pair;
|
||||||
|
|
||||||
import org.jackhuang.hmcl.Launcher;
|
import org.jackhuang.hmcl.Launcher;
|
||||||
import org.jackhuang.hmcl.auth.Account;
|
import org.jackhuang.hmcl.auth.Account;
|
||||||
import org.jackhuang.hmcl.auth.AccountFactory;
|
import org.jackhuang.hmcl.auth.AccountFactory;
|
||||||
@@ -50,10 +53,10 @@ public final class Accounts {
|
|||||||
public static final String YGGDRASIL_ACCOUNT_KEY = "yggdrasil";
|
public static final String YGGDRASIL_ACCOUNT_KEY = "yggdrasil";
|
||||||
public static final String AUTHLIB_INJECTOR_ACCOUNT_KEY = "authlibInjector";
|
public static final String AUTHLIB_INJECTOR_ACCOUNT_KEY = "authlibInjector";
|
||||||
|
|
||||||
public static final Map<String, AccountFactory<?>> ACCOUNT_FACTORY = Lang.mapOf(
|
public static final Map<String, AccountFactory<?>> ACCOUNT_FACTORY = mapOf(
|
||||||
new Pair<>(OFFLINE_ACCOUNT_KEY, OfflineAccountFactory.INSTANCE),
|
pair(OFFLINE_ACCOUNT_KEY, OfflineAccountFactory.INSTANCE),
|
||||||
new Pair<>(YGGDRASIL_ACCOUNT_KEY, new YggdrasilAccountFactory(MojangYggdrasilProvider.INSTANCE)),
|
pair(YGGDRASIL_ACCOUNT_KEY, new YggdrasilAccountFactory(MojangYggdrasilProvider.INSTANCE)),
|
||||||
new Pair<>(AUTHLIB_INJECTOR_ACCOUNT_KEY, new AuthlibInjectorAccountFactory(Accounts::downloadAuthlibInjector))
|
pair(AUTHLIB_INJECTOR_ACCOUNT_KEY, new AuthlibInjectorAccountFactory(Accounts::downloadAuthlibInjector))
|
||||||
);
|
);
|
||||||
|
|
||||||
private static final Map<String, String> AUTHLIB_INJECTOR_SERVER_NAMES = new HashMap<>();
|
private static final Map<String, String> AUTHLIB_INJECTOR_SERVER_NAMES = new HashMap<>();
|
||||||
|
|||||||
@@ -17,6 +17,9 @@
|
|||||||
*/
|
*/
|
||||||
package org.jackhuang.hmcl.ui.export;
|
package org.jackhuang.hmcl.ui.export;
|
||||||
|
|
||||||
|
import static org.jackhuang.hmcl.util.Lang.mapOf;
|
||||||
|
import static org.jackhuang.hmcl.util.Pair.pair;
|
||||||
|
|
||||||
import com.jfoenix.controls.JFXTreeView;
|
import com.jfoenix.controls.JFXTreeView;
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
import javafx.scene.control.CheckBox;
|
import javafx.scene.control.CheckBox;
|
||||||
@@ -33,8 +36,6 @@ import org.jackhuang.hmcl.ui.construct.NoneMultipleSelectionModel;
|
|||||||
import org.jackhuang.hmcl.ui.wizard.WizardController;
|
import org.jackhuang.hmcl.ui.wizard.WizardController;
|
||||||
import org.jackhuang.hmcl.ui.wizard.WizardPage;
|
import org.jackhuang.hmcl.ui.wizard.WizardPage;
|
||||||
import org.jackhuang.hmcl.util.FileUtils;
|
import org.jackhuang.hmcl.util.FileUtils;
|
||||||
import org.jackhuang.hmcl.util.Lang;
|
|
||||||
import org.jackhuang.hmcl.util.Pair;
|
|
||||||
import org.jackhuang.hmcl.util.StringUtils;
|
import org.jackhuang.hmcl.util.StringUtils;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
@@ -154,19 +155,19 @@ public final class ModpackFileSelectionPage extends StackPane implements WizardP
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static final String MODPACK_FILE_SELECTION = "modpack.accepted";
|
public static final String MODPACK_FILE_SELECTION = "modpack.accepted";
|
||||||
private static final Map<String, String> TRANSLATION = Lang.mapOf(
|
private static final Map<String, String> TRANSLATION = mapOf(
|
||||||
new Pair<>("minecraft/servers.dat", Launcher.i18n("modpack.files.servers_dat")),
|
pair("minecraft/servers.dat", Launcher.i18n("modpack.files.servers_dat")),
|
||||||
new Pair<>("minecraft/saves", Launcher.i18n("modpack.files.saves")),
|
pair("minecraft/saves", Launcher.i18n("modpack.files.saves")),
|
||||||
new Pair<>("minecraft/mods", Launcher.i18n("modpack.files.mods")),
|
pair("minecraft/mods", Launcher.i18n("modpack.files.mods")),
|
||||||
new Pair<>("minecraft/config", Launcher.i18n("modpack.files.config")),
|
pair("minecraft/config", Launcher.i18n("modpack.files.config")),
|
||||||
new Pair<>("minecraft/liteconfig", Launcher.i18n("modpack.files.liteconfig")),
|
pair("minecraft/liteconfig", Launcher.i18n("modpack.files.liteconfig")),
|
||||||
new Pair<>("minecraft/resourcepacks", Launcher.i18n("modpack.files.resourcepacks")),
|
pair("minecraft/resourcepacks", Launcher.i18n("modpack.files.resourcepacks")),
|
||||||
new Pair<>("minecraft/resources", Launcher.i18n("modpack.files.resourcepacks")),
|
pair("minecraft/resources", Launcher.i18n("modpack.files.resourcepacks")),
|
||||||
new Pair<>("minecraft/options.txt", Launcher.i18n("modpack.files.options_txt")),
|
pair("minecraft/options.txt", Launcher.i18n("modpack.files.options_txt")),
|
||||||
new Pair<>("minecraft/optionsshaders.txt", Launcher.i18n("modpack.files.optionsshaders_txt")),
|
pair("minecraft/optionsshaders.txt", Launcher.i18n("modpack.files.optionsshaders_txt")),
|
||||||
new Pair<>("minecraft/mods/VoxelMods", Launcher.i18n("modpack.files.mods.voxelmods")),
|
pair("minecraft/mods/VoxelMods", Launcher.i18n("modpack.files.mods.voxelmods")),
|
||||||
new Pair<>("minecraft/dumps", Launcher.i18n("modpack.files.dumps")),
|
pair("minecraft/dumps", Launcher.i18n("modpack.files.dumps")),
|
||||||
new Pair<>("minecraft/blueprints", Launcher.i18n("modpack.files.blueprints")),
|
pair("minecraft/blueprints", Launcher.i18n("modpack.files.blueprints")),
|
||||||
new Pair<>("minecraft/scripts", Launcher.i18n("modpack.files.scripts"))
|
pair("minecraft/scripts", Launcher.i18n("modpack.files.scripts"))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,8 +61,6 @@ public abstract class Account {
|
|||||||
*/
|
*/
|
||||||
public abstract AuthInfo playOffline();
|
public abstract AuthInfo playOffline();
|
||||||
|
|
||||||
public abstract void logOut();
|
|
||||||
|
|
||||||
public abstract Map<Object, Object> toStorage();
|
public abstract Map<Object, Object> toStorage();
|
||||||
|
|
||||||
public abstract void clearCache();
|
public abstract void clearCache();
|
||||||
|
|||||||
@@ -17,6 +17,8 @@
|
|||||||
*/
|
*/
|
||||||
package org.jackhuang.hmcl.auth;
|
package org.jackhuang.hmcl.auth;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
import org.jackhuang.hmcl.game.Arguments;
|
import org.jackhuang.hmcl.game.Arguments;
|
||||||
import org.jackhuang.hmcl.util.Immutable;
|
import org.jackhuang.hmcl.util.Immutable;
|
||||||
|
|
||||||
@@ -28,29 +30,19 @@ import org.jackhuang.hmcl.util.Immutable;
|
|||||||
public final class AuthInfo {
|
public final class AuthInfo {
|
||||||
|
|
||||||
private final String username;
|
private final String username;
|
||||||
private final String userId;
|
private final UUID uuid;
|
||||||
private final String accessToken;
|
private final String accessToken;
|
||||||
private final UserType userType;
|
|
||||||
private final String userProperties;
|
private final String userProperties;
|
||||||
private final Arguments arguments;
|
private final Arguments arguments;
|
||||||
|
|
||||||
public AuthInfo(String username, String userId, String accessToken) {
|
public AuthInfo(String username, UUID uuid, String accessToken, String userProperties) {
|
||||||
this(username, userId, accessToken, UserType.LEGACY);
|
this(username, uuid, accessToken, userProperties, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public AuthInfo(String username, String userId, String accessToken, UserType userType) {
|
public AuthInfo(String username, UUID uuid, String accessToken, String userProperties, Arguments arguments) {
|
||||||
this(username, userId, accessToken, userType, "{}");
|
|
||||||
}
|
|
||||||
|
|
||||||
public AuthInfo(String username, String userId, String accessToken, UserType userType, String userProperties) {
|
|
||||||
this(username, userId, accessToken, userType, userProperties, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public AuthInfo(String username, String userId, String accessToken, UserType userType, String userProperties, Arguments arguments) {
|
|
||||||
this.username = username;
|
this.username = username;
|
||||||
this.userId = userId;
|
this.uuid = uuid;
|
||||||
this.accessToken = accessToken;
|
this.accessToken = accessToken;
|
||||||
this.userType = userType;
|
|
||||||
this.userProperties = userProperties;
|
this.userProperties = userProperties;
|
||||||
this.arguments = arguments;
|
this.arguments = arguments;
|
||||||
}
|
}
|
||||||
@@ -59,18 +51,14 @@ public final class AuthInfo {
|
|||||||
return username;
|
return username;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getUserId() {
|
public UUID getUUID() {
|
||||||
return userId;
|
return uuid;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getAccessToken() {
|
public String getAccessToken() {
|
||||||
return accessToken;
|
return accessToken;
|
||||||
}
|
}
|
||||||
|
|
||||||
public UserType getUserType() {
|
|
||||||
return userType;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Properties of this user.
|
* Properties of this user.
|
||||||
* Don't know the difference between user properties and user property map.
|
* Don't know the difference between user properties and user property map.
|
||||||
@@ -81,11 +69,14 @@ public final class AuthInfo {
|
|||||||
return userProperties;
|
return userProperties;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return null if no argument is specified
|
||||||
|
*/
|
||||||
public Arguments getArguments() {
|
public Arguments getArguments() {
|
||||||
return arguments;
|
return arguments;
|
||||||
}
|
}
|
||||||
|
|
||||||
public AuthInfo setArguments(Arguments arguments) {
|
public AuthInfo withArguments(Arguments arguments) {
|
||||||
return new AuthInfo(username, userId, accessToken, userType, userProperties, arguments);
|
return new AuthInfo(username, uuid, accessToken, userProperties, arguments);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,49 +0,0 @@
|
|||||||
/*
|
|
||||||
* Hello Minecraft! Launcher.
|
|
||||||
* Copyright (C) 2018 huangyuhui <huanghongxun2008@126.com>
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
|
||||||
*/
|
|
||||||
package org.jackhuang.hmcl.auth;
|
|
||||||
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @author huangyuhui
|
|
||||||
*/
|
|
||||||
public enum UserType {
|
|
||||||
LEGACY,
|
|
||||||
MOJANG;
|
|
||||||
|
|
||||||
public static UserType fromName(String name) {
|
|
||||||
return BY_NAME.get(name.toLowerCase());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static UserType fromLegacy(boolean isLegacy) {
|
|
||||||
return isLegacy ? LEGACY : MOJANG;
|
|
||||||
}
|
|
||||||
|
|
||||||
static {
|
|
||||||
HashMap<String, UserType> byName = new HashMap<>();
|
|
||||||
for (UserType type : values())
|
|
||||||
byName.put(type.name().toLowerCase(), type);
|
|
||||||
BY_NAME = Collections.unmodifiableMap(byName);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static final Map<String, UserType> BY_NAME;
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -39,8 +39,8 @@ public class AuthlibInjectorAccount extends YggdrasilAccount {
|
|||||||
private final String serverBaseURL;
|
private final String serverBaseURL;
|
||||||
private final ExceptionalSupplier<String, ?> injectorJarPath;
|
private final ExceptionalSupplier<String, ?> injectorJarPath;
|
||||||
|
|
||||||
protected AuthlibInjectorAccount(YggdrasilService service, String serverBaseURL, ExceptionalSupplier<String, ?> injectorJarPath, String username, String clientToken, String character, YggdrasilSession session) {
|
protected AuthlibInjectorAccount(YggdrasilService service, String serverBaseURL, ExceptionalSupplier<String, ?> injectorJarPath, String username, String character, YggdrasilSession session) {
|
||||||
super(service, username, clientToken, character, session);
|
super(service, username, character, session);
|
||||||
|
|
||||||
this.injectorJarPath = injectorJarPath;
|
this.injectorJarPath = injectorJarPath;
|
||||||
this.serverBaseURL = serverBaseURL;
|
this.serverBaseURL = serverBaseURL;
|
||||||
@@ -72,7 +72,7 @@ public class AuthlibInjectorAccount extends YggdrasilAccount {
|
|||||||
if (flag.get())
|
if (flag.get())
|
||||||
arguments = Arguments.addJVMArguments(arguments, "-Dorg.to2mbn.authlibinjector.config.prefetched=" + new String(Base64.getEncoder().encode(getTask.getResult().getBytes()), UTF_8));
|
arguments = Arguments.addJVMArguments(arguments, "-Dorg.to2mbn.authlibinjector.config.prefetched=" + new String(Base64.getEncoder().encode(getTask.getResult().getBytes()), UTF_8));
|
||||||
|
|
||||||
return info.setArguments(arguments);
|
return info.withArguments(arguments);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new AuthenticationException("Unable to get authlib injector jar path", e);
|
throw new AuthenticationException("Unable to get authlib injector jar path", e);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,12 +7,10 @@ import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilService;
|
|||||||
import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilSession;
|
import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilSession;
|
||||||
import org.jackhuang.hmcl.util.ExceptionalSupplier;
|
import org.jackhuang.hmcl.util.ExceptionalSupplier;
|
||||||
import org.jackhuang.hmcl.util.NetworkUtils;
|
import org.jackhuang.hmcl.util.NetworkUtils;
|
||||||
import org.jackhuang.hmcl.util.UUIDTypeAdapter;
|
|
||||||
|
|
||||||
import java.net.Proxy;
|
import java.net.Proxy;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
import static org.jackhuang.hmcl.util.Lang.tryCast;
|
import static org.jackhuang.hmcl.util.Lang.tryCast;
|
||||||
|
|
||||||
@@ -34,7 +32,7 @@ public class AuthlibInjectorAccountFactory extends AccountFactory<AuthlibInjecto
|
|||||||
throw new IllegalArgumentException("Additional data should be server base url string for authlib injector accounts.");
|
throw new IllegalArgumentException("Additional data should be server base url string for authlib injector accounts.");
|
||||||
|
|
||||||
AuthlibInjectorAccount account = new AuthlibInjectorAccount(new YggdrasilService(new AuthlibInjectorProvider((String) serverBaseURL), proxy),
|
AuthlibInjectorAccount account = new AuthlibInjectorAccount(new YggdrasilService(new AuthlibInjectorProvider((String) serverBaseURL), proxy),
|
||||||
(String) serverBaseURL, injectorJarPathSupplier, username, UUIDTypeAdapter.fromUUID(UUID.randomUUID()), null, null);
|
(String) serverBaseURL, injectorJarPathSupplier, username, null, null);
|
||||||
account.logInWithPassword(password, selector);
|
account.logInWithPassword(password, selector);
|
||||||
return account;
|
return account;
|
||||||
}
|
}
|
||||||
@@ -46,14 +44,12 @@ public class AuthlibInjectorAccountFactory extends AccountFactory<AuthlibInjecto
|
|||||||
|
|
||||||
String username = tryCast(storage.get("username"), String.class)
|
String username = tryCast(storage.get("username"), String.class)
|
||||||
.orElseThrow(() -> new IllegalArgumentException("storage does not have username"));
|
.orElseThrow(() -> new IllegalArgumentException("storage does not have username"));
|
||||||
String clientToken = tryCast(storage.get("clientToken"), String.class)
|
|
||||||
.orElseThrow(() -> new IllegalArgumentException("storage does not have client token."));
|
|
||||||
String character = tryCast(storage.get("clientToken"), String.class)
|
String character = tryCast(storage.get("clientToken"), String.class)
|
||||||
.orElseThrow(() -> new IllegalArgumentException("storage does not have selected character name."));
|
.orElseThrow(() -> new IllegalArgumentException("storage does not have selected character name."));
|
||||||
String apiRoot = tryCast(storage.get("serverBaseURL"), String.class)
|
String apiRoot = tryCast(storage.get("serverBaseURL"), String.class)
|
||||||
.orElseThrow(() -> new IllegalArgumentException("storage does not have API root."));
|
.orElseThrow(() -> new IllegalArgumentException("storage does not have API root."));
|
||||||
|
|
||||||
return new AuthlibInjectorAccount(new YggdrasilService(new AuthlibInjectorProvider(apiRoot), proxy),
|
return new AuthlibInjectorAccount(new YggdrasilService(new AuthlibInjectorProvider(apiRoot), proxy),
|
||||||
apiRoot, injectorJarPathSupplier, username, clientToken, character, YggdrasilSession.fromStorage(storage));
|
apiRoot, injectorJarPathSupplier, username, character, YggdrasilSession.fromStorage(storage));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,15 +17,20 @@
|
|||||||
*/
|
*/
|
||||||
package org.jackhuang.hmcl.auth.offline;
|
package org.jackhuang.hmcl.auth.offline;
|
||||||
|
|
||||||
import org.jackhuang.hmcl.auth.Account;
|
import static org.jackhuang.hmcl.util.Lang.mapOf;
|
||||||
import org.jackhuang.hmcl.auth.AuthInfo;
|
import static org.jackhuang.hmcl.util.Pair.pair;
|
||||||
import org.jackhuang.hmcl.auth.AuthenticationException;
|
|
||||||
import org.jackhuang.hmcl.util.*;
|
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import org.jackhuang.hmcl.auth.Account;
|
||||||
|
import org.jackhuang.hmcl.auth.AuthInfo;
|
||||||
|
import org.jackhuang.hmcl.auth.AuthenticationException;
|
||||||
|
import org.jackhuang.hmcl.util.StringUtils;
|
||||||
|
import org.jackhuang.hmcl.util.ToStringBuilder;
|
||||||
|
import org.jackhuang.hmcl.util.UUIDTypeAdapter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @author huang
|
* @author huang
|
||||||
@@ -33,9 +38,9 @@ import java.util.UUID;
|
|||||||
public class OfflineAccount extends Account {
|
public class OfflineAccount extends Account {
|
||||||
|
|
||||||
private final String username;
|
private final String username;
|
||||||
private final String uuid;
|
private final UUID uuid;
|
||||||
|
|
||||||
OfflineAccount(String username, String uuid) {
|
OfflineAccount(String username, UUID uuid) {
|
||||||
Objects.requireNonNull(username);
|
Objects.requireNonNull(username);
|
||||||
Objects.requireNonNull(uuid);
|
Objects.requireNonNull(uuid);
|
||||||
|
|
||||||
@@ -48,7 +53,7 @@ public class OfflineAccount extends Account {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public UUID getUUID() {
|
public UUID getUUID() {
|
||||||
return UUIDTypeAdapter.fromString(uuid);
|
return uuid;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -66,7 +71,7 @@ public class OfflineAccount extends Account {
|
|||||||
if (StringUtils.isBlank(username))
|
if (StringUtils.isBlank(username))
|
||||||
throw new AuthenticationException("Username cannot be empty");
|
throw new AuthenticationException("Username cannot be empty");
|
||||||
|
|
||||||
return new AuthInfo(username, uuid, uuid);
|
return new AuthInfo(username, uuid, UUIDTypeAdapter.fromUUID(UUID.randomUUID()), "{}");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -74,11 +79,6 @@ public class OfflineAccount extends Account {
|
|||||||
return logIn();
|
return logIn();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void logOut() {
|
|
||||||
// Offline account need not log out.
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canPlayOffline() {
|
public boolean canPlayOffline() {
|
||||||
return false;
|
return false;
|
||||||
@@ -91,9 +91,9 @@ public class OfflineAccount extends Account {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<Object, Object> toStorage() {
|
public Map<Object, Object> toStorage() {
|
||||||
return Lang.mapOf(
|
return mapOf(
|
||||||
new Pair<>("uuid", uuid),
|
pair("uuid", UUIDTypeAdapter.fromUUID(uuid)),
|
||||||
new Pair<>("username", username)
|
pair("username", username)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -23,9 +23,9 @@ import org.jackhuang.hmcl.util.UUIDTypeAdapter;
|
|||||||
|
|
||||||
import java.net.Proxy;
|
import java.net.Proxy;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
import static org.jackhuang.hmcl.util.DigestUtils.digest;
|
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||||
import static org.jackhuang.hmcl.util.Hex.encodeHex;
|
|
||||||
import static org.jackhuang.hmcl.util.Lang.tryCast;
|
import static org.jackhuang.hmcl.util.Lang.tryCast;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -47,17 +47,15 @@ public class OfflineAccountFactory extends AccountFactory<OfflineAccount> {
|
|||||||
public OfflineAccount fromStorage(Map<Object, Object> storage, Proxy proxy) {
|
public OfflineAccount fromStorage(Map<Object, Object> storage, Proxy proxy) {
|
||||||
String username = tryCast(storage.get("username"), String.class)
|
String username = tryCast(storage.get("username"), String.class)
|
||||||
.orElseThrow(() -> new IllegalStateException("Offline account configuration malformed."));
|
.orElseThrow(() -> new IllegalStateException("Offline account configuration malformed."));
|
||||||
String uuid = tryCast(storage.get("uuid"), String.class)
|
UUID uuid = tryCast(storage.get("uuid"), String.class)
|
||||||
|
.map(UUIDTypeAdapter::fromString)
|
||||||
.orElse(getUUIDFromUserName(username));
|
.orElse(getUUIDFromUserName(username));
|
||||||
|
|
||||||
// Check if the uuid is vaild
|
|
||||||
UUIDTypeAdapter.fromString(uuid);
|
|
||||||
|
|
||||||
return new OfflineAccount(username, uuid);
|
return new OfflineAccount(username, uuid);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String getUUIDFromUserName(String username) {
|
private static UUID getUUIDFromUserName(String username) {
|
||||||
return encodeHex(digest("MD5", username));
|
return UUID.nameUUIDFromBytes(("OfflinePlayer:" + username).getBytes(UTF_8));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,66 +0,0 @@
|
|||||||
/*
|
|
||||||
* Hello Minecraft! Launcher.
|
|
||||||
* Copyright (C) 2018 huangyuhui <huanghongxun2008@126.com>
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
|
||||||
*/
|
|
||||||
package org.jackhuang.hmcl.auth.yggdrasil;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @author huangyuhui
|
|
||||||
*/
|
|
||||||
final class AuthenticationResponse extends ErrorResponse {
|
|
||||||
|
|
||||||
private final String accessToken;
|
|
||||||
private final String clientToken;
|
|
||||||
private final GameProfile selectedProfile;
|
|
||||||
private final GameProfile[] availableProfiles;
|
|
||||||
private final User user;
|
|
||||||
|
|
||||||
public AuthenticationResponse() {
|
|
||||||
this(null, null, null, null, null, null, null, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public AuthenticationResponse(String accessToken, String clientToken, GameProfile selectedProfile, GameProfile[] availableProfiles, User user, String error, String errorMessage, String cause) {
|
|
||||||
super(error, errorMessage, cause);
|
|
||||||
|
|
||||||
this.accessToken = accessToken;
|
|
||||||
this.clientToken = clientToken;
|
|
||||||
this.selectedProfile = selectedProfile;
|
|
||||||
this.availableProfiles = availableProfiles;
|
|
||||||
this.user = user;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getAccessToken() {
|
|
||||||
return accessToken;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getClientToken() {
|
|
||||||
return clientToken;
|
|
||||||
}
|
|
||||||
|
|
||||||
public GameProfile getSelectedProfile() {
|
|
||||||
return selectedProfile;
|
|
||||||
}
|
|
||||||
|
|
||||||
public GameProfile[] getAvailableProfiles() {
|
|
||||||
return availableProfiles;
|
|
||||||
}
|
|
||||||
|
|
||||||
public User getUser() {
|
|
||||||
return user;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
package org.jackhuang.hmcl.auth.yggdrasil;
|
|
||||||
|
|
||||||
class ErrorResponse {
|
|
||||||
private final String error;
|
|
||||||
private final String errorMessage;
|
|
||||||
private final String cause;
|
|
||||||
|
|
||||||
public ErrorResponse() {
|
|
||||||
this(null, null, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ErrorResponse(String error, String errorMessage, String cause) {
|
|
||||||
this.error = error;
|
|
||||||
this.errorMessage = errorMessage;
|
|
||||||
this.cause = cause;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getError() {
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getErrorMessage() {
|
|
||||||
return errorMessage;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getCause() {
|
|
||||||
return cause;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -17,11 +17,8 @@
|
|||||||
*/
|
*/
|
||||||
package org.jackhuang.hmcl.auth.yggdrasil;
|
package org.jackhuang.hmcl.auth.yggdrasil;
|
||||||
|
|
||||||
import com.google.gson.*;
|
|
||||||
import org.jackhuang.hmcl.auth.UserType;
|
|
||||||
import org.jackhuang.hmcl.util.Immutable;
|
import org.jackhuang.hmcl.util.Immutable;
|
||||||
|
|
||||||
import java.lang.reflect.Type;
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -34,7 +31,6 @@ public final class GameProfile {
|
|||||||
private final UUID id;
|
private final UUID id;
|
||||||
private final String name;
|
private final String name;
|
||||||
private final PropertyMap properties;
|
private final PropertyMap properties;
|
||||||
private final boolean legacy;
|
|
||||||
|
|
||||||
public GameProfile() {
|
public GameProfile() {
|
||||||
this(null, null);
|
this(null, null);
|
||||||
@@ -45,14 +41,9 @@ public final class GameProfile {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public GameProfile(UUID id, String name, PropertyMap properties) {
|
public GameProfile(UUID id, String name, PropertyMap properties) {
|
||||||
this(id, name, properties, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public GameProfile(UUID id, String name, PropertyMap properties, boolean legacy) {
|
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.properties = properties;
|
this.properties = properties;
|
||||||
this.legacy = legacy;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public UUID getId() {
|
public UUID getId() {
|
||||||
@@ -63,46 +54,11 @@ public final class GameProfile {
|
|||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return nullable
|
||||||
|
*/
|
||||||
public PropertyMap getProperties() {
|
public PropertyMap getProperties() {
|
||||||
return properties;
|
return properties;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isLegacy() {
|
|
||||||
return legacy;
|
|
||||||
}
|
|
||||||
|
|
||||||
public UserType getUserType() {
|
|
||||||
return UserType.fromLegacy(isLegacy());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class Serializer implements JsonSerializer<GameProfile>, JsonDeserializer<GameProfile> {
|
|
||||||
|
|
||||||
public static final Serializer INSTANCE = new Serializer();
|
|
||||||
|
|
||||||
private Serializer() {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public JsonElement serialize(GameProfile src, Type type, JsonSerializationContext context) {
|
|
||||||
JsonObject result = new JsonObject();
|
|
||||||
if (src.getId() != null)
|
|
||||||
result.add("id", context.serialize(src.getId()));
|
|
||||||
if (src.getName() != null)
|
|
||||||
result.addProperty("name", src.getName());
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public GameProfile deserialize(JsonElement je, Type type, JsonDeserializationContext context) throws JsonParseException {
|
|
||||||
if (!(je instanceof JsonObject))
|
|
||||||
throw new JsonParseException("The json element is not a JsonObject.");
|
|
||||||
|
|
||||||
JsonObject json = (JsonObject) je;
|
|
||||||
|
|
||||||
UUID id = json.has("id") ? context.deserialize(json.get("id"), UUID.class) : null;
|
|
||||||
String name = json.has("name") ? json.getAsJsonPrimitive("name").getAsString() : null;
|
|
||||||
return new GameProfile(id, name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,53 +0,0 @@
|
|||||||
/*
|
|
||||||
* Hello Minecraft! Launcher.
|
|
||||||
* Copyright (C) 2018 huangyuhui <huanghongxun2008@126.com>
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
|
||||||
*/
|
|
||||||
package org.jackhuang.hmcl.auth.yggdrasil;
|
|
||||||
|
|
||||||
import org.jackhuang.hmcl.util.Immutable;
|
|
||||||
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
@Immutable
|
|
||||||
public final class TextureResponse {
|
|
||||||
private final UUID profileId;
|
|
||||||
private final String profileName;
|
|
||||||
private final Map<TextureType, Texture> textures;
|
|
||||||
|
|
||||||
public TextureResponse() {
|
|
||||||
this(UUID.randomUUID(), "", Collections.emptyMap());
|
|
||||||
}
|
|
||||||
|
|
||||||
public TextureResponse(UUID profileId, String profileName, Map<TextureType, Texture> textures) {
|
|
||||||
this.profileId = profileId;
|
|
||||||
this.profileName = profileName;
|
|
||||||
this.textures = textures;
|
|
||||||
}
|
|
||||||
|
|
||||||
public UUID getProfileId() {
|
|
||||||
return profileId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getProfileName() {
|
|
||||||
return profileName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Map<TextureType, Texture> getTextures() {
|
|
||||||
return textures == null ? null : Collections.unmodifiableMap(textures);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -17,7 +17,6 @@
|
|||||||
*/
|
*/
|
||||||
package org.jackhuang.hmcl.auth.yggdrasil;
|
package org.jackhuang.hmcl.auth.yggdrasil;
|
||||||
|
|
||||||
import com.google.gson.GsonBuilder;
|
|
||||||
import org.jackhuang.hmcl.auth.*;
|
import org.jackhuang.hmcl.auth.*;
|
||||||
import org.jackhuang.hmcl.util.StringUtils;
|
import org.jackhuang.hmcl.util.StringUtils;
|
||||||
import org.jackhuang.hmcl.util.UUIDTypeAdapter;
|
import org.jackhuang.hmcl.util.UUIDTypeAdapter;
|
||||||
@@ -34,14 +33,12 @@ public class YggdrasilAccount extends Account {
|
|||||||
private final YggdrasilService service;
|
private final YggdrasilService service;
|
||||||
private boolean isOnline = false;
|
private boolean isOnline = false;
|
||||||
private YggdrasilSession session;
|
private YggdrasilSession session;
|
||||||
private final String clientToken;
|
|
||||||
private String character;
|
private String character;
|
||||||
|
|
||||||
protected YggdrasilAccount(YggdrasilService service, String username, String clientToken, String character, YggdrasilSession session) {
|
protected YggdrasilAccount(YggdrasilService service, String username, String character, YggdrasilSession session) {
|
||||||
this.service = service;
|
this.service = service;
|
||||||
this.username = username;
|
this.username = username;
|
||||||
this.session = session;
|
this.session = session;
|
||||||
this.clientToken = clientToken;
|
|
||||||
this.character = character;
|
this.character = character;
|
||||||
|
|
||||||
if (session == null || session.getSelectedProfile() == null || StringUtils.isBlank(session.getAccessToken()))
|
if (session == null || session.getSelectedProfile() == null || StringUtils.isBlank(session.getAccessToken()))
|
||||||
@@ -72,7 +69,7 @@ public class YggdrasilAccount extends Account {
|
|||||||
logInWithToken();
|
logInWithToken();
|
||||||
selectProfile(new SpecificCharacterSelector(character));
|
selectProfile(new SpecificCharacterSelector(character));
|
||||||
}
|
}
|
||||||
return toAuthInfo();
|
return session.toAuthInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -81,9 +78,9 @@ public class YggdrasilAccount extends Account {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected AuthInfo logInWithPassword(String password, CharacterSelector selector) throws AuthenticationException {
|
protected AuthInfo logInWithPassword(String password, CharacterSelector selector) throws AuthenticationException {
|
||||||
session = service.authenticate(username, password, clientToken);
|
session = service.authenticate(username, password, UUIDTypeAdapter.fromUUID(UUID.randomUUID()));
|
||||||
selectProfile(selector);
|
selectProfile(selector);
|
||||||
return toAuthInfo();
|
return session.toAuthInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void selectProfile(CharacterSelector selector) throws AuthenticationException {
|
private void selectProfile(CharacterSelector selector) throws AuthenticationException {
|
||||||
@@ -91,25 +88,18 @@ public class YggdrasilAccount extends Account {
|
|||||||
if (session.getAvailableProfiles() == null || session.getAvailableProfiles().length <= 0)
|
if (session.getAvailableProfiles() == null || session.getAvailableProfiles().length <= 0)
|
||||||
throw new NoCharacterException(this);
|
throw new NoCharacterException(this);
|
||||||
|
|
||||||
session.setSelectedProfile(selector.select(this, Arrays.asList(session.getAvailableProfiles())));
|
session = service.refresh(session.getAccessToken(), session.getClientToken(), selector.select(this, Arrays.asList(session.getAvailableProfiles())));
|
||||||
}
|
}
|
||||||
|
|
||||||
character = session.getSelectedProfile().getName();
|
character = session.getSelectedProfile().getName();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void logInWithToken() throws AuthenticationException {
|
private void logInWithToken() throws AuthenticationException {
|
||||||
if (service.validate(session.getAccessToken(), clientToken)) {
|
if (service.validate(session.getAccessToken(), session.getClientToken())) {
|
||||||
isOnline = true;
|
isOnline = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
session = service.refresh(session.getAccessToken(), clientToken);
|
session = service.refresh(session.getAccessToken(), session.getClientToken(), null);
|
||||||
}
|
|
||||||
|
|
||||||
private AuthInfo toAuthInfo() {
|
|
||||||
GameProfile profile = session.getSelectedProfile();
|
|
||||||
|
|
||||||
return new AuthInfo(profile.getName(), UUIDTypeAdapter.fromUUID(profile.getId()), session.getAccessToken(), profile.getUserType(),
|
|
||||||
new GsonBuilder().registerTypeAdapter(PropertyMap.class, PropertyMap.LegacySerializer.INSTANCE).create().toJson(Optional.ofNullable(session.getUser()).map(User::getProperties).orElseGet(PropertyMap::new)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -122,13 +112,7 @@ public class YggdrasilAccount extends Account {
|
|||||||
if (!canPlayOffline())
|
if (!canPlayOffline())
|
||||||
throw new IllegalStateException("Current account " + this + " cannot play offline.");
|
throw new IllegalStateException("Current account " + this + " cannot play offline.");
|
||||||
|
|
||||||
return toAuthInfo();
|
return session.toAuthInfo();
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void logOut() {
|
|
||||||
isOnline = false;
|
|
||||||
session = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -136,7 +120,6 @@ public class YggdrasilAccount extends Account {
|
|||||||
HashMap<Object, Object> storage = new HashMap<>();
|
HashMap<Object, Object> storage = new HashMap<>();
|
||||||
|
|
||||||
storage.put("username", getUsername());
|
storage.put("username", getUsername());
|
||||||
storage.put("clientToken", clientToken);
|
|
||||||
storage.put("character", character);
|
storage.put("character", character);
|
||||||
if (session != null)
|
if (session != null)
|
||||||
storage.putAll(session.toStorage());
|
storage.putAll(session.toStorage());
|
||||||
@@ -144,6 +127,7 @@ public class YggdrasilAccount extends Account {
|
|||||||
return storage;
|
return storage;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public UUID getUUID() {
|
public UUID getUUID() {
|
||||||
if (session == null || session.getSelectedProfile() == null)
|
if (session == null || session.getSelectedProfile() == null)
|
||||||
return null;
|
return null;
|
||||||
@@ -157,7 +141,7 @@ public class YggdrasilAccount extends Account {
|
|||||||
|
|
||||||
public Optional<Texture> getSkin(GameProfile profile) throws AuthenticationException {
|
public Optional<Texture> getSkin(GameProfile profile) throws AuthenticationException {
|
||||||
if (!service.getTextures(profile).isPresent()) {
|
if (!service.getTextures(profile).isPresent()) {
|
||||||
session.setAvailableProfile(profile = service.getCompleteGameProfile(profile.getId()));
|
profile = service.getCompleteGameProfile(profile.getId()).orElse(profile);
|
||||||
}
|
}
|
||||||
|
|
||||||
return service.getTextures(profile).map(map -> map.get(TextureType.SKIN));
|
return service.getTextures(profile).map(map -> map.get(TextureType.SKIN));
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ public class YggdrasilAccountFactory extends AccountFactory<YggdrasilAccount> {
|
|||||||
Objects.requireNonNull(password);
|
Objects.requireNonNull(password);
|
||||||
Objects.requireNonNull(proxy);
|
Objects.requireNonNull(proxy);
|
||||||
|
|
||||||
YggdrasilAccount account = new YggdrasilAccount(new YggdrasilService(provider, proxy), username, UUIDTypeAdapter.fromUUID(UUID.randomUUID()), null, null);
|
YggdrasilAccount account = new YggdrasilAccount(new YggdrasilService(provider, proxy), username, null, null);
|
||||||
account.logInWithPassword(password, selector);
|
account.logInWithPassword(password, selector);
|
||||||
return account;
|
return account;
|
||||||
}
|
}
|
||||||
@@ -60,12 +60,10 @@ public class YggdrasilAccountFactory extends AccountFactory<YggdrasilAccount> {
|
|||||||
|
|
||||||
String username = tryCast(storage.get("username"), String.class)
|
String username = tryCast(storage.get("username"), String.class)
|
||||||
.orElseThrow(() -> new IllegalArgumentException("storage does not have username"));
|
.orElseThrow(() -> new IllegalArgumentException("storage does not have username"));
|
||||||
String clientToken = tryCast(storage.get("clientToken"), String.class)
|
|
||||||
.orElseThrow(() -> new IllegalArgumentException("storage does not have client token."));
|
|
||||||
String character = tryCast(storage.get("clientToken"), String.class)
|
String character = tryCast(storage.get("clientToken"), String.class)
|
||||||
.orElseThrow(() -> new IllegalArgumentException("storage does not have selected character name."));
|
.orElseThrow(() -> new IllegalArgumentException("storage does not have selected character name."));
|
||||||
|
|
||||||
return new YggdrasilAccount(new YggdrasilService(provider, proxy), username, clientToken, character, YggdrasilSession.fromStorage(storage));
|
return new YggdrasilAccount(new YggdrasilService(provider, proxy), username, character, YggdrasilSession.fromStorage(storage));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String randomToken() {
|
public static String randomToken() {
|
||||||
|
|||||||
@@ -1,17 +1,33 @@
|
|||||||
package org.jackhuang.hmcl.auth.yggdrasil;
|
package org.jackhuang.hmcl.auth.yggdrasil;
|
||||||
|
|
||||||
import com.google.gson.Gson;
|
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||||
import com.google.gson.GsonBuilder;
|
import static org.jackhuang.hmcl.util.Lang.liftFunction;
|
||||||
import com.google.gson.JsonParseException;
|
import static org.jackhuang.hmcl.util.Lang.mapOf;
|
||||||
import org.jackhuang.hmcl.auth.*;
|
import static org.jackhuang.hmcl.util.Pair.pair;
|
||||||
import org.jackhuang.hmcl.util.*;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.Proxy;
|
import java.net.Proxy;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.util.*;
|
import java.util.Base64;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
import org.jackhuang.hmcl.auth.AuthenticationException;
|
||||||
|
import org.jackhuang.hmcl.auth.InvalidCredentialsException;
|
||||||
|
import org.jackhuang.hmcl.auth.InvalidPasswordException;
|
||||||
|
import org.jackhuang.hmcl.auth.InvalidTokenException;
|
||||||
|
import org.jackhuang.hmcl.auth.ServerDisconnectException;
|
||||||
|
import org.jackhuang.hmcl.auth.ServerResponseMalformedException;
|
||||||
|
import org.jackhuang.hmcl.util.NetworkUtils;
|
||||||
|
import org.jackhuang.hmcl.util.StringUtils;
|
||||||
|
import org.jackhuang.hmcl.util.UUIDTypeAdapter;
|
||||||
|
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.GsonBuilder;
|
||||||
|
import com.google.gson.JsonParseException;
|
||||||
|
|
||||||
public class YggdrasilService {
|
public class YggdrasilService {
|
||||||
|
|
||||||
@@ -33,23 +49,39 @@ public class YggdrasilService {
|
|||||||
Objects.requireNonNull(clientToken);
|
Objects.requireNonNull(clientToken);
|
||||||
|
|
||||||
Map<String, Object> request = new HashMap<>();
|
Map<String, Object> request = new HashMap<>();
|
||||||
request.put("agent", Lang.mapOf(
|
request.put("agent", mapOf(
|
||||||
new Pair<>("name", "Minecraft"),
|
pair("name", "Minecraft"),
|
||||||
new Pair<>("version", 1)
|
pair("version", 1)
|
||||||
));
|
));
|
||||||
request.put("username", username);
|
request.put("username", username);
|
||||||
request.put("password", password);
|
request.put("password", password);
|
||||||
request.put("clientToken", clientToken);
|
request.put("clientToken", clientToken);
|
||||||
request.put("requestUser", true);
|
request.put("requestUser", true);
|
||||||
|
|
||||||
return handle(request(provider.getAuthenticationURL(), request), clientToken);
|
return handleAuthenticationResponse(request(provider.getAuthenticationURL(), request), clientToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
public YggdrasilSession refresh(String accessToken, String clientToken) throws AuthenticationException {
|
private Map<String, Object> createRequestWithCredentials(String accessToken, String clientToken) {
|
||||||
|
Map<String, Object> request = new HashMap<>();
|
||||||
|
request.put("accessToken", accessToken);
|
||||||
|
request.put("clientToken", clientToken);
|
||||||
|
return request;
|
||||||
|
}
|
||||||
|
|
||||||
|
public YggdrasilSession refresh(String accessToken, String clientToken, GameProfile characterToSelect) throws AuthenticationException {
|
||||||
Objects.requireNonNull(accessToken);
|
Objects.requireNonNull(accessToken);
|
||||||
Objects.requireNonNull(clientToken);
|
Objects.requireNonNull(clientToken);
|
||||||
|
|
||||||
return handle(request(provider.getRefreshmentURL(), new RefreshRequest(accessToken, clientToken)), clientToken);
|
Map<String, Object> request = createRequestWithCredentials(accessToken, clientToken);
|
||||||
|
request.put("requestUser", true);
|
||||||
|
|
||||||
|
if (characterToSelect != null) {
|
||||||
|
request.put("selectedProfile", mapOf(
|
||||||
|
pair("id", characterToSelect.getId()),
|
||||||
|
pair("name", characterToSelect.getName())));
|
||||||
|
}
|
||||||
|
|
||||||
|
return handleAuthenticationResponse(request(provider.getRefreshmentURL(), request), clientToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean validate(String accessToken) throws AuthenticationException {
|
public boolean validate(String accessToken) throws AuthenticationException {
|
||||||
@@ -60,7 +92,7 @@ public class YggdrasilService {
|
|||||||
Objects.requireNonNull(accessToken);
|
Objects.requireNonNull(accessToken);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
requireEmpty(request(provider.getValidationURL(), new RefreshRequest(accessToken, clientToken)));
|
requireEmpty(request(provider.getValidationURL(), createRequestWithCredentials(accessToken, clientToken)));
|
||||||
return true;
|
return true;
|
||||||
} catch (InvalidCredentialsException | InvalidTokenException e) {
|
} catch (InvalidCredentialsException | InvalidTokenException e) {
|
||||||
return false;
|
return false;
|
||||||
@@ -74,7 +106,7 @@ public class YggdrasilService {
|
|||||||
public void invalidate(String accessToken, String clientToken) throws AuthenticationException {
|
public void invalidate(String accessToken, String clientToken) throws AuthenticationException {
|
||||||
Objects.requireNonNull(accessToken);
|
Objects.requireNonNull(accessToken);
|
||||||
|
|
||||||
requireEmpty(request(provider.getInvalidationURL(), GSON.toJson(new RefreshRequest(accessToken, clientToken))));
|
requireEmpty(request(provider.getInvalidationURL(), createRequestWithCredentials(accessToken, clientToken)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -82,49 +114,33 @@ public class YggdrasilService {
|
|||||||
*
|
*
|
||||||
* Game profile provided from authentication is not complete (no skin data in properties).
|
* Game profile provided from authentication is not complete (no skin data in properties).
|
||||||
*
|
*
|
||||||
* @param userId the userId that the character corresponding to.
|
* @param uuid the uuid that the character corresponding to.
|
||||||
* @return the complete game profile(filled with more properties), null if character corresponding to {@code userId} does not exist.
|
* @return the complete game profile(filled with more properties)
|
||||||
* @throws AuthenticationException if an I/O error occurred or server response malformed.
|
|
||||||
*/
|
*/
|
||||||
public GameProfile getCompleteGameProfile(UUID userId) throws AuthenticationException {
|
public Optional<GameProfile> getCompleteGameProfile(UUID uuid) throws AuthenticationException {
|
||||||
Objects.requireNonNull(userId);
|
Objects.requireNonNull(uuid);
|
||||||
|
|
||||||
ProfileResponse response = fromJson(request(provider.getProfilePropertiesURL(userId), null), ProfileResponse.class);
|
return Optional.ofNullable(fromJson(request(provider.getProfilePropertiesURL(uuid), null), GameProfile.class));
|
||||||
if (response == null)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
return new GameProfile(response.getId(), response.getName(), response.getProperties());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Optional<Map<TextureType, Texture>> getTextures(GameProfile profile) throws AuthenticationException {
|
public Optional<Map<TextureType, Texture>> getTextures(GameProfile profile) throws AuthenticationException {
|
||||||
Objects.requireNonNull(profile);
|
Objects.requireNonNull(profile);
|
||||||
|
|
||||||
return Optional.ofNullable(profile.getProperties())
|
return Optional.ofNullable(profile.getProperties())
|
||||||
.map(properties -> properties.get("textures"))
|
.flatMap(properties -> Optional.ofNullable(properties.get("textures")))
|
||||||
.map(encodedTextures -> new String(Base64.getDecoder().decode(encodedTextures), UTF_8))
|
.map(encodedTextures -> new String(Base64.getDecoder().decode(encodedTextures), UTF_8))
|
||||||
.map(Lang.liftFunction(textures -> fromJson(textures, TextureResponse.class)))
|
.map(liftFunction(textures -> fromJson(textures, TextureResponse.class)))
|
||||||
.map(TextureResponse::getTextures);
|
.flatMap(response -> Optional.ofNullable(response.textures));
|
||||||
}
|
}
|
||||||
|
|
||||||
private String request(URL url, Object input) throws AuthenticationException {
|
private static YggdrasilSession handleAuthenticationResponse(String responseText, String clientToken) throws AuthenticationException {
|
||||||
try {
|
|
||||||
if (input == null)
|
|
||||||
return NetworkUtils.doGet(url, proxy);
|
|
||||||
else
|
|
||||||
return NetworkUtils.doPost(url, input instanceof String ? (String) input : GSON.toJson(input), "application/json");
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new ServerDisconnectException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static YggdrasilSession handle(String responseText, String clientToken) throws AuthenticationException {
|
|
||||||
AuthenticationResponse response = fromJson(responseText, AuthenticationResponse.class);
|
AuthenticationResponse response = fromJson(responseText, AuthenticationResponse.class);
|
||||||
handleErrorMessage(response);
|
handleErrorMessage(response);
|
||||||
|
|
||||||
if (!clientToken.equals(response.getClientToken()))
|
if (!clientToken.equals(response.clientToken))
|
||||||
throw new AuthenticationException("Client token changed from " + response.getClientToken() + " to " + clientToken);
|
throw new AuthenticationException("Client token changed from " + clientToken + " to " + response.clientToken);
|
||||||
|
|
||||||
return new YggdrasilSession(response.getAccessToken(), response.getSelectedProfile(), response.getAvailableProfiles(), response.getUser());
|
return new YggdrasilSession(response.clientToken, response.accessToken, response.selectedProfile, response.availableProfiles, response.user);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void requireEmpty(String response) throws AuthenticationException {
|
private static void requireEmpty(String response) throws AuthenticationException {
|
||||||
@@ -139,15 +155,30 @@ public class YggdrasilService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static void handleErrorMessage(ErrorResponse response) throws AuthenticationException {
|
private static void handleErrorMessage(ErrorResponse response) throws AuthenticationException {
|
||||||
if (!StringUtils.isBlank(response.getError())) {
|
if (!StringUtils.isBlank(response.error)) {
|
||||||
if (response.getErrorMessage() != null)
|
if (response.errorMessage != null && "ForbiddenOperationException".equals(response.error)) {
|
||||||
if (response.getErrorMessage().contains("Invalid credentials"))
|
if (response.errorMessage.contains("Invalid credentials"))
|
||||||
throw new InvalidCredentialsException();
|
throw new InvalidCredentialsException();
|
||||||
else if (response.getErrorMessage().contains("Invalid token"))
|
|
||||||
|
else if (response.errorMessage.contains("Invalid token"))
|
||||||
throw new InvalidTokenException();
|
throw new InvalidTokenException();
|
||||||
else if (response.getErrorMessage().contains("Invalid username or password"))
|
|
||||||
|
else if (response.errorMessage.contains("Invalid username or password"))
|
||||||
throw new InvalidPasswordException();
|
throw new InvalidPasswordException();
|
||||||
throw new RemoteAuthenticationException(response.getError(), response.getErrorMessage(), response.getCause());
|
}
|
||||||
|
|
||||||
|
throw new RemoteAuthenticationException(response.error, response.errorMessage, response.cause);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String request(URL url, Object payload) throws AuthenticationException {
|
||||||
|
try {
|
||||||
|
if (payload == null)
|
||||||
|
return NetworkUtils.doGet(url, proxy);
|
||||||
|
else
|
||||||
|
return NetworkUtils.doPost(url, payload instanceof String ? (String) payload : GSON.toJson(payload), "application/json");
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new ServerDisconnectException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -159,77 +190,27 @@ public class YggdrasilService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static final Gson GSON = new GsonBuilder()
|
private class TextureResponse {
|
||||||
.registerTypeAdapter(GameProfile.class, GameProfile.Serializer.INSTANCE)
|
public Map<TextureType, Texture> textures;
|
||||||
|
}
|
||||||
|
|
||||||
|
private class AuthenticationResponse extends ErrorResponse {
|
||||||
|
public String accessToken;
|
||||||
|
public String clientToken;
|
||||||
|
public GameProfile selectedProfile;
|
||||||
|
public GameProfile[] availableProfiles;
|
||||||
|
public User user;
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ErrorResponse {
|
||||||
|
public String error;
|
||||||
|
public String errorMessage;
|
||||||
|
public String cause;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final Gson GSON = new GsonBuilder()
|
||||||
.registerTypeAdapter(PropertyMap.class, PropertyMap.Serializer.INSTANCE)
|
.registerTypeAdapter(PropertyMap.class, PropertyMap.Serializer.INSTANCE)
|
||||||
.registerTypeAdapter(UUID.class, UUIDTypeAdapter.INSTANCE)
|
.registerTypeAdapter(UUID.class, UUIDTypeAdapter.INSTANCE)
|
||||||
.create();
|
.create();
|
||||||
|
|
||||||
private static final class RefreshRequest {
|
|
||||||
|
|
||||||
private final String accessToken;
|
|
||||||
private final String clientToken;
|
|
||||||
private final GameProfile selectedProfile;
|
|
||||||
private final boolean requestUser;
|
|
||||||
|
|
||||||
public RefreshRequest(String accessToken, String clientToken) {
|
|
||||||
this(accessToken, clientToken, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public RefreshRequest(String accessToken, String clientToken, GameProfile selectedProfile) {
|
|
||||||
this(accessToken, clientToken, selectedProfile, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public RefreshRequest(String accessToken, String clientToken, GameProfile selectedProfile, boolean requestUser) {
|
|
||||||
this.accessToken = accessToken;
|
|
||||||
this.clientToken = clientToken;
|
|
||||||
this.selectedProfile = selectedProfile;
|
|
||||||
this.requestUser = requestUser;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getAccessToken() {
|
|
||||||
return accessToken;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getClientToken() {
|
|
||||||
return clientToken;
|
|
||||||
}
|
|
||||||
|
|
||||||
public GameProfile getSelectedProfile() {
|
|
||||||
return selectedProfile;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isRequestUser() {
|
|
||||||
return requestUser;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final class ProfileResponse {
|
|
||||||
private final UUID id;
|
|
||||||
private final String name;
|
|
||||||
private final PropertyMap properties;
|
|
||||||
|
|
||||||
public ProfileResponse() {
|
|
||||||
this(UUID.randomUUID(), "", null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ProfileResponse(UUID id, String name, PropertyMap properties) {
|
|
||||||
this.id = id;
|
|
||||||
this.name = name;
|
|
||||||
this.properties = properties;
|
|
||||||
}
|
|
||||||
|
|
||||||
public UUID getId() {
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getName() {
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public PropertyMap getProperties() {
|
|
||||||
return properties;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,35 +1,53 @@
|
|||||||
package org.jackhuang.hmcl.auth.yggdrasil;
|
package org.jackhuang.hmcl.auth.yggdrasil;
|
||||||
|
|
||||||
import org.jackhuang.hmcl.util.UUIDTypeAdapter;
|
import static org.jackhuang.hmcl.util.Lang.mapOf;
|
||||||
|
import static org.jackhuang.hmcl.util.Lang.tryCast;
|
||||||
|
import static org.jackhuang.hmcl.util.Pair.pair;
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
import static org.jackhuang.hmcl.util.Lang.tryCast;
|
import org.jackhuang.hmcl.auth.AuthInfo;
|
||||||
|
import org.jackhuang.hmcl.util.UUIDTypeAdapter;
|
||||||
|
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.GsonBuilder;
|
||||||
|
|
||||||
public class YggdrasilSession {
|
public class YggdrasilSession {
|
||||||
|
|
||||||
private final String accessToken;
|
private String clientToken;
|
||||||
|
private String accessToken;
|
||||||
private GameProfile selectedProfile;
|
private GameProfile selectedProfile;
|
||||||
private final GameProfile[] availableProfiles;
|
private GameProfile[] availableProfiles;
|
||||||
private final User user;
|
private User user;
|
||||||
|
|
||||||
public YggdrasilSession(String accessToken, GameProfile selectedProfile, GameProfile[] availableProfiles, User user) {
|
public YggdrasilSession(String clientToken, String accessToken, GameProfile selectedProfile, GameProfile[] availableProfiles, User user) {
|
||||||
|
this.clientToken = clientToken;
|
||||||
this.accessToken = accessToken;
|
this.accessToken = accessToken;
|
||||||
this.selectedProfile = selectedProfile;
|
this.selectedProfile = selectedProfile;
|
||||||
this.availableProfiles = availableProfiles;
|
this.availableProfiles = availableProfiles;
|
||||||
this.user = user;
|
this.user = user;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getClientToken() {
|
||||||
|
return clientToken;
|
||||||
|
}
|
||||||
|
|
||||||
public String getAccessToken() {
|
public String getAccessToken() {
|
||||||
return accessToken;
|
return accessToken;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return nullable (null if no character is selected)
|
||||||
|
*/
|
||||||
public GameProfile getSelectedProfile() {
|
public GameProfile getSelectedProfile() {
|
||||||
return selectedProfile;
|
return selectedProfile;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return nullable (null if the YggdrasilSession is loaded from storage)
|
||||||
|
*/
|
||||||
public GameProfile[] getAvailableProfiles() {
|
public GameProfile[] getAvailableProfiles() {
|
||||||
return availableProfiles;
|
return availableProfiles;
|
||||||
}
|
}
|
||||||
@@ -38,53 +56,40 @@ public class YggdrasilSession {
|
|||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setAvailableProfile(GameProfile profile) {
|
|
||||||
if (availableProfiles != null)
|
|
||||||
for (int i = 0; i < availableProfiles.length; ++i)
|
|
||||||
if (availableProfiles[i].getId().equals(profile.getId()))
|
|
||||||
availableProfiles[i] = profile;
|
|
||||||
|
|
||||||
if (selectedProfile != null && profile.getId().equals(selectedProfile.getId()))
|
|
||||||
selectedProfile = profile;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setSelectedProfile(GameProfile selectedProfile) {
|
|
||||||
this.selectedProfile = selectedProfile;
|
|
||||||
|
|
||||||
setAvailableProfile(selectedProfile);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static YggdrasilSession fromStorage(Map<?, ?> storage) {
|
public static YggdrasilSession fromStorage(Map<?, ?> storage) {
|
||||||
Optional<String> profileId = tryCast(storage.get("uuid"), String.class);
|
UUID uuid = tryCast(storage.get("uuid"), String.class).map(UUIDTypeAdapter::fromString).orElseThrow(() -> new IllegalArgumentException("uuid is missing"));
|
||||||
Optional<String> profileName = tryCast(storage.get("displayName"), String.class);
|
String name = tryCast(storage.get("displayName"), String.class).orElseThrow(() -> new IllegalArgumentException("displayName is missing"));
|
||||||
GameProfile profile = null;
|
String clientToken = tryCast(storage.get("clientToken"), String.class).orElseThrow(() -> new IllegalArgumentException("clientToken is missing"));
|
||||||
if (profileId.isPresent() && profileName.isPresent()) {
|
String accessToken = tryCast(storage.get("accessToken"), String.class).orElseThrow(() -> new IllegalArgumentException("accessToken is missing"));
|
||||||
profile = new GameProfile(UUIDTypeAdapter.fromString(profileId.get()), profileName.get(),
|
String userId = tryCast(storage.get("userid"), String.class).orElseThrow(() -> new IllegalArgumentException("userid is missing"));
|
||||||
tryCast(storage.get("profileProperties"), Map.class).map(PropertyMap::fromMap).orElseGet(PropertyMap::new));
|
PropertyMap userProperties = tryCast(storage.get("userProperties"), Map.class).map(PropertyMap::fromMap).orElse(null);
|
||||||
}
|
return new YggdrasilSession(clientToken, accessToken, new GameProfile(uuid, name), null, new User(userId, userProperties));
|
||||||
|
|
||||||
return new YggdrasilSession(
|
|
||||||
tryCast(storage.get("accessToken"), String.class).orElse(null),
|
|
||||||
profile,
|
|
||||||
null,
|
|
||||||
tryCast(storage.get("userid"), String.class)
|
|
||||||
.map(userId -> new User(userId, tryCast(storage.get("userProperties"), Map.class).map(PropertyMap::fromMap).orElse(null)))
|
|
||||||
.orElse(null)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map<Object, Object> toStorage() {
|
public Map<Object, Object> toStorage() {
|
||||||
Map<Object, Object> storage = new HashMap<>();
|
if (selectedProfile == null)
|
||||||
storage.put("accessToken", accessToken);
|
throw new IllegalStateException("No character is selected");
|
||||||
if (selectedProfile != null) {
|
if (user == null)
|
||||||
storage.put("uuid", UUIDTypeAdapter.fromUUID(selectedProfile.getId()));
|
throw new IllegalStateException("No user is specified");
|
||||||
storage.put("displayName", selectedProfile.getName());
|
|
||||||
storage.put("profileProperties", selectedProfile.getProperties());
|
return mapOf(
|
||||||
|
pair("clientToken", clientToken),
|
||||||
|
pair("accessToken", accessToken),
|
||||||
|
pair("uuid", UUIDTypeAdapter.fromUUID(selectedProfile.getId())),
|
||||||
|
pair("displayName", selectedProfile.getName()),
|
||||||
|
pair("userid", user.getId()),
|
||||||
|
pair("userProperties", user.getProperties()));
|
||||||
}
|
}
|
||||||
if (user != null) {
|
|
||||||
storage.put("userid", user.getId());
|
public AuthInfo toAuthInfo() {
|
||||||
storage.put("userProperties", user.getProperties());
|
if (selectedProfile == null)
|
||||||
}
|
throw new IllegalStateException("No character is selected");
|
||||||
return storage;
|
if (user == null)
|
||||||
|
throw new IllegalStateException("No user is specified");
|
||||||
|
|
||||||
|
return new AuthInfo(selectedProfile.getName(), selectedProfile.getId(), accessToken,
|
||||||
|
Optional.ofNullable(user.getProperties()).map(GSON_PROPERTIES::toJson).orElse("{}"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final Gson GSON_PROPERTIES = new GsonBuilder().registerTypeAdapter(PropertyMap.class, PropertyMap.LegacySerializer.INSTANCE).create();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,8 @@
|
|||||||
*/
|
*/
|
||||||
package org.jackhuang.hmcl.download.game;
|
package org.jackhuang.hmcl.download.game;
|
||||||
|
|
||||||
|
import static org.jackhuang.hmcl.util.Pair.pair;
|
||||||
|
|
||||||
import org.jackhuang.hmcl.download.AbstractDependencyManager;
|
import org.jackhuang.hmcl.download.AbstractDependencyManager;
|
||||||
import org.jackhuang.hmcl.game.AssetIndex;
|
import org.jackhuang.hmcl.game.AssetIndex;
|
||||||
import org.jackhuang.hmcl.game.AssetIndexInfo;
|
import org.jackhuang.hmcl.game.AssetIndexInfo;
|
||||||
@@ -82,7 +84,7 @@ public final class GameAssetRefreshTask extends TaskResult<Collection<Pair<File,
|
|||||||
if (Thread.interrupted())
|
if (Thread.interrupted())
|
||||||
throw new InterruptedException();
|
throw new InterruptedException();
|
||||||
|
|
||||||
res.add(new Pair<>(dependencyManager.getGameRepository().getAssetObject(version.getId(), assetIndexInfo.getId(), assetObject), assetObject));
|
res.add(pair(dependencyManager.getGameRepository().getAssetObject(version.getId(), assetIndexInfo.getId(), assetObject), assetObject));
|
||||||
updateProgress(++progress, index.getObjects().size());
|
updateProgress(++progress, index.getObjects().size());
|
||||||
}
|
}
|
||||||
setResult(res);
|
setResult(res);
|
||||||
|
|||||||
@@ -17,6 +17,9 @@
|
|||||||
*/
|
*/
|
||||||
package org.jackhuang.hmcl.launch;
|
package org.jackhuang.hmcl.launch;
|
||||||
|
|
||||||
|
import static org.jackhuang.hmcl.util.Lang.mapOf;
|
||||||
|
import static org.jackhuang.hmcl.util.Pair.pair;
|
||||||
|
|
||||||
import org.jackhuang.hmcl.auth.AuthInfo;
|
import org.jackhuang.hmcl.auth.AuthInfo;
|
||||||
import org.jackhuang.hmcl.game.*;
|
import org.jackhuang.hmcl.game.*;
|
||||||
import org.jackhuang.hmcl.util.*;
|
import org.jackhuang.hmcl.util.*;
|
||||||
@@ -191,8 +194,8 @@ public class DefaultLauncher extends Launcher {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private final Map<String, Supplier<Boolean>> forbiddens = Lang.mapOf(
|
private final Map<String, Supplier<Boolean>> forbiddens = mapOf(
|
||||||
new Pair<String, Supplier<Boolean>>("-Xincgc", () -> options.getJava().getParsedVersion() >= JavaVersion.JAVA_9)
|
pair("-Xincgc", () -> options.getJava().getParsedVersion() >= JavaVersion.JAVA_9)
|
||||||
);
|
);
|
||||||
|
|
||||||
protected Map<String, Supplier<Boolean>> getForbiddens() {
|
protected Map<String, Supplier<Boolean>> getForbiddens() {
|
||||||
@@ -232,18 +235,18 @@ public class DefaultLauncher extends Launcher {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected Map<String, String> getConfigurations() {
|
protected Map<String, String> getConfigurations() {
|
||||||
return Lang.mapOf(
|
return mapOf(
|
||||||
new Pair<>("${auth_player_name}", authInfo.getUsername()),
|
pair("${auth_player_name}", authInfo.getUsername()),
|
||||||
new Pair<>("${auth_session}", authInfo.getAccessToken()),
|
pair("${auth_session}", authInfo.getAccessToken()),
|
||||||
new Pair<>("${auth_access_token}", authInfo.getAccessToken()),
|
pair("${auth_access_token}", authInfo.getAccessToken()),
|
||||||
new Pair<>("${auth_uuid}", authInfo.getUserId()),
|
pair("${auth_uuid}", UUIDTypeAdapter.fromUUID(authInfo.getUUID())),
|
||||||
new Pair<>("${version_name}", Optional.ofNullable(options.getVersionName()).orElse(version.getId())),
|
pair("${version_name}", Optional.ofNullable(options.getVersionName()).orElse(version.getId())),
|
||||||
new Pair<>("${profile_name}", Optional.ofNullable(options.getProfileName()).orElse("Minecraft")),
|
pair("${profile_name}", Optional.ofNullable(options.getProfileName()).orElse("Minecraft")),
|
||||||
new Pair<>("${version_type}", version.getType().getId()),
|
pair("${version_type}", version.getType().getId()),
|
||||||
new Pair<>("${game_directory}", repository.getRunDirectory(version.getId()).getAbsolutePath()),
|
pair("${game_directory}", repository.getRunDirectory(version.getId()).getAbsolutePath()),
|
||||||
new Pair<>("${user_type}", authInfo.getUserType().toString().toLowerCase()),
|
pair("${user_type}", "mojang"),
|
||||||
new Pair<>("${assets_index_name}", version.getAssetIndex().getId()),
|
pair("${assets_index_name}", version.getAssetIndex().getId()),
|
||||||
new Pair<>("${user_properties}", authInfo.getUserProperties())
|
pair("${user_properties}", authInfo.getUserProperties())
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -26,9 +26,14 @@ import java.util.Objects;
|
|||||||
*/
|
*/
|
||||||
public class Pair<K, V> implements Map.Entry<K, V> {
|
public class Pair<K, V> implements Map.Entry<K, V> {
|
||||||
|
|
||||||
|
public static <K, V> Pair<K, V> pair(K key, V value) {
|
||||||
|
return new Pair<>(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
private K key;
|
private K key;
|
||||||
private V value;
|
private V value;
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
public Pair(K key, V value) {
|
public Pair(K key, V value) {
|
||||||
this.key = key;
|
this.key = key;
|
||||||
this.value = value;
|
this.value = value;
|
||||||
|
|||||||
Reference in New Issue
Block a user