aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/packages/ui/src/stores/game-store.ts
diff options
context:
space:
mode:
author苏向夜 <fu050409@163.com>2026-03-26 09:06:56 +0800
committer苏向夜 <fu050409@163.com>2026-03-26 09:06:56 +0800
commit2412f7a3a626fc3b9e7b59ce1fc900468b792972 (patch)
tree68b2ad2b56daa1ad040a4a0df0f7db509e16d53c /packages/ui/src/stores/game-store.ts
parent788715b1ca5ab5b67fcc2e69650b74e14c953a57 (diff)
parent94b0d8e208363c802c12b56d8bdbef574dd1fb91 (diff)
downloadDropOut-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.ts147
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 });
}
},