// /server_modules/gameManager.js const { v4: uuidv4 } = require('uuid'); const GameInstance = require('./gameInstance'); class GameManager { constructor(io) { this.io = io; this.games = {}; this.socketToGame = {}; this.pendingPvPGames = []; } createGame(socket, mode = 'ai') { const gameId = uuidv4(); const game = new GameInstance(gameId, this.io, mode); this.games[gameId] = game; this.socketToGame[socket.id] = gameId; if (game.addPlayer(socket)) { console.log(`[GameManager] Игра создана: ${gameId} (режим: ${mode}) игроком ${socket.id}`); socket.emit('gameCreated', { gameId: gameId, mode: mode, yourPlayerId: game.players[socket.id].id }); if (mode === 'pvp') { this.pendingPvPGames.push(gameId); this.broadcastAvailablePvPGames(); } } else { delete this.games[gameId]; delete this.socketToGame[socket.id]; socket.emit('gameError', { message: 'Не удалось создать игру или добавить игрока.' }); } } joinGame(socket, gameId) { const game = this.games[gameId]; if (game) { if (game.mode === 'pvp' && game.playerCount < 2) { if (game.addPlayer(socket)) { this.socketToGame[socket.id] = gameId; console.log(`[GameManager] Игрок ${socket.id} присоединился к PvP игре ${gameId} как ${game.players[socket.id].id}`); this.pendingPvPGames = this.pendingPvPGames.filter(id => id !== gameId); this.broadcastAvailablePvPGames(); } else { socket.emit('gameError', { message: 'Не удалось присоединиться к игре (возможно, она уже заполнена или произошла ошибка).' }); } } else if (game.mode !== 'pvp') { socket.emit('gameError', { message: 'Эта игра не является PvP игрой.' }); } else { socket.emit('gameError', { message: 'Эта PvP игра уже заполнена.' }); } } else { socket.emit('gameError', { message: 'Игра с таким ID не найдена.' }); } } handlePlayerAction(socketId, actionData) { const gameIdFromSocket = this.socketToGame[socketId]; const game = this.games[gameIdFromSocket]; if (game) { // ИЗМЕНЕНО: Проверка на actionData.gameId теперь не так важна, если мы не требуем его от клиента для playerAction // Если actionData.gameId существует И не совпадает, тогда выводим предупреждение. // Если actionData.gameId отсутствует, предупреждения не будет. if (actionData && actionData.gameId && actionData.gameId !== gameIdFromSocket) { console.warn(`[GameManager] Несоответствие gameId в actionData (${actionData.gameId}) и gameId сокета (${gameIdFromSocket}) для ${socketId}. Игнорируем gameId из actionData.`); } game.processPlayerAction(socketId, actionData); } else { console.error(`[GameManager] Игра не найдена для действия от сокета ${socketId}. Данные действия:`, actionData); const playerSocket = this.io.sockets.sockets.get(socketId); if (playerSocket) { playerSocket.emit('gameError', { message: 'Внутренняя ошибка сервера: игровая сессия потеряна.' }); } } } requestRestart(socketId, gameId) { const game = this.games[gameId]; if (game && game.players[socketId]) { game.handleVoteRestart(socketId); } else { console.warn(`[GameManager] Запрос на рестарт для игры ${gameId} от сокета ${socketId} отклонен: игра или игрок не найдены в сессии.`); const playerSocket = this.io.sockets.sockets.get(socketId); if (playerSocket) { playerSocket.emit('gameError', { message: 'Не удалось перезапустить: игровая сессия не найдена или вы не в ней.' }); } } } handleDisconnect(socketId) { const gameId = this.socketToGame[socketId]; if (gameId && this.games[gameId]) { console.log(`[GameManager] Игрок ${socketId} отключился от игры ${gameId}.`); const game = this.games[gameId]; game.removePlayer(socketId); if (game.playerCount === 0) { console.log(`[GameManager] Игра ${gameId} пуста и будет удалена.`); delete this.games[gameId]; this.pendingPvPGames = this.pendingPvPGames.filter(id => id !== gameId); this.broadcastAvailablePvPGames(); } else if (game.mode === 'pvp' && game.playerCount === 1 && (!game.gameState || !game.gameState.isGameOver)) { if (!this.pendingPvPGames.includes(gameId)) { this.pendingPvPGames.push(gameId); } this.broadcastAvailablePvPGames(); } } delete this.socketToGame[socketId]; } findAndJoinRandomPvPGame(socket) { if (this.pendingPvPGames.length > 0) { const gameIdToJoin = this.pendingPvPGames[0]; this.joinGame(socket, gameIdToJoin); } else { this.createGame(socket, 'pvp'); socket.emit('noPendingGamesFound', { message: 'Свободных PvP игр не найдено. Создана новая игра для вас. Ожидайте противника.' }); } } getAvailablePvPGamesListForClient() { const availableGamesInfo = this.pendingPvPGames.map(gameId => { const game = this.games[gameId]; if (game && game.mode === 'pvp' && game.playerCount === 1 && (!game.gameState || !game.gameState.isGameOver)) { const firstPlayerSocketId = Object.keys(game.players)[0]; const firstPlayerInfo = game.players[firstPlayerSocketId]; const firstPlayerName = firstPlayerInfo ? firstPlayerInfo.characterName : 'Игрок 1'; return { id: gameId, status: `Ожидает 1 игрока (Создал: ${firstPlayerName})` }; } return null; }).filter(info => info !== null); return availableGamesInfo; } broadcastAvailablePvPGames() { const availableGamesInfo = this.getAvailablePvPGamesListForClient(); this.io.emit('availablePvPGamesList', availableGamesInfo); console.log("[GameManager] Broadcasted available PvP games:", availableGamesInfo); } getActiveGamesList() { return Object.values(this.games).map(game => ({ id: game.id, mode: game.mode, playerCount: game.playerCount, isGameOver: game.gameState ? game.gameState.isGameOver : 'N/A', players: Object.values(game.players).map(p => ({id: p.id, character: p.characterName, socketId: p.socket.id })) })); } } module.exports = GameManager;