Added input tracking and variable delay for Rollback

This commit is contained in:
Sage King 2022-08-10 17:22:26 -06:00 committed by Nayla Hanegan
parent 95f2300174
commit ef34d8fe0f
No known key found for this signature in database
GPG key ID: 3075216CED0DB01D
3 changed files with 1783 additions and 153 deletions

View file

@ -713,9 +713,24 @@ void NetPlayClient::OnPadData(sf::Packet& packet)
pad.substickY >> pad.triggerLeft >> pad.triggerRight >> pad.isConnected; pad.substickY >> pad.triggerLeft >> pad.triggerRight >> pad.isConnected;
} }
{
std::lock_guard lock(crit_netplay_client);
inputs.at(map).push_back(pad);
}
if (m_pad_buffer.at(map).Size() == 0)
{
m_pad_buffer.at(map).Push(pad);
}
else
{
m_pad_buffer.at(map).Pop();
m_pad_buffer.at(map).Push(pad);
}
// Trusting server for good map value (>=0 && <4) // Trusting server for good map value (>=0 && <4)
// add to pad buffer // add to pad buffer
m_pad_buffer.at(map).Push(pad); /* m_pad_buffer.at(map).Push(pad);*/
m_gc_pad_event.Set(); m_gc_pad_event.Set();
} }
} }
@ -959,6 +974,11 @@ void NetPlayClient::OnStartGame(sf::Packet& packet)
m_net_settings.is_hosting = m_local_player->IsHost(); 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>{});
m_dialog->OnMsgStartGame(); m_dialog->OnMsgStartGame();
} }
@ -1065,17 +1085,17 @@ void NetPlayClient::OnDesyncDetected(sf::Packet& packet)
packet >> pid_to_blame; packet >> pid_to_blame;
packet >> frame; packet >> frame;
std::string player = "??"; //std::string player = "??";
std::lock_guard lkp(m_crit.players); //std::lock_guard lkp(m_crit.players);
{ //{
const auto it = m_players.find(pid_to_blame); // const auto it = m_players.find(pid_to_blame);
if (it != m_players.end()) // if (it != m_players.end())
player = it->second.name; // player = it->second.name;
} //}
INFO_LOG_FMT(NETPLAY, "Player {} ({}) desynced!", player, pid_to_blame); //INFO_LOG_FMT(NETPLAY, "Player {} ({}) desynced!", player, pid_to_blame);
m_dialog->OnDesync(frame, player); //m_dialog->OnDesync(frame, player);
} }
void NetPlayClient::OnSyncSaveData(sf::Packet& packet) void NetPlayClient::OnSyncSaveData(sf::Packet& packet)
@ -1599,6 +1619,23 @@ void NetPlayClient::OnGameDigestAbort()
m_dialog->AbortGameDigest(); m_dialog->AbortGameDigest();
} }
void NetPlayClient::OnFrameEnd()
{
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));
}
void NetPlayClient::Send(const sf::Packet& packet, const u8 channel_id) void NetPlayClient::Send(const sf::Packet& packet, const u8 channel_id)
{ {
Common::ENet::SendPacket(m_server, packet, channel_id); Common::ENet::SendPacket(m_server, packet, channel_id);
@ -2071,6 +2108,23 @@ void NetPlayClient::OnConnectFailed(Common::TraversalConnectFailedReason reason)
// called from ---CPU--- thread // called from ---CPU--- thread
bool NetPlayClient::GetNetPads(const int pad_nb, const bool batching, GCPadStatus* pad_status) bool NetPlayClient::GetNetPads(const int pad_nb, const bool batching, GCPadStatus* pad_status)
{ {
if (m_pad_buffer.at(pad_nb).Size() == 0)
{
if (inputs.at(pad_nb).size() > delay)
{
auto delayed_input = inputs.at(pad_nb).rbegin();
std::advance(delayed_input, delay);
m_pad_buffer.at(pad_nb).Push(*(delayed_input));
}
else
{
m_pad_buffer.at(pad_nb).Push(GCPadStatus{});
}
}
// This is where the inputs get used, every other use of m_pad_buffer for rollback
// is making sure this call always has a pad status.
m_pad_buffer[pad_nb].Pop(*pad_status);
// The interface for this is extremely silly. // The interface for this is extremely silly.
// //
// Imagine a physical device that links three GameCubes together // Imagine a physical device that links three GameCubes together
@ -2099,100 +2153,116 @@ bool NetPlayClient::GetNetPads(const int pad_nb, const bool batching, GCPadStatu
// and send it. // and send it.
// When here when told to so we don't deadlock in certain situations // When here when told to so we don't deadlock in certain situations
while (m_wait_on_input) // while (m_wait_on_input)
{ //{
if (!m_is_running.IsSet()) // if (!m_is_running.IsSet())
{ // {
return false; // return false;
} // }
if (m_wait_on_input_received) // if (m_wait_on_input_received)
{ // {
// Tell the server we've acknowledged the message // // Tell the server we've acknowledged the message
sf::Packet spac; // sf::Packet spac;
spac << MessageID::GolfPrepare; // spac << MessageID::GolfPrepare;
Send(spac); // Send(spac);
m_wait_on_input_received = false; // m_wait_on_input_received = false;
} // }
m_wait_on_input_event.Wait(); // m_wait_on_input_event.Wait();
} //}
if (IsFirstInGamePad(pad_nb) && batching) // if (IsFirstInGamePad(pad_nb) && batching)
{ //{
sf::Packet packet; // sf::Packet packet;
packet << MessageID::PadData; // packet << MessageID::PadData;
bool send_packet = false; // bool send_packet = false;
const int num_local_pads = NumLocalPads(); // const int num_local_pads = NumLocalPads();
for (int local_pad = 0; local_pad < num_local_pads; local_pad++) // for (int local_pad = 0; local_pad < num_local_pads; local_pad++)
{ // {
send_packet = PollLocalPad(local_pad, packet) || send_packet; // send_packet = PollLocalPad(local_pad, packet) || send_packet;
} // }
if (send_packet) // if (send_packet)
SendAsync(std::move(packet)); // SendAsync(std::move(packet));
if (m_host_input_authority) // if (m_host_input_authority)
SendPadHostPoll(-1); // SendPadHostPoll(-1);
} //}
if (!batching) // if (!batching)
{ //{
const int local_pad = InGamePadToLocalPad(pad_nb); // const int local_pad = InGamePadToLocalPad(pad_nb);
if (local_pad < 4) // if (local_pad < 4)
{ // {
sf::Packet packet; // sf::Packet packet;
packet << MessageID::PadData; // packet << MessageID::PadData;
if (PollLocalPad(local_pad, packet)) // if (PollLocalPad(local_pad, packet))
SendAsync(std::move(packet)); // SendAsync(std::move(packet));
} // }
if (m_host_input_authority) // if (m_host_input_authority)
SendPadHostPoll(pad_nb); // SendPadHostPoll(pad_nb);
} //}
if (m_host_input_authority) // if (m_host_input_authority)
{ //{
if (m_local_player->pid != m_current_golfer) // if (m_local_player->pid != m_current_golfer)
{ // {
// CoreTiming acts funny and causes what looks like frame skip if // // CoreTiming acts funny and causes what looks like frame skip if
// we toggle the emulation speed too quickly, so to prevent this // // we toggle the emulation speed too quickly, so to prevent this
// we wait until the buffer has been over for at least 1 second. // // 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; // const bool buffer_over_target = m_pad_buffer[pad_nb].Size() > m_target_buffer_size + 1;
if (!buffer_over_target) // if (!buffer_over_target)
m_buffer_under_target_last = std::chrono::steady_clock::now(); // m_buffer_under_target_last = std::chrono::steady_clock::now();
std::chrono::duration<double> time_diff = // std::chrono::duration<double> time_diff =
std::chrono::steady_clock::now() - m_buffer_under_target_last; // std::chrono::steady_clock::now() - m_buffer_under_target_last;
if (time_diff.count() >= 1.0 || !buffer_over_target) // if (time_diff.count() >= 1.0 || !buffer_over_target)
{ // {
// run fast if the buffer is overfilled, otherwise run normal speed // // run fast if the buffer is overfilled, otherwise run normal speed
Config::SetCurrent(Config::MAIN_EMULATION_SPEED, buffer_over_target ? 0.0f : 1.0f); // Config::SetCurrent(Config::MAIN_EMULATION_SPEED, buffer_over_target ? 0.0f : 1.0f);
} // }
} // }
else // else
{ // {
// Set normal speed when we're the host, otherwise it can get stuck at unlimited // // Set normal speed when we're the host, otherwise it can get stuck at unlimited
Config::SetCurrent(Config::MAIN_EMULATION_SPEED, 1.0f); // Config::SetCurrent(Config::MAIN_EMULATION_SPEED, 1.0f);
} // }
} //}
// Now, we either use the data pushed earlier, or wait for the // Now, we either use the data pushed earlier, or wait for the
// other clients to send it to us // other clients to send it to us
while (m_pad_buffer[pad_nb].Size() == 0) // while (m_pad_buffer[pad_nb].Size() == 0)
{ //{
if (!m_is_running.IsSet()) // if (!m_is_running.IsSet())
{ // {
return false; // return false;
} // }
m_gc_pad_event.Wait(); // m_gc_pad_event.Wait();
} //}
m_pad_buffer[pad_nb].Pop(*pad_status); // for (auto& input_vector1 : inputs)
//{
// for (auto& input_vector2 : inputs)
// {
// while (std::llabs(static_cast<long long>(input_vector1.size()) -
// static_cast<long long>(input_vector2.size())) >
// static_cast<long long>(rollback_frames_supported + delay))
// {
// if (!m_is_running.IsSet())
// {
// return false;
// }
// m_gc_pad_event.Wait();
// }
// }
//}
auto& movie = Core::System::GetInstance().GetMovie(); auto& movie = Core::System::GetInstance().GetMovie();
if (movie.IsRecordingInput()) if (movie.IsRecordingInput())
@ -2272,35 +2342,48 @@ bool NetPlayClient::PollLocalPad(const int local_pad, sf::Packet& packet)
pad_status = Pad::GetStatus(local_pad); pad_status = Pad::GetStatus(local_pad);
} }
if (m_host_input_authority) inputs.at(ingame_pad).push_back(pad_status);
if (m_pad_buffer.at(ingame_pad).Size() == 0)
{ {
if (m_local_player->pid != m_current_golfer) m_pad_buffer.at(ingame_pad).Push(pad_status);
{
// 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 else
{ {
// adjust the buffer either up or down m_pad_buffer.at(ingame_pad).Pop();
// inserting multiple padstates or dropping states m_pad_buffer.at(ingame_pad).Push(pad_status);
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;
}
} }
AddPadStateToPacket(ingame_pad, pad_status, packet);
data_added = true;
// if (m_host_input_authority)
//{
// 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;
// }
//}
return data_added; return data_added;
} }
@ -2341,57 +2424,57 @@ void NetPlayClient::SendPadHostPoll(const PadIndex pad_num)
// Additionally, we wait until some actual pad data has been received before buffering and sending // 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. // it, otherwise controllers get calibrated wrongly with the default values of GCPadStatus.
if (m_local_player->pid != m_current_golfer) /* if (m_local_player->pid != m_current_golfer)
return; return;
sf::Packet packet; sf::Packet packet;
packet << MessageID::PadHostData; packet << MessageID::PadHostData;
if (pad_num < 0) if (pad_num < 0)
{ {
for (size_t i = 0; i < m_pad_map.size(); i++) for (size_t i = 0; i < m_pad_map.size(); i++)
{ {
if (m_pad_map[i] <= 0) if (m_pad_map[i] <= 0)
continue; continue;
while (!m_first_pad_status_received[i]) while (!m_first_pad_status_received[i])
{ {
if (!m_is_running.IsSet()) if (!m_is_running.IsSet())
return; return;
m_first_pad_status_received_event.Wait(); m_first_pad_status_received_event.Wait();
} }
} }
for (size_t i = 0; i < m_pad_map.size(); i++) for (size_t i = 0; i < m_pad_map.size(); i++)
{ {
if (m_pad_map[i] == 0 || m_pad_buffer[i].Size() > 0) if (m_pad_map[i] == 0 || m_pad_buffer[i].Size() > 0)
continue; continue;
const GCPadStatus& pad_status = m_last_pad_status[i]; const GCPadStatus& pad_status = m_last_pad_status[i];
m_pad_buffer[i].Push(pad_status); m_pad_buffer[i].Push(pad_status);
AddPadStateToPacket(static_cast<int>(i), pad_status, packet); AddPadStateToPacket(static_cast<int>(i), pad_status, packet);
} }
} }
else if (m_pad_map[pad_num] != 0) else if (m_pad_map[pad_num] != 0)
{ {
while (!m_first_pad_status_received[pad_num]) while (!m_first_pad_status_received[pad_num])
{ {
if (!m_is_running.IsSet()) if (!m_is_running.IsSet())
return; return;
m_first_pad_status_received_event.Wait(); m_first_pad_status_received_event.Wait();
} }
if (m_pad_buffer[pad_num].Size() == 0) if (m_pad_buffer[pad_num].Size() == 0)
{ {
const GCPadStatus& pad_status = m_last_pad_status[pad_num]; const GCPadStatus& pad_status = m_last_pad_status[pad_num];
m_pad_buffer[pad_num].Push(pad_status); m_pad_buffer[pad_num].Push(pad_status);
AddPadStateToPacket(pad_num, pad_status, packet); AddPadStateToPacket(pad_num, pad_status, packet);
} }
} }
SendAsync(std::move(packet)); SendAsync(std::move(packet));*/
} }
void NetPlayClient::InvokeStop() void NetPlayClient::InvokeStop()
@ -2957,6 +3040,12 @@ void NetPlay_RegisterEvents()
event_type_sync_time_for_ext_event = event_type_sync_time_for_ext_event =
core_timing.RegisterEvent("NetPlaySyncEvent", NetPlaySyncEvent); core_timing.RegisterEvent("NetPlaySyncEvent", NetPlaySyncEvent);
} }
void OnFrameEnd()
{
std::lock_guard lk(crit_netplay_client);
netplay_client->OnFrameEnd();
}
} // namespace NetPlay } // namespace NetPlay
// stuff hacked into dolphin // stuff hacked into dolphin

View file

@ -43,6 +43,8 @@ struct SerializedWiimoteState;
namespace NetPlay namespace NetPlay
{ {
constexpr size_t rollback_frames_supported = 10;
class NetPlayUI class NetPlayUI
{ {
public: public:
@ -184,6 +186,8 @@ public:
static SyncIdentifier GetSDCardIdentifier(); static SyncIdentifier GetSDCardIdentifier();
void OnFrameEnd();
protected: protected:
struct AsyncQueueEntry struct AsyncQueueEntry
{ {
@ -357,6 +361,9 @@ private:
std::unique_ptr<IOS::HLE::FS::FileSystem> m_wii_sync_fs; std::unique_ptr<IOS::HLE::FS::FileSystem> m_wii_sync_fs;
std::vector<u64> m_wii_sync_titles; std::vector<u64> m_wii_sync_titles;
std::string m_wii_sync_redirect_folder; std::string m_wii_sync_redirect_folder;
std::vector<std::vector<GCPadStatus>> inputs;
int delay = 2;
}; };
void NetPlay_Enable(NetPlayClient* const np); void NetPlay_Enable(NetPlayClient* const np);
@ -365,4 +372,6 @@ bool NetPlay_GetWiimoteData(const std::span<NetPlayClient::WiimoteDataBatchEntry
unsigned int NetPlay_GetLocalWiimoteForSlot(unsigned int slot); unsigned int NetPlay_GetLocalWiimoteForSlot(unsigned int slot);
void NetPlay_RegisterEvents(); void NetPlay_RegisterEvents();
void OnFrameEnd();
} // namespace NetPlay } // namespace NetPlay

File diff suppressed because it is too large Load diff