chg: usr: add timers to each player - renew the whole migration #4
This commit is contained in:
@@ -10,4 +10,5 @@
|
||||
export { default as useGameRefs } from './useGameRefs';
|
||||
export { default as useGameState } from './useGameState';
|
||||
export { default as useServerCommunication } from './useServerCommunication';
|
||||
export { default as useStepTimer } from './useStepTimer';
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ import React, { useEffect, useRef } from 'react';
|
||||
import { useMutation, useQuery } from '@tanstack/react-query';
|
||||
import { useGame } from '@mine-contexts';
|
||||
import { DESC } from '@mine-utils';
|
||||
import useStepTimer from './useStepTimer';
|
||||
|
||||
/** Handles all server communication: SSE (Mercure), REST calls, and the initialization lifecycle. */
|
||||
const useServerCommunication = (gameAssoc, gameInherited, isEnvDev) => {
|
||||
@@ -32,6 +33,9 @@ const useServerCommunication = (gameAssoc, gameInherited, isEnvDev) => {
|
||||
const eventSourceRef = useRef(null);
|
||||
const rpcUsersRef = useRef(null);
|
||||
const stepCacheRef = useRef([]);
|
||||
const { getStepElapsed, resetStepTimer, startNewTurn } = useStepTimer();
|
||||
const isGameRunningRef = useRef(false);
|
||||
const lastActivePlayerRef = useRef(null);
|
||||
|
||||
/** REST mutations / queries */
|
||||
|
||||
@@ -80,7 +84,6 @@ const useServerCommunication = (gameAssoc, gameInherited, isEnvDev) => {
|
||||
<a href={`/play/${gameAssoc}`} target="_blank">Play w/ me!</a>
|
||||
</div>
|
||||
) : '');
|
||||
|
||||
setTimeout(() => revealedCells.forEach(cell => applyRevealedCell(cell, cell.player)), 0);
|
||||
};
|
||||
|
||||
@@ -96,6 +99,10 @@ const useServerCommunication = (gameAssoc, gameInherited, isEnvDev) => {
|
||||
desc: 'blue' === webPlayerRef.current ? DESC.you : DESC.buddy,
|
||||
active: true,
|
||||
}));
|
||||
isGameRunningRef.current = true;
|
||||
lastActivePlayerRef.current = 1; // Blue starts
|
||||
startNewTurn();
|
||||
resetStepTimer();
|
||||
hideOverlay();
|
||||
};
|
||||
|
||||
@@ -130,6 +137,14 @@ const useServerCommunication = (gameAssoc, gameInherited, isEnvDev) => {
|
||||
if (null === payload.data.resign) {
|
||||
isEnvDev && console.warn(payload.user + ' stepped to ' + payload.data.coords.join(','));
|
||||
syncBombSelected(payload.data.bomb);
|
||||
|
||||
// Detect if turn switched (other player made a move)
|
||||
// After their move, it's now our turn (or the opposite player's turn)
|
||||
if (lastActivePlayerRef.current !== activePlayerRef.current) {
|
||||
startNewTurn();
|
||||
lastActivePlayerRef.current = activePlayerRef.current;
|
||||
}
|
||||
|
||||
applyStep(payload.data);
|
||||
makeGameEndIfItEnds(payload.data.bluePoints, payload.data.redPoints, false, payload.data.leftMines);
|
||||
} else {
|
||||
@@ -233,7 +248,8 @@ const useServerCommunication = (gameAssoc, gameInherited, isEnvDev) => {
|
||||
const [r, c] = coords;
|
||||
if (cells[r]?.[c]?.active) return;
|
||||
|
||||
const dataPack = { coords, player: activeColor, bomb: bombSelectedRef.current, resign: null };
|
||||
const stepElapsed = getStepElapsed(activePlayerRef.current, isGameRunningRef.current);
|
||||
const dataPack = { coords, player: activeColor, bomb: bombSelectedRef.current, resign: null, stepElapsed };
|
||||
|
||||
if (connectionLostRef.current) {
|
||||
stepCacheRef.current.push(dataPack);
|
||||
@@ -251,7 +267,8 @@ const useServerCommunication = (gameAssoc, gameInherited, isEnvDev) => {
|
||||
|
||||
const clickResign = () => {
|
||||
const color = activePlayerRef.current ? 'blue' : 'red';
|
||||
stepMutation.mutate({ resign: color });
|
||||
const stepElapsed = getStepElapsed(activePlayerRef.current, isGameRunningRef.current);
|
||||
stepMutation.mutate({ resign: color, stepElapsed });
|
||||
resignProcess(webPlayerRef.current);
|
||||
};
|
||||
|
||||
|
||||
53
assets/js/mine-seeker/hooks/useStepTimer.jsx
Normal file
53
assets/js/mine-seeker/hooks/useStepTimer.jsx
Normal file
@@ -0,0 +1,53 @@
|
||||
/**
|
||||
* This file is part of the SplendidBear Websites' projects.
|
||||
*
|
||||
* Copyright (c) 2026 @ www.splendidbear.org
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
import { useRef } from 'react';
|
||||
|
||||
const useStepTimer = () => {
|
||||
// Record when the current turn started (timestamp)
|
||||
const turnStartTimeRef = useRef(null);
|
||||
// Flag to track if we've already recorded a turn start
|
||||
const turnStartedRef = useRef(false);
|
||||
|
||||
const getStepElapsed = (currentActivePlayer, isGameRunning) => {
|
||||
// If game not running, return 0
|
||||
if (!isGameRunning) return 0;
|
||||
|
||||
// Only initialize the turn timer ONCE per call to getStepElapsed
|
||||
// This prevents resetting on multiple calls
|
||||
if (!turnStartedRef.current) {
|
||||
turnStartTimeRef.current = Date.now();
|
||||
turnStartedRef.current = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// After initialization, just calculate elapsed time
|
||||
if (turnStartTimeRef.current) {
|
||||
return Math.floor((Date.now() - turnStartTimeRef.current) / 1000);
|
||||
}
|
||||
|
||||
return 0;
|
||||
};
|
||||
|
||||
const resetStepTimer = () => {
|
||||
turnStartTimeRef.current = null;
|
||||
turnStartedRef.current = false;
|
||||
};
|
||||
|
||||
// Call this when we know a turn has actually changed (from server response)
|
||||
const startNewTurn = () => {
|
||||
turnStartTimeRef.current = Date.now();
|
||||
turnStartedRef.current = true;
|
||||
};
|
||||
|
||||
return { getStepElapsed, resetStepTimer, startNewTurn };
|
||||
};
|
||||
|
||||
export default useStepTimer;
|
||||
|
||||
Reference in New Issue
Block a user