[重要] 实现 #3095 微软登录界面对 XBox 400 错误给予提示 (#3121)

* Fix #3095

* Fix

* Update I18N_zh_CN.properties

---------

Co-authored-by: Glavo <zjx001202@gmail.com>
This commit is contained in:
Burning_TNT 2024-07-20 01:56:13 +08:00 committed by GitHub
parent 1e472ea463
commit b7a5b484b5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 57 additions and 24 deletions

View File

@ -54,17 +54,19 @@ import static org.jackhuang.hmcl.setting.ConfigHolder.config;
import static org.jackhuang.hmcl.ui.FXUtils.onInvalidating;
import static org.jackhuang.hmcl.util.Lang.immutableListOf;
import static org.jackhuang.hmcl.util.Lang.mapOf;
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
import static org.jackhuang.hmcl.util.Pair.pair;
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
/**
* @author huangyuhui
*/
public final class Accounts {
private Accounts() {}
private Accounts() {
}
private static final AuthlibInjectorArtifactProvider AUTHLIB_INJECTOR_DOWNLOADER = createAuthlibInjectorArtifactProvider();
private static void triggerAuthlibInjectorUpdateCheck() {
if (AUTHLIB_INJECTOR_DOWNLOADER instanceof AuthlibInjectorDownloader) {
Schedulers.io().execute(() -> {
@ -87,6 +89,7 @@ public final class Accounts {
// ==== login type / account factory mapping ====
private static final Map<String, AccountFactory<?>> type2factory = new HashMap<>();
private static final Map<AccountFactory<?>, String> factory2type = new HashMap<>();
static {
type2factory.put("offline", FACTORY_OFFLINE);
type2factory.put("authlibInjector", FACTORY_AUTHLIB_INJECTOR);
@ -130,7 +133,7 @@ public final class Accounts {
private static final String GLOBAL_PREFIX = "$GLOBAL:";
private static final ObservableList<Map<Object, Object>> globalAccountStorages = FXCollections.observableArrayList();
private static final ObservableList<Account> accounts = observableArrayList(account -> new Observable[] { account });
private static final ObservableList<Account> accounts = observableArrayList(account -> new Observable[]{account});
private static final ObjectProperty<Account> selectedAccount = new SimpleObjectProperty<>(Accounts.class, "selectedAccount");
/**
@ -465,6 +468,8 @@ public final class Accounts {
} else {
return i18n("account.methods.microsoft.error.unknown", errorCode);
}
} else if (exception instanceof MicrosoftService.XBox400Exception) {
return i18n("account.methods.microsoft.error.wrong_verify_method");
} else if (exception instanceof MicrosoftService.NoMinecraftJavaEditionProfileException) {
return i18n("account.methods.microsoft.error.no_character");
} else if (exception instanceof MicrosoftService.NoXuiException) {

View File

@ -107,6 +107,7 @@ account.methods.microsoft.error.country_unavailable=Xbox Live is not available i
account.methods.microsoft.error.missing_xbox_account=Your Microsoft account does not have a linked Xbox account yet. Please create one before continuing.
account.methods.microsoft.error.no_character=Your account does not own the Minecraft Java Edition.\nThe game profile may not have been created,\nplease click the link above to create it.
account.methods.microsoft.error.unknown=Failed to log in, error: %d.
account.methods.microsoft.error.wrong_verify_method=Please log in using your account & password on the Microsoft account login page. Please do not use a verification code to log in.
account.methods.microsoft.logging_in=Logging in...
account.methods.microsoft.hint=Please click on the "login" button, and copy the code shown here later to finish the login process in the opened browser window.\n\
\n\

View File

@ -106,6 +106,7 @@ account.methods.microsoft.error.country_unavailable=你所在的國家或地區
account.methods.microsoft.error.missing_xbox_account=你的微軟帳號尚未關聯 XBox 帳號,你必須先創建 XBox 帳號,才能登入遊戲。
account.methods.microsoft.error.no_character=該帳戶未包含 Minecraft Java 版購買記錄\n可能未創建遊戲檔案請點擊上方鏈接創建
account.methods.microsoft.error.unknown=登入失敗,錯誤碼:%d
account.methods.microsoft.error.wrong_verify_method=請在 Microsoft 帳號登陸頁面使用帳號 + 密碼登入。請不要使用驗證碼登入。
account.methods.microsoft.logging_in=登入中...
account.methods.microsoft.makegameidsettings=創建檔案/編輯檔案名稱
account.methods.microsoft.hint=你需要按照以下步驟添加賬戶:\n\

View File

@ -101,12 +101,13 @@ account.methods.microsoft=微软账户
account.methods.microsoft.birth=如何修改账户出生日期
account.methods.microsoft.close_page=已完成微软账户授权,接下来启动器还需要完成剩余登录步骤。你已经可以关闭本页面了。
account.methods.microsoft.deauthorize=解除账户授权
account.methods.microsoft.error.add_family=由于你未满 18 岁,你的账户必须被加入到家庭中才能登录游戏。你也可以点击上方【账户设置页】更改你的账户的出生日期,使年龄满 18 岁以上以继续登录。\n你可以点击右上角帮助按钮进行求助。
account.methods.microsoft.error.add_family_probably=检查你的账户设置,如果年龄未满 18 岁,你的账户必须被加入到家庭中才能登录游戏。你也可以点击上方链接更改你的账户的出生日期,使年龄满 18 岁以上以继续登录。\n你可以点击右上角帮助按钮进行求助。
account.methods.microsoft.error.add_family=请点击上方【账户设置页】更改你的账户的出生日期,使年龄满 18 岁以上。或将账户加入到家庭中。\n你可以点击右上角帮助按钮进行求助。
account.methods.microsoft.error.add_family_probably=点击上方【账户设置页】更改你的账户的出生日期,使年龄满 18 岁以上。或将账户加入到家庭中。\n你可以点击右上角帮助按钮进行求助。
account.methods.microsoft.error.country_unavailable=你所在的国家或地区不受 XBox Live 的支持。
account.methods.microsoft.error.missing_xbox_account=你的微软账户尚未关联 XBox 账户,你必须先创建 XBox 账户,才能登录游戏。\n你可以点击右上角帮助按钮进行求助。
account.methods.microsoft.error.no_character=该账户未包含 Minecraft Java 版购买记录\n若已购买则可能未创建游戏档案请点击上方链接创建。\n若确定该账户完成了上述步骤请先在 Minecraft 官网minecraft.net登录一次账户然后再在启动器登录。\n你可以点击右上角帮助按钮进行求助。
account.methods.microsoft.error.unknown=登录失败,错误码:%d
account.methods.microsoft.error.missing_xbox_account=请点击上方【创建档案】关联 XBox 账户。\n你可以点击右上角帮助按钮进行求助。
account.methods.microsoft.error.no_character=请确认你购买了 Minecraft: Java 版。若已购买,该账户未包含 Minecraft Java 版购买记录\n请点击【创建档案】创建游戏档案。\n你可以点击右上角帮助按钮进行求助。
account.methods.microsoft.error.unknown=未知问题。错误码:%d。\n你可以点击右上角帮助按钮进行求助。
account.methods.microsoft.error.wrong_verify_method=请在 Microsoft 账户登陆页面使用账户 + 密码登录。请不要使用验证码登录。\n你可以点击右上角帮助按钮进行求助。
account.methods.microsoft.logging_in=登录中……
account.methods.microsoft.makegameidsettings=创建档案/编辑档案名称
account.methods.microsoft.hint=你需要按照以下步骤添加账户:\n\

View File

@ -21,8 +21,10 @@ import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonParseException;
import com.google.gson.annotations.SerializedName;
import org.jackhuang.hmcl.auth.*;
import org.jackhuang.hmcl.auth.AuthenticationException;
import org.jackhuang.hmcl.auth.OAuth;
import org.jackhuang.hmcl.auth.ServerDisconnectException;
import org.jackhuang.hmcl.auth.ServerResponseMalformedException;
import org.jackhuang.hmcl.auth.yggdrasil.CompleteGameProfile;
import org.jackhuang.hmcl.auth.yggdrasil.RemoteAuthenticationException;
import org.jackhuang.hmcl.auth.yggdrasil.Texture;
@ -44,8 +46,8 @@ import static java.net.HttpURLConnection.HTTP_NOT_FOUND;
import static java.util.Objects.requireNonNull;
import static org.jackhuang.hmcl.util.Lang.mapOf;
import static org.jackhuang.hmcl.util.Lang.threadPool;
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
import static org.jackhuang.hmcl.util.Pair.pair;
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
public class MicrosoftService {
private static final String SCOPE = "XboxLive.signin offline_access";
@ -123,17 +125,25 @@ public class MicrosoftService {
String uhs = getUhs(xboxResponse, null);
// Authenticate Minecraft with XSTS
XBoxLiveAuthenticationResponse minecraftXstsResponse = HttpRequest
.POST("https://xsts.auth.xboxlive.com/xsts/authorize")
.json(mapOf(
pair("Properties",
mapOf(pair("SandboxId", "RETAIL"),
pair("UserTokens", Collections.singletonList(xboxResponse.token)))),
pair("RelyingParty", "rp://api.minecraftservices.com/"), pair("TokenType", "JWT")))
.ignoreHttpErrorCode(401)
.retry(5)
.getJson(XBoxLiveAuthenticationResponse.class);
XBoxLiveAuthenticationResponse minecraftXstsResponse;
try {
minecraftXstsResponse = HttpRequest
.POST("https://xsts.auth.xboxlive.com/xsts/authorize")
.json(mapOf(
pair("Properties",
mapOf(pair("SandboxId", "RETAIL"),
pair("UserTokens", Collections.singletonList(xboxResponse.token)))),
pair("RelyingParty", "rp://api.minecraftservices.com/"), pair("TokenType", "JWT")))
.ignoreHttpErrorCode(401)
.retry(5)
.getJson(XBoxLiveAuthenticationResponse.class);
} catch (ResponseCodeException e) {
if (e.getResponseCode() == 400) {
throw new XBox400Exception();
}
throw e;
}
getUhs(minecraftXstsResponse, uhs);
@ -290,6 +300,9 @@ public class MicrosoftService {
public static final long ADD_FAMILY = 2148916238L;
}
public static class XBox400Exception extends AuthenticationException {
}
public static class NoMinecraftJavaEditionProfileException extends AuthenticationException {
}

View File

@ -187,13 +187,18 @@ public abstract class HttpRequest {
os.write(bytes);
}
URL url = new URL(this.url);
if (responseCodeTester != null) {
responseCodeTester.accept(new URL(url), con.getResponseCode());
responseCodeTester.accept(url, con.getResponseCode());
} else {
if (con.getResponseCode() / 100 != 2) {
if (!ignoreHttpCode && !toleratedHttpCodes.contains(con.getResponseCode())) {
String data = NetworkUtils.readData(con);
throw new ResponseCodeException(new URL(url), con.getResponseCode(), data);
try {
throw new ResponseCodeException(url, con.getResponseCode(), NetworkUtils.readData(con));
} catch (IOException e) {
throw new ResponseCodeException(url, con.getResponseCode(), e);
}
}
}
}

View File

@ -33,6 +33,13 @@ public final class ResponseCodeException extends IOException {
this.data = null;
}
public ResponseCodeException(URL url, int responseCode, Throwable cause) {
super("Unable to request url " + url + ", response code: " + responseCode, cause);
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;