изменения в в bc.js
This commit is contained in:
parent
79a09d583d
commit
5a6472ffdb
91
server/bc.js
91
server/bc.js
@ -1,8 +1,5 @@
|
|||||||
// /server/bc.js - Главный файл сервера Battle Club
|
// /server/bc.js - Главный файл сервера Battle Club
|
||||||
|
|
||||||
// Загружаем переменные окружения из .env файла
|
|
||||||
// Убедитесь, что `dotenv` установлен (npm install dotenv)
|
|
||||||
// и этот вызов находится как можно раньше
|
|
||||||
require('dotenv').config({ path: require('node:path').resolve(process.cwd(), '.env') });
|
require('dotenv').config({ path: require('node:path').resolve(process.cwd(), '.env') });
|
||||||
|
|
||||||
const express = require('express');
|
const express = require('express');
|
||||||
@ -12,17 +9,15 @@ const path = require('path');
|
|||||||
const jwt = require('jsonwebtoken');
|
const jwt = require('jsonwebtoken');
|
||||||
const cors = require('cors');
|
const cors = require('cors');
|
||||||
|
|
||||||
// Импорт серверных модулей
|
|
||||||
const authService = require('./auth/authService');
|
const authService = require('./auth/authService');
|
||||||
const GameManager = require('./game/GameManager');
|
const GameManager = require('./game/GameManager');
|
||||||
const db = require('./core/db'); // Предполагается, что db.js корректно инициализирует соединение
|
const db = require('./core/db');
|
||||||
const GAME_CONFIG = require('./core/config'); // Глобальная конфигурация игры
|
const GAME_CONFIG = require('./core/config');
|
||||||
|
|
||||||
const app = express();
|
const app = express();
|
||||||
const server = http.createServer(app);
|
const server = http.createServer(app);
|
||||||
|
|
||||||
// --- НАСТРОЙКА EXPRESS ---
|
// --- НАСТРОЙКА EXPRESS ---
|
||||||
// Определяем разрешенный источник для HTTP CORS
|
|
||||||
const clientOrigin = process.env.CORS_ORIGIN_CLIENT || (process.env.NODE_ENV === 'development' ? '*' : undefined);
|
const clientOrigin = process.env.CORS_ORIGIN_CLIENT || (process.env.NODE_ENV === 'development' ? '*' : undefined);
|
||||||
console.log(`[BC Server] NODE_ENV: ${process.env.NODE_ENV}`);
|
console.log(`[BC Server] NODE_ENV: ${process.env.NODE_ENV}`);
|
||||||
console.log(`[BC Server] process.env.CORS_ORIGIN_CLIENT: ${process.env.CORS_ORIGIN_CLIENT}`);
|
console.log(`[BC Server] process.env.CORS_ORIGIN_CLIENT: ${process.env.CORS_ORIGIN_CLIENT}`);
|
||||||
@ -33,13 +28,12 @@ if (!clientOrigin && process.env.NODE_ENV !== 'development') {
|
|||||||
}
|
}
|
||||||
|
|
||||||
app.use(cors({
|
app.use(cors({
|
||||||
origin: clientOrigin, // Используем рассчитанное значение
|
origin: clientOrigin,
|
||||||
methods: ["GET", "POST"],
|
methods: ["GET", "POST"],
|
||||||
credentials: true // Если вы планируете использовать куки или заголовки авторизации с CORS
|
credentials: true
|
||||||
}));
|
}));
|
||||||
|
|
||||||
app.use(express.json());
|
app.use(express.json());
|
||||||
// Раздача статических файлов из папки public уровнем выше (../public)
|
|
||||||
const publicPath = path.join(__dirname, '..', 'public');
|
const publicPath = path.join(__dirname, '..', 'public');
|
||||||
console.log(`[BC Server] Serving static files from: ${publicPath}`);
|
console.log(`[BC Server] Serving static files from: ${publicPath}`);
|
||||||
app.use(express.static(publicPath));
|
app.use(express.static(publicPath));
|
||||||
@ -79,9 +73,8 @@ app.post('/auth/login', async (req, res) => {
|
|||||||
res.status(401).json(result);
|
res.status(401).json(result);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
// ------------------------------
|
|
||||||
|
|
||||||
// Определяем разрешенный источник для Socket.IO CORS
|
// --- НАСТРОЙКА SOCKET.IO ---
|
||||||
const socketCorsOrigin = process.env.CORS_ORIGIN_SOCKET || (process.env.NODE_ENV === 'development' ? '*' : undefined);
|
const socketCorsOrigin = process.env.CORS_ORIGIN_SOCKET || (process.env.NODE_ENV === 'development' ? '*' : undefined);
|
||||||
console.log(`[BC Server] process.env.CORS_ORIGIN_SOCKET: ${process.env.CORS_ORIGIN_SOCKET}`);
|
console.log(`[BC Server] process.env.CORS_ORIGIN_SOCKET: ${process.env.CORS_ORIGIN_SOCKET}`);
|
||||||
console.log(`[BC Server] Calculated Socket.IO CORS Origin (socketCorsOrigin): ${socketCorsOrigin === '*' ? "'*'" : socketCorsOrigin || 'Not explicitly set (will likely fail if not development)'}`);
|
console.log(`[BC Server] Calculated Socket.IO CORS Origin (socketCorsOrigin): ${socketCorsOrigin === '*' ? "'*'" : socketCorsOrigin || 'Not explicitly set (will likely fail if not development)'}`);
|
||||||
@ -91,34 +84,29 @@ if (!socketCorsOrigin && process.env.NODE_ENV !== 'development') {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const io = new Server(server, {
|
const io = new Server(server, {
|
||||||
// path: '/socket.io/', // Оставьте эту строку ЗАКОММЕНТИРОВАННОЙ, если клиент подключается к /socket.io/ по умолчанию,
|
// ВАЖНО: Этот path должен соответствовать тому, как клиент подключается
|
||||||
// И ваш прокси НЕ отрезает /socket.io/ (т.е. stripPrefix: false для /socket.io в config.json)
|
// и как прокси настроен (особенно stripPrefix для /socket.io).
|
||||||
// Если клиент подключается к /, а прокси добавляет /socket.io/, то здесь нужно '/'.
|
// Если клиент и прокси работают с /socket.io/ и прокси НЕ отрезает этот префикс
|
||||||
// Если клиент подключается к /socket.io/, а прокси ОТРЕЗАЕТ /socket.io/ (stripPrefix: true), то здесь не нужен path.
|
// (stripPrefix: false для /socket.io в config.json), то здесь ДОЛЖЕН быть path.
|
||||||
// Для вашей конфигурации прокси (stripPrefix: false для /socket.io), и если клиент обращается к /socket.io/,
|
path: '/socket.io/', // <--- УБЕДИТЕСЬ, ЧТО ЭТА СТРОКА РАСКОММЕНТИРОВАНА И КОРРЕКТНА
|
||||||
// то здесь ЛИБО path: '/socket.io/', ЛИБО клиент должен обращаться к wss://domain.com/ (без /socket.io/)
|
|
||||||
// и тогда прокси должен добавлять /socket.io в target.
|
|
||||||
// САМЫЙ ПРОСТОЙ ВАРИАНТ: если клиент идет на /socket.io/ (стандарт), и прокси НЕ режет /socket.io/ (stripPrefix: false),
|
|
||||||
// то здесь path должен быть '/socket.io/'.
|
|
||||||
path: '/socket.io/', // <--- РАСКОММЕНТИРУЙТЕ ЭТО, если клиент подключается к /socket.io/ и прокси это не отрезает.
|
|
||||||
cors: {
|
cors: {
|
||||||
origin: socketCorsOrigin, // Используем рассчитанное значение
|
origin: socketCorsOrigin,
|
||||||
methods: ["GET", "POST"],
|
methods: ["GET", "POST"],
|
||||||
credentials: true
|
credentials: true
|
||||||
},
|
},
|
||||||
// transports: ['websocket', 'polling'], // Можно явно указать транспорты
|
// transports: ['websocket', 'polling'], // Можно оставить по умолчанию или указать явно
|
||||||
});
|
});
|
||||||
console.log(`[BC Server] Socket.IO server configured with path: ${io.path()}`);
|
console.log(`[BC Server] Socket.IO server configured with path: ${io.path()} and CORS origin: ${socketCorsOrigin === '*' ? "'*'" : socketCorsOrigin || 'Not set'}`);
|
||||||
|
|
||||||
|
|
||||||
const gameManager = new GameManager(io); // GameManager должен быть инициализирован ПОСЛЕ io
|
const gameManager = new GameManager(io);
|
||||||
const loggedInUsers = {}; // { socket.id: { userId, username } }
|
const loggedInUsers = {};
|
||||||
|
|
||||||
// --- MIDDLEWARE АУТЕНТИФИКАЦИИ SOCKET.IO ---
|
// --- MIDDLEWARE АУТЕНТИФИКАЦИИ SOCKET.IO ---
|
||||||
io.use(async (socket, next) => {
|
io.use(async (socket, next) => {
|
||||||
const token = socket.handshake.auth.token;
|
const token = socket.handshake.auth.token;
|
||||||
const clientIp = socket.handshake.address;
|
const clientIp = socket.handshake.address; // Может быть IP прокси, если xfwd не настроен на уровне Socket.IO
|
||||||
console.log(`[BC Socket.IO Middleware] Auth attempt for socket ${socket.id} from IP ${clientIp}. Token ${token ? 'present' : 'absent'}. Origin: ${socket.handshake.headers.origin}`);
|
console.log(`[BC Socket.IO Middleware] Auth attempt for socket ${socket.id} from IP ${clientIp}. Token ${token ? 'present' : 'absent'}. Origin: ${socket.handshake.headers.origin}. Path: ${socket.nsp.name}`);
|
||||||
|
|
||||||
if (token) {
|
if (token) {
|
||||||
try {
|
try {
|
||||||
@ -128,48 +116,37 @@ io.use(async (socket, next) => {
|
|||||||
return next();
|
return next();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.warn(`[BC Socket.IO Middleware] Socket ${socket.id} auth failed: Invalid token. Error: ${err.message}`);
|
console.warn(`[BC Socket.IO Middleware] Socket ${socket.id} auth failed: Invalid token. Error: ${err.message}`);
|
||||||
// не вызываем next(new Error(...)) чтобы разрешить неаутентифицированные подключения,
|
|
||||||
// но socket.userData не будет установлен. Логика в 'connection' должна это учитывать.
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
console.log(`[BC Socket.IO Middleware] Socket ${socket.id} has no token. Proceeding as unauthenticated.`);
|
console.log(`[BC Socket.IO Middleware] Socket ${socket.id} has no token. Proceeding as unauthenticated.`);
|
||||||
}
|
}
|
||||||
// Позволяем неаутентифицированным сокетам подключаться,
|
|
||||||
// но они не смогут выполнять действия, требующие userData
|
|
||||||
next();
|
next();
|
||||||
});
|
});
|
||||||
// ------------------------------------
|
|
||||||
|
|
||||||
|
// --- ОБРАБОТЧИКИ СОБЫТИЙ SOCKET.IO ---
|
||||||
io.on('connection', (socket) => {
|
io.on('connection', (socket) => {
|
||||||
if (socket.userData && socket.userData.userId) {
|
if (socket.userData && socket.userData.userId) {
|
||||||
console.log(`[BC Socket.IO] Authenticated user ${socket.userData.username} (ID: ${socket.userData.userId}) connected with socket: ${socket.id}`);
|
console.log(`[BC Socket.IO] Authenticated user ${socket.userData.username} (ID: ${socket.userData.userId}) connected with socket: ${socket.id} to path ${socket.nsp.name}`);
|
||||||
loggedInUsers[socket.id] = socket.userData; // Сохраняем данные пользователя для этого сокета
|
loggedInUsers[socket.id] = socket.userData;
|
||||||
// Запрашиваем состояние игры для подключившегося пользователя
|
|
||||||
if (gameManager && typeof gameManager.handleRequestGameState === 'function') {
|
if (gameManager && typeof gameManager.handleRequestGameState === 'function') {
|
||||||
// При подключении (или реконнекте с новым сокетом) пользователя,
|
|
||||||
// который уже был залогинен (токен валиден), пытаемся восстановить его игру.
|
|
||||||
gameManager.handleRequestGameState(socket, socket.userData.userId);
|
gameManager.handleRequestGameState(socket, socket.userData.userId);
|
||||||
} else {
|
} else {
|
||||||
console.error("[BC Socket.IO] CRITICAL: gameManager or handleRequestGameState not available on connect for authenticated user!");
|
console.error("[BC Socket.IO] CRITICAL: gameManager or handleRequestGameState not available on connect for authenticated user!");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
console.log(`[BC Socket.IO] Unauthenticated user connected with socket: ${socket.id}. No game state will be restored.`);
|
console.log(`[BC Socket.IO] Unauthenticated user connected with socket: ${socket.id} to path ${socket.nsp.name}. No game state will be restored.`);
|
||||||
// Можно отправить клиенту сообщение, что он не аутентифицирован, если это необходимо
|
|
||||||
// socket.emit('authError', { message: 'Вы не аутентифицированы.' });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
socket.on('logout', () => { // Это событие инициируется клиентом перед фактическим удалением токена
|
// ... (остальные обработчики событий: logout, playerSurrender, createGame, и т.д. остаются как в вашей версии с логами) ...
|
||||||
|
// Копирую их из вашего предыдущего варианта для полноты:
|
||||||
|
socket.on('logout', () => {
|
||||||
const username = socket.userData?.username || 'UnknownUser';
|
const username = socket.userData?.username || 'UnknownUser';
|
||||||
const userId = socket.userData?.userId;
|
const userId = socket.userData?.userId;
|
||||||
console.log(`[BC Socket.IO] 'logout' event from user ${username} (ID: ${userId}, Socket: ${socket.id})`);
|
console.log(`[BC Socket.IO] 'logout' event from user ${username} (ID: ${userId}, Socket: ${socket.id})`);
|
||||||
// GameManager.handleDisconnect будет вызван автоматически при событии 'disconnect',
|
|
||||||
// которое произойдет после того, как клиент разорвет соединение или обновит токен.
|
|
||||||
// Если игрок нажал "выход", но еще в игре, клиент пошлет 'playerSurrender' ДО этого.
|
|
||||||
if (loggedInUsers[socket.id]) {
|
if (loggedInUsers[socket.id]) {
|
||||||
delete loggedInUsers[socket.id];
|
delete loggedInUsers[socket.id];
|
||||||
}
|
}
|
||||||
socket.userData = null; // Очищаем данные на сокете
|
socket.userData = null;
|
||||||
// Клиент сам разорвет соединение и переподключится без токена, или с новым токеном.
|
|
||||||
console.log(`[BC Socket.IO] User ${username} (Socket: ${socket.id}) session data cleared on server due to 'logout' event.`);
|
console.log(`[BC Socket.IO] User ${username} (Socket: ${socket.id}) session data cleared on server due to 'logout' event.`);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -197,7 +174,7 @@ io.on('connection', (socket) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const identifier = socket.userData.userId;
|
const identifier = socket.userData.userId;
|
||||||
const mode = data?.mode || 'ai'; // По умолчанию AI режим
|
const mode = data?.mode || 'ai';
|
||||||
const charKey = data?.characterKey;
|
const charKey = data?.characterKey;
|
||||||
console.log(`[BC Socket.IO 'createGame'] Request from ${socket.userData.username} (ID: ${identifier}). Mode: ${mode}, Char: ${charKey}`);
|
console.log(`[BC Socket.IO 'createGame'] Request from ${socket.userData.username} (ID: ${identifier}). Mode: ${mode}, Char: ${charKey}`);
|
||||||
gameManager.createGame(socket, mode, charKey, identifier);
|
gameManager.createGame(socket, mode, charKey, identifier);
|
||||||
@ -228,15 +205,12 @@ io.on('connection', (socket) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
socket.on('requestPvPGameList', () => {
|
socket.on('requestPvPGameList', () => {
|
||||||
// Это событие может запрашивать любой подключенный сокет, аутентификация нестрогая,
|
|
||||||
// но список будет полезен только залогиненным.
|
|
||||||
console.log(`[BC Socket.IO 'requestPvPGameList'] Request from socket ${socket.id} (User: ${socket.userData?.username || 'Unauth'}).`);
|
console.log(`[BC Socket.IO 'requestPvPGameList'] Request from socket ${socket.id} (User: ${socket.userData?.username || 'Unauth'}).`);
|
||||||
const availableGames = gameManager.getAvailablePvPGamesListForClient();
|
const availableGames = gameManager.getAvailablePvPGamesListForClient();
|
||||||
socket.emit('availablePvPGamesList', availableGames);
|
socket.emit('availablePvPGamesList', availableGames);
|
||||||
});
|
});
|
||||||
|
|
||||||
socket.on('requestGameState', () => {
|
socket.on('requestGameState', () => {
|
||||||
// Это событие имеет смысл только для аутентифицированного пользователя
|
|
||||||
if (!socket.userData?.userId) {
|
if (!socket.userData?.userId) {
|
||||||
console.warn(`[BC Socket.IO 'requestGameState'] Denied for unauthenticated socket ${socket.id}.`);
|
console.warn(`[BC Socket.IO 'requestGameState'] Denied for unauthenticated socket ${socket.id}.`);
|
||||||
socket.emit('gameNotFound', { message: 'Необходимо войти для восстановления игры.' });
|
socket.emit('gameNotFound', { message: 'Необходимо войти для восстановления игры.' });
|
||||||
@ -263,18 +237,17 @@ io.on('connection', (socket) => {
|
|||||||
const username = socket.userData?.username || 'UnauthenticatedUser';
|
const username = socket.userData?.username || 'UnauthenticatedUser';
|
||||||
console.log(`[BC Socket.IO] User ${username} (ID: ${identifier || 'N/A'}, Socket: ${socket.id}) disconnected. Reason: ${reason}.`);
|
console.log(`[BC Socket.IO] User ${username} (ID: ${identifier || 'N/A'}, Socket: ${socket.id}) disconnected. Reason: ${reason}.`);
|
||||||
if (identifier) {
|
if (identifier) {
|
||||||
// Уведомляем GameManager об отключении пользователя, чтобы он мог обработать логику игры
|
|
||||||
// (например, пометить игрока как временно отключенного, запустить таймер реконнекта)
|
|
||||||
gameManager.handleDisconnect(socket.id, identifier);
|
gameManager.handleDisconnect(socket.id, identifier);
|
||||||
}
|
}
|
||||||
if (loggedInUsers[socket.id]) {
|
if (loggedInUsers[socket.id]) {
|
||||||
delete loggedInUsers[socket.id]; // Удаляем из списка активных сокетов
|
delete loggedInUsers[socket.id];
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// --- ЗАПУСК СЕРВЕРА ---
|
||||||
const PORT = parseInt(process.env.BC_APP_PORT || '3200', 10);
|
const PORT = parseInt(process.env.BC_APP_PORT || '3200', 10);
|
||||||
const HOSTNAME = process.env.BC_APP_HOSTNAME || '127.0.0.1'; // '0.0.0.0' для прослушивания на всех интерфейсах
|
const HOSTNAME = process.env.BC_APP_HOSTNAME || '127.0.0.1';
|
||||||
|
|
||||||
if (isNaN(PORT)) {
|
if (isNaN(PORT)) {
|
||||||
console.error(`[BC Server FATAL] Некорректное значение для BC_APP_PORT: "${process.env.BC_APP_PORT}". Ожидается число.`);
|
console.error(`[BC Server FATAL] Некорректное значение для BC_APP_PORT: "${process.env.BC_APP_PORT}". Ожидается число.`);
|
||||||
@ -291,19 +264,13 @@ server.listen(PORT, HOSTNAME, () => {
|
|||||||
console.log(`[BC Server] Server is listening on a specific interface: ${HOSTNAME}.`);
|
console.log(`[BC Server] Server is listening on a specific interface: ${HOSTNAME}.`);
|
||||||
}
|
}
|
||||||
console.log(`[BC Server] Static files served from: ${publicPath}`);
|
console.log(`[BC Server] Static files served from: ${publicPath}`);
|
||||||
console.log(`[BC Server] Socket.IO server using path: ${io.path()} and CORS origin: ${socketCorsOrigin === '*' ? "'*'" : socketCorsOrigin || 'Not set'}`);
|
|
||||||
console.log(`[BC Server] HTTP API CORS origin: ${clientOrigin === '*' ? "'*'" : clientOrigin || 'Not set'}`);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Глобальные обработчики необработанных ошибок
|
|
||||||
process.on('unhandledRejection', (reason, promise) => {
|
process.on('unhandledRejection', (reason, promise) => {
|
||||||
console.error('[BC Server FATAL] Unhandled Rejection at:', promise, 'reason:', reason);
|
console.error('[BC Server FATAL] Unhandled Rejection at:', promise, 'reason:', reason);
|
||||||
// В продакшене здесь может быть логика для более корректного завершения или перезапуска
|
|
||||||
});
|
});
|
||||||
|
|
||||||
process.on('uncaughtException', (err) => {
|
process.on('uncaughtException', (err) => {
|
||||||
console.error('[BC Server FATAL] Uncaught Exception:', err);
|
console.error('[BC Server FATAL] Uncaught Exception:', err);
|
||||||
// Важно: после uncaughtException приложение находится в непредсказуемом состоянии
|
|
||||||
// и должно быть перезапущено.
|
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
});
|
});
|
Loading…
x
Reference in New Issue
Block a user