Qt/NetPlay: Integrate NetPlayIndex

This commit is contained in:
spycrab 2019-03-30 14:50:57 +01:00
parent 0279d12055
commit 094bf0d2ff
15 changed files with 431 additions and 57 deletions

View File

@ -36,7 +36,7 @@ public:
static int CurlProgressCallback(Impl* impl, double dlnow, double dltotal, double ulnow,
double ultotal);
const std::string EscapeComponent(const std::string& string);
std::string EscapeComponent(const std::string& string);
private:
static std::mutex s_curl_was_inited_mutex;
@ -75,7 +75,7 @@ void HttpRequest::FollowRedirects(long max)
m_impl->FollowRedirects(max);
}
const std::string HttpRequest::EscapeComponent(const std::string& string)
std::string HttpRequest::EscapeComponent(const std::string& string)
{
return m_impl->EscapeComponent(string);
}
@ -165,7 +165,7 @@ void HttpRequest::Impl::FollowRedirects(long max)
curl_easy_setopt(m_curl.get(), CURLOPT_MAXREDIRS, max);
}
const std::string HttpRequest::Impl::EscapeComponent(const std::string& string)
std::string HttpRequest::Impl::EscapeComponent(const std::string& string)
{
return curl_easy_escape(m_curl.get(), string.c_str(), static_cast<int>(string.size()));
}

View File

@ -34,7 +34,7 @@ public:
void SetCookies(const std::string& cookies);
void UseIPv4();
void FollowRedirects(long max = 1);
const std::string EscapeComponent(const std::string& string);
std::string EscapeComponent(const std::string& string);
Response Get(const std::string& url, const Headers& headers = {});
Response Post(const std::string& url, const std::vector<u8>& payload,
const Headers& headers = {});

View File

@ -20,7 +20,7 @@ const ConfigInfo<u16> NETPLAY_TRAVERSAL_PORT{{System::Main, "NetPlay", "Traversa
const ConfigInfo<std::string> NETPLAY_TRAVERSAL_CHOICE{{System::Main, "NetPlay", "TraversalChoice"},
"direct"};
const ConfigInfo<std::string> NETPLAY_INDEX_URL{{System::Main, "NetPlay", "IndexServer"},
"https://dolphin-emu.org/lobby"};
"https://lobby.dolphin-emu.org"};
const ConfigInfo<bool> NETPLAY_USE_INDEX{{System::Main, "NetPlay", "UseIndex"}, false};
const ConfigInfo<std::string> NETPLAY_INDEX_NAME{{System::Main, "NetPlay", "IndexName"}, ""};

View File

@ -98,6 +98,7 @@ add_executable(dolphin-emu
NetPlay/ChunkedProgressDialog.cpp
NetPlay/GameListDialog.cpp
NetPlay/MD5Dialog.cpp
NetPlay/NetPlayBrowser.cpp
NetPlay/NetPlayDialog.cpp
NetPlay/NetPlaySetupDialog.cpp
NetPlay/PadMappingDialog.cpp

View File

@ -146,6 +146,7 @@
<QtMoc Include="NetPlay\ChunkedProgressDialog.h" />
<QtMoc Include="NetPlay\GameListDialog.h" />
<QtMoc Include="NetPlay\MD5Dialog.h" />
<QtMoc Include="NetPlay\NetPlayBrowser.h" />
<QtMoc Include="NetPlay\NetPlayDialog.h" />
<QtMoc Include="NetPlay\NetPlaySetupDialog.h" />
<QtMoc Include="NetPlay\PadMappingDialog.h" />
@ -247,6 +248,7 @@
<ClCompile Include="$(QtMocOutPrefix)MemoryWidget.cpp" />
<ClCompile Include="$(QtMocOutPrefix)MenuBar.cpp" />
<ClCompile Include="$(QtMocOutPrefix)ModalMessageBox.cpp" />
<ClCompile Include="$(QtMocOutPrefix)NetPlayBrowser.cpp" />
<ClCompile Include="$(QtMocOutPrefix)NetPlayDialog.cpp" />
<ClCompile Include="$(QtMocOutPrefix)NetPlaySetupDialog.cpp" />
<ClCompile Include="$(QtMocOutPrefix)NewBreakpointDialog.cpp" />
@ -364,6 +366,7 @@
<ClCompile Include="NetPlay\ChunkedProgressDialog.cpp" />
<ClCompile Include="NetPlay\GameListDialog.cpp" />
<ClCompile Include="NetPlay\MD5Dialog.cpp" />
<ClCompile Include="NetPlay\NetPlayBrowser.cpp" />
<ClCompile Include="NetPlay\NetPlayDialog.cpp" />
<ClCompile Include="NetPlay\NetPlaySetupDialog.cpp" />
<ClCompile Include="NetPlay\PadMappingDialog.cpp" />

View File

@ -80,6 +80,7 @@
#include "DolphinQt/HotkeyScheduler.h"
#include "DolphinQt/MainWindow.h"
#include "DolphinQt/MenuBar.h"
#include "DolphinQt/NetPlay/NetPlayBrowser.h"
#include "DolphinQt/NetPlay/NetPlayDialog.h"
#include "DolphinQt/NetPlay/NetPlaySetupDialog.h"
#include "DolphinQt/QtUtils/ModalMessageBox.h"
@ -449,6 +450,7 @@ void MainWindow::ConnectMenuBar()
connect(m_menu_bar, &MenuBar::PerformOnlineUpdate, this, &MainWindow::PerformOnlineUpdate);
connect(m_menu_bar, &MenuBar::BootWiiSystemMenu, this, &MainWindow::BootWiiSystemMenu);
connect(m_menu_bar, &MenuBar::StartNetPlay, this, &MainWindow::ShowNetPlaySetupDialog);
connect(m_menu_bar, &MenuBar::BrowseNetPlay, this, &MainWindow::ShowNetPlayBrowser);
connect(m_menu_bar, &MenuBar::ShowFIFOPlayer, this, &MainWindow::ShowFIFOPlayer);
connect(m_menu_bar, &MenuBar::ConnectWiiRemote, this, &MainWindow::OnConnectWiiRemote);
@ -1125,6 +1127,13 @@ void MainWindow::ShowNetPlaySetupDialog()
m_netplay_setup_dialog->activateWindow();
}
void MainWindow::ShowNetPlayBrowser()
{
auto* browser = new NetPlayBrowser(this);
connect(browser, &NetPlayBrowser::Join, this, &MainWindow::NetPlayJoin);
browser->exec();
}
void MainWindow::ShowFIFOPlayer()
{
if (!m_fifo_window)

View File

@ -148,6 +148,7 @@ private:
void ShowAboutDialog();
void ShowHotkeyDialog();
void ShowNetPlaySetupDialog();
void ShowNetPlayBrowser();
void ShowFIFOPlayer();
void ShowMemcardManager();
void ShowResourcePackManager();

View File

@ -241,6 +241,7 @@ void MenuBar::AddToolsMenu()
gc_ipl->addAction(tr("PAL"), this, [this] { emit BootGameCubeIPL(DiscIO::Region::PAL); });
tools_menu->addAction(tr("Start &NetPlay..."), this, &MenuBar::StartNetPlay);
tools_menu->addAction(tr("Browse &NetPlay Sessions...."), this, &MenuBar::BrowseNetPlay);
tools_menu->addAction(tr("FIFO Player"), this, &MenuBar::ShowFIFOPlayer);
tools_menu->addSeparator();

View File

@ -61,6 +61,7 @@ signals:
void FrameAdvance();
void Screenshot();
void StartNetPlay();
void BrowseNetPlay();
void StateLoad();
void StateSave();
void StateLoadSlot();

View File

@ -0,0 +1,243 @@
// Copyright 2019 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#include "DolphinQt/NetPlay/NetPlayBrowser.h"
#include <QComboBox>
#include <QDialogButtonBox>
#include <QGridLayout>
#include <QGroupBox>
#include <QHeaderView>
#include <QInputDialog>
#include <QLabel>
#include <QLineEdit>
#include <QPushButton>
#include <QRadioButton>
#include <QSpacerItem>
#include <QTableWidget>
#include <QTableWidgetItem>
#include <QVBoxLayout>
#include "Common/Version.h"
#include "Core/Config/NetplaySettings.h"
#include "Core/ConfigManager.h"
#include "DolphinQt/QtUtils/ModalMessageBox.h"
NetPlayBrowser::NetPlayBrowser(QWidget* parent) : QDialog(parent)
{
setWindowTitle(tr("NetPlay Session Browser"));
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
CreateWidgets();
ConnectWidgets();
resize(750, 500);
m_table_widget->verticalHeader()->setHidden(true);
m_table_widget->setAlternatingRowColors(true);
Refresh();
}
void NetPlayBrowser::CreateWidgets()
{
auto* layout = new QVBoxLayout;
m_table_widget = new QTableWidget;
m_table_widget->setSelectionBehavior(QAbstractItemView::SelectRows);
m_table_widget->setSelectionMode(QAbstractItemView::SingleSelection);
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_status_label = new QLabel;
m_button_box = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
m_button_refresh = new QPushButton(tr("Refresh"));
m_edit_name = new QLineEdit;
m_edit_game_id = new QLineEdit;
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);
auto* filter_box = new QGroupBox(tr("Filters"));
auto* filter_layout = new QGridLayout;
filter_box->setLayout(filter_layout);
filter_layout->addWidget(new QLabel(tr("Region:")), 0, 0);
filter_layout->addWidget(m_region_combo, 0, 1);
filter_layout->addWidget(new QLabel(tr("Name:")), 1, 0);
filter_layout->addWidget(m_edit_name, 1, 1, 1, -1);
filter_layout->addWidget(new QLabel(tr("Game ID:")), 2, 0);
filter_layout->addWidget(m_edit_game_id, 2, 1, 1, -1);
filter_layout->addWidget(m_radio_all, 3, 1);
filter_layout->addWidget(m_radio_public, 3, 2);
filter_layout->addWidget(m_radio_private, 3, 3);
filter_layout->addItem(new QSpacerItem(4, 1, QSizePolicy::Expanding), 2, 4);
layout->addWidget(m_table_widget);
layout->addWidget(filter_box);
layout->addWidget(m_status_label);
layout->addWidget(m_button_box);
m_button_box->addButton(m_button_refresh, QDialogButtonBox::ResetRole);
m_button_box->button(QDialogButtonBox::Ok)->setEnabled(false);
setLayout(layout);
}
void NetPlayBrowser::ConnectWidgets()
{
connect(m_button_box, &QDialogButtonBox::accepted, this, &NetPlayBrowser::accept);
connect(m_button_box, &QDialogButtonBox::rejected, this, &NetPlayBrowser::reject);
connect(m_button_refresh, &QPushButton::pressed, this, &NetPlayBrowser::Refresh);
connect(m_radio_all, &QRadioButton::toggled, this, &NetPlayBrowser::Refresh);
connect(m_radio_private, &QRadioButton::toggled, this, &NetPlayBrowser::Refresh);
connect(m_edit_name, &QLineEdit::textChanged, this, &NetPlayBrowser::Refresh);
connect(m_edit_game_id, &QLineEdit::textChanged, this, &NetPlayBrowser::Refresh);
connect(m_table_widget, &QTableWidget::itemSelectionChanged, this,
&NetPlayBrowser::OnSelectionChanged);
}
void NetPlayBrowser::Refresh()
{
m_status_label->setText(tr("Refreshing..."));
m_table_widget->clear();
m_table_widget->setColumnCount(6);
m_table_widget->setHorizontalHeaderLabels(
{tr("Region"), tr("Name"), tr("Password?"), tr("In-Game?"), tr("Game"), tr("Players")});
m_table_widget->horizontalHeader()->setSectionResizeMode(0, QHeaderView::ResizeToContents);
m_table_widget->horizontalHeader()->setSectionResizeMode(1, QHeaderView::Stretch);
m_table_widget->horizontalHeader()->setSectionResizeMode(2, QHeaderView::ResizeToContents);
m_table_widget->horizontalHeader()->setSectionResizeMode(3, QHeaderView::ResizeToContents);
m_table_widget->horizontalHeader()->setSectionResizeMode(4, QHeaderView::Stretch);
NetPlayIndex client;
std::map<std::string, std::string> filters;
filters["version"] = Common::scm_desc_str;
if (!m_edit_name->text().isEmpty())
filters["name"] = m_edit_name->text().toStdString();
if (!m_edit_game_id->text().isEmpty())
filters["game"] = m_edit_game_id->text().toStdString();
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();
auto entries = client.List(filters);
if (!entries)
{
m_status_label->setText(
tr("Error obtaining session list: %1").arg(QString::fromStdString(client.GetLastError())));
return;
}
const int session_count = static_cast<int>(entries.value().size());
m_table_widget->setRowCount(session_count);
for (int i = 0; i < session_count; i++)
{
const auto& entry = entries.value()[i];
auto* region = new QTableWidgetItem(QString::fromStdString(entry.region));
auto* name = new QTableWidgetItem(QString::fromStdString(entry.name));
auto* password = new QTableWidgetItem(entry.has_password ? tr("Yes") : tr("No"));
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));
for (const auto& item : {region, name, password, game_id, player_count})
item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable);
m_table_widget->setItem(i, 0, region);
m_table_widget->setItem(i, 1, name);
m_table_widget->setItem(i, 2, password);
m_table_widget->setItem(i, 3, in_game);
m_table_widget->setItem(i, 4, game_id);
m_table_widget->setItem(i, 5, player_count);
}
m_status_label->setText(
(session_count == 1 ? tr("%1 session found") : tr("%1 sessions found")).arg(session_count));
m_sessions = entries.value();
}
void NetPlayBrowser::OnSelectionChanged()
{
m_button_box->button(QDialogButtonBox::Ok)
->setEnabled(!m_table_widget->selectedItems().isEmpty());
}
void NetPlayBrowser::accept()
{
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 Join();
}

View File

@ -0,0 +1,52 @@
// Copyright 2019 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#pragma once
#include <vector>
#include <QDialog>
#include "UICommon/NetPlayIndex.h"
class QComboBox;
class QDialogButtonBox;
class QLabel;
class QLineEdit;
class QPushButton;
class QRadioButton;
class QTableWidget;
class NetPlayBrowser : public QDialog
{
Q_OBJECT
public:
explicit NetPlayBrowser(QWidget* parent = nullptr);
void accept() override;
signals:
void Join();
private:
void CreateWidgets();
void ConnectWidgets();
void Refresh();
void OnSelectionChanged();
QComboBox* m_region_combo;
QLabel* m_status_label;
QPushButton* m_button_refresh;
QTableWidget* m_table_widget;
QDialogButtonBox* m_button_box;
QLineEdit* m_edit_name;
QLineEdit* m_edit_game_id;
QRadioButton* m_radio_all;
QRadioButton* m_radio_private;
QRadioButton* m_radio_public;
std::vector<NetPlaySession> m_sessions;
};

View File

@ -22,6 +22,8 @@
#include "DolphinQt/QtUtils/ModalMessageBox.h"
#include "DolphinQt/Settings.h"
#include "UICommon/NetPlayIndex.h"
NetPlaySetupDialog::NetPlaySetupDialog(QWidget* parent)
: QDialog(parent), m_game_list_model(Settings::Instance().GetGameListModel())
{
@ -30,6 +32,10 @@ NetPlaySetupDialog::NetPlaySetupDialog(QWidget* parent)
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);
@ -48,10 +54,21 @@ NetPlaySetupDialog::NetPlaySetupDialog(QWidget* parent)
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_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));
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);
@ -112,6 +129,10 @@ void NetPlaySetupDialog::CreateMainLayout()
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)"));
@ -128,17 +149,33 @@ void NetPlaySetupDialog::CreateMainLayout()
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);
#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_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_widget->setLayout(host_layout);
@ -199,6 +236,11 @@ 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);
});
}
void NetPlaySetupDialog::SaveSettings()
@ -224,6 +266,13 @@ void NetPlaySetupDialog::SaveSettings()
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_NAME, m_host_server_name->text().toStdString());
Config::SetBaseOrCurrent(Config::NETPLAY_INDEX_PASSWORD,
m_host_server_password->text().toStdString());
}
void NetPlaySetupDialog::OnConnectionTypeChanged(int index)
@ -273,6 +322,12 @@ 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;
}
emit Host(items[0]->text());
}
}

View File

@ -65,6 +65,10 @@ private:
QSpinBox* m_host_force_port_box;
QCheckBox* m_host_chunked_upload_limit_check;
QSpinBox* m_host_chunked_upload_limit_box;
QCheckBox* m_host_server_browser;
QLineEdit* m_host_server_name;
QLineEdit* m_host_server_password;
QComboBox* m_host_server_region;
#ifdef USE_UPNP
QCheckBox* m_host_upnp;

View File

@ -4,17 +4,19 @@
#include "UICommon/NetPlayIndex.h"
#include <numeric>
#include <string>
#include <picojson/picojson.h>
#include "Common/Common.h"
#include "Common/HttpRequest.h"
#include "Common/Thread.h"
#include "Common/Version.h"
#include "Core/Config/NetplaySettings.h"
NetPlayIndex::NetPlayIndex()
{
}
NetPlayIndex::NetPlayIndex() = default;
NetPlayIndex::~NetPlayIndex()
{
@ -45,16 +47,15 @@ NetPlayIndex::List(const std::map<std::string, std::string>& filters)
if (!filters.empty())
{
list_url += "?";
list_url += '?';
for (const auto& filter : filters)
{
list_url += filter.first + "=" + request.EscapeComponent(filter.second) + "&";
list_url += filter.first + '=' + request.EscapeComponent(filter.second) + '&';
}
list_url = list_url.substr(0, list_url.size() - 1);
list_url.pop_back();
}
auto response = request.Get(list_url);
auto response = request.Get(list_url, {{"X-Is-Dolphin", "1"}});
if (!response)
{
m_last_error = "NO_RESPONSE";
@ -98,7 +99,9 @@ NetPlayIndex::List(const std::map<std::string, std::string>& filters)
if (!name.is<std::string>() || !region.is<std::string>() || !method.is<std::string>() ||
!server_id.is<std::string>() || !game_id.is<std::string>() || !has_password.is<bool>() ||
!player_count.is<double>() || !port.is<double>() || !in_game.is<bool>())
{
continue;
}
session.name = name.to_str();
session.region = region.to_str();
@ -110,7 +113,7 @@ NetPlayIndex::List(const std::map<std::string, std::string>& filters)
session.port = static_cast<int>(port.get<double>());
session.in_game = in_game.get<bool>();
sessions.push_back(session);
sessions.push_back(std::move(session));
}
return sessions;
@ -123,8 +126,9 @@ void NetPlayIndex::NotificationLoop()
Common::HttpRequest request;
auto response = request.Get(
Config::Get(Config::NETPLAY_INDEX_URL) + "/v0/session/active?secret=" + m_secret +
"&player_count=" + std::to_string(m_player_count) +
"&game=" + request.EscapeComponent(m_game) + "&in_game=" + std::to_string(m_in_game));
"&player_count=" + std::to_string(m_player_count) +
"&game=" + request.EscapeComponent(m_game) + "&in_game=" + std::to_string(m_in_game),
{{"X-Is-Dolphin", "1"}});
if (!response)
continue;
@ -142,7 +146,7 @@ void NetPlayIndex::NotificationLoop()
if (status != "OK")
{
m_last_error = status;
m_last_error = std::move(status);
m_running.Set(false);
return;
}
@ -156,14 +160,17 @@ bool NetPlayIndex::Add(NetPlaySession session)
m_running.Set(true);
Common::HttpRequest request;
auto response = request.Get(
Config::Get(Config::NETPLAY_INDEX_URL) + "/v0/session/add?name=" +
request.EscapeComponent(session.name) + "&region=" + request.EscapeComponent(session.region) +
"&game=" + request.EscapeComponent(session.game_id) +
"&password=" + std::to_string(session.has_password) + "&method=" + session.method +
"&server_id=" + session.server_id + "&in_game=" + std::to_string(session.in_game) +
"&port=" + std::to_string(session.port) +
"&player_count=" + std::to_string(session.player_count) + "&version=" + Common::scm_desc_str);
auto response = request.Get(Config::Get(Config::NETPLAY_INDEX_URL) +
"/v0/session/add?name=" + request.EscapeComponent(session.name) +
"&region=" + request.EscapeComponent(session.region) +
"&game=" + request.EscapeComponent(session.game_id) +
"&password=" + std::to_string(session.has_password) +
"&method=" + session.method + "&server_id=" + session.server_id +
"&in_game=" + std::to_string(session.in_game) +
"&port=" + std::to_string(session.port) +
"&player_count=" + std::to_string(session.player_count) +
"&version=" + Common::scm_desc_str,
{{"X-Is-Dolphin", "1"}});
if (!response.has_value())
{
@ -183,7 +190,7 @@ bool NetPlayIndex::Add(NetPlaySession session)
if (status != "OK")
{
m_last_error = status;
m_last_error = std::move(status);
return false;
}
@ -209,9 +216,9 @@ void NetPlayIndex::SetPlayerCount(int player_count)
m_player_count = player_count;
}
void NetPlayIndex::SetGame(const std::string& game)
void NetPlayIndex::SetGame(const std::string game)
{
m_game = game;
m_game = std::move(game);
}
void NetPlayIndex::Remove()
@ -226,16 +233,19 @@ void NetPlayIndex::Remove()
// We don't really care whether this fails or not
Common::HttpRequest request;
request.Get(Config::Get(Config::NETPLAY_INDEX_URL) + "/v0/session/remove?secret=" + m_secret);
request.Get(Config::Get(Config::NETPLAY_INDEX_URL) + "/v0/session/remove?secret=" + m_secret,
{{"X-Is-Dolphin", "1"}});
m_secret = "";
m_secret.clear();
}
std::vector<std::string> NetPlayIndex::GetRegions()
std::vector<std::pair<std::string, std::string>> NetPlayIndex::GetRegions()
{
static std::vector<std::string> regions{"AF", "CN", "EA", "EU", "NA", "OC"};
return regions;
return {
{"EA", _trans("East Asia")}, {"CN", _trans("China")}, {"EU", _trans("Europe")},
{"NA", _trans("North America")}, {"SA", _trans("South America")}, {"OC", _trans("Oceania")},
{"AF", _trans("Africa")},
};
}
// This encryption system uses simple XOR operations and a checksum
@ -247,21 +257,16 @@ bool NetPlaySession::EncryptID(const std::string& password)
if (password.empty())
return false;
u8 i = 0;
std::string to_encrypt = server_id;
// Calculate and append checksum to ID
u8 sum = 0;
for (char c : to_encrypt)
sum += c;
const u8 sum = std::accumulate(to_encrypt.begin(), to_encrypt.end(), u8{0});
to_encrypt += sum;
std::string encrypted_id;
for (const char& byte : to_encrypt)
u8 i = 0;
for (const char byte : to_encrypt)
{
char c = byte ^ password[i % password.size()];
c += i;
@ -270,7 +275,7 @@ bool NetPlaySession::EncryptID(const std::string& password)
++i;
}
server_id = encrypted_id;
server_id = std::move(encrypted_id);
return true;
}
@ -301,13 +306,11 @@ std::optional<std::string> NetPlaySession::DecryptID(const std::string& password
}
// Verify checksum
u8 expected_sum = decoded[decoded.size() - 1];
const u8 expected_sum = decoded[decoded.size() - 1];
decoded = decoded.substr(0, decoded.size() - 1);
decoded.pop_back();
u8 sum = 0;
for (char c : decoded)
sum += c;
const u8 sum = std::accumulate(decoded.begin(), decoded.end(), u8{0});
if (sum != expected_sum)
return {};

View File

@ -8,6 +8,7 @@
#include <optional>
#include <string>
#include <thread>
#include <utility>
#include <vector>
#include "Common/Flag.h"
@ -39,14 +40,14 @@ public:
std::optional<std::vector<NetPlaySession>>
List(const std::map<std::string, std::string>& filters = {});
static std::vector<std::string> GetRegions();
static std::vector<std::pair<std::string, std::string>> GetRegions();
bool Add(NetPlaySession session);
void Remove();
void SetPlayerCount(int player_count);
void SetInGame(bool in_game);
void SetGame(const std::string& game);
void SetGame(std::string game);
const std::string& GetLastError() const;