diff --git a/Assets/Mirror/Runtime/Transport/LLAPITransport.cs b/Assets/Mirror/Runtime/Transport/LLAPITransport.cs index d8c98202d..ebbc80cbe 100644 --- a/Assets/Mirror/Runtime/Transport/LLAPITransport.cs +++ b/Assets/Mirror/Runtime/Transport/LLAPITransport.cs @@ -174,6 +174,12 @@ public override void ClientDisconnect() } } + public override bool Available() + { + // websocket is available in all platforms (including webgl) + return useWebsockets || base.Available(); + } + // server ////////////////////////////////////////////////////////////// public override bool ServerActive() { diff --git a/Assets/Mirror/Runtime/Transport/MultiplexTransport.cs b/Assets/Mirror/Runtime/Transport/MultiplexTransport.cs new file mode 100644 index 000000000..808102685 --- /dev/null +++ b/Assets/Mirror/Runtime/Transport/MultiplexTransport.cs @@ -0,0 +1,191 @@ +using System; +using System.Linq; +using System.Text; +using UnityEngine; + +namespace Mirror +{ + // a transport that can listen to multiple underlying transport at the same time + public class MultiplexTransport : Transport + { + public Transport[] transports; + + public void Awake() + { + if (transports == null || transports.Length == 0) + { + Debug.LogError("Multiplex transport requires at least 1 underlying transport"); + } + InitClient(); + InitServer(); + } + + #region Client + // clients always pick the first transport + void InitClient() + { + // wire all the base transports to my events + foreach (Transport transport in transports) + { + transport.OnClientConnected.AddListener(OnClientConnected.Invoke ); + transport.OnClientDataReceived.AddListener(OnClientDataReceived.Invoke); + transport.OnClientError.AddListener(OnClientError.Invoke ); + transport.OnClientDisconnected.AddListener(OnClientDisconnected.Invoke); + } + } + + // The client just uses the first transport available + Transport getAvailableTransport() + { + foreach (Transport transport in transports) + { + if (transport.Available()) + { + return transport; + } + } + throw new Exception("No transport suitable for this platform"); + } + + public override void ClientConnect(string address) + { + getAvailableTransport().ClientConnect(address); + } + + public override bool ClientConnected() + { + return getAvailableTransport().ClientConnected(); + } + + public override void ClientDisconnect() + { + getAvailableTransport().ClientDisconnect(); + } + + public override bool ClientSend(int channelId, byte[] data) + { + return getAvailableTransport().ClientSend(channelId, data); + } + + public override int GetMaxPacketSize(int channelId = 0) + { + return getAvailableTransport().GetMaxPacketSize(channelId); + } + + #endregion + + + #region Server + // connection ids get mapped to base transports + // if we have 3 transports, then + // transport 0 will produce connection ids [0, 3, 6, 9, ...] + // transport 1 will produce connection ids [1, 4, 7, 10, ...] + // transport 2 will produce connection ids [2, 5, 8, 11, ...] + int FromBaseId(int transportId, int connectionId) + { + return connectionId * transports.Length + transportId; + } + + int ToBaseId(int connectionId) + { + return connectionId / transports.Length; + } + + int ToTransportId(int connectionId) + { + return connectionId % transports.Length; + } + + void InitServer() + { + // wire all the base transports to my events + for (int i = 0; i < transports.Length; i++) + { + // this is required for the handlers, if I use i directly + // then all the handlers will use the last i + int locali = i; + Transport transport = transports[i]; + + transport.OnServerConnected.AddListener(baseConnectionId => + { + OnServerConnected.Invoke(FromBaseId(locali, baseConnectionId)); + }); + + transport.OnServerDataReceived.AddListener((baseConnectionId, data) => + { + OnServerDataReceived.Invoke(FromBaseId(locali, baseConnectionId), data); + }); + + transport.OnServerError.AddListener((baseConnectionId, error) => + { + OnServerError.Invoke(FromBaseId(locali, baseConnectionId), error); + }); + transport.OnServerDisconnected.AddListener(baseConnectionId => + { + OnServerDisconnected.Invoke(FromBaseId(locali, baseConnectionId)); + }); + } + } + + public override bool ServerActive() + { + return transports.All(t => t.ServerActive()); + } + + public override bool GetConnectionInfo(int connectionId, out string address) + { + int baseConnectionId = ToBaseId(connectionId); + int transportId = ToTransportId(connectionId); + return transports[transportId].GetConnectionInfo(baseConnectionId, out address); + } + + public override bool ServerDisconnect(int connectionId) + { + int baseConnectionId = ToBaseId(connectionId); + int transportId = ToTransportId(connectionId); + return transports[transportId].ServerDisconnect(baseConnectionId); + } + + public override bool ServerSend(int connectionId, int channelId, byte[] data) + { + int baseConnectionId = ToBaseId(connectionId); + int transportId = ToTransportId(connectionId); + return transports[transportId].ServerSend(baseConnectionId, channelId, data); + } + + public override void ServerStart() + { + foreach (Transport transport in transports) + { + transport.ServerStart(); + } + } + + public override void ServerStop() + { + foreach (Transport transport in transports) + { + transport.ServerStop(); + } + } + #endregion + + public override void Shutdown() + { + foreach (Transport transport in transports) + { + transport.Shutdown(); + } + } + + public override string ToString() + { + StringBuilder builder = new StringBuilder(); + foreach (Transport transport in transports) + { + builder.AppendLine(transport.ToString()); + } + return builder.ToString().Trim(); + } + } +} diff --git a/Assets/Mirror/Runtime/Transport/MultiplexTransport.cs.meta b/Assets/Mirror/Runtime/Transport/MultiplexTransport.cs.meta new file mode 100644 index 000000000..40394aab8 --- /dev/null +++ b/Assets/Mirror/Runtime/Transport/MultiplexTransport.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 929e3234c7db540b899f00183fc2b1fe +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Runtime/Transport/Transport.cs b/Assets/Mirror/Runtime/Transport/Transport.cs index 73375ef3a..ac85cb940 100644 --- a/Assets/Mirror/Runtime/Transport/Transport.cs +++ b/Assets/Mirror/Runtime/Transport/Transport.cs @@ -26,10 +26,20 @@ public abstract class Transport : MonoBehaviour public abstract bool ClientSend(int channelId, byte[] data); public abstract void ClientDisconnect(); + // determines if the transport is available for this platform + // by default a transport is available in all platforms except webgl + public virtual bool Available() + { + return Application.platform != RuntimePlatform.WebGLPlayer; + } + + // server [HideInInspector] public UnityEventInt OnServerConnected; [HideInInspector] public UnityEventIntByteArray OnServerDataReceived; [HideInInspector] public UnityEventIntException OnServerError; + + [HideInInspector] public UnityEventInt OnServerDisconnected; public abstract bool ServerActive();