diff --git a/bc.js b/bc.js
index b28f6d7..99c9250 100644
--- a/bc.js
+++ b/bc.js
@@ -1,228 +1,99 @@
// bc.js - Главный файл сервера Battle Club
const express = require('express');
-const http = require('http');
+const http = require('http'); // Используем HTTP, так как SSL терминируется Apache
const { Server } = require('socket.io');
const path = require('path');
-// Импорт серверных модулей
-const auth = require('./server_modules/auth');
-const GameManager = require('./server_modules/gameManager');
-const db = require('./server_modules/db'); // Импорт для инициализации соединения с БД (хотя пул создается при require)
-const GAME_CONFIG = require('./server_modules/config'); // Конфиг игры
-// gameData импортируется внутри GameInstance и GameLogic
+// Импорт ваших серверных модулей (предполагаем, что они есть и работают)
+// const auth = require('./server_modules/auth');
+// const GameManager = require('./server_modules/gameManager');
+// const db = require('./server_modules/db');
+// const GAME_CONFIG = require('./server_modules/config');
const app = express();
const server = http.createServer(app);
+const BC_APP_INTERNAL_PORT = 3200; // Внутренний порт, на котором слушает bc.js
+const BC_APP_INTERNAL_HOST = '127.0.0.1'; // Слушать только на localhost
+const PUBLIC_PATH_PREFIX = '/battleclub'; // Публичный префикс пути, по которому приложение доступно через Apache
+
// Настройка Socket.IO
-// cors options могут потребоваться, если клиент и сервер работают на разных портах/доменах
+// Клиент будет подключаться к /battleclub/socket.io/
const io = new Server(server, {
+ path: `${PUBLIC_PATH_PREFIX}/socket.io`,
cors: {
- origin: "*", // Разрешить подключение с любого домена (для разработки). В продакшене лучше указать конкретный домен клиента.
+ origin: "https://pavel-chagovsky.com", // Укажите ваш домен для безопасности
+ // origin: "*", // Для разработки можно оставить, но для продакшена лучше конкретный домен
methods: ["GET", "POST"]
}
});
+// Middleware для логирования каждого запроса (полезно для отладки)
+app.use((req, res, next) => {
+ console.log(`[BC App] Request: ${req.method} ${req.originalUrl}`);
+ next();
+});
+
// Раздача статических файлов из папки 'public'
+// Так как Apache проксирует /battleclub/ на корень этого Express-приложения,
+// Express должен отдавать статику от своего корня.
+// В HTML ссылки на статику должны быть относительными или начинаться с /battleclub/
+// Например, если в public/js/client.js, то в HTML:
+// Или, если index.html отдается с /battleclub/, то
app.use(express.static(path.join(__dirname, 'public')));
-// Создаем экземпляр GameManager
-const gameManager = new GameManager(io);
-// Хранилище информации о залогиненных пользователях по socket.id
-// В более сложном приложении здесь может быть Redis или другое внешнее хранилище сессий
-const loggedInUsers = {}; // { socket.id: { userId: ..., username: ... } }
-
-// Обработка подключений Socket.IO
-io.on('connection', (socket) => {
- console.log(`[Socket.IO] Пользователь подключился: ${socket.id}`);
-
- // Привязываем user data к сокету (пока пустые)
- socket.userData = null; // { userId: ..., username: ... }
-
- // При подключении клиента, если он уже залогинен (например, по cookie/token, что здесь не реализовано,
- // но может быть добавлено), нужно восстановить его user data и проверить, не в игре ли он.
- // В текущей простой реализации, мы полагаемся на то, что клиент после коннекта сам отправит логин,
- // если он был залогинен. Но если бы была проверка сессии, логика была бы тут.
- // Добавляем вызов handleRequestGameState при коннекте, если есть user data (для примера,
- // но для полной реализации нужны cookies/токены)
- // if (socket.userData?.userId) { // Эта проверка сработает только после успешного логина в текущей сессии
- // gameManager.handleRequestGameState(socket, socket.userData.userId); // Передаем объект socket
- // }
-
-
- // --- Обработчики событий Аутентификации ---
- socket.on('register', async (data) => {
- console.log(`[Socket.IO] Register attempt for username: "${data?.username}" from ${socket.id}`);
- const result = await auth.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 auth.loginUser(data?.username, data?.password);
- if (result.success) {
- 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;
-
- // Проверяем, есть ли у пользователя активная игра при логине (если он был отключен)
- // ИСПРАВЛЕНИЕ: Передаем объект socket
- gameManager.handleRequestGameState(socket, socket.userData.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', () => {
- console.log(`[Socket.IO] Logout for user ${socket.userData?.username || socket.id}`);
- // Уведомляем gameManager о дисконнекте (для корректного выхода из игры, если в ней был)
- // Game Manager сам очистит ссылку socketToGame[socket.id] при handleDisconnect
- // ИСПРАВЛЕНИЕ: Передаем userId или socket.id в handleDisconnect
- gameManager.handleDisconnect(socket.id, socket.userData?.userId || socket.id);
-
- // Очищаем информацию о пользователе на сокете и в хранилище
- socket.userData = null;
- if (loggedInUsers[socket.id]) delete loggedInUsers[socket.id];
-
- // Клиент должен сам переключиться на экран аутентификации
- });
-
- // --- Обработчики событий Управления Играми ---
-
- socket.on('createGame', (data) => {
- // Пользователь, даже не залогиненный, может создать AI игру (идентифицируется по socket.id)
- // Для PvP игры нужна аутентификация (идентификация по userId)
- const identifier = socket.userData?.userId || socket.id; // Используем 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 request from ${socket.userData?.username || socket.id} (Identifier: ${identifier}). Mode: ${mode}, Character: ${data?.characterKey}`);
-
- const characterKey = data?.characterKey || 'elena'; // По умолчанию Елена
- gameManager.createGame(socket, mode, characterKey, identifier); // Передаем идентификатор
-
- });
-
- socket.on('joinGame', (data) => {
- if (!socket.userData) { // Проверяем, залогинен ли пользователь
- socket.emit('gameError', { message: 'Необходимо войти в систему для присоединения к игре.' });
- return;
- }
- console.log(`[Socket.IO] Join Game request from ${socket.userData.username} (${socket.id}). Game ID: ${data?.gameId}`);
- const gameId = data?.gameId;
- const identifier = socket.userData.userId; // Присоединиться может только залогиненный
-
- if (gameId) {
- gameManager.joinGame(socket, gameId, identifier); // Передаем идентификатор
- } else {
- socket.emit('gameError', { message: 'Не указан ID игры для присоединения.' });
- }
- });
-
- socket.on('findRandomGame', (data) => {
- if (!socket.userData) { // Проверяем, залогинен ли пользователь
- socket.emit('gameError', { message: 'Необходимо войти в систему для поиска игры.' });
- return;
- }
- console.log(`[Socket.IO] Find Random Game request from ${socket.userData.username} (${socket.id}). Preferred Character: ${data?.characterKey}`);
- const characterKey = data?.characterKey || 'elena'; // Предпочитаемый персонаж для создания, если не найдено
- const identifier = socket.userData.userId; // Ищет и создает только залогиненный
-
- gameManager.findAndJoinRandomPvPGame(socket, characterKey, identifier); // Передаем идентификатор
- });
-
- socket.on('requestPvPGameList', () => {
- // Список игр доступен всем, даже не залогиненным, но присоединиться можно только залогиненным
- // if (!socket.userData) {
- // socket.emit('gameError', { message: 'Необходимо войти в систему для просмотра игр.' });
- // return;
- // }
- console.log(`[Socket.IO] Request PvP Game List from ${socket.userData?.username || socket.id}`);
- const availableGames = gameManager.getAvailablePvPGamesListForClient();
- socket.emit('availablePvPGamesList', availableGames);
- });
-
- // Обработчик для клиента, запрашивающего состояние игры (например, при переподключении)
- socket.on('requestGameState', () => {
- // Запрашивать состояние игры может только залогиненный пользователь, т.к. только у них есть userId для идентификации
- if (!socket.userData) {
- console.log(`[Socket.IO] Request Game State from unauthenticated socket ${socket.id}.`);
- socket.emit('gameNotFound', { message: 'Необходимо войти для восстановления игры.' });
- return;
- }
- console.log(`[Socket.IO] Request Game State from ${socket.userData.username} (${socket.id}).`);
- // ИСПРАВЛЕНИЕ: Передаем объект socket и identifier (userId)
- gameManager.handleRequestGameState(socket, socket.userData.userId);
- });
-
-
- // --- Обработчик события Игрового Действия ---
- socket.on('playerAction', (actionData) => {
- // Действие в игре может совершить как залогиненный (PvP), так и не залогиненный (AI) игрок.
- // Используем userId для залогиненных, socket.id для гостей.
- const identifier = socket.userData?.userId || socket.id;
-
- // Game Manager сам проверит, находится ли идентификатор в игре и его ли сейчас ход
- // ИСПРАВЛЕНИЕ: Передаем идентификатор вместо socket.id
- gameManager.handlePlayerAction(identifier, actionData); // Передаем идентификатор
- });
-
-
- // --- Обработчик отключения сокета ---
- socket.on('disconnect', (reason) => {
- const identifier = socket.userData?.userId || socket.id; // Используем userId для залогиненных, socket.id для гостей
- console.log(`[Socket.IO] Пользователь отключился: ${socket.id} (Причина: ${reason}). Identifier: ${identifier}`);
-
- // Уведомляем gameManager о дисконнекте, чтобы он обновил состояние игры.
- // Передаем идентификатор пользователя.
- gameManager.handleDisconnect(socket.id, identifier); // Передаем как socketId, так и identifier
-
- // Удаляем пользователя из списка залогиненных, если был там
- if (loggedInUsers[socket.id]) {
- delete loggedInUsers[socket.id];
- }
- // Если сокет не был залогинен, его identifier был socket.id.
- // Связь userIdentifierToGameId будет очищена в gameManager.handleDisconnect, если игра пуста.
- });
-
- // Опционально: отправка списка активных игр на сервере для отладки (по запросу с консоли или админки)
- // global.getActiveGames = () => gameManager.getActiveGamesList();
- // console.log("Type getActiveGames() in server console to list games.");
+// Пример простого маршрута API, если он нужен (доступен по /battleclub/api/test)
+app.get('/api/test', (req, res) => {
+ res.json({ message: 'Battle Club API is working!' });
});
+// Если ваше основное приложение - это SPA (Single Page Application),
+// вам может понадобиться отдавать index.html для всех не-API и не-статических путей,
+// начинающихся с префикса /battleclub/. Но так как Apache проксирует /battleclub/ на /,
+// то Express будет видеть пути без /battleclub/.
+// Поэтому, если index.html должен отдаваться для /battleclub/ или /battleclub/some/path,
+// то здесь нужен роут для '*' или специфичные роуты.
+// Пока для простоты, предположим, что Apache проксирует /battleclub/ на корень bc.js,
+// и index.html находится в public/ и запрашивается как /battleclub/index.html (или просто /battleclub/)
+app.get('/', (req, res) => {
+ // Этот роут будет срабатывать, если Apache проксировал /battleclub/ на / этого приложения
+ res.sendFile(path.join(__dirname, 'public', 'index.html'));
+});
+
+
+// --- Обработчики событий Socket.IO ---
+io.on('connection', (socket) => {
+ console.log(`[BC App Socket.IO] User connected: ${socket.id} to path ${socket.nsp.name}`);
+
+ socket.on('messageFromClient', (data) => {
+ console.log(`[BC App Socket.IO] Message from client ${socket.id}:`, data);
+ socket.emit('messageFromServer', { text: `Server received: ${data.text}` });
+ });
+
+ socket.on('disconnect', (reason) => {
+ console.log(`[BC App Socket.IO] User disconnected: ${socket.id} (Reason: ${reason})`);
+ });
+
+ // ... ваши обработчики auth, GameManager и т.д. ...
+ // Убедитесь, что они не полагаются на префикс пути /battleclub/ во внутренних данных,
+ // так как Express его "не видит" после проксирования Apache.
+});
+
+
// Запуск HTTP сервера
-const PORT = process.env.PORT || 3200; // Использовать порт из переменных окружения или 3000 по умолчанию
-server.listen(PORT, () => {
- console.log(`Server running on port ${PORT}`);
+server.listen(BC_APP_INTERNAL_PORT, BC_APP_INTERNAL_HOST, () => {
+ console.log(`Battle Club HTTP Application Server running at http://${BC_APP_INTERNAL_HOST}:${BC_APP_INTERNAL_PORT}`);
+ console.log(`Socket.IO will be available via proxy at path: ${PUBLIC_PATH_PREFIX}/socket.io`);
console.log(`Serving static files from: ${path.join(__dirname, 'public')}`);
- // console.log("Database connection pool created/checked (from db.js require)."); // db.js уже логирует
});
-// Обработка необработанных промис-ошибок
+// Обработчики глобальных ошибок
process.on('unhandledRejection', (reason, promise) => {
- console.error('[UNHANDLED REJECTION] Unhandled Rejection at:', promise, 'reason:', reason);
- // Логировать ошибку, возможно, завершить процесс в продакшене
+ console.error('[BC App UNHANDLED REJECTION] At:', promise, 'reason:', reason);
});
-
process.on('uncaughtException', (err) => {
- console.error('[UNCAUGHT EXCEPTION] Caught exception:', err);
- // Логировать ошибку, выполнить очистку ресурсов, и завершить процесс
- // В продакшене здесь может быть более сложная логика, например, graceful shutdown
- // process.exit(1); // Аварийное завершение процесса - можно раскомментировать в продакшене
+ console.error('[BC App UNCAUGHT EXCEPTION] Caught exception:', err);
+ // process.exit(1); // В продакшене может быть оправдано
});
\ No newline at end of file
diff --git a/deploy-script.sh b/deploy-script.sh
new file mode 100755
index 0000000..6a12e39
--- /dev/null
+++ b/deploy-script.sh
@@ -0,0 +1,4 @@
+#!/bin/bash
+cd /home/nodejs/bc/ || exit 1
+git pull origin main
+pm2 restart /home/nodejs/bc/bc.js
\ No newline at end of file
diff --git a/node_modules/child_process/README.md b/node_modules/child_process/README.md
new file mode 100644
index 0000000..5e9a74c
--- /dev/null
+++ b/node_modules/child_process/README.md
@@ -0,0 +1,9 @@
+# Security holding package
+
+This package name is not currently in use, but was formerly occupied
+by another package. To avoid malicious use, npm is hanging on to the
+package name, but loosely, and we'll probably give it to you if you
+want it.
+
+You may adopt this package by contacting support@npmjs.com and
+requesting the name.
diff --git a/node_modules/child_process/package.json b/node_modules/child_process/package.json
new file mode 100644
index 0000000..50ba9be
--- /dev/null
+++ b/node_modules/child_process/package.json
@@ -0,0 +1,20 @@
+{
+ "name": "child_process",
+ "version": "1.0.2",
+ "description": "",
+ "main": "index.js",
+ "scripts": {
+ "test": "echo \"Error: no test specified\" && exit 1"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/npm/security-holder.git"
+ },
+ "keywords": [],
+ "author": "",
+ "license": "ISC",
+ "bugs": {
+ "url": "https://github.com/npm/security-holder/issues"
+ },
+ "homepage": "https://github.com/npm/security-holder#readme"
+}
diff --git a/node_modules/crypto/README.md b/node_modules/crypto/README.md
new file mode 100644
index 0000000..5437f14
--- /dev/null
+++ b/node_modules/crypto/README.md
@@ -0,0 +1,7 @@
+# Deprecated Package
+
+This package is no longer supported and has been deprecated. To avoid malicious use, npm is hanging on to the package name.
+
+It's now a built-in Node module. If you've depended on crypto, you should switch to the one that's built-in.
+
+Please contact support@npmjs.com if you have questions about this package.
diff --git a/node_modules/crypto/package.json b/node_modules/crypto/package.json
new file mode 100644
index 0000000..01aa4d3
--- /dev/null
+++ b/node_modules/crypto/package.json
@@ -0,0 +1,19 @@
+{
+ "name": "crypto",
+ "version": "1.0.1",
+ "description": "",
+ "main": "index.js",
+ "scripts": {
+ "test": "echo \"Error: no test specified\" && exit 1"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/npm/deprecate-holder.git"
+ },
+ "author": "",
+ "license": "ISC",
+ "bugs": {
+ "url": "https://github.com/npm/deprecate-holder/issues"
+ },
+ "homepage": "https://github.com/npm/deprecate-holder#readme"
+}
diff --git a/public/index.html b/public/index.html
index 51d02a5..0f1ea9f 100644
--- a/public/index.html
+++ b/public/index.html
@@ -214,7 +214,7 @@
-
+