From 63e42fbd31aecaf7a5ec41c6ece211c1e1b49de9 Mon Sep 17 00:00:00 2001 From: Itms Date: Sun, 30 Aug 2015 17:47:18 +0000 Subject: [PATCH] Display which player(s) are OOS when it happens. Also fix some encoding issues with file paths. Patch by elexis, fixes #3293. This was SVN commit r16963. --- source/network/NetClient.cpp | 2 +- source/network/NetMessages.h | 3 ++ source/network/NetServer.cpp | 2 +- source/network/NetTurnManager.cpp | 68 +++++++++++++++++++------------ source/network/NetTurnManager.h | 12 ++++-- 5 files changed, 57 insertions(+), 30 deletions(-) diff --git a/source/network/NetClient.cpp b/source/network/NetClient.cpp index 358c1a981f..c5f9c7d7c7 100644 --- a/source/network/NetClient.cpp +++ b/source/network/NetClient.cpp @@ -643,7 +643,7 @@ bool CNetClient::OnInGame(void *context, CFsmEvent* event) else if (message->GetType() == NMT_SYNC_ERROR) { CSyncErrorMessage* syncMessage = static_cast (message); - client->m_ClientTurnManager->OnSyncError(syncMessage->m_Turn, syncMessage->m_HashExpected); + client->m_ClientTurnManager->OnSyncError(syncMessage->m_Turn, syncMessage->m_HashExpected, syncMessage->m_PlayerNames); } else if (message->GetType() == NMT_END_COMMAND_BATCH) { diff --git a/source/network/NetMessages.h b/source/network/NetMessages.h index 13a8aaaff0..ca78bfaa59 100644 --- a/source/network/NetMessages.h +++ b/source/network/NetMessages.h @@ -181,6 +181,9 @@ END_NMT_CLASS() START_NMT_CLASS_(SyncError, NMT_SYNC_ERROR) NMT_FIELD_INT(m_Turn, u32, 4) NMT_FIELD(CStr, m_HashExpected) + NMT_START_ARRAY(m_PlayerNames) + NMT_FIELD(CStrW, m_Name) + NMT_END_ARRAY() END_NMT_CLASS() END_NMTS() diff --git a/source/network/NetServer.cpp b/source/network/NetServer.cpp index 94fa777601..d88c8f3dab 100644 --- a/source/network/NetServer.cpp +++ b/source/network/NetServer.cpp @@ -894,7 +894,7 @@ bool CNetServerWorker::OnInGame(void* context, CFsmEvent* event) else if (message->GetType() == (uint)NMT_SYNC_CHECK) { CSyncCheckMessage* syncMessage = static_cast (message); - server.m_ServerTurnManager->NotifyFinishedClientUpdate(session->GetHostID(), syncMessage->m_Turn, syncMessage->m_Hash); + server.m_ServerTurnManager->NotifyFinishedClientUpdate(session->GetHostID(), session->GetUserName(), syncMessage->m_Turn, syncMessage->m_Hash); } else if (message->GetType() == (uint)NMT_END_COMMAND_BATCH) { diff --git a/source/network/NetTurnManager.cpp b/source/network/NetTurnManager.cpp index aa60c5ca73..60d4834d88 100644 --- a/source/network/NetTurnManager.cpp +++ b/source/network/NetTurnManager.cpp @@ -18,6 +18,7 @@ #include "precompiled.h" #include "NetTurnManager.h" +#include "NetMessage.h" #include "network/NetServer.h" #include "network/NetClient.h" @@ -218,7 +219,7 @@ bool CNetTurnManager::UpdateFastForward() return true; } -void CNetTurnManager::OnSyncError(u32 turn, const std::string& expectedHash) +void CNetTurnManager::OnSyncError(u32 turn, const CStr& expectedHash, std::vector& playerNames) { NETTURN_LOG((L"OnSyncError(%d, %hs)\n", turn, Hexify(expectedHash).c_str())); @@ -238,31 +239,27 @@ void CNetTurnManager::OnSyncError(u32 turn, const std::string& expectedHash) hash = Hexify(hash); const std::string& expectedHashHex = Hexify(expectedHash); - DisplayOOSError(turn, hash, expectedHashHex, false, &path); + DisplayOOSError(turn, hash, expectedHashHex, false, &playerNames, &path); } -void CNetTurnManager::DisplayOOSError(u32 turn, const std::string& hash, const std::string& expectedHash, bool isReplay, OsPath* path = NULL) +void CNetTurnManager::DisplayOOSError(u32 turn, const CStr& hash, const CStr& expectedHash, bool isReplay, std::vector* playerNames = NULL, OsPath* path = NULL) { m_HasSyncError = true; std::stringstream msg; - msg << "Out of sync on turn " << turn << ": expected hash " << expectedHash << "\n"; + msg << "Out of sync on turn " << turn; - if (expectedHash != hash || m_CurrentTurn != turn) - msg << "\nCurrent state: turn " << m_CurrentTurn << ", hash " << hash << "\n\n"; + if (playerNames) + for (size_t i = 0; i < playerNames->size(); ++i) + msg << (i == 0 ? "\nPlayers: " : ", ") << utf8_from_wstring((*playerNames)[i].m_Name); if (isReplay) - msg << "\nThe current game state is different from the original game state.\n\n"; + msg << "\n\n" << "The current game state is different from the original game state."; else - { - if (expectedHash == hash) - msg << "Your game state is identical to the hosts game state.\n\n"; - else - msg << "Your game state is different from the hosts game state.\n\n"; - } + msg << "\n\n" << "Your game state is " << (expectedHash == hash ? "identical to" : "different from") << " the hosts game state."; if (path) - msg << "Dumping current state to " << utf8_from_wstring(OsPath(*path).string()); + msg << "\n\n" << "Dumping current state to " << OsString(OsPath(*path)); LOGERROR("%s", msg.str()); @@ -429,6 +426,10 @@ void CNetClientTurnManager::NotifyFinishedUpdate(u32 turn) m_Replay.Hash(hash, quick); + // Don't send the hash if OOS + if (m_HasSyncError) + return; + // Send message to the server CSyncCheckMessage msg; msg.m_Turn = turn; @@ -520,7 +521,7 @@ void CNetReplayTurnManager::NotifyFinishedUpdate(u32 turn) debug_printf("Executing turn %d of %d\n", turn, m_FinalReplayTurn); DoTurn(turn); - // Compare hash if it exists in the replay and if we didn't have an oos already + // Compare hash if it exists in the replay and if we didn't have an OOS already if (m_HasSyncError || m_ReplayHash.find(turn) == m_ReplayHash.end()) return; @@ -554,7 +555,7 @@ void CNetReplayTurnManager::DoTurn(u32 turn) } CNetServerTurnManager::CNetServerTurnManager(CNetServerWorker& server) : - m_NetServer(server), m_ReadyTurn(1), m_TurnLength(DEFAULT_TURN_LENGTH_MP) + m_NetServer(server), m_ReadyTurn(1), m_TurnLength(DEFAULT_TURN_LENGTH_MP), m_HasSyncError(false) { // The first turn we will actually execute is number 2, // so store dummy values into the saved lengths list @@ -603,12 +604,17 @@ void CNetServerTurnManager::CheckClientsReady() m_SavedTurnLengths.push_back(m_TurnLength); } -void CNetServerTurnManager::NotifyFinishedClientUpdate(int client, u32 turn, const std::string& hash) +void CNetServerTurnManager::NotifyFinishedClientUpdate(int client, const CStrW& playername, u32 turn, const CStr& hash) { // Clients must advance one turn at a time ENSURE(turn == m_ClientsSimulated[client] + 1); m_ClientsSimulated[client] = turn; + // Check for OOS only if in sync + if (m_HasSyncError) + return; + + m_ClientPlayernames[client] = playername; m_ClientStateHashes[turn][client] = hash; // Find the newest turn which we know all clients have simulated @@ -628,22 +634,34 @@ void CNetServerTurnManager::NotifyFinishedClientUpdate(int client, u32 turn, con // Assume the host is correct (maybe we should choose the most common instead to help debugging) std::string expected = it->second.begin()->second; + // Find all players that are OOS on that turn + std::vector OOSPlayerNames; for (std::map::iterator cit = it->second.begin(); cit != it->second.end(); ++cit) { NETTURN_LOG((L"sync check %d: %d = %hs\n", it->first, cit->first, Hexify(cit->second).c_str())); if (cit->second != expected) { // Oh no, out of sync - - // Tell everyone about it - CSyncErrorMessage msg; - msg.m_Turn = it->first; - msg.m_HashExpected = expected; - m_NetServer.Broadcast(&msg); - - break; + m_HasSyncError = true; + OOSPlayerNames.push_back(m_ClientPlayernames[cit->first]); } } + + // Tell everyone about it + if (m_HasSyncError) + { + CSyncErrorMessage msg; + msg.m_Turn = it->first; + msg.m_HashExpected = expected; + for (const CStrW& playername : OOSPlayerNames) + { + CSyncErrorMessage::S_m_PlayerNames h; + h.m_Name = playername; + msg.m_PlayerNames.push_back(h); + } + m_NetServer.Broadcast(&msg); + break; + } } // Delete the saved hashes for all turns that we've already verified diff --git a/source/network/NetTurnManager.h b/source/network/NetTurnManager.h index 33cd8a4408..ea136db30d 100644 --- a/source/network/NetTurnManager.h +++ b/source/network/NetTurnManager.h @@ -20,6 +20,7 @@ #include "simulation2/helpers/SimulationCommand.h" #include "lib/os_path.h" +#include "NetMessage.h" #include #include @@ -107,12 +108,12 @@ public: /** * Called when there has been an out-of-sync error. */ - virtual void OnSyncError(u32 turn, const std::string& expectedHash); + virtual void OnSyncError(u32 turn, const CStr& expectedHash, std::vector& playerNames); /** * Shows a message box when an out of sync error has been detected in the session or visual replay. */ - virtual void DisplayOOSError(u32 turn, const std::string& hash, const std::string& expectedHash, bool isReplay, OsPath* path); + virtual void DisplayOOSError(u32 turn, const CStr& hash, const CStr& expectedHash, bool isReplay, std::vector* playerNames, OsPath* path); /** * Called by simulation code, to add a new command to be distributed to all clients and executed soon. @@ -293,7 +294,7 @@ public: void NotifyFinishedClientCommands(int client, u32 turn); - void NotifyFinishedClientUpdate(int client, u32 turn, const std::string& hash); + void NotifyFinishedClientUpdate(int client, const CStrW& playername, u32 turn, const CStr& hash); /** * Inform the turn manager of a new client who will be sending commands. @@ -336,6 +337,9 @@ protected: // Map of turn -> {Client ID -> state hash}; old indexes <= min(m_ClientsSimulated) are deleted std::map > m_ClientStateHashes; + // Map of client ID -> playername + std::map m_ClientPlayernames; + // Current turn length u32 m_TurnLength; @@ -343,6 +347,8 @@ protected: std::vector m_SavedTurnLengths; CNetServerWorker& m_NetServer; + + bool m_HasSyncError; }; #endif // INCLUDED_NETTURNMANAGER