// Copyright 2017 Dolphin Emulator Project // Licensed under GPLv2+ // Refer to the license.txt file included. #include "DolphinQt/NetPlay/NetPlaySetupDialog.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "Core/Config/NetplaySettings.h" #include "DolphinQt/GameList/GameListModel.h" #include "DolphinQt/Settings.h" NetPlaySetupDialog::NetPlaySetupDialog(QWidget* parent) : QDialog(parent), m_game_list_model(Settings::Instance().GetGameListModel()) { setWindowTitle(tr("NetPlay Setup")); setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); CreateMainLayout(); 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); m_host_upnp->setChecked(use_upnp); #endif m_nickname_edit->setText(QString::fromStdString(nickname)); m_connection_type->setCurrentIndex(traversal_choice == "direct" ? 0 : 1); m_connect_port_box->setValue(connect_port); m_host_port_box->setValue(host_port); m_host_force_port_check->setChecked(false); m_host_force_port_box->setValue(host_listen_port); m_host_force_port_box->setEnabled(false); 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); OnConnectionTypeChanged(m_connection_type->currentIndex()); ConnectWidgets(); } void NetPlaySetupDialog::CreateMainLayout() { m_main_layout = new QGridLayout; m_button_box = new QDialogButtonBox(QDialogButtonBox::Cancel); m_nickname_edit = new QLineEdit; m_connection_type = new QComboBox; m_reset_traversal_button = new QPushButton(tr("Reset Traversal Settings")); m_tab_widget = new QTabWidget; // Connection widget auto* connection_widget = new QWidget; auto* connection_layout = new QGridLayout; m_ip_label = new QLabel; m_ip_edit = new QLineEdit; m_connect_port_label = new QLabel(tr("Port:")); m_connect_port_box = new QSpinBox; m_connect_button = new QPushButton(tr("Connect")); m_connect_port_box->setMaximum(65535); connection_layout->addWidget(m_ip_label, 0, 0); connection_layout->addWidget(m_ip_edit, 0, 1); connection_layout->addWidget(m_connect_port_label, 0, 2); connection_layout->addWidget(m_connect_port_box, 0, 3); connection_layout->addWidget( new QLabel(tr( "ALERT:\n\n" "All players must use the same Dolphin version.\n" "All memory cards, SD cards and cheats must be identical between players or disabled.\n" "If DSP LLE is used, DSP ROMs must be identical between players.\n" "If connecting directly, the host must have the chosen UDP port open/forwarded!\n" "\n" "Wii Remote support in netplay is experimental and should not be expected to work.\n")), 1, 0, -1, -1); connection_layout->addWidget(m_connect_button, 3, 3, Qt::AlignRight); connection_widget->setLayout(connection_layout); // 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; #ifdef USE_UPNP m_host_upnp = new QCheckBox(tr("Forward port (UPnP)")); #endif m_host_games = new QListWidget; m_host_button = new QPushButton(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.")); host_layout->addWidget(m_host_port_label, 0, 0); host_layout->addWidget(m_host_port_box, 0, 1); #ifdef USE_UPNP host_layout->addWidget(m_host_upnp, 0, 2); #endif host_layout->addWidget(m_host_games, 1, 0, 1, -1); host_layout->addWidget(m_host_force_port_check, 2, 0); host_layout->addWidget(m_host_force_port_box, 2, 1, Qt::AlignLeft); host_layout->addWidget(m_host_chunked_upload_limit_check, 3, 0); host_layout->addWidget(m_host_chunked_upload_limit_box, 3, 1, Qt::AlignLeft); host_layout->addWidget(m_host_button, 2, 2, 2, 1, Qt::AlignRight); host_widget->setLayout(host_layout); m_connection_type->addItem(tr("Direct Connection")); m_connection_type->addItem(tr("Traversal Server")); m_main_layout->addWidget(new QLabel(tr("Connection Type:")), 0, 0); m_main_layout->addWidget(m_connection_type, 0, 1); m_main_layout->addWidget(m_reset_traversal_button, 0, 2); m_main_layout->addWidget(new QLabel(tr("Nickname:")), 1, 0); m_main_layout->addWidget(m_nickname_edit, 1, 1); m_main_layout->addWidget(m_tab_widget, 2, 0, 1, -1); m_main_layout->addWidget(m_button_box, 3, 0, 1, -1); // Tabs m_tab_widget->addTab(connection_widget, tr("Connect")); m_tab_widget->addTab(host_widget, tr("Host")); setLayout(m_main_layout); } void NetPlaySetupDialog::ConnectWidgets() { connect(m_connection_type, static_cast(&QComboBox::currentIndexChanged), this, &NetPlaySetupDialog::OnConnectionTypeChanged); connect(m_nickname_edit, &QLineEdit::textChanged, this, &NetPlaySetupDialog::SaveSettings); // Connect widget connect(m_ip_edit, &QLineEdit::textChanged, this, &NetPlaySetupDialog::SaveSettings); connect(m_connect_port_box, static_cast(&QSpinBox::valueChanged), this, &NetPlaySetupDialog::SaveSettings); // Host widget connect(m_host_port_box, static_cast(&QSpinBox::valueChanged), this, &NetPlaySetupDialog::SaveSettings); connect(m_host_games, static_cast(&QListWidget::currentRowChanged), [this](int index) { Settings::GetQSettings().setValue(QStringLiteral("netplay/hostgame"), m_host_games->item(index)->text()); }); 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, static_cast(&QSpinBox::valueChanged), this, &NetPlaySetupDialog::SaveSettings); #ifdef USE_UPNP connect(m_host_upnp, &QCheckBox::stateChanged, this, &NetPlaySetupDialog::SaveSettings); #endif connect(m_connect_button, &QPushButton::clicked, this, &QDialog::accept); connect(m_host_button, &QPushButton::clicked, this, &QDialog::accept); connect(m_button_box, &QDialogButtonBox::rejected, this, &QDialog::reject); connect(m_reset_traversal_button, &QPushButton::clicked, this, &NetPlaySetupDialog::ResetTraversalHost); } void NetPlaySetupDialog::SaveSettings() { Config::SetBaseOrCurrent(Config::NETPLAY_NICKNAME, m_nickname_edit->text().toStdString()); Config::SetBaseOrCurrent(m_connection_type->currentIndex() == 0 ? Config::NETPLAY_ADDRESS : Config::NETPLAY_HOST_CODE, m_ip_edit->text().toStdString()); Config::SetBaseOrCurrent(Config::NETPLAY_CONNECT_PORT, static_cast(m_connect_port_box->value())); Config::SetBaseOrCurrent(Config::NETPLAY_HOST_PORT, static_cast(m_host_port_box->value())); #ifdef USE_UPNP 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(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()); } 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 = index == 0 ? Config::Get(Config::NETPLAY_ADDRESS) : Config::Get(Config::NETPLAY_HOST_CODE); m_ip_label->setText(index == 0 ? tr("IP Address:") : tr("Host Code:")); m_ip_edit->setText(QString::fromStdString(address)); Config::SetBaseOrCurrent(Config::NETPLAY_TRAVERSAL_CHOICE, std::string(index == 0 ? "direct" : "traversal")); } void NetPlaySetupDialog::show() { PopulateGameList(); QDialog::show(); } void NetPlaySetupDialog::accept() { SaveSettings(); if (m_tab_widget->currentIndex() == 0) { emit Join(); } else { auto items = m_host_games->selectedItems(); if (items.size() == 0) { QMessageBox::critical(this, tr("Error"), tr("You must select a game to host!")); return; } emit Host(items[0]->text()); } } void NetPlaySetupDialog::PopulateGameList() { QSignalBlocker blocker(m_host_games); m_host_games->clear(); for (int i = 0; i < m_game_list_model->rowCount(QModelIndex()); i++) { auto title = m_game_list_model->GetUniqueIdentifier(i); auto path = m_game_list_model->GetPath(i); auto* item = new QListWidgetItem(title); item->setData(Qt::UserRole, path); m_host_games->addItem(item); } m_host_games->sortItems(); QString selected_game = Settings::GetQSettings() .value(QStringLiteral("netplay/hostgame"), QStringLiteral("")) .toString(); auto find_list = m_host_games->findItems(selected_game, Qt::MatchFlag::MatchExactly); if (find_list.count() > 0) m_host_games->setCurrentItem(find_list[0]); } void NetPlaySetupDialog::ResetTraversalHost() { Config::SetBaseOrCurrent(Config::NETPLAY_TRAVERSAL_SERVER, Config::NETPLAY_TRAVERSAL_SERVER.default_value); Config::SetBaseOrCurrent(Config::NETPLAY_TRAVERSAL_PORT, Config::NETPLAY_TRAVERSAL_PORT.default_value); QMessageBox::information( this, tr("Reset Traversal Server"), tr("Reset Traversal Server to %1:%2") .arg(QString::fromStdString(Config::NETPLAY_TRAVERSAL_SERVER.default_value), QString::number(Config::NETPLAY_TRAVERSAL_PORT.default_value))); }