From 405d980311e687e0bf1438e34c3cd2992b9ca64b Mon Sep 17 00:00:00 2001 From: Ap_Tx <3045929398@qq.com> Date: Sat, 19 Sep 2026 19:51:42 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E7=99=BB=E5=BD=95=E7=9A=AE=E8=82=A4?= =?UTF-8?q?=E7=AB=99=E5=90=8E=E4=BE=A7=E6=A0=8F=E7=AB=8B=E5=8D=B3=E6=98=BE?= =?UTF-8?q?=E7=A4=BA=E7=8E=A9=E5=AE=B6=E9=80=89=E6=8B=A9=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 登录 StarLight 皮肤站后,侧栏账户选择器(AccountsCard)不会列出 皮肤站玩家,必须先使用该玩家启动一次实例,玩家才会出现。 原因:账户选择器列出的是 auth::get_users 的账户库,而皮肤站玩家 是 iframe 推送的另一套数据。两者唯一的打通点是 auth|login_skin_site_player(把玩家注册为 yggdrasil 账户),而它 此前只在启动实例选择玩家时被调用。 修复: - instance-player 新增 registerSkinSitePlayers:玩家列表就绪后, 把尚未注册的皮肤站玩家逐个注册为账户。幂等(跳过账户库中已存在 的 profile id)、best-effort(单个失败不影响其余)、每个玩家 单独获取下载 token(皮肤站登录接口可能将 token 绑定到单一玩家)。 - AccountsCard 的玩家监听新增 skinSiteUser 依赖,玩家就绪后调用 注册并刷新账户列表,使玩家无需先启动实例即可在选择器中选择。 --- .../src/components/ui/AccountsCard.vue | 16 ++++++- .../src/helpers/instance-player.ts | 46 +++++++++++++++++++ 2 files changed, 60 insertions(+), 2 deletions(-) diff --git a/apps/app-frontend/src/components/ui/AccountsCard.vue b/apps/app-frontend/src/components/ui/AccountsCard.vue index 85fc43b..82fe394 100644 --- a/apps/app-frontend/src/components/ui/AccountsCard.vue +++ b/apps/app-frontend/src/components/ui/AccountsCard.vue @@ -291,6 +291,7 @@ import { users, } from '@/helpers/auth' import { process_listener } from '@/helpers/events' +import { registerSkinSitePlayers } from '@/helpers/instance-player' import { getPlayerHeadUrl } from '@/helpers/rendering/batch-skin-renderer.ts' import type { Skin } from '@/helpers/skins' import { get_available_skins } from '@/helpers/skins' @@ -630,11 +631,22 @@ async function setAccount(account: MinecraftCredential) { } watch( - [skinSitePlayers, defaultUser], - ([availablePlayers, selectedLocalUser]) => { + [skinSitePlayers, defaultUser, skinSiteUser], + ([availablePlayers, selectedLocalUser, siteUser]) => { if (!selectedLocalUser && !selectedSkinSitePlayerId.value && availablePlayers.length > 0) { selectSkinSitePlayer(availablePlayers[0].uuid) } + // Register skin-site players as launcher accounts as soon as they are + // available, so the account picker shows them without requiring a first + // launch. `registerSkinSitePlayers` is idempotent and best-effort. + if (siteUser?.uuid && availablePlayers.length > 0) { + const pendingIds = availablePlayers.map((player) => player.uuid) + void registerSkinSitePlayers(pendingIds, siteUser.uuid) + .then(() => refreshValues()) + .catch((error) => { + console.warn('Failed to register skin site players:', error) + }) + } }, { immediate: true }, ) diff --git a/apps/app-frontend/src/helpers/instance-player.ts b/apps/app-frontend/src/helpers/instance-player.ts index c44725b..86f55b1 100644 --- a/apps/app-frontend/src/helpers/instance-player.ts +++ b/apps/app-frontend/src/helpers/instance-player.ts @@ -56,6 +56,52 @@ export async function authenticateInstancePlayer(player: InstancePlayer) { } } +/** + * Registers every skin-site player as a launcher account so they show up in the + * account picker immediately after signing in to the skin site, instead of only + * after a first launch. Idempotent: players that already exist in the account + * list (matched by profile UUID) are skipped, and already-signed-in players are + * not re-requested. Best-effort: a single player failing does not abort the rest. + */ +export async function registerSkinSitePlayers( + playerIds: string[], + userUuid: string, +): Promise { + if (playerIds.length === 0) return + if (skinSiteStatus.value !== 'signed-in' || skinSiteUser.value?.uuid !== userUuid) return + + let known = new Set() + try { + const existing = await users() + known = new Set( + (existing as Array<{ profile?: { id?: string } }>) + .map((account) => account?.profile?.id) + .filter((id): id is string => typeof id === 'string'), + ) + } catch { + // If the account list cannot be read, still attempt to register; the + // backend upsert is idempotent. + } + + for (const playerId of playerIds) { + if (known.has(playerId)) continue + if (skinSiteStatus.value !== 'signed-in' || skinSiteUser.value?.uuid !== userUuid) return + try { + // Request a fresh download token per player: the skin site login + // endpoint may bind a token to a single player id. + const token = await requestSkinSiteDownloadToken() + await invoke('plugin:auth|login_skin_site_player', { + token, + playerId, + userId: userUuid, + }) + known.add(playerId) + } catch (error) { + console.warn(`Failed to register skin site player ${playerId}:`, error) + } + } +} + export async function saveInstancePlayer(instanceId: string, player: InstancePlayer) { await authenticateInstancePlayer(player) await invoke('plugin:auth|set_instance_player', { instanceId, player })