From ca9027879bc76e2c51185f0a62139c4a62e7af14 Mon Sep 17 00:00:00 2001 From: mimimi085181 Date: Thu, 5 May 2016 15:32:54 +0200 Subject: [PATCH 1/2] Netplay: Fix gamecube controller mapping This fixes issues with setups like: Player 1 uses port 1 and player 2 uses port 3, or player 1 uses port 2 and player 2 uses port 3, so nobody uses port 1 --- Source/Core/Core/HW/SI_DeviceGCAdapter.cpp | 13 +++- Source/Core/Core/HW/SI_DeviceGCController.cpp | 12 +++- Source/Core/Core/NetPlayClient.cpp | 62 +++++++++---------- 3 files changed, 50 insertions(+), 37 deletions(-) diff --git a/Source/Core/Core/HW/SI_DeviceGCAdapter.cpp b/Source/Core/Core/HW/SI_DeviceGCAdapter.cpp index e68176ac3e..bb42ff812a 100644 --- a/Source/Core/Core/HW/SI_DeviceGCAdapter.cpp +++ b/Source/Core/Core/HW/SI_DeviceGCAdapter.cpp @@ -8,7 +8,7 @@ #include "Common/MsgHandler.h" #include "Common/Logging/Log.h" #include "Core/ConfigManager.h" -#include "Core/NetPlayProto.h" +#include "Core/NetPlayClient.h" #include "Core/HW/GCPad.h" #include "Core/HW/SI_DeviceGCAdapter.h" #include "InputCommon/GCAdapter.h" @@ -27,7 +27,16 @@ GCPadStatus CSIDevice_GCAdapter::GetPadStatus() GCPadStatus PadStatus; memset(&PadStatus, 0, sizeof(PadStatus)); - GCAdapter::Input(ISIDevice::m_iDeviceNumber, &PadStatus); + if (NetPlay::IsNetPlayRunning()) + { + const u8 numPAD = NetPlay_InGamePadToLocalPad(ISIDevice::m_iDeviceNumber); + if (numPAD < 4) + GCAdapter::Input(numPAD, &PadStatus); + } + else + { + GCAdapter::Input(ISIDevice::m_iDeviceNumber, &PadStatus); + } HandleMoviePadStatus(&PadStatus); diff --git a/Source/Core/Core/HW/SI_DeviceGCController.cpp b/Source/Core/Core/HW/SI_DeviceGCController.cpp index a6c25f689a..cd06dc65ee 100644 --- a/Source/Core/Core/HW/SI_DeviceGCController.cpp +++ b/Source/Core/Core/HW/SI_DeviceGCController.cpp @@ -8,6 +8,7 @@ #include "Common/Logging/Log.h" #include "Core/CoreTiming.h" #include "Core/Movie.h" +#include "Core/NetPlayClient.h" #include "Core/HW/GCPad.h" #include "Core/HW/ProcessorInterface.h" #include "Core/HW/SI_Device.h" @@ -144,7 +145,16 @@ GCPadStatus CSIDevice_GCController::GetPadStatus() GCPadStatus PadStatus; memset(&PadStatus, 0, sizeof(PadStatus)); - Pad::GetStatus(ISIDevice::m_iDeviceNumber, &PadStatus); + if (NetPlay::IsNetPlayRunning()) + { + const u8 numPAD = NetPlay_InGamePadToLocalPad(ISIDevice::m_iDeviceNumber); + if (numPAD < 4) + Pad::GetStatus(numPAD, &PadStatus); + } + else + { + Pad::GetStatus(ISIDevice::m_iDeviceNumber, &PadStatus); + } HandleMoviePadStatus(&PadStatus); return PadStatus; diff --git a/Source/Core/Core/NetPlayClient.cpp b/Source/Core/Core/NetPlayClient.cpp index e292f16465..229658cd5a 100644 --- a/Source/Core/Core/NetPlayClient.cpp +++ b/Source/Core/Core/NetPlayClient.cpp @@ -752,35 +752,34 @@ bool NetPlayClient::ChangeGame(const std::string&) void NetPlayClient::UpdateDevices() { u8 local_pad = 0; - // Add local pads first: - // As stated in the comment in NetPlayClient::GetNetPads, the pads pertaining - // to the local user are always locally mapped to the first gamecube ports, - // so they should be added first. + u8 pad = 0; + for (auto player_id : m_pad_map) { // Use local controller types for local controllers if they are compatible + // Only GCController-like controllers are supported, GBA and similar + // exotic devices are not supported on netplay. if (player_id == m_local_player->pid) { if (SIDevice_IsGCController(SConfig::GetInstance().m_SIDevice[local_pad])) { - SerialInterface::AddDevice(SConfig::GetInstance().m_SIDevice[local_pad], local_pad); + SerialInterface::AddDevice(SConfig::GetInstance().m_SIDevice[local_pad], pad); } else { - SerialInterface::AddDevice(SIDEVICE_GC_CONTROLLER, local_pad); + SerialInterface::AddDevice(SIDEVICE_GC_CONTROLLER, pad); } local_pad++; } - } - for (auto player_id : m_pad_map) - { - if (player_id != m_local_player->pid) + else if (player_id > 0) { - // Only GCController-like controllers are supported, GBA and similar - // exotic devices are not supported on netplay. - SerialInterface::AddDevice(player_id > 0 ? SIDEVICE_GC_CONTROLLER : SIDEVICE_NONE, local_pad); - local_pad++; + SerialInterface::AddDevice(SIDEVICE_GC_CONTROLLER, pad); } + else + { + SerialInterface::AddDevice(SIDEVICE_NONE, pad); + } + pad++; } } @@ -863,39 +862,34 @@ bool NetPlayClient::GetNetPads(const u8 pad_nb, GCPadStatus* pad_status) // 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 + // The slot number is the "local" pad number, and what player // it actually means is the "in-game" pad number. // - // The interface here gives us the status of local pads, and - // expects to get back "in-game" pad numbers back in response. - // e.g. it asks "here's the input that slot 1 has, and by the - // way, what's the state of P1?" - // - // We should add this split between "in-game" pads and "local" - // pads higher up. + // GetNetPads() gets called for all "in-game" pads with the their + // status. For the pads, which are mapped locally, the status is + // polled in GetPadStatus() earlier, and here it is send to the + // other clients. For all "in-game" pads, which are mapped to + // the others, the passed over status is ignored. Instead we + // wait for the other clients to send the status to this client. - int in_game_num = LocalPadToInGamePad(pad_nb); - - // If this in-game pad is one of ours, then update from the - // information given. - if (in_game_num < 4) + // If this in-game pad is mapped to this client, then update + // with the status polled earlier in GetPadStatus() + if (m_pad_map[pad_nb] == m_local_player->pid) { // adjust the buffer either up or down // inserting multiple padstates or dropping states - while (m_pad_buffer[in_game_num].Size() <= m_target_buffer_size) + while (m_pad_buffer[pad_nb].Size() <= m_target_buffer_size) { // add to buffer - m_pad_buffer[in_game_num].Push(*pad_status); + m_pad_buffer[pad_nb].Push(*pad_status); // send - SendPadState(in_game_num, *pad_status); + SendPadState(pad_nb, *pad_status); } } - // Now, we need to swap out the local value with the values - // retrieved from NetPlay. This could be the value we pushed - // above if we're configured as P1 and the code is trying - // to retrieve data for slot 1. + // Now, we either use the data we just pushed, or wait for the + // other clients to send it to us while (!m_pad_buffer[pad_nb].Pop(*pad_status)) { if (!m_is_running.load()) From 1d90719abea9e223fd553bd29f42599a94af4346 Mon Sep 17 00:00:00 2001 From: mimimi085181 Date: Sun, 8 May 2016 15:29:01 +0200 Subject: [PATCH 2/2] Fix latency regression On master, when polling the 1st in-game controller, Dolphin would poll all the 1st local controllers. With the 1st commit, each client waits its turn, which would dramatically increase the lag. Now with this commit, it even polls all local controllers at once, so it should have even less latency than master in a few setups. Like one player with 3 controllers and the 2nd one with just one controller. --- Source/Core/Core/HW/SI_DeviceGCAdapter.cpp | 12 ++-- Source/Core/Core/HW/SI_DeviceGCController.cpp | 12 ++-- Source/Core/Core/NetPlayClient.cpp | 63 +++++++++++++------ Source/Core/Core/NetPlayClient.h | 5 +- 4 files changed, 57 insertions(+), 35 deletions(-) diff --git a/Source/Core/Core/HW/SI_DeviceGCAdapter.cpp b/Source/Core/Core/HW/SI_DeviceGCAdapter.cpp index bb42ff812a..2f955164bf 100644 --- a/Source/Core/Core/HW/SI_DeviceGCAdapter.cpp +++ b/Source/Core/Core/HW/SI_DeviceGCAdapter.cpp @@ -8,7 +8,7 @@ #include "Common/MsgHandler.h" #include "Common/Logging/Log.h" #include "Core/ConfigManager.h" -#include "Core/NetPlayClient.h" +#include "Core/NetPlayProto.h" #include "Core/HW/GCPad.h" #include "Core/HW/SI_DeviceGCAdapter.h" #include "InputCommon/GCAdapter.h" @@ -27,13 +27,9 @@ GCPadStatus CSIDevice_GCAdapter::GetPadStatus() GCPadStatus PadStatus; memset(&PadStatus, 0, sizeof(PadStatus)); - if (NetPlay::IsNetPlayRunning()) - { - const u8 numPAD = NetPlay_InGamePadToLocalPad(ISIDevice::m_iDeviceNumber); - if (numPAD < 4) - GCAdapter::Input(numPAD, &PadStatus); - } - else + // For netplay, the local controllers are polled in GetNetPads(), and + // the remote controllers receive their status there as well + if (!NetPlay::IsNetPlayRunning()) { GCAdapter::Input(ISIDevice::m_iDeviceNumber, &PadStatus); } diff --git a/Source/Core/Core/HW/SI_DeviceGCController.cpp b/Source/Core/Core/HW/SI_DeviceGCController.cpp index cd06dc65ee..572e49c8b8 100644 --- a/Source/Core/Core/HW/SI_DeviceGCController.cpp +++ b/Source/Core/Core/HW/SI_DeviceGCController.cpp @@ -8,7 +8,7 @@ #include "Common/Logging/Log.h" #include "Core/CoreTiming.h" #include "Core/Movie.h" -#include "Core/NetPlayClient.h" +#include "Core/NetPlayProto.h" #include "Core/HW/GCPad.h" #include "Core/HW/ProcessorInterface.h" #include "Core/HW/SI_Device.h" @@ -145,13 +145,9 @@ GCPadStatus CSIDevice_GCController::GetPadStatus() GCPadStatus PadStatus; memset(&PadStatus, 0, sizeof(PadStatus)); - if (NetPlay::IsNetPlayRunning()) - { - const u8 numPAD = NetPlay_InGamePadToLocalPad(ISIDevice::m_iDeviceNumber); - if (numPAD < 4) - Pad::GetStatus(numPAD, &PadStatus); - } - else + // For netplay, the local controllers are polled in GetNetPads(), and + // the remote controllers receive their status there as well + if (!NetPlay::IsNetPlayRunning()) { Pad::GetStatus(ISIDevice::m_iDeviceNumber, &PadStatus); } diff --git a/Source/Core/Core/NetPlayClient.cpp b/Source/Core/Core/NetPlayClient.cpp index 229658cd5a..a8992e1a21 100644 --- a/Source/Core/Core/NetPlayClient.cpp +++ b/Source/Core/Core/NetPlayClient.cpp @@ -19,6 +19,7 @@ #include "Core/HW/WiimoteEmu/WiimoteEmu.h" #include "Core/HW/WiimoteReal/WiimoteReal.h" #include "Core/IPC_HLE/WII_IPC_HLE_Device_usb.h" +#include "InputCommon/GCAdapter.h" static std::mutex crit_netplay_client; static NetPlayClient * netplay_client = nullptr; @@ -864,31 +865,43 @@ bool NetPlayClient::GetNetPads(const u8 pad_nb, GCPadStatus* pad_status) // // The slot number is the "local" pad number, and what player // it actually means is the "in-game" pad number. - // - // GetNetPads() gets called for all "in-game" pads with the their - // status. For the pads, which are mapped locally, the status is - // polled in GetPadStatus() earlier, and here it is send to the - // other clients. For all "in-game" pads, which are mapped to - // the others, the passed over status is ignored. Instead we - // wait for the other clients to send the status to this client. - // If this in-game pad is mapped to this client, then update - // with the status polled earlier in GetPadStatus() - if (m_pad_map[pad_nb] == m_local_player->pid) + // When the 1st in-game pad is polled, we assume the others will + // will be polled as well. To reduce latency, we poll all local + // controllers at once and then send the status to the other + // clients. + if (IsFirstInGamePad(pad_nb)) { - // adjust the buffer either up or down - // inserting multiple padstates or dropping states - while (m_pad_buffer[pad_nb].Size() <= m_target_buffer_size) + const u8 num_local_pads = NumLocalPads(); + for (u8 local_pad = 0; local_pad < num_local_pads; local_pad++) { - // add to buffer - m_pad_buffer[pad_nb].Push(*pad_status); + switch (SConfig::GetInstance().m_SIDevice[local_pad]) + { + case SIDEVICE_WIIU_ADAPTER: + GCAdapter::Input(local_pad, pad_status); + break; + case SIDEVICE_GC_CONTROLLER: + default: + Pad::GetStatus(local_pad, pad_status); + break; + } - // send - SendPadState(pad_nb, *pad_status); + u8 ingame_pad = LocalPadToInGamePad(local_pad); + + // 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); + + // send + SendPadState(ingame_pad, *pad_status); + } } } - // Now, we either use the data we just pushed, or wait for the + // 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].Pop(*pad_status)) { @@ -1061,6 +1074,20 @@ bool NetPlayClient::LocalPlayerHasControllerMapped() const std::any_of(m_wiimote_map.begin(), m_wiimote_map.end(), mapping_matches_player_id); } +bool NetPlayClient::IsFirstInGamePad(u8 ingame_pad) const +{ + return std::none_of(m_pad_map.begin(), m_pad_map.begin() + ingame_pad, [](auto mapping) { + return mapping > 0; + }); +} + +u8 NetPlayClient::NumLocalPads() const +{ + return static_cast(std::count_if(m_pad_map.begin(), m_pad_map.end(), [this](auto mapping) { + return mapping == m_local_player->pid; + })); +} + u8 NetPlayClient::InGamePadToLocalPad(u8 ingame_pad) { // not our pad diff --git a/Source/Core/Core/NetPlayClient.h b/Source/Core/Core/NetPlayClient.h index 3c9222e087..285db13b5b 100644 --- a/Source/Core/Core/NetPlayClient.h +++ b/Source/Core/Core/NetPlayClient.h @@ -75,8 +75,11 @@ public: void OnConnectReady(ENetAddress addr) override; void OnConnectFailed(u8 reason) override; + bool IsFirstInGamePad(u8 ingame_pad) const; + u8 NumLocalPads() const; + + u8 InGamePadToLocalPad(u8 ingame_pad); u8 LocalPadToInGamePad(u8 localPad); - u8 InGamePadToLocalPad(u8 localPad); u8 LocalWiimoteToInGameWiimote(u8 local_pad);