Make netplay's "same game" check more robust

Instead of comparing the game ID, revision, disc number and name,
we can compare a hash of important parts of the disc including
all the aforementioned data but also additional data such as the
FST. The primary reason why I'm making this change is to let us
catch more desyncs before they happen, but this should also fix
https://bugs.dolphin-emu.org/issues/12115. As a bonus, the UI can
now distinguish the case where a client doesn't have the game at
all from the case where a client has the wrong version of the game.
This commit is contained in:
JosJuice 2020-06-07 22:58:03 +02:00
commit a41166bb37
34 changed files with 502 additions and 148 deletions

View file

@ -51,6 +51,7 @@
#include "Core/IOS/IOS.h"
#include "Core/IOS/Uids.h"
#include "Core/NetPlayClient.h" //for NetPlayUI
#include "Core/SyncIdentifier.h"
#include "DiscIO/Enums.h"
#include "InputCommon/ControllerEmu/ControlGroup/Attachments.h"
#include "InputCommon/GCPadStatus.h"
@ -182,7 +183,7 @@ void NetPlayServer::SetupIndex()
session.region = Config::Get(Config::NETPLAY_INDEX_REGION);
session.has_password = !Config::Get(Config::NETPLAY_INDEX_PASSWORD).empty();
session.method = m_traversal_client ? "traversal" : "direct";
session.game_id = m_selected_game.empty() ? "UNKNOWN" : m_selected_game;
session.game_id = m_selected_game_name.empty() ? "UNKNOWN" : m_selected_game_name;
session.player_count = static_cast<int>(m_players.size());
session.in_game = m_is_running;
session.port = GetPort();
@ -238,7 +239,7 @@ void NetPlayServer::ThreadFunc()
SendToClients(spac);
m_index.SetPlayerCount(static_cast<int>(m_players.size()));
m_index.SetGame(m_selected_game);
m_index.SetGame(m_selected_game_name);
m_index.SetInGame(m_is_running);
m_update_pings = false;
@ -348,6 +349,20 @@ void NetPlayServer::ThreadFunc()
}
} // namespace NetPlay
static void SendSyncIdentifier(sf::Packet& spac, const SyncIdentifier& sync_identifier)
{
// We cast here due to a potential long vs long long mismatch
spac << static_cast<sf::Uint64>(sync_identifier.dol_elf_size);
spac << sync_identifier.game_id;
spac << sync_identifier.revision;
spac << sync_identifier.disc_number;
spac << sync_identifier.is_datel;
for (const u8& x : sync_identifier.sync_hash)
spac << x;
}
// called from ---NETPLAY--- thread
unsigned int NetPlayServer::OnConnect(ENetPeer* socket, sf::Packet& rpac)
{
@ -413,11 +428,12 @@ unsigned int NetPlayServer::OnConnect(ENetPeer* socket, sf::Packet& rpac)
Send(player.socket, spac);
// send new client the selected game
if (!m_selected_game.empty())
if (!m_selected_game_name.empty())
{
spac.clear();
spac << static_cast<MessageId>(NP_MSG_CHANGE_GAME);
spac << m_selected_game;
SendSyncIdentifier(spac, m_selected_game_identifier);
spac << m_selected_game_name;
Send(player.socket, spac);
}
@ -913,7 +929,7 @@ unsigned int NetPlayServer::OnData(sf::Packet& packet, Client& player)
u32 status;
packet >> status;
m_players[player.pid].game_status = static_cast<PlayerGameStatus>(status);
m_players[player.pid].game_status = static_cast<SyncIdentifierComparison>(status);
// send msg to other clients
sf::Packet spac;
@ -1153,16 +1169,19 @@ void NetPlayServer::SendChatMessage(const std::string& msg)
}
// called from ---GUI--- thread
bool NetPlayServer::ChangeGame(const std::string& game)
bool NetPlayServer::ChangeGame(const SyncIdentifier& sync_identifier,
const std::string& netplay_name)
{
std::lock_guard<std::recursive_mutex> lkg(m_crit.game);
m_selected_game = game;
m_selected_game_identifier = sync_identifier;
m_selected_game_name = netplay_name;
// send changed game to clients
sf::Packet spac;
spac << static_cast<MessageId>(NP_MSG_CHANGE_GAME);
spac << game;
SendSyncIdentifier(spac, m_selected_game_identifier);
spac << m_selected_game_name;
SendAsyncToClients(std::move(spac));
@ -1170,11 +1189,11 @@ bool NetPlayServer::ChangeGame(const std::string& game)
}
// called from ---GUI--- thread
bool NetPlayServer::ComputeMD5(const std::string& file_identifier)
bool NetPlayServer::ComputeMD5(const SyncIdentifier& sync_identifier)
{
sf::Packet spac;
spac << static_cast<MessageId>(NP_MSG_COMPUTE_MD5);
spac << file_identifier;
SendSyncIdentifier(spac, sync_identifier);
SendAsyncToClients(std::move(spac));
@ -1260,7 +1279,7 @@ bool NetPlayServer::StartGame()
const sf::Uint64 initial_rtc = GetInitialNetPlayRTC();
const std::string region = SConfig::GetDirectoryForRegion(
SConfig::ToGameCubeRegion(m_dialog->FindGameFile(m_selected_game)->GetRegion()));
SConfig::ToGameCubeRegion(m_dialog->FindGameFile(m_selected_game_identifier)->GetRegion()));
// sync GC SRAM with clients
if (!g_SRAM_netplay_initialized)
@ -1395,7 +1414,7 @@ bool NetPlayServer::SyncSaveData()
}
}
const auto game = m_dialog->FindGameFile(m_selected_game);
const auto game = m_dialog->FindGameFile(m_selected_game_identifier);
if (game == nullptr)
{
PanicAlertT("Selected game doesn't exist in game list!");
@ -1618,7 +1637,7 @@ bool NetPlayServer::SyncCodes()
m_codes_synced = false;
// Get Game Path
const auto game = m_dialog->FindGameFile(m_selected_game);
const auto game = m_dialog->FindGameFile(m_selected_game_identifier);
if (game == nullptr)
{
PanicAlertT("Selected game doesn't exist in game list!");