summaryrefslogtreecommitdiff
path: root/src/input_common/udp
diff options
context:
space:
mode:
authorGravatar Levi2021-01-10 22:09:56 -0700
committerGravatar Levi2021-01-10 22:09:56 -0700
commit7a3c884e39fccfbb498b855080bffabc9ce2e7f1 (patch)
tree5056f9406dec188439cb0deb87603498243a9412 /src/input_common/udp
parentMore forgetting... duh (diff)
parentMerge pull request #5229 from Morph1984/fullscreen-opt (diff)
downloadyuzu-7a3c884e39fccfbb498b855080bffabc9ce2e7f1.tar.gz
yuzu-7a3c884e39fccfbb498b855080bffabc9ce2e7f1.tar.xz
yuzu-7a3c884e39fccfbb498b855080bffabc9ce2e7f1.zip
Merge remote-tracking branch 'upstream/master' into int-flags
Diffstat (limited to 'src/input_common/udp')
-rw-r--r--src/input_common/udp/client.cpp234
-rw-r--r--src/input_common/udp/client.h52
-rw-r--r--src/input_common/udp/protocol.h11
-rw-r--r--src/input_common/udp/udp.cpp84
4 files changed, 227 insertions, 154 deletions
diff --git a/src/input_common/udp/client.cpp b/src/input_common/udp/client.cpp
index 2b6a68d4b..412d57896 100644
--- a/src/input_common/udp/client.cpp
+++ b/src/input_common/udp/client.cpp
@@ -26,11 +26,11 @@ class Socket {
26public: 26public:
27 using clock = std::chrono::system_clock; 27 using clock = std::chrono::system_clock;
28 28
29 explicit Socket(const std::string& host, u16 port, u8 pad_index, u32 client_id, 29 explicit Socket(const std::string& host, u16 port, std::size_t pad_index_, u32 client_id_,
30 SocketCallback callback) 30 SocketCallback callback_)
31 : callback(std::move(callback)), timer(io_service), 31 : callback(std::move(callback_)), timer(io_service),
32 socket(io_service, udp::endpoint(udp::v4(), 0)), client_id(client_id), 32 socket(io_service, udp::endpoint(udp::v4(), 0)), client_id(client_id_),
33 pad_index(pad_index) { 33 pad_index(pad_index_) {
34 boost::system::error_code ec{}; 34 boost::system::error_code ec{};
35 auto ipv4 = boost::asio::ip::make_address_v4(host, ec); 35 auto ipv4 = boost::asio::ip::make_address_v4(host, ec);
36 if (ec.value() != boost::system::errc::success) { 36 if (ec.value() != boost::system::errc::success) {
@@ -63,7 +63,7 @@ public:
63 } 63 }
64 64
65private: 65private:
66 void HandleReceive(const boost::system::error_code& error, std::size_t bytes_transferred) { 66 void HandleReceive(const boost::system::error_code&, std::size_t bytes_transferred) {
67 if (auto type = Response::Validate(receive_buffer.data(), bytes_transferred)) { 67 if (auto type = Response::Validate(receive_buffer.data(), bytes_transferred)) {
68 switch (*type) { 68 switch (*type) {
69 case Type::Version: { 69 case Type::Version: {
@@ -90,16 +90,20 @@ private:
90 StartReceive(); 90 StartReceive();
91 } 91 }
92 92
93 void HandleSend(const boost::system::error_code& error) { 93 void HandleSend(const boost::system::error_code&) {
94 boost::system::error_code _ignored{}; 94 boost::system::error_code _ignored{};
95 // Send a request for getting port info for the pad 95 // Send a request for getting port info for the pad
96 Request::PortInfo port_info{1, {pad_index, 0, 0, 0}}; 96 const Request::PortInfo port_info{1, {static_cast<u8>(pad_index), 0, 0, 0}};
97 const auto port_message = Request::Create(port_info, client_id); 97 const auto port_message = Request::Create(port_info, client_id);
98 std::memcpy(&send_buffer1, &port_message, PORT_INFO_SIZE); 98 std::memcpy(&send_buffer1, &port_message, PORT_INFO_SIZE);
99 socket.send_to(boost::asio::buffer(send_buffer1), send_endpoint, {}, _ignored); 99 socket.send_to(boost::asio::buffer(send_buffer1), send_endpoint, {}, _ignored);
100 100
101 // Send a request for getting pad data for the pad 101 // Send a request for getting pad data for the pad
102 Request::PadData pad_data{Request::PadData::Flags::Id, pad_index, EMPTY_MAC_ADDRESS}; 102 const Request::PadData pad_data{
103 Request::PadData::Flags::Id,
104 static_cast<u8>(pad_index),
105 EMPTY_MAC_ADDRESS,
106 };
103 const auto pad_message = Request::Create(pad_data, client_id); 107 const auto pad_message = Request::Create(pad_data, client_id);
104 std::memcpy(send_buffer2.data(), &pad_message, PAD_DATA_SIZE); 108 std::memcpy(send_buffer2.data(), &pad_message, PAD_DATA_SIZE);
105 socket.send_to(boost::asio::buffer(send_buffer2), send_endpoint, {}, _ignored); 109 socket.send_to(boost::asio::buffer(send_buffer2), send_endpoint, {}, _ignored);
@@ -112,7 +116,7 @@ private:
112 udp::socket socket; 116 udp::socket socket;
113 117
114 u32 client_id{}; 118 u32 client_id{};
115 u8 pad_index{}; 119 std::size_t pad_index{};
116 120
117 static constexpr std::size_t PORT_INFO_SIZE = sizeof(Message<Request::PortInfo>); 121 static constexpr std::size_t PORT_INFO_SIZE = sizeof(Message<Request::PortInfo>);
118 static constexpr std::size_t PAD_DATA_SIZE = sizeof(Message<Request::PadData>); 122 static constexpr std::size_t PAD_DATA_SIZE = sizeof(Message<Request::PadData>);
@@ -132,15 +136,7 @@ static void SocketLoop(Socket* socket) {
132 136
133Client::Client() { 137Client::Client() {
134 LOG_INFO(Input, "Udp Initialization started"); 138 LOG_INFO(Input, "Udp Initialization started");
135 for (std::size_t client = 0; client < clients.size(); client++) { 139 ReloadSockets();
136 u8 pad = client % 4;
137 StartCommunication(client, Settings::values.udp_input_address,
138 Settings::values.udp_input_port, pad, 24872);
139 // Set motion parameters
140 // SetGyroThreshold value should be dependent on GyroscopeZeroDriftMode
141 // Real HW values are unknown, 0.0001 is an approximate to Standard
142 clients[client].motion.SetGyroThreshold(0.0001f);
143 }
144} 140}
145 141
146Client::~Client() { 142Client::~Client() {
@@ -163,39 +159,77 @@ std::vector<Common::ParamPackage> Client::GetInputDevices() const {
163 return devices; 159 return devices;
164} 160}
165 161
166bool Client::DeviceConnected(std::size_t pad) const { 162bool Client::DeviceConnected(std::size_t client) const {
167 // Use last timestamp to detect if the socket has stopped sending data 163 // Use last timestamp to detect if the socket has stopped sending data
168 const auto now = std::chrono::system_clock::now(); 164 const auto now = std::chrono::steady_clock::now();
169 u64 time_difference = 165 const auto time_difference =
170 std::chrono::duration_cast<std::chrono::milliseconds>(now - clients[pad].last_motion_update) 166 static_cast<u64>(std::chrono::duration_cast<std::chrono::milliseconds>(
171 .count(); 167 now - clients[client].last_motion_update)
172 return time_difference < 1000 && clients[pad].active == 1; 168 .count());
169 return time_difference < 1000 && clients[client].active == 1;
173} 170}
174 171
175void Client::ReloadUDPClient() { 172void Client::ReloadSockets() {
176 for (std::size_t client = 0; client < clients.size(); client++) { 173 Reset();
177 ReloadSocket(Settings::values.udp_input_address, Settings::values.udp_input_port, client); 174
175 std::stringstream servers_ss(Settings::values.udp_input_servers);
176 std::string server_token;
177 std::size_t client = 0;
178 while (std::getline(servers_ss, server_token, ',')) {
179 if (client == max_udp_clients) {
180 break;
181 }
182 std::stringstream server_ss(server_token);
183 std::string token;
184 std::getline(server_ss, token, ':');
185 std::string udp_input_address = token;
186 std::getline(server_ss, token, ':');
187 char* temp;
188 const u16 udp_input_port = static_cast<u16>(std::strtol(token.c_str(), &temp, 0));
189 if (*temp != '\0') {
190 LOG_ERROR(Input, "Port number is not valid {}", token);
191 continue;
192 }
193
194 for (std::size_t pad = 0; pad < 4; ++pad) {
195 const std::size_t client_number =
196 GetClientNumber(udp_input_address, udp_input_port, pad);
197 if (client_number != max_udp_clients) {
198 LOG_ERROR(Input, "Duplicated UDP servers found");
199 continue;
200 }
201 StartCommunication(client++, udp_input_address, udp_input_port, pad, 24872);
202 }
178 } 203 }
179} 204}
180void Client::ReloadSocket(const std::string& host, u16 port, u8 pad_index, u32 client_id) { 205
181 // client number must be determined from host / port and pad index 206std::size_t Client::GetClientNumber(std::string_view host, u16 port, std::size_t pad) const {
182 std::size_t client = pad_index; 207 for (std::size_t client = 0; client < clients.size(); client++) {
183 clients[client].socket->Stop(); 208 if (clients[client].active == -1) {
184 clients[client].thread.join(); 209 continue;
185 StartCommunication(client, host, port, pad_index, client_id); 210 }
211 if (clients[client].host == host && clients[client].port == port &&
212 clients[client].pad_index == pad) {
213 return client;
214 }
215 }
216 return max_udp_clients;
186} 217}
187 218
188void Client::OnVersion(Response::Version data) { 219void Client::OnVersion([[maybe_unused]] Response::Version data) {
189 LOG_TRACE(Input, "Version packet received: {}", data.version); 220 LOG_TRACE(Input, "Version packet received: {}", data.version);
190} 221}
191 222
192void Client::OnPortInfo(Response::PortInfo data) { 223void Client::OnPortInfo([[maybe_unused]] Response::PortInfo data) {
193 LOG_TRACE(Input, "PortInfo packet received: {}", data.model); 224 LOG_TRACE(Input, "PortInfo packet received: {}", data.model);
194} 225}
195 226
196void Client::OnPadData(Response::PadData data) { 227void Client::OnPadData(Response::PadData data, std::size_t client) {
197 // client number must be determined from host / port and pad index 228 // Accept packets only for the correct pad
198 std::size_t client = data.info.id; 229 if (static_cast<u8>(clients[client].pad_index) != data.info.id) {
230 return;
231 }
232
199 LOG_TRACE(Input, "PadData packet received"); 233 LOG_TRACE(Input, "PadData packet received");
200 if (data.packet_counter == clients[client].packet_sequence) { 234 if (data.packet_counter == clients[client].packet_sequence) {
201 LOG_WARNING( 235 LOG_WARNING(
@@ -204,14 +238,15 @@ void Client::OnPadData(Response::PadData data) {
204 clients[client].packet_sequence, data.packet_counter); 238 clients[client].packet_sequence, data.packet_counter);
205 return; 239 return;
206 } 240 }
207 clients[client].active = data.info.is_pad_active; 241 clients[client].active = static_cast<s8>(data.info.is_pad_active);
208 clients[client].packet_sequence = data.packet_counter; 242 clients[client].packet_sequence = data.packet_counter;
209 const auto now = std::chrono::system_clock::now(); 243 const auto now = std::chrono::steady_clock::now();
210 u64 time_difference = std::chrono::duration_cast<std::chrono::microseconds>( 244 const auto time_difference =
211 now - clients[client].last_motion_update) 245 static_cast<u64>(std::chrono::duration_cast<std::chrono::microseconds>(
212 .count(); 246 now - clients[client].last_motion_update)
247 .count());
213 clients[client].last_motion_update = now; 248 clients[client].last_motion_update = now;
214 Common::Vec3f raw_gyroscope = {data.gyro.pitch, data.gyro.roll, -data.gyro.yaw}; 249 const Common::Vec3f raw_gyroscope = {data.gyro.pitch, data.gyro.roll, -data.gyro.yaw};
215 clients[client].motion.SetAcceleration({data.accel.x, -data.accel.z, data.accel.y}); 250 clients[client].motion.SetAcceleration({data.accel.x, -data.accel.z, data.accel.y});
216 // Gyroscope values are not it the correct scale from better joy. 251 // Gyroscope values are not it the correct scale from better joy.
217 // Dividing by 312 allows us to make one full turn = 1 turn 252 // Dividing by 312 allows us to make one full turn = 1 turn
@@ -219,14 +254,10 @@ void Client::OnPadData(Response::PadData data) {
219 clients[client].motion.SetGyroscope(raw_gyroscope / 312.0f); 254 clients[client].motion.SetGyroscope(raw_gyroscope / 312.0f);
220 clients[client].motion.UpdateRotation(time_difference); 255 clients[client].motion.UpdateRotation(time_difference);
221 clients[client].motion.UpdateOrientation(time_difference); 256 clients[client].motion.UpdateOrientation(time_difference);
222 Common::Vec3f gyroscope = clients[client].motion.GetGyroscope();
223 Common::Vec3f accelerometer = clients[client].motion.GetAcceleration();
224 Common::Vec3f rotation = clients[client].motion.GetRotations();
225 std::array<Common::Vec3f, 3> orientation = clients[client].motion.GetOrientation();
226 257
227 { 258 {
228 std::lock_guard guard(clients[client].status.update_mutex); 259 std::lock_guard guard(clients[client].status.update_mutex);
229 clients[client].status.motion_status = {accelerometer, gyroscope, rotation, orientation}; 260 clients[client].status.motion_status = clients[client].motion.GetMotion();
230 261
231 // TODO: add a setting for "click" touch. Click touch refers to a device that differentiates 262 // TODO: add a setting for "click" touch. Click touch refers to a device that differentiates
232 // between a simple "tap" and a hard press that causes the touch screen to click. 263 // between a simple "tap" and a hard press that causes the touch screen to click.
@@ -241,98 +272,129 @@ void Client::OnPadData(Response::PadData data) {
241 const u16 min_y = clients[client].status.touch_calibration->min_y; 272 const u16 min_y = clients[client].status.touch_calibration->min_y;
242 const u16 max_y = clients[client].status.touch_calibration->max_y; 273 const u16 max_y = clients[client].status.touch_calibration->max_y;
243 274
244 x = (std::clamp(static_cast<u16>(data.touch_1.x), min_x, max_x) - min_x) / 275 x = static_cast<float>(std::clamp(static_cast<u16>(data.touch_1.x), min_x, max_x) -
276 min_x) /
245 static_cast<float>(max_x - min_x); 277 static_cast<float>(max_x - min_x);
246 y = (std::clamp(static_cast<u16>(data.touch_1.y), min_y, max_y) - min_y) / 278 y = static_cast<float>(std::clamp(static_cast<u16>(data.touch_1.y), min_y, max_y) -
279 min_y) /
247 static_cast<float>(max_y - min_y); 280 static_cast<float>(max_y - min_y);
248 } 281 }
249 282
250 clients[client].status.touch_status = {x, y, is_active}; 283 clients[client].status.touch_status = {x, y, is_active};
251 284
252 if (configuring) { 285 if (configuring) {
286 const Common::Vec3f gyroscope = clients[client].motion.GetGyroscope();
287 const Common::Vec3f accelerometer = clients[client].motion.GetAcceleration();
253 UpdateYuzuSettings(client, accelerometer, gyroscope, is_active); 288 UpdateYuzuSettings(client, accelerometer, gyroscope, is_active);
254 } 289 }
255 } 290 }
256} 291}
257 292
258void Client::StartCommunication(std::size_t client, const std::string& host, u16 port, u8 pad_index, 293void Client::StartCommunication(std::size_t client, const std::string& host, u16 port,
259 u32 client_id) { 294 std::size_t pad_index, u32 client_id) {
260 SocketCallback callback{[this](Response::Version version) { OnVersion(version); }, 295 SocketCallback callback{[this](Response::Version version) { OnVersion(version); },
261 [this](Response::PortInfo info) { OnPortInfo(info); }, 296 [this](Response::PortInfo info) { OnPortInfo(info); },
262 [this](Response::PadData data) { OnPadData(data); }}; 297 [this, client](Response::PadData data) { OnPadData(data, client); }};
263 LOG_INFO(Input, "Starting communication with UDP input server on {}:{}", host, port); 298 LOG_INFO(Input, "Starting communication with UDP input server on {}:{}:{}", host, port,
299 pad_index);
300 clients[client].host = host;
301 clients[client].port = port;
302 clients[client].pad_index = pad_index;
303 clients[client].active = 0;
264 clients[client].socket = std::make_unique<Socket>(host, port, pad_index, client_id, callback); 304 clients[client].socket = std::make_unique<Socket>(host, port, pad_index, client_id, callback);
265 clients[client].thread = std::thread{SocketLoop, clients[client].socket.get()}; 305 clients[client].thread = std::thread{SocketLoop, clients[client].socket.get()};
306 // Set motion parameters
307 // SetGyroThreshold value should be dependent on GyroscopeZeroDriftMode
308 // Real HW values are unknown, 0.0001 is an approximate to Standard
309 clients[client].motion.SetGyroThreshold(0.0001f);
266} 310}
267 311
268void Client::Reset() { 312void Client::Reset() {
269 for (std::size_t client = 0; client < clients.size(); client++) { 313 for (auto& client : clients) {
270 clients[client].socket->Stop(); 314 if (client.thread.joinable()) {
271 clients[client].thread.join(); 315 client.active = -1;
316 client.socket->Stop();
317 client.thread.join();
318 }
272 } 319 }
273} 320}
274 321
275void Client::UpdateYuzuSettings(std::size_t client, const Common::Vec3<float>& acc, 322void Client::UpdateYuzuSettings(std::size_t client, const Common::Vec3<float>& acc,
276 const Common::Vec3<float>& gyro, bool touch) { 323 const Common::Vec3<float>& gyro, bool touch) {
277 UDPPadStatus pad; 324 if (gyro.Length() > 0.2f) {
325 LOG_DEBUG(Input, "UDP Controller {}: gyro=({}, {}, {}), accel=({}, {}, {}), touch={}",
326 client, gyro[0], gyro[1], gyro[2], acc[0], acc[1], acc[2], touch);
327 }
328 UDPPadStatus pad{
329 .host = clients[client].host,
330 .port = clients[client].port,
331 .pad_index = clients[client].pad_index,
332 };
278 if (touch) { 333 if (touch) {
279 pad.touch = PadTouch::Click; 334 pad.touch = PadTouch::Click;
280 pad_queue[client].Push(pad); 335 pad_queue.Push(pad);
281 } 336 }
282 for (size_t i = 0; i < 3; ++i) { 337 for (size_t i = 0; i < 3; ++i) {
283 if (gyro[i] > 6.0f || gyro[i] < -6.0f) { 338 if (gyro[i] > 5.0f || gyro[i] < -5.0f) {
284 pad.motion = static_cast<PadMotion>(i); 339 pad.motion = static_cast<PadMotion>(i);
285 pad.motion_value = gyro[i]; 340 pad.motion_value = gyro[i];
286 pad_queue[client].Push(pad); 341 pad_queue.Push(pad);
287 } 342 }
288 if (acc[i] > 2.0f || acc[i] < -2.0f) { 343 if (acc[i] > 1.75f || acc[i] < -1.75f) {
289 pad.motion = static_cast<PadMotion>(i + 3); 344 pad.motion = static_cast<PadMotion>(i + 3);
290 pad.motion_value = acc[i]; 345 pad.motion_value = acc[i];
291 pad_queue[client].Push(pad); 346 pad_queue.Push(pad);
292 } 347 }
293 } 348 }
294} 349}
295 350
296void Client::BeginConfiguration() { 351void Client::BeginConfiguration() {
297 for (auto& pq : pad_queue) { 352 pad_queue.Clear();
298 pq.Clear();
299 }
300 configuring = true; 353 configuring = true;
301} 354}
302 355
303void Client::EndConfiguration() { 356void Client::EndConfiguration() {
304 for (auto& pq : pad_queue) { 357 pad_queue.Clear();
305 pq.Clear();
306 }
307 configuring = false; 358 configuring = false;
308} 359}
309 360
310DeviceStatus& Client::GetPadState(std::size_t pad) { 361DeviceStatus& Client::GetPadState(const std::string& host, u16 port, std::size_t pad) {
311 return clients[pad].status; 362 const std::size_t client_number = GetClientNumber(host, port, pad);
363 if (client_number == max_udp_clients) {
364 return clients[0].status;
365 }
366 return clients[client_number].status;
312} 367}
313 368
314const DeviceStatus& Client::GetPadState(std::size_t pad) const { 369const DeviceStatus& Client::GetPadState(const std::string& host, u16 port, std::size_t pad) const {
315 return clients[pad].status; 370 const std::size_t client_number = GetClientNumber(host, port, pad);
371 if (client_number == max_udp_clients) {
372 return clients[0].status;
373 }
374 return clients[client_number].status;
316} 375}
317 376
318std::array<Common::SPSCQueue<UDPPadStatus>, 4>& Client::GetPadQueue() { 377Common::SPSCQueue<UDPPadStatus>& Client::GetPadQueue() {
319 return pad_queue; 378 return pad_queue;
320} 379}
321 380
322const std::array<Common::SPSCQueue<UDPPadStatus>, 4>& Client::GetPadQueue() const { 381const Common::SPSCQueue<UDPPadStatus>& Client::GetPadQueue() const {
323 return pad_queue; 382 return pad_queue;
324} 383}
325 384
326void TestCommunication(const std::string& host, u16 port, u8 pad_index, u32 client_id, 385void TestCommunication(const std::string& host, u16 port, std::size_t pad_index, u32 client_id,
327 std::function<void()> success_callback, 386 const std::function<void()>& success_callback,
328 std::function<void()> failure_callback) { 387 const std::function<void()>& failure_callback) {
329 std::thread([=] { 388 std::thread([=] {
330 Common::Event success_event; 389 Common::Event success_event;
331 SocketCallback callback{[](Response::Version version) {}, [](Response::PortInfo info) {}, 390 SocketCallback callback{
332 [&](Response::PadData data) { success_event.Set(); }}; 391 .version = [](Response::Version) {},
392 .port_info = [](Response::PortInfo) {},
393 .pad_data = [&](Response::PadData) { success_event.Set(); },
394 };
333 Socket socket{host, port, pad_index, client_id, std::move(callback)}; 395 Socket socket{host, port, pad_index, client_id, std::move(callback)};
334 std::thread worker_thread{SocketLoop, &socket}; 396 std::thread worker_thread{SocketLoop, &socket};
335 bool result = success_event.WaitFor(std::chrono::seconds(8)); 397 const bool result = success_event.WaitFor(std::chrono::seconds(5));
336 socket.Stop(); 398 socket.Stop();
337 worker_thread.join(); 399 worker_thread.join();
338 if (result) { 400 if (result) {
@@ -344,7 +406,7 @@ void TestCommunication(const std::string& host, u16 port, u8 pad_index, u32 clie
344} 406}
345 407
346CalibrationConfigurationJob::CalibrationConfigurationJob( 408CalibrationConfigurationJob::CalibrationConfigurationJob(
347 const std::string& host, u16 port, u8 pad_index, u32 client_id, 409 const std::string& host, u16 port, std::size_t pad_index, u32 client_id,
348 std::function<void(Status)> status_callback, 410 std::function<void(Status)> status_callback,
349 std::function<void(u16, u16, u16, u16)> data_callback) { 411 std::function<void(u16, u16, u16, u16)> data_callback) {
350 412
@@ -357,14 +419,14 @@ CalibrationConfigurationJob::CalibrationConfigurationJob(
357 u16 max_y{}; 419 u16 max_y{};
358 420
359 Status current_status{Status::Initialized}; 421 Status current_status{Status::Initialized};
360 SocketCallback callback{[](Response::Version version) {}, [](Response::PortInfo info) {}, 422 SocketCallback callback{[](Response::Version) {}, [](Response::PortInfo) {},
361 [&](Response::PadData data) { 423 [&](Response::PadData data) {
362 if (current_status == Status::Initialized) { 424 if (current_status == Status::Initialized) {
363 // Receiving data means the communication is ready now 425 // Receiving data means the communication is ready now
364 current_status = Status::Ready; 426 current_status = Status::Ready;
365 status_callback(current_status); 427 status_callback(current_status);
366 } 428 }
367 if (!data.touch_1.is_active) { 429 if (data.touch_1.is_active == 0) {
368 return; 430 return;
369 } 431 }
370 LOG_DEBUG(Input, "Current touch: {} {}", data.touch_1.x, 432 LOG_DEBUG(Input, "Current touch: {} {}", data.touch_1.x,
diff --git a/src/input_common/udp/client.h b/src/input_common/udp/client.h
index 523dc6a7a..00c8b09f5 100644
--- a/src/input_common/udp/client.h
+++ b/src/input_common/udp/client.h
@@ -21,8 +21,7 @@
21 21
22namespace InputCommon::CemuhookUDP { 22namespace InputCommon::CemuhookUDP {
23 23
24constexpr u16 DEFAULT_PORT = 26760; 24constexpr char DEFAULT_SRV[] = "127.0.0.1:26760";
25constexpr char DEFAULT_ADDR[] = "127.0.0.1";
26 25
27class Socket; 26class Socket;
28 27
@@ -48,6 +47,9 @@ enum class PadTouch {
48}; 47};
49 48
50struct UDPPadStatus { 49struct UDPPadStatus {
50 std::string host{"127.0.0.1"};
51 u16 port{26760};
52 std::size_t pad_index{};
51 PadTouch touch{PadTouch::Undefined}; 53 PadTouch touch{PadTouch::Undefined};
52 PadMotion motion{PadMotion::Undefined}; 54 PadMotion motion{PadMotion::Undefined};
53 f32 motion_value{0.0f}; 55 f32 motion_value{0.0f};
@@ -82,46 +84,52 @@ public:
82 84
83 std::vector<Common::ParamPackage> GetInputDevices() const; 85 std::vector<Common::ParamPackage> GetInputDevices() const;
84 86
85 bool DeviceConnected(std::size_t pad) const; 87 bool DeviceConnected(std::size_t client) const;
86 void ReloadUDPClient(); 88 void ReloadSockets();
87 void ReloadSocket(const std::string& host = "127.0.0.1", u16 port = 26760, u8 pad_index = 0,
88 u32 client_id = 24872);
89 89
90 std::array<Common::SPSCQueue<UDPPadStatus>, 4>& GetPadQueue(); 90 Common::SPSCQueue<UDPPadStatus>& GetPadQueue();
91 const std::array<Common::SPSCQueue<UDPPadStatus>, 4>& GetPadQueue() const; 91 const Common::SPSCQueue<UDPPadStatus>& GetPadQueue() const;
92 92
93 DeviceStatus& GetPadState(std::size_t pad); 93 DeviceStatus& GetPadState(const std::string& host, u16 port, std::size_t pad);
94 const DeviceStatus& GetPadState(std::size_t pad) const; 94 const DeviceStatus& GetPadState(const std::string& host, u16 port, std::size_t pad) const;
95 95
96private: 96private:
97 struct ClientData { 97 struct ClientData {
98 std::string host{"127.0.0.1"};
99 u16 port{26760};
100 std::size_t pad_index{};
98 std::unique_ptr<Socket> socket; 101 std::unique_ptr<Socket> socket;
99 DeviceStatus status; 102 DeviceStatus status;
100 std::thread thread; 103 std::thread thread;
101 u64 packet_sequence = 0; 104 u64 packet_sequence{};
102 u8 active; 105 s8 active{-1};
103 106
104 // Realtime values 107 // Realtime values
105 // motion is initalized with PID values for drift correction on joycons 108 // motion is initalized with PID values for drift correction on joycons
106 InputCommon::MotionInput motion{0.3f, 0.005f, 0.0f}; 109 InputCommon::MotionInput motion{0.3f, 0.005f, 0.0f};
107 std::chrono::time_point<std::chrono::system_clock> last_motion_update; 110 std::chrono::time_point<std::chrono::steady_clock> last_motion_update;
108 }; 111 };
109 112
110 // For shutting down, clear all data, join all threads, release usb 113 // For shutting down, clear all data, join all threads, release usb
111 void Reset(); 114 void Reset();
112 115
116 // Translates configuration to client number
117 std::size_t GetClientNumber(std::string_view host, u16 port, std::size_t pad) const;
118
113 void OnVersion(Response::Version); 119 void OnVersion(Response::Version);
114 void OnPortInfo(Response::PortInfo); 120 void OnPortInfo(Response::PortInfo);
115 void OnPadData(Response::PadData); 121 void OnPadData(Response::PadData, std::size_t client);
116 void StartCommunication(std::size_t client, const std::string& host, u16 port, u8 pad_index, 122 void StartCommunication(std::size_t client, const std::string& host, u16 port,
117 u32 client_id); 123 std::size_t pad_index, u32 client_id);
118 void UpdateYuzuSettings(std::size_t client, const Common::Vec3<float>& acc, 124 void UpdateYuzuSettings(std::size_t client, const Common::Vec3<float>& acc,
119 const Common::Vec3<float>& gyro, bool touch); 125 const Common::Vec3<float>& gyro, bool touch);
120 126
121 bool configuring = false; 127 bool configuring = false;
122 128
123 std::array<ClientData, 4> clients; 129 // Allocate clients for 8 udp servers
124 std::array<Common::SPSCQueue<UDPPadStatus>, 4> pad_queue; 130 const std::size_t max_udp_clients = 32;
131 std::array<ClientData, 4 * 8> clients;
132 Common::SPSCQueue<UDPPadStatus> pad_queue;
125}; 133};
126 134
127/// An async job allowing configuration of the touchpad calibration. 135/// An async job allowing configuration of the touchpad calibration.
@@ -139,7 +147,7 @@ public:
139 * @param status_callback Callback for job status updates 147 * @param status_callback Callback for job status updates
140 * @param data_callback Called when calibration data is ready 148 * @param data_callback Called when calibration data is ready
141 */ 149 */
142 explicit CalibrationConfigurationJob(const std::string& host, u16 port, u8 pad_index, 150 explicit CalibrationConfigurationJob(const std::string& host, u16 port, std::size_t pad_index,
143 u32 client_id, std::function<void(Status)> status_callback, 151 u32 client_id, std::function<void(Status)> status_callback,
144 std::function<void(u16, u16, u16, u16)> data_callback); 152 std::function<void(u16, u16, u16, u16)> data_callback);
145 ~CalibrationConfigurationJob(); 153 ~CalibrationConfigurationJob();
@@ -149,8 +157,8 @@ private:
149 Common::Event complete_event; 157 Common::Event complete_event;
150}; 158};
151 159
152void TestCommunication(const std::string& host, u16 port, u8 pad_index, u32 client_id, 160void TestCommunication(const std::string& host, u16 port, std::size_t pad_index, u32 client_id,
153 std::function<void()> success_callback, 161 const std::function<void()>& success_callback,
154 std::function<void()> failure_callback); 162 const std::function<void()>& failure_callback);
155 163
156} // namespace InputCommon::CemuhookUDP 164} // namespace InputCommon::CemuhookUDP
diff --git a/src/input_common/udp/protocol.h b/src/input_common/udp/protocol.h
index 3ba4d1fc8..fc1aea4b9 100644
--- a/src/input_common/udp/protocol.h
+++ b/src/input_common/udp/protocol.h
@@ -7,7 +7,16 @@
7#include <array> 7#include <array>
8#include <optional> 8#include <optional>
9#include <type_traits> 9#include <type_traits>
10
11#ifdef _MSC_VER
12#pragma warning(push)
13#pragma warning(disable : 4701)
14#endif
10#include <boost/crc.hpp> 15#include <boost/crc.hpp>
16#ifdef _MSC_VER
17#pragma warning(pop)
18#endif
19
11#include "common/bit_field.h" 20#include "common/bit_field.h"
12#include "common/swap.h" 21#include "common/swap.h"
13 22
@@ -93,7 +102,7 @@ static_assert(std::is_trivially_copyable_v<PadData>,
93 102
94/** 103/**
95 * Creates a message with the proper header data that can be sent to the server. 104 * Creates a message with the proper header data that can be sent to the server.
96 * @param T data Request body to send 105 * @param data Request body to send
97 * @param client_id ID of the udp client (usually not checked on the server) 106 * @param client_id ID of the udp client (usually not checked on the server)
98 */ 107 */
99template <typename T> 108template <typename T>
diff --git a/src/input_common/udp/udp.cpp b/src/input_common/udp/udp.cpp
index eba077a36..c5da27a38 100644
--- a/src/input_common/udp/udp.cpp
+++ b/src/input_common/udp/udp.cpp
@@ -2,8 +2,6 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <atomic>
6#include <list>
7#include <mutex> 5#include <mutex>
8#include <utility> 6#include <utility>
9#include "common/assert.h" 7#include "common/assert.h"
@@ -15,36 +13,36 @@ namespace InputCommon {
15 13
16class UDPMotion final : public Input::MotionDevice { 14class UDPMotion final : public Input::MotionDevice {
17public: 15public:
18 UDPMotion(std::string ip_, int port_, int pad_, CemuhookUDP::Client* client_) 16 explicit UDPMotion(std::string ip_, u16 port_, u16 pad_, CemuhookUDP::Client* client_)
19 : ip(ip_), port(port_), pad(pad_), client(client_) {} 17 : ip(std::move(ip_)), port(port_), pad(pad_), client(client_) {}
20 18
21 Input::MotionStatus GetStatus() const override { 19 Input::MotionStatus GetStatus() const override {
22 return client->GetPadState(pad).motion_status; 20 return client->GetPadState(ip, port, pad).motion_status;
23 } 21 }
24 22
25private: 23private:
26 const std::string ip; 24 const std::string ip;
27 const int port; 25 const u16 port;
28 const int pad; 26 const u16 pad;
29 CemuhookUDP::Client* client; 27 CemuhookUDP::Client* client;
30 mutable std::mutex mutex; 28 mutable std::mutex mutex;
31}; 29};
32 30
33/// A motion device factory that creates motion devices from JC Adapter 31/// A motion device factory that creates motion devices from a UDP client
34UDPMotionFactory::UDPMotionFactory(std::shared_ptr<CemuhookUDP::Client> client_) 32UDPMotionFactory::UDPMotionFactory(std::shared_ptr<CemuhookUDP::Client> client_)
35 : client(std::move(client_)) {} 33 : client(std::move(client_)) {}
36 34
37/** 35/**
38 * Creates motion device 36 * Creates motion device
39 * @param params contains parameters for creating the device: 37 * @param params contains parameters for creating the device:
40 * - "port": the nth jcpad on the adapter 38 * - "port": the UDP port number
41 */ 39 */
42std::unique_ptr<Input::MotionDevice> UDPMotionFactory::Create(const Common::ParamPackage& params) { 40std::unique_ptr<Input::MotionDevice> UDPMotionFactory::Create(const Common::ParamPackage& params) {
43 const std::string ip = params.Get("ip", "127.0.0.1"); 41 auto ip = params.Get("ip", "127.0.0.1");
44 const int port = params.Get("port", 26760); 42 const auto port = static_cast<u16>(params.Get("port", 26760));
45 const int pad = params.Get("pad_index", 0); 43 const auto pad = static_cast<u16>(params.Get("pad_index", 0));
46 44
47 return std::make_unique<UDPMotion>(ip, port, pad, client.get()); 45 return std::make_unique<UDPMotion>(std::move(ip), port, pad, client.get());
48} 46}
49 47
50void UDPMotionFactory::BeginConfiguration() { 48void UDPMotionFactory::BeginConfiguration() {
@@ -61,54 +59,52 @@ Common::ParamPackage UDPMotionFactory::GetNextInput() {
61 Common::ParamPackage params; 59 Common::ParamPackage params;
62 CemuhookUDP::UDPPadStatus pad; 60 CemuhookUDP::UDPPadStatus pad;
63 auto& queue = client->GetPadQueue(); 61 auto& queue = client->GetPadQueue();
64 for (std::size_t pad_number = 0; pad_number < queue.size(); ++pad_number) { 62 while (queue.Pop(pad)) {
65 while (queue[pad_number].Pop(pad)) { 63 if (pad.motion == CemuhookUDP::PadMotion::Undefined || std::abs(pad.motion_value) < 1) {
66 if (pad.motion == CemuhookUDP::PadMotion::Undefined || std::abs(pad.motion_value) < 1) { 64 continue;
67 continue;
68 }
69 params.Set("engine", "cemuhookudp");
70 params.Set("ip", "127.0.0.1");
71 params.Set("port", 26760);
72 params.Set("pad_index", static_cast<int>(pad_number));
73 params.Set("motion", static_cast<u16>(pad.motion));
74 return params;
75 } 65 }
66 params.Set("engine", "cemuhookudp");
67 params.Set("ip", pad.host);
68 params.Set("port", static_cast<u16>(pad.port));
69 params.Set("pad_index", static_cast<u16>(pad.pad_index));
70 params.Set("motion", static_cast<u16>(pad.motion));
71 return params;
76 } 72 }
77 return params; 73 return params;
78} 74}
79 75
80class UDPTouch final : public Input::TouchDevice { 76class UDPTouch final : public Input::TouchDevice {
81public: 77public:
82 UDPTouch(std::string ip_, int port_, int pad_, CemuhookUDP::Client* client_) 78 explicit UDPTouch(std::string ip_, u16 port_, u16 pad_, CemuhookUDP::Client* client_)
83 : ip(std::move(ip_)), port(port_), pad(pad_), client(client_) {} 79 : ip(std::move(ip_)), port(port_), pad(pad_), client(client_) {}
84 80
85 std::tuple<float, float, bool> GetStatus() const override { 81 std::tuple<float, float, bool> GetStatus() const override {
86 return client->GetPadState(pad).touch_status; 82 return client->GetPadState(ip, port, pad).touch_status;
87 } 83 }
88 84
89private: 85private:
90 const std::string ip; 86 const std::string ip;
91 const int port; 87 const u16 port;
92 const int pad; 88 const u16 pad;
93 CemuhookUDP::Client* client; 89 CemuhookUDP::Client* client;
94 mutable std::mutex mutex; 90 mutable std::mutex mutex;
95}; 91};
96 92
97/// A motion device factory that creates motion devices from JC Adapter 93/// A motion device factory that creates motion devices from a UDP client
98UDPTouchFactory::UDPTouchFactory(std::shared_ptr<CemuhookUDP::Client> client_) 94UDPTouchFactory::UDPTouchFactory(std::shared_ptr<CemuhookUDP::Client> client_)
99 : client(std::move(client_)) {} 95 : client(std::move(client_)) {}
100 96
101/** 97/**
102 * Creates motion device 98 * Creates motion device
103 * @param params contains parameters for creating the device: 99 * @param params contains parameters for creating the device:
104 * - "port": the nth jcpad on the adapter 100 * - "port": the UDP port number
105 */ 101 */
106std::unique_ptr<Input::TouchDevice> UDPTouchFactory::Create(const Common::ParamPackage& params) { 102std::unique_ptr<Input::TouchDevice> UDPTouchFactory::Create(const Common::ParamPackage& params) {
107 const std::string ip = params.Get("ip", "127.0.0.1"); 103 auto ip = params.Get("ip", "127.0.0.1");
108 const int port = params.Get("port", 26760); 104 const auto port = static_cast<u16>(params.Get("port", 26760));
109 const int pad = params.Get("pad_index", 0); 105 const auto pad = static_cast<u16>(params.Get("pad_index", 0));
110 106
111 return std::make_unique<UDPTouch>(ip, port, pad, client.get()); 107 return std::make_unique<UDPTouch>(std::move(ip), port, pad, client.get());
112} 108}
113 109
114void UDPTouchFactory::BeginConfiguration() { 110void UDPTouchFactory::BeginConfiguration() {
@@ -125,18 +121,16 @@ Common::ParamPackage UDPTouchFactory::GetNextInput() {
125 Common::ParamPackage params; 121 Common::ParamPackage params;
126 CemuhookUDP::UDPPadStatus pad; 122 CemuhookUDP::UDPPadStatus pad;
127 auto& queue = client->GetPadQueue(); 123 auto& queue = client->GetPadQueue();
128 for (std::size_t pad_number = 0; pad_number < queue.size(); ++pad_number) { 124 while (queue.Pop(pad)) {
129 while (queue[pad_number].Pop(pad)) { 125 if (pad.touch == CemuhookUDP::PadTouch::Undefined) {
130 if (pad.touch == CemuhookUDP::PadTouch::Undefined) { 126 continue;
131 continue;
132 }
133 params.Set("engine", "cemuhookudp");
134 params.Set("ip", "127.0.0.1");
135 params.Set("port", 26760);
136 params.Set("pad_index", static_cast<int>(pad_number));
137 params.Set("touch", static_cast<u16>(pad.touch));
138 return params;
139 } 127 }
128 params.Set("engine", "cemuhookudp");
129 params.Set("ip", pad.host);
130 params.Set("port", static_cast<u16>(pad.port));
131 params.Set("pad_index", static_cast<u16>(pad.pad_index));
132 params.Set("touch", static_cast<u16>(pad.touch));
133 return params;
140 } 134 }
141 return params; 135 return params;
142} 136}