Initial Commit
This commit is contained in:
parent
53eb92e9af
commit
270ab7d11f
15341 changed files with 700234 additions and 0 deletions
|
@ -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
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: adf0b801ebdee8c4b838e20292b36ac4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: d0151d4283b329e498204f86534292ef
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 5c875a40b1475a5419ffd2a6b002cad2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: e47bade9963ff5b41b86d331006b5f67
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 1f6f5dde2cfa0614eb5985556ca1ac17
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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 } };
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 1be79791c1bf89b4db05351e0f45d650
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 8ed76ef44843842b3ad15c253703e5c5
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: e6aab111d2a894a7e836bdd742e248d9
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: f94b11ceafb6c426aa67067ec95539ed
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
fileFormatVersion: 2
|
||||
guid: cf3c8d3561be742cc889cc4e329fe386
|
||||
AssemblyDefinitionImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: a7bc986a2f8f5584988f87dbf35965fe
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 901256c415b484744b9da9551d85073a
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
|||
namespace MLAPI.Transports.UNET
|
||||
{
|
||||
public static class ProfilerConstants
|
||||
{
|
||||
public const string NumberOfTransportSends = nameof(NumberOfTransportSends);
|
||||
public const string NumberOfTransportSendQueues = nameof(NumberOfTransportSendQueues);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: c5bc85b7565054d6295b7847967cdbd8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 11544f78e46198d4cb39bfb970a357aa
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: e864534da30ef604992c0ed33c75d3c6
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: b84c2d8dfe509a34fb59e2b81f8e1319
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
Loading…
Add table
Add a link
Reference in a new issue