123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306 |
- using System;
- using System.Text;
- using UnityEngine;
- namespace Mirror
- {
- // a transport that can listen to multiple underlying transport at the same time
- [DisallowMultipleComponent]
- public class MultiplexTransport : Transport
- {
- public Transport[] transports;
- Transport available;
- public void Awake()
- {
- if (transports == null || transports.Length == 0)
- {
- Debug.LogError("Multiplex transport requires at least 1 underlying transport");
- }
- }
- public override void ClientEarlyUpdate()
- {
- foreach (Transport transport in transports)
- {
- transport.ClientEarlyUpdate();
- }
- }
- public override void ServerEarlyUpdate()
- {
- foreach (Transport transport in transports)
- {
- transport.ServerEarlyUpdate();
- }
- }
- public override void ClientLateUpdate()
- {
- foreach (Transport transport in transports)
- {
- transport.ClientLateUpdate();
- }
- }
- public override void ServerLateUpdate()
- {
- foreach (Transport transport in transports)
- {
- transport.ServerLateUpdate();
- }
- }
- void OnEnable()
- {
- foreach (Transport transport in transports)
- {
- transport.enabled = true;
- }
- }
- void OnDisable()
- {
- foreach (Transport transport in transports)
- {
- transport.enabled = false;
- }
- }
- public override bool Available()
- {
- // available if any of the transports is available
- foreach (Transport transport in transports)
- {
- if (transport.Available())
- {
- return true;
- }
- }
- return false;
- }
- #region Client
- public override void ClientConnect(string address)
- {
- foreach (Transport transport in transports)
- {
- if (transport.Available())
- {
- available = transport;
- transport.OnClientConnected = OnClientConnected;
- transport.OnClientDataReceived = OnClientDataReceived;
- transport.OnClientError = OnClientError;
- transport.OnClientDisconnected = OnClientDisconnected;
- transport.ClientConnect(address);
- return;
- }
- }
- throw new ArgumentException("No transport suitable for this platform");
- }
- public override void ClientConnect(Uri uri)
- {
- foreach (Transport transport in transports)
- {
- if (transport.Available())
- {
- try
- {
- available = transport;
- transport.OnClientConnected = OnClientConnected;
- transport.OnClientDataReceived = OnClientDataReceived;
- transport.OnClientError = OnClientError;
- transport.OnClientDisconnected = OnClientDisconnected;
- transport.ClientConnect(uri);
- return;
- }
- catch (ArgumentException)
- {
- // transport does not support the schema, just move on to the next one
- }
- }
- }
- throw new ArgumentException("No transport suitable for this platform");
- }
- public override bool ClientConnected()
- {
- return (object)available != null && available.ClientConnected();
- }
- public override void ClientDisconnect()
- {
- if ((object)available != null)
- available.ClientDisconnect();
- }
- public override void ClientSend(ArraySegment<byte> segment, int channelId)
- {
- available.ClientSend(segment, 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 AddServerCallbacks()
- {
- // 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 = (baseConnectionId =>
- {
- OnServerConnected.Invoke(FromBaseId(locali, baseConnectionId));
- });
- transport.OnServerDataReceived = (baseConnectionId, data, channel) =>
- {
- OnServerDataReceived.Invoke(FromBaseId(locali, baseConnectionId), data, channel);
- };
- transport.OnServerError = (baseConnectionId, error, reason) =>
- {
- OnServerError.Invoke(FromBaseId(locali, baseConnectionId), error, reason);
- };
- transport.OnServerDisconnected = baseConnectionId =>
- {
- OnServerDisconnected.Invoke(FromBaseId(locali, baseConnectionId));
- };
- }
- }
- // for now returns the first uri,
- // should we return all available uris?
- public override Uri ServerUri()
- {
- return transports[0].ServerUri();
- }
- public override bool ServerActive()
- {
- // avoid Linq.All allocations
- foreach (Transport transport in transports)
- {
- if (!transport.ServerActive())
- {
- return false;
- }
- }
- return true;
- }
- public override string ServerGetClientAddress(int connectionId)
- {
- int baseConnectionId = ToBaseId(connectionId);
- int transportId = ToTransportId(connectionId);
- return transports[transportId].ServerGetClientAddress(baseConnectionId);
- }
- public override void ServerDisconnect(int connectionId)
- {
- int baseConnectionId = ToBaseId(connectionId);
- int transportId = ToTransportId(connectionId);
- transports[transportId].ServerDisconnect(baseConnectionId);
- }
- public override void ServerSend(int connectionId, ArraySegment<byte> segment, int channelId)
- {
- int baseConnectionId = ToBaseId(connectionId);
- int transportId = ToTransportId(connectionId);
- for (int i = 0; i < transports.Length; ++i)
- {
- if (i == transportId)
- {
- transports[i].ServerSend(baseConnectionId, segment, channelId);
- }
- }
- }
- public override void ServerStart()
- {
- foreach (Transport transport in transports)
- {
- AddServerCallbacks();
- transport.ServerStart();
- }
- }
- public override void ServerStop()
- {
- foreach (Transport transport in transports)
- {
- transport.ServerStop();
- }
- }
- #endregion
- public override int GetMaxPacketSize(int channelId = 0)
- {
- // finding the max packet size in a multiplex environment has to be
- // done very carefully:
- // * servers run multiple transports at the same time
- // * different clients run different transports
- // * there should only ever be ONE true max packet size for everyone,
- // otherwise a spawn message might be sent to all tcp sockets, but
- // be too big for some udp sockets. that would be a debugging
- // nightmare and allow for possible exploits and players on
- // different platforms seeing a different game state.
- // => the safest solution is to use the smallest max size for all
- // transports. that will never fail.
- int mininumAllowedSize = int.MaxValue;
- foreach (Transport transport in transports)
- {
- int size = transport.GetMaxPacketSize(channelId);
- mininumAllowedSize = Mathf.Min(size, mininumAllowedSize);
- }
- return mininumAllowedSize;
- }
- 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();
- }
- }
- }
|