diff --git a/src/core/netplay.cpp b/src/core/netplay.cpp index 8d0e8cb8e..732a625ed 100644 --- a/src/core/netplay.cpp +++ b/src/core/netplay.cpp @@ -8,18 +8,21 @@ #pragma comment(lib, "ws2_32.lib") #endif +Netplay::LoopTimer s_timer; +std::string s_game_path; +u32 s_max_pred = 0; + +GGPOPlayerHandle s_local_handle = GGPO_INVALID_HANDLE; +GGPONetworkStats s_last_net_stats{}; +GGPOSession* s_ggpo = nullptr; + +std::array, NUM_CONTROLLER_AND_CARD_PORTS> s_net_input; + // Netplay Impl -Netplay::Session::Session() = default; -Netplay::Session::~Session() +s32 Netplay::Start(s32 lhandle, u16 lport, std::string& raddr, u16 rport, s32 ldelay, u32 pred) { - Close(); -} - -int32_t Netplay::Session::Start(int32_t lhandle, uint16_t lport, std::string& raddr, uint16_t rport, int32_t ldelay, - uint32_t pred) -{ - s_net_session.m_max_pred = pred; + s_max_pred = pred; /* TODO: since saving every frame during rollback loses us time to do actual gamestate iterations it might be better to hijack the update / save / load cycle to only save every confirmed frame only saving when actually needed. @@ -35,11 +38,10 @@ int32_t Netplay::Session::Start(int32_t lhandle, uint16_t lport, std::string& ra GGPOErrorCode result; - result = ggpo_start_session(&s_net_session.p_ggpo, &cb, "Duckstation-Netplay", 2, sizeof(Netplay::Input), lport, - s_net_session.m_max_pred); + result = ggpo_start_session(&s_ggpo, &cb, "Duckstation-Netplay", 2, sizeof(Netplay::Input), lport, s_max_pred); - ggpo_set_disconnect_timeout(s_net_session.p_ggpo, 3000); - ggpo_set_disconnect_notify_start(s_net_session.p_ggpo, 1000); + ggpo_set_disconnect_timeout(s_ggpo, 3000); + ggpo_set_disconnect_notify_start(s_ggpo, 1000); for (int i = 1; i <= 2; i++) { @@ -52,8 +54,8 @@ int32_t Netplay::Session::Start(int32_t lhandle, uint16_t lport, std::string& ra if (lhandle == i) { player.type = GGPOPlayerType::GGPO_PLAYERTYPE_LOCAL; - result = ggpo_add_player(s_net_session.p_ggpo, &player, &handle); - s_net_session.m_local_handle = handle; + result = ggpo_add_player(s_ggpo, &player, &handle); + s_local_handle = handle; } else { @@ -64,38 +66,38 @@ int32_t Netplay::Session::Start(int32_t lhandle, uint16_t lport, std::string& ra strcpy(player.u.remote.ip_address, raddr.c_str()); #endif player.u.remote.port = rport; - result = ggpo_add_player(s_net_session.p_ggpo, &player, &handle); + result = ggpo_add_player(s_ggpo, &player, &handle); } } - ggpo_set_frame_delay(s_net_session.p_ggpo, s_net_session.m_local_handle, ldelay); + ggpo_set_frame_delay(s_ggpo, s_local_handle, ldelay); return result; } -void Netplay::Session::Close() +void Netplay::Close() { - ggpo_close_session(s_net_session.p_ggpo); - s_net_session.p_ggpo = nullptr; - s_net_session.m_local_handle = GGPO_INVALID_HANDLE; - s_net_session.m_max_pred = 0; + ggpo_close_session(s_ggpo); + s_ggpo = nullptr; + s_local_handle = GGPO_INVALID_HANDLE; + s_max_pred = 0; } -bool Netplay::Session::IsActive() +bool Netplay::IsActive() { - return s_net_session.p_ggpo != nullptr; + return s_ggpo != nullptr; } -void Netplay::Session::RunIdle() +void Netplay::RunIdle() { - ggpo_idle(s_net_session.p_ggpo); + ggpo_idle(s_ggpo); } -void Netplay::Session::AdvanceFrame(uint16_t checksum) +void Netplay::AdvanceFrame(u16 checksum) { - ggpo_advance_frame(s_net_session.p_ggpo, checksum); + ggpo_advance_frame(s_ggpo, checksum); } -void Netplay::Session::RunFrame(int32_t& waitTime) +void Netplay::RunFrame(s32& waitTime) { // run game auto result = GGPO_OK; @@ -115,7 +117,7 @@ void Netplay::Session::RunFrame(int32_t& waitTime) { // enable again when rolling back done SPU::SetAudioOutputMuted(false); - System::NetplayAdvanceFrame (inputs, disconnectFlags); + System::NetplayAdvanceFrame(inputs, disconnectFlags); } else RunIdle(); @@ -126,79 +128,79 @@ void Netplay::Session::RunFrame(int32_t& waitTime) waitTime = GetTimer()->UsToWaitThisLoop(); } -int32_t Netplay::Session::CurrentFrame() +s32 Netplay::CurrentFrame() { - int32_t frame; - ggpo_get_current_frame(s_net_session.p_ggpo, frame); + s32 frame; + ggpo_get_current_frame(s_ggpo, frame); return frame; } -void Netplay::Session::CollectInput(uint32_t slot, uint32_t bind, float value) +void Netplay::CollectInput(u32 slot, u32 bind, float value) { - s_net_session.m_net_input[slot][bind] = value; + s_net_input[slot][bind] = value; } -Netplay::Input Netplay::Session::ReadLocalInput() +Netplay::Input Netplay::ReadLocalInput() { // get controller data of the first controller (0 internally) Netplay::Input inp{0}; - for (uint32_t i = 0; i < (uint32_t)DigitalController::Button::Count; i++) + for (u32 i = 0; i < (u32)DigitalController::Button::Count; i++) { - if (s_net_session.m_net_input[0][i] >= 0.25f) + if (s_net_input[0][i] >= 0.25f) inp.button_data |= 1 << i; } return inp; } -std::string& Netplay::Session::GetGamePath() +std::string& Netplay::GetGamePath() { - return s_net_session.m_game_path; + return s_game_path; } -void Netplay::Session::SetGamePath(std::string& path) +void Netplay::SetGamePath(std::string& path) { - s_net_session.m_game_path = path; + s_game_path = path; } -void Netplay::Session::SendMsg(const char* msg) +void Netplay::SendMsg(const char* msg) { - ggpo_client_chat(s_net_session.p_ggpo, msg); + ggpo_client_chat(s_ggpo, msg); } -GGPOErrorCode Netplay::Session::SyncInput(Netplay::Input inputs[2], int* disconnect_flags) +GGPOErrorCode Netplay::SyncInput(Netplay::Input inputs[2], int* disconnect_flags) { - return ggpo_synchronize_input(s_net_session.p_ggpo, inputs, sizeof(Netplay::Input) * 2, disconnect_flags); + return ggpo_synchronize_input(s_ggpo, inputs, sizeof(Netplay::Input) * 2, disconnect_flags); } -GGPOErrorCode Netplay::Session::AddLocalInput(Netplay::Input input) +GGPOErrorCode Netplay::AddLocalInput(Netplay::Input input) { - return ggpo_add_local_input(s_net_session.p_ggpo, s_net_session.m_local_handle, &input, sizeof(Netplay::Input)); + return ggpo_add_local_input(s_ggpo, s_local_handle, &input, sizeof(Netplay::Input)); } -GGPONetworkStats& Netplay::Session::GetNetStats(int32_t handle) +GGPONetworkStats& Netplay::GetNetStats(s32 handle) { - ggpo_get_network_stats(s_net_session.p_ggpo, handle, &s_net_session.m_last_net_stats); - return s_net_session.m_last_net_stats; + ggpo_get_network_stats(s_ggpo, handle, &s_last_net_stats); + return s_last_net_stats; } -int32_t Netplay::Session::GetPing() +s32 Netplay::GetPing() { const int handle = GetLocalHandle() == 1 ? 2 : 1; - ggpo_get_network_stats(s_net_session.p_ggpo, handle, &s_net_session.m_last_net_stats); - return s_net_session.m_last_net_stats.network.ping; + ggpo_get_network_stats(s_ggpo, handle, &s_last_net_stats); + return s_last_net_stats.network.ping; } -uint32_t Netplay::Session::GetMaxPrediction() +u32 Netplay::GetMaxPrediction() { - return s_net_session.m_max_pred; + return s_max_pred; } -GGPOPlayerHandle Netplay::Session::GetLocalHandle() +GGPOPlayerHandle Netplay::GetLocalHandle() { - return s_net_session.m_local_handle; + return s_local_handle; } -void Netplay::Session::SetInputs(Netplay::Input inputs[2]) +void Netplay::SetInputs(Netplay::Input inputs[2]) { for (u32 i = 0; i < 2; i++) { @@ -209,15 +211,15 @@ void Netplay::Session::SetInputs(Netplay::Input inputs[2]) } } -Netplay::LoopTimer* Netplay::Session::GetTimer() +Netplay::LoopTimer* Netplay::GetTimer() { - return &s_net_session.m_timer; + return &s_timer; } -uint16_t Netplay::Session::Fletcher16(uint8_t* data, int count) +u16 Netplay::Fletcher16(uint8_t* data, int count) { - uint16_t sum1 = 0; - uint16_t sum2 = 0; + u16 sum1 = 0; + u16 sum2 = 0; int index; for (index = 0; index < count; ++index) @@ -229,7 +231,7 @@ uint16_t Netplay::Session::Fletcher16(uint8_t* data, int count) return (sum2 << 8) | sum1; } -void Netplay::LoopTimer::Init(uint32_t fps, uint32_t frames_to_spread_wait) +void Netplay::LoopTimer::Init(u32 fps, u32 frames_to_spread_wait) { m_us_per_game_loop = 1000000 / fps; m_us_ahead = 0; @@ -255,9 +257,9 @@ void Netplay::LoopTimer::OnGGPOTimeSyncEvent(float frames_ahead) } } -int32_t Netplay::LoopTimer::UsToWaitThisLoop() +s32 Netplay::LoopTimer::UsToWaitThisLoop() { - int32_t timetoWait = m_us_per_game_loop; + s32 timetoWait = m_us_per_game_loop; if (m_wait_count) { timetoWait += m_us_extra_to_wait; diff --git a/src/core/netplay.h b/src/core/netplay.h index e2659355c..15549909a 100644 --- a/src/core/netplay.h +++ b/src/core/netplay.h @@ -1,8 +1,5 @@ #pragma once -#ifndef _NETPLAY_H -#define _NETPLAY_H - #include #include #include @@ -16,8 +13,8 @@ // C GGPO Event Callbacks. Should be defined in system.cpp extern "C" { bool NpAdvFrameCb(void* ctx, int flags); -bool NpSaveFrameCb(void* ctx, uint8_t** buffer, int* len, int* checksum, int frame); -bool NpLoadFrameCb(void* ctx, uint8_t* buffer, int len, int rb_frames, int frame_to_load); +bool NpSaveFrameCb(void* ctx, unsigned char** buffer, int* len, int* checksum, int frame); +bool NpLoadFrameCb(void* ctx, unsigned char* buffer, int len, int rb_frames, int frame_to_load); bool NpBeginGameCb(void* ctx, const char* game_name); void NpFreeBuffCb(void* ctx, void* buffer); bool NpOnEventCb(void* ctx, GGPOEvent* ev); @@ -27,76 +24,52 @@ namespace Netplay { struct Input { - uint32_t button_data; + u32 button_data; }; struct LoopTimer { public: - void Init(uint32_t fps, uint32_t frames_to_spread_wait); + void Init(u32 fps, u32 frames_to_spread_wait); void OnGGPOTimeSyncEvent(float frames_ahead); // Call every loop, to get the amount of time the current iteration of gameloop should take - int32_t UsToWaitThisLoop(); + s32 UsToWaitThisLoop(); private: float m_last_advantage = 0.0f; - int32_t m_us_per_game_loop = 0; - int32_t m_us_ahead = 0; - int32_t m_us_extra_to_wait = 0; - int32_t m_frames_to_spread_wait = 0; - int32_t m_wait_count = 0; + s32 m_us_per_game_loop = 0; + s32 m_us_ahead = 0; + s32 m_us_extra_to_wait = 0; + s32 m_frames_to_spread_wait = 0; + s32 m_wait_count = 0; }; -class Session -{ -public: - Session(); - ~Session(); - // l = local, r = remote - static int32_t Start(int32_t lhandle, uint16_t lport, std::string& raddr, uint16_t rport, int32_t ldelay, - uint32_t pred); +// l = local, r = remote +s32 Start(s32 lhandle, u16 lport, std::string& raddr, u16 rport, s32 ldelay, u32 pred); - static void Close(); - static bool IsActive(); - static void RunIdle(); - - static void AdvanceFrame(uint16_t checksum = 0); - static void RunFrame(int32_t& waitTime); - static int32_t CurrentFrame(); +void Close(); +bool IsActive(); +void RunIdle(); - static void CollectInput(uint32_t slot, uint32_t bind, float value); - static Netplay::Input ReadLocalInput(); +void AdvanceFrame(u16 checksum = 0); +void RunFrame(s32& waitTime); +s32 CurrentFrame(); - static std::string& GetGamePath(); - static void SetGamePath(std::string& path); - static void SendMsg(const char* msg); +void CollectInput(u32 slot, u32 bind, float value); +Netplay::Input ReadLocalInput(); - static GGPOErrorCode SyncInput(Netplay::Input inputs[2], int* disconnect_flags); - static GGPOErrorCode AddLocalInput(Netplay::Input input); - static GGPONetworkStats& GetNetStats(int32_t handle); - static int32_t GetPing(); - static uint32_t GetMaxPrediction(); - static GGPOPlayerHandle GetLocalHandle(); - static void SetInputs(Netplay::Input inputs[2]); +std::string& GetGamePath(); +void SetGamePath(std::string& path); +void SendMsg(const char* msg); - static Netplay::LoopTimer* GetTimer(); - static uint16_t Fletcher16(uint8_t* data, int count); - -private: - Netplay::LoopTimer m_timer; - std::string m_game_path; - uint32_t m_max_pred = 0; - - GGPOPlayerHandle m_local_handle = GGPO_INVALID_HANDLE; - GGPONetworkStats m_last_net_stats{}; - GGPOSession* p_ggpo = nullptr; - - std::array, NUM_CONTROLLER_AND_CARD_PORTS> m_net_input; -}; +GGPOErrorCode SyncInput(Netplay::Input inputs[2], int* disconnect_flags); +GGPOErrorCode AddLocalInput(Netplay::Input input); +GGPONetworkStats& GetNetStats(s32 handle); +s32 GetPing(); +u32 GetMaxPrediction(); +GGPOPlayerHandle GetLocalHandle(); +void SetInputs(Netplay::Input inputs[2]); +Netplay::LoopTimer* GetTimer(); +u16 Fletcher16(uint8_t* data, int count); } // namespace Netplay - -// Netplay Instance -static Netplay::Session s_net_session = Netplay::Session(); - -#endif // !_NETPLAY_H diff --git a/src/core/pad.cpp b/src/core/pad.cpp index f7cfdee01..c31ab9d40 100644 --- a/src/core/pad.cpp +++ b/src/core/pad.cpp @@ -169,12 +169,12 @@ void Pad::Reset() bool Pad::ShouldAvoidSavingToState() { // Currently only runahead, will also be used for netplay. - return g_settings.IsRunaheadEnabled() || Netplay::Session::IsActive(); + return g_settings.IsRunaheadEnabled() || Netplay::IsActive(); } u32 Pad::GetMaximumRollbackFrames() { - return (Netplay::Session::IsActive() ? Netplay::Session::GetMaxPrediction() : g_settings.runahead_frames); + return (Netplay::IsActive() ? Netplay::GetMaxPrediction() : g_settings.runahead_frames); } bool Pad::DoStateController(StateWrapper& sw, u32 i) diff --git a/src/core/system.cpp b/src/core/system.cpp index 71738f712..d66c1b953 100644 --- a/src/core/system.cpp +++ b/src/core/system.cpp @@ -1541,7 +1541,7 @@ void System::Execute() // this can shut us down Host::PumpMessagesOnCPUThread(); - if (!IsValid() || Netplay::Session::IsActive()) + if (!IsValid() || Netplay::IsActive()) return; if (s_frame_step_request) @@ -1574,17 +1574,17 @@ void System::ExecuteNetplay() s32 timeToWait; std::chrono::steady_clock::time_point start, next, now; start = next = now = std::chrono::steady_clock::now(); - while (Netplay::Session::IsActive() && System::IsRunning()) + while (Netplay::IsActive() && System::IsRunning()) { now = std::chrono::steady_clock::now(); if (now >= next) { - Netplay::Session::RunFrame(timeToWait); + Netplay::RunFrame(timeToWait); next = now + std::chrono::microseconds(timeToWait); s_next_frame_time += timeToWait; // this can shut us down Host::PumpMessagesOnCPUThread(); - if (!IsValid() || !Netplay::Session::IsActive()) + if (!IsValid() || !Netplay::IsActive()) break; const bool skip_present = g_host_display->ShouldSkipDisplayingFrame(); @@ -4471,15 +4471,15 @@ void System::StartNetplaySession(s32 local_handle, u16 local_port, std::string& s32 input_delay, std::string& game_path) { // dont want to start a session when theres already one going on. - if (Netplay::Session::IsActive()) + if (Netplay::IsActive()) return; // set game path for later loading during the begin game callback - Netplay::Session::SetGamePath(game_path); + Netplay::SetGamePath(game_path); // set netplay timer const u32 fps = (s_region == ConsoleRegion::PAL ? 50 : 60); - Netplay::Session::GetTimer()->Init(fps, 180); + Netplay::GetTimer()->Init(fps, 180); // create session - int result = Netplay::Session::Start(local_handle, local_port, remote_addr, remote_port, input_delay, 8); + int result = Netplay::Start(local_handle, local_port, remote_addr, remote_port, input_delay, 8); if (result != GGPO_OK) { Log_ErrorPrintf("Failed to Create Netplay Session! Error: %d", result); @@ -4488,17 +4488,17 @@ void System::StartNetplaySession(s32 local_handle, u16 local_port, std::string& void System::StopNetplaySession() { - if (!Netplay::Session::IsActive()) + if (!Netplay::IsActive()) return; s_netplay_states.clear(); - Netplay::Session::Close(); + Netplay::Close(); } void System::NetplayAdvanceFrame(Netplay::Input inputs[], int disconnect_flags) { - Netplay::Session::SetInputs(inputs); + Netplay::SetInputs(inputs); System::DoRunFrame(); - Netplay::Session::AdvanceFrame(); + Netplay::AdvanceFrame(); } bool NpBeginGameCb(void* ctx, const char* game_name) @@ -4507,7 +4507,7 @@ bool NpBeginGameCb(void* ctx, const char* game_name) if (System::IsValid()) System::ShutdownSystem(false); // fast boot the selected game and wait for the other player - auto param = SystemBootParameters(Netplay::Session::GetGamePath()); + auto param = SystemBootParameters(Netplay::GetGamePath()); param.override_fast_boot = true; if (!System::BootSystem(param)) { @@ -4526,7 +4526,7 @@ bool NpAdvFrameCb(void* ctx, int flags) { Netplay::Input inputs[2] = {}; int disconnectFlags; - Netplay::Session::SyncInput(inputs, &disconnectFlags); + Netplay::SyncInput(inputs, &disconnectFlags); System::NetplayAdvanceFrame(inputs, disconnectFlags); return true; } @@ -4542,7 +4542,7 @@ bool NpSaveFrameCb(void* ctx, uint8_t** buffer, int* len, int* checksum, int fra return false; memcpy(*buffer, &dummyData, *len); // store state for later. - int pred = Netplay::Session::GetMaxPrediction(); + int pred = Netplay::GetMaxPrediction(); if (frame < pred && s_netplay_states.size() < pred) { MemorySaveState save; @@ -4561,7 +4561,7 @@ bool NpLoadFrameCb(void* ctx, uint8_t* buffer, int len, int rb_frames, int frame { // Disable Audio For upcoming rollback SPU::SetAudioOutputMuted(true); - return System::LoadMemoryState(s_netplay_states[frame_to_load % Netplay::Session::GetMaxPrediction()]); + return System::LoadMemoryState(s_netplay_states[frame_to_load % Netplay::GetMaxPrediction()]); } bool NpOnEventCb(void* ctx, GGPOEvent* ev) @@ -4603,7 +4603,7 @@ bool NpOnEventCb(void* ctx, GGPOEvent* ev) msg = buff; break; case GGPOEventCode::GGPO_EVENTCODE_TIMESYNC: - Netplay::Session::GetTimer()->OnGGPOTimeSyncEvent(ev->u.timesync.frames_ahead); + Netplay::GetTimer()->OnGGPOTimeSyncEvent(ev->u.timesync.frames_ahead); break; case GGPOEventCode::GGPO_EVENTCODE_DESYNC: sprintf(buff, "Netplay Desync Detected!: Frame: %d, L:%u, R:%u", ev->u.desync.nFrameOfDesync, diff --git a/src/duckstation-qt/qthost.cpp b/src/duckstation-qt/qthost.cpp index 9dc241444..1712ee8f9 100644 --- a/src/duckstation-qt/qthost.cpp +++ b/src/duckstation-qt/qthost.cpp @@ -1100,7 +1100,7 @@ void EmuThread::sendNetplayMessage(const QString& message) QMetaObject::invokeMethod(this, "sendNetplayMessage", Qt::QueuedConnection, Q_ARG(const QString&, message)); return; } - Netplay::Session::SendMsg(message.toStdString().c_str()); + Netplay::SendMsg(message.toStdString().c_str()); } void EmuThread::stopNetplaySession() @@ -1462,12 +1462,13 @@ void EmuThread::run() // main loop while (!m_shutdown_flag) { - if (System::IsRunning()) + if (Netplay::IsActive()) { - if (Netplay::Session::IsActive()) - System::ExecuteNetplay(); - else - System::Execute(); + System::ExecuteNetplay(); + } + else if (System::IsRunning()) + { + System::Execute(); } else { @@ -1711,7 +1712,7 @@ void EmuThread::updatePerformanceCounters() m_last_video_fps = vfps; } - const s32 ping = Netplay::Session::GetPing(); + const s32 ping = Netplay::GetPing(); if (m_last_ping != ping) { QMetaObject::invokeMethod(g_main_window->getStatusPingWidget(), "setText", Qt::QueuedConnection, diff --git a/src/frontend-common/input_manager.cpp b/src/frontend-common/input_manager.cpp index aba111cf4..123320e96 100644 --- a/src/frontend-common/input_manager.cpp +++ b/src/frontend-common/input_manager.cpp @@ -714,9 +714,9 @@ void InputManager::AddPadBindings(SettingsInterface& si, const std::string& sect if (!System::IsValid()) return; - if (Netplay::Session::IsActive()) + if (Netplay::IsActive()) { - Netplay::Session::CollectInput(pad_index, bind_index, value); + Netplay::CollectInput(pad_index, bind_index, value); return; }