diff options
| author | 2026-03-26 09:06:56 +0800 | |
|---|---|---|
| committer | 2026-03-26 09:06:56 +0800 | |
| commit | 2412f7a3a626fc3b9e7b59ce1fc900468b792972 (patch) | |
| tree | 68b2ad2b56daa1ad040a4a0df0f7db509e16d53c /packages/ui/src/stores/game-store.ts | |
| parent | 788715b1ca5ab5b67fcc2e69650b74e14c953a57 (diff) | |
| parent | 94b0d8e208363c802c12b56d8bdbef574dd1fb91 (diff) | |
| download | DropOut-2412f7a3a626fc3b9e7b59ce1fc900468b792972.tar.gz DropOut-2412f7a3a626fc3b9e7b59ce1fc900468b792972.zip | |
Merge branch 'main' into refactor/fe
Diffstat (limited to 'packages/ui/src/stores/game-store.ts')
| -rw-r--r-- | packages/ui/src/stores/game-store.ts | 147 |
1 files changed, 114 insertions, 33 deletions
diff --git a/packages/ui/src/stores/game-store.ts b/packages/ui/src/stores/game-store.ts index fa0f9f8..1eaf7e7 100644 --- a/packages/ui/src/stores/game-store.ts +++ b/packages/ui/src/stores/game-store.ts @@ -1,49 +1,92 @@ +import { listen, type UnlistenFn } from "@tauri-apps/api/event"; import { toast } from "sonner"; import { create } from "zustand"; -import { getVersions } from "@/client"; +import { + getVersions, + getVersionsOfInstance, + startGame as startGameCommand, + stopGame as stopGameCommand, +} from "@/client"; +import type { Account } from "@/types/bindings/auth"; +import type { GameExitedEvent } from "@/types/bindings/core"; import type { Version } from "@/types/bindings/manifest"; interface GameState { - // State versions: Version[]; selectedVersion: string; + runningInstanceId: string | null; + runningVersionId: string | null; + launchingInstanceId: string | null; + stoppingInstanceId: string | null; + lifecycleUnlisten: UnlistenFn | null; - // Computed property latestRelease: Version | undefined; + isGameRunning: boolean; - // Actions + initLifecycle: () => Promise<void>; loadVersions: (instanceId?: string) => Promise<void>; startGame: ( - currentAccount: any, + currentAccount: Account | null, openLoginModal: () => void, activeInstanceId: string | null, - setView: (view: any) => void, - ) => Promise<void>; + versionId: string | null, + setView: (view: string) => void, + ) => Promise<string | null>; + stopGame: (instanceId?: string | null) => Promise<string | null>; setSelectedVersion: (version: string) => void; setVersions: (versions: Version[]) => void; } export const useGameStore = create<GameState>((set, get) => ({ - // Initial state versions: [], selectedVersion: "", + runningInstanceId: null, + runningVersionId: null, + launchingInstanceId: null, + stoppingInstanceId: null, + lifecycleUnlisten: null, - // Computed property get latestRelease() { return get().versions.find((v) => v.type === "release"); }, - // Actions + get isGameRunning() { + return get().runningInstanceId !== null; + }, + + initLifecycle: async () => { + if (get().lifecycleUnlisten) { + return; + } + + const unlisten = await listen<GameExitedEvent>("game-exited", (event) => { + const { instanceId, versionId, wasStopped } = event.payload; + + set({ + runningInstanceId: null, + runningVersionId: null, + launchingInstanceId: null, + stoppingInstanceId: null, + }); + + if (wasStopped) { + toast.success(`Stopped Minecraft ${versionId} for instance ${instanceId}`); + } else { + toast.info(`Minecraft ${versionId} exited for instance ${instanceId}`); + } + }); + + set({ lifecycleUnlisten: unlisten }); + }, + loadVersions: async (instanceId?: string) => { - console.log("Loading versions for instance:", instanceId); try { - // Ask the backend for known versions (optionally scoped to an instance). - // The Tauri command `get_versions` is expected to return an array of `Version`. - const versions = await getVersions(); + const versions = instanceId + ? await getVersionsOfInstance(instanceId) + : await getVersions(); set({ versions: versions ?? [] }); } catch (e) { console.error("Failed to load versions:", e); - // Keep the store consistent on error by clearing versions. set({ versions: [] }); } }, @@ -52,42 +95,80 @@ export const useGameStore = create<GameState>((set, get) => ({ currentAccount, openLoginModal, activeInstanceId, + versionId, setView, ) => { - const { selectedVersion } = get(); + const { isGameRunning } = get(); + const targetVersion = versionId ?? get().selectedVersion; if (!currentAccount) { - alert("Please login first!"); + toast.info("Please login first"); openLoginModal(); - return; + return null; } - if (!selectedVersion) { - alert("Please select a version!"); - return; + if (!targetVersion) { + toast.info("Please select a version first"); + return null; } if (!activeInstanceId) { - alert("Please select an instance first!"); + toast.info("Please select an instance first"); setView("instances"); - return; + return null; } - toast.info("Preparing to launch " + selectedVersion + "..."); + if (isGameRunning) { + toast.info("A game is already running"); + return null; + } + + set({ + launchingInstanceId: activeInstanceId, + selectedVersion: targetVersion, + }); + toast.info(`Preparing to launch ${targetVersion}...`); try { - // Note: In production, this would call Tauri invoke - // const msg = await invoke<string>("start_game", { - // instanceId: activeInstanceId, - // versionId: selectedVersion, - // }); - - // Simulate success - await new Promise((resolve) => setTimeout(resolve, 1000)); - toast.success("Game started successfully!"); + const message = await startGameCommand(activeInstanceId, targetVersion); + set({ + launchingInstanceId: null, + runningInstanceId: activeInstanceId, + runningVersionId: targetVersion, + }); + toast.success(message); + return message; } catch (e) { console.error(e); + set({ launchingInstanceId: null }); toast.error(`Error: ${e}`); + return null; + } + }, + + stopGame: async (instanceId) => { + const { runningInstanceId } = get(); + + if (!runningInstanceId) { + toast.info("No running game found"); + return null; + } + + if (instanceId && instanceId !== runningInstanceId) { + toast.info("That instance is not the one currently running"); + return null; + } + + set({ stoppingInstanceId: runningInstanceId }); + + try { + return await stopGameCommand(); + } catch (e) { + console.error("Failed to stop game:", e); + toast.error(`Failed to stop game: ${e}`); + return null; + } finally { + set({ stoppingInstanceId: null }); } }, |