67 lines
2.2 KiB
TypeScript
Executable File
67 lines
2.2 KiB
TypeScript
Executable File
"use client";
|
||
// 回响星核 / Echo Nexus — 主循环 hook
|
||
import { useEffect, useRef } from "react";
|
||
import { useGameStore } from "@/store/gameStore";
|
||
|
||
/** 250ms tick:产能 + 自动产晶体 + 自动解码 + 星潮 + 成就检测 */
|
||
export function useGameLoop() {
|
||
const tick = useGameStore((s) => s.tick);
|
||
const autoDecodeTick = useGameStore((s) => s.autoDecodeTick);
|
||
const tickTide = useGameStore((s) => s.tickTide);
|
||
const checkAchievements = useGameStore((s) => s.checkAchievements);
|
||
const init = useGameStore((s) => s.init);
|
||
const inited = useRef(false);
|
||
|
||
useEffect(() => {
|
||
// 等待 persist rehydrate 完成后再 init(v0.5.2 修复离线收益不弹窗 BUG)
|
||
// Zustand persist 即使用同步 localStorage,rehydrate 也是异步(Promise.resolve 包装)
|
||
const doInit = () => {
|
||
if (!inited.current) {
|
||
init();
|
||
inited.current = true;
|
||
}
|
||
};
|
||
let unsub: (() => void) | null = null;
|
||
let fallback: ReturnType<typeof setTimeout> | null = null;
|
||
if (useGameStore.persist.hasHydrated()) {
|
||
doInit();
|
||
} else {
|
||
unsub = useGameStore.persist.onFinishHydration(doInit);
|
||
// 兜底:500ms 后强制 init(防止 hydrated 状态异常)
|
||
fallback = setTimeout(doInit, 500);
|
||
}
|
||
let counter = 0;
|
||
const id = setInterval(() => {
|
||
const now = Date.now();
|
||
tick(now);
|
||
autoDecodeTick();
|
||
// 星潮检测每 tick 都查(结束/触发判定需及时)
|
||
tickTide(now);
|
||
counter++;
|
||
// 成就检测每秒执行一次(降低开销)
|
||
if (counter % 4 === 0) {
|
||
checkAchievements();
|
||
}
|
||
}, 250);
|
||
return () => {
|
||
clearInterval(id);
|
||
if (unsub) unsub();
|
||
if (fallback) clearTimeout(fallback);
|
||
};
|
||
}, [tick, autoDecodeTick, tickTide, checkAchievements, init]);
|
||
|
||
// 页面可见性:切回时补 tick + 星潮 + 成就
|
||
useEffect(() => {
|
||
const onVis = () => {
|
||
if (document.visibilityState === "visible") {
|
||
tick(Date.now());
|
||
tickTide(Date.now());
|
||
checkAchievements();
|
||
}
|
||
};
|
||
document.addEventListener("visibilitychange", onVis);
|
||
return () => document.removeEventListener("visibilitychange", onVis);
|
||
}, [tick, tickTide, checkAchievements]);
|
||
}
|
||
|