156 lines
7.7 KiB
JavaScript
156 lines
7.7 KiB
JavaScript
// /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; |