diff --git a/src/game/game_handler.cpp b/src/game/game_handler.cpp index 963ac33d..448fdcc4 100644 --- a/src/game/game_handler.cpp +++ b/src/game/game_handler.cpp @@ -253,6 +253,13 @@ void GameHandler::update(float deltaTime) { auto socketEnd = std::chrono::high_resolution_clock::now(); socketTime += std::chrono::duration(socketEnd - socketStart).count(); + // Detect server-side disconnect (socket closed during update) + if (socket && !socket->isConnected() && state != WorldState::DISCONNECTED) { + LOG_WARNING("Server closed connection in state: ", worldStateName(state)); + disconnect(); + return; + } + // Post-gate visibility: determine whether server goes silent or closes after Warden requirement. if (wardenGateSeen_ && socket && socket->isConnected()) { wardenGateElapsed_ += deltaTime; @@ -2281,13 +2288,12 @@ void GameHandler::handleWardenData(network::Packet& packet) { break; } - // SHA1(seed + moduleImage) — the server verifies this against its own copy + // SHA1(seed) fallback — wrong answer but sends a response to avoid silent hang. + // Correct answer requires executing the Warden module via emulator (not yet functional). + // Server will likely disconnect, but GameHandler::update() now detects that gracefully. { - std::vector hashInput; - hashInput.insert(hashInput.end(), seed.begin(), seed.end()); - hashInput.insert(hashInput.end(), wardenModuleData_.begin(), wardenModuleData_.end()); - auto hash = auth::Crypto::sha1(hashInput); - LOG_INFO("Warden: SHA1 fallback hash over ", hashInput.size(), " bytes (seed+module)"); + auto hash = auth::Crypto::sha1(seed); + LOG_WARNING("Warden: Sending SHA1(seed) fallback — server will likely reject"); std::vector resp; resp.push_back(0x04); resp.insert(resp.end(), hash.begin(), hash.end()); diff --git a/src/ui/character_screen.cpp b/src/ui/character_screen.cpp index e6f92a0a..74bd25ca 100644 --- a/src/ui/character_screen.cpp +++ b/src/ui/character_screen.cpp @@ -59,6 +59,18 @@ void CharacterScreen::render(game::GameHandler& gameHandler) { // Get character list const auto& characters = gameHandler.getCharacters(); + // Handle disconnected state (e.g. Warden kicked us) + if (gameHandler.getState() == game::WorldState::DISCONNECTED || + gameHandler.getState() == game::WorldState::FAILED) { + ImGui::TextColored(ImVec4(1.0f, 0.4f, 0.4f, 1.0f), "Disconnected from server."); + ImGui::TextWrapped("The server closed the connection. This may be caused by " + "anti-cheat (Warden) verification failure."); + ImGui::Spacing(); + if (ImGui::Button("Back", ImVec2(120, 36))) { if (onBack) onBack(); } + ImGui::End(); + return; + } + // Request character list if not available. // Also show a loading state while CHAR_LIST_REQUESTED is in-flight (characters may be cleared to avoid stale UI). if (characters.empty() &&