Private
Public Access
1
0

chg: usr: add timers to each player - renew the whole migration #4

This commit is contained in:
2026-04-11 15:19:59 +02:00
parent 5b55a6ce73
commit d388e25192
10 changed files with 391 additions and 107 deletions

View File

@@ -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';

View File

@@ -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);
};

View 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;