Resolve merge conflicts

This commit is contained in:
Jared M. White 2024-07-04 13:08:06 -05:00
commit ad37599dba
13 changed files with 529 additions and 149 deletions

View file

@ -8,6 +8,7 @@
#include <cstring>
#include <fstream>
#include <functional>
#include <latch>
#include <memory>
#include <mutex>
#include <span>
@ -40,9 +41,13 @@
#include "Core/Config/SessionSettings.h"
#include "Core/Config/WiimoteSettings.h"
#include "Core/ConfigManager.h"
#include "Core/Core.h"
#include "Core/GeckoCode.h"
#include "Core/HW/EXI/EXI.h"
#include "Core/HW/EXI/EXI_DeviceIPL.h"
#include "Core/PowerPC/CPUCoreBase.h"
#include "Core/PowerPC/JitInterface.h"
#include "Core/State.h"
#ifdef HAS_LIBMGBA
#include "Core/HW/GBACore.h"
#endif
@ -75,6 +80,8 @@
#include "UICommon/GameFile.h"
#include "VideoCommon/OnScreenDisplay.h"
#include "VideoCommon/VideoConfig.h"
#include "Common/HookableEvent.h"
#include "VideoCommon/VideoEvents.h"
namespace NetPlay
{
@ -83,6 +90,11 @@ using namespace WiimoteCommon;
static std::mutex crit_netplay_client;
static NetPlayClient* netplay_client = nullptr;
static bool s_si_poll_batching = false;
static std::atomic<bool> is_rollingback;
static Common::EventHook s_after_frame_event = AfterFrameEvent::Register(
[](const Core::System& system) { OnFrameEnd(); },
"Netplay::OnFrameEnd");
// called from ---GUI--- thread
NetPlayClient::~NetPlayClient()
@ -689,10 +701,22 @@ void NetPlayClient::OnPadData(sf::Packet& packet)
pad.substickY >> pad.triggerLeft >> pad.triggerRight >> pad.isConnected;
}
// Trusting server for good map value (>=0 && <4)
// add to pad buffer
m_pad_buffer.at(map).Push(pad);
m_gc_pad_event.Set();
if (m_net_settings.m_RollbackMode)
{
{
std::lock_guard lock(crit_netplay_client);
inputs.at(map).push_back(pad);
}
wait_for_inputs.notify_all();
}
else
{
// Trusting server for good map value (>=0 && <4)
// add to pad buffer
m_pad_buffer.at(map).Push(pad);
m_gc_pad_event.Set();
}
}
}
@ -924,7 +948,7 @@ void NetPlayClient::OnStartGame(sf::Packet& packet)
packet >> m_net_settings.save_data_region;
packet >> m_net_settings.sync_codes;
packet >> m_net_settings.m_RollbackMode;
packet >> m_net_settings.golf_mode;
packet >> m_net_settings.use_fma;
packet >> m_net_settings.hide_remote_gbas;
@ -935,6 +959,13 @@ void NetPlayClient::OnStartGame(sf::Packet& packet)
m_net_settings.is_hosting = m_local_player->IsHost();
}
inputs.clear();
for (int i = 0; i < m_players.size(); i++)
inputs.push_back(std::vector<GCPadStatus>{GCPadStatus{}});
save_states.reset();
current_frame = 0;
m_dialog->OnMsgStartGame();
}
@ -981,22 +1012,25 @@ void NetPlayClient::OnPlayerPingData(sf::Packet& packet)
void NetPlayClient::OnDesyncDetected(sf::Packet& packet)
{
int pid_to_blame;
u32 frame;
packet >> pid_to_blame;
packet >> frame;
std::string player = "??";
std::lock_guard lkp(m_crit.players);
if (!m_net_settings.m_RollbackMode)
{
const auto it = m_players.find(pid_to_blame);
if (it != m_players.end())
player = it->second.name;
int pid_to_blame;
u32 frame;
packet >> pid_to_blame;
packet >> frame;
std::string player = "??";
std::lock_guard lkp(m_crit.players);
{
const auto it = m_players.find(pid_to_blame);
if (it != m_players.end())
player = it->second.name;
}
INFO_LOG_FMT(NETPLAY, "Player {} ({}) desynced!", player, pid_to_blame);
m_dialog->OnDesync(frame, player);
}
INFO_LOG_FMT(NETPLAY, "Player {} ({}) desynced!", player, pid_to_blame);
m_dialog->OnDesync(frame, player);
}
void NetPlayClient::OnSyncSaveData(sf::Packet& packet)
@ -1520,6 +1554,128 @@ void NetPlayClient::OnGameDigestAbort()
m_dialog->AbortGameDigest();
}
bool NetPlayClient::LoadFromFrame(u64 frame)
{
auto save_state = std::find_if(
save_states.main_array.begin(), save_states.main_array.end(),
[frame](const std::shared_ptr<SaveState>& save) { return save->second == frame; });
if (save_state == save_states.main_array.end())
{
return false;
}
else
{
State::LoadFromBuffer(Core::System::GetInstance(), (**save_state).first);
return true;
}
}
void NetPlayClient::RollbackToFrame(u64 frame)
{
is_rollingback = true;
if (LoadFromFrame(frame))
{
frame_to_stop_at = current_frame;
current_frame = frame;
Config::SetCurrent(Config::MAIN_EMULATION_SPEED, 0.0);
}
else
{
is_rollingback = false;
DEBUG_LOG_FMT(NETPLAY, "Failed to roll back to frame {}!", frame);
}
}
void NetPlayClient::OnFrameEnd(std::unique_lock<std::mutex>& lock)
{
// this function is only called in rollback mode, but the logic to skip it is in
// OnFrameEnd() (the one not in NetPlayClient::)
sf::Packet packet;
packet << MessageID::PadData;
bool send_packet = false;
const int num_local_pads = NumLocalPads();
for (int local_pad = 0; local_pad < num_local_pads; local_pad++)
{
// inputs for local players are acquired here
send_packet = PollLocalPad(local_pad, packet) || send_packet;
}
if (send_packet)
SendAsync(std::move(packet));
std::shared_ptr<SaveState> new_save_state =
std::make_shared<SaveState>(std::vector<u8>{}, current_frame);
Core::System& system = Core::System::GetInstance();
auto save_state_lambda = [&system, new_save_state]() {
State::SaveToBuffer(system, new_save_state->first);
};
system.GetJitInterface().GetCore()->RegisterCPUFunction(save_state_lambda);
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;
}
bool needs_to_rollback = false;
u64 farthest_rollback_frame = 0;
for (int remote_players = 0; remote_players < inputs.size(); remote_players++)
{
auto frame_difference = static_cast<long long>(inputs.at(local_player_port).size()) -
static_cast<long long>(inputs.at(remote_players).size());
if (remote_players == local_player_port)
continue;
if (frame_difference <= delay)
{
continue;
}
else
{
if (frame_difference <= rollback_frames_supported + delay)
{
needs_to_rollback = true;
farthest_rollback_frame = std::max(inputs.at(local_player_port).size() - frame_difference,
farthest_rollback_frame);
}
else
{
while (frame_difference > rollback_frames_supported + delay)
{
frame_difference = static_cast<long long>(inputs.at(local_player_port).size()) -
static_cast<long long>(inputs.at(remote_players).size());
wait_for_inputs.wait_for(lock, 1ms);
}
needs_to_rollback = true;
farthest_rollback_frame = rollback_frames_supported + delay;
remote_players = 0;
}
}
}
if (needs_to_rollback)
RollbackToFrame(farthest_rollback_frame);
}
bool NetPlayClient::IsRollingBack()
{
return is_rollingback.load();
}
bool NetPlayClient::IsInRollbackMode()
{
return m_net_settings.m_RollbackMode;
}
void NetPlayClient::Send(const sf::Packet& packet, const u8 channel_id)
{
Common::ENet::SendPacket(m_server, packet, channel_id);
@ -1992,129 +2148,138 @@ void NetPlayClient::OnConnectFailed(Common::TraversalConnectFailedReason reason)
// called from ---CPU--- thread
bool NetPlayClient::GetNetPads(const int pad_nb, const bool batching, GCPadStatus* pad_status)
{
// The interface for this is extremely silly.
//
// Imagine a physical device that links three GameCubes together
// and emulates NetPlay that way. Which GameCube controls which
// in-game controllers can be configured on the device (m_pad_map)
// but which sockets on each individual GameCube should be used
// to control which players? The solution that Dolphin uses is
// that we hardcode the knowledge that they go in order, so if
// you have a 3P game with three GameCubes, then every single
// controller should be plugged into slot 1.
//
// If you have a 4P game, then one of the GameCubes will have
// a controller plugged into slot 1, and another in slot 2.
//
// The slot number is the "local" pad number, and what player
// it actually means is the "in-game" pad number.
// When the 1st in-game pad is polled and batching is set, the
// others will be polled as well. To reduce latency, we poll all
// local controllers at once and then send the status to the other
// clients.
//
// Batching is enabled when polled from VI. If batching is not
// enabled, the poll is probably from MMIO, which can poll any
// specific pad arbitrarily. In this case, we poll just that pad
// and send it.
// When here when told to so we don't deadlock in certain situations
while (m_wait_on_input)
if (m_net_settings.m_RollbackMode)
{
if (!m_is_running.IsSet())
{
return false;
}
if (m_wait_on_input_received)
{
// Tell the server we've acknowledged the message
sf::Packet spac;
spac << MessageID::GolfPrepare;
Send(spac);
m_wait_on_input_received = false;
}
m_wait_on_input_event.Wait();
if (is_rollingback && inputs.at(pad_nb).size() > current_frame)
*pad_status = inputs.at(pad_nb).at(current_frame);
else
*pad_status = inputs.at(pad_nb).back();
}
if (IsFirstInGamePad(pad_nb) && batching)
else
{
sf::Packet packet;
packet << MessageID::PadData;
// The interface for this is extremely silly.
//
// Imagine a physical device that links three GameCubes together
// and emulates NetPlay that way. Which GameCube controls which
// in-game controllers can be configured on the device (m_pad_map)
// but which sockets on each individual GameCube should be used
// to control which players? The solution that Dolphin uses is
// that we hardcode the knowledge that they go in order, so if
// you have a 3P game with three GameCubes, then every single
// controller should be plugged into slot 1.
//
// If you have a 4P game, then one of the GameCubes will have
// a controller plugged into slot 1, and another in slot 2.
//
// The slot number is the "local" pad number, and what player
// it actually means is the "in-game" pad number.
bool send_packet = false;
const int num_local_pads = NumLocalPads();
for (int local_pad = 0; local_pad < num_local_pads; local_pad++)
// When the 1st in-game pad is polled and batching is set, the
// others will be polled as well. To reduce latency, we poll all
// local controllers at once and then send the status to the other
// clients.
//
// Batching is enabled when polled from VI. If batching is not
// enabled, the poll is probably from MMIO, which can poll any
// specific pad arbitrarily. In this case, we poll just that pad
// and send it.
// When here when told to so we don't deadlock in certain situations
while (m_wait_on_input)
{
send_packet = PollLocalPad(local_pad, packet) || send_packet;
if (!m_is_running.IsSet())
{
return false;
}
if (m_wait_on_input_received)
{
// Tell the server we've acknowledged the message
sf::Packet spac;
spac << MessageID::GolfPrepare;
Send(spac);
m_wait_on_input_received = false;
}
m_wait_on_input_event.Wait();
}
if (send_packet)
SendAsync(std::move(packet));
if (m_host_input_authority)
SendPadHostPoll(-1);
}
if (!batching)
{
const int local_pad = InGamePadToLocalPad(pad_nb);
if (local_pad < 4)
if (IsFirstInGamePad(pad_nb) && batching)
{
sf::Packet packet;
packet << MessageID::PadData;
if (PollLocalPad(local_pad, packet))
bool send_packet = false;
const int num_local_pads = NumLocalPads();
for (int local_pad = 0; local_pad < num_local_pads; local_pad++)
{
send_packet = PollLocalPad(local_pad, packet) || send_packet;
}
if (send_packet)
SendAsync(std::move(packet));
if (m_host_input_authority)
SendPadHostPoll(-1);
}
if (!batching)
{
const int local_pad = InGamePadToLocalPad(pad_nb);
if (local_pad < 4)
{
sf::Packet packet;
packet << MessageID::PadData;
if (PollLocalPad(local_pad, packet))
SendAsync(std::move(packet));
}
if (m_host_input_authority)
SendPadHostPoll(pad_nb);
}
if (m_host_input_authority)
SendPadHostPoll(pad_nb);
}
if (m_host_input_authority)
{
if (m_local_player->pid != m_current_golfer)
{
// CoreTiming acts funny and causes what looks like frame skip if
// we toggle the emulation speed too quickly, so to prevent this
// we wait until the buffer has been over for at least 1 second.
const bool buffer_over_target = m_pad_buffer[pad_nb].Size() > m_target_buffer_size + 1;
if (!buffer_over_target)
m_buffer_under_target_last = std::chrono::steady_clock::now();
std::chrono::duration<double> time_diff =
std::chrono::steady_clock::now() - m_buffer_under_target_last;
if (time_diff.count() >= 1.0 || !buffer_over_target)
if (m_local_player->pid != m_current_golfer)
{
// run fast if the buffer is overfilled, otherwise run normal speed
Config::SetCurrent(Config::MAIN_EMULATION_SPEED, buffer_over_target ? 0.0f : 1.0f);
// CoreTiming acts funny and causes what looks like frame skip if
// we toggle the emulation speed too quickly, so to prevent this
// we wait until the buffer has been over for at least 1 second.
const bool buffer_over_target = m_pad_buffer[pad_nb].Size() > m_target_buffer_size + 1;
if (!buffer_over_target)
m_buffer_under_target_last = std::chrono::steady_clock::now();
std::chrono::duration<double> time_diff =
std::chrono::steady_clock::now() - m_buffer_under_target_last;
if (time_diff.count() >= 1.0 || !buffer_over_target)
{
// run fast if the buffer is overfilled, otherwise run normal speed
Config::SetCurrent(Config::MAIN_EMULATION_SPEED, buffer_over_target ? 0.0f : 1.0f);
}
}
else
{
// Set normal speed when we're the host, otherwise it can get stuck at unlimited
Config::SetCurrent(Config::MAIN_EMULATION_SPEED, 1.0f);
}
}
else
{
// Set normal speed when we're the host, otherwise it can get stuck at unlimited
Config::SetCurrent(Config::MAIN_EMULATION_SPEED, 1.0f);
}
}
// Now, we either use the data pushed earlier, or wait for the
// other clients to send it to us
while (m_pad_buffer[pad_nb].Size() == 0)
{
if (!m_is_running.IsSet())
// Now, we either use the data pushed earlier, or wait for the
// other clients to send it to us
while (m_pad_buffer[pad_nb].Size() == 0)
{
return false;
if (!m_is_running.IsSet())
{
return false;
}
m_gc_pad_event.Wait();
}
m_gc_pad_event.Wait();
m_pad_buffer[pad_nb].Pop(*pad_status);
}
m_pad_buffer[pad_nb].Pop(*pad_status);
auto& movie = Core::System::GetInstance().GetMovie();
if (movie.IsRecordingInput())
{
@ -2193,33 +2358,42 @@ bool NetPlayClient::PollLocalPad(const int local_pad, sf::Packet& packet)
pad_status = Pad::GetStatus(local_pad);
}
if (m_host_input_authority)
if (m_net_settings.m_RollbackMode)
{
if (m_local_player->pid != m_current_golfer)
{
// add to packet
AddPadStateToPacket(ingame_pad, pad_status, packet);
data_added = true;
}
else
{
// set locally
m_last_pad_status[ingame_pad] = pad_status;
m_first_pad_status_received[ingame_pad] = true;
}
inputs.at(ingame_pad).push_back(pad_status);
AddPadStateToPacket(ingame_pad, pad_status, packet);
data_added = true;
}
else
{
// adjust the buffer either up or down
// inserting multiple padstates or dropping states
while (m_pad_buffer[ingame_pad].Size() <= m_target_buffer_size)
if (m_host_input_authority)
{
// add to buffer
m_pad_buffer[ingame_pad].Push(pad_status);
if (m_local_player->pid != m_current_golfer)
{
// add to packet
AddPadStateToPacket(ingame_pad, pad_status, packet);
data_added = true;
}
else
{
// set locally
m_last_pad_status[ingame_pad] = pad_status;
m_first_pad_status_received[ingame_pad] = true;
}
}
else
{
// adjust the buffer either up or down
// inserting multiple padstates or dropping states
while (m_pad_buffer[ingame_pad].Size() <= m_target_buffer_size)
{
// add to buffer
m_pad_buffer[ingame_pad].Push(pad_status);
// add to packet
AddPadStateToPacket(ingame_pad, pad_status, packet);
data_added = true;
// add to packet
AddPadStateToPacket(ingame_pad, pad_status, packet);
data_added = true;
}
}
}
@ -2255,12 +2429,13 @@ void NetPlayClient::SendPadHostPoll(const PadIndex pad_num)
// pads (used for batched polls), while 0..3 will poll the respective pad (used for MMIO polls).
// See GetNetPads for more details.
//
// If the local buffer is non-empty, we skip actually buffering and sending new pad data, this way
// don't end up with permanent local latency. It does create a period of time where no inputs are
// accepted, but under typical circumstances this is not noticeable.
// If the local buffer is non-empty, we skip actually buffering and sending new pad data, this
// way don't end up with permanent local latency. It does create a period of time where no
// inputs are accepted, but under typical circumstances this is not noticeable.
//
// Additionally, we wait until some actual pad data has been received before buffering and sending
// it, otherwise controllers get calibrated wrongly with the default values of GCPadStatus.
// Additionally, we wait until some actual pad data has been received before buffering and
// sending it, otherwise controllers get calibrated wrongly with the default values of
// GCPadStatus.
if (m_local_player->pid != m_current_golfer)
return;
@ -2767,6 +2942,39 @@ void NetPlay_Disable()
std::lock_guard lk(crit_netplay_client);
netplay_client = nullptr;
}
void OnFrameEnd()
{
if (IsNetPlayRunning() && netplay_client)
{
if (netplay_client->IsInRollbackMode())
{
if (!is_rollingback)
{
std::unique_lock lock(crit_netplay_client);
netplay_client->OnFrameEnd(lock);
}
else if (netplay_client->current_frame >= netplay_client->frame_to_stop_at)
{
Config::SetCurrent(Config::MAIN_EMULATION_SPEED, 1.0);
is_rollingback = false;
}
netplay_client->current_frame++;
}
}
}
bool IsRollingBack()
{
return netplay_client->IsRollingBack();
}
bool IsInRollbackMode()
{
return netplay_client->IsInRollbackMode();
}
} // namespace NetPlay
// stuff hacked into dolphin

View file

@ -43,6 +43,35 @@ struct SerializedWiimoteState;
namespace NetPlay
{
constexpr int rollback_frames_supported = 10;
using SaveState = std::pair<std::vector<u8>, u64>;
// 0 is the closest SaveState in time from the current frame
class SaveStateArray
{
public:
std::shared_ptr<SaveState>& New()
{
std::array<std::shared_ptr<SaveState>, 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<SaveState>{};
main_array = std::move(new_array);
return main_array.at(0);
};
void reset()
{
for (auto& save_state : main_array)
save_state = std::shared_ptr<SaveState>{};
}
std::array<std::shared_ptr<SaveState>, rollback_frames_supported> main_array;
};
class NetPlayUI
{
public:
@ -179,6 +208,17 @@ public:
static SyncIdentifier GetSDCardIdentifier();
void OnFrameEnd(std::unique_lock<std::mutex>& lock);
bool IsRollingBack();
bool IsInRollbackMode();
// Only for use in NetPlayClient.cpp >:(
size_t current_frame = 0;
// Only for use in NetPlayClient.cpp >:(
size_t frame_to_stop_at = 0;
bool done_fast_forwarding;
protected:
struct AsyncQueueEntry
{
@ -350,10 +390,24 @@ private:
std::unique_ptr<IOS::HLE::FS::FileSystem> m_wii_sync_fs;
std::vector<u64> m_wii_sync_titles;
std::string m_wii_sync_redirect_folder;
std::vector<std::vector<GCPadStatus>> inputs;
int delay = 2;
std::condition_variable wait_for_inputs;
SaveStateArray save_states;
bool LoadFromFrame(u64 frame);
void RollbackToFrame(u64 frame);
};
void NetPlay_Enable(NetPlayClient* const np);
void NetPlay_Disable();
bool NetPlay_GetWiimoteData(const std::span<NetPlayClient::WiimoteDataBatchEntry>& entries);
unsigned int NetPlay_GetLocalWiimoteForSlot(unsigned int slot);
void OnFrameEnd();
// tells when Dolphin is actually mid rollback
bool IsRollingBack();
// tells if we're using rollback networking
bool IsInRollbackMode();
} // namespace NetPlay

View file

@ -105,6 +105,7 @@ struct NetSettings
bool golf_mode = false;
bool use_fma = false;
bool hide_remote_gbas = false;
bool m_RollbackMode = false;
Sram sram;

View file

@ -1450,6 +1450,7 @@ bool NetPlayServer::SetupNetSettings()
settings.strict_settings_sync = Config::Get(Config::NETPLAY_STRICT_SETTINGS_SYNC);
settings.sync_codes = Config::Get(Config::NETPLAY_SYNC_CODES);
settings.golf_mode = Config::Get(Config::NETPLAY_NETWORK_MODE) == "golf";
settings.m_RollbackMode = Config::Get(Config::NETPLAY_NETWORK_MODE) == "rollback";
settings.use_fma = DoAllPlayersHaveHardwareFMA();
settings.hide_remote_gbas = Config::Get(Config::NETPLAY_HIDE_REMOTE_GBAS);
@ -1659,6 +1660,7 @@ bool NetPlayServer::StartGame()
spac << m_settings.golf_mode;
spac << m_settings.use_fma;
spac << m_settings.hide_remote_gbas;
spac << m_settings.m_RollbackMode;
for (size_t i = 0; i < sizeof(m_settings.sram); ++i)
spac << m_settings.sram[i];

View file

@ -10,6 +10,7 @@ public:
virtual void Init() = 0;
virtual void Shutdown() = 0;
virtual void ClearCache() = 0;
virtual void RegisterCPUFunction(std::function<void()> function){};
virtual void Run() = 0;
virtual void SingleStep() = 0;
virtual const char* GetName() const = 0;

View file

@ -4,6 +4,7 @@
#include "Core/PowerPC/Jit64/Jit.h"
#include <map>
#include <mutex>
#include <sstream>
#include <string>
@ -31,6 +32,7 @@
#include "Core/HW/Memmap.h"
#include "Core/HW/ProcessorInterface.h"
#include "Core/MachineContext.h"
#include "Core/NetPlayClient.h"
#include "Core/PatchEngine.h"
#include "Core/PowerPC/Interpreter/Interpreter.h"
#include "Core/PowerPC/Jit64/JitAsm.h"
@ -879,6 +881,18 @@ bool Jit64::DoJit(u32 em_address, JitBlock* b, u32 nextPC)
// TODO: Test if this or AlignCode16 make a difference from GetCodePtr
b->normalEntry = AlignCode4();
{
std::unique_lock lock(m_external_functions_mutex);
while (!m_external_functions.empty())
{
auto external_function = m_external_functions.front();
m_external_functions.pop();
lock.unlock();
external_function();
lock.lock();
}
}
// Used to get a trace of the last few blocks before a crash, sometimes VERY useful
if (m_im_here_debug)
{

View file

@ -19,6 +19,7 @@
#include <optional>
#include <queue>
#include <rangeset/rangesizeset.h>
#include "Common/CommonTypes.h"
@ -106,6 +107,13 @@ public:
bool Cleanup();
// Runs a function on the CPU during the next JIT compilation
void RegisterCPUFunction(std::function<void()> function) override
{
std::lock_guard lock(m_external_functions_mutex);
m_external_functions.push(function);
}
void GenerateConstantOverflow(bool overflow);
void GenerateConstantOverflow(s64 val);
void GenerateOverflow(Gen::CCFlags cond = Gen::CCFlags::CC_NO);
@ -284,6 +292,9 @@ private:
const bool m_im_here_debug = false;
const bool m_im_here_log = false;
std::map<u32, int> m_been_here;
std::mutex m_external_functions_mutex;
std::queue<std::function<void()>> m_external_functions{};
};
void LogGeneratedX86(size_t size, const PPCAnalyst::CodeBuffer& code_buffer, const u8* normalEntry,

View file

@ -204,9 +204,75 @@ static void DoState(Core::System& system, PointerWrap& p)
#endif // USE_RETRO_ACHIEVEMENTS
}
void DecompressBuffer(std::vector<u8>& buffer)
{
//std::vector<u8> return_vector{};
//size_t i = 0;
//while (true)
//{
// lzo_uint32 cur_len = 0; // number of bytes to read
// lzo_uint new_len = 0; // number of bytes to write
// if (i > buffer.size())
// break;
// std::memcpy(&cur_len, buffer.data() + i, sizeof(cur_len));
// std::memcpy(out, buffer.data() + i, cur_len);
// const int res = lzo1x_decompress(out, cur_len, &buffer[i], &new_len, nullptr);
// if (res != LZO_E_OK)
// {
// // This doesn't seem to happen anymore.
// PanicAlertFmtT("Internal LZO Error - decompression failed ({0}) ({1}, {2}) \n"
// "Try loading the state again",
// res, i, new_len);
// return;
// }
// i += new_len;
//}
}
void CompressBuffer(std::vector<u8>& buffer)
{
/* std::vector<u8> return_vector{};
lzo_uint i = 0;
while (true)
{
lzo_uint32 cur_len = 0;
lzo_uint out_len = 0;
if ((i + IN_LEN) >= buffer.size())
{
cur_len = (lzo_uint32)(buffer.size() - i);
}
else
{
cur_len = IN_LEN;
}
if (lzo1x_1_compress(buffer.data() + i, cur_len, out, &out_len, wrkmem) != LZO_E_OK)
PanicAlertFmtT("Internal LZO Error - compression failed");
size_t old_size = return_vector.size();
return_vector.resize(sizeof(out_len));
std::memcpy(return_vector.data() + old_size, &out_len, sizeof(out_len));
old_size = return_vector.size();
return_vector.resize(out_len);
std::memcpy(return_vector.data() + old_size, out, out_len);
if (cur_len != IN_LEN)
break;
i += cur_len;
}
buffer = return_vector;*/
}
void LoadFromBuffer(Core::System& system, std::vector<u8>& buffer)
{
if (NetPlay::IsNetPlayRunning())
if (!NetPlay::IsInRollbackMode() && NetPlay::IsNetPlayRunning())
{
OSD::AddMessage("Loading savestates is disabled in Netplay to prevent desyncs");
return;

View file

@ -188,6 +188,9 @@ void NetPlayDialog::CreateMainLayout()
"configured by the host.\nSuitable for competitive games where fairness and minimal "
"latency are most important."));
m_fixed_delay_action->setCheckable(true);
m_rollback_action = m_network_menu->addAction(tr("Rollback"));
m_rollback_action->setToolTip(tr("[WIP]"));
m_rollback_action->setCheckable(true);
m_host_input_authority_action = m_network_menu->addAction(tr("Host Input Authority"));
m_host_input_authority_action->setToolTip(
tr("Host has control of sending all inputs to the game, as received from other players, "
@ -204,6 +207,7 @@ void NetPlayDialog::CreateMainLayout()
m_network_mode_group = new QActionGroup(this);
m_network_mode_group->setExclusive(true);
m_network_mode_group->addAction(m_fixed_delay_action);
m_network_mode_group->addAction(m_rollback_action);
m_network_mode_group->addAction(m_host_input_authority_action);
m_network_mode_group->addAction(m_golf_mode_action);
m_fixed_delay_action->setChecked(true);
@ -377,6 +381,7 @@ void NetPlayDialog::ConnectWidgets()
[hia_function] { hia_function(true); });
connect(m_golf_mode_action, &QAction::toggled, this, [hia_function] { hia_function(true); });
connect(m_fixed_delay_action, &QAction::toggled, this, [hia_function] { hia_function(false); });
connect(m_rollback_action, &QAction::toggled, this, [hia_function] { hia_function(false); });
connect(m_start_button, &QPushButton::clicked, this, &NetPlayDialog::OnStart);
connect(m_quit_button, &QPushButton::clicked, this, &NetPlayDialog::reject);
@ -425,6 +430,7 @@ void NetPlayDialog::ConnectWidgets()
connect(m_golf_mode_action, &QAction::toggled, this, &NetPlayDialog::SaveSettings);
connect(m_golf_mode_overlay_action, &QAction::toggled, this, &NetPlayDialog::SaveSettings);
connect(m_fixed_delay_action, &QAction::toggled, this, &NetPlayDialog::SaveSettings);
connect(m_rollback_action, &QAction::toggled, this, &NetPlayDialog::SaveSettings);
connect(m_hide_remote_gbas_action, &QAction::toggled, this, &NetPlayDialog::SaveSettings);
}
@ -863,6 +869,7 @@ void NetPlayDialog::SetOptionsEnabled(bool enabled)
m_host_input_authority_action->setEnabled(enabled);
m_golf_mode_action->setEnabled(enabled);
m_fixed_delay_action->setEnabled(enabled);
m_rollback_action->setEnabled(enabled);
}
m_record_input_action->setEnabled(enabled);
@ -1158,6 +1165,10 @@ void NetPlayDialog::LoadSettings()
{
m_fixed_delay_action->setChecked(true);
}
else if (network_mode == "rollback")
{
m_rollback_action->setChecked(true);
}
else if (network_mode == "hostinputauthority")
{
m_host_input_authority_action->setChecked(true);
@ -1204,6 +1215,10 @@ void NetPlayDialog::SaveSettings()
{
network_mode = "hostinputauthority";
}
else if (m_rollback_action->isChecked())
{
network_mode = "rollback";
}
else if (m_golf_mode_action->isChecked())
{
network_mode = "golf";

View file

@ -156,6 +156,7 @@ private:
QAction* m_host_input_authority_action;
QAction* m_golf_mode_action;
QAction* m_golf_mode_overlay_action;
QAction* m_rollback_action;
QAction* m_fixed_delay_action;
QAction* m_hide_remote_gbas_action;
QPushButton* m_quit_button;

View file

@ -120,6 +120,9 @@ public:
bool IsUSBKeyboardConnected() const;
void SetUSBKeyboardConnected(bool connected);
void SetIsContinuouslyFrameStepping(bool is_stepping);
bool GetIsContinuouslyFrameStepping() const;
// Graphics
Config::ShowCursor GetCursorVisibility() const;
bool GetLockCursor() const;
@ -229,6 +232,8 @@ private:
Settings();
bool m_batch = false;
std::atomic<bool> m_continuously_frame_stepping = false;
std::shared_ptr<NetPlay::NetPlayClient> m_client;
std::shared_ptr<NetPlay::NetPlayServer> m_server;
ControllerInterface::HotplugCallbackHandle m_hotplug_callback_handle;

View file

@ -21,6 +21,7 @@
#include "Core/HW/Memmap.h"
#include "Core/HW/VideoInterface.h"
#include "Core/System.h"
#include "Core/NetPlayClient.h"
#include "VideoCommon/BPFunctions.h"
#include "VideoCommon/BPMemory.h"
@ -355,7 +356,7 @@ static void BPWritten(PixelShaderManager& pixel_shader_manager, XFStateManager&
// display.
auto& system = Core::System::GetInstance();
if (g_ActiveConfig.bImmediateXFB)
if (g_ActiveConfig.bImmediateXFB && !NetPlay::IsRollingBack())
{
// below div two to convert from bytes to pixels - it expects width, not stride
u64 ticks = system.GetCoreTiming().GetTicks();

View file

@ -8,6 +8,7 @@
#include "Core/HW/VideoInterface.h"
#include "Core/Host.h"
#include "Core/System.h"
#include "Core/NetPlayClient.h"
#include "InputCommon/ControllerInterface/ControllerInterface.h"
@ -829,7 +830,7 @@ void Presenter::Present()
RenderXFBToScreen(render_target_rc, m_xfb_entry->texture.get(), render_source_rc);
}
if (m_onscreen_ui)
if (m_onscreen_ui && !NetPlay::IsRollingBack())
{
m_onscreen_ui->Finalize();
m_onscreen_ui->DrawImGui();
@ -848,7 +849,7 @@ void Presenter::Present()
SetSuggestedWindowSize(m_xfb_rect.GetWidth(), m_xfb_rect.GetHeight());
}
if (m_onscreen_ui)
if (m_onscreen_ui && !NetPlay::IsRollingBack())
m_onscreen_ui->BeginImGuiFrame(m_backbuffer_width, m_backbuffer_height);
g_gfx->EndUtilityDrawing();