diff --git a/Source/Core/Core/ConfigManager.h b/Source/Core/Core/ConfigManager.h index 42cd8d5d2b..ee60a059ae 100644 --- a/Source/Core/Core/ConfigManager.h +++ b/Source/Core/Core/ConfigManager.h @@ -118,6 +118,8 @@ struct SConfig : NonCopyable bool m_GameCubeAdapter; bool m_AdapterRumble; + bool m_NetplayDesyncCheck; + SysConf* m_SYSCONF; // Save settings diff --git a/Source/Core/Core/Movie.cpp b/Source/Core/Core/Movie.cpp index b59ff1d78a..2df26d93e8 100644 --- a/Source/Core/Core/Movie.cpp +++ b/Source/Core/Core/Movie.cpp @@ -17,6 +17,7 @@ #include "Core/Core.h" #include "Core/CoreTiming.h" #include "Core/Movie.h" +#include "Core/NetPlayClient.h" #include "Core/NetPlayProto.h" #include "Core/State.h" #include "Core/DSP/DSPCore.h" @@ -163,6 +164,9 @@ void FrameUpdate() FrameSkipping(); s_bPolled = false; + + if (NetPlay::IsNetPlayRunning() && SConfig::GetInstance().m_NetplayDesyncCheck) + NetPlayClient::SendTimeBase(); } // called when game is booting up, even if no movie is active, diff --git a/Source/Core/Core/NetPlayClient.cpp b/Source/Core/Core/NetPlayClient.cpp index 3f53d58a32..6670acb202 100644 --- a/Source/Core/Core/NetPlayClient.cpp +++ b/Source/Core/Core/NetPlayClient.cpp @@ -374,6 +374,7 @@ unsigned int NetPlayClient::OnData(sf::Packet& packet) case NP_MSG_START_GAME: { { + SConfig::GetInstance().m_NetplayDesyncCheck = true; std::lock_guard lkg(m_crit.game); packet >> m_current_game; packet >> g_NetPlaySettings.m_CPUthread; @@ -442,6 +443,24 @@ unsigned int NetPlayClient::OnData(sf::Packet& packet) } break; + case NP_MSG_DESYNC_DETECTED: + { + if (!SConfig::GetInstance().m_NetplayDesyncCheck) + break; + + int id; + u32 frame; + packet >> id; + packet >> frame; + std::string sID = ""; + if (id != -1) + sID = StringFromFormat(" from player ID %d", id); + + m_dialog->AppendChat("Possible desync detected" + sID + StringFromFormat(" on frame: %u", frame)); + SConfig::GetInstance().m_NetplayDesyncCheck = false; + } + break; + default: PanicAlertT("Unknown message received with id : %d", mid); break; @@ -1044,6 +1063,20 @@ u8 NetPlayClient::LocalWiimoteToInGameWiimote(u8 local_pad) return ingame_pad; } +void NetPlayClient::SendTimeBase() +{ + std::lock_guard lk(crit_netplay_client); + + u64 timebase = SystemTimers::GetFakeTimeBase(); + + sf::Packet* spac = new sf::Packet; + *spac << (MessageId)NP_MSG_TIMEBASE; + *spac << (u32)timebase; + *spac << (u32)(timebase << 32); + *spac << (u32)Movie::g_currentFrame; + netplay_client->SendAsync(spac); +} + // stuff hacked into dolphin // called from ---CPU--- thread diff --git a/Source/Core/Core/NetPlayClient.h b/Source/Core/Core/NetPlayClient.h index ce5d0ee63b..f12b489b7d 100644 --- a/Source/Core/Core/NetPlayClient.h +++ b/Source/Core/Core/NetPlayClient.h @@ -77,6 +77,8 @@ public: u8 LocalWiimoteToInGameWiimote(u8 local_pad); + static void SendTimeBase(); + enum State { WaitingForTraversalClientConnection, diff --git a/Source/Core/Core/NetPlayProto.h b/Source/Core/Core/NetPlayProto.h index 7f17e0aa60..e473c660fa 100644 --- a/Source/Core/Core/NetPlayProto.h +++ b/Source/Core/Core/NetPlayProto.h @@ -29,7 +29,7 @@ struct Rpt : public std::vector typedef std::vector NetWiimote; -#define NETPLAY_VERSION "Dolphin NetPlay 2014-01-08" +#define NETPLAY_VERSION "Dolphin NetPlay 2015-03-10" extern u64 g_netplay_initial_gctime; @@ -53,6 +53,9 @@ enum NP_MSG_STOP_GAME = 0xA2, NP_MSG_DISABLE_GAME = 0xA3, + NP_MSG_TIMEBASE = 0xB0, + NP_MSG_DESYNC_DETECTED = 0xB1, + NP_MSG_READY = 0xD0, NP_MSG_NOT_READY = 0xD1, diff --git a/Source/Core/Core/NetPlayServer.cpp b/Source/Core/Core/NetPlayServer.cpp index f53793f3be..d47857e723 100644 --- a/Source/Core/Core/NetPlayServer.cpp +++ b/Source/Core/Core/NetPlayServer.cpp @@ -23,6 +23,8 @@ u64 g_netplay_initial_gctime = 1272737767; +static std::map>> s_timebase; + NetPlayServer::~NetPlayServer() { if (is_connected) @@ -110,8 +112,7 @@ void NetPlayServer::ThreadFunc() while (m_do_loop) { // update pings every so many seconds - - if ((m_ping_timer.GetTimeElapsed() > 1000) || m_update_pings) + if (m_update_pings || (m_ping_timer.GetTimeElapsed() > 1000)) { m_ping_key = Common::Timer::GetTimeMs(); @@ -208,6 +209,7 @@ void NetPlayServer::ThreadFunc() break; } } + Common::SleepCurrentThread(1); } // close listening socket and client sockets @@ -581,6 +583,50 @@ unsigned int NetPlayServer::OnData(sf::Packet& packet, Client& player) } break; + case NP_MSG_TIMEBASE: + { + u32 x, y, frame; + packet >> x; + packet >> y; + packet >> frame; + s_timebase[frame].emplace_back(x | ((u64)y >> 32), player.pid); + + if (!std::all_of(s_timebase[frame].begin(), s_timebase[frame].end(), [&frame](std::pair i){ return i.first == s_timebase[frame][0].first; })) + { + sf::Packet spac; + spac << (MessageId) NP_MSG_DESYNC_DETECTED; + + int pid = -1; + if (s_timebase[frame].size() > 2) + { + for (auto time : s_timebase[frame]) + { + int count = 0; + for (auto _time : s_timebase[frame]) + { + if (_time.first == time.first) + count++; + } + if ((size_t)count != s_timebase[frame].size() - 1) + { + if (pid == -1) + { + pid = time.second; + } + else + { + pid = -1; + break; + } + } + } + } + spac << pid; + spac << frame; + SendToClients(spac); + } + } + break; default: PanicAlertT("Unknown message with id:%d received from player:%d Kicking player!", mid, player.pid); // unknown message, kick the client @@ -635,6 +681,7 @@ void NetPlayServer::SetNetSettings(const NetSettings &settings) // called from ---GUI--- thread bool NetPlayServer::StartGame() { + s_timebase.clear(); std::lock_guard lkg(m_crit.game); m_current_game = Common::Timer::GetTimeMs();