using System;
using System.Collections.Generic;
using MLAPI.Transports.Tasks;
namespace MLAPI.Transports.Multiplex
{
///
/// Multiplex transport adapter.
///
public class MultiplexTransportAdapter : NetworkTransport
{
///
/// The method to use to distribute the transport connectionIds in a fixed size 64 bit integer.
///
public enum ConnectionIdSpreadMethod
{
///
/// Drops the first few bits (left side) by shifting the transport clientId to the left and inserting the transportId in the first bits.
/// Ensure that ALL transports dont use the last bits in their produced clientId.
/// For incremental clientIds, this is the most space efficient assuming that every transport get used an equal amount.
///
MakeRoomLastBits,
///
/// Drops the first few bits (left side) and replaces them with the transport index.
/// Ensure that ALL transports dont use the first few bits in the produced clientId.
///
ReplaceFirstBits,
///
/// Drops the last few bits (right side) and replaces them with the transport index.
/// Ensure that ALL transports dont use the last bits in their produced clientId.
/// This option is for advanced users and will not work with the official MLAPI transports as they use the last bits.
///
ReplaceLastBits,
///
/// Drops the last few bits (right side) by shifting the transport clientId to the right and inserting the transportId in the first bits.
/// Ensure that ALL transports dont use the first bits in their produced clientId.
///
MakeRoomFirstBits,
///
/// Spreads the clientIds evenly among the transports.
///
Spread
}
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
public ConnectionIdSpreadMethod SpreadMethod = ConnectionIdSpreadMethod.MakeRoomLastBits;
public NetworkTransport[] Transports = new NetworkTransport[0];
public override ulong ServerClientId => 0;
private byte m_LastProcessedTransportIndex;
public override bool IsSupported => true;
public override void DisconnectLocalClient()
{
Transports[GetFirstSupportedTransportIndex()].DisconnectLocalClient();
}
public override void DisconnectRemoteClient(ulong clientId)
{
GetMultiplexTransportDetails(clientId, out byte transportId, out ulong connectionId);
Transports[transportId].DisconnectRemoteClient(connectionId);
}
public override ulong GetCurrentRtt(ulong clientId)
{
GetMultiplexTransportDetails(clientId, out byte transportId, out ulong connectionId);
return Transports[transportId].GetCurrentRtt(connectionId);
}
public override void Init()
{
for (int i = 0; i < Transports.Length; i++)
{
if (Transports[i].IsSupported)
{
Transports[i].Init();
}
}
}
public override NetworkEvent PollEvent(out ulong clientId, out NetworkChannel networkChannel, out ArraySegment payload, out float receiveTime)
{
if (m_LastProcessedTransportIndex >= Transports.Length - 1)
{
m_LastProcessedTransportIndex = 0;
}
for (byte i = m_LastProcessedTransportIndex; i < Transports.Length; i++)
{
m_LastProcessedTransportIndex = i;
if (Transports[i].IsSupported)
{
var networkEvent = Transports[i].PollEvent(out ulong connectionId, out networkChannel, out payload, out receiveTime);
if (networkEvent != NetworkEvent.Nothing)
{
clientId = GetMLAPIClientId(i, connectionId, false);
return networkEvent;
}
}
}
clientId = 0;
networkChannel = 0;
payload = new ArraySegment();
receiveTime = 0;
return NetworkEvent.Nothing;
}
public override void Send(ulong clientId, ArraySegment data, NetworkChannel networkChannel)
{
GetMultiplexTransportDetails(clientId, out byte transportId, out ulong connectionId);
Transports[transportId].Send(connectionId, data, networkChannel);
}
public override void Shutdown()
{
for (int i = 0; i < Transports.Length; i++)
{
if (Transports[i].IsSupported)
{
Transports[i].Shutdown();
}
}
}
public override SocketTasks StartClient()
{
var socketTasks = new List();
for (int i = 0; i < Transports.Length; i++)
{
if (Transports[i].IsSupported)
{
socketTasks.AddRange(Transports[i].StartClient().Tasks);
}
}
return new SocketTasks { Tasks = socketTasks.ToArray() };
}
public override SocketTasks StartServer()
{
var socketTasks = new List();
for (int i = 0; i < Transports.Length; i++)
{
if (Transports[i].IsSupported)
{
socketTasks.AddRange(Transports[i].StartServer().Tasks);
}
}
return new SocketTasks { Tasks = socketTasks.ToArray() };
}
public ulong GetMLAPIClientId(byte transportId, ulong connectionId, bool isServer)
{
if (isServer)
{
return ServerClientId;
}
switch (SpreadMethod)
{
case ConnectionIdSpreadMethod.ReplaceFirstBits:
{
// Calculate bits to store transportId
byte bits = (byte)UnityEngine.Mathf.CeilToInt(UnityEngine.Mathf.Log(Transports.Length, 2));
// Drop first bits of connectionId
ulong clientId = ((connectionId << bits) >> bits);
// Place transportId there
ulong shiftedTransportId = (ulong)transportId << ((sizeof(ulong) * 8) - bits);
return (clientId | shiftedTransportId) + 1;
}
case ConnectionIdSpreadMethod.MakeRoomFirstBits:
{
// Calculate bits to store transportId
byte bits = (byte)UnityEngine.Mathf.CeilToInt(UnityEngine.Mathf.Log(Transports.Length, 2));
// Drop first bits of connectionId
ulong clientId = (connectionId >> bits);
// Place transportId there
ulong shiftedTransportId = (ulong)transportId << ((sizeof(ulong) * 8) - bits);
return (clientId | shiftedTransportId) + 1;
}
case ConnectionIdSpreadMethod.ReplaceLastBits:
{
// Calculate bits to store transportId
byte bits = (byte)UnityEngine.Mathf.CeilToInt(UnityEngine.Mathf.Log(Transports.Length, 2));
// Drop the last bits of connectionId
ulong clientId = ((connectionId >> bits) << bits);
// Return the transport inserted at the end
return (clientId | transportId) + 1;
}
case ConnectionIdSpreadMethod.MakeRoomLastBits:
{
// Calculate bits to store transportId
byte bits = (byte)UnityEngine.Mathf.CeilToInt(UnityEngine.Mathf.Log(Transports.Length, 2));
// Drop the last bits of connectionId
ulong clientId = (connectionId << bits);
// Return the transport inserted at the end
return (clientId | transportId) + 1;
}
case ConnectionIdSpreadMethod.Spread:
{
return (connectionId * (ulong)Transports.Length + (ulong)transportId) + 1;
}
default:
{
return ServerClientId;
}
}
}
public void GetMultiplexTransportDetails(ulong clientId, out byte transportId, out ulong connectionId)
{
if (clientId == ServerClientId)
{
transportId = GetFirstSupportedTransportIndex();
connectionId = Transports[transportId].ServerClientId;
}
else
{
switch (SpreadMethod)
{
case ConnectionIdSpreadMethod.ReplaceFirstBits:
{
// The first clientId is reserved. Thus every clientId is always offset by 1
clientId--;
// Calculate bits to store transportId
byte bits = (byte)UnityEngine.Mathf.CeilToInt(UnityEngine.Mathf.Log(Transports.Length, 2));
transportId = (byte)(clientId >> ((sizeof(ulong) * 8) - bits));
connectionId = ((clientId << bits) >> bits);
break;
}
case ConnectionIdSpreadMethod.MakeRoomFirstBits:
{
// The first clientId is reserved. Thus every clientId is always offset by 1
clientId--;
// Calculate bits to store transportId
byte bits = (byte)UnityEngine.Mathf.CeilToInt(UnityEngine.Mathf.Log(Transports.Length, 2));
transportId = (byte)(clientId >> ((sizeof(ulong) * 8) - bits));
connectionId = (clientId << bits);
break;
}
case ConnectionIdSpreadMethod.ReplaceLastBits:
{
// The first clientId is reserved. Thus every clientId is always offset by 1
clientId--;
// Calculate bits to store transportId
byte bits = (byte)UnityEngine.Mathf.CeilToInt(UnityEngine.Mathf.Log(Transports.Length, 2));
transportId = (byte)((clientId << ((sizeof(ulong) * 8) - bits)) >> ((sizeof(ulong) * 8) - bits));
connectionId = ((clientId >> bits) << bits);
break;
}
case ConnectionIdSpreadMethod.MakeRoomLastBits:
{
// The first clientId is reserved. Thus every clientId is always offset by 1
clientId--;
// Calculate bits to store transportId
byte bits = (byte)UnityEngine.Mathf.CeilToInt(UnityEngine.Mathf.Log(Transports.Length, 2));
transportId = (byte)((clientId << ((sizeof(ulong) * 8) - bits)) >> ((sizeof(ulong) * 8) - bits));
connectionId = (clientId >> bits);
break;
}
case ConnectionIdSpreadMethod.Spread:
{
// The first clientId is reserved. Thus every clientId is always offset by 1
clientId--;
transportId = (byte)(clientId % (ulong)Transports.Length);
connectionId = (clientId / (ulong)Transports.Length);
break;
}
default:
{
transportId = GetFirstSupportedTransportIndex();
connectionId = Transports[transportId].ServerClientId;
break;
}
}
}
}
public byte GetFirstSupportedTransportIndex()
{
for (byte i = 0; i < Transports.Length; i++)
{
if (Transports[i].IsSupported)
{
return i;
}
}
return 0;
}
#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member
}
}