diff --git a/Source/Core/Core/NetPlayClient.cpp b/Source/Core/Core/NetPlayClient.cpp index 9eff4e5846..3f95bc4ac6 100644 --- a/Source/Core/Core/NetPlayClient.cpp +++ b/Source/Core/Core/NetPlayClient.cpp @@ -45,6 +45,7 @@ #include "Core/GeckoCode.h" #include "Core/HW/EXI/EXI.h" #include "Core/HW/EXI/EXI_DeviceIPL.h" +#include "Core/State.h" #ifdef HAS_LIBMGBA #include "Core/HW/GBACore.h" #endif @@ -975,10 +976,11 @@ void NetPlayClient::OnStartGame(sf::Packet& packet) } inputs.clear(); - for (int i = 0; i < m_players.size(); i++) inputs.push_back(std::vector{}); + save_states.reset(); + m_dialog->OnMsgStartGame(); } @@ -1634,6 +1636,47 @@ void NetPlayClient::OnFrameEnd() if (send_packet) SendAsync(std::move(packet)); + + lock.unlock(); + std::shared_ptr new_save_state = std::make_shared(); + State::SaveToBuffer(*new_save_state); + lock.lock(); + save_states.New() = new_save_state; + + // Wait for inputs if others are behind us, continue if we're behind them + int local_player_port = -1; + for (int i = 0; i < m_pad_map.size(); i++) + { + if (m_pad_map.at(i) == m_local_player->pid) + local_player_port = i; + } + + for (int remote_players = 0; remote_players < inputs.size(); remote_players++) + { + if (remote_players == local_player_port) + continue; + + auto frame_difference = static_cast(inputs.at(local_player_port).size()) - + static_cast(inputs.at(remote_players).size()); + + if (frame_difference <= 0) + { + continue; + } + else + { + while (frame_difference > rollback_frames_supported + delay) + { + if (!m_is_running.IsSet()) + break; + + frame_difference = static_cast(inputs.at(local_player_port).size()) - + static_cast(inputs.at(remote_players).size()); + + wait_for_inputs.wait_for(lock, 1ms); + } + } + } } void NetPlayClient::Send(const sf::Packet& packet, const u8 channel_id) diff --git a/Source/Core/Core/NetPlayClient.h b/Source/Core/Core/NetPlayClient.h index 68f5f6c900..b86971c7d8 100644 --- a/Source/Core/Core/NetPlayClient.h +++ b/Source/Core/Core/NetPlayClient.h @@ -43,7 +43,35 @@ struct SerializedWiimoteState; namespace NetPlay { -constexpr size_t rollback_frames_supported = 10; +constexpr int rollback_frames_supported = 10; +using SaveState = std::vector; +// 0 is the closest SaveState in time from the current frame +class SaveStateArray +{ +public: + std::shared_ptr& New() + { + std::array, rollback_frames_supported> new_array{}; + + for (int i = rollback_frames_supported - 2; i >= 0; i--) + { + new_array.at(i + 1) = main_array.at(i); + } + new_array.at(0) = std::shared_ptr{}; + main_array = std::move(new_array); + + return main_array.at(0); + }; + + void reset() + { + for (auto& save_state : main_array) + save_state = std::shared_ptr{}; + } + +private: + std::array, rollback_frames_supported> main_array; +}; class NetPlayUI { @@ -364,6 +392,8 @@ private: std::vector> inputs; int delay = 2; + std::condition_variable wait_for_inputs; + SaveStateArray save_states; }; void NetPlay_Enable(NetPlayClient* const np);