Merge branch 'dolphin-mpn' into master

This commit is contained in:
Nayla 2023-04-05 04:10:05 -04:00 committed by GitHub
commit f3669dbbd4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
903 changed files with 4243 additions and 386 deletions

View file

@ -9,13 +9,19 @@
#include <QComboBox>
#include <QDialogButtonBox>
#include <QGridLayout>
#include <QGroupBox>
#include <QHeaderView>
#include <QInputDialog>
#include <QLabel>
#include <QLineEdit>
#include <QListWidget>
#include <QPushButton>
#include <QRadioButton>
#include <QSignalBlocker>
#include <QSpinBox>
#include <QTabWidget>
#include <QTableWidget>
#include <QTableWidgetItem>
#include "Core/Config/NetplaySettings.h"
#include "Core/NetPlayProto.h"
@ -25,6 +31,8 @@
#include "DolphinQt/QtUtils/UTF8CodePointCountValidator.h"
#include "DolphinQt/Settings.h"
#include "Common/Version.h"
#include "DolphinQt/NetPlay/NetPlayBrowser.h"
#include "UICommon/GameFile.h"
#include "UICommon/NetPlayIndex.h"
@ -37,16 +45,11 @@ NetPlaySetupDialog::NetPlaySetupDialog(const GameListModel& game_list_model, QWi
CreateMainLayout();
bool use_index = Config::Get(Config::NETPLAY_USE_INDEX);
std::string index_region = Config::Get(Config::NETPLAY_INDEX_REGION);
std::string index_name = Config::Get(Config::NETPLAY_INDEX_NAME);
std::string index_password = Config::Get(Config::NETPLAY_INDEX_PASSWORD);
std::string nickname = Config::Get(Config::NETPLAY_NICKNAME);
std::string traversal_choice = Config::Get(Config::NETPLAY_TRAVERSAL_CHOICE);
int connect_port = Config::Get(Config::NETPLAY_CONNECT_PORT);
int host_port = Config::Get(Config::NETPLAY_HOST_PORT);
int host_listen_port = Config::Get(Config::NETPLAY_LISTEN_PORT);
bool enable_chunked_upload_limit = Config::Get(Config::NETPLAY_ENABLE_CHUNKED_UPLOAD_LIMIT);
u32 chunked_upload_limit = Config::Get(Config::NETPLAY_CHUNKED_UPLOAD_LIMIT);
#ifdef USE_UPNP
bool use_upnp = Config::Get(Config::NETPLAY_USE_UPNP);
@ -58,28 +61,41 @@ NetPlaySetupDialog::NetPlaySetupDialog(const GameListModel& game_list_model, QWi
m_connect_port_box->setValue(connect_port);
m_host_port_box->setValue(host_port);
m_host_force_port_box->setValue(host_listen_port);
m_host_force_port_box->setEnabled(false);
m_host_server_browser->setChecked(use_index);
m_host_server_region->setEnabled(use_index);
m_host_server_region->setCurrentIndex(
m_host_server_region->findData(QString::fromStdString(index_region)));
m_host_server_name->setEnabled(use_index);
m_host_server_name->setText(QString::fromStdString(index_name));
m_host_server_password->setEnabled(use_index);
m_host_server_password->setText(QString::fromStdString(index_password));
// Browser Stuff
const auto& settings = Settings::Instance().GetQSettings();
m_host_chunked_upload_limit_check->setChecked(enable_chunked_upload_limit);
m_host_chunked_upload_limit_box->setValue(chunked_upload_limit);
m_host_chunked_upload_limit_box->setEnabled(enable_chunked_upload_limit);
const QByteArray geometry =
settings.value(QStringLiteral("netplaybrowser/geometry")).toByteArray();
if (!geometry.isEmpty())
restoreGeometry(geometry);
const QString region = settings.value(QStringLiteral("netplaybrowser/region")).toString();
const bool valid_region = m_region_combo->findText(region) != -1;
if (valid_region)
m_region_combo->setCurrentText(region);
m_edit_name->setText(settings.value(QStringLiteral("netplaybrowser/name")).toString());
const QString visibility = settings.value(QStringLiteral("netplaybrowser/visibility")).toString();
if (visibility == QStringLiteral("public"))
m_radio_public->setChecked(true);
else if (visibility == QStringLiteral("private"))
m_radio_private->setChecked(true);
m_check_hide_ingame->setChecked(true);
OnConnectionTypeChanged(m_connection_type->currentIndex());
ConnectWidgets();
m_refresh_run.Set(true);
m_refresh_thread = std::thread([this] { RefreshLoopBrowser(); });
UpdateListBrowser();
RefreshBrowser();
}
void NetPlaySetupDialog::CreateMainLayout()
@ -88,7 +104,10 @@ void NetPlaySetupDialog::CreateMainLayout()
m_button_box = new QDialogButtonBox(QDialogButtonBox::Cancel);
m_nickname_edit = new QLineEdit;
m_connection_type = new QComboBox;
m_connection_type->setCurrentIndex(1);
m_reset_traversal_button = new NonDefaultQPushButton(tr("Reset Traversal Settings"));
m_tab_widget = new QTabWidget;
m_nickname_edit->setValidator(
@ -98,6 +117,50 @@ void NetPlaySetupDialog::CreateMainLayout()
auto* connection_widget = new QWidget;
auto* connection_layout = new QGridLayout;
// NetPlay Browser
auto* browser_widget = new QWidget;
auto* layout = new QVBoxLayout;
m_table_widget = new QTableWidget;
m_table_widget->setTabKeyNavigation(false);
m_table_widget->setSelectionBehavior(QAbstractItemView::SelectRows);
m_table_widget->setSelectionMode(QAbstractItemView::SingleSelection);
m_table_widget->setWordWrap(false);
m_region_combo = new QComboBox;
m_region_combo->addItem(tr("Any Region"));
for (const auto& region : NetPlayIndex::GetRegions())
{
m_region_combo->addItem(
tr("%1 (%2)").arg(tr(region.second.c_str())).arg(QString::fromStdString(region.first)),
QString::fromStdString(region.first));
}
m_region_combo->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Preferred);
m_status_label = new QLabel;
m_b_button_box = new QDialogButtonBox;
m_button_refresh = new QPushButton(tr("Refresh"));
m_edit_name = new QLineEdit;
m_check_hide_ingame = new QCheckBox(tr("Hide In-Game Sessions"));
m_radio_all = new QRadioButton(tr("Private and Public"));
m_radio_private = new QRadioButton(tr("Private"));
m_radio_public = new QRadioButton(tr("Public"));
m_radio_all->setChecked(true);
layout->addWidget(m_table_widget);
layout->addWidget(m_status_label);
layout->addWidget(m_b_button_box);
m_b_button_box->addButton(m_button_refresh, QDialogButtonBox::ResetRole);
browser_widget->setLayout(layout);
m_ip_label = new QLabel;
m_ip_edit = new QLineEdit;
m_connect_port_label = new QLabel(tr("Port:"));
@ -113,7 +176,7 @@ void NetPlaySetupDialog::CreateMainLayout()
connection_layout->addWidget(
new QLabel(
tr("ALERT:\n\n"
"All players must use the same Dolphin version.\n"
"All players must use the same Dolphin version, unless Orange Dolphin is used to host\n"
"If enabled, SD cards must be identical between players.\n"
"If DSP LLE is used, DSP ROMs must be identical between players.\n"
"If a game is hanging on boot, it may not support Dual Core Netplay."
@ -130,16 +193,10 @@ void NetPlaySetupDialog::CreateMainLayout()
// Host widget
auto* host_widget = new QWidget;
auto* host_layout = new QGridLayout;
m_host_port_label = new QLabel(tr("Port:"));
m_host_port_box = new QSpinBox;
m_host_force_port_check = new QCheckBox(tr("Force Listen Port:"));
m_host_force_port_box = new QSpinBox;
m_host_chunked_upload_limit_check = new QCheckBox(tr("Limit Chunked Upload Speed:"));
m_host_chunked_upload_limit_box = new QSpinBox;
m_host_server_browser = new QCheckBox(tr("Show in server browser"));
m_host_server_name = new QLineEdit;
m_host_server_password = new QLineEdit;
m_host_server_region = new QComboBox;
#ifdef USE_UPNP
m_host_upnp = new QCheckBox(tr("Forward port (UPnP)"));
@ -148,41 +205,17 @@ void NetPlaySetupDialog::CreateMainLayout()
m_host_button = new NonDefaultQPushButton(tr("Host"));
m_host_port_box->setMaximum(65535);
m_host_force_port_box->setMaximum(65535);
m_host_chunked_upload_limit_box->setRange(1, 1000000);
m_host_chunked_upload_limit_box->setSingleStep(100);
m_host_chunked_upload_limit_box->setSuffix(QStringLiteral(" kbps"));
m_host_chunked_upload_limit_check->setToolTip(tr(
"This will limit the speed of chunked uploading per client, which is used for save sync."));
m_host_server_name->setToolTip(tr("Name of your session shown in the server browser"));
m_host_server_name->setPlaceholderText(tr("Name"));
m_host_server_password->setToolTip(tr("Password for joining your game (leave empty for none)"));
m_host_server_password->setPlaceholderText(tr("Password"));
for (const auto& region : NetPlayIndex::GetRegions())
{
m_host_server_region->addItem(
tr("%1 (%2)").arg(tr(region.second.c_str())).arg(QString::fromStdString(region.first)),
QString::fromStdString(region.first));
}
host_layout->addWidget(m_host_port_label, 0, 0);
host_layout->addWidget(m_host_port_box, 0, 1);
host_layout->addWidget(m_host_port_box, 0, 0, Qt::AlignLeft);
#ifdef USE_UPNP
host_layout->addWidget(m_host_upnp, 0, 2);
host_layout->addWidget(m_host_upnp, 0, 5, Qt::AlignRight);
#endif
host_layout->addWidget(m_host_server_browser, 1, 0);
host_layout->addWidget(m_host_server_region, 1, 1);
host_layout->addWidget(m_host_server_name, 1, 2);
host_layout->addWidget(m_host_server_password, 1, 3);
host_layout->addWidget(m_host_games, 2, 0, 1, -1);
host_layout->addWidget(m_host_force_port_check, 3, 0);
host_layout->addWidget(m_host_force_port_box, 3, 1, Qt::AlignLeft);
host_layout->addWidget(m_host_chunked_upload_limit_check, 4, 0);
host_layout->addWidget(m_host_chunked_upload_limit_box, 4, 1, Qt::AlignLeft);
host_layout->addWidget(m_host_button, 4, 3, 2, 1, Qt::AlignRight);
host_layout->addWidget(m_host_button, 4, 5, Qt::AlignRight);
host_widget->setLayout(host_layout);
@ -200,10 +233,21 @@ void NetPlaySetupDialog::CreateMainLayout()
// Tabs
m_tab_widget->addTab(connection_widget, tr("Connect"));
m_tab_widget->addTab(host_widget, tr("Host"));
m_tab_widget->addTab(browser_widget, tr("Browser"));
setLayout(m_main_layout);
}
NetPlaySetupDialog::~NetPlaySetupDialog()
{
m_refresh_run.Set(false);
m_refresh_event.Set();
if (m_refresh_thread.joinable())
m_refresh_thread.join();
SaveSettings();
}
void NetPlaySetupDialog::ConnectWidgets()
{
connect(m_connection_type, qOverload<int>(&QComboBox::currentIndexChanged), this,
@ -222,23 +266,12 @@ void NetPlaySetupDialog::ConnectWidgets()
m_host_games->item(index)->text());
});
// refresh browser on tab changed
connect(m_tab_widget, &QTabWidget::currentChanged, this, &NetPlaySetupDialog::RefreshBrowser);
connect(m_host_games, &QListWidget::itemDoubleClicked, this, &NetPlaySetupDialog::accept);
connect(m_host_force_port_check, &QCheckBox::toggled,
[this](bool value) { m_host_force_port_box->setEnabled(value); });
connect(m_host_chunked_upload_limit_check, &QCheckBox::toggled, this, [this](bool value) {
m_host_chunked_upload_limit_box->setEnabled(value);
SaveSettings();
});
connect(m_host_chunked_upload_limit_box, qOverload<int>(&QSpinBox::valueChanged), this,
&NetPlaySetupDialog::SaveSettings);
connect(m_host_server_browser, &QCheckBox::toggled, this, &NetPlaySetupDialog::SaveSettings);
connect(m_host_server_name, &QLineEdit::textChanged, this, &NetPlaySetupDialog::SaveSettings);
connect(m_host_server_password, &QLineEdit::textChanged, this, &NetPlaySetupDialog::SaveSettings);
connect(m_host_server_region,
static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,
&NetPlaySetupDialog::SaveSettings);
#ifdef USE_UPNP
connect(m_host_upnp, &QCheckBox::stateChanged, this, &NetPlaySetupDialog::SaveSettings);
@ -249,11 +282,26 @@ void NetPlaySetupDialog::ConnectWidgets()
connect(m_button_box, &QDialogButtonBox::rejected, this, &QDialog::reject);
connect(m_reset_traversal_button, &QPushButton::clicked, this,
&NetPlaySetupDialog::ResetTraversalHost);
connect(m_host_server_browser, &QCheckBox::toggled, this, [this](bool value) {
m_host_server_region->setEnabled(value);
m_host_server_name->setEnabled(value);
m_host_server_password->setEnabled(value);
});
// Browser Stuff
connect(m_region_combo, qOverload<int>(&QComboBox::currentIndexChanged), this,
&NetPlaySetupDialog::RefreshBrowser);
connect(m_button_refresh, &QPushButton::clicked, this, &NetPlaySetupDialog::RefreshBrowser);
connect(m_radio_all, &QRadioButton::toggled, this, &NetPlaySetupDialog::RefreshBrowser);
connect(m_radio_private, &QRadioButton::toggled, this, &NetPlaySetupDialog::RefreshBrowser);
connect(m_check_hide_ingame, &QRadioButton::toggled, this, &NetPlaySetupDialog::RefreshBrowser);
connect(m_edit_name, &QLineEdit::textChanged, this, &NetPlaySetupDialog::RefreshBrowser);
connect(m_table_widget, &QTableWidget::itemDoubleClicked, this,
&NetPlaySetupDialog::acceptBrowser);
connect(this, &NetPlaySetupDialog::UpdateStatusRequestedBrowser, this,
&NetPlaySetupDialog::OnUpdateStatusRequestedBrowser, Qt::QueuedConnection);
connect(this, &NetPlaySetupDialog::UpdateListRequestedBrowser, this,
&NetPlaySetupDialog::OnUpdateListRequestedBrowser, Qt::QueuedConnection);
}
void NetPlaySetupDialog::SaveSettings()
@ -271,21 +319,27 @@ void NetPlaySetupDialog::SaveSettings()
Config::SetBaseOrCurrent(Config::NETPLAY_USE_UPNP, m_host_upnp->isChecked());
#endif
if (m_host_force_port_check->isChecked())
Config::SetBaseOrCurrent(Config::NETPLAY_LISTEN_PORT,
static_cast<u16>(m_host_force_port_box->value()));
Config::SetBaseOrCurrent(Config::NETPLAY_ENABLE_CHUNKED_UPLOAD_LIMIT,
m_host_chunked_upload_limit_check->isChecked());
Config::SetBaseOrCurrent(Config::NETPLAY_CHUNKED_UPLOAD_LIMIT,
m_host_chunked_upload_limit_box->value());
Config::SetBaseOrCurrent(Config::NETPLAY_USE_INDEX, m_host_server_browser->isChecked());
Config::SetBaseOrCurrent(Config::NETPLAY_INDEX_REGION,
m_host_server_region->currentData().toString().toStdString());
Config::SetBaseOrCurrent(Config::NETPLAY_INDEX_REGION, "NA");
Config::SetBaseOrCurrent(Config::NETPLAY_INDEX_NAME, m_host_server_name->text().toStdString());
Config::SetBaseOrCurrent(Config::NETPLAY_INDEX_PASSWORD,
m_host_server_password->text().toStdString());
Config::SetBaseOrCurrent(Config::NETPLAY_INDEX_PASSWORD, "");
// Browser Stuff
auto& settings = Settings::Instance().GetQSettings();
settings.setValue(QStringLiteral("netplaybrowser/geometry"), saveGeometry());
settings.setValue(QStringLiteral("netplaybrowser/region"), m_region_combo->currentText());
settings.setValue(QStringLiteral("netplaybrowser/name"), m_edit_name->text());
QString visibility(QStringLiteral("all"));
if (m_radio_public->isChecked())
visibility = QStringLiteral("public");
else if (m_radio_private->isChecked())
visibility = QStringLiteral("private");
settings.setValue(QStringLiteral("netplaybrowser/visibility"), visibility);
settings.setValue(QStringLiteral("netplaybrowser/hide_incompatible"), true);
settings.setValue(QStringLiteral("netplaybrowser/hide_ingame"), m_check_hide_ingame->isChecked());
}
void NetPlaySetupDialog::OnConnectionTypeChanged(int index)
@ -293,14 +347,10 @@ void NetPlaySetupDialog::OnConnectionTypeChanged(int index)
m_connect_port_box->setHidden(index != 0);
m_connect_port_label->setHidden(index != 0);
m_host_port_label->setHidden(index != 0);
m_host_port_box->setHidden(index != 0);
#ifdef USE_UPNP
m_host_upnp->setHidden(index != 0);
#endif
m_host_force_port_check->setHidden(index == 0);
m_host_force_port_box->setHidden(index == 0);
m_reset_traversal_button->setHidden(index == 0);
std::string address =
@ -315,6 +365,16 @@ void NetPlaySetupDialog::OnConnectionTypeChanged(int index)
void NetPlaySetupDialog::show()
{
// Here i'm setting the lobby name if it's empty to make
// NetPlay sessions start more easily for first time players
if (m_host_server_name->text().isEmpty())
{
std::string nickname = Config::Get(Config::NETPLAY_NICKNAME);
m_host_server_name->setText(QString::fromStdString(nickname));
}
m_connection_type->setCurrentIndex(1);
m_tab_widget->setCurrentIndex(2); // start on browser
PopulateGameList();
QDialog::show();
}
@ -335,20 +395,6 @@ void NetPlaySetupDialog::accept()
return;
}
if (m_host_server_browser->isChecked() && m_host_server_name->text().isEmpty())
{
ModalMessageBox::critical(this, tr("Error"), tr("You must provide a name for your session!"));
return;
}
if (m_host_server_browser->isChecked() &&
m_host_server_region->currentData().toString().isEmpty())
{
ModalMessageBox::critical(this, tr("Error"),
tr("You must provide a region for your session!"));
return;
}
emit Host(*items[0]->data(Qt::UserRole).value<std::shared_ptr<const UICommon::GameFile>>());
}
}
@ -391,3 +437,176 @@ void NetPlaySetupDialog::ResetTraversalHost()
.arg(QString::fromStdString(Config::NETPLAY_TRAVERSAL_SERVER.GetDefaultValue()),
QString::number(Config::NETPLAY_TRAVERSAL_PORT.GetDefaultValue())));
}
void NetPlaySetupDialog::RefreshBrowser()
{
std::map<std::string, std::string> filters;
if (!m_edit_name->text().isEmpty())
filters["name"] = m_edit_name->text().toStdString();
if (true)
filters["version"] = Common::GetScmDescStr();
if (!m_radio_all->isChecked())
filters["password"] = std::to_string(m_radio_private->isChecked());
if (m_region_combo->currentIndex() != 0)
filters["region"] = m_region_combo->currentData().toString().toStdString();
if (m_check_hide_ingame->isChecked())
filters["in_game"] = "0";
std::unique_lock<std::mutex> lock(m_refresh_filters_mutex);
m_refresh_filters = std::move(filters);
m_refresh_event.Set();
SaveSettings();
}
void NetPlaySetupDialog::RefreshLoopBrowser()
{
while (m_refresh_run.IsSet())
{
m_refresh_event.Wait();
std::unique_lock<std::mutex> lock(m_refresh_filters_mutex);
if (m_refresh_filters)
{
auto filters = std::move(*m_refresh_filters);
m_refresh_filters.reset();
lock.unlock();
emit UpdateStatusRequestedBrowser(tr("Refreshing..."));
NetPlayIndex client;
auto entries = client.List(filters);
if (entries)
{
emit UpdateListRequestedBrowser(std::move(*entries));
}
else
{
emit UpdateStatusRequestedBrowser(tr("Error obtaining session list: %1")
.arg(QString::fromStdString(client.GetLastError())));
}
}
}
}
void NetPlaySetupDialog::UpdateListBrowser()
{
const int session_count = static_cast<int>(m_sessions.size());
m_table_widget->clear();
m_table_widget->setColumnCount(4);
m_table_widget->setHorizontalHeaderLabels({
tr("Name"),
tr("Game"),
tr("Players"),
tr("In-Game"),
});
auto* hor_header = m_table_widget->horizontalHeader();
hor_header->setSectionResizeMode(0, QHeaderView::Stretch);
hor_header->setSectionResizeMode(0, QHeaderView::Stretch);
hor_header->setSectionResizeMode(1, QHeaderView::Stretch);
hor_header->setSectionResizeMode(2, QHeaderView::ResizeToContents);
hor_header->setSectionResizeMode(3, QHeaderView::ResizeToContents);
hor_header->setHighlightSections(false);
m_table_widget->setRowCount(session_count);
for (int i = 0; i < session_count; i++)
{
const auto& entry = m_sessions[i];
auto* name = new QTableWidgetItem(QString::fromStdString(entry.name));
auto* in_game = new QTableWidgetItem(entry.in_game ? tr("Yes") : tr("No"));
auto* game_id = new QTableWidgetItem(QString::fromStdString(entry.game_id));
auto* player_count = new QTableWidgetItem(QStringLiteral("%1").arg(entry.player_count));
const bool enabled = Common::GetScmDescStr() == entry.version;
for (const auto& item : {name, game_id, player_count, in_game})
item->setFlags(enabled ? Qt::ItemIsEnabled | Qt::ItemIsSelectable : Qt::NoItemFlags);
m_table_widget->setItem(i, 0, name);
m_table_widget->setItem(i, 1, game_id);
m_table_widget->setItem(i, 2, player_count);
m_table_widget->setItem(i, 3, in_game);
}
m_status_label->setText(
(session_count == 1 ? tr("%1 session found") : tr("%1 sessions found")).arg(session_count));
}
void NetPlaySetupDialog::OnSelectionChangedBrowser()
{
return;
}
void NetPlaySetupDialog::OnUpdateStatusRequestedBrowser(const QString& status)
{
m_status_label->setText(status);
}
void NetPlaySetupDialog::OnUpdateListRequestedBrowser(std::vector<NetPlaySession> sessions)
{
m_sessions = std::move(sessions);
UpdateListBrowser();
}
void NetPlaySetupDialog::acceptBrowser()
{
if (m_table_widget->selectedItems().isEmpty())
return;
const int index = m_table_widget->selectedItems()[0]->row();
NetPlaySession& session = m_sessions[index];
std::string server_id = session.server_id;
if (m_sessions[index].has_password)
{
auto* dialog = new QInputDialog(this);
dialog->setWindowFlags(dialog->windowFlags() & ~Qt::WindowContextHelpButtonHint);
dialog->setWindowTitle(tr("Enter password"));
dialog->setLabelText(tr("This session requires a password:"));
dialog->setWindowModality(Qt::WindowModal);
dialog->setTextEchoMode(QLineEdit::Password);
if (dialog->exec() != QDialog::Accepted)
return;
const std::string password = dialog->textValue().toStdString();
auto decrypted_id = session.DecryptID(password);
if (!decrypted_id)
{
ModalMessageBox::warning(this, tr("Error"), tr("Invalid password provided."));
return;
}
server_id = decrypted_id.value();
}
QDialog::accept();
Config::SetBaseOrCurrent(Config::NETPLAY_TRAVERSAL_CHOICE, session.method);
Config::SetBaseOrCurrent(Config::NETPLAY_CONNECT_PORT, session.port);
if (session.method == "traversal")
Config::SetBaseOrCurrent(Config::NETPLAY_HOST_CODE, server_id);
else
Config::SetBaseOrCurrent(Config::NETPLAY_ADDRESS, server_id);
emit JoinBrowser();
}