// bc.js (или server.js - ваш основной файл сервера) const express = require('express'); const http = require('http'); const socketIo = require('socket.io'); const path = require('path'); //hello // Серверные модули const GameManager = require('./server_modules/gameManager'); const authController = require('./server_modules/auth'); // Ваш модуль аутентификации // const GAME_CONFIG = require('./server_modules/config'); // Не используется напрямую здесь, но может быть полезен для отладки const hostname = 'localhost'; // или '0.0.0.0' для доступа извне const app = express(); const server = http.createServer(app); const io = socketIo(server, { cors: { origin: "*", // Разрешить все источники для простоты разработки. В продакшене укажите конкретный домен клиента. methods: ["GET", "POST"] } }); const PORT = process.env.PORT || 3200; // Статическое обслуживание файлов из папки 'public' app.use(express.static(path.join(__dirname, 'public'))); // Создание экземпляра GameManager const gameManager = new GameManager(io); io.on('connection', (socket) => { console.log(`[Server BC.JS] New client connected: ${socket.id}`); // При подключении нового клиента, отправляем ему текущий список доступных PvP игр const availableGames = gameManager.getAvailablePvPGamesListForClient(); socket.emit('availablePvPGamesList', availableGames); // Обработчик запроса на обновление списка PvP игр socket.on('requestPvPGameList', () => { const currentAvailableGames = gameManager.getAvailablePvPGamesListForClient(); socket.emit('availablePvPGamesList', currentAvailableGames); }); // --- Аутентификация --- socket.on('register', async (data) => { console.log(`[Server BC.JS] Received 'register' event from ${socket.id} with username: ${data?.username}`); if (!data || typeof data.username !== 'string' || typeof data.password !== 'string') { socket.emit('registerResponse', { success: false, message: 'Некорректные данные запроса для регистрации.' }); return; } const result = await authController.registerUser(data.username, data.password); socket.emit('registerResponse', result); }); socket.on('login', async (data) => { console.log(`[Server BC.JS] Received 'login' event from ${socket.id} with username: ${data?.username}`); if (!data || typeof data.username !== 'string' || typeof data.password !== 'string') { socket.emit('loginResponse', { success: false, message: 'Некорректные данные запроса для входа.' }); return; } const result = await authController.loginUser(data.username, data.password); if (result.success) { // Сохраняем данные пользователя в объекте сокета для последующего использования socket.userData = { userId: result.userId, username: result.username }; console.log(`[Server BC.JS] User ${result.username} (ID: ${result.userId}) associated with socket ${socket.id}. Welcome!`); } socket.emit('loginResponse', result); }); socket.on('logout', () => { const username = socket.userData?.username || socket.id; console.log(`[Server BC.JS] Received 'logout' event from ${username}.`); if (socket.userData) { // При выходе пользователя, обрабатываем его возможное участие в играх gameManager.handleDisconnect(socket.id, socket.userData.userId); // Используем userId для более точной обработки delete socket.userData; // Удаляем данные пользователя из сокета console.log(`[Server BC.JS] User data cleared for ${username}.`); } // Можно отправить подтверждение выхода, если нужно // socket.emit('logoutResponse', { success: true, message: 'Вы успешно вышли.' }); }); // --- Управление Играми --- socket.on('createGame', (data) => { if (!socket.userData) { socket.emit('gameError', { message: "Ошибка: Вы не авторизованы для создания игры." }); return; } const mode = data?.mode || 'ai'; // 'ai' или 'pvp' const characterKey = (data?.characterKey === 'almagest') ? 'almagest' : 'elena'; // По умолчанию Елена console.log(`[Server BC.JS] User ${socket.userData.username} (socket: ${socket.id}) requests createGame. Mode: ${mode}, Character: ${characterKey}`); gameManager.createGame(socket, mode, characterKey, socket.userData.userId); }); socket.on('joinGame', (data) => { if (!socket.userData) { socket.emit('gameError', { message: "Ошибка: Вы не авторизованы для присоединения к игре." }); return; } console.log(`[Server BC.JS] User ${socket.userData.username} (socket: ${socket.id}) requests joinGame for ID: ${data?.gameId}`); if (data && typeof data.gameId === 'string') { gameManager.joinGame(socket, data.gameId, socket.userData.userId); } else { socket.emit('gameError', { message: 'Ошибка присоединения: неверный формат ID игры.' }); } }); socket.on('findRandomGame', (data) => { if (!socket.userData) { socket.emit('gameError', { message: "Ошибка: Вы не авторизованы для поиска игры." }); return; } const characterKey = (data?.characterKey === 'almagest') ? 'almagest' : 'elena'; console.log(`[Server BC.JS] User ${socket.userData.username} (socket: ${socket.id}) requests findRandomGame. Preferred Character: ${characterKey}`); gameManager.findAndJoinRandomPvPGame(socket, characterKey, socket.userData.userId); }); // --- Игровые Действия --- socket.on('playerAction', (data) => { if (!socket.userData) { // Если пользователь не авторизован, но пытается совершить действие (маловероятно при правильной логике клиента) socket.emit('gameError', { message: "Ошибка: Вы не авторизованы для совершения этого действия." }); return; } // GameManager сам проверит, принадлежит ли этот сокет к активной игре gameManager.handlePlayerAction(socket.id, data); }); // Обработчик 'requestRestart' удален, так как эта функциональность заменена на "возврат в меню" // --- Отключение Клиента --- socket.on('disconnect', (reason) => { const username = socket.userData?.username || socket.id; console.log(`[Server BC.JS] Client ${username} disconnected. Reason: ${reason}. Socket ID: ${socket.id}`); // Передаем userId, если он есть, для более точной обработки в GameManager // (например, для удаления его ожидающих игр или корректного завершения активной игры) const userId = socket.userData?.userId; gameManager.handleDisconnect(socket.id, userId); // socket.userData автоматически очистится для этого объекта socket при его удалении из io.sockets }); // Для отладки: вывод списка активных игр каждые N секунд // setInterval(() => { // console.log("--- Active Games ---"); // const activeGames = gameManager.getActiveGamesList(); // if (activeGames.length > 0) { // activeGames.forEach(game => { // console.log(`ID: ${game.id}, Mode: ${game.mode}, Players: ${game.playerCount}, GameOver: ${game.isGameOver}, P1: ${game.playerSlot}, P2: ${game.opponentSlot}, Owner: ${game.ownerUserId}, Pending: ${game.pending}`); // }); // } else { // console.log("No active games."); // } // console.log("--- Pending PvP Games IDs ---"); // console.log(gameManager.pendingPvPGames.map(id => id.substring(0,8))); // console.log("--- User to Pending Game Map ---"); // console.log(gameManager.userToPendingGame); // console.log("---------------------"); // }, 30000); // Каждые 30 секунд }); server.listen(PORT, hostname, () => { console.log(`==== Medieval Clash Server ====`); console.log(` Listening on http://${hostname}:${PORT}`); console.log(` Public files served from: ${path.join(__dirname, 'public')}`); console.log(` Waiting for connections...`); console.log(`===============================`); });