diff --git a/src/input_common/udp/client.cpp b/src/input_common/udp/client.cpp index df73f9ff7b..e72df924b2 100644 --- a/src/input_common/udp/client.cpp +++ b/src/input_common/udp/client.cpp @@ -27,11 +27,9 @@ class Socket { public: using clock = std::chrono::system_clock; - explicit Socket(const std::string& host, u16 port, std::size_t pad_index_, - SocketCallback callback_) + explicit Socket(const std::string& host, u16 port, SocketCallback callback_) : callback(std::move(callback_)), timer(io_service), - socket(io_service, udp::endpoint(udp::v4(), 0)), client_id(GenerateRandomClientId()), - pad_index(pad_index_) { + socket(io_service, udp::endpoint(udp::v4(), 0)), client_id(GenerateRandomClientId()) { boost::system::error_code ec{}; auto ipv4 = boost::asio::ip::make_address_v4(host, ec); if (ec.value() != boost::system::errc::success) { @@ -99,15 +97,15 @@ private: void HandleSend(const boost::system::error_code&) { boost::system::error_code _ignored{}; // Send a request for getting port info for the pad - const Request::PortInfo port_info{1, {static_cast(pad_index), 0, 0, 0}}; + const Request::PortInfo port_info{4, {0, 1, 2, 3}}; const auto port_message = Request::Create(port_info, client_id); std::memcpy(&send_buffer1, &port_message, PORT_INFO_SIZE); socket.send_to(boost::asio::buffer(send_buffer1), send_endpoint, {}, _ignored); // Send a request for getting pad data for the pad const Request::PadData pad_data{ - Request::PadData::Flags::Id, - static_cast(pad_index), + Request::PadData::Flags::AllPorts, + 0, EMPTY_MAC_ADDRESS, }; const auto pad_message = Request::Create(pad_data, client_id); @@ -122,7 +120,6 @@ private: udp::socket socket; const u32 client_id; - std::size_t pad_index{}; static constexpr std::size_t PORT_INFO_SIZE = sizeof(Message); static constexpr std::size_t PAD_DATA_SIZE = sizeof(Message); @@ -150,34 +147,32 @@ Client::~Client() { Reset(); } -Client::ClientData::ClientData() = default; +Client::ClientConnection::ClientConnection() = default; -Client::ClientData::~ClientData() = default; +Client::ClientConnection::~ClientConnection() = default; std::vector Client::GetInputDevices() const { std::vector devices; - for (std::size_t client = 0; client < clients.size(); client++) { - if (!DeviceConnected(client)) { + for (std::size_t pad = 0; pad < pads.size(); pad++) { + if (!DeviceConnected(pad)) { continue; } - std::string name = fmt::format("UDP Controller {}", client); + std::string name = fmt::format("UDP Controller {}", pad); devices.emplace_back(Common::ParamPackage{ {"class", "cemuhookudp"}, {"display", std::move(name)}, - {"port", std::to_string(client)}, + {"port", std::to_string(pad)}, }); } return devices; } -bool Client::DeviceConnected(std::size_t client) const { +bool Client::DeviceConnected(std::size_t pad) const { // Use last timestamp to detect if the socket has stopped sending data const auto now = std::chrono::steady_clock::now(); - const auto time_difference = - static_cast(std::chrono::duration_cast( - now - clients[client].last_motion_update) - .count()); - return time_difference < 1000 && clients[client].active == 1; + const auto time_difference = static_cast( + std::chrono::duration_cast(now - pads[pad].last_update).count()); + return time_difference < 1000 && pads[pad].connected; } void Client::ReloadSockets() { @@ -202,25 +197,21 @@ void Client::ReloadSockets() { continue; } - for (std::size_t pad = 0; pad < 4; ++pad) { - const std::size_t client_number = - GetClientNumber(udp_input_address, udp_input_port, pad); - if (client_number != MAX_UDP_CLIENTS) { - LOG_ERROR(Input, "Duplicated UDP servers found"); - continue; - } - StartCommunication(client++, udp_input_address, udp_input_port, pad); + const std::size_t client_number = GetClientNumber(udp_input_address, udp_input_port); + if (client_number != MAX_UDP_CLIENTS) { + LOG_ERROR(Input, "Duplicated UDP servers found"); + continue; } + StartCommunication(client++, udp_input_address, udp_input_port); } } -std::size_t Client::GetClientNumber(std::string_view host, u16 port, std::size_t pad) const { +std::size_t Client::GetClientNumber(std::string_view host, u16 port) const { for (std::size_t client = 0; client < clients.size(); client++) { if (clients[client].active == -1) { continue; } - if (clients[client].host == host && clients[client].port == port && - clients[client].pad_index == pad) { + if (clients[client].host == host && clients[client].port == port) { return client; } } @@ -236,69 +227,75 @@ void Client::OnPortInfo([[maybe_unused]] Response::PortInfo data) { } void Client::OnPadData(Response::PadData data, std::size_t client) { - // Accept packets only for the correct pad - if (static_cast(clients[client].pad_index) != data.info.id) { + const std::size_t pad_index = (client * PADS_PER_CLIENT) + data.info.id; + + if (pad_index >= pads.size()) { + LOG_ERROR(Input, "Invalid pad id {}", data.info.id); return; } LOG_TRACE(Input, "PadData packet received"); - if (data.packet_counter == clients[client].packet_sequence) { + if (data.packet_counter == pads[pad_index].packet_sequence) { LOG_WARNING( Input, "PadData packet dropped because its stale info. Current count: {} Packet count: {}", - clients[client].packet_sequence, data.packet_counter); + pads[pad_index].packet_sequence, data.packet_counter); + pads[pad_index].connected = false; return; } - clients[client].active = static_cast(data.info.is_pad_active); - clients[client].packet_sequence = data.packet_counter; + + clients[client].active = 1; + pads[pad_index].connected = true; + pads[pad_index].packet_sequence = data.packet_counter; + const auto now = std::chrono::steady_clock::now(); - const auto time_difference = - static_cast(std::chrono::duration_cast( - now - clients[client].last_motion_update) - .count()); - clients[client].last_motion_update = now; + const auto time_difference = static_cast( + std::chrono::duration_cast(now - pads[pad_index].last_update) + .count()); + pads[pad_index].last_update = now; + const Common::Vec3f raw_gyroscope = {data.gyro.pitch, data.gyro.roll, -data.gyro.yaw}; - clients[client].motion.SetAcceleration({data.accel.x, -data.accel.z, data.accel.y}); + pads[pad_index].motion.SetAcceleration({data.accel.x, -data.accel.z, data.accel.y}); // Gyroscope values are not it the correct scale from better joy. // Dividing by 312 allows us to make one full turn = 1 turn // This must be a configurable valued called sensitivity - clients[client].motion.SetGyroscope(raw_gyroscope / 312.0f); - clients[client].motion.UpdateRotation(time_difference); - clients[client].motion.UpdateOrientation(time_difference); + pads[pad_index].motion.SetGyroscope(raw_gyroscope / 312.0f); + pads[pad_index].motion.UpdateRotation(time_difference); + pads[pad_index].motion.UpdateOrientation(time_difference); { - std::lock_guard guard(clients[client].status.update_mutex); - clients[client].status.motion_status = clients[client].motion.GetMotion(); + std::lock_guard guard(pads[pad_index].status.update_mutex); + pads[pad_index].status.motion_status = pads[pad_index].motion.GetMotion(); for (std::size_t id = 0; id < data.touch.size(); ++id) { UpdateTouchInput(data.touch[id], client, id); } if (configuring) { - const Common::Vec3f gyroscope = clients[client].motion.GetGyroscope(); - const Common::Vec3f accelerometer = clients[client].motion.GetAcceleration(); - UpdateYuzuSettings(client, accelerometer, gyroscope); + const Common::Vec3f gyroscope = pads[pad_index].motion.GetGyroscope(); + const Common::Vec3f accelerometer = pads[pad_index].motion.GetAcceleration(); + UpdateYuzuSettings(client, data.info.id, accelerometer, gyroscope); } } } -void Client::StartCommunication(std::size_t client, const std::string& host, u16 port, - std::size_t pad_index) { +void Client::StartCommunication(std::size_t client, const std::string& host, u16 port) { SocketCallback callback{[this](Response::Version version) { OnVersion(version); }, [this](Response::PortInfo info) { OnPortInfo(info); }, [this, client](Response::PadData data) { OnPadData(data, client); }}; - LOG_INFO(Input, "Starting communication with UDP input server on {}:{}:{}", host, port, - pad_index); + LOG_INFO(Input, "Starting communication with UDP input server on {}:{}", host, port); clients[client].host = host; clients[client].port = port; - clients[client].pad_index = pad_index; clients[client].active = 0; - clients[client].socket = std::make_unique(host, port, pad_index, callback); + clients[client].socket = std::make_unique(host, port, callback); clients[client].thread = std::thread{SocketLoop, clients[client].socket.get()}; + // Set motion parameters // SetGyroThreshold value should be dependent on GyroscopeZeroDriftMode // Real HW values are unknown, 0.0001 is an approximate to Standard - clients[client].motion.SetGyroThreshold(0.0001f); + for (std::size_t pad = 0; pad < PADS_PER_CLIENT; pad++) { + pads[client * PADS_PER_CLIENT + pad].motion.SetGyroThreshold(0.0001f); + } } void Client::Reset() { @@ -311,8 +308,8 @@ void Client::Reset() { } } -void Client::UpdateYuzuSettings(std::size_t client, const Common::Vec3& acc, - const Common::Vec3& gyro) { +void Client::UpdateYuzuSettings(std::size_t client, std::size_t pad_index, + const Common::Vec3& acc, const Common::Vec3& gyro) { if (gyro.Length() > 0.2f) { LOG_DEBUG(Input, "UDP Controller {}: gyro=({}, {}, {}), accel=({}, {}, {})", client, gyro[0], gyro[1], gyro[2], acc[0], acc[1], acc[2]); @@ -320,7 +317,7 @@ void Client::UpdateYuzuSettings(std::size_t client, const Common::Vec3& a UDPPadStatus pad{ .host = clients[client].host, .port = clients[client].port, - .pad_index = clients[client].pad_index, + .pad_index = pad_index, }; for (std::size_t i = 0; i < 3; ++i) { if (gyro[i] > 5.0f || gyro[i] < -5.0f) { @@ -391,19 +388,19 @@ void Client::EndConfiguration() { } DeviceStatus& Client::GetPadState(const std::string& host, u16 port, std::size_t pad) { - const std::size_t client_number = GetClientNumber(host, port, pad); - if (client_number == MAX_UDP_CLIENTS) { - return clients[0].status; + const std::size_t client_number = GetClientNumber(host, port); + if (client_number == MAX_UDP_CLIENTS || pad >= PADS_PER_CLIENT) { + return pads[0].status; } - return clients[client_number].status; + return pads[(client_number * PADS_PER_CLIENT) + pad].status; } const DeviceStatus& Client::GetPadState(const std::string& host, u16 port, std::size_t pad) const { - const std::size_t client_number = GetClientNumber(host, port, pad); - if (client_number == MAX_UDP_CLIENTS) { - return clients[0].status; + const std::size_t client_number = GetClientNumber(host, port); + if (client_number == MAX_UDP_CLIENTS || pad >= PADS_PER_CLIENT) { + return pads[0].status; } - return clients[client_number].status; + return pads[(client_number * PADS_PER_CLIENT) + pad].status; } Input::TouchStatus& Client::GetTouchState() { @@ -422,7 +419,7 @@ const Common::SPSCQueue& Client::GetPadQueue() const { return pad_queue; } -void TestCommunication(const std::string& host, u16 port, std::size_t pad_index, +void TestCommunication(const std::string& host, u16 port, const std::function& success_callback, const std::function& failure_callback) { std::thread([=] { @@ -432,9 +429,10 @@ void TestCommunication(const std::string& host, u16 port, std::size_t pad_index, .port_info = [](Response::PortInfo) {}, .pad_data = [&](Response::PadData) { success_event.Set(); }, }; - Socket socket{host, port, pad_index, std::move(callback)}; + Socket socket{host, port, std::move(callback)}; std::thread worker_thread{SocketLoop, &socket}; - const bool result = success_event.WaitFor(std::chrono::seconds(5)); + const bool result = + success_event.WaitUntil(std::chrono::steady_clock::now() + std::chrono::seconds(10)); socket.Stop(); worker_thread.join(); if (result) { @@ -446,8 +444,7 @@ void TestCommunication(const std::string& host, u16 port, std::size_t pad_index, } CalibrationConfigurationJob::CalibrationConfigurationJob( - const std::string& host, u16 port, std::size_t pad_index, - std::function status_callback, + const std::string& host, u16 port, std::function status_callback, std::function data_callback) { std::thread([=, this] { @@ -491,7 +488,7 @@ CalibrationConfigurationJob::CalibrationConfigurationJob( complete_event.Set(); } }}; - Socket socket{host, port, pad_index, std::move(callback)}; + Socket socket{host, port, std::move(callback)}; std::thread worker_thread{SocketLoop, &socket}; complete_event.Wait(); socket.Stop(); diff --git a/src/input_common/udp/client.h b/src/input_common/udp/client.h index e9e438e884..a11ea30689 100644 --- a/src/input_common/udp/client.h +++ b/src/input_common/udp/client.h @@ -84,7 +84,7 @@ public: std::vector GetInputDevices() const; - bool DeviceConnected(std::size_t client) const; + bool DeviceConnected(std::size_t pad) const; void ReloadSockets(); Common::SPSCQueue& GetPadQueue(); @@ -97,38 +97,40 @@ public: const Input::TouchStatus& GetTouchState() const; private: - struct ClientData { - ClientData(); - ~ClientData(); - - std::string host{"127.0.0.1"}; - u16 port{26760}; + struct PadData { std::size_t pad_index{}; - std::unique_ptr socket; + bool connected{}; DeviceStatus status; - std::thread thread; u64 packet_sequence{}; - s8 active{-1}; // Realtime values // motion is initalized with PID values for drift correction on joycons InputCommon::MotionInput motion{0.3f, 0.005f, 0.0f}; - std::chrono::time_point last_motion_update; + std::chrono::time_point last_update; + }; + + struct ClientConnection { + ClientConnection(); + ~ClientConnection(); + std::string host{"127.0.0.1"}; + u16 port{26760}; + s8 active{-1}; + std::unique_ptr socket; + std::thread thread; }; // For shutting down, clear all data, join all threads, release usb void Reset(); // Translates configuration to client number - std::size_t GetClientNumber(std::string_view host, u16 port, std::size_t pad) const; + std::size_t GetClientNumber(std::string_view host, u16 port) const; void OnVersion(Response::Version); void OnPortInfo(Response::PortInfo); void OnPadData(Response::PadData, std::size_t client); - void StartCommunication(std::size_t client, const std::string& host, u16 port, - std::size_t pad_index); - void UpdateYuzuSettings(std::size_t client, const Common::Vec3& acc, - const Common::Vec3& gyro); + void StartCommunication(std::size_t client, const std::string& host, u16 port); + void UpdateYuzuSettings(std::size_t client, std::size_t pad_index, + const Common::Vec3& acc, const Common::Vec3& gyro); // Returns an unused finger id, if there is no fingers available std::nullopt will be // returned @@ -140,10 +142,12 @@ private: bool configuring = false; // Allocate clients for 8 udp servers - static constexpr std::size_t MAX_UDP_CLIENTS = 4 * 8; + static constexpr std::size_t MAX_UDP_CLIENTS = 8; + static constexpr std::size_t PADS_PER_CLIENT = 4; // Each client can have up 2 touch inputs static constexpr std::size_t MAX_TOUCH_FINGERS = MAX_UDP_CLIENTS * 2; - std::array clients{}; + std::array pads{}; + std::array clients{}; Common::SPSCQueue pad_queue{}; Input::TouchStatus touch_status{}; std::array finger_id{}; @@ -164,7 +168,7 @@ public: * @param status_callback Callback for job status updates * @param data_callback Called when calibration data is ready */ - explicit CalibrationConfigurationJob(const std::string& host, u16 port, std::size_t pad_index, + explicit CalibrationConfigurationJob(const std::string& host, u16 port, std::function status_callback, std::function data_callback); ~CalibrationConfigurationJob(); @@ -174,7 +178,7 @@ private: Common::Event complete_event; }; -void TestCommunication(const std::string& host, u16 port, std::size_t pad_index, +void TestCommunication(const std::string& host, u16 port, const std::function& success_callback, const std::function& failure_callback); diff --git a/src/yuzu/configuration/configure_motion_touch.cpp b/src/yuzu/configuration/configure_motion_touch.cpp index 52fdf72653..083d1ea43d 100644 --- a/src/yuzu/configuration/configure_motion_touch.cpp +++ b/src/yuzu/configuration/configure_motion_touch.cpp @@ -23,8 +23,7 @@ #include "yuzu/configuration/configure_touch_from_button.h" CalibrationConfigurationDialog::CalibrationConfigurationDialog(QWidget* parent, - const std::string& host, u16 port, - u8 pad_index) + const std::string& host, u16 port) : QDialog(parent) { layout = new QVBoxLayout; status_label = new QLabel(tr("Communicating with the server...")); @@ -41,7 +40,7 @@ CalibrationConfigurationDialog::CalibrationConfigurationDialog(QWidget* parent, using namespace InputCommon::CemuhookUDP; job = std::make_unique( - host, port, pad_index, + host, port, [this](CalibrationConfigurationJob::Status status) { QString text; switch (status) { @@ -217,7 +216,7 @@ void ConfigureMotionTouch::OnCemuhookUDPTest() { ui->udp_test->setText(tr("Testing")); udp_test_in_progress = true; InputCommon::CemuhookUDP::TestCommunication( - ui->udp_server->text().toStdString(), static_cast(ui->udp_port->text().toInt()), 0, + ui->udp_server->text().toStdString(), static_cast(ui->udp_port->text().toInt()), [this] { LOG_INFO(Frontend, "UDP input test success"); QMetaObject::invokeMethod(this, "ShowUDPTestResult", Q_ARG(bool, true)); @@ -232,7 +231,7 @@ void ConfigureMotionTouch::OnConfigureTouchCalibration() { ui->touch_calibration_config->setEnabled(false); ui->touch_calibration_config->setText(tr("Configuring")); CalibrationConfigurationDialog dialog(this, ui->udp_server->text().toStdString(), - static_cast(ui->udp_port->text().toUInt()), 0); + static_cast(ui->udp_port->text().toUInt())); dialog.exec(); if (dialog.completed) { min_x = dialog.min_x; diff --git a/src/yuzu/configuration/configure_motion_touch.h b/src/yuzu/configuration/configure_motion_touch.h index d76bc81541..8b707d2ffb 100644 --- a/src/yuzu/configuration/configure_motion_touch.h +++ b/src/yuzu/configuration/configure_motion_touch.h @@ -29,8 +29,7 @@ class ConfigureMotionTouch; class CalibrationConfigurationDialog : public QDialog { Q_OBJECT public: - explicit CalibrationConfigurationDialog(QWidget* parent, const std::string& host, u16 port, - u8 pad_index); + explicit CalibrationConfigurationDialog(QWidget* parent, const std::string& host, u16 port); ~CalibrationConfigurationDialog() override; private: