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;