bc/server_modules/gameManager.js
2025-05-09 12:11:07 +00:00

156 lines
7.7 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_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;