bc/server/game/instance/TurnTimer.js

120 lines
6.5 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// /server/game/instance/TurnTimer.js
class TurnTimer {
/**
* Конструктор таймера хода.
* @param {number} turnDurationMs - Длительность хода в миллисекундах.
* @param {number} updateIntervalMs - Интервал для отправки обновлений времени клиентам (в мс).
* @param {function} onTimeoutCallback - Колбэк, вызываемый при истечении времени хода.
* @param {function} onTickCallback - Колбэк, вызываемый на каждом тике обновления (передает remainingTime, isPlayerTurnForTimer).
*/
constructor(turnDurationMs, updateIntervalMs, onTimeoutCallback, onTickCallback) {
this.turnDurationMs = turnDurationMs;
this.updateIntervalMs = updateIntervalMs;
this.onTimeoutCallback = onTimeoutCallback;
this.onTickCallback = onTickCallback;
this.timerId = null; // ID для setTimeout (обработка таймаута)
this.updateIntervalId = null; // ID для setInterval (обновление клиента)
this.startTime = 0; // Время начала текущего отсчета (Date.now())
this.isRunning = false;
this.isCurrentPlayerActualTurnForTick = false; // Храним, для чьего хода запущен таймер (для onTickCallback)
this.isAiCurrentlyMakingMove = false; // Флаг, что сейчас ход AI (таймер не тикает для игрока)
// console.log(`[TurnTimer] Initialized with duration: ${turnDurationMs}ms, update interval: ${updateIntervalMs}ms`);
}
/**
* Запускает или перезапускает таймер хода.
* @param {boolean} isPlayerTurn - true, если сейчас ход слота 'player', false - если ход слота 'opponent'.
* @param {boolean} isAiTurn - true, если текущий ход делает AI (в этом случае таймер для реального игрока не тикает).
*/
start(isPlayerTurn, isAiTurn = false) {
this.clear(); // Сначала очищаем предыдущие таймеры
this.isCurrentPlayerActualTurnForTick = isPlayerTurn; // Сохраняем, чей ход для onTick
this.isAiCurrentlyMakingMove = isAiTurn;
// Таймер и отсчет времени запускаются только если это НЕ ход AI
if (this.isAiCurrentlyMakingMove) {
this.isRunning = false;
// console.log(`[TurnTimer] Start called, but it's AI's turn. Timer not started for player.`);
// Уведомляем один раз, что таймер неактивен (ход AI)
if (this.onTickCallback) {
this.onTickCallback(null, this.isCurrentPlayerActualTurnForTick);
}
return;
}
this.startTime = Date.now();
this.isRunning = true;
// console.log(`[TurnTimer] Started for ${isPlayerTurn ? 'Player' : 'Opponent'} at ${new Date(this.startTime).toLocaleTimeString()}. AI turn: ${isAiTurn}`);
// Таймер на истечение общего времени хода
this.timerId = setTimeout(() => {
// console.log(`[TurnTimer] Timeout occurred! Was running: ${this.isRunning}`);
if (this.isRunning) { // Дополнительная проверка, что таймер все еще должен был работать
this.isRunning = false; // Помечаем, что таймер больше не работает
if (this.onTimeoutCallback) {
this.onTimeoutCallback();
}
this.clear(); // Очищаем и интервал обновления после таймаута
}
}, this.turnDurationMs);
// Интервал для отправки обновлений клиентам
this.updateIntervalId = setInterval(() => {
if (!this.isRunning) { // Если таймер был остановлен (например, ход сделан или игра окончена)
this.clear(); // Убедимся, что интервал тоже очищен
return;
}
const elapsedTime = Date.now() - this.startTime;
const remainingTime = Math.max(0, this.turnDurationMs - elapsedTime);
if (this.onTickCallback) {
// Передаем isCurrentPlayerActualTurnForTick, чтобы клиент знал, для чьего хода это время
this.onTickCallback(remainingTime, this.isCurrentPlayerActualTurnForTick);
}
if (remainingTime <= 0 && this.isRunning) { // Если время вышло по интервалу (на всякий случай, setTimeout должен сработать)
// console.log(`[TurnTimer] Remaining time reached 0 in interval. Forcing timeout logic.`);
// Не вызываем onTimeoutCallback здесь напрямую, чтобы избежать двойного вызова,
// setTimeout должен это обработать. Просто очищаем интервал.
this.clear(); // Очищаем интервал, setTimeout сработает для onTimeoutCallback
}
}, this.updateIntervalMs);
// Отправляем начальное значение немедленно
if (this.onTickCallback) {
this.onTickCallback(this.turnDurationMs, this.isCurrentPlayerActualTurnForTick);
}
}
/**
* Очищает (останавливает) все активные таймеры (setTimeout и setInterval).
*/
clear() {
if (this.timerId) {
clearTimeout(this.timerId);
this.timerId = null;
}
if (this.updateIntervalId) {
clearInterval(this.updateIntervalId);
this.updateIntervalId = null;
}
this.isRunning = false;
this.startTime = 0;
// console.log(`[TurnTimer] Cleared.`);
}
/**
* Проверяет, активен ли таймер в данный момент.
* @returns {boolean}
*/
isActive() {
return this.isRunning;
}
}
module.exports = TurnTimer;