fix: 修复未登录皮肤站时启动卡住且不再弹玩家选择
Some checks failed
Axolotl desktop CI / guardrails (push) Has been cancelled
Repository checks / typos (push) Has been cancelled
Repository checks / tombi (push) Has been cancelled
Rust checks / shear (push) Has been cancelled
Sync source to CNB / Sync Git ref (push) Has been cancelled
Axolotl desktop CI / desktop (macos-latest) (push) Has been cancelled
Axolotl desktop CI / desktop (ubuntu-latest) (push) Has been cancelled
Axolotl desktop CI / desktop (windows-latest) (push) Has been cancelled
Axolotl desktop CI / website (push) Has been cancelled
Sync LobeHub models / sync (push) Has been cancelled
Some checks failed
Axolotl desktop CI / guardrails (push) Has been cancelled
Repository checks / typos (push) Has been cancelled
Repository checks / tombi (push) Has been cancelled
Rust checks / shear (push) Has been cancelled
Sync source to CNB / Sync Git ref (push) Has been cancelled
Axolotl desktop CI / desktop (macos-latest) (push) Has been cancelled
Axolotl desktop CI / desktop (ubuntu-latest) (push) Has been cancelled
Axolotl desktop CI / desktop (windows-latest) (push) Has been cancelled
Axolotl desktop CI / website (push) Has been cancelled
Sync LobeHub models / sync (push) Has been cancelled
未登录皮肤站时启动游戏会弹窗要求选择/登录玩家。若点击“登录皮肤站” 跳转后不登录直接返回,再次启动不会重新弹窗,而是卡在“正在启动”。 原因:InstancePlayerModal 的 signInSkinSite 跳转登录时只隐藏弹窗, 没有 settle 玩家选择的 Promise,因此 prepareInstancePlayer 在 preparing 表中留下的任务永不结束。再次启动时复用了这个悬空任务, 既不再弹窗也无法继续启动。 修复: - instance-player 新增哨兵错误 PlayerSelectionNavigatedAwayError。 - signInSkinSite 跳转登录前以该错误 reject 当前选择,使 prepareInstancePlayer 的任务正常结束、清理 in-flight 记录,下次 启动会重新弹出玩家选择。 - App.vue 的启动错误处理识别该哨兵:跳转登录是用户主动操作而非启动 失败,静默中止启动,不弹错误提示。
This commit is contained in:
@ -113,6 +113,7 @@ import {
|
|||||||
} from '@/helpers/events.js'
|
} from '@/helpers/events.js'
|
||||||
import { install_create_modpack_instance, install_get_modpack_preview } from '@/helpers/install'
|
import { install_create_modpack_instance, install_get_modpack_preview } from '@/helpers/install'
|
||||||
import { type DirectLinkSyncReport, get as getInstance, run } from '@/helpers/instance'
|
import { type DirectLinkSyncReport, get as getInstance, run } from '@/helpers/instance'
|
||||||
|
import { PlayerSelectionNavigatedAwayError } from '@/helpers/instance-player'
|
||||||
import { reconcileMojangAuthSourceAtStartup } from '@/helpers/mojang-auth'
|
import { reconcileMojangAuthSourceAtStartup } from '@/helpers/mojang-auth'
|
||||||
import { cancelLogin, get as getCreds, login, logout } from '@/helpers/mr_auth.ts'
|
import { cancelLogin, get as getCreds, login, logout } from '@/helpers/mr_auth.ts'
|
||||||
import { mergeUrlQuery, parseModrinthLink } from '@/helpers/project-links.ts'
|
import { mergeUrlQuery, parseModrinthLink } from '@/helpers/project-links.ts'
|
||||||
@ -1757,6 +1758,10 @@ async function handleCommand(e) {
|
|||||||
} else if (e.event === 'LaunchInstance') {
|
} else if (e.event === 'LaunchInstance') {
|
||||||
const instance = await getInstance(e.id).catch(() => null)
|
const instance = await getInstance(e.id).catch(() => null)
|
||||||
const handleLaunchCommandError = async (launchError) => {
|
const handleLaunchCommandError = async (launchError) => {
|
||||||
|
// Navigating to the skin-site login to pick a player is a deliberate
|
||||||
|
// user action, not a launch failure: stay silent and let the user
|
||||||
|
// re-trigger the launch after signing in.
|
||||||
|
if (launchError instanceof PlayerSelectionNavigatedAwayError) return
|
||||||
const handled =
|
const handled =
|
||||||
(await minecraftCrashModal.value?.handleLaunchError(launchError, {
|
(await minecraftCrashModal.value?.handleLaunchError(launchError, {
|
||||||
instance_id: e.id,
|
instance_id: e.id,
|
||||||
|
|||||||
@ -14,6 +14,7 @@ import {
|
|||||||
import { users } from '@/helpers/auth'
|
import { users } from '@/helpers/auth'
|
||||||
import { getInstanceMode } from '@/helpers/hosted-packs'
|
import { getInstanceMode } from '@/helpers/hosted-packs'
|
||||||
import {
|
import {
|
||||||
|
PlayerSelectionNavigatedAwayError,
|
||||||
registerInstancePlayerPicker,
|
registerInstancePlayerPicker,
|
||||||
saveInstancePlayer,
|
saveInstancePlayer,
|
||||||
waitForSkinSiteSession,
|
waitForSkinSiteSession,
|
||||||
@ -127,6 +128,14 @@ async function select(player: PlayerChoice) {
|
|||||||
function signInSkinSite() {
|
function signInSkinSite() {
|
||||||
awaitingSkinLogin.value = true
|
awaitingSkinLogin.value = true
|
||||||
hideForLogin = true
|
hideForLogin = true
|
||||||
|
active.value = false
|
||||||
|
generation++
|
||||||
|
// Settle the pending selection so `prepareInstancePlayer` releases its
|
||||||
|
// in-flight entry; otherwise a later launch would await this forever and
|
||||||
|
// never re-prompt. The caller treats this sentinel as a silent abort.
|
||||||
|
rejectSelection?.(new PlayerSelectionNavigatedAwayError())
|
||||||
|
resolveSelection = undefined
|
||||||
|
rejectSelection = undefined
|
||||||
openSkinSiteLogin()
|
openSkinSiteLogin()
|
||||||
modal.value?.hide()
|
modal.value?.hide()
|
||||||
void router.push('/starlight-skin')
|
void router.push('/starlight-skin')
|
||||||
|
|||||||
@ -17,6 +17,18 @@ export type InstancePlayer = {
|
|||||||
skin_site_user?: string | null
|
skin_site_user?: string | null
|
||||||
}
|
}
|
||||||
export type PlayerChoice = InstancePlayer & { head?: string }
|
export type PlayerChoice = InstancePlayer & { head?: string }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Thrown when the player picker is dismissed because the user navigated to the
|
||||||
|
* skin-site login page. Callers should treat this as a silent launch abort
|
||||||
|
* (the user will pick a player next time), not as a real failure.
|
||||||
|
*/
|
||||||
|
export class PlayerSelectionNavigatedAwayError extends Error {
|
||||||
|
constructor() {
|
||||||
|
super('已跳转至皮肤站登录,请登录后重新启动。')
|
||||||
|
this.name = 'PlayerSelectionNavigatedAwayError'
|
||||||
|
}
|
||||||
|
}
|
||||||
type Picker = (instanceId: string, locked: InstancePlayer | null) => Promise<InstancePlayer>
|
type Picker = (instanceId: string, locked: InstancePlayer | null) => Promise<InstancePlayer>
|
||||||
let picker: Picker | undefined
|
let picker: Picker | undefined
|
||||||
const preparing = new Map<string, Promise<void>>()
|
const preparing = new Map<string, Promise<void>>()
|
||||||
|
|||||||
Reference in New Issue
Block a user