Ticket #3293: t3293_show_who_is_oos_v1.2.patch
File t3293_show_who_is_oos_v1.2.patch, 10.9 KB (added by , 9 years ago) |
---|
-
source/network/NetClient.cpp
641 641 client->m_ClientTurnManager->OnSimulationMessage(simMessage); 642 642 } 643 643 else if (message->GetType() == NMT_SYNC_ERROR) 644 644 { 645 645 CSyncErrorMessage* syncMessage = static_cast<CSyncErrorMessage*> (message); 646 client->m_ClientTurnManager->OnSyncError(syncMessage->m_Turn, syncMessage->m_HashExpected );646 client->m_ClientTurnManager->OnSyncError(syncMessage->m_Turn, syncMessage->m_HashExpected, syncMessage->m_PlayerNames); 647 647 } 648 648 else if (message->GetType() == NMT_END_COMMAND_BATCH) 649 649 { 650 650 CEndCommandBatchMessage* endMessage = static_cast<CEndCommandBatchMessage*> (message); 651 651 client->m_ClientTurnManager->FinishedAllCommands(endMessage->m_Turn, endMessage->m_TurnLength); -
source/network/NetMessages.h
179 179 END_NMT_CLASS() 180 180 181 181 START_NMT_CLASS_(SyncError, NMT_SYNC_ERROR) 182 182 NMT_FIELD_INT(m_Turn, u32, 4) 183 183 NMT_FIELD(CStr, m_HashExpected) 184 NMT_START_ARRAY(m_PlayerNames) 185 NMT_FIELD(CStrW, m_Name) 186 NMT_END_ARRAY() 184 187 END_NMT_CLASS() 185 188 186 189 END_NMTS() 187 190 188 191 #else -
source/network/NetServer.cpp
878 878 // TODO: we shouldn't send the message back to the client that first sent it 879 879 } 880 880 else if (message->GetType() == (uint)NMT_SYNC_CHECK) 881 881 { 882 882 CSyncCheckMessage* syncMessage = static_cast<CSyncCheckMessage*> (message); 883 server.m_ServerTurnManager->NotifyFinishedClientUpdate(session->GetHostID(), s yncMessage->m_Turn, syncMessage->m_Hash);883 server.m_ServerTurnManager->NotifyFinishedClientUpdate(session->GetHostID(), session->GetUserName(), syncMessage->m_Turn, syncMessage->m_Hash); 884 884 } 885 885 else if (message->GetType() == (uint)NMT_END_COMMAND_BATCH) 886 886 { 887 887 CEndCommandBatchMessage* endMessage = static_cast<CEndCommandBatchMessage*> (message); 888 888 server.m_ServerTurnManager->NotifyFinishedClientCommands(session->GetHostID(), endMessage->m_Turn); -
source/network/NetTurnManager.cpp
16 16 */ 17 17 18 18 #include "precompiled.h" 19 19 20 20 #include "NetTurnManager.h" 21 #include "NetMessage.h" 21 22 22 23 #include "network/NetServer.h" 23 24 #include "network/NetClient.h" 24 25 #include "network/NetMessage.h" 25 26 … … 216 217 } 217 218 218 219 return true; 219 220 } 220 221 221 void CNetTurnManager::OnSyncError(u32 turn, const std::string& expectedHash)222 void CNetTurnManager::OnSyncError(u32 turn, const CStr& expectedHash, std::vector<CSyncErrorMessage::S_m_PlayerNames>& playerNames) 222 223 { 223 224 NETTURN_LOG((L"OnSyncError(%d, %hs)\n", turn, Hexify(expectedHash).c_str())); 224 225 225 226 // Only complain the first time 226 227 if (m_HasSyncError) … … 236 237 file.close(); 237 238 238 239 hash = Hexify(hash); 239 240 const std::string& expectedHashHex = Hexify(expectedHash); 240 241 241 DisplayOOSError(turn, hash, expectedHashHex, false, &p ath);242 DisplayOOSError(turn, hash, expectedHashHex, false, &playerNames, &path); 242 243 } 243 244 244 void CNetTurnManager::DisplayOOSError(u32 turn, const std::string& hash, const std::string& expectedHash, bool isReplay, OsPath* path = NULL)245 void CNetTurnManager::DisplayOOSError(u32 turn, const CStr& hash, const CStr& expectedHash, bool isReplay, std::vector<CSyncErrorMessage::S_m_PlayerNames>* playerNames = NULL, OsPath* path = NULL) 245 246 { 246 247 m_HasSyncError = true; 247 248 248 249 std::stringstream msg; 249 msg << "Out of sync on turn " << turn << ": expected hash " << expectedHash << "\n";250 msg << "Out of sync on turn " << turn; 250 251 251 if (expectedHash != hash || m_CurrentTurn != turn) 252 msg << "\nCurrent state: turn " << m_CurrentTurn << ", hash " << hash << "\n\n"; 252 if (playerNames) 253 for (size_t i = 0; i < playerNames->size(); ++i) 254 msg << (i == 0 ? "\nPlayers: " : ", ") << utf8_from_wstring((*playerNames)[i].m_Name); 253 255 254 256 if (isReplay) 255 msg << "\n The current game state is different from the original game state.\n\n";257 msg << "\n\n" << "The current game state is different from the original game state."; 256 258 else 257 { 258 if (expectedHash == hash) 259 msg << "Your game state is identical to the hosts game state.\n\n"; 260 else 261 msg << "Your game state is different from the hosts game state.\n\n"; 262 } 259 msg << "\n\n" << "Your game state is " << (expectedHash == hash ? "identical to" : "different from") << " the hosts game state."; 263 260 264 261 if (path) 265 msg << " Dumping current state to " << utf8_from_wstring(OsPath(*path).string());262 msg << "\n\n" << "Dumping current state to " << OsString(OsPath(*path)); 266 263 267 264 LOGERROR("%s", msg.str()); 268 265 269 266 if (g_GUI) 270 267 g_GUI->DisplayMessageBox(600, 350, L"Sync error", wstring_from_utf8(msg.str())); … … 427 424 428 425 NETTURN_LOG((L"NotifyFinishedUpdate(%d, %hs)\n", turn, Hexify(hash).c_str())); 429 426 430 427 m_Replay.Hash(hash, quick); 431 428 429 // Don't send the hash if OOS 430 if (m_HasSyncError) 431 return; 432 432 433 // Send message to the server 433 434 CSyncCheckMessage msg; 434 435 msg.m_Turn = turn; 435 436 msg.m_Hash = hash; 436 437 m_NetClient.SendMessage(&msg); … … 518 519 return; 519 520 520 521 debug_printf("Executing turn %d of %d\n", turn, m_FinalReplayTurn); 521 522 DoTurn(turn); 522 523 523 // Compare hash if it exists in the replay and if we didn't have an oosalready524 // Compare hash if it exists in the replay and if we didn't have an OOS already 524 525 if (m_HasSyncError || m_ReplayHash.find(turn) == m_ReplayHash.end()) 525 526 return; 526 527 527 528 std::string expectedHash = m_ReplayHash[turn].first; 528 529 bool quickHash = m_ReplayHash[turn].second; … … 552 553 if (turn == m_FinalReplayTurn) 553 554 g_GUI->SendEventToAll("ReplayFinished"); 554 555 } 555 556 556 557 CNetServerTurnManager::CNetServerTurnManager(CNetServerWorker& server) : 557 m_NetServer(server), m_ReadyTurn(1), m_TurnLength(DEFAULT_TURN_LENGTH_MP) 558 m_NetServer(server), m_ReadyTurn(1), m_TurnLength(DEFAULT_TURN_LENGTH_MP), m_HasSyncError(false) 558 559 { 559 560 // The first turn we will actually execute is number 2, 560 561 // so store dummy values into the saved lengths list 561 562 m_SavedTurnLengths.push_back(0); 562 563 m_SavedTurnLengths.push_back(0); … … 601 602 // Save the turn length in case it's needed later 602 603 ENSURE(m_SavedTurnLengths.size() == m_ReadyTurn); 603 604 m_SavedTurnLengths.push_back(m_TurnLength); 604 605 } 605 606 606 void CNetServerTurnManager::NotifyFinishedClientUpdate(int client, u32 turn, const std::string& hash)607 void CNetServerTurnManager::NotifyFinishedClientUpdate(int client, const CStrW& playername, u32 turn, const CStr& hash) 607 608 { 608 609 // Clients must advance one turn at a time 609 610 ENSURE(turn == m_ClientsSimulated[client] + 1); 610 611 m_ClientsSimulated[client] = turn; 611 612 613 // Check for OOS only if in sync 614 if (m_HasSyncError) 615 return; 616 617 m_ClientPlayernames[client] = playername; 612 618 m_ClientStateHashes[turn][client] = hash; 613 619 614 620 // Find the newest turn which we know all clients have simulated 615 621 u32 newest = std::numeric_limits<u32>::max(); 616 622 for (std::map<int, u32>::iterator it = m_ClientsSimulated.begin(); it != m_ClientsSimulated.end(); ++it) … … 626 632 break; 627 633 628 634 // Assume the host is correct (maybe we should choose the most common instead to help debugging) 629 635 std::string expected = it->second.begin()->second; 630 636 637 // Find all players that are OOS on that turn 638 std::vector<CStrW> OOSPlayerNames; 631 639 for (std::map<int, std::string>::iterator cit = it->second.begin(); cit != it->second.end(); ++cit) 632 640 { 633 641 NETTURN_LOG((L"sync check %d: %d = %hs\n", it->first, cit->first, Hexify(cit->second).c_str())); 634 642 if (cit->second != expected) 635 643 { 636 644 // Oh no, out of sync 645 m_HasSyncError = true; 646 OOSPlayerNames.push_back(m_ClientPlayernames[cit->first]); 647 } 648 } 637 649 638 // Tell everyone about it 639 CSyncErrorMessage msg; 640 msg.m_Turn = it->first; 641 msg.m_HashExpected = expected; 642 m_NetServer.Broadcast(&msg); 643 644 break; 650 // Tell everyone about it 651 if (m_HasSyncError) 652 { 653 CSyncErrorMessage msg; 654 msg.m_Turn = it->first; 655 msg.m_HashExpected = expected; 656 for (const CStrW& playername : OOSPlayerNames) 657 { 658 CSyncErrorMessage::S_m_PlayerNames h; 659 h.m_Name = playername; 660 msg.m_PlayerNames.push_back(h); 645 661 } 662 m_NetServer.Broadcast(&msg); 663 break; 646 664 } 647 665 } 648 666 649 667 // Delete the saved hashes for all turns that we've already verified 650 668 m_ClientStateHashes.erase(m_ClientStateHashes.begin(), m_ClientStateHashes.lower_bound(newest+1)); -
source/network/NetTurnManager.h
18 18 #ifndef INCLUDED_NETTURNMANAGER 19 19 #define INCLUDED_NETTURNMANAGER 20 20 21 21 #include "simulation2/helpers/SimulationCommand.h" 22 22 #include "lib/os_path.h" 23 #include "NetMessage.h" 23 24 24 25 #include <list> 25 26 #include <map> 26 27 #include <vector> 27 28 … … 105 106 virtual void OnSimulationMessage(CSimulationMessage* msg) = 0; 106 107 107 108 /** 108 109 * Called when there has been an out-of-sync error. 109 110 */ 110 virtual void OnSyncError(u32 turn, const std::string& expectedHash);111 virtual void OnSyncError(u32 turn, const CStr& expectedHash, std::vector<CSyncErrorMessage::S_m_PlayerNames>& playerNames); 111 112 112 113 /** 113 114 * Shows a message box when an out of sync error has been detected in the session or visual replay. 114 115 */ 115 virtual void DisplayOOSError(u32 turn, const std::string& hash, const std::string& expectedHash, bool isReplay, OsPath* path);116 virtual void DisplayOOSError(u32 turn, const CStr& hash, const CStr& expectedHash, bool isReplay, std::vector<CSyncErrorMessage::S_m_PlayerNames>* playerNames, OsPath* path); 116 117 117 118 /** 118 119 * Called by simulation code, to add a new command to be distributed to all clients and executed soon. 119 120 */ 120 121 virtual void PostCommand(JS::HandleValue data) = 0; … … 291 292 public: 292 293 CNetServerTurnManager(CNetServerWorker& server); 293 294 294 295 void NotifyFinishedClientCommands(int client, u32 turn); 295 296 296 void NotifyFinishedClientUpdate(int client, u32 turn, const std::string& hash);297 void NotifyFinishedClientUpdate(int client, const CStrW& playername, u32 turn, const CStr& hash); 297 298 298 299 /** 299 300 * Inform the turn manager of a new client who will be sending commands. 300 301 */ 301 302 void InitialiseClient(int client, u32 turn); … … 334 335 std::map<int, u32> m_ClientsSimulated; 335 336 336 337 // Map of turn -> {Client ID -> state hash}; old indexes <= min(m_ClientsSimulated) are deleted 337 338 std::map<u32, std::map<int, std::string> > m_ClientStateHashes; 338 339 340 // Map of client ID -> playername 341 std::map<u32, CStrW> m_ClientPlayernames; 342 339 343 // Current turn length 340 344 u32 m_TurnLength; 341 345 342 346 // Turn lengths for all previously executed turns 343 347 std::vector<u32> m_SavedTurnLengths; 344 348 345 349 CNetServerWorker& m_NetServer; 350 351 bool m_HasSyncError; 346 352 }; 347 353 348 354 #endif // INCLUDED_NETTURNMANAGER