1
0
forked from 0ad/0ad

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:
Nicolas Auvray 2015-08-30 17:47:18 +00:00
parent f47cb2c711
commit 63e42fbd31
5 changed files with 57 additions and 30 deletions

View File

@ -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)
{

View File

@ -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()

View File

@ -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)
{

View File

@ -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,21 +634,33 @@ 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
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;
m_NetServer.Broadcast(&msg);
break;
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;
}
}

View File

@ -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