summaryrefslogtreecommitdiff
path: root/Source/Core
diff options
context:
space:
mode:
authoriwubcode <iwubcode@users.noreply.github.com>2020-05-09 15:44:45 -0500
committeriwubcode <iwubcode@users.noreply.github.com>2020-07-17 14:53:39 -0500
commit58aa0150e1d3d391d5fd4e04ab402afb10a60fcc (patch)
treeadcca627f92d26bf324eea6e8d73dbaaf149aa1d /Source/Core
parenta98df567b11758c9581b23dc72cb1ee9afaf79ce (diff)
DolphinQt / InputCommon: Support multiple DSU servers
Diffstat (limited to 'Source/Core')
-rw-r--r--Source/Core/DolphinQt/CMakeLists.txt2
-rw-r--r--Source/Core/DolphinQt/Config/ControllerInterface/DualShockUDPClientAddServerDialog.cpp75
-rw-r--r--Source/Core/DolphinQt/Config/ControllerInterface/DualShockUDPClientAddServerDialog.h29
-rw-r--r--Source/Core/DolphinQt/Config/ControllerInterface/DualShockUDPClientWidget.cpp127
-rw-r--r--Source/Core/DolphinQt/Config/ControllerInterface/DualShockUDPClientWidget.h22
-rw-r--r--Source/Core/DolphinQt/DolphinQt.vcxproj3
-rw-r--r--Source/Core/InputCommon/ControllerInterface/DualShockUDPClient/DualShockUDPClient.cpp219
-rw-r--r--Source/Core/InputCommon/ControllerInterface/DualShockUDPClient/DualShockUDPClient.h8
-rw-r--r--Source/Core/InputCommon/ControllerInterface/DualShockUDPClient/DualShockUDPProto.h4
9 files changed, 359 insertions, 130 deletions
diff --git a/Source/Core/DolphinQt/CMakeLists.txt b/Source/Core/DolphinQt/CMakeLists.txt
index 3ef723300d..8c95ec3b30 100644
--- a/Source/Core/DolphinQt/CMakeLists.txt
+++ b/Source/Core/DolphinQt/CMakeLists.txt
@@ -57,6 +57,8 @@ add_executable(dolphin-emu
Config/CheatCodeEditor.h
Config/CheatWarningWidget.cpp
Config/CheatWarningWidget.h
+ Config/ControllerInterface/DualShockUDPClientAddServerDialog.cpp
+ Config/ControllerInterface/DualShockUDPClientAddServerDialog.h
Config/ControllerInterface/DualShockUDPClientWidget.cpp
Config/ControllerInterface/DualShockUDPClientWidget.h
Config/ControllerInterface/ControllerInterfaceWindow.cpp
diff --git a/Source/Core/DolphinQt/Config/ControllerInterface/DualShockUDPClientAddServerDialog.cpp b/Source/Core/DolphinQt/Config/ControllerInterface/DualShockUDPClientAddServerDialog.cpp
new file mode 100644
index 0000000000..ff510165db
--- /dev/null
+++ b/Source/Core/DolphinQt/Config/ControllerInterface/DualShockUDPClientAddServerDialog.cpp
@@ -0,0 +1,75 @@
+// Copyright 2020 Dolphin Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#include "DolphinQt/Config/ControllerInterface/DualShockUDPClientAddServerDialog.h"
+
+#include <fmt/format.h>
+
+#include <QButtonGroup>
+#include <QDialog>
+#include <QDialogButtonBox>
+#include <QGridLayout>
+#include <QLabel>
+#include <QLineEdit>
+#include <QPushButton>
+#include <QSpinBox>
+#include <QWidget>
+
+#include "Common/Config/Config.h"
+#include "InputCommon/ControllerInterface/DualShockUDPClient/DualShockUDPClient.h"
+
+DualShockUDPClientAddServerDialog::DualShockUDPClientAddServerDialog(QWidget* parent)
+ : QDialog(parent)
+{
+ CreateWidgets();
+ setLayout(m_main_layout);
+}
+
+void DualShockUDPClientAddServerDialog::CreateWidgets()
+{
+ setWindowTitle(tr("Add New DSU Server"));
+ setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
+
+ m_main_layout = new QGridLayout;
+
+ m_description = new QLineEdit();
+ m_description->setPlaceholderText(tr("BetterJoy, DS4Windows, etc"));
+
+ m_server_address =
+ new QLineEdit(QString::fromStdString(ciface::DualShockUDPClient::DEFAULT_SERVER_ADDRESS));
+
+ m_server_port = new QSpinBox();
+ m_server_port->setMaximum(65535);
+ m_server_port->setValue(ciface::DualShockUDPClient::DEFAULT_SERVER_PORT);
+
+ m_main_layout->addWidget(new QLabel(tr("Description")), 1, 0);
+ m_main_layout->addWidget(m_description, 1, 1);
+ m_main_layout->addWidget(new QLabel(tr("Server IP Address")), 2, 0);
+ m_main_layout->addWidget(m_server_address, 2, 1);
+ m_main_layout->addWidget(new QLabel(tr("Server Port")), 3, 0);
+ m_main_layout->addWidget(m_server_port, 3, 1);
+
+ m_buttonbox = new QDialogButtonBox();
+ auto* add_button = new QPushButton(tr("Add"));
+ auto* cancel_button = new QPushButton(tr("Cancel"));
+ m_buttonbox->addButton(add_button, QDialogButtonBox::AcceptRole);
+ m_buttonbox->addButton(cancel_button, QDialogButtonBox::RejectRole);
+ connect(add_button, &QPushButton::clicked, this,
+ &DualShockUDPClientAddServerDialog::OnServerAdded);
+ connect(cancel_button, &QPushButton::clicked, this, &DualShockUDPClientAddServerDialog::reject);
+ add_button->setDefault(true);
+
+ m_main_layout->addWidget(m_buttonbox, 4, 0, 1, 2);
+}
+
+void DualShockUDPClientAddServerDialog::OnServerAdded()
+{
+ const auto& servers_setting = Config::Get(ciface::DualShockUDPClient::Settings::SERVERS);
+ Config::SetBaseOrCurrent(ciface::DualShockUDPClient::Settings::SERVERS,
+ servers_setting + fmt::format("{}:{}:{};",
+ m_description->text().toStdString(),
+ m_server_address->text().toStdString(),
+ m_server_port->value()));
+ accept();
+}
diff --git a/Source/Core/DolphinQt/Config/ControllerInterface/DualShockUDPClientAddServerDialog.h b/Source/Core/DolphinQt/Config/ControllerInterface/DualShockUDPClientAddServerDialog.h
new file mode 100644
index 0000000000..c7b91f45c4
--- /dev/null
+++ b/Source/Core/DolphinQt/Config/ControllerInterface/DualShockUDPClientAddServerDialog.h
@@ -0,0 +1,29 @@
+// Copyright 2020 Dolphin Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <QDialog>
+
+class QDialogButtonBox;
+class QGridLayout;
+class QLineEdit;
+class QSpinBox;
+
+class DualShockUDPClientAddServerDialog final : public QDialog
+{
+ Q_OBJECT
+public:
+ explicit DualShockUDPClientAddServerDialog(QWidget* parent);
+
+private:
+ void CreateWidgets();
+ void OnServerAdded();
+
+ QDialogButtonBox* m_buttonbox;
+ QGridLayout* m_main_layout;
+ QLineEdit* m_description;
+ QLineEdit* m_server_address;
+ QSpinBox* m_server_port;
+};
diff --git a/Source/Core/DolphinQt/Config/ControllerInterface/DualShockUDPClientWidget.cpp b/Source/Core/DolphinQt/Config/ControllerInterface/DualShockUDPClientWidget.cpp
index 68e8a75030..eac6cea219 100644
--- a/Source/Core/DolphinQt/Config/ControllerInterface/DualShockUDPClientWidget.cpp
+++ b/Source/Core/DolphinQt/Config/ControllerInterface/DualShockUDPClientWidget.cpp
@@ -4,14 +4,16 @@
#include "DolphinQt/Config/ControllerInterface/DualShockUDPClientWidget.h"
+#include <fmt/format.h>
+
+#include <QBoxLayout>
#include <QCheckBox>
-#include <QGridLayout>
-#include <QGroupBox>
#include <QLabel>
-#include <QLineEdit>
-#include <QSpinBox>
+#include <QListWidget>
+#include <QPushButton>
#include "Common/Config/Config.h"
+#include "DolphinQt/Config/ControllerInterface/DualShockUDPClientAddServerDialog.h"
#include "InputCommon/ControllerInterface/DualShockUDPClient/DualShockUDPClient.h"
DualShockUDPClientWidget::DualShockUDPClientWidget()
@@ -22,19 +24,27 @@ DualShockUDPClientWidget::DualShockUDPClientWidget()
void DualShockUDPClientWidget::CreateWidgets()
{
- auto* main_layout = new QGridLayout;
+ auto* main_layout = new QVBoxLayout;
+
+ m_servers_enabled = new QCheckBox(tr("Enable"));
+ m_servers_enabled->setChecked(Config::Get(ciface::DualShockUDPClient::Settings::SERVERS_ENABLED));
+ main_layout->addWidget(m_servers_enabled, 0, 0);
+
+ m_server_list = new QListWidget();
+ main_layout->addWidget(m_server_list);
+
+ m_add_server = new QPushButton(tr("Add..."));
+ m_add_server->setEnabled(m_servers_enabled->isChecked());
- m_server_enabled = new QCheckBox(tr("Enable"));
- m_server_enabled->setChecked(Config::Get(ciface::DualShockUDPClient::Settings::SERVER_ENABLED));
+ m_remove_server = new QPushButton(tr("Remove"));
+ m_remove_server->setEnabled(m_servers_enabled->isChecked());
- m_server_address = new QLineEdit(
- QString::fromStdString(Config::Get(ciface::DualShockUDPClient::Settings::SERVER_ADDRESS)));
- m_server_address->setEnabled(m_server_enabled->isChecked());
+ QHBoxLayout* hlayout = new QHBoxLayout;
+ hlayout->addStretch();
+ hlayout->addWidget(m_add_server);
+ hlayout->addWidget(m_remove_server);
- m_server_port = new QSpinBox();
- m_server_port->setMaximum(65535);
- m_server_port->setValue(Config::Get(ciface::DualShockUDPClient::Settings::SERVER_PORT));
- m_server_port->setEnabled(m_server_enabled->isChecked());
+ main_layout->addItem(hlayout);
auto* description =
new QLabel(tr("DSU protocol enables the use of input and motion data from compatible "
@@ -46,34 +56,75 @@ void DualShockUDPClientWidget::CreateWidgets()
description->setWordWrap(true);
description->setTextInteractionFlags(Qt::TextBrowserInteraction);
description->setOpenExternalLinks(true);
-
- main_layout->addWidget(m_server_enabled, 1, 1);
- main_layout->addWidget(new QLabel(tr("Server IP Address")), 2, 1);
- main_layout->addWidget(m_server_address, 2, 2);
- main_layout->addWidget(new QLabel(tr("Server Port")), 3, 1);
- main_layout->addWidget(m_server_port, 3, 2);
- main_layout->addWidget(description, 4, 1, 1, 2);
+ main_layout->addWidget(description);
setLayout(main_layout);
+
+ RefreshServerList();
}
void DualShockUDPClientWidget::ConnectWidgets()
{
- connect(m_server_enabled, &QCheckBox::toggled, this, [this] {
- bool checked = m_server_enabled->isChecked();
- Config::SetBaseOrCurrent(ciface::DualShockUDPClient::Settings::SERVER_ENABLED, checked);
- m_server_address->setEnabled(checked);
- m_server_port->setEnabled(checked);
- });
-
- connect(m_server_address, &QLineEdit::editingFinished, this, [this] {
- Config::SetBaseOrCurrent(ciface::DualShockUDPClient::Settings::SERVER_ADDRESS,
- m_server_address->text().toStdString());
- });
-
- connect(m_server_port, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this,
- [this] {
- Config::SetBaseOrCurrent(ciface::DualShockUDPClient::Settings::SERVER_PORT,
- static_cast<u16>(m_server_port->value()));
- });
+ connect(m_servers_enabled, &QCheckBox::clicked, this,
+ &DualShockUDPClientWidget::OnServersToggled);
+ connect(m_add_server, &QPushButton::clicked, this, &DualShockUDPClientWidget::OnServerAdded);
+ connect(m_remove_server, &QPushButton::clicked, this, &DualShockUDPClientWidget::OnServerRemoved);
+}
+
+void DualShockUDPClientWidget::RefreshServerList()
+{
+ m_server_list->clear();
+
+ const auto& servers_setting = Config::Get(ciface::DualShockUDPClient::Settings::SERVERS);
+ const auto server_details = SplitString(servers_setting, ';');
+ for (const std::string& server_detail : server_details)
+ {
+ const auto server_info = SplitString(server_detail, ':');
+ if (server_info.size() < 3)
+ continue;
+
+ QListWidgetItem* list_item = new QListWidgetItem(QString::fromStdString(
+ fmt::format("{}:{} - {}", server_info[1], server_info[2], server_info[0])));
+ m_server_list->addItem(list_item);
+ }
+ emit ConfigChanged();
+}
+
+void DualShockUDPClientWidget::OnServerAdded()
+{
+ DualShockUDPClientAddServerDialog add_server_dialog(this);
+ connect(&add_server_dialog, &DualShockUDPClientAddServerDialog::accepted, this,
+ &DualShockUDPClientWidget::RefreshServerList);
+ add_server_dialog.exec();
+}
+
+void DualShockUDPClientWidget::OnServerRemoved()
+{
+ const int row_to_remove = m_server_list->currentRow();
+
+ const auto& servers_setting = Config::Get(ciface::DualShockUDPClient::Settings::SERVERS);
+ const auto server_details = SplitString(servers_setting, ';');
+
+ std::string new_server_setting;
+ for (int i = 0; i < m_server_list->count(); i++)
+ {
+ if (i == row_to_remove)
+ {
+ continue;
+ }
+
+ new_server_setting += server_details[i] + ';';
+ }
+
+ Config::SetBaseOrCurrent(ciface::DualShockUDPClient::Settings::SERVERS, new_server_setting);
+
+ RefreshServerList();
+}
+
+void DualShockUDPClientWidget::OnServersToggled()
+{
+ bool checked = m_servers_enabled->isChecked();
+ Config::SetBaseOrCurrent(ciface::DualShockUDPClient::Settings::SERVERS_ENABLED, checked);
+ m_add_server->setEnabled(checked);
+ m_remove_server->setEnabled(checked);
}
diff --git a/Source/Core/DolphinQt/Config/ControllerInterface/DualShockUDPClientWidget.h b/Source/Core/DolphinQt/Config/ControllerInterface/DualShockUDPClientWidget.h
index 22e5a88011..e8c5557fbd 100644
--- a/Source/Core/DolphinQt/Config/ControllerInterface/DualShockUDPClientWidget.h
+++ b/Source/Core/DolphinQt/Config/ControllerInterface/DualShockUDPClientWidget.h
@@ -4,11 +4,12 @@
#pragma once
+#include <QStringList>
#include <QWidget>
class QCheckBox;
-class QLineEdit;
-class QSpinBox;
+class QListWidget;
+class QPushButton;
class DualShockUDPClientWidget final : public QWidget
{
@@ -16,11 +17,22 @@ class DualShockUDPClientWidget final : public QWidget
public:
explicit DualShockUDPClientWidget();
+signals:
+ // Emitted when config has changed so widgets can update to reflect the change.
+ void ConfigChanged();
+
private:
void CreateWidgets();
void ConnectWidgets();
- QCheckBox* m_server_enabled;
- QLineEdit* m_server_address;
- QSpinBox* m_server_port;
+ void RefreshServerList();
+
+ void OnServerAdded();
+ void OnServerRemoved();
+ void OnServersToggled();
+
+ QCheckBox* m_servers_enabled;
+ QListWidget* m_server_list;
+ QPushButton* m_add_server;
+ QPushButton* m_remove_server;
};
diff --git a/Source/Core/DolphinQt/DolphinQt.vcxproj b/Source/Core/DolphinQt/DolphinQt.vcxproj
index 0fdfc53118..0720ff8e8a 100644
--- a/Source/Core/DolphinQt/DolphinQt.vcxproj
+++ b/Source/Core/DolphinQt/DolphinQt.vcxproj
@@ -124,6 +124,7 @@
<QtMoc Include="Config\Graphics\HacksWidget.h" />
<QtMoc Include="Config\Graphics\PostProcessingConfigWindow.h" />
<QtMoc Include="Config\Graphics\SoftwareRendererWidget.h" />
+ <QtMoc Include="Config\ControllerInterface\DualShockUDPClientAddServerDialog.h" />
<QtMoc Include="Config\ControllerInterface\DualShockUDPClientWidget.h" />
<QtMoc Include="Config\ControllerInterface\ControllerInterfaceWindow.h" />
<QtMoc Include="Config\InfoWidget.h" />
@@ -212,6 +213,7 @@
<ClCompile Include="$(QtMocOutPrefix)ChunkedProgressDialog.cpp" />
<ClCompile Include="$(QtMocOutPrefix)CodeViewWidget.cpp" />
<ClCompile Include="$(QtMocOutPrefix)CodeWidget.cpp" />
+ <ClCompile Include="$(QtMocOutPrefix)DualShockUDPClientAddServerDialog.cpp" />
<ClCompile Include="$(QtMocOutPrefix)DualShockUDPClientWidget.cpp" />
<ClCompile Include="$(QtMocOutPrefix)ControllerInterfaceWindow.cpp" />
<ClCompile Include="$(QtMocOutPrefix)ControllersWindow.cpp" />
@@ -325,6 +327,7 @@
<ClCompile Include="Config\CheatCodeEditor.cpp" />
<ClCompile Include="Config\ARCodeWidget.cpp" />
<ClCompile Include="Config\CheatWarningWidget.cpp" />
+ <ClCompile Include="Config\ControllerInterface\DualShockUDPClientAddServerDialog.cpp" />
<ClCompile Include="Config\ControllerInterface\DualShockUDPClientWidget.cpp" />
<ClCompile Include="Config\ControllerInterface\ControllerInterfaceWindow.cpp" />
<ClCompile Include="Config\ControllersWindow.cpp" />
diff --git a/Source/Core/InputCommon/ControllerInterface/DualShockUDPClient/DualShockUDPClient.cpp b/Source/Core/InputCommon/ControllerInterface/DualShockUDPClient/DualShockUDPClient.cpp
index c398225bfe..837be2c3df 100644
--- a/Source/Core/InputCommon/ControllerInterface/DualShockUDPClient/DualShockUDPClient.cpp
+++ b/Source/Core/InputCommon/ControllerInterface/DualShockUDPClient/DualShockUDPClient.cpp
@@ -12,6 +12,7 @@
#include <SFML/Network/SocketSelector.hpp>
#include <SFML/Network/UdpSocket.hpp>
+#include <fmt/format.h>
#include "Common/Config/Config.h"
#include "Common/Flag.h"
@@ -19,6 +20,7 @@
#include "Common/MathUtil.h"
#include "Common/Matrix.h"
#include "Common/Random.h"
+#include "Common/StringUtil.h"
#include "Common/Thread.h"
#include "Core/CoreTiming.h"
#include "InputCommon/ControllerInterface/ControllerInterface.h"
@@ -28,15 +30,10 @@ namespace ciface::DualShockUDPClient
{
namespace Settings
{
-constexpr char DEFAULT_SERVER_ADDRESS[] = "127.0.0.1";
-constexpr u16 DEFAULT_SERVER_PORT = 26760;
-
-const Config::Info<bool> SERVER_ENABLED{{Config::System::DualShockUDPClient, "Server", "Enabled"},
- false};
-const Config::Info<std::string> SERVER_ADDRESS{
- {Config::System::DualShockUDPClient, "Server", "IPAddress"}, DEFAULT_SERVER_ADDRESS};
-const Config::Info<int> SERVER_PORT{{Config::System::DualShockUDPClient, "Server", "Port"},
- DEFAULT_SERVER_PORT};
+const Config::Info<std::string> SERVERS{{Config::System::DualShockUDPClient, "Server", "Entries"},
+ ""};
+const Config::Info<bool> SERVERS_ENABLED{{Config::System::DualShockUDPClient, "Server", "Enabled"},
+ false};
} // namespace Settings
// Clock type used for querying timeframes
@@ -130,14 +127,14 @@ private:
public:
void UpdateInput() override;
- Device(Proto::DsModel model, int index);
+ Device(std::string name, int index, std::string server_address, u16 server_port);
std::string GetName() const final override;
std::string GetSource() const final override;
std::optional<int> GetPreferredId() const final override;
private:
- const Proto::DsModel m_model;
+ const std::string m_name;
const int m_index;
u32 m_client_uid = Common::Random::GenerateValue<u32>();
sf::UdpSocket m_socket;
@@ -147,6 +144,8 @@ private:
bool m_prev_touch_valid = false;
int m_touch_x = 0;
int m_touch_y = 0;
+ std::string m_server_address;
+ u16 m_server_port;
};
using MathUtil::GRAVITY_ACCELERATION;
@@ -155,16 +154,40 @@ constexpr auto SERVER_LISTPORTS_INTERVAL = std::chrono::seconds{1};
constexpr int TOUCH_X_AXIS_MAX = 1000;
constexpr int TOUCH_Y_AXIS_MAX = 500;
-static bool s_server_enabled;
-static std::string s_server_address;
-static u16 s_server_port;
+struct Server
+{
+ Server(std::string description, std::string address, u16 port)
+ : m_description{std::move(description)}, m_address{std::move(address)}, m_port{port}
+ {
+ }
+ Server(const Server&) = delete;
+ Server(Server&& other) noexcept
+ {
+ m_description = std::move(other.m_description);
+ m_address = std::move(other.m_address);
+ m_port = other.m_port;
+ m_port_info = std::move(other.m_port_info);
+ }
+
+ Server& operator=(const Server&) = delete;
+ Server& operator=(Server&&) = delete;
+
+ ~Server() = default;
+
+ std::string m_description;
+ std::string m_address;
+ u16 m_port;
+ std::mutex m_port_info_mutex;
+ std::array<Proto::MessageType::PortInfo, Proto::PORT_COUNT> m_port_info;
+ sf::UdpSocket m_socket;
+};
+
+static bool s_servers_enabled;
+static std::vector<Server> s_servers;
static u32 s_client_uid;
static SteadyClock::time_point s_next_listports;
static std::thread s_hotplug_thread;
static Common::Flag s_hotplug_thread_running;
-static std::mutex s_port_info_mutex;
-static std::array<Proto::MessageType::PortInfo, Proto::PORT_COUNT> s_port_info;
-static sf::UdpSocket s_socket;
static bool IsSameController(const Proto::MessageType::PortInfo& a,
const Proto::MessageType::PortInfo& b)
@@ -198,39 +221,48 @@ static void HotplugThreadFunc()
{
s_next_listports = now + SERVER_LISTPORTS_INTERVAL;
- // Request info on the four controller ports
- Proto::Message<Proto::MessageType::ListPorts> msg(s_client_uid);
- auto& list_ports = msg.m_message;
- list_ports.pad_request_count = 4;
- list_ports.pad_id = {0, 1, 2, 3};
- msg.Finish();
- if (s_socket.send(&list_ports, sizeof list_ports, s_server_address, s_server_port) !=
- sf::Socket::Status::Done)
- ERROR_LOG(SERIALINTERFACE, "DualShockUDPClient HotplugThreadFunc send failed");
+ for (auto& server : s_servers)
+ {
+ // Request info on the four controller ports
+ Proto::Message<Proto::MessageType::ListPorts> msg(s_client_uid);
+ auto& list_ports = msg.m_message;
+ list_ports.pad_request_count = 4;
+ list_ports.pad_id = {0, 1, 2, 3};
+ msg.Finish();
+ if (server.m_socket.send(&list_ports, sizeof list_ports, server.m_address, server.m_port) !=
+ sf::Socket::Status::Done)
+ {
+ ERROR_LOG(SERIALINTERFACE, "DualShockUDPClient HotplugThreadFunc send failed");
+ }
+ }
}
- // Receive controller port info
- using namespace std::chrono;
- using namespace std::chrono_literals;
- Proto::Message<Proto::MessageType::FromServer> msg;
- const auto timeout = s_next_listports - SteadyClock::now();
- // ReceiveWithTimeout treats a timeout of zero as infinite timeout, which we don't want
- const auto timeout_ms = std::max(duration_cast<milliseconds>(timeout), 1ms);
- std::size_t received_bytes;
- sf::IpAddress sender;
- u16 port;
- if (ReceiveWithTimeout(s_socket, &msg, sizeof(msg), received_bytes, sender, port,
- sf::milliseconds(timeout_ms.count())) == sf::Socket::Status::Done)
+ for (auto& server : s_servers)
{
- if (auto port_info = msg.CheckAndCastTo<Proto::MessageType::PortInfo>())
+ // Receive controller port info
+ using namespace std::chrono;
+ using namespace std::chrono_literals;
+ Proto::Message<Proto::MessageType::FromServer> msg;
+ const auto timeout = s_next_listports - SteadyClock::now();
+ // ReceiveWithTimeout treats a timeout of zero as infinite timeout, which we don't want
+ const auto timeout_ms = std::max(duration_cast<milliseconds>(timeout), 1ms);
+ std::size_t received_bytes;
+ sf::IpAddress sender;
+ u16 port;
+ if (ReceiveWithTimeout(server.m_socket, &msg, sizeof(msg), received_bytes, sender, port,
+ sf::milliseconds(timeout_ms.count())) == sf::Socket::Status::Done)
{
- const bool port_changed = !IsSameController(*port_info, s_port_info[port_info->pad_id]);
+ if (auto port_info = msg.CheckAndCastTo<Proto::MessageType::PortInfo>())
{
- std::lock_guard lock{s_port_info_mutex};
- s_port_info[port_info->pad_id] = *port_info;
+ const bool port_changed =
+ !IsSameController(*port_info, server.m_port_info[port_info->pad_id]);
+ {
+ std::lock_guard lock{server.m_port_info_mutex};
+ server.m_port_info[port_info->pad_id] = *port_info;
+ }
+ if (port_changed)
+ PopulateDevices();
}
- if (port_changed)
- PopulateDevices();
}
}
}
@@ -258,7 +290,10 @@ static void StopHotplugThread()
return;
}
- s_socket.unbind(); // interrupt blocking socket
+ for (auto& server : s_servers)
+ {
+ server.m_socket.unbind(); // interrupt blocking socket
+ }
s_hotplug_thread.join();
}
@@ -270,29 +305,56 @@ static void Restart()
s_client_uid = Common::Random::GenerateValue<u32>();
s_next_listports = std::chrono::steady_clock::time_point::min();
- for (size_t port_index = 0; port_index < s_port_info.size(); port_index++)
+ for (auto& server : s_servers)
{
- s_port_info[port_index] = {};
- s_port_info[port_index].pad_id = static_cast<u8>(port_index);
+ for (size_t port_index = 0; port_index < server.m_port_info.size(); port_index++)
+ {
+ server.m_port_info[port_index] = {};
+ server.m_port_info[port_index].pad_id = static_cast<u8>(port_index);
+ }
}
PopulateDevices(); // remove devices
- if (s_server_enabled)
+ if (s_servers_enabled && !s_servers.empty())
StartHotplugThread();
}
static void ConfigChanged()
{
- bool server_enabled = Config::Get(Settings::SERVER_ENABLED);
- std::string server_address = Config::Get(Settings::SERVER_ADDRESS);
- u16 server_port = Config::Get(Settings::SERVER_PORT);
- if (server_enabled != s_server_enabled || server_address != s_server_address ||
- server_port != s_server_port)
+ const bool servers_enabled = Config::Get(Settings::SERVERS_ENABLED);
+ const std::string servers_setting = Config::Get(Settings::SERVERS);
+
+ std::string new_servers_setting;
+ for (const auto& server : s_servers)
+ {
+ new_servers_setting +=
+ fmt::format("{}:{}:{};", server.m_description, server.m_address, server.m_port);
+ }
+
+ if (servers_enabled != s_servers_enabled || servers_setting != new_servers_setting)
{
- s_server_enabled = server_enabled;
- s_server_address = server_address;
- s_server_port = server_port;
+ s_servers_enabled = servers_enabled;
+ s_servers.clear();
+
+ const auto server_details = SplitString(servers_setting, ';');
+ for (const auto& server_detail : server_details)
+ {
+ const auto server_info = SplitString(server_detail, ':');
+ if (server_info.size() < 3)
+ continue;
+
+ const std::string description = server_info[0];
+ const std::string server_address = server_info[1];
+ const auto port = std::stoi(server_info[2]);
+ if (port >= std::numeric_limits<u16>::max())
+ {
+ continue;
+ }
+ u16 server_port = static_cast<u16>(port);
+
+ s_servers.emplace_back(description, server_address, server_port);
+ }
Restart();
}
}
@@ -306,18 +368,21 @@ void PopulateDevices()
{
INFO_LOG(SERIALINTERFACE, "DualShockUDPClient PopulateDevices");
- g_controller_interface.RemoveDevice(
- [](const auto* dev) { return dev->GetSource() == "DSUClient"; });
-
- std::lock_guard lock{s_port_info_mutex};
- for (size_t port_index = 0; port_index < s_port_info.size(); port_index++)
+ for (auto& server : s_servers)
{
- const Proto::MessageType::PortInfo& port_info = s_port_info[port_index];
- if (port_info.pad_state != Proto::DsState::Connected)
- continue;
+ g_controller_interface.RemoveDevice(
+ [&server](const auto* dev) { return dev->GetName() == server.m_description; });
+
+ std::lock_guard lock{server.m_port_info_mutex};
+ for (size_t port_index = 0; port_index < server.m_port_info.size(); port_index++)
+ {
+ const Proto::MessageType::PortInfo& port_info = server.m_port_info[port_index];
+ if (port_info.pad_state != Proto::DsState::Connected)
+ continue;
- g_controller_interface.AddDevice(
- std::make_shared<Device>(port_info.model, static_cast<int>(port_index)));
+ g_controller_interface.AddDevice(std::make_shared<Device>(
+ server.m_description, static_cast<int>(port_index), server.m_address, server.m_port));
+ }
}
}
@@ -326,7 +391,9 @@ void DeInit()
StopHotplugThread();
}
-Device::Device(Proto::DsModel model, int index) : m_model{model}, m_index{index}
+Device::Device(std::string name, int index, std::string server_address, u16 server_port)
+ : m_name{std::move(name)}, m_index{index}, m_server_address{std::move(server_address)},
+ m_server_port{server_port}
{
m_socket.setBlocking(false);
@@ -388,19 +455,7 @@ Device::Device(Proto::DsModel model, int index) : m_model{model}, m_index{index}
std::string Device::GetName() const
{
- switch (m_model)
- {
- case Proto::DsModel::None:
- return "None";
- case Proto::DsModel::DS3:
- return "DualShock 3";
- case Proto::DsModel::DS4:
- return "DualShock 4";
- case Proto::DsModel::Generic:
- return "Generic Gamepad";
- default:
- return "Device";
- }
+ return m_name;
}
std::string Device::GetSource() const
@@ -421,7 +476,7 @@ void Device::UpdateInput()
data_req.register_flags = Proto::RegisterFlags::PadID;
data_req.pad_id_to_register = m_index;
msg.Finish();
- if (m_socket.send(&data_req, sizeof(data_req), s_server_address, s_server_port) !=
+ if (m_socket.send(&data_req, sizeof(data_req), m_server_address, m_server_port) !=
sf::Socket::Status::Done)
ERROR_LOG(SERIALINTERFACE, "DualShockUDPClient UpdateInput send failed");
}
diff --git a/Source/Core/InputCommon/ControllerInterface/DualShockUDPClient/DualShockUDPClient.h b/Source/Core/InputCommon/ControllerInterface/DualShockUDPClient/DualShockUDPClient.h
index 1b51a2e1b9..e7a9057569 100644
--- a/Source/Core/InputCommon/ControllerInterface/DualShockUDPClient/DualShockUDPClient.h
+++ b/Source/Core/InputCommon/ControllerInterface/DualShockUDPClient/DualShockUDPClient.h
@@ -8,11 +8,13 @@
namespace ciface::DualShockUDPClient
{
+constexpr char DEFAULT_SERVER_ADDRESS[] = "127.0.0.1";
+constexpr u16 DEFAULT_SERVER_PORT = 26760;
+
namespace Settings
{
-extern const Config::Info<bool> SERVER_ENABLED;
-extern const Config::Info<std::string> SERVER_ADDRESS;
-extern const Config::Info<int> SERVER_PORT;
+extern const Config::Info<std::string> SERVERS;
+extern const Config::Info<bool> SERVERS_ENABLED;
} // namespace Settings
void Init();
diff --git a/Source/Core/InputCommon/ControllerInterface/DualShockUDPClient/DualShockUDPProto.h b/Source/Core/InputCommon/ControllerInterface/DualShockUDPClient/DualShockUDPProto.h
index 5e3e9f0dde..97b453a68a 100644
--- a/Source/Core/InputCommon/ControllerInterface/DualShockUDPClient/DualShockUDPProto.h
+++ b/Source/Core/InputCommon/ControllerInterface/DualShockUDPClient/DualShockUDPProto.h
@@ -45,8 +45,8 @@ enum class DsConnection : u8
enum class DsModel : u8
{
None = 0,
- DS3 = 1,
- DS4 = 2,
+ PartialGyro = 1,
+ FullGyro = 2,
Generic = 3
};