bc/server/bc.js

181 lines
8.6 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/bc.js - Главный файл сервера Battle Club
// Загружаем переменные окружения В САМОМ НАЧАЛЕ, до всех других импортов,
// которые могут зависеть от process.env
// Убедитесь, что путь к .env правильный относительно места запуска приложения.
// Если bc.js запускается из папки server/, а .env в корне, то путь должен быть '../.env'
// Но обычно dotenv ищет .env в process.cwd()
// Но обычно dotenv ищет .env в process.cwd()
require('dotenv').config({ path: require('node:path').resolve(process.cwd(), '.env') });
const express = require('express');
const http = require('http');
const { Server } = require('socket.io');
const path = require('path');
// Импорт серверных модулей из их новых местоположений
const authService = require('./auth/authService'); // Сервис аутентификации
const GameManager = require('./game/GameManager'); // Менеджер игр
const db = require('./core/db'); // Модуль базы данных (важно, чтобы он тоже использовал dotenv)
const GAME_CONFIG = require('./core/config'); // Глобальный конфиг игры
const app = express();
const server = http.createServer(app);
// Настройка Socket.IO
const io = new Server(server, {
cors: {
// origin: process.env.CORS_ORIGIN || "https://pavel-chagovsky.com:3200", // Пример, если нужно CORS из .env
// methods: ["GET", "POST"]
},
// pingInterval: 10000,
// pingTimeout: 5000,
});
// Раздача статических файлов из папки 'public'
app.use(express.static(path.join(__dirname, '..', 'public')));
// Создаем экземпляр GameManager
const gameManager = new GameManager(io);
const loggedInUsers = {};
io.on('connection', (socket) => {
console.log(`[Socket.IO] Пользователь подключился: ${socket.id}`);
socket.userData = null;
socket.on('register', async (data) => {
console.log(`[Socket.IO] Register attempt for username: "${data?.username}" from ${socket.id}`);
const result = await authService.registerUser(data?.username, data?.password);
if (result.success) {
console.log(`[Socket.IO] Registration successful for ${result.username} (${result.userId})`);
} else {
console.warn(`[Socket.IO] Registration failed for "${data?.username}": ${result.message}`);
}
socket.emit('registerResponse', result);
});
socket.on('login', async (data) => {
console.log(`[Socket.IO] Login attempt for username: "${data?.username}" from ${socket.id}`);
const result = await authService.loginUser(data?.username, data?.password);
if (result.success && result.userId && result.username) {
console.log(`[Socket.IO] Login successful for ${result.username} (${result.userId}). Assigning to socket ${socket.id}.`);
socket.userData = { userId: result.userId, username: result.username };
loggedInUsers[socket.id] = socket.userData;
if (gameManager && typeof gameManager.handleRequestGameState === 'function') {
gameManager.handleRequestGameState(socket, result.userId);
}
} else {
console.warn(`[Socket.IO] Login failed for "${data?.username}": ${result.message}`);
socket.userData = null;
if (loggedInUsers[socket.id]) delete loggedInUsers[socket.id];
}
socket.emit('loginResponse', result);
});
socket.on('logout', () => {
const username = socket.userData?.username || 'UnknownUser';
const userId = socket.userData?.userId;
console.log(`[Socket.IO] Logout request from user ${username} (ID: ${userId}, Socket: ${socket.id})`);
if (gameManager && typeof gameManager.handleDisconnect === 'function' && userId) {
gameManager.handleDisconnect(socket.id, userId);
}
if (loggedInUsers[socket.id]) {
delete loggedInUsers[socket.id];
}
socket.userData = null;
console.log(`[Socket.IO] User ${username} (Socket: ${socket.id}) logged out.`);
});
socket.on('createGame', (data) => {
const identifier = socket.userData?.userId || socket.id;
const mode = data?.mode || 'ai';
if (mode === 'pvp' && !socket.userData) {
socket.emit('gameError', { message: 'Необходимо войти в систему для создания PvP игры.' });
return;
}
console.log(`[Socket.IO] Create Game from ${socket.userData?.username || socket.id} (ID: ${identifier}). Mode: ${mode}, Char: ${data?.characterKey}`);
gameManager.createGame(socket, mode, data?.characterKey, identifier);
});
socket.on('joinGame', (data) => {
if (!socket.userData?.userId) {
socket.emit('gameError', { message: 'Необходимо войти для присоединения к PvP игре.' });
return;
}
console.log(`[Socket.IO] Join Game from ${socket.userData.username} (ID: ${socket.userData.userId}). GameID: ${data?.gameId}`);
gameManager.joinGame(socket, data?.gameId, socket.userData.userId);
});
socket.on('findRandomGame', (data) => {
if (!socket.userData?.userId) {
socket.emit('gameError', { message: 'Необходимо войти для поиска случайной PvP игры.' });
return;
}
console.log(`[Socket.IO] Find Random Game from ${socket.userData.username} (ID: ${socket.userData.userId}). PrefChar: ${data?.characterKey}`);
gameManager.findAndJoinRandomPvPGame(socket, data?.characterKey, socket.userData.userId);
});
socket.on('requestPvPGameList', () => {
const availableGames = gameManager.getAvailablePvPGamesListForClient();
socket.emit('availablePvPGamesList', availableGames);
});
socket.on('requestGameState', () => {
if (!socket.userData?.userId) {
socket.emit('gameNotFound', { message: 'Необходимо войти для восстановления игры.' });
return;
}
gameManager.handleRequestGameState(socket, socket.userData.userId);
});
socket.on('playerAction', (actionData) => {
const identifier = socket.userData?.userId || socket.id;
gameManager.handlePlayerAction(identifier, actionData);
});
socket.on('disconnect', (reason) => {
const identifier = socket.userData?.userId || socket.id;
console.log(`[Socket.IO] Пользователь отключился: ${socket.id} (Причина: ${reason}). Identifier: ${identifier}`);
gameManager.handleDisconnect(socket.id, identifier);
if (loggedInUsers[socket.id]) {
delete loggedInUsers[socket.id];
}
});
});
// Запуск HTTP сервера
// Используем переменные окружения или значения по умолчанию
const PORT = parseInt(process.env.BC_APP_PORT || '3200', 10);
const HOSTNAME = process.env.BC_APP_HOSTNAME || '127.0.0.1';
// Проверка, что порт является числом
if (isNaN(PORT)) {
console.error(`[Server FATAL] Некорректное значение для BC_APP_PORT: "${process.env.BC_APP_PORT}". Ожидается число.`);
process.exit(1);
}
server.listen(PORT, HOSTNAME, () => {
console.log(`Battle Club HTTP Application Server running at http://${HOSTNAME}:${PORT}`);
if (HOSTNAME === '127.0.0.1') {
console.log(`Server is listening on localhost only. This is suitable if a reverse proxy handles external traffic.`);
} else if (HOSTNAME === '0.0.0.0') {
console.log(`Server is listening on all available network interfaces.`);
} else {
console.log(`Server is listening on a specific interface: ${HOSTNAME}.`);
}
console.log(`Environment (NODE_ENV): ${process.env.NODE_ENV || 'not set (defaults to development behavior)'}`);
console.log(`Serving static files from: ${path.join(__dirname, '..', 'public')}`);
});
process.on('unhandledRejection', (reason, promise) => {
console.error('[Server FATAL] Unhandled Rejection at:', promise, 'reason:', reason);
// process.exit(1);
});
process.on('uncaughtException', (err) => {
console.error('[Server FATAL] Uncaught Exception:', err);
process.exit(1);
});