Shorten Channel Enums (#2644)

Co-authored-by: MrGadget1024 <chris@clevertech.net>
This commit is contained in:
MrGadget 2021-03-15 23:46:10 -04:00 committed by vis2k
parent 254a0b929d
commit 53dad15e04
18 changed files with 287 additions and 182 deletions

View File

@ -212,7 +212,7 @@ bool NeedsTeleport()
}
// local authority client sends sync message to server for broadcasting
[Command(channel = Channels.DefaultUnreliable)]
[Command(channel = Channels.Unreliable)]
void CmdClientToServerSync(Vector3 position, uint packedRotation, Vector3 scale)
{
// Ignore messages from client if not in client authority mode
@ -229,7 +229,7 @@ void CmdClientToServerSync(Vector3 position, uint packedRotation, Vector3 scale)
RpcMove(position, packedRotation, scale);
}
[ClientRpc(channel = Channels.DefaultUnreliable)]
[ClientRpc(channel = Channels.Unreliable)]
void RpcMove(Vector3 position, uint packedRotation, Vector3 scale)
{
if (hasAuthority && excludeOwnerUpdate) return;
@ -456,7 +456,7 @@ void DoTeleport(Vector3 newLocalPosition, Quaternion newLocalRotation)
lastRotation = newLocalRotation;
}
[ClientRpc(channel = Channels.DefaultUnreliable)]
[ClientRpc(channel = Channels.Unreliable)]
void RpcTeleport(Vector3 newPosition, uint newPackedRotation, bool isClientAuthority)
{
DoTeleport(newPosition, Compression.DecompressQuaternion(newPackedRotation));
@ -470,7 +470,7 @@ void RpcTeleport(Vector3 newPosition, uint newPackedRotation, bool isClientAutho
/// This RPC will be invoked on server after client finishes overriding the position.
/// </summary>
/// <param name="initialAuthority"></param>
[Command(channel = Channels.DefaultUnreliable)]
[Command(channel = Channels.Unreliable)]
void CmdTeleportFinished()
{
if (clientAuthorityBeforeTeleport)

View File

@ -20,7 +20,7 @@ public class SyncVarAttribute : PropertyAttribute
[AttributeUsage(AttributeTargets.Method)]
public class CommandAttribute : Attribute
{
public int channel = Channels.DefaultReliable;
public int channel = Channels.Reliable;
public bool requiresAuthority = true;
}
@ -30,7 +30,7 @@ public class CommandAttribute : Attribute
[AttributeUsage(AttributeTargets.Method)]
public class ClientRpcAttribute : Attribute
{
public int channel = Channels.DefaultReliable;
public int channel = Channels.Reliable;
public bool includeOwner = true;
}
@ -40,7 +40,7 @@ public class ClientRpcAttribute : Attribute
[AttributeUsage(AttributeTargets.Method)]
public class TargetRpcAttribute : Attribute
{
public int channel = Channels.DefaultReliable;
public int channel = Channels.Reliable;
}
/// <summary>

View File

@ -14,7 +14,7 @@ public LocalConnectionToClient() : base(LocalConnectionId, false, 0) {}
public override string address => "localhost";
internal override void Send(ArraySegment<byte> segment, int channelId = Channels.DefaultReliable)
internal override void Send(ArraySegment<byte> segment, int channelId = Channels.Reliable)
{
// get a writer to copy the message into since the segment is only
// valid until returning.
@ -63,7 +63,7 @@ internal class LocalConnectionToServer : NetworkConnectionToServer
internal void QueueConnectedEvent() => connectedEventPending = true;
internal void QueueDisconnectedEvent() => disconnectedEventPending = true;
internal override void Send(ArraySegment<byte> segment, int channelId = Channels.DefaultReliable)
internal override void Send(ArraySegment<byte> segment, int channelId = Channels.Reliable)
{
if (segment.Count == 0)
{
@ -91,7 +91,7 @@ internal void Update()
PooledNetworkWriter writer = queue.Dequeue();
ArraySegment<byte> segment = writer.ToArraySegment();
//Debug.Log("Dequeue " + BitConverter.ToString(segment.Array, segment.Offset, segment.Count));
TransportReceive(segment, Channels.DefaultReliable);
TransportReceive(segment, Channels.Reliable);
NetworkWriterPool.Recycle(writer);
}

View File

@ -290,7 +290,7 @@ static void OnDisconnected()
// send ////////////////////////////////////////////////////////////////
/// <summary>Send a NetworkMessage to the server over the given channel.</summary>
public static void Send<T>(T message, int channelId = Channels.DefaultReliable)
public static void Send<T>(T message, int channelId = Channels.Reliable)
where T : struct, NetworkMessage
{
if (connection != null)

View File

@ -72,7 +72,7 @@ internal void SetHandlers(Dictionary<int, NetworkMessageDelegate> handlers)
}
/// <summary>Send a NetworkMessage to this connection over the given channel.</summary>
public void Send<T>(T msg, int channelId = Channels.DefaultReliable)
public void Send<T>(T msg, int channelId = Channels.Reliable)
where T : struct, NetworkMessage
{
using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter())
@ -110,7 +110,7 @@ protected static bool ValidatePacketSize(ArraySegment<byte> segment, int channel
// internal because no one except Mirror should send bytes directly to
// the client. they would be detected as a message. send messages instead.
internal abstract void Send(ArraySegment<byte> segment, int channelId = Channels.DefaultReliable);
internal abstract void Send(ArraySegment<byte> segment, int channelId = Channels.Reliable);
public override string ToString() => $"connection({connectionId})";

View File

@ -122,7 +122,7 @@ internal void SendBatch(int channelId, Batch batch)
batch.lastSendTime = NetworkTime.time;
}
internal override void Send(ArraySegment<byte> segment, int channelId = Channels.DefaultReliable)
internal override void Send(ArraySegment<byte> segment, int channelId = Channels.Reliable)
{
//Debug.Log("ConnectionSend " + this + " bytes:" + BitConverter.ToString(segment.Array, segment.Offset, segment.Count));

View File

@ -6,7 +6,7 @@ public class NetworkConnectionToServer : NetworkConnection
{
public override string address => "";
internal override void Send(ArraySegment<byte> segment, int channelId = Channels.DefaultReliable)
internal override void Send(ArraySegment<byte> segment, int channelId = Channels.Reliable)
{
// Debug.Log("ConnectionSend " + this + " bytes:" + BitConverter.ToString(segment.Array, segment.Offset, segment.Count));

View File

@ -225,7 +225,7 @@ public static bool NoExternalConnections()
// send ////////////////////////////////////////////////////////////////
/// <summary>Send a message to all clients, even those that haven't joined the world yet (non ready)</summary>
public static void SendToAll<T>(T message, int channelId = Channels.DefaultReliable, bool sendToReadyOnly = false)
public static void SendToAll<T>(T message, int channelId = Channels.Reliable, bool sendToReadyOnly = false)
where T : struct, NetworkMessage
{
if (!active)
@ -260,7 +260,7 @@ public static void SendToAll<T>(T message, int channelId = Channels.DefaultRelia
/// <summary>Send a message to all clients which have joined the world (are ready).</summary>
// TODO put rpcs into NetworkServer.Update WorldState packet, then finally remove SendToReady!
public static void SendToReady<T>(T message, int channelId = Channels.DefaultReliable)
public static void SendToReady<T>(T message, int channelId = Channels.Reliable)
where T : struct, NetworkMessage
{
if (!active)
@ -274,7 +274,7 @@ public static void SendToReady<T>(T message, int channelId = Channels.DefaultRel
/// <summary>Send a message to only clients which are ready with option to include the owner of the object identity</summary>
// TODO put rpcs into NetworkServer.Update WorldState packet, then finally remove SendToReady!
public static void SendToReady<T>(NetworkIdentity identity, T message, bool includeOwner = true, int channelId = Channels.DefaultReliable)
public static void SendToReady<T>(NetworkIdentity identity, T message, bool includeOwner = true, int channelId = Channels.Reliable)
where T : struct, NetworkMessage
{
// Debug.Log("Server.SendToReady msgType:" + typeof(T));
@ -312,7 +312,7 @@ public static void SendToReady<T>(NetworkIdentity identity, T message, int chann
// this is like SendToReady - but it doesn't check the ready flag on the connection.
// this is used for ObjectDestroy messages.
static void SendToObservers<T>(NetworkIdentity identity, T message, int channelId = Channels.DefaultReliable)
static void SendToObservers<T>(NetworkIdentity identity, T message, int channelId = Channels.Reliable)
where T : struct, NetworkMessage
{
// Debug.Log("Server.SendToObservers id:" + typeof(T));
@ -336,7 +336,7 @@ static void SendToObservers<T>(NetworkIdentity identity, T message, int channelI
/// <summary>Send this message to the player only</summary>
[Obsolete("Use identity.connectionToClient.Send() instead! Previously Mirror needed this function internally, but not anymore.")]
public static void SendToClientOfPlayer<T>(NetworkIdentity identity, T msg, int channelId = Channels.DefaultReliable)
public static void SendToClientOfPlayer<T>(NetworkIdentity identity, T msg, int channelId = Channels.Reliable)
where T : struct, NetworkMessage
{
if (identity != null)

View File

@ -47,7 +47,7 @@ internal static void UpdateClient()
if (Time.time - lastPingTime >= PingFrequency)
{
NetworkPingMessage pingMessage = new NetworkPingMessage(LocalTime());
NetworkClient.Send(pingMessage, Channels.DefaultUnreliable);
NetworkClient.Send(pingMessage, Channels.Unreliable);
lastPingTime = Time.time;
}
}
@ -63,7 +63,7 @@ internal static void OnServerPing(NetworkConnection conn, NetworkPingMessage mes
clientTime = message.clientTime,
serverTime = LocalTime()
};
conn.Send(pongMessage, Channels.DefaultUnreliable);
conn.Send(pongMessage, Channels.Unreliable);
}
// Executed at the client when we receive a Pong message

View File

@ -57,14 +57,14 @@ void Awake()
// client
client = new KcpClient(
() => OnClientConnected.Invoke(),
(message) => OnClientDataReceived.Invoke(message, Channels.DefaultReliable),
(message) => OnClientDataReceived.Invoke(message, Channels.Reliable),
() => OnClientDisconnected.Invoke()
);
// server
server = new KcpServer(
(connectionId) => OnServerConnected.Invoke(connectionId),
(connectionId, message) => OnServerDataReceived.Invoke(connectionId, message, Channels.DefaultReliable),
(connectionId, message) => OnServerDataReceived.Invoke(connectionId, message, Channels.Reliable),
(connectionId) => OnServerDisconnected.Invoke(connectionId),
NoDelay,
Interval,
@ -97,7 +97,7 @@ public override void ClientSend(int channelId, ArraySegment<byte> segment)
// default to reliable just to be sure.
switch (channelId)
{
case Channels.DefaultUnreliable:
case Channels.Unreliable:
client.Send(segment, KcpChannel.Unreliable);
break;
default:
@ -156,7 +156,7 @@ public override void ServerSend(int connectionId, int channelId, ArraySegment<by
// default to reliable just to be sure.
switch (channelId)
{
case Channels.DefaultUnreliable:
case Channels.Unreliable:
server.Send(connectionId, segment, KcpChannel.Unreliable);
break;
default:
@ -187,14 +187,14 @@ public override void ServerEarlyUpdate()
public override void Shutdown() {}
// max message size
public override int GetMaxPacketSize(int channelId = Channels.DefaultReliable)
public override int GetMaxPacketSize(int channelId = Channels.Reliable)
{
// switch to kcp channel.
// unreliable or reliable.
// default to reliable just to be sure.
switch (channelId)
{
case Channels.DefaultUnreliable:
case Channels.Unreliable:
return KcpConnection.UnreliableMaxMessageSize;
default:
return KcpConnection.ReliableMaxMessageSize;

View File

@ -12,7 +12,7 @@ struct QueuedMessage
{
public int connectionId;
public byte[] bytes;
public float timeToSend;
public float time;
}
[HelpURL("https://mirror-networking.gitbook.io/docs/transports/latency-simulaton-transport")]
@ -24,19 +24,15 @@ public class LatencySimulation : Transport
[Header("Reliable Messages")]
[Tooltip("Reliable latency in seconds")]
public float reliableLatency = 0;
[Tooltip("Simulate latency spikes with % of latency for % of messages.")]
[Range(0, 1)] public float reliableLatencySpikes;
// note: packet loss over reliable manifests itself in latency.
// don't need (and can't add) a loss option here.
// note: reliable is ordered by definition. no need to scramble.
[Header("Unreliable Messages")]
[Tooltip("Unreliable latency in seconds")]
public float unreliableLatency = 0;
[Tooltip("Simulate latency spikes with % of latency for % of messages.")]
[Range(0, 1)] public float unreliableLatencySpikes;
[Tooltip("Packet loss in %")]
[Range(0, 1)] public float unreliableLoss;
[Tooltip("Unreliable latency in seconds")]
public float unreliableLatency = 0;
[Tooltip("Scramble unreliable messages, just like over the real network. Mirror unreliable is unordered.")]
public bool unreliableScramble;
@ -64,19 +60,6 @@ public void Awake()
void OnEnable() { wrap.enabled = true; }
void OnDisable() { wrap.enabled = false; }
// helper function to simulate latency & spike with spike probability
float SimulateLatency(float latency, float spikesPercent)
{
// will this one spike?
bool spike = random.NextDouble() < spikesPercent;
// if it spiked, add spike latency by percent of original latency
float add = spike ? latency * spikesPercent : 0;
// return latency + spike
return latency + add;
}
// helper function to simulate a send with latency/loss/scramble
void SimulateSend(int connectionId, int channelId, ArraySegment<byte> segment, List<QueuedMessage> reliableQueue, List<QueuedMessage> unreliableQueue)
{
@ -84,34 +67,30 @@ void SimulateSend(int connectionId, int channelId, ArraySegment<byte> segment, L
// (allocates for now. it's only for testing anyway.)
byte[] bytes = new byte[segment.Count];
Buffer.BlockCopy(segment.Array, segment.Offset, bytes, 0, segment.Count);
// enqueue message. send after latency interval.
QueuedMessage message = new QueuedMessage
{
connectionId = connectionId,
bytes = bytes
bytes = bytes,
time = Time.time
};
switch (channelId)
{
case Channels.DefaultReliable:
// simulate latency & spikes
message.timeToSend = Time.time + SimulateLatency(reliableLatency, reliableLatencySpikes);
case Channels.Reliable:
// simulate latency
reliableQueue.Add(message);
break;
case Channels.DefaultUnreliable:
case Channels.Unreliable:
// simulate packet loss
bool drop = random.NextDouble() < unreliableLoss;
if (!drop)
{
// simulate scramble (Random.Next is < max, so +1)
// note that list entries are NOT ordered by time anymore
// after inserting randomly.
int last = unreliableQueue.Count;
int index = unreliableScramble ? random.Next(0, last + 1) : last;
// simulate latency & spikes
message.timeToSend = Time.time + SimulateLatency(unreliableLatency, unreliableLatencySpikes);
// simulate latency
unreliableQueue.Insert(index, message);
}
break;
@ -184,32 +163,34 @@ public override void ServerStop()
public override void ServerEarlyUpdate() => wrap.ServerEarlyUpdate();
public override void ClientLateUpdate()
{
// flush reliable messages that are ready to be sent
// => list isn't ordered (due to scramble). need to iterate all.
for (int i = 0; i < reliableClientToServer.Count; ++i)
// flush reliable messages after latency
while (reliableClientToServer.Count > 0)
{
QueuedMessage message = reliableClientToServer[i];
if (Time.time >= message.timeToSend)
// check the first message time
QueuedMessage message = reliableClientToServer[0];
if (message.time + reliableLatency <= Time.time)
{
// send and remove
wrap.ClientSend(Channels.DefaultReliable, new ArraySegment<byte>(message.bytes));
reliableClientToServer.RemoveAt(i);
--i;
// send and eat
wrap.ClientSend(Channels.Reliable, new ArraySegment<byte>(message.bytes));
reliableClientToServer.RemoveAt(0);
}
// not enough time elapsed yet
break;
}
// flush unrelabe messages that are ready to be sent
// => list isn't ordered (due to scramble). need to iterate all.
for (int i = 0; i < unreliableClientToServer.Count; ++i)
// flush unreliable messages after latency
while (unreliableClientToServer.Count > 0)
{
QueuedMessage message = unreliableClientToServer[i];
if (Time.time >= message.timeToSend)
// check the first message time
QueuedMessage message = unreliableClientToServer[0];
if (message.time + unreliableLatency <= Time.time)
{
// send and remove
wrap.ClientSend(Channels.DefaultUnreliable, new ArraySegment<byte>(message.bytes));
unreliableClientToServer.RemoveAt(i);
--i;
// send and eat
wrap.ClientSend(Channels.Unreliable, new ArraySegment<byte>(message.bytes));
unreliableClientToServer.RemoveAt(0);
}
// not enough time elapsed yet
break;
}
// update wrapped transport too
@ -217,32 +198,34 @@ public override void ClientLateUpdate()
}
public override void ServerLateUpdate()
{
// flush reliable messages that are ready to be sent
// => list isn't ordered (due to scramble). need to iterate all.
for (int i = 0; i < reliableServerToClient.Count; ++i)
// flush reliable messages after latency
while (reliableServerToClient.Count > 0)
{
QueuedMessage message = reliableServerToClient[i];
if (Time.time >= message.timeToSend)
// check the first message time
QueuedMessage message = reliableServerToClient[0];
if (message.time + reliableLatency <= Time.time)
{
// send and remove
wrap.ServerSend(message.connectionId, Channels.DefaultReliable, new ArraySegment<byte>(message.bytes));
reliableServerToClient.RemoveAt(i);
--i;
// send and eat
wrap.ServerSend(message.connectionId, Channels.Reliable, new ArraySegment<byte>(message.bytes));
reliableServerToClient.RemoveAt(0);
}
// not enough time elapsed yet
break;
}
// flush unrelabe messages that are ready to be sent
// => list isn't ordered (due to scramble). need to iterate all.
for (int i = 0; i < unreliableServerToClient.Count; ++i)
// flush unreliable messages after latency
while (unreliableServerToClient.Count > 0)
{
QueuedMessage message = unreliableServerToClient[i];
if (Time.time >= message.timeToSend)
// check the first message time
QueuedMessage message = unreliableServerToClient[0];
if (message.time + unreliableLatency <= Time.time)
{
// send and remove
wrap.ServerSend(message.connectionId, Channels.DefaultUnreliable, new ArraySegment<byte>(message.bytes));
unreliableServerToClient.RemoveAt(i);
--i;
// send and eat
wrap.ServerSend(message.connectionId, Channels.Unreliable, new ArraySegment<byte>(message.bytes));
unreliableServerToClient.RemoveAt(0);
}
// not enough time elapsed yet
break;
}
// update wrapped transport too

View File

@ -150,7 +150,7 @@ public override void ClientConnect(string hostname)
// there should be no more messages after disconnect
client = null;
};
client.onData += (ArraySegment<byte> data) => OnClientDataReceived.Invoke(data, Channels.DefaultReliable);
client.onData += (ArraySegment<byte> data) => OnClientDataReceived.Invoke(data, Channels.Reliable);
client.onError += (Exception e) =>
{
OnClientError.Invoke(e);
@ -214,7 +214,7 @@ public override void ServerStart()
server.onConnect += OnServerConnected.Invoke;
server.onDisconnect += OnServerDisconnected.Invoke;
server.onData += (int connId, ArraySegment<byte> data) => OnServerDataReceived.Invoke(connId, data, Channels.DefaultReliable);
server.onData += (int connId, ArraySegment<byte> data) => OnServerDataReceived.Invoke(connId, data, Channels.Reliable);
server.onError += OnServerError.Invoke;
SendLoopConfig.batchSend = batchSend || waitBeforeSend;

View File

@ -80,7 +80,7 @@ void Awake()
// them all in a lambda and always call the latest hook.
// (= lazy call)
client.OnConnected = () => OnClientConnected.Invoke();
client.OnData = (segment) => OnClientDataReceived.Invoke(segment, Channels.DefaultReliable);
client.OnData = (segment) => OnClientDataReceived.Invoke(segment, Channels.Reliable);
client.OnDisconnected = () => OnClientDisconnected.Invoke();
// client configuration
@ -98,7 +98,7 @@ void Awake()
// them all in a lambda and always call the latest hook.
// (= lazy call)
server.OnConnected = (connectionId) => OnServerConnected.Invoke(connectionId);
server.OnData = (connectionId, segment) => OnServerDataReceived.Invoke(connectionId, segment, Channels.DefaultReliable);
server.OnData = (connectionId, segment) => OnServerDataReceived.Invoke(connectionId, segment, Channels.Reliable);
server.OnDisconnected = (connectionId) => OnServerDisconnected.Invoke(connectionId);
// server configuration

View File

@ -1,46 +1,99 @@
// Transport Rules
//
// All transports should follow these rules so that they work correctly with mirror:
// * When Monobehaviour is disabled the Transport should not invoke callbacks
// * Callbacks should be invoked on main thread. It is best to do this from LateUpdate
// * Callbacks can be invoked after ServerStop or ClientDisconnect has been called
// * ServerStop or ClientDisconnect can be called by mirror multiple times
// * Available should check the platform and 32 vs 64 bit if the transport only works on some of them
// * GetMaxPacketSize should return size even if transport is not running
// * Default channel should be reliable Channels.DefaultReliable
using System;
using UnityEngine;
namespace Mirror
{
/// <summary>Abstract transport layer component</summary>
/// <summary>
/// Abstract transport layer component
/// </summary>
/// <remarks>
/// <h2>
/// Transport Rules
/// </h2>
/// <list type="bullet">
/// <listheader><description>
/// All transports should follow these rules so that they work correctly with mirror
/// </description></listheader>
/// <item><description>
/// When Monobehaviour is disabled the Transport should not invoke callbacks
/// </description></item>
/// <item><description>
/// Callbacks should be invoked on main thread. It is best to do this from LateUpdate
/// </description></item>
/// <item><description>
/// Callbacks can be invoked after <see cref="ServerStop"/> or <see cref="ClientDisconnect"/> as been called
/// </description></item>
/// <item><description>
/// <see cref="ServerStop"/> or <see cref="ClientDisconnect"/> can be called by mirror multiple times
/// </description></item>
/// <item><description>
/// <see cref="Available"/> should check the platform and 32 vs 64 bit if the transport only works on some of them
/// </description></item>
/// <item><description>
/// <see cref="GetMaxPacketSize"/> should return size even if transport is not running
/// </description></item>
/// <item><description>
/// Default channel should be reliable <see cref="Channels.Reliable"/>
/// </description></item>
/// </list>
/// </remarks>
public abstract class Transport : MonoBehaviour
{
/// <summary>The current transport used by Mirror.</summary>
/// <summary>
/// The current transport used by Mirror.
/// </summary>
public static Transport activeTransport;
/// <summary>Transport available on this platform? Some aren't available on all platforms.</summary>
/// <summary>
/// Is this transport available in the current platform?
/// <para>Some transports might only be available in mobile</para>
/// <para>Many will not work in webgl</para>
/// <para>Example usage: return Application.platform == RuntimePlatform.WebGLPlayer</para>
/// </summary>
/// <returns>True if this transport works in the current platform</returns>
public abstract bool Available();
/// <summary>Notify subscribers when this client establish a successful connection to the server</summary>
#region Client
/// <summary>
/// Notify subscribers when this client establish a successful connection to the server
/// <para>callback()</para>
/// </summary>
public Action OnClientConnected = () => Debug.LogWarning("OnClientConnected called with no handler");
/// <summary>Notify subscribers when this client receive data from the server</summary>
/// <summary>
/// Notify subscribers when this client receive data from the server
/// <para>callback(ArraySegment&lt;byte&gt; data, int channel)</para>
/// </summary>
public Action<ArraySegment<byte>, int> OnClientDataReceived = (data, channel) => Debug.LogWarning("OnClientDataReceived called with no handler");
/// <summary>Notify subscribers when this client encounters an error communicating with the server</summary>
/// <summary>
/// Notify subscribers when this client encounters an error communicating with the server
/// <para>callback(Exception e)</para>
/// </summary>
public Action<Exception> OnClientError = (error) => Debug.LogWarning("OnClientError called with no handler");
/// <summary>Notify subscribers when this client disconnects from the server</summary>
/// <summary>
/// Notify subscribers when this client disconnects from the server
/// <para>callback()</para>
/// </summary>
public Action OnClientDisconnected = () => Debug.LogWarning("OnClientDisconnected called with no handler");
/// <summary>Determines if we are currently connected to the server</summary>
/// <summary>
/// Determines if we are currently connected to the server
/// </summary>
/// <returns>True if a connection has been established to the server</returns>
public abstract bool ClientConnected();
/// <summary>Establish a connection to a server</summary>
/// <summary>
/// Establish a connection to a server
/// </summary>
/// <param name="address">The IP address or FQDN of the server we are trying to connect to</param>
public abstract void ClientConnect(string address);
/// <summary>Establish a connection to a server</summary>
/// <summary>
/// Establish a connection to a server
/// </summary>
/// <param name="uri">The address of the server we are trying to connect to</param>
public virtual void ClientConnect(Uri uri)
{
// By default, to keep backwards compatibility, just connect to the host
@ -48,60 +101,120 @@ public virtual void ClientConnect(Uri uri)
ClientConnect(uri.Host);
}
/// <summary>Send data to the server over a given channel</summary>
/// <summary>
/// Send data to the server
/// </summary>
/// <param name="channelId">The channel to use. 0 is the default channel,
/// but some transports might want to provide unreliable, encrypted, compressed, or any other feature
/// as new channels</param>
/// <param name="segment">The data to send to the server. Will be recycled after returning, so either use it directly or copy it internally. This allows for allocation-free sends!</param>
public abstract void ClientSend(int channelId, ArraySegment<byte> segment);
/// <summary>Disconnect this client from the server</summary>
/// <summary>
/// Disconnect this client from the server
/// </summary>
public abstract void ClientDisconnect();
/// <summary>Get the address of this server. Useful for network discovery</summary>
#endregion
#region Server
/// <summary>
/// Retrieves the address of this server.
/// Useful for network discovery
/// </summary>
/// <returns>the url at which this server can be reached</returns>
public abstract Uri ServerUri();
/// <summary>Notify subscribers when a client connects to this server</summary>
/// <summary>
/// Notify subscribers when a client connects to this server
/// <para>callback(int connId)</para>
/// </summary>
public Action<int> OnServerConnected = (connId) => Debug.LogWarning("OnServerConnected called with no handler");
/// <summary>Notify subscribers when this server receives data from the client</summary>
/// <summary>
/// Notify subscribers when this server receives data from the client
/// <para>callback(int connId, ArraySegment&lt;byte&gt; data, int channel)</para>
/// </summary>
public Action<int, ArraySegment<byte>, int> OnServerDataReceived = (connId, data, channel) => Debug.LogWarning("OnServerDataReceived called with no handler");
/// <summary>Notify subscribers when this server has some problem communicating with the client</summary>
/// <summary>
/// Notify subscribers when this server has some problem communicating with the client
/// <para>callback(int connId, Exception e)</para>
/// </summary>
public Action<int, Exception> OnServerError = (connId, error) => Debug.LogWarning("OnServerError called with no handler");
/// <summary>Notify subscribers when a client disconnects from this server</summary>
/// <summary>
/// Notify subscribers when a client disconnects from this server
/// <para>callback(int connId)</para>
/// </summary>
public Action<int> OnServerDisconnected = (connId) => Debug.LogWarning("OnServerDisconnected called with no handler");
/// <summary>Determines if the server is up and running</summary>
/// <summary>
/// Determines if the server is up and running
/// </summary>
/// <returns>true if the transport is ready for connections from clients</returns>
public abstract bool ServerActive();
/// <summary>Start listening for clients</summary>
/// <summary>
/// Start listening for clients
/// </summary>
public abstract void ServerStart();
/// <summary>Send data to the client with connectionId over the given channel.</summary>
/// <summary>
/// Send data to a client.
/// </summary>
/// <param name="connectionId">The client connection id to send the data to</param>
/// <param name="channelId">The channel to be used. Transports can use channels to implement
/// other features such as unreliable, encryption, compression, etc...</param>
/// <param name="data"></param>
public abstract void ServerSend(int connectionId, int channelId, ArraySegment<byte> segment);
/// <summary>Disconnect a client from this server. Useful to kick people out.</summary>
/// <summary>
/// Disconnect a client from this server. Useful to kick people out.
/// </summary>
/// <param name="connectionId">the id of the client to disconnect</param>
/// <returns>true if the client was kicked</returns>
public abstract bool ServerDisconnect(int connectionId);
/// <summary>Get the client address, useful for IP bans etc.</summary>
/// <summary>
/// Get the client address
/// </summary>
/// <param name="connectionId">id of the client</param>
/// <returns>address of the client</returns>
public abstract string ServerGetClientAddress(int connectionId);
/// <summary>Stop listening for clients and disconnect all existing clients</summary>
/// <summary>
/// Stop listening for clients and disconnect all existing clients
/// </summary>
public abstract void ServerStop();
/// <summary>The maximum packet size for a given channel.</summary>
// Unreliable transports usually can only deliver small packets.
// Reliable fragmented channels can usually deliver large ones.
//
// GetMaxPacketSize needs to return a value at all times. Even if the
// Transport isn't running, or isn't Available(). This is because
// Fallback and Multiplex transports need to find the smallest possible
// packet size at runtime.
public abstract int GetMaxPacketSize(int channelId = Channels.DefaultReliable);
#endregion
/// <summary>The maximum batch(!) size for a given channel.</summary>
// Uses GetMaxPacketSize by default.
// Some transports like kcp support large max packet sizes which should
// not be used for batching all the time because they end up being too
// slow (head of line blocking etc.).
/// <summary>
/// The maximum packet size for a given channel. Unreliable transports
/// usually can only deliver small packets. Reliable fragmented channels
/// can usually deliver large ones.
///
/// GetMaxPacketSize needs to return a value at all times. Even if the
/// Transport isn't running, or isn't Available(). This is because
/// Fallback and Multiplex transports need to find the smallest possible
/// packet size at runtime.
/// </summary>
/// <param name="channelId">channel id</param>
/// <returns>the size in bytes that can be sent via the provided channel</returns>
public abstract int GetMaxPacketSize(int channelId = Channels.Reliable);
/// <summary>
/// The maximum batch(!) size for a given channel.
/// Uses GetMaxPacketSize by default.
/// Some transports like kcp support large max packet sizes which should
/// not be used for batching all the time because they end up being too
/// slow (head of line blocking etc.).
/// </summary>
/// <param name="channelId">channel id</param>
/// <returns>the size in bytes that should be batched via the provided channel</returns>
public virtual int GetMaxBatchSize(int channelId) =>
GetMaxPacketSize(channelId);
@ -122,12 +235,14 @@ public void Update() {}
public void LateUpdate() {}
#pragma warning restore UNT0001 // Empty Unity message
// NetworkLoop NetworkEarly/LateUpdate were added for a proper network
// update order. the goal is to:
// process_incoming()
// update_world()
// process_outgoing()
// in order to avoid unnecessary latency and data races.
/// <summary>
/// NetworkLoop NetworkEarly/LateUpdate were added for a proper network
/// update order. the goal is to:
/// process_incoming()
/// update_world()
/// process_outgoing()
/// in order to avoid unnecessary latency and data races.
/// </summary>
// => split into client and server parts so that we can cleanly call
// them from NetworkClient/Server
// => VIRTUAL for now so we can take our time to convert transports
@ -137,13 +252,15 @@ public virtual void ServerEarlyUpdate() {}
public virtual void ClientLateUpdate() {}
public virtual void ServerLateUpdate() {}
/// <summary>Shut down the transport, both as client and server</summary>
/// <summary>
/// Shut down the transport, both as client and server
/// </summary>
public abstract void Shutdown();
// called when quitting the application by closing the window / pressing
// stop in the editor.
// virtual so that inheriting classes' OnApplicationQuit() can call
// base.OnApplicationQuit() too</para>
/// <summary>
/// called when quitting the application by closing the window / pressing stop in the editor
/// <para>virtual so that inheriting classes' OnApplicationQuit() can call base.OnApplicationQuit() too</para>
/// </summary>
public virtual void OnApplicationQuit()
{
// stop transport (e.g. to shut down threads)

View File

@ -38,8 +38,13 @@ public enum Version
// add custom channels anymore.
public static class Channels
{
public const int DefaultReliable = 0;
public const int DefaultUnreliable = 1;
public const int Reliable = 0; // ordered
public const int Unreliable = 1; // unordered
[Obsolete("Use Channels.Reliable instead")]
public const int DefaultReliable = Reliable;
[Obsolete("Use Channels.Unreliable instead")]
public const int DefaultUnreliable = Unreliable;
}
// -- helpers for float conversion without allocations --

View File

@ -39,7 +39,7 @@ public void TearDown()
public void SendEmptyBatch()
{
// empty batch - nothing should be sent
connection.SendBatch(Channels.DefaultReliable, batch);
connection.SendBatch(Channels.Reliable, batch);
Assert.That(transport.clientIncoming.Count, Is.EqualTo(0));
}
@ -47,7 +47,7 @@ public void SendEmptyBatch()
public void SendAlmostMaxBatchSizedMessageBatch()
{
// create a message < max batch size
int max = transport.GetMaxBatchSize(Channels.DefaultReliable);
int max = transport.GetMaxBatchSize(Channels.Reliable);
byte[] message = new byte[max-1];
// add to batch queue
@ -56,7 +56,7 @@ public void SendAlmostMaxBatchSizedMessageBatch()
batch.messages.Enqueue(writer);
// send batch - client should receive that exact message
connection.SendBatch(Channels.DefaultReliable, batch);
connection.SendBatch(Channels.Reliable, batch);
Assert.That(transport.clientIncoming.Count, Is.EqualTo(1));
Assert.That(transport.clientIncoming.Dequeue().data.Length, Is.EqualTo(message.Length));
}
@ -65,7 +65,7 @@ public void SendAlmostMaxBatchSizedMessageBatch()
public void SendMaxBatchSizedMessageBatch()
{
// create a message == max batch size
int max = transport.GetMaxBatchSize(Channels.DefaultReliable);
int max = transport.GetMaxBatchSize(Channels.Reliable);
byte[] message = new byte[max];
// add to batch queue
@ -74,7 +74,7 @@ public void SendMaxBatchSizedMessageBatch()
batch.messages.Enqueue(writer);
// send batch - client should receive that exact message
connection.SendBatch(Channels.DefaultReliable, batch);
connection.SendBatch(Channels.Reliable, batch);
Assert.That(transport.clientIncoming.Count, Is.EqualTo(1));
Assert.That(transport.clientIncoming.Dequeue().data.Length, Is.EqualTo(message.Length));
}
@ -97,7 +97,7 @@ public void SendTwoSmallMessagesBatch()
batch.messages.Enqueue(writerB);
// send batch - client should receive one message that contains A, B
connection.SendBatch(Channels.DefaultReliable, batch);
connection.SendBatch(Channels.Reliable, batch);
Assert.That(transport.clientIncoming.Count, Is.EqualTo(1));
MemoryTransport.Message msg = transport.clientIncoming.Dequeue();
Assert.That(msg.data.Length, Is.EqualTo(4));
@ -111,7 +111,7 @@ public void SendTwoSmallMessagesBatch()
public void SendAlmostMaxBatchSizedAndSmallMessageBatch()
{
// create a message < max batch size
int max = transport.GetMaxBatchSize(Channels.DefaultReliable);
int max = transport.GetMaxBatchSize(Channels.Reliable);
byte[] almost = new byte[max-1];
// create small message
@ -129,7 +129,7 @@ public void SendAlmostMaxBatchSizedAndSmallMessageBatch()
// send batch - should send the first one and then the second one
// because both together would've been > max
connection.SendBatch(Channels.DefaultReliable, batch);
connection.SendBatch(Channels.Reliable, batch);
Assert.That(transport.clientIncoming.Count, Is.EqualTo(2));
Assert.That(transport.clientIncoming.Dequeue().data.Length, Is.EqualTo(almost.Length));
Assert.That(transport.clientIncoming.Dequeue().data.Length, Is.EqualTo(small.Length));
@ -142,8 +142,8 @@ public void SendAlmostMaxBatchSizedAndSmallMessageBatch()
[Test]
public void SendLargerMaxBatchSizedMessageBatch()
{
int maxBatch = transport.GetMaxBatchSize(Channels.DefaultReliable);
int maxPacket = transport.GetMaxPacketSize(Channels.DefaultReliable);
int maxBatch = transport.GetMaxBatchSize(Channels.Reliable);
int maxPacket = transport.GetMaxPacketSize(Channels.Reliable);
// we can only tested if transport max batch < max message
Assert.That(maxBatch < maxPacket, Is.True);
@ -157,7 +157,7 @@ public void SendLargerMaxBatchSizedMessageBatch()
batch.messages.Enqueue(writer);
// send batch - client should receive that exact message
connection.SendBatch(Channels.DefaultReliable, batch);
connection.SendBatch(Channels.Reliable, batch);
Assert.That(transport.clientIncoming.Count, Is.EqualTo(1));
Assert.That(transport.clientIncoming.Dequeue().data.Length, Is.EqualTo(message.Length));
}

View File

@ -21,11 +21,11 @@ public void CommandAttributeTest()
{
CommandAttribute attrib = new CommandAttribute();
Assert.That(attrib.channel == Channels.DefaultReliable);
Assert.That(attrib.channel == Channels.Reliable);
attrib.channel = Channels.DefaultUnreliable;
attrib.channel = Channels.Unreliable;
Assert.That(attrib.channel == Channels.DefaultUnreliable);
Assert.That(attrib.channel == Channels.Unreliable);
}
[Test]
@ -33,11 +33,11 @@ public void ClientRPCAttributeTest()
{
ClientRpcAttribute attrib = new ClientRpcAttribute();
Assert.That(attrib.channel == Channels.DefaultReliable);
Assert.That(attrib.channel == Channels.Reliable);
attrib.channel = Channels.DefaultUnreliable;
attrib.channel = Channels.Unreliable;
Assert.That(attrib.channel == Channels.DefaultUnreliable);
Assert.That(attrib.channel == Channels.Unreliable);
}
[Test]
@ -45,9 +45,9 @@ public void TargetRPCAttributeTest()
{
TargetRpcAttribute attrib = new TargetRpcAttribute();
Assert.That(attrib.channel == Channels.DefaultReliable);
Assert.That(attrib.channel == Channels.Reliable);
attrib.channel = Channels.DefaultUnreliable;
attrib.channel = Channels.Unreliable;
Assert.That(attrib.channel == 1);
}

View File

@ -44,9 +44,9 @@ public void TestAvailable(bool available)
}
[Test]
[TestCase(Channels.DefaultReliable, 4000)]
[TestCase(Channels.DefaultReliable, 2000)]
[TestCase(Channels.DefaultUnreliable, 4000)]
[TestCase(Channels.Reliable, 4000)]
[TestCase(Channels.Reliable, 2000)]
[TestCase(Channels.Unreliable, 4000)]
public void TestGetMaxPacketSize(int channel, int packageSize)
{
inner.GetMaxPacketSize(Arg.Any<int>()).Returns(packageSize);
@ -97,8 +97,8 @@ public void TestClientDisconnect()
}
[Test]
[TestCase(Channels.DefaultReliable)]
[TestCase(Channels.DefaultUnreliable)]
[TestCase(Channels.Reliable)]
[TestCase(Channels.Unreliable)]
public void TestClientSend(int channel)
{
byte[] array = new byte[10];