Skip to content

Commit

Permalink
Merge pull request #7360 from Techjar/netplay-host-input-authority
Browse files Browse the repository at this point in the history
NetPlay host input authority mode
  • Loading branch information
delroth committed Aug 26, 2018
2 parents 31b4461 + 5519efa commit 9bcd7bf
Show file tree
Hide file tree
Showing 9 changed files with 366 additions and 50 deletions.
2 changes: 2 additions & 0 deletions Source/Core/Core/BootManager.cpp
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -372,6 +372,8 @@ bool BootCore(std::unique_ptr<BootParameters> boot)
StartUp.bMMU = netplay_settings.m_MMU; StartUp.bMMU = netplay_settings.m_MMU;
StartUp.bFastmem = netplay_settings.m_Fastmem; StartUp.bFastmem = netplay_settings.m_Fastmem;
StartUp.bHLE_BS2 = netplay_settings.m_SkipIPL; StartUp.bHLE_BS2 = netplay_settings.m_SkipIPL;
if (netplay_settings.m_HostInputAuthority && !netplay_settings.m_IsHosting)
config_cache.bSetEmulationSpeed = true;
} }
else else
{ {
Expand Down
7 changes: 7 additions & 0 deletions Source/Core/Core/HW/SI/SI.cpp
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -595,6 +595,10 @@ void ChangeDeviceDeterministic(SIDevices device, int channel)


void UpdateDevices() void UpdateDevices()
{ {
// Hinting NetPlay that all controllers will be polled in
// succession, in order to optimize networking
NetPlay::SetSIPollBatching(true);

// Update inputs at the rate of SI // Update inputs at the rate of SI
// Typically 120hz but is variable // Typically 120hz but is variable
g_controller_interface.UpdateInput(); g_controller_interface.UpdateInput();
Expand All @@ -610,6 +614,9 @@ void UpdateDevices()
!!s_channel[3].device->GetData(s_channel[3].in_hi.hex, s_channel[3].in_lo.hex); !!s_channel[3].device->GetData(s_channel[3].in_hi.hex, s_channel[3].in_lo.hex);


UpdateInterrupts(); UpdateInterrupts();

// Polling finished
NetPlay::SetSIPollBatching(false);
} }


SIDevices GetDeviceType(int channel) SIDevices GetDeviceType(int channel)
Expand Down
198 changes: 164 additions & 34 deletions Source/Core/Core/NetPlayClient.cpp
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ namespace NetPlay
static std::mutex crit_netplay_client; static std::mutex crit_netplay_client;
static NetPlayClient* netplay_client = nullptr; static NetPlayClient* netplay_client = nullptr;
static std::unique_ptr<IOS::HLE::FS::FileSystem> s_wii_sync_fs; static std::unique_ptr<IOS::HLE::FS::FileSystem> s_wii_sync_fs;
static bool s_si_poll_batching;


// called from ---GUI--- thread // called from ---GUI--- thread
NetPlayClient::~NetPlayClient() NetPlayClient::~NetPlayClient()
Expand Down Expand Up @@ -407,6 +408,22 @@ unsigned int NetPlayClient::OnData(sf::Packet& packet)
} }
break; break;


case NP_MSG_PAD_FIRST_RECEIVED:
{
PadMapping map;
packet >> map;
packet >> m_first_pad_status_received[map];
m_first_pad_status_received_event.Set();
}
break;

case NP_MSG_HOST_INPUT_AUTHORITY:
{
packet >> m_host_input_authority;
m_dialog->OnHostInputAuthorityChanged(m_host_input_authority);
}
break;

case NP_MSG_CHANGE_GAME: case NP_MSG_CHANGE_GAME:
{ {
{ {
Expand Down Expand Up @@ -532,7 +549,9 @@ unsigned int NetPlayClient::OnData(sf::Packet& packet)


packet >> m_net_settings.m_SyncSaveData; packet >> m_net_settings.m_SyncSaveData;
packet >> m_net_settings.m_SaveDataRegion; packet >> m_net_settings.m_SaveDataRegion;
m_net_settings.m_IsHosting = m_dialog->IsHosting();
m_net_settings.m_IsHosting = m_local_player->IsHost();
m_net_settings.m_HostInputAuthority = m_host_input_authority;
} }


m_dialog->OnMsgStartGame(); m_dialog->OnMsgStartGame();
Expand Down Expand Up @@ -636,7 +655,7 @@ unsigned int NetPlayClient::OnData(sf::Packet& packet)


case SYNC_SAVE_DATA_RAW: case SYNC_SAVE_DATA_RAW:
{ {
if (m_dialog->IsHosting()) if (m_local_player->IsHost())
return 0; return 0;


bool is_slot_a; bool is_slot_a;
Expand All @@ -660,7 +679,7 @@ unsigned int NetPlayClient::OnData(sf::Packet& packet)


case SYNC_SAVE_DATA_GCI: case SYNC_SAVE_DATA_GCI:
{ {
if (m_dialog->IsHosting()) if (m_local_player->IsHost())
return 0; return 0;


bool is_slot_a; bool is_slot_a;
Expand Down Expand Up @@ -696,7 +715,7 @@ unsigned int NetPlayClient::OnData(sf::Packet& packet)


case SYNC_SAVE_DATA_WII: case SYNC_SAVE_DATA_WII:
{ {
if (m_dialog->IsHosting()) if (m_local_player->IsHost())
return 0; return 0;


const auto game = m_dialog->FindGameFile(m_selected_game); const auto game = m_dialog->FindGameFile(m_selected_game);
Expand Down Expand Up @@ -1118,6 +1137,8 @@ bool NetPlayClient::StartGame(const std::string& path)


ClearBuffers(); ClearBuffers();


m_first_pad_status_received.fill(false);

if (m_dialog->IsRecording()) if (m_dialog->IsRecording())
{ {
if (Movie::IsReadOnly()) if (Movie::IsReadOnly())
Expand Down Expand Up @@ -1366,7 +1387,7 @@ void NetPlayClient::OnConnectFailed(u8 reason)
} }


// called from ---CPU--- thread // called from ---CPU--- thread
bool NetPlayClient::GetNetPads(const int pad_nb, GCPadStatus* pad_status) bool NetPlayClient::GetNetPads(const int pad_nb, const bool batching, GCPadStatus* pad_status)
{ {
// The interface for this is extremely silly. // The interface for this is extremely silly.
// //
Expand All @@ -1385,11 +1406,17 @@ bool NetPlayClient::GetNetPads(const int pad_nb, GCPadStatus* pad_status)
// The slot number is the "local" pad number, and what player // The slot number is the "local" pad number, and what player
// it actually means is the "in-game" pad number. // it actually means is the "in-game" pad number.


// When the 1st in-game pad is polled, we assume the others will // When the 1st in-game pad is polled and batching is set, the
// will be polled as well. To reduce latency, we poll all local // others will be polled as well. To reduce latency, we poll all
// controllers at once and then send the status to the other // local controllers at once and then send the status to the other
// clients. // clients.
if (IsFirstInGamePad(pad_nb)) //
// 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.

if (IsFirstInGamePad(pad_nb) && batching)
{ {
sf::Packet packet; sf::Packet packet;
packet << static_cast<MessageId>(NP_MSG_PAD_DATA); packet << static_cast<MessageId>(NP_MSG_PAD_DATA);
Expand All @@ -1398,34 +1425,44 @@ bool NetPlayClient::GetNetPads(const int pad_nb, GCPadStatus* pad_status)
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++)
{ {
switch (SConfig::GetInstance().m_SIDevice[local_pad]) send_packet = PollLocalPad(local_pad, packet) || send_packet;
{ }
case SerialInterface::SIDEVICE_WIIU_ADAPTER:
*pad_status = GCAdapter::Input(local_pad);
break;
case SerialInterface::SIDEVICE_GC_CONTROLLER:
default:
*pad_status = Pad::GetStatus(local_pad);
break;
}


int ingame_pad = LocalPadToInGamePad(local_pad); if (send_packet)
SendAsync(std::move(packet));


// adjust the buffer either up or down if (m_host_input_authority)
// inserting multiple padstates or dropping states SendPadHostPoll(-1);
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 if (!batching)
AddPadStateToPacket(ingame_pad, *pad_status, packet); {
send_packet = true; int local_pad = InGamePadToLocalPad(pad_nb);
} if (local_pad < 4)
{
sf::Packet packet;
packet << static_cast<MessageId>(NP_MSG_PAD_DATA);
if (PollLocalPad(local_pad, packet))
SendAsync(std::move(packet));
} }


if (send_packet) if (m_host_input_authority)
SendAsync(std::move(packet)); SendPadHostPoll(pad_nb);
}

if (m_host_input_authority && !m_local_player->IsHost())
{
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
SConfig::GetInstance().m_EmulationSpeed = buffer_over_target ? 0.0f : 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
Expand Down Expand Up @@ -1534,6 +1571,86 @@ bool NetPlayClient::WiimoteUpdate(int _number, u8* data, const u8 size, u8 repor
return true; return true;
} }


bool NetPlayClient::PollLocalPad(const int local_pad, sf::Packet& packet)
{
GCPadStatus pad_status;

switch (SConfig::GetInstance().m_SIDevice[local_pad])
{
case SerialInterface::SIDEVICE_WIIU_ADAPTER:
pad_status = GCAdapter::Input(local_pad);
break;
case SerialInterface::SIDEVICE_GC_CONTROLLER:
default:
pad_status = Pad::GetStatus(local_pad);
break;
}

const int ingame_pad = LocalPadToInGamePad(local_pad);
bool data_added = false;

if (m_host_input_authority)
{
// add to packet
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)
{
// 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;
}

void NetPlayClient::SendPadHostPoll(const PadMapping pad_num)
{
if (!m_local_player->IsHost())
return;

if (pad_num < 0)
{
for (size_t i = 0; i < m_pad_map.size(); i++)
{
if (m_pad_map[i] <= 0)
continue;

while (!m_first_pad_status_received[i])
{
if (!m_is_running.IsSet())
return;

m_first_pad_status_received_event.Wait();
}
}
}
else if (m_pad_map[pad_num] > 0)
{
while (!m_first_pad_status_received[pad_num])
{
if (!m_is_running.IsSet())
return;

m_first_pad_status_received_event.Wait();
}
}

sf::Packet packet;
packet << static_cast<MessageId>(NP_MSG_PAD_HOST_POLL);
packet << pad_num;
SendAsync(std::move(packet));
}

// called from ---GUI--- thread and ---NETPLAY--- thread (client side) // called from ---GUI--- thread and ---NETPLAY--- thread (client side)
bool NetPlayClient::StopGame() bool NetPlayClient::StopGame()
{ {
Expand All @@ -1542,6 +1659,7 @@ bool NetPlayClient::StopGame()
// stop waiting for input // stop waiting for input
m_gc_pad_event.Set(); m_gc_pad_event.Set();
m_wii_pad_event.Set(); m_wii_pad_event.Set();
m_first_pad_status_received_event.Set();


NetPlay_Disable(); NetPlay_Disable();


Expand All @@ -1564,6 +1682,7 @@ void NetPlayClient::Stop()
// stop waiting for input // stop waiting for input
m_gc_pad_event.Set(); m_gc_pad_event.Set();
m_wii_pad_event.Set(); m_wii_pad_event.Set();
m_first_pad_status_received_event.Set();


// Tell the server to stop if we have a pad mapped in game. // Tell the server to stop if we have a pad mapped in game.
if (LocalPlayerHasControllerMapped()) if (LocalPlayerHasControllerMapped())
Expand Down Expand Up @@ -1719,6 +1838,12 @@ const PadMappingArray& NetPlayClient::GetWiimoteMapping() const
return m_wiimote_map; return m_wiimote_map;
} }


void NetPlayClient::AdjustPadBufferSize(const unsigned int size)
{
m_target_buffer_size = size;
m_dialog->OnPadBufferChanged(size);
}

bool IsNetPlayRunning() bool IsNetPlayRunning()
{ {
return netplay_client != nullptr; return netplay_client != nullptr;
Expand Down Expand Up @@ -1750,6 +1875,11 @@ void ClearWiiSyncFS()
s_wii_sync_fs.reset(); s_wii_sync_fs.reset();
} }


void SetSIPollBatching(bool state)
{
s_si_poll_batching = state;
}

void NetPlay_Enable(NetPlayClient* const np) void NetPlay_Enable(NetPlayClient* const np)
{ {
std::lock_guard<std::mutex> lk(crit_netplay_client); std::lock_guard<std::mutex> lk(crit_netplay_client);
Expand All @@ -1767,12 +1897,12 @@ void NetPlay_Disable()


// called from ---CPU--- thread // called from ---CPU--- thread
// Actual Core function which is called on every frame // Actual Core function which is called on every frame
bool SerialInterface::CSIDevice_GCController::NetPlay_GetInput(int numPAD, GCPadStatus* PadStatus) bool SerialInterface::CSIDevice_GCController::NetPlay_GetInput(int pad_num, GCPadStatus* status)
{ {
std::lock_guard<std::mutex> lk(NetPlay::crit_netplay_client); std::lock_guard<std::mutex> lk(NetPlay::crit_netplay_client);


if (NetPlay::netplay_client) if (NetPlay::netplay_client)
return NetPlay::netplay_client->GetNetPads(numPAD, PadStatus); return NetPlay::netplay_client->GetNetPads(pad_num, NetPlay::s_si_poll_batching, status);


return false; return false;
} }
Expand Down
Loading

0 comments on commit 9bcd7bf

Please sign in to comment.