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 2f56baa0e..fbfdc82fa 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/setting/Accounts.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/setting/Accounts.java @@ -369,6 +369,8 @@ public final class Accounts { } } else if (exception instanceof MicrosoftService.NoMinecraftJavaEditionProfileException) { return i18n("account.methods.microsoft.error.no_character"); + } else if (exception instanceof MicrosoftService.NoXuiException) { + return i18n("account.methods.microsoft.error.add_family_probably"); } else if (exception.getClass() == AuthenticationException.class) { return exception.getLocalizedMessage(); } else { diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/CreateAccountPane.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/CreateAccountPane.java index 0078626c6..23886c3de 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/CreateAccountPane.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/CreateAccountPane.java @@ -120,6 +120,8 @@ public class CreateAccountPane extends JFXDialogLayout implements DialogAware { { lblErrorMessage = new Label(); + lblErrorMessage.setWrapText(true); + lblErrorMessage.setMaxWidth(400); btnAccept = new JFXButton(i18n("account.login")); btnAccept.getStyleClass().add("dialog-accept"); @@ -134,7 +136,10 @@ public class CreateAccountPane extends JFXDialogLayout implements DialogAware { btnCancel.setOnAction(e -> onCancel()); onEscPressed(this, btnCancel::fire); - setActions(lblErrorMessage, new HBox(spinner, btnCancel)); + HBox hbox = new HBox(spinner, btnCancel); + hbox.setAlignment(Pos.CENTER_RIGHT); + + setActions(lblErrorMessage, hbox); } if (showMethodSwitcher) { @@ -254,6 +259,7 @@ public class CreateAccountPane extends JFXDialogLayout implements DialogAware { lblErrorMessage.setText(""); } if (factory == Accounts.FACTORY_MICROSOFT) { + VBox vbox = new VBox(8); HintPane hintPane = new HintPane(MessageDialogPane.MessageType.INFORMATION); hintPane.textProperty().bind(BindingMapping.of(logging).map(logging -> logging @@ -264,7 +270,18 @@ public class CreateAccountPane extends JFXDialogLayout implements DialogAware { FXUtils.copyText(MicrosoftAuthenticationServer.lastlyOpenedURL); } }); - detailsPane = hintPane; + + HBox box = new HBox(8); + Hyperlink birthLink = new Hyperlink(i18n("account.methods.microsoft.birth")); + birthLink.setOnAction(e -> FXUtils.openLink("https://support.microsoft.com/zh-cn/account-billing/如何更改-microsoft-帐户上的出生日期-837badbc-999e-54d2-2617-d19206b9540a")); + Hyperlink profileLink = new Hyperlink(i18n("account.methods.microsoft.profile")); + profileLink.setOnAction(e -> FXUtils.openLink("https://account.live.com/editprof.aspx")); + box.getChildren().setAll(profileLink, birthLink); + GridPane.setColumnSpan(box, 2); + + vbox.getChildren().setAll(hintPane, box); + + detailsPane = vbox; btnAccept.setDisable(false); } else { detailsPane = new AccountDetailsInputPane(factory, btnAccept::fire); @@ -608,4 +625,6 @@ public class CreateAccountPane extends JFXDialogLayout implements DialogAware { } } } + + private static final String MICROSOFT_ACCOUNT_EDIT_PROFILE_URL = "https://support.microsoft.com/zh-cn/account-billing/%E5%A6%82%E4%BD%95%E6%9B%B4%E6%94%B9-microsoft-%E5%B8%90%E6%88%B7%E4%B8%8A%E7%9A%84%E5%87%BA%E7%94%9F%E6%97%A5%E6%9C%9F-837badbc-999e-54d2-2617-d19206b9540a"; } diff --git a/HMCL/src/main/resources/assets/css/root.css b/HMCL/src/main/resources/assets/css/root.css index 5f15611eb..673f97894 100644 --- a/HMCL/src/main/resources/assets/css/root.css +++ b/HMCL/src/main/resources/assets/css/root.css @@ -344,6 +344,7 @@ } .jfx-layout-actions { + -fx-pref-width: 400; -fx-padding: 10.0px 0.0 0.0 0.0; -fx-alignment: center-right; } diff --git a/HMCL/src/main/resources/assets/microsoft_auth.html b/HMCL/src/main/resources/assets/microsoft_auth.html index 4c6df8a53..f9de67484 100644 --- a/HMCL/src/main/resources/assets/microsoft_auth.html +++ b/HMCL/src/main/resources/assets/microsoft_auth.html @@ -27,6 +27,12 @@ along with this program. If not, see .
%close-page%
+ + \ No newline at end of file diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/microsoft/MicrosoftService.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/microsoft/MicrosoftService.java index a97557564..9d938edcc 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/microsoft/MicrosoftService.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/microsoft/MicrosoftService.java @@ -136,7 +136,7 @@ public class MicrosoftService { if (response.displayClaims == null || response.displayClaims.xui == null || response.displayClaims.xui.size() == 0 || !response.displayClaims.xui.get(0).containsKey("uhs")) { LOG.log(Level.WARNING, "Unrecognized xbox authorization response " + GSON.toJson(response)); - throw new ServerResponseMalformedException(); + throw new NoXuiException(); } String uhs = (String) response.displayClaims.xui.get(0).get("uhs"); @@ -169,6 +169,7 @@ public class MicrosoftService { mapOf(pair("SandboxId", "RETAIL"), pair("UserTokens", Collections.singletonList(xboxResponse.token)))), pair("RelyingParty", "rp://api.minecraftservices.com/"), pair("TokenType", "JWT"))) + .ignoreHttpErrorCode(401) .getJson(XBoxLiveAuthenticationResponse.class); getUhs(minecraftXstsResponse, uhs); @@ -331,6 +332,9 @@ public class MicrosoftService { public static class NoMinecraftJavaEditionProfileException extends AuthenticationException { } + public static class NoXuiException extends AuthenticationException { + } + /** * Error response: {"error":"invalid_grant","error_description":"The provided * value for the 'redirect_uri' is not valid. The value must exactly match the diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/io/HttpRequest.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/io/HttpRequest.java index eee824501..705f60f46 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/io/HttpRequest.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/io/HttpRequest.java @@ -31,7 +31,9 @@ import java.net.MalformedURLException; import java.net.URL; import java.nio.charset.StandardCharsets; import java.util.HashMap; +import java.util.HashSet; import java.util.Map; +import java.util.Set; import java.util.concurrent.CompletableFuture; import static java.nio.charset.StandardCharsets.UTF_8; @@ -46,6 +48,7 @@ public abstract class HttpRequest { protected final String method; protected final Map headers = new HashMap<>(); protected ExceptionalBiConsumer responseCodeTester; + protected final Set toleratedHttpCodes = new HashSet<>(); private HttpRequest(String url, String method) { this.url = url; @@ -92,6 +95,11 @@ public abstract class HttpRequest { return this; } + public HttpRequest ignoreHttpErrorCode(int code) { + toleratedHttpCodes.add(code); + return this; + } + public HttpURLConnection createConnection() throws IOException { HttpURLConnection con = createHttpConnection(new URL(url)); con.setRequestMethod(method); @@ -157,7 +165,10 @@ public abstract class HttpRequest { responseCodeTester.accept(new URL(url), con.getResponseCode()); } else { if (con.getResponseCode() / 100 != 2) { - throw new ResponseCodeException(new URL(url), con.getResponseCode()); + if (!toleratedHttpCodes.contains(con.getResponseCode())) { + String data = NetworkUtils.readData(con); + throw new ResponseCodeException(new URL(url), con.getResponseCode(), data); + } } } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/io/ResponseCodeException.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/io/ResponseCodeException.java index 204cfdc6e..bdd28f090 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/io/ResponseCodeException.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/io/ResponseCodeException.java @@ -24,11 +24,20 @@ public class ResponseCodeException extends IOException { private final URL url; private final int responseCode; + private final String data; public ResponseCodeException(URL url, int responseCode) { super("Unable to request url " + url + ", response code: " + responseCode); this.url = url; this.responseCode = responseCode; + this.data = null; + } + + public ResponseCodeException(URL url, int responseCode, String data) { + super("Unable to request url " + url + ", response code: " + responseCode + ", data: " + data); + this.url = url; + this.responseCode = responseCode; + this.data = data; } public URL getUrl() { @@ -38,4 +47,8 @@ public class ResponseCodeException extends IOException { public int getResponseCode() { return responseCode; } + + public String getData() { + return data; + } }