[fix] update data lost between page and stutter caused by periodly checking game stats in sync mode
This commit is contained in:
2
next-env.d.ts
vendored
2
next-env.d.ts
vendored
@@ -1,6 +1,6 @@
|
||||
/// <reference types="next" />
|
||||
/// <reference types="next/image-types/global" />
|
||||
import "./.next/types/routes.d.ts";
|
||||
import "./.next/dev/types/routes.d.ts";
|
||||
|
||||
// NOTE: This file should not be edited
|
||||
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
|
||||
|
||||
@@ -64,8 +64,8 @@ pub fn kill_game() -> Result<String, String> {
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn check_process_running(process_name: &str) -> Result<bool, String> {
|
||||
Ok(common::check_process_running(process_name))
|
||||
pub async fn check_process_running(process_name: &str) -> Result<bool, String> {
|
||||
Ok(common::check_process_running_async(process_name).await)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
|
||||
@@ -120,6 +120,41 @@ pub fn check_process_running(name: &str) -> bool {
|
||||
}
|
||||
}
|
||||
|
||||
// 异步版本的进程检测函数
|
||||
pub async fn check_process_running_async(name: &str) -> bool {
|
||||
use tokio::process::Command;
|
||||
|
||||
// 使用tasklist命令检查进程是否存在
|
||||
#[cfg(windows)]
|
||||
{
|
||||
let mut cmd = Command::new("tasklist");
|
||||
cmd.args(&["/FI", &format!("IMAGENAME eq {}", name)]);
|
||||
cmd.creation_flags(CREATE_NO_WINDOW);
|
||||
|
||||
match cmd.output().await {
|
||||
Ok(output) => {
|
||||
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||
// 检查输出中是否包含进程名(排除表头)
|
||||
stdout.contains(name) && stdout.contains("exe")
|
||||
}
|
||||
Err(_) => false,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(windows))]
|
||||
{
|
||||
// 对于非Windows系统,可以使用pgrep命令
|
||||
let mut cmd = Command::new("pgrep");
|
||||
cmd.arg("-f");
|
||||
cmd.arg(name);
|
||||
|
||||
match cmd.output().await {
|
||||
Ok(output) => !output.stdout.is_empty(),
|
||||
Err(_) => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mod tests {
|
||||
#[test]
|
||||
fn test_open_path() {
|
||||
|
||||
@@ -52,7 +52,7 @@
|
||||
},
|
||||
"productName": "CS工具箱",
|
||||
"mainBinaryName": "cstb",
|
||||
"version": "0.0.6-beta.9",
|
||||
"version": "0.0.6-beta.8",
|
||||
"identifier": "upup.cool",
|
||||
"plugins": {
|
||||
"deep-link": {
|
||||
|
||||
@@ -1,32 +1,3 @@
|
||||
import { useState, useEffect, useCallback } from "react"
|
||||
import { invoke } from "@tauri-apps/api/core"
|
||||
|
||||
export function useGameMonitor() {
|
||||
const [isGameRunning, setIsGameRunning] = useState(false)
|
||||
|
||||
// 检测游戏是否运行
|
||||
const checkGameRunning = useCallback(async () => {
|
||||
try {
|
||||
const result = await invoke<boolean>("check_process_running", {
|
||||
processName: "cs2.exe",
|
||||
}).catch(() => false)
|
||||
setIsGameRunning(result)
|
||||
return result
|
||||
} catch {
|
||||
setIsGameRunning(false)
|
||||
return false
|
||||
}
|
||||
}, [])
|
||||
|
||||
// 定期检测游戏运行状态
|
||||
useEffect(() => {
|
||||
void checkGameRunning()
|
||||
const interval = setInterval(() => {
|
||||
void checkGameRunning()
|
||||
}, 3000)
|
||||
return () => clearInterval(interval)
|
||||
}, [checkGameRunning])
|
||||
|
||||
return { isGameRunning, checkGameRunning }
|
||||
}
|
||||
// 使用全局游戏状态监控,避免重复检测
|
||||
export { useGlobalGameMonitor as useGameMonitor } from "@/hooks/useGlobalGameMonitor"
|
||||
|
||||
|
||||
@@ -16,15 +16,9 @@ import { invoke } from "@tauri-apps/api/core"
|
||||
import { listen } from "@tauri-apps/api/event"
|
||||
import { relaunch } from "@tauri-apps/plugin-process"
|
||||
import { addToast } from "@heroui/react"
|
||||
import { useAppStore } from "@/store/app"
|
||||
import { useAppStore, type UpdateInfo } from "@/store/app"
|
||||
import { MarkdownRender } from "@/components/markdown"
|
||||
|
||||
interface UpdateInfo {
|
||||
version: string
|
||||
notes?: string
|
||||
download_url: string
|
||||
}
|
||||
|
||||
interface UpdateCheckerProps {
|
||||
customEndpoint?: string
|
||||
includePrerelease?: boolean
|
||||
@@ -38,10 +32,11 @@ export function UpdateChecker({
|
||||
}: UpdateCheckerProps) {
|
||||
const app = useAppStore()
|
||||
const [checking, setChecking] = useState(false)
|
||||
const [downloading, setDownloading] = useState(false)
|
||||
const [updateInfo, setUpdateInfo] = useState<UpdateInfo | null>(null)
|
||||
const [downloadProgress, setDownloadProgress] = useState(0)
|
||||
const [downloadCompleted, setDownloadCompleted] = useState(false)
|
||||
// 从 store 读取状态
|
||||
const updateInfo = app.state.updateInfo
|
||||
const downloading = app.state.downloading
|
||||
const downloadProgress = app.state.downloadProgress
|
||||
const downloadCompleted = app.state.downloadCompleted
|
||||
const {
|
||||
isOpen: isChangelogOpen,
|
||||
onOpen: onChangelogOpen,
|
||||
@@ -52,27 +47,27 @@ export function UpdateChecker({
|
||||
useEffect(() => {
|
||||
const unlisten = listen<number>("update-download-progress", (event) => {
|
||||
const progress = event.payload
|
||||
setDownloadProgress(progress)
|
||||
app.setDownloadProgress(progress)
|
||||
|
||||
// 如果进度达到 100%,标记下载完成
|
||||
if (progress === 100) {
|
||||
setDownloading(false)
|
||||
setDownloadCompleted(true)
|
||||
app.setDownloading(false)
|
||||
app.setDownloadCompleted(true)
|
||||
}
|
||||
})
|
||||
|
||||
return () => {
|
||||
unlisten.then((fn) => fn())
|
||||
}
|
||||
}, [])
|
||||
}, [app.setDownloadProgress, app.setDownloading, app.setDownloadCompleted])
|
||||
|
||||
// 检查更新
|
||||
const handleCheckUpdate = async () => {
|
||||
setChecking(true)
|
||||
setUpdateInfo(null)
|
||||
setDownloadProgress(0)
|
||||
setDownloading(false)
|
||||
setDownloadCompleted(false)
|
||||
app.setUpdateInfo(null)
|
||||
app.setDownloadProgress(0)
|
||||
app.setDownloading(false)
|
||||
app.setDownloadCompleted(false)
|
||||
|
||||
try {
|
||||
// 根据是否包含测试版来选择不同的 endpoint
|
||||
@@ -97,7 +92,7 @@ export function UpdateChecker({
|
||||
})
|
||||
|
||||
if (result) {
|
||||
setUpdateInfo(result)
|
||||
app.setUpdateInfo(result)
|
||||
// 更新 store 中的更新状态和最新版本号
|
||||
app.setHasUpdate(true)
|
||||
app.setLatestVersion(result.version)
|
||||
@@ -110,6 +105,7 @@ export function UpdateChecker({
|
||||
// 没有更新,更新 store 状态
|
||||
app.setHasUpdate(false)
|
||||
app.setLatestVersion("")
|
||||
app.setUpdateInfo(null)
|
||||
addToast({
|
||||
title: "已是最新版本",
|
||||
color: "default",
|
||||
@@ -132,18 +128,18 @@ export function UpdateChecker({
|
||||
return
|
||||
}
|
||||
|
||||
setDownloading(true)
|
||||
setDownloadProgress(0)
|
||||
setDownloadCompleted(false)
|
||||
app.setDownloading(true)
|
||||
app.setDownloadProgress(0)
|
||||
app.setDownloadCompleted(false)
|
||||
|
||||
try {
|
||||
// 使用官方 updater 插件,传递 useCdn 参数
|
||||
await invoke("download_app_update", { useCdn: useCdn })
|
||||
|
||||
// 下载完成,标记状态
|
||||
setDownloadProgress(100)
|
||||
setDownloading(false)
|
||||
setDownloadCompleted(true)
|
||||
app.setDownloadProgress(100)
|
||||
app.setDownloading(false)
|
||||
app.setDownloadCompleted(true)
|
||||
|
||||
addToast({
|
||||
title: "下载完成",
|
||||
@@ -164,9 +160,9 @@ export function UpdateChecker({
|
||||
color: "danger",
|
||||
})
|
||||
}
|
||||
setDownloadProgress(0)
|
||||
setDownloading(false)
|
||||
setDownloadCompleted(false)
|
||||
app.setDownloadProgress(0)
|
||||
app.setDownloading(false)
|
||||
app.setDownloadCompleted(false)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -174,9 +170,9 @@ export function UpdateChecker({
|
||||
const handleCancelDownload = async () => {
|
||||
try {
|
||||
await invoke("cancel_download_update")
|
||||
setDownloading(false)
|
||||
setDownloadProgress(0)
|
||||
setDownloadCompleted(false)
|
||||
app.setDownloading(false)
|
||||
app.setDownloadProgress(0)
|
||||
app.setDownloadCompleted(false)
|
||||
addToast({
|
||||
title: "已取消下载",
|
||||
color: "default",
|
||||
|
||||
@@ -9,35 +9,21 @@ import { useSteamStore } from "@/store/steam"
|
||||
import { useDebounce, useDebounceFn, useThrottleFn } from "ahooks"
|
||||
import { invoke } from "@tauri-apps/api/core"
|
||||
import { listen } from "@tauri-apps/api/event"
|
||||
import { useGlobalGameMonitor } from "@/hooks/useGlobalGameMonitor"
|
||||
|
||||
const VideoSetting = () => {
|
||||
const [hide, setHide] = useState(false)
|
||||
const [edit, setEdit] = useState(false)
|
||||
const [isGameRunning, setIsGameRunning] = useState(false)
|
||||
const tool = useToolStore()
|
||||
const steam = useSteamStore()
|
||||
// 使用全局游戏状态监控,避免重复检测
|
||||
const { isGameRunning, checkGameRunning } = useGlobalGameMonitor()
|
||||
// 使用 ref 存储 edit 的最新值,供 throttle 回调使用
|
||||
const editRef = useRef(edit)
|
||||
useEffect(() => {
|
||||
editRef.current = edit
|
||||
}, [edit])
|
||||
|
||||
// 检测游戏是否运行
|
||||
const checkGameRunning = useCallback(async () => {
|
||||
try {
|
||||
// 尝试检测cs2.exe进程
|
||||
const result = await invoke<boolean>("check_process_running", {
|
||||
processName: "cs2.exe",
|
||||
}).catch(() => false)
|
||||
setIsGameRunning(result)
|
||||
return result
|
||||
} catch {
|
||||
// 如果命令不存在,使用简单的检测方法
|
||||
setIsGameRunning(false)
|
||||
return false
|
||||
}
|
||||
}, [])
|
||||
|
||||
// 防抖的读取函数
|
||||
const { run: debouncedGetVideoConfig } = useDebounceFn(
|
||||
async () => {
|
||||
@@ -286,16 +272,6 @@ const VideoSetting = () => {
|
||||
|
||||
const [vconfig, setVconfig] = useState<VideoConfig>(tool.state.videoSetting)
|
||||
|
||||
// 初始化时检测游戏运行状态
|
||||
useEffect(() => {
|
||||
void checkGameRunning()
|
||||
// 定期检测游戏运行状态
|
||||
const interval = setInterval(() => {
|
||||
void checkGameRunning()
|
||||
}, 4000)
|
||||
return () => clearInterval(interval)
|
||||
}, [checkGameRunning])
|
||||
|
||||
useEffect(() => {
|
||||
if (steam.state.steamDirValid && steam.currentUser())
|
||||
void tool.getVideoConfig(steam.state.steamDir, steam.currentUser()?.steam_id32 || 0)
|
||||
|
||||
65
src/hooks/useGlobalGameMonitor.ts
Normal file
65
src/hooks/useGlobalGameMonitor.ts
Normal file
@@ -0,0 +1,65 @@
|
||||
import { useEffect, useRef } from "react"
|
||||
import { useToolStore } from "@/store/tool"
|
||||
|
||||
// 全局检测间隔(毫秒)- 增加到5秒以减少性能影响
|
||||
const CHECK_INTERVAL = 5000
|
||||
|
||||
// 全局检测状态管理
|
||||
let globalInterval: NodeJS.Timeout | null = null
|
||||
let subscriberCount = 0
|
||||
|
||||
/**
|
||||
* 全局游戏状态监控 Hook
|
||||
* 多个组件可以共享同一个检测循环,避免重复检测
|
||||
*
|
||||
* @param enabled 是否启用检测(默认 true)
|
||||
* @returns 游戏运行状态和手动检测函数
|
||||
*/
|
||||
export function useGlobalGameMonitor(enabled: boolean = true) {
|
||||
const tool = useToolStore()
|
||||
const isGameRunning = tool.state.isGameRunning
|
||||
const checkGameRunning = tool.checkGameRunning
|
||||
const enabledRef = useRef(enabled)
|
||||
|
||||
// 更新 enabled 引用
|
||||
useEffect(() => {
|
||||
enabledRef.current = enabled
|
||||
}, [enabled])
|
||||
|
||||
useEffect(() => {
|
||||
if (!enabled) {
|
||||
return
|
||||
}
|
||||
|
||||
// 增加订阅者计数
|
||||
subscriberCount++
|
||||
|
||||
// 如果这是第一个订阅者,启动全局检测循环
|
||||
if (subscriberCount === 1) {
|
||||
// 立即检测一次
|
||||
void checkGameRunning()
|
||||
|
||||
// 启动定期检测
|
||||
globalInterval = setInterval(() => {
|
||||
void checkGameRunning()
|
||||
}, CHECK_INTERVAL)
|
||||
}
|
||||
|
||||
// 清理函数:减少订阅者计数
|
||||
return () => {
|
||||
subscriberCount--
|
||||
|
||||
// 如果没有订阅者了,停止检测循环
|
||||
if (subscriberCount === 0 && globalInterval) {
|
||||
clearInterval(globalInterval)
|
||||
globalInterval = null
|
||||
}
|
||||
}
|
||||
}, [enabled, checkGameRunning])
|
||||
|
||||
return {
|
||||
isGameRunning,
|
||||
checkGameRunning,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,10 +4,20 @@ import { DEFAULT_STORE_CONFIG } from "./config"
|
||||
import { enable, disable } from "@tauri-apps/plugin-autostart"
|
||||
import { LazyStore } from '@tauri-apps/plugin-store';
|
||||
|
||||
interface UpdateInfo {
|
||||
version: string
|
||||
notes?: string
|
||||
download_url: string
|
||||
}
|
||||
|
||||
const defaultValue = {
|
||||
version: "0.0.1",
|
||||
hasUpdate: false,
|
||||
latestVersion: "", // 最新版本号
|
||||
updateInfo: null as UpdateInfo | null, // 更新信息
|
||||
downloading: false, // 是否正在下载
|
||||
downloadProgress: 0, // 下载进度 0-100
|
||||
downloadCompleted: false, // 下载是否完成
|
||||
inited: false,
|
||||
notice: "",
|
||||
useMirror: true, // 默认使用镜像源(CDN 加速)
|
||||
@@ -19,6 +29,8 @@ const defaultValue = {
|
||||
steamUsersViewMode: "list-large" as "card" | "list" | "list-large",
|
||||
}
|
||||
|
||||
export type { UpdateInfo }
|
||||
|
||||
export const appStore = store("app", { ...defaultValue }, DEFAULT_STORE_CONFIG)
|
||||
|
||||
export const useAppStore = () => {
|
||||
@@ -31,6 +43,10 @@ export const useAppStore = () => {
|
||||
setVersion,
|
||||
setHasUpdate,
|
||||
setLatestVersion,
|
||||
setUpdateInfo,
|
||||
setDownloading,
|
||||
setDownloadProgress,
|
||||
setDownloadCompleted,
|
||||
setInited,
|
||||
setNotice,
|
||||
setUseMirror,
|
||||
@@ -56,6 +72,18 @@ const setHasUpdate = (hasUpdate: boolean) => {
|
||||
const setLatestVersion = (latestVersion: string) => {
|
||||
appStore.state.latestVersion = latestVersion
|
||||
}
|
||||
const setUpdateInfo = (updateInfo: UpdateInfo | null) => {
|
||||
appStore.state.updateInfo = updateInfo
|
||||
}
|
||||
const setDownloading = (downloading: boolean) => {
|
||||
appStore.state.downloading = downloading
|
||||
}
|
||||
const setDownloadProgress = (downloadProgress: number) => {
|
||||
appStore.state.downloadProgress = downloadProgress
|
||||
}
|
||||
const setDownloadCompleted = (downloadCompleted: boolean) => {
|
||||
appStore.state.downloadCompleted = downloadCompleted
|
||||
}
|
||||
const setInited = (inited: boolean) => {
|
||||
appStore.state.inited = inited
|
||||
}
|
||||
@@ -100,6 +128,10 @@ const resetAppStore = () => {
|
||||
setVersion(defaultValue.version)
|
||||
setHasUpdate(defaultValue.hasUpdate)
|
||||
setLatestVersion(defaultValue.latestVersion)
|
||||
setUpdateInfo(defaultValue.updateInfo)
|
||||
setDownloading(defaultValue.downloading)
|
||||
setDownloadProgress(defaultValue.downloadProgress)
|
||||
setDownloadCompleted(defaultValue.downloadCompleted)
|
||||
setInited(defaultValue.inited)
|
||||
setNotice(defaultValue.notice)
|
||||
setUseMirror(defaultValue.useMirror)
|
||||
|
||||
@@ -124,6 +124,7 @@ const defaultValue = {
|
||||
launchIndex: 0,
|
||||
powerPlan: 0,
|
||||
autoCloseGame: true, // 帧数测试自动关闭游戏
|
||||
isGameRunning: false, // 游戏运行状态(全局共享)
|
||||
videoSetting: {
|
||||
version: "15",
|
||||
vendor_id: "0",
|
||||
@@ -191,6 +192,8 @@ export const useToolStore = () => {
|
||||
setVideoConfig,
|
||||
addLaunchOption,
|
||||
resetToolStore,
|
||||
setIsGameRunning,
|
||||
checkGameRunning,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -288,10 +291,28 @@ const addLaunchOption = (option: LaunchOption) => {
|
||||
sendCurrentLaunchOptionToTray(toolStore.state.launchIndex)
|
||||
}
|
||||
|
||||
const setIsGameRunning = (running: boolean) => {
|
||||
toolStore.state.isGameRunning = running
|
||||
}
|
||||
|
||||
const checkGameRunning = async (): Promise<boolean> => {
|
||||
try {
|
||||
const result = await invoke<boolean>("check_process_running", {
|
||||
processName: "cs2.exe",
|
||||
}).catch(() => false)
|
||||
setIsGameRunning(result)
|
||||
return result
|
||||
} catch {
|
||||
setIsGameRunning(false)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
const resetToolStore = () => {
|
||||
setLaunchOptions(defaultValue.launchOptions)
|
||||
setLaunchIndex(defaultValue.launchIndex)
|
||||
setPowerPlan(defaultValue.powerPlan)
|
||||
setAutoCloseGame(defaultValue.autoCloseGame)
|
||||
setVideoSetting(defaultValue.videoSetting)
|
||||
setIsGameRunning(defaultValue.isGameRunning)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user