Brak opisu
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

RemoteInputPlayerConnection.cs 8.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. using System;
  2. using UnityEngine.InputSystem.Utilities;
  3. using UnityEngine.Networking.PlayerConnection;
  4. #if UNITY_EDITOR
  5. using UnityEditor;
  6. #endif
  7. namespace UnityEngine.InputSystem
  8. {
  9. // Transports input remoting messages from and to players. Can be used to
  10. // make input on either side fully available on the other side. I.e. player
  11. // input can be fully debugged in the editor and editor input can conversely
  12. // be fed into the player.
  13. //
  14. // NOTE: The Unity EditorConnection/PlayerConnection mechanism requires this to
  15. // be a ScriptableObject as it will register every listeners as a persistent
  16. // one.
  17. [Serializable]
  18. internal class RemoteInputPlayerConnection :
  19. #if UNITY_EDITOR
  20. // In the editor, we need to make sure that we get the same instance after domain reloads.
  21. // Otherwise, callbacks we have registered before the reload will no longer be valid, because
  22. // the object instance they point to will not deserialize to a valid object. So we use a
  23. // ScriptableSingleton instance, which fullfills these requirements. In the player, we need to
  24. // use a simple ScriptableObject, as ScriptableSingleton is an editor-only class.
  25. ScriptableSingleton<RemoteInputPlayerConnection>,
  26. #else
  27. ScriptableObject,
  28. #endif
  29. IObserver<InputRemoting.Message>, IObservable<InputRemoting.Message>
  30. {
  31. public static readonly Guid kNewDeviceMsg = new Guid("fcd9651ded40425995dfa6aeb78f1f1c");
  32. public static readonly Guid kNewLayoutMsg = new Guid("fccfec2b7369466d88502a9dd38505f4");
  33. public static readonly Guid kNewEventsMsg = new Guid("53546641df1347bc8aa315278a603586");
  34. public static readonly Guid kRemoveDeviceMsg = new Guid("e5e299b2d9e44255b8990bb71af8922d");
  35. public static readonly Guid kChangeUsagesMsg = new Guid("b9fe706dfc854d7ca109a5e38d7db730");
  36. public static readonly Guid kStartSendingMsg = new Guid("0d58e99045904672b3ef34b8797d23cb");
  37. public static readonly Guid kStopSendingMsg = new Guid("548716b2534a45369ab0c9323fc8b4a8");
  38. public void Bind(IEditorPlayerConnection connection, bool isConnected)
  39. {
  40. if (m_Connection != null)
  41. {
  42. if (m_Connection == connection)
  43. return;
  44. throw new InvalidOperationException("Already bound to an IEditorPlayerConnection");
  45. }
  46. // If there's already connections on the given IEditorPlayerConnection,
  47. // calling RegisterConnection() will invoke the given callback for every
  48. // already existing connection. However, it seems to do so only in the
  49. // editor which is why we do the 'isConnected' dance below.
  50. connection.RegisterConnection(OnConnected);
  51. connection.RegisterDisconnection(OnDisconnected);
  52. connection.Register(kNewDeviceMsg, OnNewDevice);
  53. connection.Register(kNewLayoutMsg, OnNewLayout);
  54. connection.Register(kNewEventsMsg, OnNewEvents);
  55. connection.Register(kRemoveDeviceMsg, OnRemoveDevice);
  56. connection.Register(kChangeUsagesMsg, OnChangeUsages);
  57. connection.Register(kStartSendingMsg, OnStartSending);
  58. connection.Register(kStopSendingMsg, OnStopSending);
  59. m_Connection = connection;
  60. if (isConnected)
  61. OnConnected(0);
  62. }
  63. public IDisposable Subscribe(IObserver<InputRemoting.Message> observer)
  64. {
  65. if (observer == null)
  66. throw new System.ArgumentNullException(nameof(observer));
  67. var subscriber = new Subscriber {owner = this, observer = observer};
  68. ArrayHelpers.Append(ref m_Subscribers, subscriber);
  69. if (m_ConnectedIds != null)
  70. {
  71. foreach (var id in m_ConnectedIds)
  72. observer.OnNext(new InputRemoting.Message { type = InputRemoting.MessageType.Connect, participantId = id });
  73. }
  74. return subscriber;
  75. }
  76. ////REVIEW: given that the PlayerConnection will connect to the editor regardless, we end up
  77. //// on this path whether input remoting is enabled or not
  78. private void OnConnected(int id)
  79. {
  80. if (m_ConnectedIds != null && ArrayHelpers.Contains(m_ConnectedIds, id))
  81. return;
  82. ArrayHelpers.Append(ref m_ConnectedIds, id);
  83. SendToSubscribers(InputRemoting.MessageType.Connect, new MessageEventArgs {playerId = id});
  84. }
  85. private void OnDisconnected(int id)
  86. {
  87. if (m_ConnectedIds == null || !ArrayHelpers.Contains(m_ConnectedIds, id))
  88. return;
  89. ArrayHelpers.Erase(ref m_ConnectedIds, id);
  90. SendToSubscribers(InputRemoting.MessageType.Disconnect, new MessageEventArgs {playerId = id});
  91. }
  92. private void OnNewDevice(MessageEventArgs args)
  93. {
  94. SendToSubscribers(InputRemoting.MessageType.NewDevice, args);
  95. }
  96. private void OnNewLayout(MessageEventArgs args)
  97. {
  98. SendToSubscribers(InputRemoting.MessageType.NewLayout, args);
  99. }
  100. private void OnNewEvents(MessageEventArgs args)
  101. {
  102. SendToSubscribers(InputRemoting.MessageType.NewEvents, args);
  103. }
  104. private void OnRemoveDevice(MessageEventArgs args)
  105. {
  106. SendToSubscribers(InputRemoting.MessageType.RemoveDevice, args);
  107. }
  108. private void OnChangeUsages(MessageEventArgs args)
  109. {
  110. SendToSubscribers(InputRemoting.MessageType.ChangeUsages, args);
  111. }
  112. private void OnStartSending(MessageEventArgs args)
  113. {
  114. SendToSubscribers(InputRemoting.MessageType.StartSending, args);
  115. }
  116. private void OnStopSending(MessageEventArgs args)
  117. {
  118. SendToSubscribers(InputRemoting.MessageType.StopSending, args);
  119. }
  120. private void SendToSubscribers(InputRemoting.MessageType type, MessageEventArgs args)
  121. {
  122. if (m_Subscribers == null)
  123. return;
  124. var msg = new InputRemoting.Message
  125. {
  126. participantId = args.playerId,
  127. type = type,
  128. data = args.data
  129. };
  130. for (var i = 0; i < m_Subscribers.Length; ++i)
  131. m_Subscribers[i].observer.OnNext(msg);
  132. }
  133. void IObserver<InputRemoting.Message>.OnNext(InputRemoting.Message msg)
  134. {
  135. if (m_Connection == null)
  136. return;
  137. ////TODO: this should really be sending to a specific player in the editor (can't
  138. //// do that through the IEditorPlayerConnection interface though)
  139. switch (msg.type)
  140. {
  141. case InputRemoting.MessageType.NewDevice:
  142. m_Connection.Send(kNewDeviceMsg, msg.data);
  143. break;
  144. case InputRemoting.MessageType.NewLayout:
  145. m_Connection.Send(kNewLayoutMsg, msg.data);
  146. break;
  147. case InputRemoting.MessageType.NewEvents:
  148. m_Connection.Send(kNewEventsMsg, msg.data);
  149. break;
  150. case InputRemoting.MessageType.ChangeUsages:
  151. m_Connection.Send(kChangeUsagesMsg, msg.data);
  152. break;
  153. case InputRemoting.MessageType.RemoveDevice:
  154. m_Connection.Send(kRemoveDeviceMsg, msg.data);
  155. break;
  156. }
  157. }
  158. void IObserver<InputRemoting.Message>.OnError(Exception error)
  159. {
  160. }
  161. void IObserver<InputRemoting.Message>.OnCompleted()
  162. {
  163. }
  164. [SerializeField] private IEditorPlayerConnection m_Connection;
  165. [NonSerialized] private Subscriber[] m_Subscribers;
  166. [SerializeField] private int[] m_ConnectedIds;
  167. private class Subscriber : IDisposable
  168. {
  169. public RemoteInputPlayerConnection owner;
  170. public IObserver<InputRemoting.Message> observer;
  171. public void Dispose()
  172. {
  173. ArrayHelpers.Erase(ref owner.m_Subscribers, this);
  174. }
  175. }
  176. }
  177. }