From 83bc6748a34d0a9d1b89d2b2f9f398781bf8abc1 Mon Sep 17 00:00:00 2001 From: huanghongxun Date: Sat, 17 Feb 2018 11:01:08 +0800 Subject: [PATCH] HMCLCore authlib injector support --- .../main/java/org/jackhuang/hmcl/Main.java | 2 +- .../hmcl/game/HMCLModpackManager.java | 9 +-- .../org/jackhuang/hmcl/setting/Accounts.java | 25 ++++++- .../org/jackhuang/hmcl/setting/Config.java | 18 +++++ .../org/jackhuang/hmcl/setting/Settings.java | 17 +++-- .../hmcl/upgrade/AppDataUpgrader.java | 2 +- .../jackhuang/hmcl/auth/AccountFactory.java | 8 ++- .../org/jackhuang/hmcl/auth/AuthInfo.java | 17 +++++ .../yggdrasil/AuthlibInjectorAccount.java | 68 ++++++++++++++++++ .../AuthlibInjectorAccountFactory.java | 60 ++++++++++++++++ .../yggdrasil/AuthlibInjectorBuildInfo.java | 43 +++++++++++ .../hmcl/auth/yggdrasil/YggdrasilAccount.java | 30 +++++--- .../yggdrasil/YggdrasilAccountFactory.java | 22 ++++-- .../org/jackhuang/hmcl/game/Arguments.java | 12 ++++ .../org/jackhuang/hmcl/mod/CurseManifest.java | 5 +- .../jackhuang/hmcl/util/DateTypeAdapter.java | 2 +- .../java/org/jackhuang/hmcl/util/Lang.java | 12 ++++ .../org/jackhuang/hmcl/util/NetworkUtils.java | 10 +++ gradle/wrapper/gradle-wrapper.jar | Bin 54708 -> 54727 bytes gradle/wrapper/gradle-wrapper.properties | 3 +- gradlew | 4 +- gradlew.bat | 4 +- 22 files changed, 329 insertions(+), 44 deletions(-) create mode 100644 HMCLCore/src/main/java/org/jackhuang/hmcl/auth/yggdrasil/AuthlibInjectorAccount.java create mode 100644 HMCLCore/src/main/java/org/jackhuang/hmcl/auth/yggdrasil/AuthlibInjectorAccountFactory.java create mode 100644 HMCLCore/src/main/java/org/jackhuang/hmcl/auth/yggdrasil/AuthlibInjectorBuildInfo.java diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/Main.java b/HMCL/src/main/java/org/jackhuang/hmcl/Main.java index 075b10f33..ab0a06685 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/Main.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/Main.java @@ -121,11 +121,11 @@ public final class Main extends Application { } public static final File MINECRAFT_DIRECTORY = getWorkingDirectory("minecraft"); + public static final File HMCL_DIRECTORY = getWorkingDirectory("hmcl"); public static final String VERSION = "@HELLO_MINECRAFT_LAUNCHER_VERSION_FOR_GRADLE_REPLACING@"; public static final String NAME = "HMCL"; public static final String TITLE = NAME + " " + VERSION; - public static final File APPDATA = getWorkingDirectory("hmcl"); public static final ResourceBundle RESOURCE_BUNDLE = Settings.INSTANCE.getLocale().getResourceBundle(); public static final UpdateChecker UPDATE_CHECKER = new UpdateChecker(VersionNumber.asVersion(VERSION)); public static final CrashReporter CRASH_REPORTER = new CrashReporter(); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLModpackManager.java b/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLModpackManager.java index 07640f1a2..cc4d38111 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLModpackManager.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLModpackManager.java @@ -21,6 +21,7 @@ import com.google.gson.JsonParseException; import org.jackhuang.hmcl.mod.Modpack; import org.jackhuang.hmcl.util.CompressingUtils; import org.jackhuang.hmcl.util.Constants; +import org.jackhuang.hmcl.util.Lang; import org.jackhuang.hmcl.util.StringUtils; import java.io.File; @@ -81,13 +82,9 @@ public final class HMCLModpackManager { */ public static Modpack readHMCLModpackManifest(File file) throws IOException, JsonParseException { String manifestJson = CompressingUtils.readTextZipEntry(file, "modpack.json"); - Modpack manifest = Constants.GSON.fromJson(manifestJson, Modpack.class); - if (manifest == null) - throw new JsonParseException("`modpack.json` not found. " + file + " is not a valid HMCL modpack."); + Modpack manifest = Lang.requireJsonNonNull(Constants.GSON.fromJson(manifestJson, Modpack.class)); String gameJson = CompressingUtils.readTextZipEntry(file, "minecraft/pack.json"); - Version game = Constants.GSON.fromJson(gameJson, Version.class); - if (game == null) - throw new JsonParseException("`minecraft/pack.json` not found. " + file + " iot a valid HMCL modpack."); + Version game = Lang.requireJsonNonNull(Constants.GSON.fromJson(gameJson, Version.class)); if (game.getJar() == null) if (StringUtils.isBlank(manifest.getVersion())) throw new JsonParseException("Cannot recognize the game version of modpack " + file + "."); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/setting/Accounts.java b/HMCL/src/main/java/org/jackhuang/hmcl/setting/Accounts.java index 4b342137e..2766141fa 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/setting/Accounts.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/setting/Accounts.java @@ -17,15 +17,19 @@ */ package org.jackhuang.hmcl.setting; +import org.jackhuang.hmcl.Main; import org.jackhuang.hmcl.auth.Account; import org.jackhuang.hmcl.auth.AccountFactory; import org.jackhuang.hmcl.auth.OfflineAccount; import org.jackhuang.hmcl.auth.OfflineAccountFactory; -import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilAccount; -import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilAccountFactory; +import org.jackhuang.hmcl.auth.yggdrasil.*; +import org.jackhuang.hmcl.task.FileDownloadTask; +import org.jackhuang.hmcl.util.FileUtils; import org.jackhuang.hmcl.util.Lang; import org.jackhuang.hmcl.util.Pair; +import java.io.File; +import java.net.URL; import java.util.Map; import java.util.Optional; @@ -37,14 +41,17 @@ public final class Accounts { public static final String OFFLINE_ACCOUNT_KEY = "offline"; public static final String YGGDRASIL_ACCOUNT_KEY = "yggdrasil"; + public static final String AUTHLIB_INJECTOR_ACCOUNT_KEY = "authlibInjector"; public static final Map> ACCOUNT_FACTORY = Lang.mapOf( new Pair<>(OFFLINE_ACCOUNT_KEY, OfflineAccountFactory.INSTANCE), - new Pair<>(YGGDRASIL_ACCOUNT_KEY, YggdrasilAccountFactory.INSTANCE) + new Pair<>(YGGDRASIL_ACCOUNT_KEY, new YggdrasilAccountFactory()), + new Pair<>(AUTHLIB_INJECTOR_ACCOUNT_KEY, new AuthlibInjectorAccountFactory(Accounts::downloadAuthlibInjector)) ); public static String getAccountType(Account account) { if (account instanceof OfflineAccount) return OFFLINE_ACCOUNT_KEY; + else if (account instanceof AuthlibInjectorAccount) return AUTHLIB_INJECTOR_ACCOUNT_KEY; else if (account instanceof YggdrasilAccount) return YGGDRASIL_ACCOUNT_KEY; else return YGGDRASIL_ACCOUNT_KEY; } @@ -75,4 +82,16 @@ public final class Accounts { static String getAccountId(String username, String character) { return username + ":" + character; } + + private static String downloadAuthlibInjector() throws Exception { + AuthlibInjectorBuildInfo buildInfo = AuthlibInjectorBuildInfo.requestBuildInfo(); + File jar = new File(Main.HMCL_DIRECTORY, "authlib-injector.jar"); + File local = new File(Main.HMCL_DIRECTORY, "authlib-injector.txt"); + int buildNumber = Integer.parseInt(FileUtils.readText(local)); + if (buildNumber < buildInfo.getBuildNumber()) { + new FileDownloadTask(new URL(buildInfo.getUrl()), jar).run(); + FileUtils.writeText(local, String.valueOf(buildInfo.getBuildNumber())); + } + return jar.getAbsolutePath(); + } } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/setting/Config.java b/HMCL/src/main/java/org/jackhuang/hmcl/setting/Config.java index 95fb31b97..cc5110ea2 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/setting/Config.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/setting/Config.java @@ -19,6 +19,7 @@ package org.jackhuang.hmcl.setting; import com.google.gson.annotations.SerializedName; import org.jackhuang.hmcl.Main; +import org.jackhuang.hmcl.auth.yggdrasil.AuthlibInjectorBuildInfo; import org.jackhuang.hmcl.util.JavaVersion; import java.util.LinkedList; @@ -91,6 +92,9 @@ public final class Config { @SerializedName("logLines") private int logLines = 100; + @SerializedName("authlibInjectorServerURL") + private String authlibInjectorServerURL = AuthlibInjectorBuildInfo.UPDATE_URL; + public String getSelectedProfile() { return selectedProfile; } @@ -278,4 +282,18 @@ public final class Config { public void setLogLines(int logLines) { this.logLines = logLines; } + + public String getAuthlibInjectorServerURL() { + return authlibInjectorServerURL; + } + + /** + * Will not invoke Settings.INSTANCE.save() + * @param authlibInjectorServerURL new server url. + */ + public void setAuthlibInjectorServerURL(String authlibInjectorServerURL) { + this.authlibInjectorServerURL = authlibInjectorServerURL; + // Do not invoke Settings.INSTANCE.save() + // Because we want users set it theirself. + } } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/setting/Settings.java b/HMCL/src/main/java/org/jackhuang/hmcl/setting/Settings.java index 43adbe380..a1f5a60d7 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/setting/Settings.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/setting/Settings.java @@ -28,6 +28,7 @@ import javafx.scene.text.Font; import org.jackhuang.hmcl.Main; import org.jackhuang.hmcl.auth.Account; import org.jackhuang.hmcl.auth.AccountFactory; +import org.jackhuang.hmcl.auth.yggdrasil.AuthlibInjectorBuildInfo; import org.jackhuang.hmcl.download.BMCLAPIDownloadProvider; import org.jackhuang.hmcl.download.DownloadProvider; import org.jackhuang.hmcl.download.MojangDownloadProvider; @@ -39,10 +40,7 @@ import org.jackhuang.hmcl.util.*; import java.io.File; import java.io.IOException; -import java.net.Authenticator; -import java.net.InetSocketAddress; -import java.net.PasswordAuthentication; -import java.net.Proxy; +import java.net.*; import java.util.*; import java.util.logging.Level; import java.util.stream.Collectors; @@ -106,6 +104,13 @@ public class Settings { }); loadProxy(); + + try { + new URL(SETTINGS.getAuthlibInjectorServerURL()); + } catch (MalformedURLException ex) { + Logging.LOG.log(Level.SEVERE, "Authlib injector server URL is malformed, using official update url.", ex); + SETTINGS.setAuthlibInjectorServerURL(AuthlibInjectorBuildInfo.UPDATE_URL); + } } private Config initSettings() { @@ -275,6 +280,10 @@ public class Settings { SETTINGS.setLogLines(logLines); } + public String getAuthlibInjectorServerURL() { + return SETTINGS.getAuthlibInjectorServerURL(); + } + public DownloadProvider getDownloadProvider() { switch (SETTINGS.getDownloadType()) { case 0: diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/AppDataUpgrader.java b/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/AppDataUpgrader.java index 27dcdd62d..8d36d8e25 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/AppDataUpgrader.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/AppDataUpgrader.java @@ -154,7 +154,7 @@ public class AppDataUpgrader extends IUpgrader { public static class AppDataUpgraderPackGzTask extends Task { - public static final File BASE_FOLDER = Main.getWorkingDirectory("hmcl"); + public static final File BASE_FOLDER = Main.HMCL_DIRECTORY; public static final File HMCL_VER_FILE = new File(BASE_FOLDER, "hmclver.json"); public static File getSelf(String ver) { diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/AccountFactory.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/AccountFactory.java index dbb6b3acd..502dfb01e 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/AccountFactory.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/AccountFactory.java @@ -28,10 +28,14 @@ import java.util.Map; public abstract class AccountFactory { public final T fromUsername(String username) { - return fromUsername(username, ""); + return fromUsername(username, "", null); } - public abstract T fromUsername(String username, String password); + public final T fromUsername(String username, String password) { + return fromUsername(username, password, null); + } + + public abstract T fromUsername(String username, String password, Object additionalData); protected abstract T fromStorageImpl(Map storage); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/AuthInfo.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/AuthInfo.java index 4e0488df0..4eed07e0b 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/AuthInfo.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/AuthInfo.java @@ -18,12 +18,15 @@ package org.jackhuang.hmcl.auth; import org.jackhuang.hmcl.auth.yggdrasil.GameProfile; +import org.jackhuang.hmcl.game.Arguments; +import org.jackhuang.hmcl.util.Immutable; import org.jackhuang.hmcl.util.UUIDTypeAdapter; /** * * @author huangyuhui */ +@Immutable public final class AuthInfo { private final String username; @@ -32,6 +35,7 @@ public final class AuthInfo { private final UserType userType; private final String userProperties; private final String userPropertyMap; + private final Arguments arguments; public AuthInfo(String username, String userId, String authToken) { this(username, userId, authToken, UserType.LEGACY); @@ -46,12 +50,17 @@ public final class AuthInfo { } public AuthInfo(String username, String userId, String authToken, UserType userType, String userProperties, String userPropertyMap) { + this(username, userId, authToken, userType, userProperties, userPropertyMap, null); + } + + public AuthInfo(String username, String userId, String authToken, UserType userType, String userProperties, String userPropertyMap, Arguments arguments) { this.username = username; this.userId = userId; this.authToken = authToken; this.userType = userType; this.userProperties = userProperties; this.userPropertyMap = userPropertyMap; + this.arguments = arguments; } public AuthInfo(GameProfile profile, String authToken, UserType userType, String userProperties) { @@ -93,4 +102,12 @@ public final class AuthInfo { public String getUserPropertyMap() { return userPropertyMap; } + + public Arguments getArguments() { + return arguments; + } + + public AuthInfo setArguments(Arguments arguments) { + return new AuthInfo(username, userId, authToken, userType, userProperties, userPropertyMap, arguments); + } } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/yggdrasil/AuthlibInjectorAccount.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/yggdrasil/AuthlibInjectorAccount.java new file mode 100644 index 000000000..33a3a0bc2 --- /dev/null +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/yggdrasil/AuthlibInjectorAccount.java @@ -0,0 +1,68 @@ +package org.jackhuang.hmcl.auth.yggdrasil; + +import org.jackhuang.hmcl.auth.AuthInfo; +import org.jackhuang.hmcl.auth.AuthenticationException; +import org.jackhuang.hmcl.auth.MultiCharacterSelector; +import org.jackhuang.hmcl.game.Arguments; +import org.jackhuang.hmcl.task.GetTask; +import org.jackhuang.hmcl.util.ExceptionalSupplier; +import org.jackhuang.hmcl.util.Lang; +import org.jackhuang.hmcl.util.NetworkUtils; + +import java.net.Proxy; +import java.util.Map; +import java.util.concurrent.atomic.AtomicBoolean; + +public class AuthlibInjectorAccount extends YggdrasilAccount { + private final String serverBaseURL; + private final ExceptionalSupplier injectorJarPath; + + public AuthlibInjectorAccount(ExceptionalSupplier injectorJarPath, String serverBaseURL, String username) { + super(serverBaseURL + "authserver/", serverBaseURL + "sessionserver/", username); + + this.injectorJarPath = injectorJarPath; + this.serverBaseURL = serverBaseURL; + } + + @Override + public AuthInfo logIn(MultiCharacterSelector selector, 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(() -> { + try { + getTask.run(); + } catch (Exception ignore) { + flag.set(false); + } + }); + + AuthInfo info = super.logIn(selector, 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 toStorageImpl() { + Map map = super.toStorageImpl(); + map.put(STORAGE_KEY_SERVER_BASE_URL, serverBaseURL); + return map; + } + + public String getServerBaseURL() { + return serverBaseURL; + } + + public static final String STORAGE_KEY_SERVER_BASE_URL = "serverBaseURL"; +} diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/yggdrasil/AuthlibInjectorAccountFactory.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/yggdrasil/AuthlibInjectorAccountFactory.java new file mode 100644 index 000000000..95cff3d9f --- /dev/null +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/yggdrasil/AuthlibInjectorAccountFactory.java @@ -0,0 +1,60 @@ +package org.jackhuang.hmcl.auth.yggdrasil; + +import org.jackhuang.hmcl.auth.AccountFactory; +import org.jackhuang.hmcl.util.ExceptionalSupplier; +import org.jackhuang.hmcl.util.Lang; +import org.jackhuang.hmcl.util.NetworkUtils; +import org.jackhuang.hmcl.util.UUIDTypeAdapter; + +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.function.Supplier; + +import static org.jackhuang.hmcl.auth.yggdrasil.AuthlibInjectorAccount.STORAGE_KEY_SERVER_BASE_URL; +import static org.jackhuang.hmcl.auth.yggdrasil.YggdrasilAccount.*; + +public class AuthlibInjectorAccountFactory extends AccountFactory { + private final ExceptionalSupplier injectorJarPathSupplier; + + public AuthlibInjectorAccountFactory(ExceptionalSupplier injectorJarPathSupplier) { + this.injectorJarPathSupplier = injectorJarPathSupplier; + } + + @Override + public AuthlibInjectorAccount fromUsername(String username, String password, Object additionalData) { + if (!(additionalData instanceof String) || !NetworkUtils.isURL((String) additionalData)) + throw new IllegalArgumentException("Additional data should be server base url string for authlib injector accounts."); + + AuthlibInjectorAccount account = new AuthlibInjectorAccount(injectorJarPathSupplier, (String) additionalData, username); + account.setPassword(password); + return account; + } + + @Override + public AuthlibInjectorAccount fromStorageImpl(Map storage) { + String username = Lang.get(storage, STORAGE_KEY_USER_NAME, String.class) + .orElseThrow(() -> new IllegalArgumentException("storage does not have key " + STORAGE_KEY_USER_NAME)); + String serverBaseURL = Lang.get(storage, STORAGE_KEY_SERVER_BASE_URL, String.class) + .orElseThrow(() -> new IllegalArgumentException("storage does not have key " + STORAGE_KEY_SERVER_BASE_URL)); + + AuthlibInjectorAccount account = new AuthlibInjectorAccount(injectorJarPathSupplier, serverBaseURL, username); + account.setUserId(Lang.get(storage, STORAGE_KEY_USER_ID, String.class, username)); + account.setAccessToken(Lang.get(storage, STORAGE_KEY_ACCESS_TOKEN, String.class, null)); + account.setClientToken(Lang.get(storage, STORAGE_KEY_CLIENT_TOKEN, String.class) + .orElseThrow(() -> new IllegalArgumentException("storage does not have key " + STORAGE_KEY_CLIENT_TOKEN))); + + Lang.get(storage, STORAGE_KEY_USER_PROPERTIES, List.class) + .ifPresent(account.getUserProperties()::fromList); + Optional profileId = Lang.get(storage, STORAGE_KEY_PROFILE_ID, String.class); + Optional profileName = Lang.get(storage, STORAGE_KEY_PROFILE_NAME, String.class); + GameProfile profile = null; + if (profileId.isPresent() && profileName.isPresent()) { + profile = new GameProfile(UUIDTypeAdapter.fromString(profileId.get()), profileName.get()); + Lang.get(storage, STORAGE_KEY_PROFILE_PROPERTIES, List.class) + .ifPresent(profile.getProperties()::fromList); + } + account.setSelectedProfile(profile); + return account; + } +} diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/yggdrasil/AuthlibInjectorBuildInfo.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/yggdrasil/AuthlibInjectorBuildInfo.java new file mode 100644 index 000000000..f27b9650b --- /dev/null +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/yggdrasil/AuthlibInjectorBuildInfo.java @@ -0,0 +1,43 @@ +package org.jackhuang.hmcl.auth.yggdrasil; + +import com.google.gson.JsonParseException; +import org.jackhuang.hmcl.util.Constants; +import org.jackhuang.hmcl.util.Immutable; +import org.jackhuang.hmcl.util.Lang; +import org.jackhuang.hmcl.util.NetworkUtils; + +import java.io.IOException; + +@Immutable +public final class AuthlibInjectorBuildInfo { + + private final int buildNumber; + private final String url; + + public AuthlibInjectorBuildInfo() { + this(0, ""); + } + + public AuthlibInjectorBuildInfo(int buildNumber, String url) { + this.buildNumber = buildNumber; + this.url = url; + } + + public int getBuildNumber() { + return buildNumber; + } + + public String getUrl() { + return url; + } + + public static AuthlibInjectorBuildInfo requestBuildInfo() throws IOException, JsonParseException { + return requestBuildInfo(UPDATE_URL); + } + + public static AuthlibInjectorBuildInfo requestBuildInfo(String updateUrl) throws IOException, JsonParseException { + return Lang.requireJsonNonNull(Constants.GSON.fromJson(NetworkUtils.doGet(NetworkUtils.toURL(updateUrl)), AuthlibInjectorBuildInfo.class)); + } + + public static final String UPDATE_URL = "https://authlib-injector.to2mbn.org/api/buildInfo"; +} diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/yggdrasil/YggdrasilAccount.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/yggdrasil/YggdrasilAccount.java index 12baa8818..f633ade4f 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/yggdrasil/YggdrasilAccount.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/yggdrasil/YggdrasilAccount.java @@ -35,7 +35,7 @@ import java.util.*; * * @author huang */ -public final class YggdrasilAccount extends Account { +public class YggdrasilAccount extends Account { private final String username; private String password; @@ -48,8 +48,15 @@ public final class YggdrasilAccount extends Account { private GameProfile[] profiles; private UserType userType = UserType.LEGACY; - public YggdrasilAccount(String username) { + public YggdrasilAccount(String baseAuthServer, String baseSessionServer, String username) { + this.baseAuthServer = baseAuthServer; + this.baseSessionServer = baseSessionServer; + this.baseProfile = baseSessionServer + "session/minecraft/profile/"; this.username = username; + + this.routeAuthenticate = NetworkUtils.toURL(baseAuthServer + "authenticate"); + this.routeRefresh = NetworkUtils.toURL(baseAuthServer + "refresh"); + this.routeValidate = NetworkUtils.toURL(baseAuthServer + "validate"); } @Override @@ -136,9 +143,9 @@ public final class YggdrasilAccount extends Account { isOnline = true; return; } - logIn1(ROUTE_REFRESH, new RefreshRequest(accessToken, clientToken), proxy); + logIn1(routeRefresh, new RefreshRequest(accessToken, clientToken), proxy); } else if (StringUtils.isNotBlank(password)) - logIn1(ROUTE_AUTHENTICATE, new AuthenticationRequest(username, password, clientToken), proxy); + logIn1(routeAuthenticate, new AuthenticationRequest(username, password, clientToken), proxy); else throw new AuthenticationException("Password cannot be blank"); } @@ -250,7 +257,7 @@ public final class YggdrasilAccount extends Account { return false; try { - makeRequest(ROUTE_VALIDATE, new ValidateRequest(accessToken, clientToken), proxy); + makeRequest(routeValidate, new ValidateRequest(accessToken, clientToken), proxy); return true; } catch (AuthenticationException e) { return false; @@ -268,7 +275,7 @@ public final class YggdrasilAccount extends Account { if (StringUtils.isBlank(userId)) throw new IllegalStateException("Not logged in"); - ProfileResponse response = GSON.fromJson(NetworkUtils.doGet(NetworkUtils.toURL(BASE_PROFILE + UUIDTypeAdapter.fromUUID(profile.getId()))), ProfileResponse.class); + ProfileResponse response = GSON.fromJson(NetworkUtils.doGet(NetworkUtils.toURL(baseProfile + UUIDTypeAdapter.fromUUID(profile.getId()))), ProfileResponse.class); if (response.getProperties() == null) return Optional.empty(); Property textureProperty = response.getProperties().get("textures"); if (textureProperty == null) return Optional.empty(); @@ -287,11 +294,12 @@ public final class YggdrasilAccount extends Account { return "YggdrasilAccount[username=" + getUsername() + "]"; } - private static final String BASE_URL = "https://authserver.mojang.com/"; - private static final String BASE_PROFILE = "https://sessionserver.mojang.com/session/minecraft/profile/"; - private static final URL ROUTE_AUTHENTICATE = NetworkUtils.toURL(BASE_URL + "authenticate"); - private static final URL ROUTE_REFRESH = NetworkUtils.toURL(BASE_URL + "refresh"); - private static final URL ROUTE_VALIDATE = NetworkUtils.toURL(BASE_URL + "validate"); + private final String baseAuthServer; + private final String baseSessionServer; + private final String baseProfile; + private final URL routeAuthenticate; + private final URL routeRefresh; + private final URL routeValidate; static final String STORAGE_KEY_ACCESS_TOKEN = "accessToken"; static final String STORAGE_KEY_PROFILE_NAME = "displayName"; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/yggdrasil/YggdrasilAccountFactory.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/yggdrasil/YggdrasilAccountFactory.java index 1a96ddddc..f7c7a714f 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/yggdrasil/YggdrasilAccountFactory.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/yggdrasil/YggdrasilAccountFactory.java @@ -31,15 +31,23 @@ import static org.jackhuang.hmcl.auth.yggdrasil.YggdrasilAccount.*; * * @author huangyuhui */ -public final class YggdrasilAccountFactory extends AccountFactory { - public static final YggdrasilAccountFactory INSTANCE = new YggdrasilAccountFactory(); +public class YggdrasilAccountFactory extends AccountFactory { - private YggdrasilAccountFactory() { + private final String baseAuthServer; + private final String baseSessionServer; + + public YggdrasilAccountFactory() { + this(MOJANG_AUTH_SERVER, MOJANG_SESSION_SERVER); + } + + public YggdrasilAccountFactory(String baseAuthServer, String baseSessionServer) { + this.baseAuthServer = baseAuthServer; + this.baseSessionServer = baseSessionServer; } @Override - public YggdrasilAccount fromUsername(String username, String password) { - YggdrasilAccount account = new YggdrasilAccount(username); + public YggdrasilAccount fromUsername(String username, String password, Object additionalData) { + YggdrasilAccount account = new YggdrasilAccount(MOJANG_AUTH_SERVER, MOJANG_SESSION_SERVER, username); account.setPassword(password); return account; } @@ -49,7 +57,7 @@ public final class YggdrasilAccountFactory extends AccountFactory new IllegalArgumentException("storage does not have key " + STORAGE_KEY_USER_NAME)); - YggdrasilAccount account = new YggdrasilAccount(username); + YggdrasilAccount account = new YggdrasilAccount(baseAuthServer, baseSessionServer, username); account.setUserId(Lang.get(storage, STORAGE_KEY_USER_ID, String.class, username)); account.setAccessToken(Lang.get(storage, STORAGE_KEY_ACCESS_TOKEN, String.class, null)); account.setClientToken(Lang.get(storage, STORAGE_KEY_CLIENT_TOKEN, String.class) @@ -69,4 +77,6 @@ public final class YggdrasilAccountFactory extends AccountFactory jvmArguments) { + List list = jvmArguments.stream().map(StringArgument::new).collect(Collectors.toList()); + if (arguments == null) + return new Arguments(null, list); + else + return new Arguments(arguments.getGame(), Lang.merge(arguments.getJvm(), list)); + } + public static Arguments merge(Arguments a, Arguments b) { if (a == null) return b; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/CurseManifest.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/CurseManifest.java index 2815803c2..90b9ecab6 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/CurseManifest.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/CurseManifest.java @@ -22,6 +22,7 @@ import com.google.gson.annotations.SerializedName; import org.jackhuang.hmcl.util.CompressingUtils; import org.jackhuang.hmcl.util.Constants; import org.jackhuang.hmcl.util.Immutable; +import org.jackhuang.hmcl.util.Lang; import java.io.File; import java.io.IOException; @@ -119,9 +120,7 @@ public final class CurseManifest { */ public static Modpack readCurseForgeModpackManifest(File f) throws IOException, JsonParseException { String json = CompressingUtils.readTextZipEntry(f, "manifest.json"); - CurseManifest manifest = Constants.GSON.fromJson(json, CurseManifest.class); - if (manifest == null) - throw new JsonParseException("`manifest.json` not found. Not a valid Curse modpack."); + CurseManifest manifest = Lang.requireJsonNonNull(Constants.GSON.fromJson(json, CurseManifest.class)); return new Modpack(manifest.getName(), manifest.getAuthor(), manifest.getVersion(), manifest.getMinecraft().getGameVersion(), CompressingUtils.readTextZipEntryQuietly(f, "modlist.html").orElse( "No description"), manifest); } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/DateTypeAdapter.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/DateTypeAdapter.java index c89b5e46a..008873114 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/DateTypeAdapter.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/DateTypeAdapter.java @@ -73,7 +73,7 @@ public final class DateTypeAdapter implements JsonSerializer, JsonDeserial cleaned = cleaned.substring(0, 22) + cleaned.substring(23); return ISO_8601_FORMAT.parse(cleaned); } catch (Exception e) { - throw new JsonSyntaxException("Invalid date: " + string, e); + throw new JsonParseException("Invalid date: " + string, e); } } } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/Lang.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/Lang.java index 6e39b8fa0..40b7f4256 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/Lang.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/Lang.java @@ -5,6 +5,8 @@ */ package org.jackhuang.hmcl.util; +import com.google.gson.JsonParseException; + import java.util.*; import java.util.function.Consumer; import java.util.function.Function; @@ -22,6 +24,16 @@ public final class Lang { public static final Consumer EMPTY_CONSUMER = a -> { }; + public static T requireJsonNonNull(T obj) throws JsonParseException { + return requireJsonNonNull(obj, "Json object cannot be null."); + } + + public static T requireJsonNonNull(T obj, String message) throws JsonParseException { + if (obj == null) + throw new JsonParseException(message); + return obj; + } + public static Map mapOf(Pair... pairs) { HashMap map = new HashMap<>(); for (Pair pair : pairs) diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/NetworkUtils.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/NetworkUtils.java index a5546bd24..2b1519498 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/NetworkUtils.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/NetworkUtils.java @@ -25,6 +25,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.HttpURLConnection; +import java.net.MalformedURLException; import java.net.Proxy; import java.net.URL; import java.security.GeneralSecurityException; @@ -187,4 +188,13 @@ public final class NetworkUtils { public static URL toURL(String str) { return Lang.invoke(() -> new URL(str)); } + + public static boolean isURL(String str) { + try { + new URL(str); + return true; + } catch (MalformedURLException e) { + return false; + } + } } diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 7a3265ee94c0ab25cf079ac8ccdf87f41d455d42..27768f1bbac3ce2d055b20d521f12da78d331e8e 100644 GIT binary patch delta 1862 zcmYL}X*ApU7sd%es1cMBJ1wE5C~B>eseK4Wi`3Ru>;|>fGOwt)+?vCH7sL zps~ijmN9g(r5(Fb{4;sc|2^k>&T~Ka-shh4eQ_HIEd2zQ^qZ{q?_`xTbR5`rgZ>F( zW=eHf9)BrCEtxIHRXh-$ z`!s%Xy2Bs7jv6$V@GMU-&0F6~!j9}&?O_{EaaHe$?l~56{Dpc1z4m3=?~!ZzXJ`92 znXa!cz?vVn)9^$i+7=$o^vOx&4_m*%P7bx6&PsQek= zPHkJ2f1;YrUsmO(>3WeIPpceCDp4f1spXAfwn2@YB}P)HymCwb%uUw9=4!LRKKOOG z<}AlNaW}7{`m;p_1#g5VJKmqzflr4B~$+xm<^cG%>OgH)0H6=T-dNlt*xT~Xq zCwTzkpjssQ=Gq8Xq*)ixQ(fh6x!ELjSflFO>eVQG>dY*P#7z+0Z+KU9xYlyDJEvgk zy8aMX0_*L+odaFEU9amHtla61a=>~zXUg5>Xi(CEKBpELIX$bb19t`we8k1Qcod__ zLZ}=Z>(Mf;u@l?2su{4@U3oN6ICP#;+#+&!r@GvKRW# zV}=N)MwA`ml9fh)@%U!I4(2E*T+7RDt4hL-&oumc3KNpL03+0peP_d*>4 zJi{64GkZlszOt3DB2FWOKVQ*H@eY@efPeA#%}C}^k4i|JqvAJJO35@iM~s>aNqlSS z8|2nht(95v%H!74xLcpR_0~nUR>i_>5qvDyzTe;8D5aUFcph=O_#7Ga(ITzOHqxzz zmIddCq>Oce#gS)TgOUPF6~!la1`%y(&rVWc3YzAp zTfa*E^KD+66IxmXY+11RUBPsdBI@9rYC^u+y4B^^eUlBHRddya>}u7uc3h@2h<0|A zr>&xL$@SYFn@`>GpU__4s069?HU^km&ZDy_0K5~Dpfk@Z%aJytRE?91+g4Ds(-d_pD5l11a!*y7Q|YUV z5jGAxYbf>lTa>TvK9^zqAa!1O_e*j2?esNRMNpjD%AF+m& z^_n-Bj6D!iIqR4k1Wdc0fZ3DMDgrnVF*1JrZqoXO4y%q+qWe1$S>q}ZmFTP$%lWDd zs<;lDaQRc4)_#a%2!`0ljSLAYXqyRm7ZQ|3V14mwoXD5f*-?Uvh{4+82t}{+uy!Fj zwj>4hQ}JoQGI!=U(+!7|@zb{1BSasYoxRC7Jsgm#Z>-vxu)i#Yr_oTDcYovsN0mm&rT&eBehu{F z=NVh@T-eOL=4Q+Nm&zHH6(#J(-`=w6E)iOLJ8~|`03H7HNtaQs$59x>BQSL>uNS;1 z4fg(c$={IoY(o!m)ima@=GRxDRH8XD&|LODbt z(xBewb+MipperIQa=&YKk>}(9!KHuWm(%;+MfY{n7e#^gG3nN5{~*&eQ-l&+T_7Uv z=bkx5?TP5{UwKAz`(@7edPT~u)begQUv;+rj^$6yQpSsBfAG;b^nSS3oBddy`3b}+ zu#ls<0J5IYyo6rzv9VBm$kjW4I!zI1A>|LEz0A8{d~&+Jki+Sk4`3VMfEGu{tkD`@ z>qeThumVNHu=?hZP)klWCZst#2YTQv9=J)iVqEz8Wbyz94nx}K zTwv^A#X~PJ{Rjx2QPjZ;(zhvqRtp%mJcYb;KLwC>F(Vb7Fad>^0I?--05_okl4%5f znv@1C8`&6!2OUtZAAU?H0QaN=h`9&g?9l~qLr|b$62?3Qj839K6(pdJBn8+{K}G)i aJ%0KSFZ~Jqf0(Y0f%vJbY&PWo;Qs(ksB^sl delta 1775 zcmVbznQ*=)eqSh4iL) z%q4IG^9kIZyW|G$FlzBc!5uEXJV6a7QTLjlmGAC2`gi z4@*W##(NTEtK{gGS(eZ>en-8l<|>YEt?>PK%Lk~Hwe0DTb0tR?kq|;+z8sTi@Bn$77Q(yH}u@7wX~#~g>l`~ zCf&$x644Re)SZ(OQt1GUF-No21w$Jin2`_}wF;Uj8Bb1BmKHR7TD%j$t-NZ?sJ1Tl zo-%5AX|?J7egRxiu2aXd88zZ8>g>Xv7h|k{uc41iLQj2gw!j0>ERPl)%&ys1)FEEf zoXJ%JZAcG!};HwQ|K^W?MpU&s21ypa}+a(s!M+qv^1(HK9xr)@j4A?gg>7rw2G5Tm4sm zR|rTi8Ct+3zB*xALBeJ?xA4R%)L*F0*ig8`YnrlTx`8Z$Pe;@L!t(h()uP)9_F=z&f=}?Nf&)VO44;eb7xXb-YeL^(A1}3A3ep&0 z;pc029d%z~RKZvHTERCWcHfICRfl|X;f|JfTvE)e;&xxmSUP(jnV!3Pjy(!r1rlPq zcWJTEb-fQd{J0e^!Wu8LH zJVV>uQ?x%pB=a-YJwddlk+Um*h)K9|a0ML_9%37XxWw~`iOesE46Zk>Kx{{XCiGn^$m{_=pNeFa)^L zP8=ojW0V!HRn2ifuU9FZpqFY^IqrnbIsxsmYmjq`D3tr$pj9X9yZ}{yCnMCohLfI? zYg`2gIg=HYcve%g!R-x(lv7U0nyw0ZxbBr2c(~;ST#A=|n zK%hfBo(o{^@B&FV-PFnYW=;;f_?kI6gHg}PpI*Y+Gjy`)UE{1?_j4#&u`Xl7EZ5|7 zq_fM|_#NWe?!IMgn$0GEH;Z?|DCr8x9?v%Q5WS7Iy{^eJw$uQgASJ<8#@NIz?t0mW zT`bUkc4Ckj9$}uZ5$0L`UB)?FCwb;@k-tJO1LOZ!Y3@<7R%$P=VO&TiA08o(p_c*cDm1*b3BIDJ}^=>x7b z9bLKcu-53IqU9O9u!_tMF};O2khDIFaT`vz^4s z2niy3$y$;H005bjaLr``nRSz4&m)ui%`pOnc9UVxBa=|hL;@RnlVQ&zlTgkDlfKS3 z0uOzYVb3Fz8qY-mQ$lVQ&zllad@0uzjrVb3FzWsDn>e$Xlc6_1la(kYW| zj~0{Q(h!sR&>8{=l9NHwDU)E55R-t?5R+cf8UhxTlR?rclWvs}li<=2lf=;)0uz^$ zLDDai57ICK_L-AG(rE(FoReYCBa_e4I|3)4lVQ&zlP}Xq0%f9;Vb3Cyo6|4?5Veyb z)FPAb(=Y+(lOWVt0`9+)9l$)39@P+&$J81E!oidA!8?;4)hYp%lS$Pw1WK#$ZIgf1 R76Imymenf;Kg0k4003uUWfcGb diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 9eb8b1334..92165eede 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,5 @@ -#Sun Oct 22 14:32:40 CST 2017 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.3-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-4.3-bin.zip diff --git a/gradlew b/gradlew index 8b3531e29..cccdd3d51 100644 --- a/gradlew +++ b/gradlew @@ -78,14 +78,14 @@ if [ -n "$JAVA_HOME" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME Please set the JAVA_HOME variable in your environment to match the -path of your Java installation." +location of your Java installation." fi else JAVACMD="java" which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the -path of your Java installation." +location of your Java installation." fi # Increase the maximum file descriptors if we can. diff --git a/gradlew.bat b/gradlew.bat index 2797c4770..f9553162f 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -27,7 +27,7 @@ echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. echo. echo Please set the JAVA_HOME variable in your environment to match the -echo path of your Java installation. +echo location of your Java installation. goto fail @@ -41,7 +41,7 @@ echo. echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% echo. echo Please set the JAVA_HOME variable in your environment to match the -echo path of your Java installation. +echo location of your Java installation. goto fail