From e2f1da52107284d1d797ac71671f1c5cb6919077 Mon Sep 17 00:00:00 2001 From: Techjar Date: Mon, 1 Apr 2019 22:36:48 -0400 Subject: [PATCH 1/3] NetPlay: Move host input authority logic to client This is a prerequisite for golf mode, as the client needs to be in control of sending pad states. --- Source/Core/Core/NetPlayClient.cpp | 68 ++++++++--- Source/Core/Core/NetPlayClient.h | 1 + Source/Core/Core/NetPlayProto.h | 3 +- Source/Core/Core/NetPlayServer.cpp | 108 +++++++----------- Source/Core/Core/NetPlayServer.h | 4 - Source/Core/DolphinQt/MainWindow.cpp | 3 +- .../Core/DolphinQt/NetPlay/NetPlayDialog.cpp | 34 +++--- 7 files changed, 112 insertions(+), 109 deletions(-) diff --git a/Source/Core/Core/NetPlayClient.cpp b/Source/Core/Core/NetPlayClient.cpp index c90aba3959..5de24892e3 100644 --- a/Source/Core/Core/NetPlayClient.cpp +++ b/Source/Core/Core/NetPlayClient.cpp @@ -442,6 +442,30 @@ unsigned int NetPlayClient::OnData(sf::Packet& packet) } break; + case NP_MSG_PAD_HOST_DATA: + { + while (!packet.endOfPacket()) + { + PadIndex map; + packet >> map; + + GCPadStatus pad; + packet >> pad.button >> pad.analogA >> pad.analogB >> pad.stickX >> pad.stickY >> + pad.substickX >> pad.substickY >> pad.triggerLeft >> pad.triggerRight >> pad.isConnected; + + // Trusting server for good map value (>=0 && <4) + // write to last status + m_last_pad_status[map] = pad; + + if (!m_first_pad_status_received[map]) + { + m_first_pad_status_received[map] = true; + m_first_pad_status_received_event.Set(); + } + } + } + break; + case NP_MSG_WIIMOTE_DATA: { PadIndex map; @@ -471,15 +495,6 @@ unsigned int NetPlayClient::OnData(sf::Packet& packet) } break; - case NP_MSG_PAD_FIRST_RECEIVED: - { - PadIndex 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; @@ -1865,9 +1880,18 @@ bool NetPlayClient::PollLocalPad(const int local_pad, sf::Packet& packet) if (m_host_input_authority) { - // add to packet - AddPadStateToPacket(ingame_pad, pad_status, packet); - data_added = true; + if (!m_local_player->IsHost()) + { + // 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 { @@ -1892,6 +1916,9 @@ void NetPlayClient::SendPadHostPoll(const PadIndex pad_num) if (!m_local_player->IsHost()) return; + sf::Packet packet; + packet << static_cast(NP_MSG_PAD_HOST_DATA); + if (pad_num < 0) { for (size_t i = 0; i < m_pad_map.size(); i++) @@ -1907,6 +1934,16 @@ void NetPlayClient::SendPadHostPoll(const PadIndex pad_num) m_first_pad_status_received_event.Wait(); } } + + for (size_t i = 0; i < m_pad_map.size(); i++) + { + if (m_pad_map[i] == 0) + continue; + + const GCPadStatus& pad_status = m_last_pad_status[i]; + m_pad_buffer[i].Push(pad_status); + AddPadStateToPacket(static_cast(i), pad_status, packet); + } } else if (m_pad_map[pad_num] != 0) { @@ -1917,11 +1954,12 @@ void NetPlayClient::SendPadHostPoll(const PadIndex pad_num) m_first_pad_status_received_event.Wait(); } + + const GCPadStatus& pad_status = m_last_pad_status[pad_num]; + m_pad_buffer[pad_num].Push(pad_status); + AddPadStateToPacket(pad_num, pad_status, packet); } - sf::Packet packet; - packet << static_cast(NP_MSG_PAD_HOST_POLL); - packet << pad_num; SendAsync(std::move(packet)); } diff --git a/Source/Core/Core/NetPlayClient.h b/Source/Core/Core/NetPlayClient.h index e7b3b20363..cfe6e37543 100644 --- a/Source/Core/Core/NetPlayClient.h +++ b/Source/Core/Core/NetPlayClient.h @@ -158,6 +158,7 @@ protected: std::array, 4> m_pad_buffer; std::array, 4> m_wiimote_buffer; + std::array m_last_pad_status{}; std::array m_first_pad_status_received{}; std::chrono::time_point m_buffer_under_target_last; diff --git a/Source/Core/Core/NetPlayProto.h b/Source/Core/Core/NetPlayProto.h index 5eaedcd840..908b8fe004 100644 --- a/Source/Core/Core/NetPlayProto.h +++ b/Source/Core/Core/NetPlayProto.h @@ -123,8 +123,7 @@ enum NP_MSG_PAD_DATA = 0x60, NP_MSG_PAD_MAPPING = 0x61, NP_MSG_PAD_BUFFER = 0x62, - NP_MSG_PAD_HOST_POLL = 0x63, - NP_MSG_PAD_FIRST_RECEIVED = 0x64, + NP_MSG_PAD_HOST_DATA = 0x63, NP_MSG_WIIMOTE_DATA = 0x70, NP_MSG_WIIMOTE_MAPPING = 0x71, diff --git a/Source/Core/Core/NetPlayServer.cpp b/Source/Core/Core/NetPlayServer.cpp index 166ce881bf..2c6e4aa181 100644 --- a/Source/Core/Core/NetPlayServer.cpp +++ b/Source/Core/Core/NetPlayServer.cpp @@ -527,12 +527,16 @@ void NetPlayServer::AdjustPadBufferSize(unsigned int size) m_target_buffer_size = size; - // tell clients to change buffer size - sf::Packet spac; - spac << static_cast(NP_MSG_PAD_BUFFER); - spac << static_cast(m_target_buffer_size); + // not needed on clients with host input authority + if (!m_host_input_authority) + { + // tell clients to change buffer size + sf::Packet spac; + spac << static_cast(NP_MSG_PAD_BUFFER); + spac << static_cast(m_target_buffer_size); - SendAsyncToClients(std::move(spac)); + SendAsyncToClients(std::move(spac)); + } } void NetPlayServer::SetHostInputAuthority(const bool enable) @@ -652,7 +656,7 @@ unsigned int NetPlayServer::OnData(sf::Packet& packet, Client& player) break; sf::Packet spac; - spac << static_cast(NP_MSG_PAD_DATA); + spac << static_cast(m_host_input_authority ? NP_MSG_PAD_HOST_DATA : NP_MSG_PAD_DATA); while (!packet.endOfPacket()) { @@ -670,59 +674,42 @@ unsigned int NetPlayServer::OnData(sf::Packet& packet, Client& player) packet >> pad.button >> pad.analogA >> pad.analogB >> pad.stickX >> pad.stickY >> pad.substickX >> pad.substickY >> pad.triggerLeft >> pad.triggerRight >> pad.isConnected; - if (m_host_input_authority) - { - m_last_pad_status[map] = pad; - - if (!m_first_pad_status_received[map]) - { - m_first_pad_status_received[map] = true; - SendFirstReceivedToHost(map, true); - } - } - else - { - spac << map << pad.button << pad.analogA << pad.analogB << pad.stickX << pad.stickY - << pad.substickX << pad.substickY << pad.triggerLeft << pad.triggerRight - << pad.isConnected; - } - } - - if (!m_host_input_authority) - SendToClients(spac, player.pid); - } - break; - - case NP_MSG_PAD_HOST_POLL: - { - PadIndex pad_num; - packet >> pad_num; - - sf::Packet spac; - spac << static_cast(NP_MSG_PAD_DATA); - - if (pad_num < 0) - { - for (size_t i = 0; i < m_pad_map.size(); i++) - { - if (m_pad_map[i] == 0) - continue; - - const GCPadStatus& pad = m_last_pad_status[i]; - spac << static_cast(i) << pad.button << pad.analogA << pad.analogB << pad.stickX - << pad.stickY << pad.substickX << pad.substickY << pad.triggerLeft << pad.triggerRight - << pad.isConnected; - } - } - else if (m_pad_map.at(pad_num) != 0) - { - const GCPadStatus& pad = m_last_pad_status[pad_num]; - spac << pad_num << pad.button << pad.analogA << pad.analogB << pad.stickX << pad.stickY + spac << map << pad.button << pad.analogA << pad.analogB << pad.stickX << pad.stickY << pad.substickX << pad.substickY << pad.triggerLeft << pad.triggerRight << pad.isConnected; } - SendToClients(spac); + if (m_host_input_authority) + Send(m_players.at(1).socket, spac); + else + SendToClients(spac, player.pid); + } + break; + + case NP_MSG_PAD_HOST_DATA: + { + // Kick player if they're not the host. + if (!player.IsHost()) + return 1; + + sf::Packet spac; + spac << static_cast(NP_MSG_PAD_DATA); + + while (!packet.endOfPacket()) + { + PadIndex map; + packet >> map; + + GCPadStatus pad; + packet >> pad.button >> pad.analogA >> pad.analogB >> pad.stickX >> pad.stickY >> + pad.substickX >> pad.substickY >> pad.triggerLeft >> pad.triggerRight >> pad.isConnected; + + spac << map << pad.button << pad.analogA << pad.analogB << pad.stickX << pad.stickY + << pad.substickX << pad.substickY << pad.triggerLeft << pad.triggerRight + << pad.isConnected; + } + + SendToClients(spac, player.pid); } break; @@ -1141,8 +1128,6 @@ bool NetPlayServer::StartGame() if (!m_host_input_authority) AdjustPadBufferSize(m_target_buffer_size); - m_first_pad_status_received.fill(false); - const sf::Uint64 initial_rtc = GetInitialNetPlayRTC(); const std::string region = SConfig::GetDirectoryForRegion( @@ -1720,15 +1705,6 @@ bool NetPlayServer::CompressBufferIntoPacket(const std::vector& in_buffer, s return true; } -void NetPlayServer::SendFirstReceivedToHost(const PadIndex map, const bool state) -{ - sf::Packet pac; - pac << static_cast(NP_MSG_PAD_FIRST_RECEIVED); - pac << map; - pac << state; - Send(m_players.at(1).socket, pac); -} - u64 NetPlayServer::GetInitialNetPlayRTC() const { const auto& config = SConfig::GetInstance(); diff --git a/Source/Core/Core/NetPlayServer.h b/Source/Core/Core/NetPlayServer.h index 70c354a41d..447b3de161 100644 --- a/Source/Core/Core/NetPlayServer.h +++ b/Source/Core/Core/NetPlayServer.h @@ -118,7 +118,6 @@ private: void CheckSyncAndStartGame(); bool CompressFileIntoPacket(const std::string& file_path, sf::Packet& packet); bool CompressBufferIntoPacket(const std::vector& in_buffer, sf::Packet& packet); - void SendFirstReceivedToHost(PadIndex map, bool state); u64 GetInitialNetPlayRTC() const; @@ -161,9 +160,6 @@ private: std::unordered_map>> m_timebase_by_frame; bool m_desync_detected; - std::array m_last_pad_status{}; - std::array m_first_pad_status_received{}; - struct { std::recursive_mutex game; diff --git a/Source/Core/DolphinQt/MainWindow.cpp b/Source/Core/DolphinQt/MainWindow.cpp index 9814644f78..5e7a5f2b46 100644 --- a/Source/Core/DolphinQt/MainWindow.cpp +++ b/Source/Core/DolphinQt/MainWindow.cpp @@ -1284,8 +1284,7 @@ bool MainWindow::NetPlayJoin() if (server) { server->SetHostInputAuthority(host_input_authority); - if (!host_input_authority) - server->AdjustPadBufferSize(Config::Get(Config::NETPLAY_BUFFER_SIZE)); + server->AdjustPadBufferSize(Config::Get(Config::NETPLAY_BUFFER_SIZE)); } // Create Client diff --git a/Source/Core/DolphinQt/NetPlay/NetPlayDialog.cpp b/Source/Core/DolphinQt/NetPlay/NetPlayDialog.cpp index 49c0c8310c..9adafba0b4 100644 --- a/Source/Core/DolphinQt/NetPlay/NetPlayDialog.cpp +++ b/Source/Core/DolphinQt/NetPlay/NetPlayDialog.cpp @@ -300,7 +300,7 @@ void NetPlayDialog::ConnectWidgets() auto client = Settings::Instance().GetNetPlayClient(); auto server = Settings::Instance().GetNetPlayServer(); - if (server) + if (server && !m_host_input_authority) server->AdjustPadBufferSize(value); else client->AdjustPadBufferSize(value); @@ -539,8 +539,6 @@ void NetPlayDialog::show(std::string nickname, bool use_traversal) m_game_button->setEnabled(is_hosting); m_kick_button->setEnabled(false); - m_buffer_label->setText(is_hosting ? tr("Buffer:") : tr("Max Buffer:")); - QDialog::show(); UpdateGUI(); } @@ -857,9 +855,8 @@ void NetPlayDialog::OnPadBufferChanged(u32 buffer) const QSignalBlocker blocker(m_buffer_size_box); m_buffer_size_box->setValue(buffer); }); - DisplayMessage(m_host_input_authority && !IsHosting() ? - tr("Max buffer size changed to %1").arg(buffer) : - tr("Buffer size changed to %1").arg(buffer), + DisplayMessage(m_host_input_authority ? tr("Max buffer size changed to %1").arg(buffer) : + tr("Buffer size changed to %1").arg(buffer), "darkcyan"); m_buffer_size = static_cast(buffer); @@ -867,6 +864,10 @@ void NetPlayDialog::OnPadBufferChanged(u32 buffer) void NetPlayDialog::OnHostInputAuthorityChanged(bool enabled) { + m_host_input_authority = enabled; + DisplayMessage(enabled ? tr("Host input authority enabled") : tr("Host input authority disabled"), + ""); + QueueOnObject(this, [this, enabled] { const bool is_hosting = IsHosting(); const bool enable_buffer = is_hosting != enabled; @@ -878,7 +879,7 @@ void NetPlayDialog::OnHostInputAuthorityChanged(bool enabled) m_buffer_size_box->setHidden(false); m_buffer_label->setHidden(false); - QSignalBlocker blocker(m_host_input_authority_action); + const QSignalBlocker blocker(m_host_input_authority_action); m_host_input_authority_action->setChecked(enabled); } else @@ -887,15 +888,12 @@ void NetPlayDialog::OnHostInputAuthorityChanged(bool enabled) m_buffer_label->setEnabled(true); m_buffer_size_box->setHidden(!enable_buffer); m_buffer_label->setHidden(!enable_buffer); - - if (enabled) - m_buffer_size_box->setValue(Config::Get(Config::NETPLAY_CLIENT_BUFFER_SIZE)); } - }); - DisplayMessage(enabled ? tr("Host input authority enabled") : tr("Host input authority disabled"), - ""); - m_host_input_authority = enabled; + m_buffer_label->setText(enabled ? tr("Max Buffer:") : tr("Buffer:")); + if (enabled) + m_buffer_size_box->setValue(Config::Get(Config::NETPLAY_CLIENT_BUFFER_SIZE)); + }); } void NetPlayDialog::OnDesync(u32 frame, const std::string& player) @@ -1002,14 +1000,10 @@ void NetPlayDialog::SaveSettings() Config::ConfigChangeCallbackGuard config_guard; if (m_host_input_authority) - { - if (!IsHosting()) - Config::SetBase(Config::NETPLAY_CLIENT_BUFFER_SIZE, m_buffer_size_box->value()); - } + Config::SetBase(Config::NETPLAY_CLIENT_BUFFER_SIZE, m_buffer_size_box->value()); else - { Config::SetBase(Config::NETPLAY_BUFFER_SIZE, m_buffer_size_box->value()); - } + Config::SetBase(Config::NETPLAY_WRITE_SAVE_SDCARD_DATA, m_save_sd_action->isChecked()); Config::SetBase(Config::NETPLAY_LOAD_WII_SAVE, m_load_wii_action->isChecked()); Config::SetBase(Config::NETPLAY_SYNC_SAVES, m_sync_save_data_action->isChecked()); From 1a128763305489df3d691a990bbd6033792dd1e1 Mon Sep 17 00:00:00 2001 From: Techjar Date: Tue, 2 Apr 2019 08:08:27 -0400 Subject: [PATCH 2/3] NetPlay: Implement golf mode This is an extension of host input authority that allows switching the host (who has zero latency) on the fly, at the further expense of everyone else's latency. This is useful for turn-based games where the latency of players not on their turn doesn't matter. To become the so-called golfer, the player simply presses a hotkey. When the host is the golfer, latency is identical to normal host input authority. --- Source/Core/Core/Config/NetplaySettings.cpp | 1 + Source/Core/Core/Config/NetplaySettings.h | 1 + Source/Core/Core/HotkeyManager.cpp | 5 +- Source/Core/Core/HotkeyManager.h | 5 +- Source/Core/Core/NetPlayClient.cpp | 131 +++++++++++++++--- Source/Core/Core/NetPlayClient.h | 10 ++ Source/Core/Core/NetPlayProto.h | 9 ++ Source/Core/Core/NetPlayServer.cpp | 82 ++++++++++- Source/Core/Core/NetPlayServer.h | 4 + Source/Core/DolphinQt/HotkeyScheduler.cpp | 3 + Source/Core/DolphinQt/HotkeyScheduler.h | 1 + Source/Core/DolphinQt/MainWindow.cpp | 9 ++ Source/Core/DolphinQt/MainWindow.h | 1 + .../Core/DolphinQt/NetPlay/NetPlayDialog.cpp | 27 +++- Source/Core/DolphinQt/NetPlay/NetPlayDialog.h | 2 + 15 files changed, 263 insertions(+), 28 deletions(-) diff --git a/Source/Core/Core/Config/NetplaySettings.cpp b/Source/Core/Core/Config/NetplaySettings.cpp index 6abedb7f82..66bc826b31 100644 --- a/Source/Core/Core/Config/NetplaySettings.cpp +++ b/Source/Core/Core/Config/NetplaySettings.cpp @@ -55,5 +55,6 @@ const ConfigInfo NETPLAY_HOST_INPUT_AUTHORITY{{System::Main, "NetPlay", "H false}; const ConfigInfo NETPLAY_SYNC_ALL_WII_SAVES{{System::Main, "NetPlay", "SyncAllWiiSaves"}, false}; +const ConfigInfo NETPLAY_GOLF_MODE{{System::Main, "NetPlay", "GolfMode"}, false}; } // namespace Config diff --git a/Source/Core/Core/Config/NetplaySettings.h b/Source/Core/Core/Config/NetplaySettings.h index a7d9cf667d..672b6c1aff 100644 --- a/Source/Core/Core/Config/NetplaySettings.h +++ b/Source/Core/Core/Config/NetplaySettings.h @@ -45,5 +45,6 @@ extern const ConfigInfo NETPLAY_REDUCE_POLLING_RATE; extern const ConfigInfo NETPLAY_STRICT_SETTINGS_SYNC; extern const ConfigInfo NETPLAY_HOST_INPUT_AUTHORITY; extern const ConfigInfo NETPLAY_SYNC_ALL_WII_SAVES; +extern const ConfigInfo NETPLAY_GOLF_MODE; } // namespace Config diff --git a/Source/Core/Core/HotkeyManager.cpp b/Source/Core/Core/HotkeyManager.cpp index 2a161bdf45..8f32b80109 100644 --- a/Source/Core/Core/HotkeyManager.cpp +++ b/Source/Core/Core/HotkeyManager.cpp @@ -20,7 +20,7 @@ #include "InputCommon/GCPadStatus.h" // clang-format off -constexpr std::array s_hotkey_labels{{ +constexpr std::array s_hotkey_labels{{ _trans("Open"), _trans("Change Disc"), _trans("Eject Disc"), @@ -32,6 +32,7 @@ constexpr std::array s_hotkey_labels{{ _trans("Take Screenshot"), _trans("Exit"), _trans("Activate NetPlay Chat"), + _trans("Control NetPlay Golf Mode"), _trans("Volume Down"), _trans("Volume Up"), @@ -275,7 +276,7 @@ struct HotkeyGroupInfo }; constexpr std::array s_groups_info = { - {{_trans("General"), HK_OPEN, HK_ACTIVATE_CHAT}, + {{_trans("General"), HK_OPEN, HK_REQUEST_GOLF_CONTROL}, {_trans("Volume"), HK_VOLUME_DOWN, HK_VOLUME_TOGGLE_MUTE}, {_trans("Emulation Speed"), HK_DECREASE_EMULATION_SPEED, HK_TOGGLE_THROTTLE}, {_trans("Frame Advance"), HK_FRAME_ADVANCE, HK_FRAME_ADVANCE_RESET_SPEED}, diff --git a/Source/Core/Core/HotkeyManager.h b/Source/Core/Core/HotkeyManager.h index 3dbb88ce9b..a1dd55b03c 100644 --- a/Source/Core/Core/HotkeyManager.h +++ b/Source/Core/Core/HotkeyManager.h @@ -15,7 +15,7 @@ namespace ControllerEmu { class ControllerEmu; class Buttons; -} +} // namespace ControllerEmu enum Hotkey { @@ -30,6 +30,7 @@ enum Hotkey HK_SCREENSHOT, HK_EXIT, HK_ACTIVATE_CHAT, + HK_REQUEST_GOLF_CONTROL, HK_VOLUME_DOWN, HK_VOLUME_UP, @@ -236,4 +237,4 @@ void GetStatus(); bool IsEnabled(); void Enable(bool enable_toggle); bool IsPressed(int Id, bool held); -} +} // namespace HotkeyManagerEmu diff --git a/Source/Core/Core/NetPlayClient.cpp b/Source/Core/Core/NetPlayClient.cpp index 5de24892e3..2fe9998248 100644 --- a/Source/Core/Core/NetPlayClient.cpp +++ b/Source/Core/Core/NetPlayClient.cpp @@ -502,6 +502,43 @@ unsigned int NetPlayClient::OnData(sf::Packet& packet) } break; + case NP_MSG_GOLF_SWITCH: + { + PlayerId pid; + packet >> pid; + + const PlayerId previous_golfer = m_current_golfer; + m_current_golfer = pid; + m_dialog->OnGolferChanged(m_local_player->pid == pid, pid != 0 ? m_players[pid].name : ""); + + if (m_local_player->pid == previous_golfer) + { + sf::Packet spac; + spac << static_cast(NP_MSG_GOLF_RELEASE); + Send(spac); + } + else if (m_local_player->pid == pid) + { + sf::Packet spac; + spac << static_cast(NP_MSG_GOLF_ACQUIRE); + Send(spac); + + // Pads are already calibrated so we can just ignore this + m_first_pad_status_received.fill(true); + + m_wait_on_input = false; + m_wait_on_input_event.Set(); + } + } + break; + + case NP_MSG_GOLF_PREPARE: + { + m_wait_on_input_received = true; + m_wait_on_input = true; + } + break; + case NP_MSG_CHANGE_GAME: { { @@ -637,6 +674,8 @@ unsigned int NetPlayClient::OnData(sf::Packet& packet) for (int& extension : m_net_settings.m_WiimoteExtension) packet >> extension; + packet >> m_net_settings.m_GolfMode; + m_net_settings.m_IsHosting = m_local_player->IsHost(); m_net_settings.m_HostInputAuthority = m_host_input_authority; } @@ -1390,6 +1429,8 @@ bool NetPlayClient::StartGame(const std::string& path) } m_timebase_frame = 0; + m_current_golfer = 1; + m_wait_on_input = false; m_is_running.Set(); NetPlay_Enable(this); @@ -1701,6 +1742,27 @@ bool NetPlayClient::GetNetPads(const int pad_nb, const bool batching, GCPadStatu // 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_is_running.IsSet()) + { + return false; + } + + if (m_wait_on_input_received) + { + // Tell the server we've acknowledged the message + sf::Packet spac; + spac << static_cast(NP_MSG_GOLF_PREPARE); + Send(spac); + + m_wait_on_input_received = false; + } + + m_wait_on_input_event.Wait(); + } + if (IsFirstInGamePad(pad_nb) && batching) { sf::Packet packet; @@ -1735,22 +1797,30 @@ bool NetPlayClient::GetNetPads(const int pad_nb, const bool batching, GCPadStatu SendPadHostPoll(pad_nb); } - if (m_host_input_authority && !m_local_player->IsHost()) + if (m_host_input_authority) { - // 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 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 - SConfig::GetInstance().m_EmulationSpeed = 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 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; + } + } + else + { + // Set normal speed when we're the host, otherwise it can get stuck at unlimited + SConfig::GetInstance().m_EmulationSpeed = 1.0f; } } @@ -1880,7 +1950,7 @@ bool NetPlayClient::PollLocalPad(const int local_pad, sf::Packet& packet) if (m_host_input_authority) { - if (!m_local_player->IsHost()) + if (m_local_player->pid != m_current_golfer) { // add to packet AddPadStateToPacket(ingame_pad, pad_status, packet); @@ -1913,7 +1983,7 @@ bool NetPlayClient::PollLocalPad(const int local_pad, sf::Packet& packet) void NetPlayClient::SendPadHostPoll(const PadIndex pad_num) { - if (!m_local_player->IsHost()) + if (m_local_player->pid != m_current_golfer) return; sf::Packet packet; @@ -1937,7 +2007,7 @@ void NetPlayClient::SendPadHostPoll(const PadIndex pad_num) for (size_t i = 0; i < m_pad_map.size(); i++) { - if (m_pad_map[i] == 0) + if (m_pad_map[i] == 0 || m_pad_buffer[i].Size() > 0) continue; const GCPadStatus& pad_status = m_last_pad_status[i]; @@ -1955,9 +2025,12 @@ void NetPlayClient::SendPadHostPoll(const PadIndex pad_num) m_first_pad_status_received_event.Wait(); } - const GCPadStatus& pad_status = m_last_pad_status[pad_num]; - m_pad_buffer[pad_num].Push(pad_status); - AddPadStateToPacket(pad_num, pad_status, packet); + if (m_pad_buffer[pad_num].Size() == 0) + { + const GCPadStatus& pad_status = m_last_pad_status[pad_num]; + m_pad_buffer[pad_num].Push(pad_status); + AddPadStateToPacket(pad_num, pad_status, packet); + } } SendAsync(std::move(packet)); @@ -1972,6 +2045,7 @@ bool NetPlayClient::StopGame() m_gc_pad_event.Set(); m_wii_pad_event.Set(); m_first_pad_status_received_event.Set(); + m_wait_on_input_event.Set(); NetPlay_Disable(); @@ -1995,6 +2069,7 @@ void NetPlayClient::Stop() m_gc_pad_event.Set(); m_wii_pad_event.Set(); m_first_pad_status_received_event.Set(); + m_wait_on_input_event.Set(); // Tell the server to stop if we have a pad mapped in game. if (LocalPlayerHasControllerMapped()) @@ -2017,6 +2092,22 @@ void NetPlayClient::SendPowerButtonEvent() SendAsync(std::move(packet)); } +void NetPlayClient::RequestGolfControl(const PlayerId pid) +{ + if (!m_host_input_authority || !m_net_settings.m_GolfMode) + return; + + sf::Packet packet; + packet << static_cast(NP_MSG_GOLF_REQUEST); + packet << pid; + SendAsync(std::move(packet)); +} + +void NetPlayClient::RequestGolfControl() +{ + RequestGolfControl(m_local_player->pid); +} + // called from ---GUI--- thread bool NetPlayClient::LocalPlayerHasControllerMapped() const { diff --git a/Source/Core/Core/NetPlayClient.h b/Source/Core/Core/NetPlayClient.h index cfe6e37543..54206eb408 100644 --- a/Source/Core/Core/NetPlayClient.h +++ b/Source/Core/Core/NetPlayClient.h @@ -54,6 +54,7 @@ public: virtual void OnTraversalError(TraversalClient::FailureReason error) = 0; virtual void OnTraversalStateChanged(TraversalClient::State state) = 0; virtual void OnSaveDataSyncFailure() = 0; + virtual void OnGolferChanged(bool is_golfer, const std::string& golfer_name) = 0; virtual bool IsRecording() = 0; virtual std::string FindGame(const std::string& game) = 0; @@ -111,6 +112,8 @@ public: void SendChatMessage(const std::string& msg); void RequestStopGame(); void SendPowerButtonEvent(); + void RequestGolfControl(PlayerId pid); + void RequestGolfControl(); // Send and receive pads values bool WiimoteUpdate(int _number, u8* data, const u8 size, u8 reporting_mode); @@ -179,6 +182,12 @@ protected: // speeding up the game to drain the buffer. unsigned int m_target_buffer_size = 20; bool m_host_input_authority = false; + PlayerId m_current_golfer = 1; + + // This bool will stall the client at the start of GetNetPads, used for switching input control + // without deadlocking. Use the correspondingly named Event to wake it up. + bool m_wait_on_input; + bool m_wait_on_input_received; Player* m_local_player = nullptr; @@ -239,6 +248,7 @@ private: Common::Event m_gc_pad_event; Common::Event m_wii_pad_event; Common::Event m_first_pad_status_received_event; + Common::Event m_wait_on_input_event; u8 m_sync_save_data_count = 0; u8 m_sync_save_data_success_count = 0; u16 m_sync_gecko_codes_count = 0; diff --git a/Source/Core/Core/NetPlayProto.h b/Source/Core/Core/NetPlayProto.h index 908b8fe004..a517cdd9ba 100644 --- a/Source/Core/Core/NetPlayProto.h +++ b/Source/Core/Core/NetPlayProto.h @@ -83,6 +83,9 @@ struct NetSettings std::string m_SaveDataRegion; bool m_SyncAllWiiSaves; std::array m_WiimoteExtension; + bool m_GolfMode; + + // These aren't sent over the network directly bool m_IsHosting; bool m_HostInputAuthority; }; @@ -128,6 +131,12 @@ enum NP_MSG_WIIMOTE_DATA = 0x70, NP_MSG_WIIMOTE_MAPPING = 0x71, + NP_MSG_GOLF_REQUEST = 0x90, + NP_MSG_GOLF_SWITCH = 0x91, + NP_MSG_GOLF_ACQUIRE = 0x92, + NP_MSG_GOLF_RELEASE = 0x93, + NP_MSG_GOLF_PREPARE = 0x94, + NP_MSG_START_GAME = 0xA0, NP_MSG_CHANGE_GAME = 0xA1, NP_MSG_STOP_GAME = 0xA2, diff --git a/Source/Core/Core/NetPlayServer.cpp b/Source/Core/Core/NetPlayServer.cpp index 2c6e4aa181..017fc75588 100644 --- a/Source/Core/Core/NetPlayServer.cpp +++ b/Source/Core/Core/NetPlayServer.cpp @@ -680,16 +680,22 @@ unsigned int NetPlayServer::OnData(sf::Packet& packet, Client& player) } if (m_host_input_authority) - Send(m_players.at(1).socket, spac); + { + // Prevent crash before game stop if the golfer disconnects + if (m_current_golfer != 0 && m_players.find(m_current_golfer) != m_players.end()) + Send(m_players.at(m_current_golfer).socket, spac); + } else + { SendToClients(spac, player.pid); + } } break; case NP_MSG_PAD_HOST_DATA: { - // Kick player if they're not the host. - if (!player.IsHost()) + // Kick player if they're not the golfer. + if (m_current_golfer != 0 && player.pid != m_current_golfer) return 1; sf::Packet spac; @@ -745,6 +751,63 @@ unsigned int NetPlayServer::OnData(sf::Packet& packet, Client& player) } break; + case NP_MSG_GOLF_REQUEST: + { + PlayerId pid; + packet >> pid; + + // Check if player ID is valid and sender isn't a spectator + if (!m_players.count(pid) || !PlayerHasControllerMapped(player.pid)) + break; + + if (m_host_input_authority && m_settings.m_GolfMode && m_pending_golfer == 0 && + m_current_golfer != pid && PlayerHasControllerMapped(pid)) + { + m_pending_golfer = pid; + + sf::Packet spac; + spac << static_cast(NP_MSG_GOLF_PREPARE); + Send(m_players[pid].socket, spac); + } + } + break; + + case NP_MSG_GOLF_RELEASE: + { + if (m_pending_golfer == 0) + break; + + sf::Packet spac; + spac << static_cast(NP_MSG_GOLF_SWITCH); + spac << static_cast(m_pending_golfer); + SendToClients(spac); + } + break; + + case NP_MSG_GOLF_ACQUIRE: + { + if (m_pending_golfer == 0) + break; + + m_current_golfer = m_pending_golfer; + m_pending_golfer = 0; + } + break; + + case NP_MSG_GOLF_PREPARE: + { + if (m_pending_golfer == 0) + break; + + m_current_golfer = 0; + + sf::Packet spac; + spac << static_cast(NP_MSG_GOLF_SWITCH); + spac << static_cast(0); + SendToClients(spac); + } + break; + case NP_MSG_PONG: { const u32 ping = (u32)m_ping_timer.GetTimeElapsed(); @@ -1128,6 +1191,9 @@ bool NetPlayServer::StartGame() if (!m_host_input_authority) AdjustPadBufferSize(m_target_buffer_size); + m_current_golfer = 1; + m_pending_golfer = 0; + const sf::Uint64 initial_rtc = GetInitialNetPlayRTC(); const std::string region = SConfig::GetDirectoryForRegion( @@ -1212,6 +1278,8 @@ bool NetPlayServer::StartGame() spac << extension; } + spac << m_settings.m_GolfMode; + SendAsyncToClients(std::move(spac)); m_start_pending = false; @@ -1747,6 +1815,14 @@ void NetPlayServer::KickPlayer(PlayerId player) } } +bool NetPlayServer::PlayerHasControllerMapped(const PlayerId pid) const +{ + const auto mapping_matches_player_id = [pid](const PlayerId& mapping) { return mapping == pid; }; + + return std::any_of(m_pad_map.begin(), m_pad_map.end(), mapping_matches_player_id) || + std::any_of(m_wiimote_map.begin(), m_wiimote_map.end(), mapping_matches_player_id); +} + u16 NetPlayServer::GetPort() const { return m_server->address.port; diff --git a/Source/Core/Core/NetPlayServer.h b/Source/Core/Core/NetPlayServer.h index 447b3de161..79e63e1556 100644 --- a/Source/Core/Core/NetPlayServer.h +++ b/Source/Core/Core/NetPlayServer.h @@ -137,6 +137,8 @@ private: void ChunkedDataThreadFunc(); void ChunkedDataSend(sf::Packet&& packet, PlayerId pid, const TargetMode target_mode); + bool PlayerHasControllerMapped(PlayerId pid) const; + NetSettings m_settings; bool m_is_running = false; @@ -154,6 +156,8 @@ private: bool m_codes_synced = true; bool m_start_pending = false; bool m_host_input_authority = false; + PlayerId m_current_golfer = 1; + PlayerId m_pending_golfer = 0; std::map m_players; diff --git a/Source/Core/DolphinQt/HotkeyScheduler.cpp b/Source/Core/DolphinQt/HotkeyScheduler.cpp index 956de04486..89277a3905 100644 --- a/Source/Core/DolphinQt/HotkeyScheduler.cpp +++ b/Source/Core/DolphinQt/HotkeyScheduler.cpp @@ -200,6 +200,9 @@ void HotkeyScheduler::Run() if (IsHotkey(HK_ACTIVATE_CHAT)) emit ActivateChat(); + if (IsHotkey(HK_REQUEST_GOLF_CONTROL)) + emit RequestGolfControl(); + // Recording if (IsHotkey(HK_START_RECORDING)) emit StartRecording(); diff --git a/Source/Core/DolphinQt/HotkeyScheduler.h b/Source/Core/DolphinQt/HotkeyScheduler.h index ca5aee36ec..f104abb44d 100644 --- a/Source/Core/DolphinQt/HotkeyScheduler.h +++ b/Source/Core/DolphinQt/HotkeyScheduler.h @@ -27,6 +27,7 @@ signals: void ExitHotkey(); void ActivateChat(); + void RequestGolfControl(); void FullScreenHotkey(); void StopHotkey(); void ResetHotkey(); diff --git a/Source/Core/DolphinQt/MainWindow.cpp b/Source/Core/DolphinQt/MainWindow.cpp index 5e7a5f2b46..78ac86323f 100644 --- a/Source/Core/DolphinQt/MainWindow.cpp +++ b/Source/Core/DolphinQt/MainWindow.cpp @@ -494,6 +494,8 @@ void MainWindow::ConnectHotkeys() connect(m_hotkey_scheduler, &HotkeyScheduler::ExitHotkey, this, &MainWindow::close); connect(m_hotkey_scheduler, &HotkeyScheduler::TogglePauseHotkey, this, &MainWindow::TogglePause); connect(m_hotkey_scheduler, &HotkeyScheduler::ActivateChat, this, &MainWindow::OnActivateChat); + connect(m_hotkey_scheduler, &HotkeyScheduler::RequestGolfControl, this, + &MainWindow::OnRequestGolfControl); connect(m_hotkey_scheduler, &HotkeyScheduler::RefreshGameListHotkey, this, &MainWindow::RefreshGameList); connect(m_hotkey_scheduler, &HotkeyScheduler::StopHotkey, this, &MainWindow::RequestStop); @@ -1604,6 +1606,13 @@ void MainWindow::OnActivateChat() g_netplay_chat_ui->Activate(); } +void MainWindow::OnRequestGolfControl() +{ + auto client = Settings::Instance().GetNetPlayClient(); + if (client) + client->RequestGolfControl(); +} + void MainWindow::ShowTASInput() { for (int i = 0; i < num_gc_controllers; i++) diff --git a/Source/Core/DolphinQt/MainWindow.h b/Source/Core/DolphinQt/MainWindow.h index edc6fc479b..bf9027960b 100644 --- a/Source/Core/DolphinQt/MainWindow.h +++ b/Source/Core/DolphinQt/MainWindow.h @@ -170,6 +170,7 @@ private: void OnStopRecording(); void OnExportRecording(); void OnActivateChat(); + void OnRequestGolfControl(); void ShowTASInput(); void ChangeDisc(); diff --git a/Source/Core/DolphinQt/NetPlay/NetPlayDialog.cpp b/Source/Core/DolphinQt/NetPlay/NetPlayDialog.cpp index 9adafba0b4..206015ab82 100644 --- a/Source/Core/DolphinQt/NetPlay/NetPlayDialog.cpp +++ b/Source/Core/DolphinQt/NetPlay/NetPlayDialog.cpp @@ -85,6 +85,7 @@ NetPlayDialog::NetPlayDialog(QWidget* parent) const bool strict_settings_sync = Config::Get(Config::NETPLAY_STRICT_SETTINGS_SYNC); const bool host_input_authority = Config::Get(Config::NETPLAY_HOST_INPUT_AUTHORITY); const bool sync_all_wii_saves = Config::Get(Config::NETPLAY_SYNC_ALL_WII_SAVES); + const bool golf_mode = Config::Get(Config::NETPLAY_GOLF_MODE); m_buffer_size_box->setValue(buffer_size); m_save_sd_action->setChecked(write_save_sdcard_data); @@ -96,6 +97,7 @@ NetPlayDialog::NetPlayDialog(QWidget* parent) m_strict_settings_sync_action->setChecked(strict_settings_sync); m_host_input_authority_action->setChecked(host_input_authority); m_sync_all_wii_saves_action->setChecked(sync_all_wii_saves); + m_golf_mode_action->setChecked(golf_mode); ConnectWidgets(); @@ -144,6 +146,8 @@ void NetPlayDialog::CreateMainLayout() m_reduce_polling_rate_action->setCheckable(true); m_host_input_authority_action = m_network_menu->addAction(tr("Host Input Authority")); m_host_input_authority_action->setCheckable(true); + m_golf_mode_action = m_network_menu->addAction(tr("Golf Mode")); + m_golf_mode_action->setCheckable(true); m_other_menu = m_menu_bar->addMenu(tr("Other")); m_record_input_action = m_other_menu->addAction(tr("Record Inputs")); @@ -306,10 +310,12 @@ void NetPlayDialog::ConnectWidgets() client->AdjustPadBufferSize(value); }); - connect(m_host_input_authority_action, &QAction::toggled, [](bool checked) { + connect(m_host_input_authority_action, &QAction::toggled, this, [=](bool checked) { auto server = Settings::Instance().GetNetPlayServer(); if (server) server->SetHostInputAuthority(checked); + + m_golf_mode_action->setEnabled(checked); }); connect(m_start_button, &QPushButton::clicked, this, &NetPlayDialog::OnStart); @@ -355,6 +361,7 @@ void NetPlayDialog::ConnectWidgets() connect(m_strict_settings_sync_action, &QAction::toggled, this, &NetPlayDialog::SaveSettings); connect(m_host_input_authority_action, &QAction::toggled, this, &NetPlayDialog::SaveSettings); connect(m_sync_all_wii_saves_action, &QAction::toggled, this, &NetPlayDialog::SaveSettings); + connect(m_golf_mode_action, &QAction::toggled, this, &NetPlayDialog::SaveSettings); } void NetPlayDialog::SendMessage(const std::string& msg) @@ -482,6 +489,7 @@ void NetPlayDialog::OnStart() settings.m_SyncCodes = m_sync_codes_action->isChecked(); settings.m_SyncAllWiiSaves = m_sync_all_wii_saves_action->isChecked() && m_sync_save_data_action->isChecked(); + settings.m_GolfMode = m_golf_mode_action->isChecked(); // Unload GameINI to restore things to normal Config::RemoveLayer(Config::LayerType::GlobalGame); @@ -815,6 +823,7 @@ void NetPlayDialog::SetOptionsEnabled(bool enabled) m_strict_settings_sync_action->setEnabled(enabled); m_host_input_authority_action->setEnabled(enabled); m_sync_all_wii_saves_action->setEnabled(enabled && m_sync_save_data_action->isChecked()); + m_golf_mode_action->setEnabled(enabled && m_host_input_authority_action->isChecked()); } m_record_input_action->setEnabled(enabled); @@ -881,6 +890,7 @@ void NetPlayDialog::OnHostInputAuthorityChanged(bool enabled) const QSignalBlocker blocker(m_host_input_authority_action); m_host_input_authority_action->setChecked(enabled); + m_golf_mode_action->setEnabled(enabled); } else { @@ -956,6 +966,20 @@ void NetPlayDialog::OnSaveDataSyncFailure() QueueOnObject(this, [this] { SetOptionsEnabled(true); }); } +void NetPlayDialog::OnGolferChanged(const bool is_golfer, const std::string& golfer_name) +{ + if (m_host_input_authority) + { + QueueOnObject(this, [this, is_golfer] { + m_buffer_size_box->setEnabled(!is_golfer); + m_buffer_label->setEnabled(!is_golfer); + }); + } + + if (!golfer_name.empty()) + DisplayMessage(tr("%1 is now golfing").arg(QString::fromStdString(golfer_name)), ""); +} + bool NetPlayDialog::IsRecording() { std::optional is_recording = RunOnObject(m_record_input_action, &QAction::isChecked); @@ -1013,6 +1037,7 @@ void NetPlayDialog::SaveSettings() Config::SetBase(Config::NETPLAY_STRICT_SETTINGS_SYNC, m_strict_settings_sync_action->isChecked()); Config::SetBase(Config::NETPLAY_HOST_INPUT_AUTHORITY, m_host_input_authority_action->isChecked()); Config::SetBase(Config::NETPLAY_SYNC_ALL_WII_SAVES, m_sync_all_wii_saves_action->isChecked()); + Config::SetBase(Config::NETPLAY_GOLF_MODE, m_golf_mode_action->isChecked()); } void NetPlayDialog::ShowMD5Dialog(const std::string& file_identifier) diff --git a/Source/Core/DolphinQt/NetPlay/NetPlayDialog.h b/Source/Core/DolphinQt/NetPlay/NetPlayDialog.h index 49acc0fd42..e0aefdb311 100644 --- a/Source/Core/DolphinQt/NetPlay/NetPlayDialog.h +++ b/Source/Core/DolphinQt/NetPlay/NetPlayDialog.h @@ -58,6 +58,7 @@ public: void OnTraversalError(TraversalClient::FailureReason error) override; void OnTraversalStateChanged(TraversalClient::State state) override; void OnSaveDataSyncFailure() override; + void OnGolferChanged(bool is_golfer, const std::string& golfer_name) override; bool IsRecording() override; std::string FindGame(const std::string& game) override; @@ -131,6 +132,7 @@ private: QAction* m_strict_settings_sync_action; QAction* m_host_input_authority_action; QAction* m_sync_all_wii_saves_action; + QAction* m_golf_mode_action; QPushButton* m_quit_button; QSplitter* m_splitter; From 6c393f9ff420c2a4185122cfb1bed0fbb2bcfad9 Mon Sep 17 00:00:00 2001 From: Techjar Date: Tue, 2 Apr 2019 17:13:42 -0400 Subject: [PATCH 3/3] Add imgui golf mode overlay --- Source/Core/Core/Config/NetplaySettings.cpp | 2 + Source/Core/Core/Config/NetplaySettings.h | 1 + Source/Core/Core/NetPlayClient.cpp | 29 ++++++-- Source/Core/Core/NetPlayClient.h | 7 +- .../Core/DolphinQt/NetPlay/NetPlayDialog.cpp | 13 ++++ Source/Core/DolphinQt/NetPlay/NetPlayDialog.h | 1 + Source/Core/DolphinQt/Settings.cpp | 2 + Source/Core/VideoCommon/CMakeLists.txt | 1 + Source/Core/VideoCommon/NetPlayGolfUI.cpp | 66 +++++++++++++++++++ Source/Core/VideoCommon/NetPlayGolfUI.h | 27 ++++++++ Source/Core/VideoCommon/RenderBase.cpp | 5 ++ Source/Core/VideoCommon/VideoCommon.vcxproj | 4 +- .../VideoCommon/VideoCommon.vcxproj.filters | 8 ++- 13 files changed, 156 insertions(+), 10 deletions(-) create mode 100644 Source/Core/VideoCommon/NetPlayGolfUI.cpp create mode 100644 Source/Core/VideoCommon/NetPlayGolfUI.h diff --git a/Source/Core/Core/Config/NetplaySettings.cpp b/Source/Core/Core/Config/NetplaySettings.cpp index 66bc826b31..1f01452bcb 100644 --- a/Source/Core/Core/Config/NetplaySettings.cpp +++ b/Source/Core/Core/Config/NetplaySettings.cpp @@ -56,5 +56,7 @@ const ConfigInfo NETPLAY_HOST_INPUT_AUTHORITY{{System::Main, "NetPlay", "H const ConfigInfo NETPLAY_SYNC_ALL_WII_SAVES{{System::Main, "NetPlay", "SyncAllWiiSaves"}, false}; const ConfigInfo NETPLAY_GOLF_MODE{{System::Main, "NetPlay", "GolfMode"}, false}; +const ConfigInfo NETPLAY_GOLF_MODE_OVERLAY{{System::Main, "NetPlay", "GolfModeOverlay"}, + true}; } // namespace Config diff --git a/Source/Core/Core/Config/NetplaySettings.h b/Source/Core/Core/Config/NetplaySettings.h index 672b6c1aff..adbac875e0 100644 --- a/Source/Core/Core/Config/NetplaySettings.h +++ b/Source/Core/Core/Config/NetplaySettings.h @@ -46,5 +46,6 @@ extern const ConfigInfo NETPLAY_STRICT_SETTINGS_SYNC; extern const ConfigInfo NETPLAY_HOST_INPUT_AUTHORITY; extern const ConfigInfo NETPLAY_SYNC_ALL_WII_SAVES; extern const ConfigInfo NETPLAY_GOLF_MODE; +extern const ConfigInfo NETPLAY_GOLF_MODE_OVERLAY; } // namespace Config diff --git a/Source/Core/Core/NetPlayClient.cpp b/Source/Core/Core/NetPlayClient.cpp index 2fe9998248..a2e6dcc562 100644 --- a/Source/Core/Core/NetPlayClient.cpp +++ b/Source/Core/Core/NetPlayClient.cpp @@ -2108,15 +2108,19 @@ void NetPlayClient::RequestGolfControl() RequestGolfControl(m_local_player->pid); } +// called from ---GUI--- thread +std::string NetPlayClient::GetCurrentGolfer() +{ + std::lock_guard lkp(m_crit.players); + if (m_players.count(m_current_golfer)) + return m_players[m_current_golfer].name; + return ""; +} + // called from ---GUI--- thread bool NetPlayClient::LocalPlayerHasControllerMapped() const { - const auto mapping_matches_player_id = [this](const PlayerId& mapping) { - return mapping == m_local_player->pid; - }; - - return std::any_of(m_pad_map.begin(), m_pad_map.end(), mapping_matches_player_id) || - std::any_of(m_wiimote_map.begin(), m_wiimote_map.end(), mapping_matches_player_id); + return PlayerHasControllerMapped(m_local_player->pid); } bool NetPlayClient::IsFirstInGamePad(int ingame_pad) const @@ -2169,6 +2173,19 @@ int NetPlayClient::LocalPadToInGamePad(int local_pad) const return ingame_pad; } +bool NetPlayClient::PlayerHasControllerMapped(const PlayerId pid) const +{ + const auto mapping_matches_player_id = [pid](const PlayerId& mapping) { return mapping == pid; }; + + return std::any_of(m_pad_map.begin(), m_pad_map.end(), mapping_matches_player_id) || + std::any_of(m_wiimote_map.begin(), m_wiimote_map.end(), mapping_matches_player_id); +} + +bool NetPlayClient::IsLocalPlayer(const PlayerId pid) const +{ + return pid == m_local_player->pid; +} + void NetPlayClient::SendTimeBase() { std::lock_guard lk(crit_netplay_client); diff --git a/Source/Core/Core/NetPlayClient.h b/Source/Core/Core/NetPlayClient.h index 54206eb408..9afa624dd4 100644 --- a/Source/Core/Core/NetPlayClient.h +++ b/Source/Core/Core/NetPlayClient.h @@ -114,6 +114,7 @@ public: void SendPowerButtonEvent(); void RequestGolfControl(PlayerId pid); void RequestGolfControl(); + std::string GetCurrentGolfer(); // Send and receive pads values bool WiimoteUpdate(int _number, u8* data, const u8 size, u8 reporting_mode); @@ -131,6 +132,10 @@ public: int InGamePadToLocalPad(int ingame_pad) const; int LocalPadToInGamePad(int localPad) const; + bool PlayerHasControllerMapped(PlayerId pid) const; + bool LocalPlayerHasControllerMapped() const; + bool IsLocalPlayer(PlayerId pid) const; + static void SendTimeBase(); bool DoAllPlayersHaveGame(); @@ -209,8 +214,6 @@ private: Failure }; - bool LocalPlayerHasControllerMapped() const; - void SendStartGamePacket(); void SendStopGamePacket(); diff --git a/Source/Core/DolphinQt/NetPlay/NetPlayDialog.cpp b/Source/Core/DolphinQt/NetPlay/NetPlayDialog.cpp index 206015ab82..80b643b648 100644 --- a/Source/Core/DolphinQt/NetPlay/NetPlayDialog.cpp +++ b/Source/Core/DolphinQt/NetPlay/NetPlayDialog.cpp @@ -55,6 +55,7 @@ #include "UICommon/UICommon.h" #include "VideoCommon/NetPlayChatUI.h" +#include "VideoCommon/NetPlayGolfUI.h" #include "VideoCommon/RenderBase.h" #include "VideoCommon/VideoConfig.h" @@ -86,6 +87,7 @@ NetPlayDialog::NetPlayDialog(QWidget* parent) const bool host_input_authority = Config::Get(Config::NETPLAY_HOST_INPUT_AUTHORITY); const bool sync_all_wii_saves = Config::Get(Config::NETPLAY_SYNC_ALL_WII_SAVES); const bool golf_mode = Config::Get(Config::NETPLAY_GOLF_MODE); + const bool golf_mode_overlay = Config::Get(Config::NETPLAY_GOLF_MODE_OVERLAY); m_buffer_size_box->setValue(buffer_size); m_save_sd_action->setChecked(write_save_sdcard_data); @@ -98,6 +100,7 @@ NetPlayDialog::NetPlayDialog(QWidget* parent) m_host_input_authority_action->setChecked(host_input_authority); m_sync_all_wii_saves_action->setChecked(sync_all_wii_saves); m_golf_mode_action->setChecked(golf_mode); + m_golf_mode_overlay_action->setChecked(golf_mode_overlay); ConnectWidgets(); @@ -152,6 +155,8 @@ void NetPlayDialog::CreateMainLayout() m_other_menu = m_menu_bar->addMenu(tr("Other")); m_record_input_action = m_other_menu->addAction(tr("Record Inputs")); m_record_input_action->setCheckable(true); + m_golf_mode_overlay_action = m_other_menu->addAction(tr("Show Golf Mode Overlay")); + m_golf_mode_overlay_action->setCheckable(true); m_game_button->setDefault(false); m_game_button->setAutoDefault(false); @@ -362,6 +367,7 @@ void NetPlayDialog::ConnectWidgets() connect(m_host_input_authority_action, &QAction::toggled, this, &NetPlayDialog::SaveSettings); connect(m_sync_all_wii_saves_action, &QAction::toggled, this, &NetPlayDialog::SaveSettings); connect(m_golf_mode_action, &QAction::toggled, this, &NetPlayDialog::SaveSettings); + connect(m_golf_mode_overlay_action, &QAction::toggled, this, &NetPlayDialog::SaveSettings); } void NetPlayDialog::SendMessage(const std::string& msg) @@ -836,6 +842,11 @@ void NetPlayDialog::OnMsgStartGame() g_netplay_chat_ui = std::make_unique([this](const std::string& message) { SendMessage(message); }); + if (Settings::Instance().GetNetPlayClient()->GetNetSettings().m_GolfMode) + { + g_netplay_golf_ui = std::make_unique(Settings::Instance().GetNetPlayClient()); + } + QueueOnObject(this, [this] { auto client = Settings::Instance().GetNetPlayClient(); @@ -848,6 +859,7 @@ void NetPlayDialog::OnMsgStartGame() void NetPlayDialog::OnMsgStopGame() { g_netplay_chat_ui.reset(); + g_netplay_golf_ui.reset(); QueueOnObject(this, [this] { UpdateDiscordPresence(); }); } @@ -1038,6 +1050,7 @@ void NetPlayDialog::SaveSettings() Config::SetBase(Config::NETPLAY_HOST_INPUT_AUTHORITY, m_host_input_authority_action->isChecked()); Config::SetBase(Config::NETPLAY_SYNC_ALL_WII_SAVES, m_sync_all_wii_saves_action->isChecked()); Config::SetBase(Config::NETPLAY_GOLF_MODE, m_golf_mode_action->isChecked()); + Config::SetBase(Config::NETPLAY_GOLF_MODE_OVERLAY, m_golf_mode_overlay_action->isChecked()); } void NetPlayDialog::ShowMD5Dialog(const std::string& file_identifier) diff --git a/Source/Core/DolphinQt/NetPlay/NetPlayDialog.h b/Source/Core/DolphinQt/NetPlay/NetPlayDialog.h index e0aefdb311..558990cb1d 100644 --- a/Source/Core/DolphinQt/NetPlay/NetPlayDialog.h +++ b/Source/Core/DolphinQt/NetPlay/NetPlayDialog.h @@ -133,6 +133,7 @@ private: QAction* m_host_input_authority_action; QAction* m_sync_all_wii_saves_action; QAction* m_golf_mode_action; + QAction* m_golf_mode_overlay_action; QPushButton* m_quit_button; QSplitter* m_splitter; diff --git a/Source/Core/DolphinQt/Settings.cpp b/Source/Core/DolphinQt/Settings.cpp index 61be8ff8c0..95242b8ceb 100644 --- a/Source/Core/DolphinQt/Settings.cpp +++ b/Source/Core/DolphinQt/Settings.cpp @@ -28,6 +28,7 @@ #include "InputCommon/InputConfig.h" #include "VideoCommon/NetPlayChatUI.h" +#include "VideoCommon/NetPlayGolfUI.h" #include "VideoCommon/RenderBase.h" Settings::Settings() @@ -298,6 +299,7 @@ void Settings::ResetNetPlayClient(NetPlay::NetPlayClient* client) m_client.reset(client); g_netplay_chat_ui.reset(); + g_netplay_golf_ui.reset(); } std::shared_ptr Settings::GetNetPlayServer() diff --git a/Source/Core/VideoCommon/CMakeLists.txt b/Source/Core/VideoCommon/CMakeLists.txt index 459fbcb7aa..dfabf8fede 100644 --- a/Source/Core/VideoCommon/CMakeLists.txt +++ b/Source/Core/VideoCommon/CMakeLists.txt @@ -23,6 +23,7 @@ add_library(videocommon IndexGenerator.cpp LightingShaderGen.cpp NetPlayChatUI.cpp + NetPlayGolfUI.cpp OnScreenDisplay.cpp OpcodeDecoding.cpp PerfQueryBase.cpp diff --git a/Source/Core/VideoCommon/NetPlayGolfUI.cpp b/Source/Core/VideoCommon/NetPlayGolfUI.cpp new file mode 100644 index 0000000000..3a831b1cf1 --- /dev/null +++ b/Source/Core/VideoCommon/NetPlayGolfUI.cpp @@ -0,0 +1,66 @@ +// Copyright 2019 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include "Common/StringUtil.h" + +#include "Core/NetPlayClient.h" + +#include "VideoCommon/NetPlayGolfUI.h" + +#include + +constexpr float DEFAULT_WINDOW_WIDTH = 220.0f; +constexpr float DEFAULT_WINDOW_HEIGHT = 45.0f; + +std::unique_ptr g_netplay_golf_ui; + +NetPlayGolfUI::NetPlayGolfUI(std::shared_ptr netplay_client) +{ + m_netplay_client = netplay_client; +} + +void NetPlayGolfUI::Display() +{ + auto client = m_netplay_client.lock(); + if (!client) + return; + + const float scale = ImGui::GetIO().DisplayFramebufferScale.x; + + ImGui::SetNextWindowPos(ImVec2((20.0f + DEFAULT_WINDOW_WIDTH) * scale, 10.0f * scale), + ImGuiCond_FirstUseEver); + ImGui::SetNextWindowSizeConstraints( + ImVec2(DEFAULT_WINDOW_WIDTH * scale, DEFAULT_WINDOW_HEIGHT * scale), + ImGui::GetIO().DisplaySize); + + // TODO: Translate these strings once imgui has multilingual fonts + if (!ImGui::Begin("Golf Mode", nullptr, ImGuiWindowFlags_None)) + { + ImGui::End(); + return; + } + + ImGui::Text("Current Golfer: %s", client->GetCurrentGolfer().c_str()); + + if (client->LocalPlayerHasControllerMapped()) + { + if (ImGui::Button("Take Control")) + { + client->RequestGolfControl(); + } + + for (auto player : client->GetPlayers()) + { + if (client->IsLocalPlayer(player->pid) || !client->PlayerHasControllerMapped(player->pid)) + continue; + + if (ImGui::Button(StringFromFormat("Give Control to %s", player->name.c_str()).c_str())) + { + client->RequestGolfControl(player->pid); + } + } + } + + ImGui::End(); +} diff --git a/Source/Core/VideoCommon/NetPlayGolfUI.h b/Source/Core/VideoCommon/NetPlayGolfUI.h new file mode 100644 index 0000000000..097c6cf600 --- /dev/null +++ b/Source/Core/VideoCommon/NetPlayGolfUI.h @@ -0,0 +1,27 @@ +// Copyright 2019 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#include +#include + +namespace NetPlay +{ +class NetPlayClient; +} + +class NetPlayGolfUI +{ +public: + explicit NetPlayGolfUI(std::shared_ptr netplay_client); + ~NetPlayGolfUI() = default; + + void Display(); + +private: + std::weak_ptr m_netplay_client; +}; + +extern std::unique_ptr g_netplay_golf_ui; diff --git a/Source/Core/VideoCommon/RenderBase.cpp b/Source/Core/VideoCommon/RenderBase.cpp index 688c7fc43b..09d59e4877 100644 --- a/Source/Core/VideoCommon/RenderBase.cpp +++ b/Source/Core/VideoCommon/RenderBase.cpp @@ -37,6 +37,7 @@ #include "Common/Timer.h" #include "Core/Analytics.h" +#include "Core/Config/NetplaySettings.h" #include "Core/Config/SYSCONFSettings.h" #include "Core/ConfigManager.h" #include "Core/Core.h" @@ -58,6 +59,7 @@ #include "VideoCommon/FramebufferManager.h" #include "VideoCommon/ImageWrite.h" #include "VideoCommon/NetPlayChatUI.h" +#include "VideoCommon/NetPlayGolfUI.h" #include "VideoCommon/OnScreenDisplay.h" #include "VideoCommon/PixelEngine.h" #include "VideoCommon/PixelShaderManager.h" @@ -532,6 +534,9 @@ void Renderer::DrawDebugText() if (g_ActiveConfig.bShowNetPlayMessages && g_netplay_chat_ui) g_netplay_chat_ui->Display(); + if (Config::Get(Config::NETPLAY_GOLF_MODE_OVERLAY) && g_netplay_golf_ui) + g_netplay_golf_ui->Display(); + if (g_ActiveConfig.bOverlayProjStats) Statistics::DisplayProj(); } diff --git a/Source/Core/VideoCommon/VideoCommon.vcxproj b/Source/Core/VideoCommon/VideoCommon.vcxproj index d18f7f444e..51f93d0457 100644 --- a/Source/Core/VideoCommon/VideoCommon.vcxproj +++ b/Source/Core/VideoCommon/VideoCommon.vcxproj @@ -58,6 +58,7 @@ + @@ -122,6 +123,7 @@ + @@ -192,4 +194,4 @@ - \ No newline at end of file + diff --git a/Source/Core/VideoCommon/VideoCommon.vcxproj.filters b/Source/Core/VideoCommon/VideoCommon.vcxproj.filters index c1ecc02dbd..ea9e2d3346 100644 --- a/Source/Core/VideoCommon/VideoCommon.vcxproj.filters +++ b/Source/Core/VideoCommon/VideoCommon.vcxproj.filters @@ -200,6 +200,9 @@ Util + + Util + @@ -392,8 +395,11 @@ Util + + Util + - \ No newline at end of file +