From f450d0d7e0c655834bd60b2de4be300b4ae83cb8 Mon Sep 17 00:00:00 2001 From: openhands Date: Fri, 18 Sep 2026 20:12:07 +0000 Subject: [PATCH] Fix crash when a version's versionInfo is missing getVersionInfo() returns null whenever a version's .json is absent or fails to parse, but the version directory itself still exists on disk. Three call sites dereferenced it with !!: - LauncherElements.TryLaunch read minecraftVersion before doing anything else. - GameHandler.execute read it twice: once for the pre-1.13 key fixups and once for loadLanguage(). This one runs as the game screen opens, which is why the crash shows up after the game has already started loading. All three now degrade instead of throwing. The launch path checks versionInfo up front and reports a new LaunchGameOperation.InvalidVersion, which sends the user to version management with an explanation rather than a crash screen; the handler skips the two version-dependent fixups when the version number is unknown. Neither fixup is needed to start the game. Also add AGENTS.md with build notes and the launch pipeline layout. Co-authored-by: openhands --- AGENTS.md | 46 +++++++++++++++++++ .../game/launch/handler/GameHandler.kt | 21 +++++---- .../content/elements/LauncherElements.kt | 17 ++++++- .../src/main/res/values-zh-rCN/strings.xml | 1 + .../src/main/res/values-zh-rTW/strings.xml | 1 + .../src/main/res/values/strings.xml | 1 + 6 files changed, 78 insertions(+), 9 deletions(-) create mode 100644 AGENTS.md diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 000000000..a2b95422a --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,46 @@ +# JavaAntLauncher — repository notes + +Android launcher for Minecraft: Java Edition. Fork of Zalith Launcher 2, which itself +descends from PojavLauncher. Repo root is the Gradle root; the Android app module is +`ZalithLauncher/`. + +## Build + +* `./gradlew :ZalithLauncher:assembleDebug` (needs a JDK and an Android SDK). +* A dev container may have neither installed — check before promising a compile or a run. +* `ZalithLauncher/gradle.properties` holds `oauth_client_id`; a git-ignored + `ZalithLauncher/.oauth_client_id.txt` overrides it locally. CI passes `OAUTH_CLIENT_ID`. +* Microsoft sign-in needs a real Azure app registration (public client flows enabled). + Without one, only offline/local accounts work. + +## Architecture worth knowing + +* Launch pipeline: `LaunchGameOperation.TryLaunch` (UI gate) → + `LaunchGameViewModel.start()` → `GameLaunchFlow.buildLaunchPhases()` (account check + + file verification) → `runGame()` → `GameLauncher.launch()`. +* `LaunchGameOperation` is a sealed interface in + `ui/screens/content/elements/LauncherElements.kt`. Adding a case means handling it in the + `when` in that same file — it is exhaustive, so the compiler enforces it. +* `Version` is `Parcelize`d and passed through Intents. `getVersionInfo()` returns + `VersionInfo?`; it is **null** whenever the version's `.json` is missing or fails to + parse, while the version can still exist on disk. `version.isValid()` is exactly + "versionInfo parsed successfully". +* `version.offlineAccountLogin` decides whether `GameLauncher` swaps in a LOCAL copy of the + account (`usingAccount`). Microsoft accounts with no entitlement are constructed as offline + identities (locally derived UUID, no official profile), so they *must* keep this flag or + the client fails session validation after launch. +* `OfflineYggdrasilServer` is only started for LOCAL accounts that have a local skin file; + it is what lets offline accounts resolve skins. + +## Conventions + +* UI strings live in `values/`, `values-zh-rCN/`, `values-zh-rTW/`. Add all three. +* Comments in this codebase are written in Chinese; match the surrounding file. +* `!!` on `getVersionInfo()` is a latent crash. Use `?.` and surface a launch error instead. + +## Gotchas + +* No JDK/Android SDK in the default dev container: verify Kotlin changes by reading, not by + compiling, unless you install a toolchain. +* `MCOptions` watches `options.txt` with a `FileObserver`; it stops watching around its own + atomic write to avoid re-entrant reloads. diff --git a/ZalithLauncher/src/main/java/com/movtery/zalithlauncher/game/launch/handler/GameHandler.kt b/ZalithLauncher/src/main/java/com/movtery/zalithlauncher/game/launch/handler/GameHandler.kt index 5e82dbf74..c7da6c810 100644 --- a/ZalithLauncher/src/main/java/com/movtery/zalithlauncher/game/launch/handler/GameHandler.kt +++ b/ZalithLauncher/src/main/java/com/movtery/zalithlauncher/game/launch/handler/GameHandler.kt @@ -103,13 +103,16 @@ class GameHandler( // set("options.narrator", "0") // set("narrator", "0") - if (version.getVersionInfo()!!.minecraftVersion.isLowerVer("1.13")) { - //fix: 牢版本按键事件 - //shift + w -> 87 错误的触发了F11,切换全屏 - set("key_key.fullscreen", "0") - //输入字符@ -> 64 错误的触发了F6,触发“开始/停止直播” - set("key_key.streamStartStop", "0") - set("key_key.streamPauseUnpause", "0") + //版本信息缺失时无法判断版本号,跳过这些兼容性修正而不是中断启动 + version.getVersionInfo()?.minecraftVersion?.let { minecraftVersion -> + if (minecraftVersion.isLowerVer("1.13")) { + //fix: 牢版本按键事件 + //shift + w -> 87 错误的触发了F11,切换全屏 + set("key_key.fullscreen", "0") + //输入字符@ -> 64 错误的触发了F6,触发“开始/停止直播” + set("key_key.streamStartStop", "0") + set("key_key.streamPauseUnpause", "0") + } } set("overrideWidth", screenSize.width.toString()) @@ -126,7 +129,9 @@ class GameHandler( else -> set(graphicsOption, graphicsApi.option) } - loadLanguage(version.getVersionInfo()!!.minecraftVersion) + version.getVersionInfo()?.minecraftVersion?.let { minecraftVersion -> + loadLanguage(minecraftVersion) + } save() } 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 cc97b4a89..e2b038e2c 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 @@ -130,6 +130,8 @@ sealed interface LaunchGameOperation { data object NoVersion : LaunchGameOperation /** 版本名称非法时 */ data class InvalidVersionName(val th: InvalidFilenameException) : LaunchGameOperation + /** 选中的版本无法读取版本信息(缺少或损坏的 json),无法启动 */ + data class InvalidVersion(val version: Version) : LaunchGameOperation /** 没有可用账号 */ data object NoAccount : LaunchGameOperation @@ -228,6 +230,13 @@ fun LaunchGameOperation( launchGameViewModel.updateOperation(LaunchGameOperation.None) } } + is LaunchGameOperation.InvalidVersion -> { + LaunchedEffect(Unit) { + eventViewModel.sendToast(androidText(R.string.game_launch_invalid_version)) + toVersionManageScreen() + launchGameViewModel.updateOperation(LaunchGameOperation.None) + } + } is LaunchGameOperation.RendererNoStoragePermission -> { LaunchedEffect(Unit) { val renderer = operation.renderer @@ -303,7 +312,13 @@ fun LaunchGameOperation( Renderers.setCurrentRenderer(version.getRenderer()) val currentRenderer = Renderers.getCurrentRenderer() - val mcVer = version.getVersionInfo()!!.minecraftVersion + //缺少或损坏的版本 json 会导致版本信息为 null,后续所有取用都会失败 + //这里提前拦截,避免启动过程中直接崩溃 + val versionInfo = version.getVersionInfo() ?: run { + launchGameViewModel.updateOperation(LaunchGameOperation.InvalidVersion(version)) + return@LaunchedEffect + } + val mcVer = versionInfo.minecraftVersion // 设备完全支持 Vulkan 时跳过渲染器的版本支持检查 if (!version.hasVulkanBackend() || !ensureVulkanSupported(version)) { diff --git a/ZalithLauncher/src/main/res/values-zh-rCN/strings.xml b/ZalithLauncher/src/main/res/values-zh-rCN/strings.xml index 7064e18c8..d6c2fa89a 100644 --- a/ZalithLauncher/src/main/res/values-zh-rCN/strings.xml +++ b/ZalithLauncher/src/main/res/values-zh-rCN/strings.xml @@ -401,6 +401,7 @@ 未选择游戏版本! 未登录游戏账号! + 该版本无法启动:版本文件缺失或已损坏。请重新安装该版本后再试。 未找到合适的运行环境,已设置为当前版本选择的运行环境。 输入法 强制关闭 diff --git a/ZalithLauncher/src/main/res/values-zh-rTW/strings.xml b/ZalithLauncher/src/main/res/values-zh-rTW/strings.xml index 5e4fa903c..d80e447f2 100644 --- a/ZalithLauncher/src/main/res/values-zh-rTW/strings.xml +++ b/ZalithLauncher/src/main/res/values-zh-rTW/strings.xml @@ -358,6 +358,7 @@ 未選擇遊戲版本! 未登入遊戲帳號! + 該版本無法啟動:版本檔案缺失或已損毀。請重新安裝該版本後再試。 找不到合適的執行環境,已設定為目前版本選擇的執行環境。 輸入法 強制關閉 diff --git a/ZalithLauncher/src/main/res/values/strings.xml b/ZalithLauncher/src/main/res/values/strings.xml index 447331dae..75a1ad05e 100644 --- a/ZalithLauncher/src/main/res/values/strings.xml +++ b/ZalithLauncher/src/main/res/values/strings.xml @@ -401,6 +401,7 @@ No game version selected! No account logged in! + This version cannot be launched: its version file is missing or damaged. Reinstall the version and try again. No compatible runtime found. Using selected version\'s runtime. Input Method Force Close