diff --git a/src/core/netplay.cpp b/src/core/netplay.cpp index 732a625ed..b23064c0e 100644 --- a/src/core/netplay.cpp +++ b/src/core/netplay.cpp @@ -1,22 +1,89 @@ #include "netplay.h" +#include "common/byte_stream.h" +#include "common/gpu_texture.h" +#include "common/log.h" +#include "digital_controller.h" +#include "ggponet.h" #include "pad.h" #include "spu.h" #include "system.h" #include +#include +Log_SetChannel(Netplay); #ifdef _WIN32 #pragma comment(lib, "ws2_32.lib") #endif -Netplay::LoopTimer s_timer; -std::string s_game_path; -u32 s_max_pred = 0; +namespace Netplay { -GGPOPlayerHandle s_local_handle = GGPO_INVALID_HANDLE; -GGPONetworkStats s_last_net_stats{}; -GGPOSession* s_ggpo = nullptr; +struct Input +{ + u32 button_data; +}; -std::array, NUM_CONTROLLER_AND_CARD_PORTS> s_net_input; +struct LoopTimer +{ +public: + 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 + s32 UsToWaitThisLoop(); + +private: + float m_last_advantage = 0.0f; + 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; +}; + +static bool NpAdvFrameCb(void* ctx, int flags); +static bool NpSaveFrameCb(void* ctx, unsigned char** buffer, int* len, int* checksum, int frame); +static bool NpLoadFrameCb(void* ctx, unsigned char* buffer, int len, int rb_frames, int frame_to_load); +static bool NpBeginGameCb(void* ctx, const char* game_name); +static void NpFreeBuffCb(void* ctx, void* buffer); +static bool NpOnEventCb(void* ctx, GGPOEvent* ev); + +static Input ReadLocalInput(); +static GGPOErrorCode AddLocalInput(Netplay::Input input); +static GGPOErrorCode SyncInput(Input inputs[2], int* disconnect_flags); +static void SetInputs(Input inputs[2]); + +static LoopTimer* GetTimer(); + +// l = local, r = remote +static s32 Start(s32 lhandle, u16 lport, std::string& raddr, u16 rport, s32 ldelay, u32 pred); +static void Close(); +static void RunIdle(); + +static void AdvanceFrame(u16 checksum = 0); +static void RunFrame(s32& waitTime); +static s32 CurrentFrame(); + +static std::string& GetGamePath(); +static void SetGamePath(std::string& path); + +static GGPONetworkStats& GetNetStats(s32 handle); +static GGPOPlayerHandle GetLocalHandle(); +static u16 Fletcher16(uint8_t* data, int count); + +static void NetplayAdvanceFrame(Netplay::Input inputs[], int disconnect_flags); + +static LoopTimer s_timer; +static std::string s_game_path; +static u32 s_max_pred = 0; + +static GGPOPlayerHandle s_local_handle = GGPO_INVALID_HANDLE; +static GGPONetworkStats s_last_net_stats{}; +static GGPOSession* s_ggpo = nullptr; + +static std::deque s_netplay_states; + +static std::array, NUM_CONTROLLER_AND_CARD_PORTS> s_net_input; + +} // namespace Netplay // Netplay Impl @@ -117,7 +184,7 @@ void Netplay::RunFrame(s32& waitTime) { // enable again when rolling back done SPU::SetAudioOutputMuted(false); - System::NetplayAdvanceFrame(inputs, disconnectFlags); + NetplayAdvanceFrame(inputs, disconnectFlags); } else RunIdle(); @@ -269,3 +336,189 @@ s32 Netplay::LoopTimer::UsToWaitThisLoop() } return timetoWait; } + +void Netplay::StartNetplaySession(s32 local_handle, u16 local_port, std::string& remote_addr, u16 remote_port, + s32 input_delay, std::string& game_path) +{ + // dont want to start a session when theres already one going on. + if (IsActive()) + return; + // set game path for later loading during the begin game callback + SetGamePath(game_path); + // set netplay timer + const u32 fps = (System::GetRegion() == ConsoleRegion::PAL ? 50 : 60); + GetTimer()->Init(fps, 180); + // create session + 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); + } +} + +void Netplay::StopNetplaySession() +{ + if (!IsActive()) + return; + s_netplay_states.clear(); + Close(); +} + +void Netplay::NetplayAdvanceFrame(Netplay::Input inputs[], int disconnect_flags) +{ + Netplay::SetInputs(inputs); + System::RunFrame(); + Netplay::AdvanceFrame(); +} + +void Netplay::ExecuteNetplay() +{ + // frame timing + s32 timeToWait; + std::chrono::steady_clock::time_point start, next, now; + start = next = now = std::chrono::steady_clock::now(); + while (Netplay::IsActive() && System::IsRunning()) + { + now = std::chrono::steady_clock::now(); + if (now >= next) + { + Netplay::RunFrame(timeToWait); + next = now + std::chrono::microseconds(timeToWait); + // s_next_frame_time += timeToWait; + + // this can shut us down + Host::PumpMessagesOnCPUThread(); + if (!System::IsValid() || !Netplay::IsActive()) + break; + + System::PresentFrame(); + System::UpdatePerformanceCounters(); + } + } +} + +bool Netplay::NpBeginGameCb(void* ctx, const char* game_name) +{ + // close system if its already running + if (System::IsValid()) + System::ShutdownSystem(false); + // fast boot the selected game and wait for the other player + auto param = SystemBootParameters(Netplay::GetGamePath()); + param.override_fast_boot = true; + if (!System::BootSystem(param)) + { + StopNetplaySession(); + return false; + } + // Fast Forward to Game Start + SPU::SetAudioOutputMuted(true); + while (System::GetInternalFrameNumber() < 2) + System::RunFrame(); + SPU::SetAudioOutputMuted(false); + return true; +} + +bool Netplay::NpAdvFrameCb(void* ctx, int flags) +{ + Netplay::Input inputs[2] = {}; + int disconnectFlags; + Netplay::SyncInput(inputs, &disconnectFlags); + NetplayAdvanceFrame(inputs, disconnectFlags); + return true; +} + +bool Netplay::NpSaveFrameCb(void* ctx, uint8_t** buffer, int* len, int* checksum, int frame) +{ + bool result = false; + // give ggpo something so it doesnt complain. + u8 dummyData = 43; + *len = sizeof(u8); + *buffer = (unsigned char*)malloc(*len); + if (!*buffer) + return false; + memcpy(*buffer, &dummyData, *len); + // store state for later. + int pred = Netplay::GetMaxPrediction(); + if (frame < pred && s_netplay_states.size() < pred) + { + System::MemorySaveState save; + result = System::SaveMemoryState(&save); + s_netplay_states.push_back(std::move(save)); + } + else + { + // reuse streams + result = System::SaveMemoryState(&s_netplay_states[frame % pred]); + } + return result; +} + +bool Netplay::NpLoadFrameCb(void* ctx, uint8_t* buffer, int len, int rb_frames, int frame_to_load) +{ + // Disable Audio For upcoming rollback + SPU::SetAudioOutputMuted(true); + return System::LoadMemoryState(s_netplay_states[frame_to_load % Netplay::GetMaxPrediction()]); +} + +bool Netplay::NpOnEventCb(void* ctx, GGPOEvent* ev) +{ + char buff[128]; + std::string msg; + switch (ev->code) + { + case GGPOEventCode::GGPO_EVENTCODE_CONNECTED_TO_PEER: + sprintf(buff, "Netplay Connected To Player: %d", ev->u.connected.player); + msg = buff; + break; + case GGPOEventCode::GGPO_EVENTCODE_SYNCHRONIZING_WITH_PEER: + sprintf(buff, "Netplay Synchronzing: %d/%d", ev->u.synchronizing.count, ev->u.synchronizing.total); + msg = buff; + break; + case GGPOEventCode::GGPO_EVENTCODE_SYNCHRONIZED_WITH_PEER: + sprintf(buff, "Netplay Synchronized With Player: %d", ev->u.synchronized.player); + msg = buff; + break; + case GGPOEventCode::GGPO_EVENTCODE_DISCONNECTED_FROM_PEER: + sprintf(buff, "Netplay Player: %d Disconnected", ev->u.disconnected.player); + msg = buff; + break; + case GGPOEventCode::GGPO_EVENTCODE_RUNNING: + msg = "Netplay Is Running"; + break; + case GGPOEventCode::GGPO_EVENTCODE_CONNECTION_INTERRUPTED: + sprintf(buff, "Netplay Player: %d Connection Interupted, Timeout: %d", ev->u.connection_interrupted.player, + ev->u.connection_interrupted.disconnect_timeout); + msg = buff; + break; + case GGPOEventCode::GGPO_EVENTCODE_CONNECTION_RESUMED: + sprintf(buff, "Netplay Player: %d Connection Resumed", ev->u.connection_resumed.player); + msg = buff; + break; + case GGPOEventCode::GGPO_EVENTCODE_CHAT: + sprintf(buff, "%s", ev->u.chat.msg); + msg = buff; + break; + case GGPOEventCode::GGPO_EVENTCODE_TIMESYNC: + 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, + ev->u.desync.ourCheckSum, ev->u.desync.remoteChecksum); + msg = buff; + break; + default: + sprintf(buff, "Netplay Event Code: %d", ev->code); + msg = buff; + } + if (!msg.empty()) + { + Host::OnNetplayMessage(msg); + Log_InfoPrintf("%s", msg.c_str()); + } + return true; +} + +void Netplay::NpFreeBuffCb(void* ctx, void* buffer) +{ + free(buffer); +} \ No newline at end of file diff --git a/src/core/netplay.h b/src/core/netplay.h index 15549909a..b873970f4 100644 --- a/src/core/netplay.h +++ b/src/core/netplay.h @@ -1,75 +1,24 @@ #pragma once -#include -#include -#include -#include -#include - -#include "common/timer.h" -#include "digital_controller.h" #include "types.h" - -// C GGPO Event Callbacks. Should be defined in system.cpp -extern "C" { -bool NpAdvFrameCb(void* ctx, int flags); -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); -} +#include namespace Netplay { -struct Input -{ - u32 button_data; -}; +void StartNetplaySession(s32 local_handle, u16 local_port, std::string& remote_addr, u16 remote_port, s32 input_delay, + std::string& game_path); +void StopNetplaySession(); -struct LoopTimer -{ -public: - 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 - s32 UsToWaitThisLoop(); - -private: - float m_last_advantage = 0.0f; - 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; -}; - -// l = local, r = remote -s32 Start(s32 lhandle, u16 lport, std::string& raddr, u16 rport, s32 ldelay, u32 pred); - -void Close(); bool IsActive(); -void RunIdle(); -void AdvanceFrame(u16 checksum = 0); -void RunFrame(s32& waitTime); -s32 CurrentFrame(); +/// Runs the VM and netplay loop. when the netplay loop cancels it switches to normal execute mode. +void ExecuteNetplay(); void CollectInput(u32 slot, u32 bind, float value); -Netplay::Input ReadLocalInput(); -std::string& GetGamePath(); -void SetGamePath(std::string& path); void SendMsg(const char* msg); -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 diff --git a/src/core/system.cpp b/src/core/system.cpp index d66c1b953..34e3a9a6a 100644 --- a/src/core/system.cpp +++ b/src/core/system.cpp @@ -75,18 +75,10 @@ SystemBootParameters::SystemBootParameters(std::string filename_) : filename(std SystemBootParameters::~SystemBootParameters() = default; -struct MemorySaveState -{ - std::unique_ptr vram_texture; - std::unique_ptr state_stream; -}; - namespace System { static std::optional InternalGetExtendedSaveStateInfo(ByteStream* stream); static bool InternalSaveState(ByteStream* state, u32 screenshot_size = 256, u32 compression_method = SAVE_STATE_HEADER::COMPRESSION_TYPE_NONE); -static bool SaveMemoryState(MemorySaveState* mss); -static bool LoadMemoryState(const MemorySaveState& mss); static bool LoadEXE(const char* filename); @@ -104,7 +96,8 @@ static void DestroySystem(); static std::string GetMediaPathFromSaveState(const char* path); static bool DoLoadState(ByteStream* stream, bool force_software_renderer, bool update_display); static bool DoState(StateWrapper& sw, GPUTexture** host_texture, bool update_display, bool is_memory_state); -static void DoRunFrame(); +static void WrappedRunFrame(); +static void RunFramesToNow(); static bool CreateGPU(GPURenderer renderer); static bool SaveUndoLoadState(); @@ -197,19 +190,17 @@ static std::unique_ptr m_undo_load_state; static bool s_memory_saves_enabled = false; -static std::deque s_rewind_states; +static std::deque s_rewind_states; static s32 s_rewind_load_frequency = -1; static s32 s_rewind_load_counter = -1; static s32 s_rewind_save_frequency = -1; static s32 s_rewind_save_counter = -1; static bool s_rewinding_first_save = false; -static std::deque s_runahead_states; +static std::deque s_runahead_states; static bool s_runahead_replay_pending = false; static u32 s_runahead_frames = 0; -static std::deque s_netplay_states; - static TinyString GetTimestampStringForFileName() { return TinyString::FromFmt("{:%Y-%m-%d_%H-%M-%S}", fmt::localtime(std::time(nullptr))); @@ -1532,12 +1523,12 @@ void System::ClearRunningGame() void System::Execute() { - while (System::IsRunning()) + while (IsRunning()) { if (s_display_all_frames) - System::RunFrame(); + WrappedRunFrame(); else - System::RunFrames(); + RunFramesToNow(); // this can shut us down Host::PumpMessagesOnCPUThread(); @@ -1550,13 +1541,7 @@ void System::Execute() PauseSystem(true); } - const bool skip_present = g_host_display->ShouldSkipDisplayingFrame(); - Host::RenderDisplay(skip_present); - if (!skip_present && g_host_display->IsGPUTimingEnabled()) - { - s_accumulated_gpu_time += g_host_display->GetAndResetAccumulatedGPUTime(); - s_presents_since_last_update++; - } + PresentFrame(); if (s_throttler_enabled) System::Throttle(); @@ -1568,35 +1553,14 @@ void System::Execute() } } -void System::ExecuteNetplay() +void System::PresentFrame() { - // frame timing - s32 timeToWait; - std::chrono::steady_clock::time_point start, next, now; - start = next = now = std::chrono::steady_clock::now(); - while (Netplay::IsActive() && System::IsRunning()) + const bool skip_present = g_host_display->ShouldSkipDisplayingFrame(); + Host::RenderDisplay(skip_present); + if (!skip_present && g_host_display->IsGPUTimingEnabled()) { - now = std::chrono::steady_clock::now(); - if (now >= next) - { - Netplay::RunFrame(timeToWait); - next = now + std::chrono::microseconds(timeToWait); - s_next_frame_time += timeToWait; - // this can shut us down - Host::PumpMessagesOnCPUThread(); - if (!IsValid() || !Netplay::IsActive()) - break; - - const bool skip_present = g_host_display->ShouldSkipDisplayingFrame(); - Host::RenderDisplay(skip_present); - if (!skip_present && g_host_display->IsGPUTimingEnabled()) - { - s_accumulated_gpu_time += g_host_display->GetAndResetAccumulatedGPUTime(); - s_presents_since_last_update++; - } - - System::UpdatePerformanceCounters(); - } + s_accumulated_gpu_time += g_host_display->GetAndResetAccumulatedGPUTime(); + s_presents_since_last_update++; } } @@ -2191,7 +2155,7 @@ void System::SingleStepCPU() g_gpu->ResetGraphicsAPIState(); } -void System::DoRunFrame() +void System::RunFrame() { g_gpu->RestoreGraphicsAPIState(); @@ -2231,7 +2195,7 @@ void System::DoRunFrame() g_gpu->ResetGraphicsAPIState(); } -void System::RunFrame() +void System::WrappedRunFrame() { if (s_rewind_load_counter >= 0) { @@ -2242,7 +2206,7 @@ void System::RunFrame() if (s_runahead_frames > 0) DoRunahead(); - DoRunFrame(); + RunFrame(); s_next_frame_time += s_frame_period; @@ -2304,7 +2268,7 @@ void System::Throttle() #endif } -void System::RunFrames() +void System::RunFramesToNow() { // If we're running more than this in a single loop... we're in for a bad time. const u32 max_frames_to_run = 2; @@ -2316,7 +2280,7 @@ void System::RunFrames() if (value < s_next_frame_time) break; - RunFrame(); + WrappedRunFrame(); frames_run++; value = Common::Timer::GetCurrentValue(); @@ -3716,7 +3680,7 @@ void System::DoRunahead() while (frames_to_run > 0) { - DoRunFrame(); + RunFrame(); SaveRunaheadState(); frames_to_run--; } @@ -4466,163 +4430,3 @@ void System::SetTimerResolutionIncreased(bool enabled) timeEndPeriod(1); #endif } - -void System::StartNetplaySession(s32 local_handle, u16 local_port, std::string& remote_addr, u16 remote_port, - s32 input_delay, std::string& game_path) -{ - // dont want to start a session when theres already one going on. - if (Netplay::IsActive()) - return; - // set game path for later loading during the begin game callback - Netplay::SetGamePath(game_path); - // set netplay timer - const u32 fps = (s_region == ConsoleRegion::PAL ? 50 : 60); - Netplay::GetTimer()->Init(fps, 180); - // create session - 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); - } -} - -void System::StopNetplaySession() -{ - if (!Netplay::IsActive()) - return; - s_netplay_states.clear(); - Netplay::Close(); -} - -void System::NetplayAdvanceFrame(Netplay::Input inputs[], int disconnect_flags) -{ - Netplay::SetInputs(inputs); - System::DoRunFrame(); - Netplay::AdvanceFrame(); -} - -bool NpBeginGameCb(void* ctx, const char* game_name) -{ - // close system if its already running - if (System::IsValid()) - System::ShutdownSystem(false); - // fast boot the selected game and wait for the other player - auto param = SystemBootParameters(Netplay::GetGamePath()); - param.override_fast_boot = true; - if (!System::BootSystem(param)) - { - System::StopNetplaySession(); - return false; - } - // Fast Forward to Game Start - SPU::SetAudioOutputMuted(true); - while (s_internal_frame_number < 2) - System::DoRunFrame(); - SPU::SetAudioOutputMuted(false); - return true; -} - -bool NpAdvFrameCb(void* ctx, int flags) -{ - Netplay::Input inputs[2] = {}; - int disconnectFlags; - Netplay::SyncInput(inputs, &disconnectFlags); - System::NetplayAdvanceFrame(inputs, disconnectFlags); - return true; -} - -bool NpSaveFrameCb(void* ctx, uint8_t** buffer, int* len, int* checksum, int frame) -{ - bool result = false; - // give ggpo something so it doesnt complain. - u8 dummyData = 43; - *len = sizeof(u8); - *buffer = (unsigned char*)malloc(*len); - if (!*buffer) - return false; - memcpy(*buffer, &dummyData, *len); - // store state for later. - int pred = Netplay::GetMaxPrediction(); - if (frame < pred && s_netplay_states.size() < pred) - { - MemorySaveState save; - result = System::SaveMemoryState(&save); - s_netplay_states.push_back(std::move(save)); - } - else - { - // reuse streams - result = System::SaveMemoryState(&s_netplay_states[frame % pred]); - } - return result; -} - -bool NpLoadFrameCb(void* ctx, uint8_t* buffer, int len, int rb_frames, int frame_to_load) -{ - // Disable Audio For upcoming rollback - SPU::SetAudioOutputMuted(true); - return System::LoadMemoryState(s_netplay_states[frame_to_load % Netplay::GetMaxPrediction()]); -} - -bool NpOnEventCb(void* ctx, GGPOEvent* ev) -{ - char buff[128]; - std::string msg; - switch (ev->code) - { - case GGPOEventCode::GGPO_EVENTCODE_CONNECTED_TO_PEER: - sprintf(buff, "Netplay Connected To Player: %d", ev->u.connected.player); - msg = buff; - break; - case GGPOEventCode::GGPO_EVENTCODE_SYNCHRONIZING_WITH_PEER: - sprintf(buff, "Netplay Synchronzing: %d/%d", ev->u.synchronizing.count, ev->u.synchronizing.total); - msg = buff; - break; - case GGPOEventCode::GGPO_EVENTCODE_SYNCHRONIZED_WITH_PEER: - sprintf(buff, "Netplay Synchronized With Player: %d", ev->u.synchronized.player); - msg = buff; - break; - case GGPOEventCode::GGPO_EVENTCODE_DISCONNECTED_FROM_PEER: - sprintf(buff, "Netplay Player: %d Disconnected", ev->u.disconnected.player); - msg = buff; - break; - case GGPOEventCode::GGPO_EVENTCODE_RUNNING: - msg = "Netplay Is Running"; - break; - case GGPOEventCode::GGPO_EVENTCODE_CONNECTION_INTERRUPTED: - sprintf(buff, "Netplay Player: %d Connection Interupted, Timeout: %d", ev->u.connection_interrupted.player, - ev->u.connection_interrupted.disconnect_timeout); - msg = buff; - break; - case GGPOEventCode::GGPO_EVENTCODE_CONNECTION_RESUMED: - sprintf(buff, "Netplay Player: %d Connection Resumed", ev->u.connection_resumed.player); - msg = buff; - break; - case GGPOEventCode::GGPO_EVENTCODE_CHAT: - sprintf(buff, "%s", ev->u.chat.msg); - msg = buff; - break; - case GGPOEventCode::GGPO_EVENTCODE_TIMESYNC: - 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, - ev->u.desync.ourCheckSum, ev->u.desync.remoteChecksum); - msg = buff; - break; - default: - sprintf(buff, "Netplay Event Code: %d", ev->code); - msg = buff; - } - if (!msg.empty()) - { - Host::OnNetplayMessage(msg); - Log_InfoPrintf("%s", msg.c_str()); - } - return true; -} - -void NpFreeBuffCb(void* ctx, void* buffer) -{ - free(buffer); -} \ No newline at end of file diff --git a/src/core/system.h b/src/core/system.h index 10c05274d..aeae19561 100644 --- a/src/core/system.h +++ b/src/core/system.h @@ -20,6 +20,9 @@ class Controller; struct CheatCode; class CheatList; +class GPUTexture; +class GrowableMemoryByteStream; + namespace BIOS { struct ImageInfo; struct Hash; @@ -224,12 +227,18 @@ bool LoadState(const char* filename); bool SaveState(const char* filename, bool backup_existing_save); bool SaveResumeState(); +/// Memory save states - only for internal use. +struct MemorySaveState +{ + std::unique_ptr vram_texture; + std::unique_ptr state_stream; +}; +bool SaveMemoryState(MemorySaveState* mss); +bool LoadMemoryState(const MemorySaveState& mss); + /// Runs the VM until the CPU execution is canceled. void Execute(); -/// Runs the VM and netplay loop. when the netplay loop cancels it switches to normal execute mode. -void ExecuteNetplay(); - /// Switches the GPU renderer by saving state, recreating the display window, and restoring state (if needed). void RecreateSystem(); @@ -238,7 +247,7 @@ bool RecreateGPU(GPURenderer renderer, bool force_recreate_display = false, bool void SingleStepCPU(); void RunFrame(); -void RunFrames(); +void PresentFrame(); /// Sets target emulation speed. float GetTargetSpeed(); @@ -454,12 +463,6 @@ void ClearMemorySaveStates(); void UpdateMemorySaveStateSettings(); bool LoadRewindState(u32 skip_saves = 0, bool consume_state = true); void SetRunaheadReplayFlag(); - -/// Netplay -void StartNetplaySession(s32 local_handle, u16 local_port, std::string& remote_addr, u16 remote_port, s32 input_delay, - std::string& game_path); -void StopNetplaySession(); -void NetplayAdvanceFrame(Netplay::Input inputs[], int disconnect_flags); } // namespace System namespace Host { diff --git a/src/duckstation-qt/qthost.cpp b/src/duckstation-qt/qthost.cpp index 1712ee8f9..52175cce0 100644 --- a/src/duckstation-qt/qthost.cpp +++ b/src/duckstation-qt/qthost.cpp @@ -1090,7 +1090,7 @@ void EmuThread::startNetplaySession(int local_handle, quint16 local_port, const auto remAddr = remote_addr.trimmed().toStdString(); auto gamePath = game_path.trimmed().toStdString(); - System::StartNetplaySession(local_handle, local_port, remAddr, remote_port, input_delay, gamePath); + Netplay::StartNetplaySession(local_handle, local_port, remAddr, remote_port, input_delay, gamePath); } void EmuThread::sendNetplayMessage(const QString& message) @@ -1110,7 +1110,7 @@ void EmuThread::stopNetplaySession() QMetaObject::invokeMethod(this, "stopNetplaySession", Qt::QueuedConnection); return; } - System::StopNetplaySession(); + Netplay::StopNetplaySession(); } void EmuThread::runOnEmuThread(std::function callback) @@ -1464,7 +1464,7 @@ void EmuThread::run() { if (Netplay::IsActive()) { - System::ExecuteNetplay(); + Netplay::ExecuteNetplay(); } else if (System::IsRunning()) {