diff options
| author | spycrab <spycrab@users.noreply.github.com> | 2018-07-07 00:58:37 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2018-07-07 00:58:37 +0200 |
| commit | 3d1a7737d86f4ecdf0131f5b84198f288800da3b (patch) | |
| tree | 747e394036c189c46add1b3e1451e50f48230e11 /Source/Core/DolphinQt/NetPlay/NetPlayDialog.cpp | |
| parent | 059880bb1640d5a3b0e1fbbf66e1a356df4b9f41 (diff) | |
| parent | 13ba24c5a6795532c8893eccf2a21ae95682e1b7 (diff) | |
Merge pull request #7205 from spycrab/qt_rename
Move DolphinQt2 to DolphinQt
Diffstat (limited to 'Source/Core/DolphinQt/NetPlay/NetPlayDialog.cpp')
| -rw-r--r-- | Source/Core/DolphinQt/NetPlay/NetPlayDialog.cpp | 669 |
1 files changed, 669 insertions, 0 deletions
diff --git a/Source/Core/DolphinQt/NetPlay/NetPlayDialog.cpp b/Source/Core/DolphinQt/NetPlay/NetPlayDialog.cpp new file mode 100644 index 0000000000..c61adc081b --- /dev/null +++ b/Source/Core/DolphinQt/NetPlay/NetPlayDialog.cpp @@ -0,0 +1,669 @@ +// Copyright 2017 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include "DolphinQt/NetPlay/NetPlayDialog.h" + +#include <QAction> +#include <QApplication> +#include <QCheckBox> +#include <QClipboard> +#include <QComboBox> +#include <QGridLayout> +#include <QGroupBox> +#include <QHBoxLayout> +#include <QHeaderView> +#include <QLabel> +#include <QLineEdit> +#include <QMenu> +#include <QMessageBox> +#include <QProgressDialog> +#include <QPushButton> +#include <QSpinBox> +#include <QSplitter> +#include <QTableWidget> +#include <QTextBrowser> +#include <QToolButton> + +#include <sstream> + +#include "Common/CommonPaths.h" +#include "Common/Config/Config.h" +#include "Common/TraversalClient.h" + +#include "Core/Config/SYSCONFSettings.h" +#include "Core/ConfigManager.h" +#include "Core/Core.h" +#include "Core/NetPlayServer.h" + +#include "DolphinQt/GameList/GameList.h" +#include "DolphinQt/NetPlay/GameListDialog.h" +#include "DolphinQt/NetPlay/MD5Dialog.h" +#include "DolphinQt/NetPlay/PadMappingDialog.h" +#include "DolphinQt/QtUtils/QueueOnObject.h" +#include "DolphinQt/QtUtils/RunOnObject.h" +#include "DolphinQt/Resources.h" +#include "DolphinQt/Settings.h" + +#include "VideoCommon/VideoConfig.h" + +NetPlayDialog::NetPlayDialog(QWidget* parent) + : QDialog(parent), m_game_list_model(Settings::Instance().GetGameListModel()) +{ + setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); + + setWindowTitle(tr("NetPlay")); + setWindowIcon(Resources::GetAppIcon()); + + m_pad_mapping = new PadMappingDialog(this); + m_md5_dialog = new MD5Dialog(this); + + CreateChatLayout(); + CreatePlayersLayout(); + CreateMainLayout(); + ConnectWidgets(); + + auto& settings = Settings::Instance().GetQSettings(); + + restoreGeometry(settings.value(QStringLiteral("netplaydialog/geometry")).toByteArray()); + m_splitter->restoreState(settings.value(QStringLiteral("netplaydialog/splitter")).toByteArray()); +} + +NetPlayDialog::~NetPlayDialog() +{ + auto& settings = Settings::Instance().GetQSettings(); + + settings.setValue(QStringLiteral("netplaydialog/geometry"), saveGeometry()); + settings.setValue(QStringLiteral("netplaydialog/splitter"), m_splitter->saveState()); +} + +void NetPlayDialog::CreateMainLayout() +{ + m_main_layout = new QGridLayout; + m_game_button = new QPushButton; + m_md5_button = new QToolButton; + m_start_button = new QPushButton(tr("Start")); + m_buffer_size_box = new QSpinBox; + m_save_sd_box = new QCheckBox(tr("Write save/SD data")); + m_load_wii_box = new QCheckBox(tr("Load Wii Save")); + m_record_input_box = new QCheckBox(tr("Record inputs")); + m_reduce_polling_rate_box = new QCheckBox(tr("Reduce Polling Rate")); + m_buffer_label = new QLabel(tr("Buffer:")); + m_quit_button = new QPushButton(tr("Quit")); + m_splitter = new QSplitter(Qt::Horizontal); + + m_game_button->setDefault(false); + m_game_button->setAutoDefault(false); + + auto* default_button = new QAction(tr("Calculate MD5 hash"), m_md5_button); + + auto* menu = new QMenu(this); + + auto* other_game_button = new QAction(tr("Other game"), this); + auto* sdcard_button = new QAction(tr("SD Card"), this); + + menu->addAction(other_game_button); + menu->addAction(sdcard_button); + + connect(default_button, &QAction::triggered, + [this] { Settings::Instance().GetNetPlayServer()->ComputeMD5(m_current_game); }); + connect(other_game_button, &QAction::triggered, [this] { + GameListDialog gld(this); + + if (gld.exec() == QDialog::Accepted) + { + Settings::Instance().GetNetPlayServer()->ComputeMD5(gld.GetSelectedUniqueID().toStdString()); + } + }); + connect(sdcard_button, &QAction::triggered, + [] { Settings::Instance().GetNetPlayServer()->ComputeMD5(WII_SDCARD); }); + + m_md5_button->setDefaultAction(default_button); + m_md5_button->setPopupMode(QToolButton::MenuButtonPopup); + m_md5_button->setMenu(menu); + + m_reduce_polling_rate_box->setToolTip( + tr("This will reduce bandwidth usage, but may add up to one frame of additional latency, as " + "controllers will be polled only once per frame.")); + + m_main_layout->addWidget(m_game_button, 0, 0); + m_main_layout->addWidget(m_md5_button, 0, 1); + m_main_layout->addWidget(m_splitter, 1, 0, 1, -1); + + m_splitter->addWidget(m_chat_box); + m_splitter->addWidget(m_players_box); + + auto* options_widget = new QHBoxLayout; + + options_widget->addWidget(m_start_button); + options_widget->addWidget(m_buffer_label); + options_widget->addWidget(m_buffer_size_box); + options_widget->addWidget(m_save_sd_box); + options_widget->addWidget(m_load_wii_box); + options_widget->addWidget(m_record_input_box); + options_widget->addWidget(m_reduce_polling_rate_box); + options_widget->addWidget(m_quit_button); + m_main_layout->addLayout(options_widget, 2, 0, 1, -1, Qt::AlignRight); + + setLayout(m_main_layout); +} + +void NetPlayDialog::CreateChatLayout() +{ + m_chat_box = new QGroupBox(tr("Chat")); + m_chat_edit = new QTextBrowser; + m_chat_type_edit = new QLineEdit; + m_chat_send_button = new QPushButton(tr("Send")); + + m_chat_send_button->setDefault(false); + m_chat_send_button->setAutoDefault(false); + + m_chat_edit->setReadOnly(true); + + auto* layout = new QGridLayout; + + layout->addWidget(m_chat_edit, 0, 0, 1, -1); + layout->addWidget(m_chat_type_edit, 1, 0); + layout->addWidget(m_chat_send_button, 1, 1); + + m_chat_box->setLayout(layout); +} + +void NetPlayDialog::CreatePlayersLayout() +{ + m_players_box = new QGroupBox(tr("Players")); + m_room_box = new QComboBox; + m_hostcode_label = new QLabel; + m_hostcode_action_button = new QPushButton(tr("Copy")); + m_players_list = new QTableWidget; + m_kick_button = new QPushButton(tr("Kick Player")); + m_assign_ports_button = new QPushButton(tr("Assign Controller Ports")); + + m_players_list->setColumnCount(5); + m_players_list->verticalHeader()->hide(); + m_players_list->setSelectionBehavior(QAbstractItemView::SelectRows); + m_players_list->horizontalHeader()->setStretchLastSection(true); + + for (int i = 0; i < 4; i++) + m_players_list->horizontalHeader()->setSectionResizeMode(i, QHeaderView::ResizeToContents); + + auto* layout = new QGridLayout; + + layout->addWidget(m_room_box, 0, 0); + layout->addWidget(m_hostcode_label, 0, 1); + layout->addWidget(m_hostcode_action_button, 0, 2); + layout->addWidget(m_players_list, 1, 0, 1, -1); + layout->addWidget(m_kick_button, 2, 0, 1, -1); + layout->addWidget(m_assign_ports_button, 3, 0, 1, -1); + + m_players_box->setLayout(layout); +} + +void NetPlayDialog::ConnectWidgets() +{ + // Players + connect(m_room_box, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, + &NetPlayDialog::UpdateGUI); + connect(m_hostcode_action_button, &QPushButton::clicked, [this] { + if (m_is_copy_button_retry && m_room_box->currentIndex() == 0) + g_TraversalClient->ReconnectToServer(); + else + QApplication::clipboard()->setText(m_hostcode_label->text()); + }); + connect(m_players_list, &QTableWidget::itemSelectionChanged, [this] { + int row = m_players_list->currentRow(); + m_kick_button->setEnabled(row > 0 && + !m_players_list->currentItem()->data(Qt::UserRole).isNull()); + }); + connect(m_kick_button, &QPushButton::clicked, [this] { + auto id = m_players_list->currentItem()->data(Qt::UserRole).toInt(); + Settings::Instance().GetNetPlayServer()->KickPlayer(id); + }); + connect(m_assign_ports_button, &QPushButton::clicked, [this] { + m_pad_mapping->exec(); + + Settings::Instance().GetNetPlayServer()->SetPadMapping(m_pad_mapping->GetGCPadArray()); + Settings::Instance().GetNetPlayServer()->SetWiimoteMapping(m_pad_mapping->GetWiimoteArray()); + }); + + // Chat + connect(m_chat_send_button, &QPushButton::clicked, this, &NetPlayDialog::OnChat); + connect(m_chat_type_edit, &QLineEdit::returnPressed, this, &NetPlayDialog::OnChat); + + // Other + connect(m_buffer_size_box, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), + [this](int value) { + if (value == m_buffer_size) + return; + + if (Settings::Instance().GetNetPlayServer() != nullptr) + Settings::Instance().GetNetPlayServer()->AdjustPadBufferSize(value); + }); + + connect(m_start_button, &QPushButton::clicked, this, &NetPlayDialog::OnStart); + connect(m_quit_button, &QPushButton::clicked, this, &NetPlayDialog::reject); + + connect(m_game_button, &QPushButton::clicked, [this] { + GameListDialog gld(this); + if (gld.exec() == QDialog::Accepted) + { + auto unique_id = gld.GetSelectedUniqueID(); + Settings::Instance().GetNetPlayServer()->ChangeGame(unique_id.toStdString()); + } + }); + + connect(&Settings::Instance(), &Settings::EmulationStateChanged, this, [=](Core::State state) { + if (isVisible()) + { + GameStatusChanged(state != Core::State::Uninitialized); + if (state == Core::State::Uninitialized) + DisplayMessage(tr("Stopped game"), "red"); + } + }); +} + +void NetPlayDialog::OnChat() +{ + QueueOnObject(this, [this] { + auto msg = m_chat_type_edit->text().toStdString(); + Settings::Instance().GetNetPlayClient()->SendChatMessage(msg); + m_chat_type_edit->clear(); + + DisplayMessage(QStringLiteral("%1: %2").arg(QString::fromStdString(m_nickname).toHtmlEscaped(), + QString::fromStdString(msg).toHtmlEscaped()), + "blue"); + }); +} + +void NetPlayDialog::OnStart() +{ + if (!Settings::Instance().GetNetPlayClient()->DoAllPlayersHaveGame()) + { + if (QMessageBox::question(this, tr("Warning"), + tr("Not all players have the game. Do you really want to start?")) == + QMessageBox::No) + return; + } + + NetSettings settings; + + // Copy all relevant settings + SConfig& instance = SConfig::GetInstance(); + settings.m_CPUthread = instance.bCPUThread; + settings.m_CPUcore = instance.cpu_core; + settings.m_EnableCheats = instance.bEnableCheats; + settings.m_SelectedLanguage = instance.SelectedLanguage; + settings.m_OverrideGCLanguage = instance.bOverrideGCLanguage; + settings.m_ProgressiveScan = Config::Get(Config::SYSCONF_PROGRESSIVE_SCAN); + settings.m_PAL60 = Config::Get(Config::SYSCONF_PAL60); + settings.m_DSPHLE = instance.bDSPHLE; + settings.m_DSPEnableJIT = instance.m_DSPEnableJIT; + settings.m_WriteToMemcard = m_save_sd_box->isChecked(); + settings.m_CopyWiiSave = m_load_wii_box->isChecked(); + settings.m_OCEnable = instance.m_OCEnable; + settings.m_OCFactor = instance.m_OCFactor; + settings.m_ReducePollingRate = m_reduce_polling_rate_box->isChecked(); + settings.m_EXIDevice[0] = instance.m_EXIDevice[0]; + settings.m_EXIDevice[1] = instance.m_EXIDevice[1]; + + Settings::Instance().GetNetPlayServer()->SetNetSettings(settings); + Settings::Instance().GetNetPlayServer()->StartGame(); +} + +void NetPlayDialog::reject() +{ + if (QMessageBox::question(this, tr("Confirmation"), + tr("Are you sure you want to quit NetPlay?")) == QMessageBox::Yes) + { + QDialog::reject(); + } +} + +void NetPlayDialog::show(std::string nickname, bool use_traversal) +{ + m_nickname = nickname; + m_use_traversal = use_traversal; + m_buffer_size = 0; + + m_room_box->clear(); + m_chat_edit->clear(); + m_chat_type_edit->clear(); + + bool is_hosting = Settings::Instance().GetNetPlayServer() != nullptr; + + if (is_hosting) + { + if (use_traversal) + m_room_box->addItem(tr("Room ID")); + + for (const auto& iface : Settings::Instance().GetNetPlayServer()->GetInterfaceSet()) + { + const auto interface = QString::fromStdString(iface); + m_room_box->addItem(iface == "!local!" ? tr("Local") : interface, interface); + } + } + + m_start_button->setHidden(!is_hosting); + m_save_sd_box->setHidden(!is_hosting); + m_load_wii_box->setHidden(!is_hosting); + m_reduce_polling_rate_box->setHidden(!is_hosting); + m_buffer_size_box->setHidden(!is_hosting); + m_buffer_label->setHidden(!is_hosting); + m_kick_button->setHidden(!is_hosting); + m_assign_ports_button->setHidden(!is_hosting); + m_md5_button->setHidden(!is_hosting); + m_room_box->setHidden(!is_hosting); + m_hostcode_label->setHidden(!is_hosting); + m_hostcode_action_button->setHidden(!is_hosting); + m_game_button->setEnabled(is_hosting); + m_kick_button->setEnabled(false); + + QDialog::show(); + UpdateGUI(); +} + +void NetPlayDialog::UpdateGUI() +{ + auto* client = Settings::Instance().GetNetPlayClient(); + + // Update Player List + const auto players = client->GetPlayers(); + const auto player_count = static_cast<int>(players.size()); + + int selection_pid = m_players_list->currentItem() ? + m_players_list->currentItem()->data(Qt::UserRole).toInt() : + -1; + + m_players_list->clear(); + m_players_list->setHorizontalHeaderLabels( + {tr("Player"), tr("Game Status"), tr("Ping"), tr("Mapping"), tr("Revision")}); + m_players_list->setRowCount(player_count); + + const auto get_mapping_string = [](const Player* player, const PadMappingArray& array) { + std::string str; + for (size_t i = 0; i < array.size(); i++) + { + if (player->pid == array[i]) + str += std::to_string(i + 1); + else + str += '-'; + } + + return '|' + str + '|'; + }; + + static const std::map<PlayerGameStatus, QString> player_status{ + {PlayerGameStatus::Ok, tr("OK")}, {PlayerGameStatus::NotFound, tr("Not Found")}}; + + for (int i = 0; i < player_count; i++) + { + const auto* p = players[i]; + + auto* name_item = new QTableWidgetItem(QString::fromStdString(p->name)); + auto* status_item = new QTableWidgetItem(player_status.count(p->game_status) ? + player_status.at(p->game_status) : + QStringLiteral("?")); + auto* ping_item = new QTableWidgetItem(QStringLiteral("%1 ms").arg(p->ping)); + auto* mapping_item = new QTableWidgetItem( + QString::fromStdString(get_mapping_string(p, client->GetPadMapping()) + + get_mapping_string(p, client->GetWiimoteMapping()))); + auto* revision_item = new QTableWidgetItem(QString::fromStdString(p->revision)); + + for (auto* item : {name_item, status_item, ping_item, mapping_item, revision_item}) + { + item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable); + item->setData(Qt::UserRole, static_cast<int>(p->pid)); + } + + m_players_list->setItem(i, 0, name_item); + m_players_list->setItem(i, 1, status_item); + m_players_list->setItem(i, 2, ping_item); + m_players_list->setItem(i, 3, mapping_item); + m_players_list->setItem(i, 4, revision_item); + + if (p->pid == selection_pid) + m_players_list->selectRow(i); + } + + // Update Room ID / IP label + if (m_use_traversal && m_room_box->currentIndex() == 0) + { + switch (g_TraversalClient->GetState()) + { + case TraversalClient::Connecting: + m_hostcode_label->setText(tr("...")); + m_hostcode_action_button->setEnabled(false); + break; + case TraversalClient::Connected: + { + const auto host_id = g_TraversalClient->GetHostID(); + m_hostcode_label->setText( + QString::fromStdString(std::string(host_id.begin(), host_id.end()))); + m_hostcode_action_button->setEnabled(true); + m_hostcode_action_button->setText(tr("Copy")); + m_is_copy_button_retry = false; + break; + } + case TraversalClient::Failure: + m_hostcode_label->setText(tr("Error")); + m_hostcode_action_button->setText(tr("Retry")); + m_hostcode_action_button->setEnabled(true); + m_is_copy_button_retry = true; + break; + } + } + else if (Settings::Instance().GetNetPlayServer()) + { + m_hostcode_label->setText( + QString::fromStdString(Settings::Instance().GetNetPlayServer()->GetInterfaceHost( + m_room_box->currentData().toString().toStdString()))); + m_hostcode_action_button->setText(tr("Copy")); + m_hostcode_action_button->setEnabled(true); + } +} + +// NetPlayUI methods + +void NetPlayDialog::BootGame(const std::string& filename) +{ + m_got_stop_request = false; + emit Boot(QString::fromStdString(filename)); +} + +void NetPlayDialog::StopGame() +{ + if (m_got_stop_request) + return; + + m_got_stop_request = true; + emit Stop(); +} + +void NetPlayDialog::Update() +{ + QueueOnObject(this, &NetPlayDialog::UpdateGUI); +} + +void NetPlayDialog::DisplayMessage(const QString& msg, const std::string& color, int duration) +{ + QueueOnObject(m_chat_edit, [this, color, msg] { + m_chat_edit->append( + QStringLiteral("<font color='%1'>%2</font>").arg(QString::fromStdString(color), msg)); + }); + + if (g_ActiveConfig.bShowNetPlayMessages && Core::IsRunning()) + { + u32 osd_color; + + // Convert the color string to a OSD color + if (color == "red") + osd_color = OSD::Color::RED; + else if (color == "cyan") + osd_color = OSD::Color::CYAN; + else if (color == "green") + osd_color = OSD::Color::GREEN; + else + osd_color = OSD::Color::YELLOW; + + OSD::AddTypedMessage(OSD::MessageType::NetPlayBuffer, msg.toStdString(), OSD::Duration::NORMAL, + osd_color); + } +} + +void NetPlayDialog::AppendChat(const std::string& msg) +{ + DisplayMessage(QString::fromStdString(msg).toHtmlEscaped(), ""); +} + +void NetPlayDialog::OnMsgChangeGame(const std::string& title) +{ + QString qtitle = QString::fromStdString(title); + QueueOnObject(this, [this, qtitle, title] { + m_game_button->setText(qtitle); + m_current_game = title; + }); + DisplayMessage(tr("Game changed to \"%1\"").arg(qtitle), "magenta"); +} + +void NetPlayDialog::GameStatusChanged(bool running) +{ + if (!running && !m_got_stop_request) + Settings::Instance().GetNetPlayClient()->RequestStopGame(); + + QueueOnObject(this, [this, running] { + if (Settings::Instance().GetNetPlayServer() != nullptr) + { + m_start_button->setEnabled(!running); + m_game_button->setEnabled(!running); + m_load_wii_box->setEnabled(!running); + m_save_sd_box->setEnabled(!running); + m_assign_ports_button->setEnabled(!running); + m_reduce_polling_rate_box->setEnabled(!running); + } + + m_record_input_box->setEnabled(!running); + }); +} + +void NetPlayDialog::OnMsgStartGame() +{ + DisplayMessage(tr("Started game"), "green"); + + QueueOnObject(this, [this] { + Settings::Instance().GetNetPlayClient()->StartGame(FindGame(m_current_game)); + }); +} + +void NetPlayDialog::OnMsgStopGame() +{ +} + +void NetPlayDialog::OnPadBufferChanged(u32 buffer) +{ + QueueOnObject(this, [this, buffer] { m_buffer_size_box->setValue(buffer); }); + DisplayMessage(tr("Buffer size changed to %1").arg(buffer), ""); + + m_buffer_size = static_cast<int>(buffer); +} + +void NetPlayDialog::OnDesync(u32 frame, const std::string& player) +{ + DisplayMessage(tr("Possible desync detected: %1 might have desynced at frame %2") + .arg(QString::fromStdString(player), QString::number(frame)), + "red", OSD::Duration::VERY_LONG); +} + +void NetPlayDialog::OnConnectionLost() +{ + DisplayMessage(tr("Lost connection to NetPlay server..."), "red"); +} + +void NetPlayDialog::OnConnectionError(const std::string& message) +{ + QueueOnObject(this, [this, message] { + QMessageBox::critical(this, tr("Error"), + tr("Failed to connect to server: %1").arg(tr(message.c_str()))); + }); +} + +void NetPlayDialog::OnTraversalError(TraversalClient::FailureReason error) +{ + QueueOnObject(this, [this, error] { + switch (error) + { + case TraversalClient::FailureReason::BadHost: + QMessageBox::critical(this, tr("Traversal Error"), tr("Couldn't look up central server")); + QDialog::reject(); + break; + case TraversalClient::FailureReason::VersionTooOld: + QMessageBox::critical(this, tr("Traversal Error"), + tr("Dolphin is too old for traversal server")); + QDialog::reject(); + break; + case TraversalClient::FailureReason::ServerForgotAboutUs: + case TraversalClient::FailureReason::SocketSendError: + case TraversalClient::FailureReason::ResendTimeout: + UpdateGUI(); + break; + } + }); +} + +bool NetPlayDialog::IsRecording() +{ + std::optional<bool> is_recording = RunOnObject(m_record_input_box, &QCheckBox::isChecked); + if (is_recording) + return *is_recording; + return false; +} + +std::string NetPlayDialog::FindGame(const std::string& game) +{ + std::optional<std::string> path = RunOnObject(this, [this, game] { + for (int i = 0; i < m_game_list_model->rowCount(QModelIndex()); i++) + { + if (m_game_list_model->GetUniqueIdentifier(i).toStdString() == game) + return m_game_list_model->GetPath(i).toStdString(); + } + return std::string(""); + }); + if (path) + return *path; + return std::string(""); +} + +void NetPlayDialog::ShowMD5Dialog(const std::string& file_identifier) +{ + QueueOnObject(this, [this, file_identifier] { + m_md5_button->setEnabled(false); + + if (m_md5_dialog->isVisible()) + m_md5_dialog->close(); + + m_md5_dialog->show(QString::fromStdString(file_identifier)); + }); +} + +void NetPlayDialog::SetMD5Progress(int pid, int progress) +{ + QueueOnObject(this, [this, pid, progress] { + if (m_md5_dialog->isVisible()) + m_md5_dialog->SetProgress(pid, progress); + }); +} + +void NetPlayDialog::SetMD5Result(int pid, const std::string& result) +{ + QueueOnObject(this, [this, pid, result] { + m_md5_dialog->SetResult(pid, result); + m_md5_button->setEnabled(true); + }); +} + +void NetPlayDialog::AbortMD5() +{ + QueueOnObject(this, [this] { + m_md5_dialog->close(); + m_md5_button->setEnabled(true); + }); +} |
