Files
Starlight_Lancher/apps/app-frontend/src/helpers/hosted-packs.ts
T
Xiao-no-love 5006f432dc
Axolotl desktop CI / guardrails (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
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
feat: 一键安装支持自选游戏目录
StarLight 实例一键安装此前固定落到启动器默认目录,无法选择游戏数据位置。
现复用创建流程的外部游戏目录能力:点击一键安装后弹出目录选择框,
只允许修改游戏目录,其余参数仍由服务器整合包决定。

- hosted::create 接受 game_dir_root,拼为 <root>/<包名> 作为 game_dir_override
  (刻意避开 versions/ 布局,该结构被外部直链实例检测占用)
- 新增顶层命令 get_launcher_root_dir,作为弹窗默认值(可执行文件所在目录)
- 新增 HostedGameDirModal 弹窗:只读路径框 + 浏览 + 默认位置 + 安装预览
- 补齐 en-US / zh-CN / zh-TW 三语文案

hosted_create 增加 game_dir_root 参数,前端 hostedCreate/install 同步透传。
2026-09-18 13:04:28 +08:00

112 lines
3.4 KiB
TypeScript

import { invoke } from '@tauri-apps/api/core'
import {
requestSkinSiteDownloadToken,
skinSiteStatus,
skinSiteUser,
waitForSkinSiteSession,
} from '../composables/skin-site-session.ts'
let sessionUpdate: Promise<void> = Promise.resolve()
const attemptListeners = new Set<(instanceId: string) => void>()
export function onHostedPackAttemptStarted(listener: (instanceId: string) => void) {
attemptListeners.add(listener)
return () => {
attemptListeners.delete(listener)
}
}
function startHostedPackAttempt(instanceId: string) {
for (const listener of attemptListeners) listener(instanceId)
}
export function clearHostedSession(): Promise<void> {
const update = sessionUpdate
.catch(() => {})
.then(() => invokeHosted<void>('plugin:install|hosted_set_session', { token: null }))
sessionUpdate = update
return update
}
export async function prepareHostedSession(): Promise<void> {
await waitForSkinSiteSession()
const userId = skinSiteUser.value?.uuid
const token = await requestSkinSiteDownloadToken()
const update = sessionUpdate
.catch(() => {})
.then(async () => {
if (skinSiteStatus.value !== 'signed-in' || skinSiteUser.value?.uuid !== userId) {
throw new Error('StarLight 登录状态已变化,请重试。')
}
await invokeHosted<void>('plugin:install|hosted_set_session', { token })
})
sessionUpdate = update
await update
}
async function invokeWithSession<T>(command: string, args?: Record<string, unknown>): Promise<T> {
await prepareHostedSession()
return invokeHosted<T>(command, args)
}
async function invokeHosted<T>(command: string, args?: Record<string, unknown>): Promise<T> {
try {
return await invoke<T>(command, args)
} catch (cause) {
if (
!(cause instanceof Error) &&
typeof cause === 'object' &&
cause !== null &&
'message' in cause &&
typeof cause.message === 'string'
) {
throw new Error(cause.message, { cause })
}
throw cause
}
}
export type InstanceMode = 'starlight' | 'local'
export const getInstanceMode = (instanceId: string) =>
invokeHosted<InstanceMode>('plugin:install|hosted_instance_mode', { instanceId })
export const setInstanceMode = (instanceId: string, mode: InstanceMode) => {
if (mode === 'starlight') startHostedPackAttempt(instanceId)
return (mode === 'starlight' ? invokeWithSession<void> : invokeHosted<void>)(
'plugin:install|hosted_set_instance_mode',
{ instanceId, mode },
)
}
export interface HostedPublication {
packId: string
releaseId: number
manifest: {
name: string
version: string
format: string
runtime: { gameVersion: string; loader: string; loaderVersion: string | null }
files: { path: string; size: number; sha256: string }[]
}
}
export interface HostedBinding {
publication: HostedPublication
}
export interface HostedSyncResult {
version: string
downloadedBytes: number
changedFiles: number
preservedFiles: string[]
}
export const hostedDefault = () =>
invokeWithSession<HostedPublication>('plugin:install|hosted_default')
export const hostedCreate = (gameDirRoot?: string | null) =>
invokeWithSession<string>('plugin:install|hosted_create', {
gameDirRoot: gameDirRoot ?? null,
})
export const hostedBinding = (instanceId: string) =>
invokeHosted<HostedBinding | null>('plugin:install|hosted_binding', { instanceId })
export const hostedSync = (instanceId: string) => {
startHostedPackAttempt(instanceId)
return invokeWithSession<HostedSyncResult>('plugin:install|hosted_sync', { instanceId })
}