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.
This commit is contained in:
parent
f47cb2c711
commit
63e42fbd31
@ -643,7 +643,7 @@ bool CNetClient::OnInGame(void *context, CFsmEvent* event)
|
||||
else if (message->GetType() == NMT_SYNC_ERROR)
|
||||
{
|
||||
CSyncErrorMessage* syncMessage = static_cast<CSyncErrorMessage*> (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)
|
||||
{
|
||||
|
@ -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()
|
||||
|
@ -894,7 +894,7 @@ bool CNetServerWorker::OnInGame(void* context, CFsmEvent* event)
|
||||
else if (message->GetType() == (uint)NMT_SYNC_CHECK)
|
||||
{
|
||||
CSyncCheckMessage* syncMessage = static_cast<CSyncCheckMessage*> (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)
|
||||
{
|
||||
|
@ -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<CSyncErrorMessage::S_m_PlayerNames>& 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<CSyncErrorMessage::S_m_PlayerNames>* 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<CStrW> OOSPlayerNames;
|
||||
for (std::map<int, std::string>::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
|
||||
|
@ -20,6 +20,7 @@
|
||||
|
||||
#include "simulation2/helpers/SimulationCommand.h"
|
||||
#include "lib/os_path.h"
|
||||
#include "NetMessage.h"
|
||||
|
||||
#include <list>
|
||||
#include <map>
|
||||
@ -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<CSyncErrorMessage::S_m_PlayerNames>& 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<CSyncErrorMessage::S_m_PlayerNames>* 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<u32, std::map<int, std::string> > m_ClientStateHashes;
|
||||
|
||||
// Map of client ID -> playername
|
||||
std::map<u32, CStrW> m_ClientPlayernames;
|
||||
|
||||
// Current turn length
|
||||
u32 m_TurnLength;
|
||||
|
||||
@ -343,6 +347,8 @@ protected:
|
||||
std::vector<u32> m_SavedTurnLengths;
|
||||
|
||||
CNetServerWorker& m_NetServer;
|
||||
|
||||
bool m_HasSyncError;
|
||||
};
|
||||
|
||||
#endif // INCLUDED_NETTURNMANAGER
|
||||
|
Loading…
Reference in New Issue
Block a user