From 6499e8d9eacf1caf91de95afd170d619987fcd64 Mon Sep 17 00:00:00 2001 From: PsiMagistr Date: Sun, 25 May 2025 11:16:48 +0300 Subject: [PATCH] =?UTF-8?q?=D0=92=D0=B5=D1=80=D0=BD=D1=83=D0=BB=D1=81?= =?UTF-8?q?=D1=8F=20=D0=BA=20=D1=81=D1=82=D0=B0=D1=80=D0=BE=D0=B9=20=D0=BA?= =?UTF-8?q?=D0=BE=D0=BD=D1=86=D0=B5=D0=BF=D1=86=D0=B8=D0=B8=20main.js=20?= =?UTF-8?q?=D0=B1=D0=B5=D0=B7=20=D1=8F=D0=B2=D0=BD=D0=BE=D0=B3=D0=BE=20?= =?UTF-8?q?=D1=83=D0=BA=D0=B0=D0=B7=D0=B0=D0=BD=D0=B8=D1=8F=20=D0=B0=D0=B4?= =?UTF-8?q?=D1=80=D0=B5=D1=81=D0=B0.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/js/main.js | 146 +++++++++++++++++----------------------------- 1 file changed, 55 insertions(+), 91 deletions(-) diff --git a/public/js/main.js b/public/js/main.js index 7ea9a31..b118d26 100644 --- a/public/js/main.js +++ b/public/js/main.js @@ -3,7 +3,7 @@ import { initAuth } from './auth.js'; import { initGameSetup } from './gameSetup.js'; import { initGameplay } from './gameplay.js'; -// ui.js загружен глобально +// ui.js загружен глобально и ожидает window.* переменных function parseJwtPayload(token) { try { @@ -23,8 +23,8 @@ function parseJwtPayload(token) { } document.addEventListener('DOMContentLoaded', () => { - const SERVER_URL = 'https://81.177.140.16:3200' //'http://127.0.0.1:3200'; - const API_BASE_URL = SERVER_URL; + // SERVER_URL и API_BASE_URL убраны. Socket.IO подключится к источнику загрузки страницы. + // auth.js будет использовать относительные пути для API запросов. const initialToken = localStorage.getItem('jwtToken'); let clientState = { @@ -33,7 +33,7 @@ document.addEventListener('DOMContentLoaded', () => { myUserId: null, isInGame: false, currentGameId: null, - currentGameState: null, // Будет объектом или null + currentGameState: null, myPlayerId: null, myCharacterKey: null, opponentCharacterKey: null, @@ -62,11 +62,12 @@ document.addEventListener('DOMContentLoaded', () => { } } - const socket = io(SERVER_URL, { + const socket = io({ // SERVER_URL удален отсюда autoConnect: false, auth: { token: localStorage.getItem('jwtToken') } }); + // --- DOM Элементы --- const authSection = document.getElementById('auth-section'); const loginForm = document.getElementById('login-form'); const registerForm = document.getElementById('register-form'); @@ -85,13 +86,13 @@ document.addEventListener('DOMContentLoaded', () => { const gameStatusMessage = document.getElementById('game-status-message'); const pvpCharacterRadios = document.querySelectorAll('input[name="pvp-character"]'); const gameWrapper = document.querySelector('.game-wrapper'); - const returnToMenuButton = document.getElementById('return-to-menu-button'); // Он же в ui.elements.gameOver.returnToMenuButton + const returnToMenuButton = document.getElementById('return-to-menu-button'); const turnTimerContainer = document.getElementById('turn-timer-container'); const turnTimerSpan = document.getElementById('turn-timer'); + // --- Функции обновления UI и состояния --- function updateGlobalWindowVariablesForUI() { - // console.log("[Main] Updating global window variables. currentGameState:", clientState.currentGameState ? JSON.parse(JSON.stringify(clientState.currentGameState)) : null); - window.gameState = clientState.currentGameState; // Может быть null + window.gameState = clientState.currentGameState; window.gameData = { playerBaseStats: clientState.playerBaseStatsServer, opponentBaseStats: clientState.opponentBaseStatsServer, @@ -99,18 +100,12 @@ document.addEventListener('DOMContentLoaded', () => { opponentAbilities: clientState.opponentAbilitiesServer }; window.myPlayerId = clientState.myPlayerId; - // window.GAME_CONFIG устанавливается при gameStarted/gameState из gameplay.js } function resetGameVariables() { console.log("[Main:resetGameVariables] Resetting game variables. State BEFORE:", JSON.parse(JSON.stringify(clientState))); clientState.currentGameId = null; - // ВАЖНО: currentGameState должен быть сброшен в состояние "нет игры" - // Либо null, либо объект, который ui.js интерпретирует как "нет игры" clientState.currentGameState = null; - // Можно также так, если ui.js лучше работает с объектом: - // clientState.currentGameState = { isGameOver: false, player: null, opponent: null, turnNumber: 0 }; - clientState.myPlayerId = null; clientState.myCharacterKey = null; clientState.opponentCharacterKey = null; @@ -118,9 +113,7 @@ document.addEventListener('DOMContentLoaded', () => { clientState.opponentBaseStatsServer = null; clientState.playerAbilitiesServer = null; clientState.opponentAbilitiesServer = null; - // clientState.isInGame будет установлено в вызывающей функции (showAuthScreen/showGameSelectionScreen) - - updateGlobalWindowVariablesForUI(); // Обновляем глобальные переменные СРАЗУ после сброса + updateGlobalWindowVariablesForUI(); console.log("[Main:resetGameVariables] Game variables reset. State AFTER:", JSON.parse(JSON.stringify(clientState))); } @@ -134,7 +127,6 @@ document.addEventListener('DOMContentLoaded', () => { if (gameOverScreenElement && !gameOverScreenElement.classList.contains(hiddenClass)) { gameOverScreenElement.classList.add(hiddenClass); - // Принудительно сбрасываем стили для анимации скрытия, если она есть gameOverScreenElement.style.opacity = '0'; if (modalContentElement) { modalContentElement.style.transform = 'scale(0.8) translateY(30px)'; @@ -144,31 +136,27 @@ document.addEventListener('DOMContentLoaded', () => { } else if (gameOverScreenElement) { console.log("[Main:explicitlyHideGameOverModal] Game Over screen was already hidden or not found."); } - if (messageElement) messageElement.textContent = ''; // Очищаем сообщение + if (messageElement) messageElement.textContent = ''; } else { console.warn("[Main:explicitlyHideGameOverModal] Cannot hide Game Over modal: gameUI or GAME_CONFIG not available."); } } - function showAuthScreen() { console.log("[Main:showAuthScreen] Showing Auth Screen. Resetting game state."); authSection.style.display = 'block'; userInfoDiv.style.display = 'none'; gameSetupDiv.style.display = 'none'; gameWrapper.style.display = 'none'; - - explicitlyHideGameOverModal(); // <-- ЯВНО СКРЫВАЕМ МОДАЛКУ - + explicitlyHideGameOverModal(); statusContainer.style.display = 'block'; - clientState.isInGame = false; // Важно - resetGameVariables(); // Сбрасываем все переменные предыдущей игры - + clientState.isInGame = false; + resetGameVariables(); if (turnTimerContainer) turnTimerContainer.style.display = 'none'; if (turnTimerSpan) turnTimerSpan.textContent = '--'; if(registerForm) registerForm.querySelector('button').disabled = false; if(loginForm) loginForm.querySelector('button').disabled = false; - if(logoutButton) logoutButton.disabled = true; // Кнопка Logout должна быть недоступна на экране логина + if(logoutButton) logoutButton.disabled = true; } function showGameSelectionScreen(username) { @@ -176,12 +164,10 @@ document.addEventListener('DOMContentLoaded', () => { authSection.style.display = 'none'; userInfoDiv.style.display = 'block'; if(loggedInUsernameSpan) loggedInUsernameSpan.textContent = username; - if(logoutButton) logoutButton.disabled = false; // Logout доступен + if(logoutButton) logoutButton.disabled = false; gameSetupDiv.style.display = 'block'; gameWrapper.style.display = 'none'; - - explicitlyHideGameOverModal(); // <-- ЯВНО СКРЫВАЕМ МОДАЛКУ - + explicitlyHideGameOverModal(); setGameStatusMessage("Выберите режим игры или присоединитесь к существующей."); statusContainer.style.display = 'block'; @@ -195,15 +181,11 @@ document.addEventListener('DOMContentLoaded', () => { if (gameIdInput) gameIdInput.value = ''; const elenaRadio = document.getElementById('char-elena'); if (elenaRadio) elenaRadio.checked = true; - - clientState.isInGame = false; // Важно - resetGameVariables(); // Сбрасываем все переменные предыдущей игры - + clientState.isInGame = false; + resetGameVariables(); if (turnTimerContainer) turnTimerContainer.style.display = 'none'; if (turnTimerSpan) turnTimerSpan.textContent = '--'; enableSetupButtons(); - // Убедимся, что кнопка "Вернуться в меню" на gameOver модалке (если она вдруг видима) активна, - // хотя сама модалка должна быть скрыта. if (window.gameUI?.uiElements?.gameOver?.returnToMenuButton) { window.gameUI.uiElements.gameOver.returnToMenuButton.disabled = false; } @@ -211,9 +193,6 @@ document.addEventListener('DOMContentLoaded', () => { function showGameScreen() { console.log("[Main:showGameScreen] Showing Game Screen."); - // Не нужно здесь вызывать explicitlyHideGameOverModal, так как если игра начинается, - // а модалка была видима, это ошибка логики где-то еще. - // GameStarted/GameState должно само приводить UI в порядок. authSection.style.display = 'none'; userInfoDiv.style.display = 'block'; if(logoutButton) logoutButton.disabled = false; @@ -221,13 +200,12 @@ document.addEventListener('DOMContentLoaded', () => { gameWrapper.style.display = 'flex'; setGameStatusMessage(""); statusContainer.style.display = 'none'; - clientState.isInGame = true; // Важно - updateGlobalWindowVariablesForUI(); // Обновляем перед тем, как UI начнет рендерить игровой экран + clientState.isInGame = true; + updateGlobalWindowVariablesForUI(); if (turnTimerContainer) turnTimerContainer.style.display = 'block'; if (turnTimerSpan) turnTimerSpan.textContent = '--'; } - function setAuthMessage(message, isError = false) { if (authMessage) { authMessage.textContent = message; @@ -259,9 +237,9 @@ document.addEventListener('DOMContentLoaded', () => { if(createPvPGameButton) createPvPGameButton.disabled = false; if(joinPvPGameButton) joinPvPGameButton.disabled = false; if(findRandomPvPGameButton) findRandomPvPGameButton.disabled = false; - // Кнопки в списке доступных игр управляются в gameSetup.js -> updateAvailableGamesList } + // --- Сборка зависимостей для модулей --- const dependencies = { socket, clientState, @@ -271,7 +249,7 @@ document.addEventListener('DOMContentLoaded', () => { showGameScreen, setAuthMessage, setGameStatusMessage, - resetGameVariables, // Передаем, чтобы другие модули могли вызвать при необходимости (хотя лучше избегать) + resetGameVariables, updateGlobalWindowVariablesForUI, disableSetupButtons, enableSetupButtons, @@ -279,16 +257,20 @@ document.addEventListener('DOMContentLoaded', () => { loginForm, registerForm, logoutButton, createAIGameButton, createPvPGameButton, joinPvPGameButton, findRandomPvPGameButton, gameIdInput, availableGamesDiv, - pvpCharacterRadios, returnToMenuButton, // returnToMenuButton из gameplay.js, но здесь тоже может быть полезен + pvpCharacterRadios, returnToMenuButton, } }, - API_BASE_URL: API_BASE_URL + // API_BASE_URL больше не передается, т.к. auth.js будет использовать относительные пути. + // Если auth.js все еще требует явного API_BASE_URL для случая, когда он не может + // сам определить window.location.origin, можно было бы передать: + // API_BASE_URL: window.location.origin }; initAuth(dependencies); initGameSetup(dependencies); initGameplay(dependencies); + // --- Обработчики событий Socket.IO --- socket.on('connect', () => { const currentToken = socket.auth.token || localStorage.getItem('jwtToken'); console.log('[Main:SocketConnect] Socket connected:', socket.id, 'Auth token sent:', !!currentToken); @@ -296,17 +278,12 @@ document.addEventListener('DOMContentLoaded', () => { if (clientState.isLoggedIn && clientState.myUserId) { console.log(`[Main:SocketConnect] Client state indicates logged in as ${clientState.loggedInUsername}. Requesting game state.`); if (authSection.style.display === 'block' || gameSetupDiv.style.display === 'block') { - // Если мы на экране логина или выбора игры, но считаем себя залогиненными, - // покажем сообщение о восстановлении. setGameStatusMessage("Восстановление игровой сессии..."); } - // Не очищаем здесь resetGameVariables, так как gameplay.js ожидает, что clientState может содержать - // предыдущие данные, которые он перезапишет при получении gameState или gameStarted. - // Если придет gameNotFound, то там уже будет reset. socket.emit('requestGameState'); } else { console.log('[Main:SocketConnect] Client state indicates NOT logged in. Showing auth screen.'); - showAuthScreen(); // Убеждаемся, что все сброшено и показан экран логина + showAuthScreen(); setAuthMessage("Пожалуйста, войдите или зарегистрируйтесь."); } }); @@ -325,19 +302,20 @@ document.addEventListener('DOMContentLoaded', () => { clientState.loggedInUsername = ''; clientState.myUserId = null; if (socket.auth) socket.auth.token = null; - - showAuthScreen(); // Это вызовет resetGameVariables и скроет модалку + showAuthScreen(); setAuthMessage("Ошибка аутентификации. Пожалуйста, войдите снова.", true); } else { + // Общая ошибка подключения + let currentScreenMessageFunc = setAuthMessage; if (clientState.isLoggedIn && clientState.isInGame) { - setGameStatusMessage(`Ошибка подключения: ${err.message}. Попытка переподключения...`, true); + currentScreenMessageFunc = setGameStatusMessage; } else if (clientState.isLoggedIn) { - setGameStatusMessage(`Ошибка подключения к серверу: ${err.message}. Попытка переподключения...`, true); - } else { - setAuthMessage(`Ошибка подключения к серверу: ${err.message}. Попытка переподключения...`, true); - if (authSection.style.display !== 'block') { - showAuthScreen(); // Если не на экране логина, но ошибка не auth, все равно показываем его - } + currentScreenMessageFunc = setGameStatusMessage; // Или setAuthMessage, если statusContainer не виден + } + + currentScreenMessageFunc(`Ошибка подключения: ${err.message}. Попытка переподключения...`, true); + if (authSection.style.display !== 'block' && !clientState.isLoggedIn) { + showAuthScreen(); // Если не на экране логина и не залогинен, показываем его } } if (turnTimerSpan) turnTimerSpan.textContent = 'Ошибка'; @@ -345,33 +323,24 @@ document.addEventListener('DOMContentLoaded', () => { socket.on('disconnect', (reason) => { console.warn('[Main:SocketDisconnect] Disconnected from server:', reason); - // Сообщения в зависимости от текущего состояния + let messageFunc = setAuthMessage; if (clientState.isInGame) { - setGameStatusMessage(`Потеряно соединение: ${reason}. Попытка переподключения...`, true); - } else if (clientState.isLoggedIn) { - // Уже должен быть на экране выбора игры или восстановления, setGameStatusMessage там уместно - if (gameSetupDiv.style.display === 'block') { - setGameStatusMessage(`Потеряно соединение с сервером: ${reason}. Попытка переподключения...`, true); - } else { - // Если где-то между экранами, но залогинен - setAuthMessage(`Потеряно соединение: ${reason}. Попытка переподключения...`, true); // Используем authMessage для общего случая - } - } else { - setAuthMessage(`Потеряно соединение с сервером: ${reason}. Попытка переподключения...`, true); + messageFunc = setGameStatusMessage; + } else if (clientState.isLoggedIn && gameSetupDiv.style.display === 'block') { + messageFunc = setGameStatusMessage; } + messageFunc(`Потеряно соединение: ${reason}. Попытка переподключения...`, true); if (turnTimerSpan) turnTimerSpan.textContent = 'Откл.'; - // Не сбрасываем clientState.isLoggedIn здесь, чтобы socket.connect мог попытаться восстановить сессию }); socket.on('gameError', (data) => { console.error('[Main:SocketGameError] Received gameError from server:', data.message); if (clientState.isInGame && window.gameUI?.addToLog) { window.gameUI.addToLog(`❌ Ошибка сервера: ${data.message}`, 'system'); - // Можно добавить setGameStatusMessage и здесь, если ошибка критическая для игры - } else if (clientState.isLoggedIn) { // На экране выбора игры + } else if (clientState.isLoggedIn) { setGameStatusMessage(`❌ Ошибка: ${data.message}`, true); - enableSetupButtons(); // Разблокировать кнопки, если ошибка при создании/присоединении - } else { // На экране логина + enableSetupButtons(); + } else { setAuthMessage(`❌ Ошибка: ${data.message}`, true); if(registerForm) registerForm.querySelector('button').disabled = false; if(loginForm) loginForm.querySelector('button').disabled = false; @@ -380,27 +349,22 @@ document.addEventListener('DOMContentLoaded', () => { socket.on('gameNotFound', (data) => { console.log('[Main:SocketGameNotFound] Game not found/ended after request:', data?.message); - - // Важно: gameNotFound означает, что активной игры нет. - // Сбрасываем состояние и показываем экран выбора игры, если залогинены. - clientState.isInGame = false; // Явно выходим из игры - resetGameVariables(); // Полный сброс игровых переменных - explicitlyHideGameOverModal(); // Убеждаемся, что модалка скрыта - + clientState.isInGame = false; + resetGameVariables(); + explicitlyHideGameOverModal(); if (turnTimerContainer) turnTimerContainer.style.display = 'none'; if (turnTimerSpan) turnTimerSpan.textContent = '--'; if (clientState.isLoggedIn && clientState.myUserId) { - showGameSelectionScreen(clientState.loggedInUsername); // Переходим на выбор игры (он вызовет resetGameVariables еще раз, но это не страшно) + showGameSelectionScreen(clientState.loggedInUsername); setGameStatusMessage(data?.message || "Активная игровая сессия не найдена. Выберите новую игру."); } else { - // Если по какой-то причине мы не залогинены (например, токен истек и connect_error сбросил isLoggedIn) showAuthScreen(); setAuthMessage(data?.message || "Пожалуйста, войдите."); } }); - // Инициализация UI + // --- Инициализация UI --- authSection.style.display = 'none'; gameSetupDiv.style.display = 'none'; gameWrapper.style.display = 'none'; @@ -408,10 +372,10 @@ document.addEventListener('DOMContentLoaded', () => { statusContainer.style.display = 'block'; if (clientState.isLoggedIn) { - setGameStatusMessage("Подключение и восстановление сессии..."); // Или setAuthMessage, если statusContainer не виден сразу + setGameStatusMessage("Подключение и восстановление сессии..."); } else { setAuthMessage("Подключение к серверу..."); } - socket.connect(); + socket.connect(); // Начинаем подключение к серверу }); \ No newline at end of file