mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-07-24 14:49:42 -06:00
NetPlay: Implement chunked data transfer
This sends arbitrary packets in chunks to be reassembled at the other end, allowing large data transfers to be speed-limited and interleaved with other packets being sent. It also enables tracking the progress of large data transfers.
This commit is contained in:
123
Source/Core/DolphinQt/NetPlay/ChunkedProgressDialog.cpp
Normal file
123
Source/Core/DolphinQt/NetPlay/ChunkedProgressDialog.cpp
Normal file
@ -0,0 +1,123 @@
|
||||
// Copyright 2017 Dolphin Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "DolphinQt/NetPlay/ChunkedProgressDialog.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <functional>
|
||||
|
||||
#include <QDialogButtonBox>
|
||||
#include <QGroupBox>
|
||||
#include <QLabel>
|
||||
#include <QProgressBar>
|
||||
#include <QPushButton>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
#include "Common/StringUtil.h"
|
||||
|
||||
#include "Core/NetPlayClient.h"
|
||||
#include "Core/NetPlayServer.h"
|
||||
|
||||
#include "DolphinQt/Settings.h"
|
||||
|
||||
static QString GetPlayerNameFromPID(int pid)
|
||||
{
|
||||
QString player_name = QObject::tr("Invalid Player ID");
|
||||
auto client = Settings::Instance().GetNetPlayClient();
|
||||
if (!client)
|
||||
return player_name;
|
||||
|
||||
for (const auto* player : client->GetPlayers())
|
||||
{
|
||||
if (player->pid == pid)
|
||||
{
|
||||
player_name = QString::fromStdString(player->name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return player_name;
|
||||
}
|
||||
|
||||
ChunkedProgressDialog::ChunkedProgressDialog(QWidget* parent) : QDialog(parent)
|
||||
{
|
||||
CreateWidgets();
|
||||
ConnectWidgets();
|
||||
setWindowTitle(tr("Data Transfer"));
|
||||
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||
}
|
||||
|
||||
void ChunkedProgressDialog::CreateWidgets()
|
||||
{
|
||||
m_main_layout = new QVBoxLayout;
|
||||
m_progress_box = new QGroupBox;
|
||||
m_progress_layout = new QVBoxLayout;
|
||||
|
||||
m_progress_box->setLayout(m_progress_layout);
|
||||
|
||||
m_main_layout->addWidget(m_progress_box);
|
||||
setLayout(m_main_layout);
|
||||
}
|
||||
|
||||
void ChunkedProgressDialog::ConnectWidgets()
|
||||
{
|
||||
}
|
||||
|
||||
void ChunkedProgressDialog::show(const QString& title, const u64 data_size,
|
||||
const std::vector<int>& players)
|
||||
{
|
||||
m_progress_box->setTitle(title);
|
||||
m_data_size = data_size;
|
||||
|
||||
for (auto& pair : m_progress_bars)
|
||||
{
|
||||
m_progress_layout->removeWidget(pair.second);
|
||||
pair.second->deleteLater();
|
||||
}
|
||||
|
||||
for (auto& pair : m_status_labels)
|
||||
{
|
||||
m_progress_layout->removeWidget(pair.second);
|
||||
pair.second->deleteLater();
|
||||
}
|
||||
|
||||
m_progress_bars.clear();
|
||||
m_status_labels.clear();
|
||||
|
||||
auto client = Settings::Instance().GetNetPlayClient();
|
||||
if (!client)
|
||||
return;
|
||||
|
||||
for (const auto* player : client->GetPlayers())
|
||||
{
|
||||
if (std::find(players.begin(), players.end(), player->pid) == players.end())
|
||||
continue;
|
||||
|
||||
m_progress_bars[player->pid] = new QProgressBar;
|
||||
m_status_labels[player->pid] = new QLabel;
|
||||
|
||||
m_progress_layout->addWidget(m_progress_bars[player->pid]);
|
||||
m_progress_layout->addWidget(m_status_labels[player->pid]);
|
||||
}
|
||||
|
||||
QDialog::show();
|
||||
}
|
||||
|
||||
void ChunkedProgressDialog::SetProgress(const int pid, const u64 progress)
|
||||
{
|
||||
QString player_name = GetPlayerNameFromPID(pid);
|
||||
|
||||
if (!m_status_labels.count(pid))
|
||||
return;
|
||||
|
||||
const float acquired = progress / 1024.0f / 1024.0f;
|
||||
const float total = m_data_size / 1024.0f / 1024.0f;
|
||||
const int prog = std::lround((static_cast<float>(progress) / m_data_size) * 100.0f);
|
||||
|
||||
m_status_labels[pid]->setText(tr("%1[%2]: %3/%4 MiB")
|
||||
.arg(player_name, QString::number(pid),
|
||||
QString::fromStdString(StringFromFormat("%.2f", acquired)),
|
||||
QString::fromStdString(StringFromFormat("%.2f", total))));
|
||||
m_progress_bars[pid]->setValue(prog);
|
||||
}
|
41
Source/Core/DolphinQt/NetPlay/ChunkedProgressDialog.h
Normal file
41
Source/Core/DolphinQt/NetPlay/ChunkedProgressDialog.h
Normal file
@ -0,0 +1,41 @@
|
||||
// Copyright 2017 Dolphin Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <QDialog>
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
|
||||
class QGroupBox;
|
||||
class QLabel;
|
||||
class QProgressBar;
|
||||
class QVBoxLayout;
|
||||
class QWidget;
|
||||
|
||||
class ChunkedProgressDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit ChunkedProgressDialog(QWidget* parent);
|
||||
|
||||
void show(const QString& title, u64 data_size, const std::vector<int>& players);
|
||||
void SetProgress(int pid, u64 progress);
|
||||
|
||||
private:
|
||||
void CreateWidgets();
|
||||
void ConnectWidgets();
|
||||
|
||||
std::map<int, QProgressBar*> m_progress_bars;
|
||||
std::map<int, QLabel*> m_status_labels;
|
||||
u64 m_data_size = 0;
|
||||
|
||||
QGroupBox* m_progress_box;
|
||||
QVBoxLayout* m_progress_layout;
|
||||
QVBoxLayout* m_main_layout;
|
||||
};
|
@ -43,6 +43,7 @@
|
||||
#include "Core/NetPlayServer.h"
|
||||
|
||||
#include "DolphinQt/GameList/GameListModel.h"
|
||||
#include "DolphinQt/NetPlay/ChunkedProgressDialog.h"
|
||||
#include "DolphinQt/NetPlay/GameListDialog.h"
|
||||
#include "DolphinQt/NetPlay/MD5Dialog.h"
|
||||
#include "DolphinQt/NetPlay/PadMappingDialog.h"
|
||||
@ -68,6 +69,7 @@ NetPlayDialog::NetPlayDialog(QWidget* parent)
|
||||
|
||||
m_pad_mapping = new PadMappingDialog(this);
|
||||
m_md5_dialog = new MD5Dialog(this);
|
||||
m_chunked_progress_dialog = new ChunkedProgressDialog(this);
|
||||
|
||||
ResetExternalIP();
|
||||
CreateChatLayout();
|
||||
@ -1046,3 +1048,27 @@ void NetPlayDialog::AbortMD5()
|
||||
m_md5_button->setEnabled(true);
|
||||
});
|
||||
}
|
||||
|
||||
void NetPlayDialog::ShowChunkedProgressDialog(const std::string& title, const u64 data_size,
|
||||
const std::vector<int>& players)
|
||||
{
|
||||
QueueOnObject(this, [this, title, data_size, players] {
|
||||
if (m_chunked_progress_dialog->isVisible())
|
||||
m_chunked_progress_dialog->close();
|
||||
|
||||
m_chunked_progress_dialog->show(QString::fromStdString(title), data_size, players);
|
||||
});
|
||||
}
|
||||
|
||||
void NetPlayDialog::HideChunkedProgressDialog()
|
||||
{
|
||||
QueueOnObject(this, [this] { m_chunked_progress_dialog->close(); });
|
||||
}
|
||||
|
||||
void NetPlayDialog::SetChunkedProgress(const int pid, const u64 progress)
|
||||
{
|
||||
QueueOnObject(this, [this, pid, progress] {
|
||||
if (m_chunked_progress_dialog->isVisible())
|
||||
m_chunked_progress_dialog->SetProgress(pid, progress);
|
||||
});
|
||||
}
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include "Core/NetPlayClient.h"
|
||||
#include "VideoCommon/OnScreenDisplay.h"
|
||||
|
||||
class ChunkedProgressDialog;
|
||||
class MD5Dialog;
|
||||
class GameListModel;
|
||||
class PadMappingDialog;
|
||||
@ -67,6 +68,11 @@ public:
|
||||
void SetMD5Progress(int pid, int progress) override;
|
||||
void SetMD5Result(int pid, const std::string& result) override;
|
||||
void AbortMD5() override;
|
||||
|
||||
void ShowChunkedProgressDialog(const std::string& title, u64 data_size,
|
||||
const std::vector<int>& players) override;
|
||||
void HideChunkedProgressDialog() override;
|
||||
void SetChunkedProgress(int pid, u64 progress) override;
|
||||
signals:
|
||||
void Boot(const QString& filename);
|
||||
void Stop();
|
||||
@ -122,6 +128,7 @@ private:
|
||||
|
||||
QGridLayout* m_main_layout;
|
||||
MD5Dialog* m_md5_dialog;
|
||||
ChunkedProgressDialog* m_chunked_progress_dialog;
|
||||
PadMappingDialog* m_pad_mapping;
|
||||
std::string m_current_game;
|
||||
Common::Lazy<std::string> m_external_ip_address;
|
||||
|
@ -35,6 +35,8 @@ NetPlaySetupDialog::NetPlaySetupDialog(QWidget* parent)
|
||||
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);
|
||||
|
||||
@ -50,6 +52,10 @@ NetPlaySetupDialog::NetPlaySetupDialog(QWidget* parent)
|
||||
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();
|
||||
@ -101,6 +107,8 @@ void NetPlaySetupDialog::CreateMainLayout()
|
||||
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)"));
|
||||
@ -110,6 +118,12 @@ void NetPlaySetupDialog::CreateMainLayout()
|
||||
|
||||
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);
|
||||
@ -119,7 +133,9 @@ void NetPlaySetupDialog::CreateMainLayout()
|
||||
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_button, 2, 2, Qt::AlignRight);
|
||||
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);
|
||||
|
||||
@ -163,7 +179,14 @@ void NetPlaySetupDialog::ConnectWidgets()
|
||||
connect(m_host_games, &QListWidget::itemDoubleClicked, this, &NetPlaySetupDialog::accept);
|
||||
|
||||
connect(m_host_force_port_check, &QCheckBox::toggled,
|
||||
[this](int value) { m_host_force_port_box->setEnabled(value); });
|
||||
[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<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this,
|
||||
&NetPlaySetupDialog::SaveSettings);
|
||||
#ifdef USE_UPNP
|
||||
connect(m_host_upnp, &QCheckBox::stateChanged, this, &NetPlaySetupDialog::SaveSettings);
|
||||
#endif
|
||||
@ -191,6 +214,11 @@ void NetPlaySetupDialog::SaveSettings()
|
||||
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());
|
||||
}
|
||||
|
||||
void NetPlaySetupDialog::OnConnectionTypeChanged(int index)
|
||||
|
@ -63,6 +63,8 @@ private:
|
||||
QPushButton* m_host_button;
|
||||
QCheckBox* m_host_force_port_check;
|
||||
QSpinBox* m_host_force_port_box;
|
||||
QCheckBox* m_host_chunked_upload_limit_check;
|
||||
QSpinBox* m_host_chunked_upload_limit_box;
|
||||
|
||||
#ifdef USE_UPNP
|
||||
QCheckBox* m_host_upnp;
|
||||
|
Reference in New Issue
Block a user