Maze / Library /PackageCache /com.unity.inputsystem@1.6.1 /InputSystem /Devices /Remote /RemoteInputPlayerConnection.cs
| using System; | |
| using UnityEngine.InputSystem.Utilities; | |
| using UnityEngine.Networking.PlayerConnection; | |
| using UnityEditor; | |
| namespace UnityEngine.InputSystem | |
| { | |
| // Transports input remoting messages from and to players. Can be used to | |
| // make input on either side fully available on the other side. I.e. player | |
| // input can be fully debugged in the editor and editor input can conversely | |
| // be fed into the player. | |
| // | |
| // NOTE: The Unity EditorConnection/PlayerConnection mechanism requires this to | |
| // be a ScriptableObject as it will register every listeners as a persistent | |
| // one. | |
| [] | |
| internal class RemoteInputPlayerConnection : | |
| #if UNITY_EDITOR | |
| // In the editor, we need to make sure that we get the same instance after domain reloads. | |
| // Otherwise, callbacks we have registered before the reload will no longer be valid, because | |
| // the object instance they point to will not deserialize to a valid object. So we use a | |
| // ScriptableSingleton instance, which fullfills these requirements. In the player, we need to | |
| // use a simple ScriptableObject, as ScriptableSingleton is an editor-only class. | |
| ScriptableSingleton<RemoteInputPlayerConnection>, | |
| #else | |
| ScriptableObject, | |
| #endif | |
| IObserver<InputRemoting.Message>, IObservable<InputRemoting.Message> | |
| { | |
| public static readonly Guid kNewDeviceMsg = new Guid("fcd9651ded40425995dfa6aeb78f1f1c"); | |
| public static readonly Guid kNewLayoutMsg = new Guid("fccfec2b7369466d88502a9dd38505f4"); | |
| public static readonly Guid kNewEventsMsg = new Guid("53546641df1347bc8aa315278a603586"); | |
| public static readonly Guid kRemoveDeviceMsg = new Guid("e5e299b2d9e44255b8990bb71af8922d"); | |
| public static readonly Guid kChangeUsagesMsg = new Guid("b9fe706dfc854d7ca109a5e38d7db730"); | |
| public static readonly Guid kStartSendingMsg = new Guid("0d58e99045904672b3ef34b8797d23cb"); | |
| public static readonly Guid kStopSendingMsg = new Guid("548716b2534a45369ab0c9323fc8b4a8"); | |
| public void Bind(IEditorPlayerConnection connection, bool isConnected) | |
| { | |
| if (m_Connection != null) | |
| { | |
| if (m_Connection == connection) | |
| return; | |
| throw new InvalidOperationException("Already bound to an IEditorPlayerConnection"); | |
| } | |
| // If there's already connections on the given IEditorPlayerConnection, | |
| // calling RegisterConnection() will invoke the given callback for every | |
| // already existing connection. However, it seems to do so only in the | |
| // editor which is why we do the 'isConnected' dance below. | |
| connection.RegisterConnection(OnConnected); | |
| connection.RegisterDisconnection(OnDisconnected); | |
| connection.Register(kNewDeviceMsg, OnNewDevice); | |
| connection.Register(kNewLayoutMsg, OnNewLayout); | |
| connection.Register(kNewEventsMsg, OnNewEvents); | |
| connection.Register(kRemoveDeviceMsg, OnRemoveDevice); | |
| connection.Register(kChangeUsagesMsg, OnChangeUsages); | |
| connection.Register(kStartSendingMsg, OnStartSending); | |
| connection.Register(kStopSendingMsg, OnStopSending); | |
| m_Connection = connection; | |
| if (isConnected) | |
| OnConnected(0); | |
| } | |
| public IDisposable Subscribe(IObserver<InputRemoting.Message> observer) | |
| { | |
| if (observer == null) | |
| throw new System.ArgumentNullException(nameof(observer)); | |
| var subscriber = new Subscriber {owner = this, observer = observer}; | |
| ArrayHelpers.Append(ref m_Subscribers, subscriber); | |
| if (m_ConnectedIds != null) | |
| { | |
| foreach (var id in m_ConnectedIds) | |
| observer.OnNext(new InputRemoting.Message { type = InputRemoting.MessageType.Connect, participantId = id }); | |
| } | |
| return subscriber; | |
| } | |
| ////REVIEW: given that the PlayerConnection will connect to the editor regardless, we end up | |
| //// on this path whether input remoting is enabled or not | |
| private void OnConnected(int id) | |
| { | |
| if (m_ConnectedIds != null && ArrayHelpers.Contains(m_ConnectedIds, id)) | |
| return; | |
| ArrayHelpers.Append(ref m_ConnectedIds, id); | |
| SendToSubscribers(InputRemoting.MessageType.Connect, new MessageEventArgs {playerId = id}); | |
| } | |
| private void OnDisconnected(int id) | |
| { | |
| if (m_ConnectedIds == null || !ArrayHelpers.Contains(m_ConnectedIds, id)) | |
| return; | |
| ArrayHelpers.Erase(ref m_ConnectedIds, id); | |
| SendToSubscribers(InputRemoting.MessageType.Disconnect, new MessageEventArgs {playerId = id}); | |
| } | |
| private void OnNewDevice(MessageEventArgs args) | |
| { | |
| SendToSubscribers(InputRemoting.MessageType.NewDevice, args); | |
| } | |
| private void OnNewLayout(MessageEventArgs args) | |
| { | |
| SendToSubscribers(InputRemoting.MessageType.NewLayout, args); | |
| } | |
| private void OnNewEvents(MessageEventArgs args) | |
| { | |
| SendToSubscribers(InputRemoting.MessageType.NewEvents, args); | |
| } | |
| private void OnRemoveDevice(MessageEventArgs args) | |
| { | |
| SendToSubscribers(InputRemoting.MessageType.RemoveDevice, args); | |
| } | |
| private void OnChangeUsages(MessageEventArgs args) | |
| { | |
| SendToSubscribers(InputRemoting.MessageType.ChangeUsages, args); | |
| } | |
| private void OnStartSending(MessageEventArgs args) | |
| { | |
| SendToSubscribers(InputRemoting.MessageType.StartSending, args); | |
| } | |
| private void OnStopSending(MessageEventArgs args) | |
| { | |
| SendToSubscribers(InputRemoting.MessageType.StopSending, args); | |
| } | |
| private void SendToSubscribers(InputRemoting.MessageType type, MessageEventArgs args) | |
| { | |
| if (m_Subscribers == null) | |
| return; | |
| var msg = new InputRemoting.Message | |
| { | |
| participantId = args.playerId, | |
| type = type, | |
| data = args.data | |
| }; | |
| for (var i = 0; i < m_Subscribers.Length; ++i) | |
| m_Subscribers[i].observer.OnNext(msg); | |
| } | |
| void IObserver<InputRemoting.Message>.OnNext(InputRemoting.Message msg) | |
| { | |
| if (m_Connection == null) | |
| return; | |
| ////TODO: this should really be sending to a specific player in the editor (can't | |
| //// do that through the IEditorPlayerConnection interface though) | |
| switch (msg.type) | |
| { | |
| case InputRemoting.MessageType.NewDevice: | |
| m_Connection.Send(kNewDeviceMsg, msg.data); | |
| break; | |
| case InputRemoting.MessageType.NewLayout: | |
| m_Connection.Send(kNewLayoutMsg, msg.data); | |
| break; | |
| case InputRemoting.MessageType.NewEvents: | |
| m_Connection.Send(kNewEventsMsg, msg.data); | |
| break; | |
| case InputRemoting.MessageType.ChangeUsages: | |
| m_Connection.Send(kChangeUsagesMsg, msg.data); | |
| break; | |
| case InputRemoting.MessageType.RemoveDevice: | |
| m_Connection.Send(kRemoveDeviceMsg, msg.data); | |
| break; | |
| } | |
| } | |
| void IObserver<InputRemoting.Message>.OnError(Exception error) | |
| { | |
| } | |
| void IObserver<InputRemoting.Message>.OnCompleted() | |
| { | |
| } | |
| [] private IEditorPlayerConnection m_Connection; | |
| [] private Subscriber[] m_Subscribers; | |
| [] private int[] m_ConnectedIds; | |
| private class Subscriber : IDisposable | |
| { | |
| public RemoteInputPlayerConnection owner; | |
| public IObserver<InputRemoting.Message> observer; | |
| public void Dispose() | |
| { | |
| ArrayHelpers.Erase(ref owner.m_Subscribers, this); | |
| } | |
| } | |
| } | |
| } | |