Initial Commit

This commit is contained in:
Sebastian Cabrera 2021-08-02 05:44:37 -04:00
parent 53eb92e9af
commit 270ab7d11f
15341 changed files with 700234 additions and 0 deletions

View file

@ -0,0 +1,330 @@
using System;
using System.Collections.Generic;
using MLAPI.Transports.Tasks;
namespace MLAPI.Transports.Multiplex
{
/// <summary>
/// Multiplex transport adapter.
/// </summary>
public class MultiplexTransportAdapter : NetworkTransport
{
/// <summary>
/// The method to use to distribute the transport connectionIds in a fixed size 64 bit integer.
/// </summary>
public enum ConnectionIdSpreadMethod
{
/// <summary>
/// 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.
/// </summary>
MakeRoomLastBits,
/// <summary>
/// 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.
/// </summary>
ReplaceFirstBits,
/// <summary>
/// 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.
/// </summary>
ReplaceLastBits,
/// <summary>
/// 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.
/// </summary>
MakeRoomFirstBits,
/// <summary>
/// Spreads the clientIds evenly among the transports.
/// </summary>
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<byte> 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<byte>();
receiveTime = 0;
return NetworkEvent.Nothing;
}
public override void Send(ulong clientId, ArraySegment<byte> 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<SocketTask>();
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<SocketTask>();
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
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: adf0b801ebdee8c4b838e20292b36ac4
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,33 @@
namespace MLAPI.Transports
{
/// <summary>
/// Delivery methods
/// </summary>
public enum NetworkDelivery
{
/// <summary>
/// Unreliable message
/// </summary>
Unreliable,
/// <summary>
/// Unreliable with sequencing
/// </summary>
UnreliableSequenced,
/// <summary>
/// Reliable message
/// </summary>
Reliable,
/// <summary>
/// Reliable message where messages are guaranteed to be in the right order
/// </summary>
ReliableSequenced,
/// <summary>
/// A reliable message with guaranteed order with fragmentation support
/// </summary>
ReliableFragmentedSequenced
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d0151d4283b329e498204f86534292ef
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,28 @@
namespace MLAPI.Transports
{
/// <summary>
/// Represents a netEvent when polling
/// </summary>
public enum NetworkEvent
{
/// <summary>
/// New data is received
/// </summary>
Data,
/// <summary>
/// A client is connected, or client connected to server
/// </summary>
Connect,
/// <summary>
/// A client disconnected, or client disconnected from server
/// </summary>
Disconnect,
/// <summary>
/// No new event
/// </summary>
Nothing
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5c875a40b1475a5419ffd2a6b002cad2
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,182 @@
using System;
using System.Collections.Generic;
using MLAPI.Transports.Tasks;
using UnityEngine;
namespace MLAPI.Transports
{
public enum NetworkChannel : byte
{
Internal,
TimeSync,
ReliableRpc,
UnreliableRpc,
SyncChannel,
DefaultMessage,
PositionUpdate,
AnimationUpdate,
NavAgentState,
NavAgentCorrection,
ChannelUnused, // <<-- must be present, and must be last
};
/// <summary>
/// A network transport
/// </summary>
public abstract class NetworkTransport : MonoBehaviour
{
/// <summary>
/// Delegate used to request channels on the underlying transport.
/// </summary>
public delegate void RequestChannelsDelegate(List<TransportChannel> channels);
/// <summary>
/// Delegate called when the transport wants to know what channels to register.
/// </summary>
public event RequestChannelsDelegate OnChannelRegistration;
/// <summary>
/// A constant clientId that represents the server.
/// When this value is found in methods such as Send, it should be treated as a placeholder that means "the server"
/// </summary>
public abstract ulong ServerClientId { get; }
/// <summary>
/// Gets a value indicating whether this <see cref="T:MLAPI.Transports.Transport"/> is supported in the current runtime context.
/// This is used by multiplex adapters.
/// </summary>
/// <value><c>true</c> if is supported; otherwise, <c>false</c>.</value>
public virtual bool IsSupported => true;
private TransportChannel[] m_ChannelsCache = null;
internal void ResetChannelCache()
{
m_ChannelsCache = null;
}
public TransportChannel[] MLAPI_CHANNELS
{
get
{
if (m_ChannelsCache == null)
{
var transportChannels = new List<TransportChannel>();
OnChannelRegistration?.Invoke(transportChannels);
m_ChannelsCache = new TransportChannel[MLAPI_INTERNAL_CHANNELS.Length + transportChannels.Count];
for (int i = 0; i < MLAPI_INTERNAL_CHANNELS.Length; i++)
{
m_ChannelsCache[i] = MLAPI_INTERNAL_CHANNELS[i];
}
for (int i = 0; i < transportChannels.Count; i++)
{
m_ChannelsCache[i + MLAPI_INTERNAL_CHANNELS.Length] = transportChannels[i];
}
}
return m_ChannelsCache;
}
}
/// <summary>
/// The channels the MLAPI will use when sending internal messages.
/// </summary>
private readonly TransportChannel[] MLAPI_INTERNAL_CHANNELS =
{
new TransportChannel(NetworkChannel.Internal, NetworkDelivery.ReliableFragmentedSequenced),
new TransportChannel(NetworkChannel.ReliableRpc, NetworkDelivery.ReliableSequenced),
new TransportChannel(NetworkChannel.UnreliableRpc, NetworkDelivery.UnreliableSequenced),
new TransportChannel(NetworkChannel.TimeSync, NetworkDelivery.Unreliable),
new TransportChannel(NetworkChannel.SyncChannel, NetworkDelivery.Unreliable),
new TransportChannel(NetworkChannel.DefaultMessage, NetworkDelivery.Reliable),
new TransportChannel(NetworkChannel.PositionUpdate, NetworkDelivery.UnreliableSequenced),
new TransportChannel(NetworkChannel.AnimationUpdate, NetworkDelivery.ReliableSequenced),
new TransportChannel(NetworkChannel.NavAgentState, NetworkDelivery.ReliableSequenced),
new TransportChannel(NetworkChannel.NavAgentCorrection, NetworkDelivery.UnreliableSequenced),
};
/// <summary>
/// Delegate for transport events.
/// </summary>
public delegate void TransportEventDelegate(NetworkEvent type, ulong clientId, NetworkChannel networkChannel, ArraySegment<byte> payload, float receiveTime);
/// <summary>
/// Occurs when the transport has a new transport event. Can be used to make an event based transport instead of a poll based.
/// Invokation has to occur on the Unity thread in the Update loop.
/// </summary>
public event TransportEventDelegate OnTransportEvent;
/// <summary>
/// Invokes the <see cref="OnTransportEvent"/>. Invokation has to occur on the Unity thread in the Update loop.
/// </summary>
/// <param name="type">The event type</param>
/// <param name="clientId">The clientId this event is for</param>
/// <param name="channelName">The channel the data arrived at. This is usually used when responding to things like RPCs</param>
/// <param name="payload">The incoming data payload</param>
/// <param name="receiveTime">The time the event was received, as reported by Time.realtimeSinceStartup.</param>
protected void InvokeOnTransportEvent(NetworkEvent type, ulong clientId, NetworkChannel networkChannel, ArraySegment<byte> payload, float receiveTime)
{
OnTransportEvent?.Invoke(type, clientId, networkChannel, payload, receiveTime);
}
/// <summary>
/// Send a payload to the specified clientId, data and channelName.
/// </summary>
/// <param name="clientId">The clientId to send to</param>
/// <param name="data">The data to send</param>
/// <param name="channelName">The channel to send data to</param>
public abstract void Send(ulong clientId, ArraySegment<byte> data, NetworkChannel networkChannel);
/// <summary>
/// Polls for incoming events, with an extra output parameter to report the precise time the event was received.
/// </summary>
/// <param name="clientId">The clientId this event is for</param>
/// <param name="channelName">The channel the data arrived at. This is usually used when responding to things like RPCs</param>
/// <param name="payload">The incoming data payload</param>
/// <param name="receiveTime">The time the event was received, as reported by Time.realtimeSinceStartup.</param>
/// <returns>Returns the event type</returns>
public abstract NetworkEvent PollEvent(out ulong clientId, out NetworkChannel networkChannel, out ArraySegment<byte> payload, out float receiveTime);
/// <summary>
/// Connects client to server
/// </summary>
public abstract SocketTasks StartClient();
/// <summary>
/// Starts to listen for incoming clients.
/// </summary>
public abstract SocketTasks StartServer();
/// <summary>
/// Disconnects a client from the server
/// </summary>
/// <param name="clientId">The clientId to disconnect</param>
public abstract void DisconnectRemoteClient(ulong clientId);
/// <summary>
/// Disconnects the local client from the server
/// </summary>
public abstract void DisconnectLocalClient();
/// <summary>
/// Gets the round trip time for a specific client. This method is optional
/// </summary>
/// <param name="clientId">The clientId to get the rtt from</param>
/// <returns>Returns the round trip time in milliseconds </returns>
public abstract ulong GetCurrentRtt(ulong clientId);
/// <summary>
/// Shuts down the transport
/// </summary>
public abstract void Shutdown();
/// <summary>
/// Initializes the transport
/// </summary>
public abstract void Init();
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e47bade9963ff5b41b86d331006b5f67
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 1f6f5dde2cfa0614eb5985556ca1ac17
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,198 @@
using System;
using System.Net.Sockets;
namespace MLAPI.Transports.Tasks
{
/// <summary>
/// Represents one or more socket tasks.
/// </summary>
public class SocketTasks
{
/// <summary>
/// Gets or sets the underlying SocketTasks.
/// </summary>
/// <value>The tasks.</value>
public SocketTask[] Tasks { get; set; }
/// <summary>
/// Gets a value indicating whether this all tasks is done.
/// </summary>
/// <value><c>true</c> if is done; otherwise, <c>false</c>.</value>
public bool IsDone
{
get
{
for (int i = 0; i < Tasks.Length; i++)
{
if (!Tasks[i].IsDone)
{
return false;
}
}
return true;
}
}
/// <summary>
/// Gets a value indicating whether all tasks were sucessful.
/// </summary>
/// <value><c>true</c> if success; otherwise, <c>false</c>.</value>
public bool Success
{
get
{
for (int i = 0; i < Tasks.Length; i++)
{
if (!Tasks[i].Success)
{
return false;
}
}
return true;
}
}
/// <summary>
/// Gets a value indicating whether any tasks were successful.
/// </summary>
/// <value><c>true</c> if any success; otherwise, <c>false</c>.</value>
public bool AnySuccess
{
get
{
for (int i = 0; i < Tasks.Length; i++)
{
if (Tasks[i].Success)
{
return true;
}
}
return false;
}
}
/// <summary>
/// Gets a value indicating whether any tasks are done.
/// </summary>
/// <value><c>true</c> if any done; otherwise, <c>false</c>.</value>
public bool AnyDone
{
get
{
for (int i = 0; i < Tasks.Length; i++)
{
if (Tasks[i].IsDone)
{
return true;
}
}
return false;
}
}
}
/// <summary>
/// A single socket task.
/// </summary>
public class SocketTask
{
// Used for states
/// <summary>
/// Gets or sets a value indicating whether this <see cref="T:MLAPI.Transports.Tasks.SocketTask"/> is done.
/// </summary>
/// <value><c>true</c> if is done; otherwise, <c>false</c>.</value>
public bool IsDone { get; set; }
/// <summary>
/// Gets or sets a value indicating whether this <see cref="T:MLAPI.Transports.Tasks.SocketTask"/> is success.
/// </summary>
/// <value><c>true</c> if success; otherwise, <c>false</c>.</value>
public bool Success { get; set; }
// These are all set by the transport
/// <summary>
/// Gets or sets the transport exception.
/// </summary>
/// <value>The transport exception.</value>
public Exception TransportException { get; set; }
/// <summary>
/// Gets or sets the socket error.
/// </summary>
/// <value>The socket error.</value>
public SocketError SocketError { get; set; }
/// <summary>
/// Gets or sets the transport code.
/// </summary>
/// <value>The transport code.</value>
public int TransportCode { get; set; }
/// <summary>
/// Gets or sets the message.
/// </summary>
/// <value>The message.</value>
public string Message { get; set; }
/// <summary>
/// Gets or sets the state.
/// </summary>
/// <value>The state.</value>
public object State { get; set; }
/// <summary>
/// Gets a done task.
/// </summary>
/// <value>The done.</value>
public static SocketTask Done => new SocketTask
{
IsDone = true,
Message = null,
SocketError = SocketError.Success,
State = null,
Success = true,
TransportCode = -1,
TransportException = null
};
/// <summary>
/// Gets a faulty task.
/// </summary>
/// <value>The fault.</value>
public static SocketTask Fault => new SocketTask
{
IsDone = true,
Message = null,
SocketError = SocketError.SocketError,
State = null,
Success = false,
TransportCode = -1,
TransportException = null
};
/// <summary>
/// Gets a working task.
/// </summary>
/// <value>The working.</value>
public static SocketTask Working => new SocketTask
{
IsDone = false,
Message = null,
SocketError = SocketError.SocketError,
State = null,
Success = false,
TransportCode = -1,
TransportException = null
};
/// <summary>
/// Converts to a SocketTasks.
/// </summary>
/// <returns>The tasks.</returns>
public SocketTasks AsTasks() => new SocketTasks { Tasks = new SocketTask[] { this } };
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1be79791c1bf89b4db05351e0f45d650
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 8ed76ef44843842b3ad15c253703e5c5
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: e6aab111d2a894a7e836bdd742e248d9
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,72 @@
using System;
using MLAPI;
using MLAPI.Configuration;
using MLAPI.Transports;
using NUnit.Framework;
using UnityEngine;
using MLAPI.Transports.UNET;
using UnityEngine.Networking;
public class TransportTest : MonoBehaviour
{
// hack, remove any NetworkObject's from the scene to avoid spawning scene objects
// which themselves might not be able initialize themselves properly
private void ForceNetworkObjectShutdown()
{
NetworkObject[] networkObjects = FindObjectsOfType<NetworkObject>();
for (int i = 0; i < networkObjects.Length; i++)
{
DestroyImmediate(networkObjects[i]);
}
}
// A Test behaves as an ordinary method
[Test]
public void UNetCustomChannelRegistrationTest()
{
ForceNetworkObjectShutdown();
GameObject o = new GameObject();
NetworkManager nm = (NetworkManager)o.AddComponent(typeof(NetworkManager));
nm.SetSingleton();
nm.NetworkConfig = new NetworkConfig();
UNetTransport ut = (UNetTransport)o.AddComponent(typeof(UNetTransport));
ut.ServerListenPort = 7777;
nm.NetworkConfig.NetworkTransport = ut;
byte CustomChannel = 0;
// test 1: add a legit channel.
ut.Channels.Add(new UNetChannel { Id = NetworkChannel.ChannelUnused + CustomChannel, Type = QosType.Unreliable });
try
{
nm.StartServer();
}
catch
{
Assert.Fail("The UNet transport won't allow registration of a legit user channel");
}
nm.StopServer();
nm.Shutdown();
ut.Channels.Clear();
// test 2: add a bogus channel (one that intersects with the MLAPI built-in ones.) Expect failure
ut.Channels.Add(new UNetChannel { Id = NetworkChannel.Internal, Type = QosType.Unreliable });
try
{
nm.StartServer();
Assert.Fail("The UNet transport allowed registration of an MLAPI-reserved channel");
}
catch (Exception ex)
{
Debug.Log(ex.Message);
}
nm.StopServer();
nm.Shutdown();
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f94b11ceafb6c426aa67067ec95539ed
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,24 @@
{
"name": "Unity.Multiplayer.MLAPI.Transports.EditorTests",
"rootNamespace": "",
"references": [
"Unity.Multiplayer.MLAPI.Runtime",
"UnityEngine.TestRunner",
"UnityEditor.TestRunner"
],
"includePlatforms": [
"Editor"
],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": true,
"precompiledReferences": [
"nunit.framework.dll"
],
"autoReferenced": false,
"defineConstraints": [
"UNITY_INCLUDE_TESTS"
],
"versionDefines": [],
"noEngineReferences": false
}

View file

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: cf3c8d3561be742cc889cc4e329fe386
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,27 @@
using System;
namespace MLAPI.Transports
{
/// <summary>
/// A transport channel used by the MLAPI
/// </summary>
[Serializable]
public struct TransportChannel
{
public TransportChannel(NetworkChannel channel, NetworkDelivery delivery)
{
Channel = channel;
Delivery = delivery;
}
/// <summary>
/// Channel identifier
/// </summary>
public NetworkChannel Channel;
/// <summary>
/// Delivery type
/// </summary>
public NetworkDelivery Delivery;
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a7bc986a2f8f5584988f87dbf35965fe
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 901256c415b484744b9da9551d85073a
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,8 @@
namespace MLAPI.Transports.UNET
{
public static class ProfilerConstants
{
public const string NumberOfTransportSends = nameof(NumberOfTransportSends);
public const string NumberOfTransportSendQueues = nameof(NumberOfTransportSendQueues);
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c5bc85b7565054d6295b7847967cdbd8
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,442 @@
#pragma warning disable 618
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
using System;
using System.Collections.Generic;
using System.Net;
using UnityEngine;
using UnityEngine.Networking;
namespace MLAPI.Transports.UNET
{
public static class RelayTransport
{
private enum MessageType
{
StartServer,
ConnectToServer,
Data,
ClientDisconnect,
AddressReport
}
private static byte s_DefaultChannelId;
private static int s_RelayConnectionId;
private static bool s_IsClient = false;
private static string s_Address;
private static ushort s_Port;
private static List<ChannelQOS> s_Channels = new List<ChannelQOS>();
public static bool Enabled { get; set; } = true;
public static string RelayAddress { get; set; } = "127.0.0.1";
public static ushort RelayPort { get; set; } = 8888;
public static event Action<IPEndPoint> OnRemoteEndpointReported;
public static int Connect(int hostId, string serverAddress, int serverPort, int exceptionConnectionId, out byte error)
{
if (!Enabled) return UnityEngine.Networking.NetworkTransport.Connect(hostId, serverAddress, serverPort, exceptionConnectionId, out error);
s_IsClient = true;
s_Address = serverAddress;
s_Port = (ushort)serverPort;
s_RelayConnectionId = UnityEngine.Networking.NetworkTransport.Connect(hostId, RelayAddress, RelayPort, exceptionConnectionId, out error); // Requests connection
return s_RelayConnectionId;
}
public static int ConnectWithSimulator(int hostId, string serverAddress, int serverPort, int exceptionConnectionId, out byte error, ConnectionSimulatorConfig conf)
{
if (!Enabled) return UnityEngine.Networking.NetworkTransport.ConnectWithSimulator(hostId, serverAddress, serverPort, exceptionConnectionId, out error, conf);
s_IsClient = true;
s_Address = serverAddress;
s_Port = (ushort)serverPort;
s_RelayConnectionId = UnityEngine.Networking.NetworkTransport.ConnectWithSimulator(hostId, RelayAddress, RelayPort, exceptionConnectionId, out error, conf); // Requests connection
return s_RelayConnectionId;
}
public static int ConnectEndPoint(int hostId, EndPoint endPoint, int exceptionConnectionId, out byte error)
{
if (!Enabled) return UnityEngine.Networking.NetworkTransport.ConnectEndPoint(hostId, endPoint, exceptionConnectionId, out error);
s_IsClient = true;
s_Address = ((IPEndPoint)endPoint).Address.ToString();
s_Port = (ushort)((IPEndPoint)endPoint).Port;
s_RelayConnectionId = UnityEngine.Networking.NetworkTransport.Connect(hostId, RelayAddress, RelayPort, exceptionConnectionId, out error); // Requests connection
return s_RelayConnectionId;
}
private static void SetChannelsFromTopology(HostTopology topology) => s_Channels = topology.DefaultConfig.Channels;
public static int AddHost(HostTopology topology, bool createServer)
{
if (!Enabled) return UnityEngine.Networking.NetworkTransport.AddHost(topology, 0, null);
s_IsClient = !createServer;
s_DefaultChannelId = topology.DefaultConfig.AddChannel(QosType.ReliableSequenced);
SetChannelsFromTopology(topology);
int ret = UnityEngine.Networking.NetworkTransport.AddHost(topology, 0, null);
if (createServer) s_RelayConnectionId = UnityEngine.Networking.NetworkTransport.Connect(ret, RelayAddress, RelayPort, 0, out byte b);
return ret;
}
public static int AddHost(HostTopology topology, int port, bool createServer)
{
if (!Enabled) return UnityEngine.Networking.NetworkTransport.AddHost(topology, port);
s_IsClient = !createServer;
s_DefaultChannelId = topology.DefaultConfig.AddChannel(QosType.ReliableSequenced);
SetChannelsFromTopology(topology);
int ret = UnityEngine.Networking.NetworkTransport.AddHost(topology, port);
if (createServer) s_RelayConnectionId = UnityEngine.Networking.NetworkTransport.Connect(ret, RelayAddress, RelayPort, 0, out byte b);
return ret;
}
public static int AddHost(HostTopology topology, int port, string ip, bool createServer)
{
if (!Enabled) return UnityEngine.Networking.NetworkTransport.AddHost(topology, port, ip);
s_IsClient = !createServer;
s_DefaultChannelId = topology.DefaultConfig.AddChannel(QosType.ReliableSequenced);
SetChannelsFromTopology(topology);
int ret = UnityEngine.Networking.NetworkTransport.AddHost(topology, port, ip);
if (createServer) s_RelayConnectionId = UnityEngine.Networking.NetworkTransport.Connect(ret, RelayAddress, RelayPort, 0, out byte b);
return ret;
}
public static int AddHostWithSimulator(HostTopology topology, int minTimeout, int maxTimeout, int port, string ip, bool createServer)
{
if (!Enabled) return UnityEngine.Networking.NetworkTransport.AddHostWithSimulator(topology, minTimeout, maxTimeout);
s_IsClient = !createServer;
s_DefaultChannelId = topology.DefaultConfig.AddChannel(QosType.ReliableSequenced);
SetChannelsFromTopology(topology);
int ret = UnityEngine.Networking.NetworkTransport.AddHostWithSimulator(topology, minTimeout, maxTimeout, port, ip);
if (createServer) s_RelayConnectionId = UnityEngine.Networking.NetworkTransport.Connect(ret, RelayAddress, RelayPort, 0, out byte b);
return ret;
}
public static int AddHostWithSimulator(HostTopology topology, int minTimeout, int maxTimeout, bool createServer)
{
if (!Enabled) return UnityEngine.Networking.NetworkTransport.AddHostWithSimulator(topology, minTimeout, maxTimeout);
s_IsClient = !createServer;
s_DefaultChannelId = topology.DefaultConfig.AddChannel(QosType.ReliableSequenced);
SetChannelsFromTopology(topology);
int ret = UnityEngine.Networking.NetworkTransport.AddHostWithSimulator(topology, minTimeout, maxTimeout);
if (createServer) s_RelayConnectionId = UnityEngine.Networking.NetworkTransport.Connect(ret, RelayAddress, RelayPort, 0, out byte b);
return ret;
}
public static int AddHostWithSimulator(HostTopology topology, int minTimeout, int maxTimeout, int port, bool createServer)
{
if (!Enabled) return UnityEngine.Networking.NetworkTransport.AddHostWithSimulator(topology, minTimeout, maxTimeout, port);
s_IsClient = !createServer;
SetChannelsFromTopology(topology);
int ret = UnityEngine.Networking.NetworkTransport.AddHostWithSimulator(topology, minTimeout, maxTimeout, port);
if (createServer) s_RelayConnectionId = UnityEngine.Networking.NetworkTransport.Connect(ret, RelayAddress, RelayPort, 0, out byte b);
return ret;
}
public static int AddWebsocketHost(HostTopology topology, int port, bool createServer)
{
if (!Enabled) return UnityEngine.Networking.NetworkTransport.AddWebsocketHost(topology, port);
s_IsClient = !createServer;
s_DefaultChannelId = topology.DefaultConfig.AddChannel(QosType.ReliableSequenced);
SetChannelsFromTopology(topology);
int ret = UnityEngine.Networking.NetworkTransport.AddWebsocketHost(topology, port);
if (createServer) s_RelayConnectionId = UnityEngine.Networking.NetworkTransport.Connect(ret, RelayAddress, RelayPort, 0, out byte b);
return ret;
}
public static int AddWebsocketHost(HostTopology topology, int port, string ip, bool createServer)
{
if (!Enabled) return UnityEngine.Networking.NetworkTransport.AddWebsocketHost(topology, port, ip);
s_IsClient = !createServer;
s_DefaultChannelId = topology.DefaultConfig.AddChannel(QosType.ReliableSequenced);
SetChannelsFromTopology(topology);
int ret = UnityEngine.Networking.NetworkTransport.AddWebsocketHost(topology, port, ip);
if (createServer) s_RelayConnectionId = UnityEngine.Networking.NetworkTransport.Connect(ret, RelayAddress, RelayPort, 0, out byte b);
return ret;
}
private static readonly byte[] disconnectBuffer = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, (byte)MessageType.ClientDisconnect };
public static bool Disconnect(int hostId, int connectionId, out byte error)
{
if (!Enabled) return UnityEngine.Networking.NetworkTransport.Disconnect(hostId, connectionId, out error);
if (!s_IsClient)
{
for (byte i = 0; i < sizeof(ulong); i++) disconnectBuffer[i] = ((byte)((ulong)connectionId >> (i * 8)));
return UnityEngine.Networking.NetworkTransport.Send(hostId, s_RelayConnectionId, s_DefaultChannelId, disconnectBuffer, 9, out error);
}
return UnityEngine.Networking.NetworkTransport.Disconnect(hostId, connectionId, out error);
}
public static bool Send(int hostId, int connectionId, int channelId, byte[] buffer, int size, out byte error)
{
if (!Enabled) return UnityEngine.Networking.NetworkTransport.Send(hostId, connectionId, channelId, buffer, size, out error);
++size;
if (!s_IsClient)
{
size += 8;
int connectionIdOffset = size - 9;
for (byte i = 0; i < sizeof(ulong); i++) buffer[connectionIdOffset + i] = ((byte)((ulong)connectionId >> (i * 8)));
}
buffer[size - 1] = (byte)MessageType.Data;
return UnityEngine.Networking.NetworkTransport.Send(hostId, s_RelayConnectionId, channelId, buffer, size, out error);
}
public static bool QueueMessageForSending(int hostId, int connectionId, int channelId, byte[] buffer, int size, out byte error)
{
if (!Enabled) return UnityEngine.Networking.NetworkTransport.QueueMessageForSending(hostId, connectionId, channelId, buffer, size, out error);
++size;
if (!s_IsClient)
{
size += 8;
int connectionIdOffset = size - 9;
for (byte i = 0; i < sizeof(ulong); i++) buffer[connectionIdOffset + i] = ((byte)((ulong)connectionId >> (i * 8)));
}
buffer[size - 1] = (byte)MessageType.Data;
return UnityEngine.Networking.NetworkTransport.QueueMessageForSending(hostId, s_RelayConnectionId, channelId, buffer, size, out error);
}
public static bool SendQueuedMessages(int hostId, int connectionId, out byte error)
{
if (!Enabled) return UnityEngine.Networking.NetworkTransport.SendQueuedMessages(hostId, connectionId, out error);
return UnityEngine.Networking.NetworkTransport.SendQueuedMessages(hostId, s_RelayConnectionId, out error);
}
public static NetworkEventType ReceiveFromHost(int hostId, out int connectionId, out int channelId, byte[] buffer, int bufferSize, out int receivedSize, out byte error)
{
if (!Enabled) return UnityEngine.Networking.NetworkTransport.ReceiveFromHost(hostId, out connectionId, out channelId, buffer, bufferSize, out receivedSize, out error);
var eventType = UnityEngine.Networking.NetworkTransport.ReceiveFromHost(hostId, out connectionId, out channelId, buffer, bufferSize, out receivedSize, out error);
return BaseReceive(eventType, hostId, ref connectionId, ref channelId, buffer, bufferSize, ref receivedSize, ref error);
}
public static NetworkEventType Receive(out int hostId, out int connectionId, out int channelId, byte[] buffer, int bufferSize, out int receivedSize, out byte error)
{
if (!Enabled) return UnityEngine.Networking.NetworkTransport.Receive(out hostId, out connectionId, out channelId, buffer, bufferSize, out receivedSize, out error);
var eventType = UnityEngine.Networking.NetworkTransport.Receive(out hostId, out connectionId, out channelId, buffer, bufferSize, out receivedSize, out error);
return BaseReceive(eventType, hostId, ref connectionId, ref channelId, buffer, bufferSize, ref receivedSize, ref error);
}
private static NetworkEventType BaseReceive(NetworkEventType netEvent, int hostId, ref int connectionId, ref int channelId, byte[] buffer, int bufferSize, ref int receivedSize, ref byte error)
{
switch (netEvent)
{
case NetworkEventType.DataEvent:
{
var messageType = (MessageType)buffer[receivedSize - 1];
switch (messageType)
{
case MessageType.AddressReport:
{
byte[] addressBytes = new byte[16];
for (int i = 0; i < addressBytes.Length; i++)
{
addressBytes[i] = buffer[i];
}
ushort remotePort = (ushort)(((ushort)buffer[16]) |
((ushort)buffer[17] << 8));
var remoteEndPoint = new IPEndPoint(new IPAddress(addressBytes), remotePort);
OnRemoteEndpointReported?.Invoke(remoteEndPoint);
break;
}
case MessageType.ConnectToServer: // Connection approved
{
if (!s_IsClient)
{
ulong _connectionId = (((ulong)buffer[receivedSize - 9]) |
((ulong)buffer[receivedSize - 8] << 8) |
((ulong)buffer[receivedSize - 7] << 16) |
((ulong)buffer[receivedSize - 6] << 24) |
((ulong)buffer[receivedSize - 5] << 32) |
((ulong)buffer[receivedSize - 4] << 40) |
((ulong)buffer[receivedSize - 3] << 48) |
((ulong)buffer[receivedSize - 2] << 56));
connectionId = (int)_connectionId;
}
return NetworkEventType.ConnectEvent;
}
case MessageType.Data:
{
// Implicitly remove header
if (s_IsClient) --receivedSize;
else
{
receivedSize -= 9;
ulong _connectionId = (((ulong)buffer[receivedSize]) |
((ulong)buffer[receivedSize + 1] << 8) |
((ulong)buffer[receivedSize + 2] << 16) |
((ulong)buffer[receivedSize + 3] << 24) |
((ulong)buffer[receivedSize + 4] << 32) |
((ulong)buffer[receivedSize + 5] << 40) |
((ulong)buffer[receivedSize + 6] << 48) |
((ulong)buffer[receivedSize + 7] << 56));
connectionId = (int)_connectionId;
}
return NetworkEventType.DataEvent;
}
case MessageType.ClientDisconnect:
{
ulong _connectionId = (((ulong)buffer[0]) |
((ulong)buffer[1] << 8) |
((ulong)buffer[2] << 16) |
((ulong)buffer[3] << 24) |
((ulong)buffer[4] << 32) |
((ulong)buffer[5] << 40) |
((ulong)buffer[6] << 48) |
((ulong)buffer[7] << 56));
connectionId = (int)_connectionId;
return NetworkEventType.DisconnectEvent;
}
}
}
break;
case NetworkEventType.ConnectEvent:
{
if (s_IsClient)
{
//Connect via relay
byte[] ipv6AddressBuffer;
var ipAddress = IPAddress.Parse(s_Address);
if (ipAddress.AddressFamily == System.Net.Sockets.AddressFamily.InterNetworkV6)
{
ipv6AddressBuffer = ipAddress.GetAddressBytes();
}
else if (ipAddress.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork)
{
byte[] ipv4Address = ipAddress.GetAddressBytes();
ipv6AddressBuffer = new byte[16] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, ipv4Address[0], ipv4Address[1], ipv4Address[2], ipv4Address[3] };
}
else
{
// TODO: Throw wrong type
ipv6AddressBuffer = null;
}
// TODO: Throw if address is not 16 bytes. It should always be
for (int i = 0; i < ipv6AddressBuffer.Length; i++) buffer[i] = ipv6AddressBuffer[i];
for (byte i = 0; i < sizeof(ushort); i++) buffer[16 + i] = ((byte)(s_Port >> (i * 8)));
buffer[16 + 2] = (byte)MessageType.ConnectToServer;
UnityEngine.Networking.NetworkTransport.Send(hostId, connectionId, s_DefaultChannelId, buffer, 16 + 2 + 1, out error);
}
else
{
//Register us as a server
buffer[0] = (byte)MessageType.StartServer;
UnityEngine.Networking.NetworkTransport.Send(hostId, connectionId, s_DefaultChannelId, buffer, 1, out error);
}
return NetworkEventType.Nothing; // Connect event is ignored
}
case NetworkEventType.DisconnectEvent:
{
if ((NetworkError)error == NetworkError.CRCMismatch) Debug.LogError("[MLAPI.Relay] The MLAPI Relay detected a CRC mismatch. This could be due to the maxClients or other connectionConfig settings not being the same");
return NetworkEventType.DisconnectEvent;
}
}
return netEvent;
}
}
public class InvalidConfigException : SystemException
{
public InvalidConfigException() { }
public InvalidConfigException(string issue) : base(issue) { }
}
}
#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member
#pragma warning restore 618

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 11544f78e46198d4cb39bfb970a357aa
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,22 @@
using System;
using UnityEngine.Networking;
namespace MLAPI.Transports
{
/// <summary>
/// A transport channel used by the MLAPI
/// </summary>
[Serializable]
public class UNetChannel
{
/// <summary>
/// The name of the channel
/// </summary>
public NetworkChannel Id;
/// <summary>
/// The type of channel
/// </summary>
public QosType Type;
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e864534da30ef604992c0ed33c75d3c6
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,475 @@
#pragma warning disable 618
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
using System;
using System.Collections.Generic;
using MLAPI.Exceptions;
using MLAPI.Logging;
using MLAPI.Profiling;
using MLAPI.Transports.Tasks;
using UnityEngine.Networking;
namespace MLAPI.Transports.UNET
{
public class UNetTransport : NetworkTransport, ITransportProfilerData
{
public enum SendMode
{
Immediately,
Queued
}
private static ProfilingDataStore s_TransportProfilerData = new ProfilingDataStore();
public static bool ProfilerEnabled;
// Inspector / settings
public int MessageBufferSize = 1024 * 5;
public int MaxConnections = 100;
public int MaxSentMessageQueueSize = 128;
public string ConnectAddress = "127.0.0.1";
public int ConnectPort = 7777;
public int ServerListenPort = 7777;
public int ServerWebsocketListenPort = 8887;
public bool SupportWebsocket = false;
// user-definable channels. To add your own channel, do something of the form:
// #define MY_CHANNEL 0
// ...
// transport.Channels.Add(
// new UNetChannel()
// {
// Id = Channel.ChannelUnused + MY_CHANNEL, <<-- must offset from reserved channel offset in MLAPI SDK
// Type = QosType.Unreliable
// }
// );
public List<UNetChannel> Channels = new List<UNetChannel>();
// Relay
public bool UseMLAPIRelay = false;
public string MLAPIRelayAddress = "184.72.104.138";
public int MLAPIRelayPort = 8888;
public SendMode MessageSendMode = SendMode.Immediately;
// Runtime / state
private byte[] m_MessageBuffer;
private WeakReference m_TemporaryBufferReference;
// Lookup / translation
private readonly Dictionary<NetworkChannel, int> m_ChannelNameToId = new Dictionary<NetworkChannel, int>();
private readonly Dictionary<int, NetworkChannel> m_ChannelIdToName = new Dictionary<int, NetworkChannel>();
private int m_ServerConnectionId;
private int m_ServerHostId;
private SocketTask m_ConnectTask;
public override ulong ServerClientId => GetMLAPIClientId(0, 0, true);
protected void LateUpdate()
{
if (UnityEngine.Networking.NetworkTransport.IsStarted && MessageSendMode == SendMode.Queued)
{
if (NetworkManager.Singleton.IsServer)
{
for (int i = 0; i < NetworkManager.Singleton.ConnectedClientsList.Count; i++)
{
SendQueued(NetworkManager.Singleton.ConnectedClientsList[i].ClientId);
}
}
else
{
SendQueued(NetworkManager.Singleton.LocalClientId);
}
}
}
public override void Send(ulong clientId, ArraySegment<byte> data, NetworkChannel networkChannel)
{
if (ProfilerEnabled)
{
s_TransportProfilerData.Increment(ProfilerConstants.NumberOfTransportSends);
}
GetUNetConnectionDetails(clientId, out byte hostId, out ushort connectionId);
int channelId = 0;
if (m_ChannelNameToId.ContainsKey(networkChannel))
{
channelId = m_ChannelNameToId[networkChannel];
}
else
{
channelId = m_ChannelNameToId[NetworkChannel.Internal];
}
byte[] buffer;
if (data.Offset > 0)
{
// UNET cant handle this, do a copy
if (m_MessageBuffer.Length >= data.Count)
{
buffer = m_MessageBuffer;
}
else
{
object bufferRef = null;
if (m_TemporaryBufferReference != null && ((bufferRef = m_TemporaryBufferReference.Target) != null) && ((byte[])bufferRef).Length >= data.Count)
{
buffer = (byte[])bufferRef;
}
else
{
buffer = new byte[data.Count];
m_TemporaryBufferReference = new WeakReference(buffer);
}
}
Buffer.BlockCopy(data.Array, data.Offset, buffer, 0, data.Count);
}
else
{
buffer = data.Array;
}
if (MessageSendMode == SendMode.Queued)
{
RelayTransport.QueueMessageForSending(hostId, connectionId, channelId, buffer, data.Count, out byte error);
}
else
{
RelayTransport.Send(hostId, connectionId, channelId, buffer, data.Count, out byte error);
}
}
public void SendQueued(ulong clientId)
{
if (ProfilerEnabled)
{
s_TransportProfilerData.Increment(ProfilerConstants.NumberOfTransportSendQueues);
}
GetUNetConnectionDetails(clientId, out byte hostId, out ushort connectionId);
RelayTransport.SendQueuedMessages(hostId, connectionId, out byte error);
}
public override NetworkEvent PollEvent(out ulong clientId, out NetworkChannel networkChannel, out ArraySegment<byte> payload, out float receiveTime)
{
var eventType = RelayTransport.Receive(out int hostId, out int connectionId, out int channelId, m_MessageBuffer, m_MessageBuffer.Length, out int receivedSize, out byte error);
clientId = GetMLAPIClientId((byte)hostId, (ushort)connectionId, false);
receiveTime = UnityEngine.Time.realtimeSinceStartup;
var networkError = (NetworkError)error;
if (networkError == NetworkError.MessageToLong)
{
byte[] tempBuffer;
if (m_TemporaryBufferReference != null && m_TemporaryBufferReference.IsAlive && ((byte[])m_TemporaryBufferReference.Target).Length >= receivedSize)
{
tempBuffer = (byte[])m_TemporaryBufferReference.Target;
}
else
{
tempBuffer = new byte[receivedSize];
m_TemporaryBufferReference = new WeakReference(tempBuffer);
}
eventType = RelayTransport.Receive(out hostId, out connectionId, out channelId, tempBuffer, tempBuffer.Length, out receivedSize, out error);
payload = new ArraySegment<byte>(tempBuffer, 0, receivedSize);
}
else
{
payload = new ArraySegment<byte>(m_MessageBuffer, 0, receivedSize);
}
if (m_ChannelIdToName.ContainsKey(channelId))
{
networkChannel = m_ChannelIdToName[channelId];
}
else
{
networkChannel = NetworkChannel.Internal;
}
if (m_ConnectTask != null && hostId == m_ServerHostId && connectionId == m_ServerConnectionId)
{
if (eventType == NetworkEventType.ConnectEvent)
{
// We just got a response to our connect request.
m_ConnectTask.Message = null;
m_ConnectTask.SocketError = networkError == NetworkError.Ok ? System.Net.Sockets.SocketError.Success : System.Net.Sockets.SocketError.SocketError;
m_ConnectTask.State = null;
m_ConnectTask.Success = networkError == NetworkError.Ok;
m_ConnectTask.TransportCode = (byte)networkError;
m_ConnectTask.TransportException = null;
m_ConnectTask.IsDone = true;
m_ConnectTask = null;
}
else if (eventType == NetworkEventType.DisconnectEvent)
{
// We just got a response to our connect request.
m_ConnectTask.Message = null;
m_ConnectTask.SocketError = System.Net.Sockets.SocketError.SocketError;
m_ConnectTask.State = null;
m_ConnectTask.Success = false;
m_ConnectTask.TransportCode = (byte)networkError;
m_ConnectTask.TransportException = null;
m_ConnectTask.IsDone = true;
m_ConnectTask = null;
}
}
if (networkError == NetworkError.Timeout)
{
// In UNET. Timeouts are not disconnects. We have to translate that here.
eventType = NetworkEventType.DisconnectEvent;
}
// Translate NetworkEventType to NetEventType
switch (eventType)
{
case NetworkEventType.DataEvent:
return NetworkEvent.Data;
case NetworkEventType.ConnectEvent:
return NetworkEvent.Connect;
case NetworkEventType.DisconnectEvent:
return NetworkEvent.Disconnect;
case NetworkEventType.Nothing:
return NetworkEvent.Nothing;
case NetworkEventType.BroadcastEvent:
return NetworkEvent.Nothing;
}
return NetworkEvent.Nothing;
}
public override SocketTasks StartClient()
{
var socketTask = SocketTask.Working;
m_ServerHostId = RelayTransport.AddHost(new HostTopology(GetConfig(), 1), false);
m_ServerConnectionId = RelayTransport.Connect(m_ServerHostId, ConnectAddress, ConnectPort, 0, out byte error);
var connectError = (NetworkError)error;
switch (connectError)
{
case NetworkError.Ok:
socketTask.Success = true;
socketTask.TransportCode = error;
socketTask.SocketError = System.Net.Sockets.SocketError.Success;
socketTask.IsDone = false;
// We want to continue to wait for the successful connect
m_ConnectTask = socketTask;
break;
default:
socketTask.Success = false;
socketTask.TransportCode = error;
socketTask.SocketError = System.Net.Sockets.SocketError.SocketError;
socketTask.IsDone = true;
break;
}
return socketTask.AsTasks();
}
public override SocketTasks StartServer()
{
var topology = new HostTopology(GetConfig(), MaxConnections);
if (SupportWebsocket)
{
if (!UseMLAPIRelay)
{
int websocketHostId = UnityEngine.Networking.NetworkTransport.AddWebsocketHost(topology, ServerWebsocketListenPort);
}
else
{
if (NetworkLog.CurrentLogLevel <= LogLevel.Error) NetworkLog.LogError("Cannot create websocket host when using MLAPI relay");
}
}
int normalHostId = RelayTransport.AddHost(topology, ServerListenPort, true);
return SocketTask.Done.AsTasks();
}
public override void DisconnectRemoteClient(ulong clientId)
{
GetUNetConnectionDetails(clientId, out byte hostId, out ushort connectionId);
RelayTransport.Disconnect((int)hostId, (int)connectionId, out byte error);
}
public override void DisconnectLocalClient()
{
RelayTransport.Disconnect(m_ServerHostId, m_ServerConnectionId, out byte error);
}
public override ulong GetCurrentRtt(ulong clientId)
{
GetUNetConnectionDetails(clientId, out byte hostId, out ushort connectionId);
if (UseMLAPIRelay)
{
return 0;
}
else
{
return (ulong)UnityEngine.Networking.NetworkTransport.GetCurrentRTT((int)hostId, (int)connectionId, out byte error);
}
}
public override void Shutdown()
{
m_ChannelIdToName.Clear();
m_ChannelNameToId.Clear();
UnityEngine.Networking.NetworkTransport.Shutdown();
}
public override void Init()
{
UpdateRelay();
m_MessageBuffer = new byte[MessageBufferSize];
s_TransportProfilerData.Clear();
UnityEngine.Networking.NetworkTransport.Init();
}
public ulong GetMLAPIClientId(byte hostId, ushort connectionId, bool isServer)
{
if (isServer)
{
return 0;
}
else
{
return ((ulong)connectionId | (ulong)hostId << 16) + 1;
}
}
public void GetUNetConnectionDetails(ulong clientId, out byte hostId, out ushort connectionId)
{
if (clientId == 0)
{
hostId = (byte)m_ServerHostId;
connectionId = (ushort)m_ServerConnectionId;
}
else
{
hostId = (byte)((clientId - 1) >> 16);
connectionId = (ushort)((clientId - 1));
}
}
public ConnectionConfig GetConfig()
{
var connectionConfig = new ConnectionConfig();
// MLAPI built-in channels
for (int i = 0; i < MLAPI_CHANNELS.Length; i++)
{
int channelId = AddMLAPIChannel(MLAPI_CHANNELS[i].Delivery, connectionConfig);
m_ChannelIdToName.Add(channelId, MLAPI_CHANNELS[i].Channel);
m_ChannelNameToId.Add(MLAPI_CHANNELS[i].Channel, channelId);
}
// Custom user-added channels
for (int i = 0; i < Channels.Count; i++)
{
int channelId = AddUNETChannel(Channels[i].Type, connectionConfig);
if (m_ChannelNameToId.ContainsKey(Channels[i].Id))
{
throw new InvalidChannelException($"Channel {channelId} already exists");
}
m_ChannelIdToName.Add(channelId, Channels[i].Id);
m_ChannelNameToId.Add(Channels[i].Id, channelId);
}
connectionConfig.MaxSentMessageQueueSize = (ushort)MaxSentMessageQueueSize;
return connectionConfig;
}
public int AddMLAPIChannel(NetworkDelivery type, ConnectionConfig config)
{
switch (type)
{
case NetworkDelivery.Unreliable:
return config.AddChannel(QosType.Unreliable);
case NetworkDelivery.Reliable:
return config.AddChannel(QosType.Reliable);
case NetworkDelivery.ReliableSequenced:
return config.AddChannel(QosType.ReliableSequenced);
case NetworkDelivery.ReliableFragmentedSequenced:
return config.AddChannel(QosType.ReliableFragmentedSequenced);
case NetworkDelivery.UnreliableSequenced:
return config.AddChannel(QosType.UnreliableSequenced);
}
return 0;
}
public int AddUNETChannel(QosType type, ConnectionConfig config)
{
switch (type)
{
case QosType.Unreliable:
return config.AddChannel(QosType.Unreliable);
case QosType.UnreliableFragmented:
return config.AddChannel(QosType.UnreliableFragmented);
case QosType.UnreliableSequenced:
return config.AddChannel(QosType.UnreliableSequenced);
case QosType.Reliable:
return config.AddChannel(QosType.Reliable);
case QosType.ReliableFragmented:
return config.AddChannel(QosType.ReliableFragmented);
case QosType.ReliableSequenced:
return config.AddChannel(QosType.ReliableSequenced);
case QosType.StateUpdate:
return config.AddChannel(QosType.StateUpdate);
case QosType.ReliableStateUpdate:
return config.AddChannel(QosType.ReliableStateUpdate);
case QosType.AllCostDelivery:
return config.AddChannel(QosType.AllCostDelivery);
case QosType.UnreliableFragmentedSequenced:
return config.AddChannel(QosType.UnreliableFragmentedSequenced);
case QosType.ReliableFragmentedSequenced:
return config.AddChannel(QosType.ReliableFragmentedSequenced);
}
return 0;
}
private void UpdateRelay()
{
RelayTransport.Enabled = UseMLAPIRelay;
RelayTransport.RelayAddress = MLAPIRelayAddress;
RelayTransport.RelayPort = (ushort)MLAPIRelayPort;
}
public void BeginNewTick()
{
s_TransportProfilerData.Clear();
}
public IReadOnlyDictionary<string, int> GetTransportProfilerData()
{
return s_TransportProfilerData.GetReadonly();
}
}
}
#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member
#pragma warning restore 618

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b84c2d8dfe509a34fb59e2b81f8e1319
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: