Ticket #3293: t3293_show_who_is_oos_v1.1_16876.patch
File t3293_show_who_is_oos_v1.1_16876.patch, 11.1 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 std::string& 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 std::string& hash, const std::string& expectedHash, bool isReplay, std::vector<CSyncErrorMessage::S_m_Playernames>* playerNames = NULL, OsPath* path = NULL) 245 246 { 246 247 m_HasSyncError = true; 247 248 248 std:: stringstream msg;249 msg << "Out of sync on turn " << turn << ": expected hash " << expectedHash <<"\n";249 std::wstringstream msg; 250 msg << L"Out of sync on turn " << turn << L"\n"; 250 251 251 if (expectedHash != hash || m_CurrentTurn != turn) 252 msg << "\nCurrent state: turn " << m_CurrentTurn << ", hash " << hash << "\n\n"; 252 if (playerNames) 253 { 254 bool first = true; 255 msg << L"Players:"; 256 for (const CSyncErrorMessage::S_m_Playernames& playername : *playerNames) 257 { 258 if (first) 259 first = false; 260 else 261 msg << L","; 262 msg << L" " << playername.m_Name; 263 } 264 msg << L"\n"; 265 } 253 266 254 267 if (isReplay) 255 msg << "\nThe current game state is different from the original game state.\n\n";268 msg << L"\nThe current game state is different from the original game state.\n\n"; 256 269 else 257 270 { 258 271 if (expectedHash == hash) 259 msg << "Your game state is identical to the hosts game state.\n\n";272 msg << L"Your game state is identical to the hosts game state.\n\n"; 260 273 else 261 msg << "Your game state is different from the hosts game state.\n\n";274 msg << L"Your game state is different from the hosts game state.\n\n"; 262 275 } 263 276 264 277 if (path) 265 msg << "Dumping current state to " << utf8_from_wstring(OsPath(*path).string());278 msg << L"Dumping current state to " << OsPath(*path).string(); 266 279 267 LOGERROR("%s", msg.str());280 LOGERROR("%s", utf8_from_wstring(msg.str())); 268 281 269 282 if (g_GUI) 270 g_GUI->DisplayMessageBox(600, 350, L"Sync error", wstring_from_utf8(msg.str()));283 g_GUI->DisplayMessageBox(600, 350, L"Sync error", msg.str()); 271 284 } 272 285 273 286 void CNetTurnManager::Interpolate(float simFrameLength, float realFrameLength) 274 287 { 275 288 // TODO: using m_TurnLength might be a bit dodgy when length changes - maybe … … 428 441 429 442 NETTURN_LOG((L"NotifyFinishedUpdate(%d, %hs)\n", turn, Hexify(hash).c_str())); 430 443 431 444 m_Replay.Hash(hash, quick); 432 445 433 // Send message to the server 434 CSyncCheckMessage msg; 435 msg.m_Turn = turn; 436 msg.m_Hash = hash; 437 m_NetClient.SendMessage(&msg); 446 if (!m_HasSyncError) 447 { 448 // Send message to the server 449 CSyncCheckMessage msg; 450 msg.m_Turn = turn; 451 msg.m_Hash = hash; 452 m_NetClient.SendMessage(&msg); 453 } 438 454 } 439 455 440 456 void CNetClientTurnManager::OnDestroyConnection() 441 457 { 442 458 NotifyFinishedOwnCommands(m_CurrentTurn + COMMAND_DELAY); … … 550 566 AddCommand(m_ClientId, pair.first, command, m_CurrentTurn + 1); 551 567 } 552 568 } 553 569 554 570 CNetServerTurnManager::CNetServerTurnManager(CNetServerWorker& server) : 555 m_NetServer(server), m_ReadyTurn(1), m_TurnLength(DEFAULT_TURN_LENGTH_MP) 571 m_NetServer(server), m_ReadyTurn(1), m_TurnLength(DEFAULT_TURN_LENGTH_MP), m_HasSyncError(false) 556 572 { 557 573 // The first turn we will actually execute is number 2, 558 574 // so store dummy values into the saved lengths list 559 575 m_SavedTurnLengths.push_back(0); 560 576 m_SavedTurnLengths.push_back(0); … … 599 615 // Save the turn length in case it's needed later 600 616 ENSURE(m_SavedTurnLengths.size() == m_ReadyTurn); 601 617 m_SavedTurnLengths.push_back(m_TurnLength); 602 618 } 603 619 604 void CNetServerTurnManager::NotifyFinishedClientUpdate(int client, u32 turn, const std::string& hash)620 void CNetServerTurnManager::NotifyFinishedClientUpdate(int client, const CStrW& playername, u32 turn, const std::string& hash) 605 621 { 606 622 // Clients must advance one turn at a time 607 623 ENSURE(turn == m_ClientsSimulated[client] + 1); 608 624 m_ClientsSimulated[client] = turn; 609 625 626 // Check for oos only if in sync 627 if (m_HasSyncError) 628 return; 629 630 m_ClientPlayernames[client] = playername; 610 631 m_ClientStateHashes[turn][client] = hash; 611 632 612 633 // Find the newest turn which we know all clients have simulated 613 634 u32 newest = std::numeric_limits<u32>::max(); 614 635 for (std::map<int, u32>::iterator it = m_ClientsSimulated.begin(); it != m_ClientsSimulated.end(); ++it) … … 624 645 break; 625 646 626 647 // Assume the host is correct (maybe we should choose the most common instead to help debugging) 627 648 std::string expected = it->second.begin()->second; 628 649 650 std::vector<CStrW> oos_Playernames; 629 651 for (std::map<int, std::string>::iterator cit = it->second.begin(); cit != it->second.end(); ++cit) 630 652 { 631 653 NETTURN_LOG((L"sync check %d: %d = %hs\n", it->first, cit->first, Hexify(cit->second).c_str())); 632 654 if (cit->second != expected) 633 655 { 634 656 // Oh no, out of sync 657 m_HasSyncError = true; 658 oos_Playernames.push_back(m_ClientPlayernames[cit->first]); 659 } 660 } 635 661 636 // Tell everyone about it 637 CSyncErrorMessage msg; 638 msg.m_Turn = it->first; 639 msg.m_HashExpected = expected; 640 m_NetServer.Broadcast(&msg); 641 642 break; 662 // Tell everyone about out it 663 if (m_HasSyncError) 664 { 665 CSyncErrorMessage msg; 666 msg.m_Turn = it->first; 667 msg.m_HashExpected = expected; 668 for (const CStrW& playername : oos_Playernames) 669 { 670 CSyncErrorMessage::S_m_Playernames h; 671 h.m_Name = playername; 672 msg.m_Playernames.push_back(h); 643 673 } 674 m_NetServer.Broadcast(&msg); 675 break; 644 676 } 645 677 } 646 678 647 679 // Delete the saved hashes for all turns that we've already verified 648 680 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 std::string& 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 std::string& hash, const std::string& 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 std::string& 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