Client.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393
  1. using Force.Crc32;
  2. using Ryujinx.Common;
  3. using Ryujinx.Common.Configuration.Hid;
  4. using Ryujinx.Common.Logging;
  5. using Ryujinx.Configuration;
  6. using System;
  7. using System.Collections.Generic;
  8. using System.IO;
  9. using System.Net;
  10. using System.Net.Sockets;
  11. using System.Numerics;
  12. using System.Threading.Tasks;
  13. namespace Ryujinx.Motion
  14. {
  15. public class Client : IDisposable
  16. {
  17. public const uint Magic = 0x43555344; // DSUC
  18. public const ushort Version = 1001;
  19. private bool _active;
  20. private readonly Dictionary<int, IPEndPoint> _hosts;
  21. private readonly Dictionary<int, Dictionary<int, MotionInput>> _motionData;
  22. private readonly Dictionary<int, UdpClient> _clients;
  23. private bool[] _clientErrorStatus = new bool[Enum.GetValues(typeof(PlayerIndex)).Length];
  24. public Client()
  25. {
  26. _hosts = new Dictionary<int, IPEndPoint>();
  27. _motionData = new Dictionary<int, Dictionary<int, MotionInput>>();
  28. _clients = new Dictionary<int, UdpClient>();
  29. CloseClients();
  30. }
  31. public void CloseClients()
  32. {
  33. _active = false;
  34. lock (_clients)
  35. {
  36. foreach (var client in _clients)
  37. {
  38. try
  39. {
  40. client.Value?.Dispose();
  41. }
  42. #pragma warning disable CS0168
  43. catch (SocketException ex)
  44. #pragma warning restore CS0168
  45. {
  46. Logger.Warning?.PrintMsg(LogClass.Hid, $"Unable to dispose motion client. Error code {ex.ErrorCode}");
  47. }
  48. }
  49. _hosts.Clear();
  50. _clients.Clear();
  51. _motionData.Clear();
  52. }
  53. }
  54. public void RegisterClient(int player, string host, int port)
  55. {
  56. if (_clients.ContainsKey(player))
  57. {
  58. return;
  59. }
  60. try
  61. {
  62. lock (_clients)
  63. {
  64. IPEndPoint endPoint = new IPEndPoint(IPAddress.Parse(host), port);
  65. UdpClient client = new UdpClient(host, port);
  66. _clients.Add(player, client);
  67. _hosts.Add(player, endPoint);
  68. _active = true;
  69. Task.Run(() =>
  70. {
  71. ReceiveLoop(player);
  72. });
  73. }
  74. }
  75. catch (FormatException fex)
  76. {
  77. if (!_clientErrorStatus[player])
  78. {
  79. Logger.Warning?.PrintMsg(LogClass.Hid, $"Unable to connect to motion source at {host}:{port}. Error {fex.Message}");
  80. _clientErrorStatus[player] = true;
  81. }
  82. }
  83. catch (SocketException ex)
  84. {
  85. if (!_clientErrorStatus[player])
  86. {
  87. Logger.Warning?.PrintMsg(LogClass.Hid, $"Unable to connect to motion source at {host}:{port}. Error code {ex.ErrorCode}");
  88. _clientErrorStatus[player] = true;
  89. }
  90. }
  91. }
  92. public bool TryGetData(int player, int slot, out MotionInput input)
  93. {
  94. lock (_motionData)
  95. {
  96. if (_motionData.ContainsKey(player))
  97. {
  98. input = _motionData[player][slot];
  99. return true;
  100. }
  101. }
  102. input = null;
  103. return false;
  104. }
  105. private void Send(byte[] data, int clientId)
  106. {
  107. if (_clients.TryGetValue(clientId, out UdpClient _client))
  108. {
  109. if (_client != null && _client.Client != null && _client.Client.Connected)
  110. {
  111. try
  112. {
  113. _client?.Send(data, data.Length);
  114. }
  115. catch (SocketException ex)
  116. {
  117. if (!_clientErrorStatus[clientId])
  118. {
  119. Logger.Warning?.PrintMsg(LogClass.Hid, $"Unable to send data request to motion source at {_client.Client.RemoteEndPoint}. Error code {ex.ErrorCode}");
  120. }
  121. _clientErrorStatus[clientId] = true;
  122. _clients.Remove(clientId);
  123. _hosts.Remove(clientId);
  124. _client?.Dispose();
  125. }
  126. }
  127. }
  128. }
  129. private byte[] Receive(int clientId)
  130. {
  131. if (_hosts.TryGetValue(clientId, out IPEndPoint endPoint))
  132. {
  133. if (_clients.TryGetValue(clientId, out UdpClient _client))
  134. {
  135. if (_client != null && _client.Client != null)
  136. {
  137. if (_client.Client.Connected)
  138. {
  139. try
  140. {
  141. var result = _client?.Receive(ref endPoint);
  142. if (result.Length > 0)
  143. {
  144. _clientErrorStatus[clientId] = false;
  145. }
  146. return result;
  147. }
  148. catch (SocketException ex)
  149. {
  150. if (!_clientErrorStatus[clientId])
  151. {
  152. Logger.Warning?.PrintMsg(LogClass.Hid, $"Unable to receive data from motion source at {endPoint}. Error code {ex.ErrorCode}");
  153. }
  154. _clientErrorStatus[clientId] = true;
  155. _clients.Remove(clientId);
  156. _hosts.Remove(clientId);
  157. _client?.Dispose();
  158. }
  159. }
  160. }
  161. }
  162. }
  163. return new byte[0];
  164. }
  165. public void ReceiveLoop(int clientId)
  166. {
  167. while (_active)
  168. {
  169. byte[] data = Receive(clientId);
  170. if (data.Length == 0)
  171. {
  172. continue;
  173. }
  174. #pragma warning disable CS4014
  175. HandleResponse(data, clientId);
  176. #pragma warning restore CS4014
  177. }
  178. }
  179. #pragma warning disable CS1998
  180. public async Task HandleResponse(byte[] data, int clientId)
  181. #pragma warning restore CS1998
  182. {
  183. MessageType type = (MessageType)BitConverter.ToUInt32(data.AsSpan().Slice(16, 4));
  184. data = data.AsSpan().Slice(16).ToArray();
  185. using (MemoryStream mem = new MemoryStream(data))
  186. {
  187. using (BinaryReader reader = new BinaryReader(mem))
  188. {
  189. switch (type)
  190. {
  191. case MessageType.Protocol:
  192. break;
  193. case MessageType.Info:
  194. ControllerInfoResponse contollerInfo = reader.ReadStruct<ControllerInfoResponse>();
  195. break;
  196. case MessageType.Data:
  197. ControllerDataResponse inputData = reader.ReadStruct<ControllerDataResponse>();
  198. Vector3 accelerometer = new Vector3()
  199. {
  200. X = -inputData.AccelerometerX,
  201. Y = inputData.AccelerometerZ,
  202. Z = -inputData.AccelerometerY
  203. };
  204. Vector3 gyroscrope = new Vector3()
  205. {
  206. X = inputData.GyroscopePitch,
  207. Y = inputData.GyroscopeRoll,
  208. Z = -inputData.GyroscopeYaw
  209. };
  210. ulong timestamp = inputData.MotionTimestamp;
  211. InputConfig config = ConfigurationState.Instance.Hid.InputConfig.Value.Find(x => x.PlayerIndex == (PlayerIndex)clientId);
  212. lock (_motionData)
  213. {
  214. int slot = inputData.Shared.Slot;
  215. if (_motionData.ContainsKey(clientId))
  216. {
  217. if (_motionData[clientId].ContainsKey(slot))
  218. {
  219. var previousData = _motionData[clientId][slot];
  220. previousData.Update(accelerometer, gyroscrope, timestamp, config.Sensitivity, (float)config.GyroDeadzone);
  221. }
  222. else
  223. {
  224. MotionInput input = new MotionInput();
  225. input.Update(accelerometer, gyroscrope, timestamp, config.Sensitivity, (float)config.GyroDeadzone);
  226. _motionData[clientId].Add(slot, input);
  227. }
  228. }
  229. else
  230. {
  231. MotionInput input = new MotionInput();
  232. input.Update(accelerometer, gyroscrope, timestamp, config.Sensitivity, (float)config.GyroDeadzone);
  233. _motionData.Add(clientId, new Dictionary<int, MotionInput>() { { slot, input } });
  234. }
  235. }
  236. break;
  237. }
  238. }
  239. }
  240. }
  241. public void RequestInfo(int clientId, int slot)
  242. {
  243. if (!_active)
  244. {
  245. return;
  246. }
  247. Header header = GenerateHeader(clientId);
  248. using (MemoryStream mem = new MemoryStream())
  249. {
  250. using (BinaryWriter writer = new BinaryWriter(mem))
  251. {
  252. writer.WriteStruct(header);
  253. ControllerInfoRequest request = new ControllerInfoRequest()
  254. {
  255. Type = MessageType.Info,
  256. PortsCount = 4
  257. };
  258. request.PortIndices[0] = (byte)slot;
  259. writer.WriteStruct(request);
  260. header.Length = (ushort)(mem.Length - 16);
  261. writer.Seek(6, SeekOrigin.Begin);
  262. writer.Write(header.Length);
  263. header.Crc32 = Crc32Algorithm.Compute(mem.ToArray());
  264. writer.Seek(8, SeekOrigin.Begin);
  265. writer.Write(header.Crc32);
  266. byte[] data = mem.ToArray();
  267. Send(data, clientId);
  268. }
  269. }
  270. }
  271. public unsafe void RequestData(int clientId, int slot)
  272. {
  273. if (!_active)
  274. {
  275. return;
  276. }
  277. Header header = GenerateHeader(clientId);
  278. using (MemoryStream mem = new MemoryStream())
  279. {
  280. using (BinaryWriter writer = new BinaryWriter(mem))
  281. {
  282. writer.WriteStruct(header);
  283. ControllerDataRequest request = new ControllerDataRequest()
  284. {
  285. Type = MessageType.Data,
  286. Slot = (byte)slot,
  287. SubscriberType = SubscriberType.Slot
  288. };
  289. writer.WriteStruct(request);
  290. header.Length = (ushort)(mem.Length - 16);
  291. writer.Seek(6, SeekOrigin.Begin);
  292. writer.Write(header.Length);
  293. header.Crc32 = Crc32Algorithm.Compute(mem.ToArray());
  294. writer.Seek(8, SeekOrigin.Begin);
  295. writer.Write(header.Crc32);
  296. byte[] data = mem.ToArray();
  297. Send(data, clientId);
  298. }
  299. }
  300. }
  301. private Header GenerateHeader(int clientId)
  302. {
  303. Header header = new Header()
  304. {
  305. ID = (uint)clientId,
  306. MagicString = Magic,
  307. Version = Version,
  308. Length = 0,
  309. Crc32 = 0
  310. };
  311. return header;
  312. }
  313. public void Dispose()
  314. {
  315. _active = false;
  316. CloseClients();
  317. }
  318. }
  319. }