diff --git a/README.md b/README.md index 936e4024a..af522ea91 100644 --- a/README.md +++ b/README.md @@ -45,24 +45,30 @@ The launcher supports three account types: | **Offline / Local** | A local username that never touches Mojang's servers — no purchase, no login | ❌ No (single-player and LAN only) | | **Auth server** | A third-party Yggdrasil / authlib server (e.g. a community server) | ✅ Yes, on that server | -**If you cannot afford Minecraft: Java Edition**, the **offline (local) account** option is the supported route. It is -not a purchased account, it is a local username: open **Settings → Accounts → Add account → Local account**, pick a -username, and you can play single-player worlds and mods immediately. You will not be able to join official online -servers, because those verify ownership with Mojang — that is a server-side rule, not something a launcher can change. +**You do not need to own Minecraft or have a Microsoft account to play.** The **offline (local) account** option is +fully supported and available to everyone on every build: open **Settings → Accounts → Add account → Local account**, +pick a username, and you can play single-player worlds, mods and resource packs immediately. The launcher never forces +you through Microsoft sign-in. -### Signing in without owning Minecraft +Local accounts are also the route to join **offline/cracked servers** (servers that do not verify ownership with +Mojang). Whether a server accepts you is that server's own check, not something a launcher can change — servers that +verify ownership with Mojang will reject an unowned account no matter what the launcher sends, so use a local account +with servers that allow it. -You can also sign in with your Microsoft account even if it has never bought Minecraft. Microsoft's identity service -will authenticate you fine; the launcher then checks ownership against Mojang's `entitlements` service as part of the -normal login flow. When that check reports no entitlement, the launcher now: +### Signing in with a Microsoft account -1. **Tells you plainly** that ownership could not be verified and that features may be limited. -2. **Signs you in anyway**, using your Xbox gamertag as a local offline identity. -3. **Lets you launch the instance you selected**, immediately, with no further interruption. +If you have a Microsoft account, you can sign in with it even if it has never bought Minecraft. Microsoft's identity +service authenticates you; the launcher then checks ownership against Mojang's `entitlements` service as part of the +normal login flow. When that check reports no entitlement, the launcher: + +1. **Signs you in anyway**, using your Xbox gamertag as a local offline identity. +2. **Lets you launch the instance you selected**, immediately, with no further interruption. +3. Shows a small "ownership unverified" hint so you know to expect the offline experience. What you get in that state is the offline experience: single-player worlds, mods, resource packs and LAN. What you do not get is online play against official Mojang servers, because **those servers verify ownership themselves** on every -connection. No client-side change can alter that — it is their check, not ours. +connection. No client-side change can alter that — it is their check, not ours. Offline/cracked servers will accept +the account normally. > [!IMPORTANT] > This fork does not remove, disable, or bypass the ownership check. It still runs exactly as upstream intends, and @@ -106,9 +112,13 @@ git clone git@github.com:ChimeraAnt-DEV/JavaAntLauncher.git ### Enabling Microsoft sign-in (required for Microsoft accounts) -Microsoft sign-in **will not work in a build that has no OAuth client ID**, and the failure is confusing: Microsoft's -device-code endpoint rejects the request with `400 Bad Request` (`AADSTS900144: The request body must contain the -following parameter: 'client_id'`). The launcher now detects this case up front and tells you, but you still have to +Microsoft sign-in **is entirely optional**. The build this text ships with has no OAuth client ID, so Microsoft sign-in +is unavailable — the launcher tells you so and points you to the local account flow instead. You can ignore Microsoft +sign-in completely and use a local account to play single-player, mods and offline/cracked servers. + +If you maintain your own fork and want to enable Microsoft sign-in, you need to supply a client ID: Microsoft's +device-code endpoint rejects a request without one with `400 Bad Request` (`AADSTS900144: The request body must contain +the following parameter: 'client_id'`). The launcher detects this case up front and tells you, but you still have to supply an ID to use Microsoft accounts. An OAuth client ID is an Azure app registration that belongs to whoever ships the build. Upstream's ID is not yours to @@ -132,7 +142,7 @@ reuse, so forks must register their own: Alternatively you can set `oauth_client_id` in `ZalithLauncher/gradle.properties`, but note that file **is** tracked by git, so it is a poor place for a real value. -Without one of these, offline accounts still work fully; only Microsoft sign-in is unavailable. +Without one of these, offline/local accounts still work fully; only Microsoft sign-in is unavailable. ## 🎨 Themes diff --git a/ZalithLauncher/src/main/java/com/movtery/zalithlauncher/game/account/AccountsManager.kt b/ZalithLauncher/src/main/java/com/movtery/zalithlauncher/game/account/AccountsManager.kt index f280ba4f1..f9756cfa9 100644 --- a/ZalithLauncher/src/main/java/com/movtery/zalithlauncher/game/account/AccountsManager.kt +++ b/ZalithLauncher/src/main/java/com/movtery/zalithlauncher/game/account/AccountsManager.kt @@ -25,10 +25,8 @@ import com.movtery.zalithlauncher.coroutine.TaskSystem import com.movtery.zalithlauncher.database.AppDatabase import com.movtery.zalithlauncher.game.account.auth_server.data.AuthServer import com.movtery.zalithlauncher.game.account.auth_server.data.AuthServerDao -import com.movtery.zalithlauncher.path.PathManager import com.movtery.zalithlauncher.setting.AllSettings import com.movtery.zalithlauncher.ui.androidText -import com.movtery.zalithlauncher.utils.isInGreaterChina import com.movtery.zalithlauncher.utils.logging.Logger import com.movtery.zalithlauncher.utils.network.isNetworkAvailable import kotlinx.coroutines.CoroutineScope @@ -38,7 +36,6 @@ import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import org.apache.commons.io.FileUtils -import java.io.File import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.CopyOnWriteArrayList @@ -64,9 +61,6 @@ object AccountsManager { /** 控制刷新所有账号衣橱 */ val refreshWardrobe = _refreshWardrobe.asStateFlow() - private val _isOffline = MutableStateFlow(false) - val isOffline = _isOffline - //本次启动器会话内已通过服务端校验的账号 private val sessionValidatedAccounts: MutableSet = ConcurrentHashMap.newKeySet() @@ -236,21 +230,12 @@ object AccountsManager { } /** - * 刷新当前账号,同时刷新非中国大陆地区的正版状态 + * 刷新当前账号状态。 + * 本地账号与微软离线账号均被允许,不再根据地区或是否拥有游戏进行限制 */ private fun refreshCurrentAccountState() { val currentAccount = getCurrentAccount() - val isOffline = checkLimit() - _currentAccountFlow.update { - //若处于非正版状态,不允许使用账号 - if (isOffline) null else currentAccount - } - _isOffline.update { isOffline } - } - - private fun checkLimit(): Boolean { - val circumventLimit = File(PathManager.DIR_FILES_EXTERNAL, "circumventLimit") - return !circumventLimit.exists() && !isInGreaterChina() && !hasMicrosoftAccount() + _currentAccountFlow.update { currentAccount } } /** diff --git a/ZalithLauncher/src/main/java/com/movtery/zalithlauncher/game/launch/GameLaunchFlow.kt b/ZalithLauncher/src/main/java/com/movtery/zalithlauncher/game/launch/GameLaunchFlow.kt index f1c3c7580..527ccf7b6 100644 --- a/ZalithLauncher/src/main/java/com/movtery/zalithlauncher/game/launch/GameLaunchFlow.kt +++ b/ZalithLauncher/src/main/java/com/movtery/zalithlauncher/game/launch/GameLaunchFlow.kt @@ -141,11 +141,6 @@ class GameLaunchFlow(scope: CoroutineScope) { version.offlineAccountLogin = true } - //未拥有 Minecraft 的微软账号无法通过服务端校验,同样以降级为离线身份启动 - if (account.isMicrosoftAccount() && !account.ownsMinecraft) { - version.offlineAccountLogin = true - } - return buildPhase { if (hasNetwork && !skipAccountRefresh && AccountsManager.isLaunchCheckNeeded(account)) { //账号管理页正在刷新该账号时,直接使用现有凭据启动 diff --git a/ZalithLauncher/src/main/java/com/movtery/zalithlauncher/ui/screens/content/AccountManageScreen.kt b/ZalithLauncher/src/main/java/com/movtery/zalithlauncher/ui/screens/content/AccountManageScreen.kt index 1cafcffc3..5f3117a10 100644 --- a/ZalithLauncher/src/main/java/com/movtery/zalithlauncher/ui/screens/content/AccountManageScreen.kt +++ b/ZalithLauncher/src/main/java/com/movtery/zalithlauncher/ui/screens/content/AccountManageScreen.kt @@ -241,7 +241,6 @@ private fun AccountManageContent( .padding(all = 12.dp) .weight(3f), currentAccount = profileUiState.currentAccount, - isOffline = profileUiState.isOffline, actions = actions ) @@ -253,7 +252,6 @@ private fun AccountManageContent( .weight(7f), accounts = profileUiState.accounts, currentAccount = profileUiState.currentAccount, - isOffline = profileUiState.isOffline, accountOperation = operationUiState.accountOp, accountSkinOperation = operationUiState.accountSkinOp, accountSkinDialogState = operationUiState.accountSkinDialogState, @@ -278,7 +276,6 @@ private fun ActionsLayout( isVisible: Boolean, modifier: Modifier = Modifier, currentAccount: Account?, - isOffline: Boolean, actions: AccountActions ) { val xOffset by swapAnimateDpAsState( @@ -350,17 +347,12 @@ private fun ActionsLayout( } } - //添加账号 + //添加账号(本地账号即可游玩,直接打开完整登录菜单) ScalingActionButton( modifier = Modifier .fillMaxWidth(), onClick = { - if (isOffline) { - //非正版状态下,只允许创建微软账号 - actions.onIntent(AccountManageIntent.UpdateMicrosoftLoginOp(MicrosoftLoginOperation.Tip)) - } else { - actions.onIntent(AccountManageIntent.UpdateLoginMenuOp(LoginMenuOperation.Login)) - } + actions.onIntent(AccountManageIntent.UpdateLoginMenuOp(LoginMenuOperation.Login)) } ) { MarqueeText(text = stringResource(R.string.account_add_new_account)) @@ -680,7 +672,6 @@ private fun AccountsLayout( modifier: Modifier = Modifier, accounts: List, currentAccount: Account?, - isOffline: Boolean, accountOperation: AccountOperation, accountSkinOperation: AccountSkinOperation, accountSkinDialogState: AccountManageViewModel.AccountSkinDialogState, @@ -723,7 +714,6 @@ private fun AccountsLayout( .padding(vertical = 6.dp), currentAccount = currentAccount, account = account, - enabled = !isOffline, //非正版状态下不允许选择任何状态 onSelected = { AccountsManager.setCurrentAccount(it) }, openChangeSkinDialog = { if (!account.isAuthServerAccount()) { diff --git a/ZalithLauncher/src/main/java/com/movtery/zalithlauncher/ui/screens/content/elements/LauncherElements.kt b/ZalithLauncher/src/main/java/com/movtery/zalithlauncher/ui/screens/content/elements/LauncherElements.kt index dae8f325b..cc97b4a89 100644 --- a/ZalithLauncher/src/main/java/com/movtery/zalithlauncher/ui/screens/content/elements/LauncherElements.kt +++ b/ZalithLauncher/src/main/java/com/movtery/zalithlauncher/ui/screens/content/elements/LauncherElements.kt @@ -224,11 +224,7 @@ fun LaunchGameOperation( is LaunchGameOperation.NoAccount -> { LaunchedEffect(Unit) { eventViewModel.sendToast(androidText(R.string.game_launch_no_account)) - val isOffline = AccountsManager.isOffline.value - toAccountManageScreen( - if (isOffline) FirstLoginMenu.MICROSOFT - else FirstLoginMenu.NORMAL - ) + toAccountManageScreen(FirstLoginMenu.NORMAL) launchGameViewModel.updateOperation(LaunchGameOperation.None) } } diff --git a/ZalithLauncher/src/main/java/com/movtery/zalithlauncher/viewmodel/AccountManageViewModel.kt b/ZalithLauncher/src/main/java/com/movtery/zalithlauncher/viewmodel/AccountManageViewModel.kt index 4034b4e6b..afd5e249f 100644 --- a/ZalithLauncher/src/main/java/com/movtery/zalithlauncher/viewmodel/AccountManageViewModel.kt +++ b/ZalithLauncher/src/main/java/com/movtery/zalithlauncher/viewmodel/AccountManageViewModel.kt @@ -249,15 +249,13 @@ class AccountManageViewModel @AssistedInject constructor( AccountsManager.accountsFlow, AccountsManager.currentAccountFlow, AccountsManager.authServersFlow, - _accountCapeOpMap, - AccountsManager.isOffline - ) { accounts, currentAccount, authServers, accountCapeOpMap, isOffline -> + _accountCapeOpMap + ) { accounts, currentAccount, authServers, accountCapeOpMap -> ProfileUiState( accounts = accounts, currentAccount = currentAccount, authServers = authServers, - accountCapeOpMap = accountCapeOpMap, - isOffline = isOffline + accountCapeOpMap = accountCapeOpMap ) }.stateIn( scope = viewModelScope, @@ -269,8 +267,7 @@ class AccountManageViewModel @AssistedInject constructor( val accounts: List = emptyList(), val currentAccount: Account? = null, val authServers: List = emptyList(), - val accountCapeOpMap: Map> = emptyMap(), - val isOffline: Boolean = false + val accountCapeOpMap: Map> = emptyMap() ) /** diff --git a/ZalithLauncher/src/main/res/values-zh-rCN/strings.xml b/ZalithLauncher/src/main/res/values-zh-rCN/strings.xml index df4d8dbe1..7064e18c8 100644 --- a/ZalithLauncher/src/main/res/values-zh-rCN/strings.xml +++ b/ZalithLauncher/src/main/res/values-zh-rCN/strings.xml @@ -477,7 +477,7 @@ 当前 IP 地址已被禁止登陆。 请先创建 Minecraft 玩家档案,然后再重新登陆! 已登录,但未检测到 Minecraft 购买信息。你仍可游玩单人游戏和使用模组;联机与 Realms 需要拥有正版。 - 此构建未配置 Microsoft OAuth 客户端 ID,无法使用微软登录。请在 ZalithLauncher/gradle.properties 中设置 oauth_client_id(或添加 OAUTH_CLIENT_ID 构建密钥),使用你自己的 Azure 应用注册后重新构建。 + 此构建未配置 Microsoft OAuth 客户端 ID,因此无法使用微软登录。无需拥有 Minecraft 或微软账号也能游玩:请改用离线本地账号登录,可直接加入离线/破解服务器。 未验证拥有权 — 仅可离线游玩 需要重新登录 启动器无法刷新该微软账号的登录凭据,账号密码可能已被修改,或登录授权已失效,需要重新登录微软账号。 diff --git a/ZalithLauncher/src/main/res/values/strings.xml b/ZalithLauncher/src/main/res/values/strings.xml index 9c9b5edeb..447331dae 100644 --- a/ZalithLauncher/src/main/res/values/strings.xml +++ b/ZalithLauncher/src/main/res/values/strings.xml @@ -477,7 +477,7 @@ The current IP address has been blocked from logging in. Please create a Minecraft player profile first, then try logging in again! Signed in, but Minecraft ownership could not be verified. You can still play single-player and use mods; online multiplayer and Realms require a purchased copy. - This build has no Microsoft OAuth client ID configured, so Microsoft sign-in cannot work. Set oauth_client_id in ZalithLauncher/gradle.properties (or add an OAUTH_CLIENT_ID build secret) using your own Azure app registration, then rebuild. + Microsoft sign-in is not available in this build (no OAuth client ID configured). You don\'t need a Microsoft account to play: add a local account instead and join offline/cracked servers directly. Ownership unverified — offline play only Sign In Required The launcher failed to refresh this Microsoft account\'s sign-in credentials. The password may have been changed or the authorization has been revoked, so you need to sign in to the Microsoft account again.