perf: Batching (#2552)

* perf: Batching. Batches message into Transport.GetMaxPacketSize sized chunks and sends them every batchInterval

* don't log time

* Transport.GetMaxBatchSize and kcp override it to always use MTU

* remove comment

* NetworkConnectionToClient tests

* Test: Send_BatchesUntilUpdate

* Test: Send_BatchesUntilInterval

* fix: initialize last send time with NetworkTime.time

* better comment

* fixing reset after sending batch

Need to reset both position and length

* revert the transportreceive change for localconnections.
before it didn't work because of the length bug that is fixed now.

* added test to avoid length bug in the future

* optional

* enable batching in benchmark demo for max scale

Co-authored-by: James Frowen <jamesfrowendev@gmail.com>
This commit is contained in:
vis2k 2021-02-03 22:47:06 +08:00 committed by GitHub
parent d870351afb
commit 1ff4fe30b7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 400 additions and 53 deletions

View File

@ -293,6 +293,8 @@ MonoBehaviour:
autoStartServerBuild: 1
showDebugMessages: 0
serverTickRate: 30
serverBatching: 1
serverBatchInterval: 0
offlineScene:
onlineScene:
transport: {fileID: 1282001521}

View File

@ -9,7 +9,7 @@ class ULocalConnectionToClient : NetworkConnectionToClient
{
internal ULocalConnectionToServer connectionToServer;
public ULocalConnectionToClient() : base(LocalConnectionId) { }
public ULocalConnectionToClient() : base(LocalConnectionId, false, 0) { }
public override string address => "localhost";

View File

@ -222,7 +222,7 @@ public bool InvokeHandler<T>(T msg, int channelId)
}
// helper function
protected void UnpackAndInvoke(NetworkReader reader, int channelId)
protected bool UnpackAndInvoke(NetworkReader reader, int channelId)
{
if (MessagePacker.Unpack(reader, out int msgType))
{
@ -231,25 +231,22 @@ protected void UnpackAndInvoke(NetworkReader reader, int channelId)
{
msgDelegate.Invoke(this, reader, channelId);
lastMessageTime = Time.time;
return true;
}
else
{
if (logger.LogEnabled()) logger.Log("Unknown message ID " + msgType + " " + this + ". May be due to no existing RegisterHandler for this message.");
return false;
}
}
else
{
logger.LogError("Closed connection: " + this + ". Invalid message header.");
Disconnect();
return false;
}
}
// note: original HLAPI HandleBytes function handled >1 message in a while loop, but this wasn't necessary
// anymore because NetworkServer/NetworkClient Update both use while loops to handle >1 data events per
// frame already.
// -> in other words, we always receive 1 message per Receive call, never two.
// -> can be tested easily with a 1000ms send delay and then logging amount received in while loops here
// and in NetworkServer/Client Update. HandleBytes already takes exactly one.
/// <summary>
/// This function allows custom network connection classes to process data from the network before it is passed to the application.
/// </summary>
@ -266,7 +263,13 @@ internal void TransportReceive(ArraySegment<byte> buffer, int channelId)
// unpack message
using (PooledNetworkReader reader = NetworkReaderPool.GetReader(buffer))
{
UnpackAndInvoke(reader, channelId);
// the other end might batch multiple messages into one packet.
// we need to try to unpack multiple times.
while (reader.Position < reader.Length)
{
if (!UnpackAndInvoke(reader, channelId))
break;
}
}
}

View File

@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using UnityEngine;
namespace Mirror
@ -9,16 +10,150 @@ public class NetworkConnectionToClient : NetworkConnection
public override string address => Transport.activeTransport.ServerGetClientAddress(connectionId);
public NetworkConnectionToClient(int networkConnectionId) : base(networkConnectionId) { }
// batching from server to client.
// fewer transport calls give us significantly better performance/scale.
//
// for a 64KB max message transport and 64 bytes/message on average, we
// reduce transport calls by a factor of 1000.
//
// depending on the transport, this can give 10x performance.
//
// Dictionary<channelId, batch> because we have multiple channels.
internal class Batch
{
// each batch needs a writer for batching
// => we allocate one writer per channel
// => it grows to Transport MaxMessageSize automatically
// TODO maybe use a pooled writer and return when disconnecting?
internal NetworkWriter writer = new NetworkWriter();
// each channel's batch has its own lastSendTime.
// (use NetworkTime for maximum precision over days)
//
// channel batches are full and flushed at different times. using
// one global time wouldn't make sense.
// -> we want to be able to reset a channels send time after Send()
// flushed it because full. global time wouldn't allow that, so
// we would often flush in Send() and then flush again in Update
// even though we just flushed in Send().
// -> initialize with current NetworkTime so first update doesn't
// calculate elapsed via 'now - 0'
internal double lastSendTime = NetworkTime.time;
}
Dictionary<int, Batch> batches = new Dictionary<int, Batch>();
// batching is optional because due to mirror's non-optimal update order
// it would increase latency.
// batching is still optional until we improve mirror's update order.
// right now it increases latency because:
// enabling batching flushes all state updates in same frame, but
// transport processes incoming messages afterwards so server would
// batch them until next frame's flush
// => disable it for super fast paced games
// => enable it for high scale / cpu heavy games
bool batching;
// batch interval is 0 by default, meaning that we send immediately.
// (useful to run tests without waiting for intervals too)
float batchInterval;
public NetworkConnectionToClient(int networkConnectionId, bool batching, float batchInterval)
: base(networkConnectionId)
{
this.batching = batching;
this.batchInterval = batchInterval;
}
Batch GetBatchForChannelId(int channelId)
{
// get existing or create new writer for the channelId
Batch batch;
if (!batches.TryGetValue(channelId, out batch))
{
batch = new Batch();
batches[channelId] = batch;
}
return batch;
}
void SendBatch(int channelId, Batch batch)
{
// send batch
Transport.activeTransport.ServerSend(connectionId, channelId, batch.writer.ToArraySegment());
// clear batch
batch.writer.Reset();
// reset send time for this channel's batch
batch.lastSendTime = NetworkTime.time;
}
internal override void Send(ArraySegment<byte> segment, int channelId = Channels.DefaultReliable)
{
if (logger.LogEnabled()) logger.Log("ConnectionSend " + this + " bytes:" + BitConverter.ToString(segment.Array, segment.Offset, segment.Count));
//Debug.Log("ConnectionSend " + this + " bytes:" + BitConverter.ToString(segment.Array, segment.Offset, segment.Count));
// validate packet size first.
if (ValidatePacketSize(segment, channelId))
{
Transport.activeTransport.ServerSend(connectionId, channelId, segment);
// batching?
if (batching)
{
// always batch!
// (even if interval == 0, in which case we flush in Update())
//
// if batch would become bigger than MaxBatchPacketSize for this
// channel then send out the previous batch first.
//
// IMPORTANT: we use maxBATCHsize not maxPACKETsize.
// some transports like kcp have large maxPACKETsize
// like 144kb, but those would extremely slow to use
// all the time for batching.
// (maxPACKETsize messages still work fine, we just
// aim for maxBATCHsize where possible)
Batch batch = GetBatchForChannelId(channelId);
int max = Transport.activeTransport.GetMaxBatchSize(channelId);
if (batch.writer.Position + segment.Count > max)
{
//UnityEngine.Debug.LogWarning($"sending batch {batch.writer.Position} / {max} after full for segment={segment.Count} for connectionId={connectionId}");
SendBatch(channelId, batch);
}
// now add segment to batch
batch.writer.WriteBytes(segment.Array, segment.Offset, segment.Count);
}
// otherwise send directly to minimize latency
else
{
Transport.activeTransport.ServerSend(connectionId, channelId, segment);
}
}
}
// flush batched messages every batchInterval to make sure that they are
// sent out every now and then, even if the batch isn't full yet.
// (avoids 30s latency if batches would only get full every 30s)
internal void Update()
{
// batching?
if (batching)
{
// go through batches for all channels
foreach (KeyValuePair<int, Batch> kvp in batches)
{
// enough time elapsed to flush this channel's batch?
// and not empty?
double elapsed = NetworkTime.time - kvp.Value.lastSendTime;
if (elapsed >= batchInterval &&
kvp.Value.writer.Position > 0)
{
// send the batch. time will be reset internally.
//Debug.Log($"sending batch of {kvp.Value.writer.Position} bytes for channel={kvp.Key} connId={connectionId}");
SendBatch(kvp.Key, kvp.Value);
}
}
}
}

View File

@ -64,6 +64,26 @@ public class NetworkManager : MonoBehaviour
[Tooltip("Server Update frequency, per second. Use around 60Hz for fast paced games like Counter-Strike to minimize latency. Use around 30Hz for games like WoW to minimize computations. Use around 1-10Hz for slow paced games like EVE.")]
public int serverTickRate = 30;
/// <summary>
/// batching is still optional until we improve mirror's update order.
/// right now it increases latency because:
/// enabling batching flushes all state updates in same frame, but
/// transport processes incoming messages afterwards so server would
/// batch them until next frame's flush
/// => disable it for super fast paced games
/// => enable it for high scale / cpu heavy games
/// </summary>
[Tooltip("Batching greatly reduces CPU & Transport load, but increases latency by one frame time. Use for high scale games / CPU intensive games. Don't use for fast paced games.")]
public bool serverBatching;
/// <summary>
/// batching from server to client.
/// fewer transport calls give us significantly better performance/scale.
/// if batch interval is 0, then we only batch until the Update() call
/// </summary>
[Tooltip("Server can batch messages up to Transport.GetMaxPacketSize to significantly reduce transport calls and improve performance/scale./nIf batch interval is 0, then we only batch until the Update() call. Otherwise we batch until interval elapsed (note that this increases latency).")]
public float serverBatchInterval = 0;
/// <summary>
/// The scene to switch to when offline.
/// <para>Setting this makes the NetworkManager do scene management. This scene will be switched to when a network session is completed - such as a client disconnect, or a server shutdown.</para>
@ -310,6 +330,10 @@ void SetupServer()
ConfigureServerFrameRate();
// batching
NetworkServer.batching = serverBatching;
NetworkServer.batchInterval = serverBatchInterval;
// Copy auto-disconnect settings to NetworkServer
NetworkServer.disconnectInactiveTimeout = disconnectInactiveTimeout;
NetworkServer.disconnectInactiveConnections = disconnectInactiveConnections;

View File

@ -57,6 +57,25 @@ public static class NetworkServer
/// </summary>
public static bool active { get; internal set; }
/// <summary>
/// batching is still optional until we improve mirror's update order.
/// right now it increases latency because:
/// enabling batching flushes all state updates in same frame, but
/// transport processes incoming messages afterwards so server would
/// batch them until next frame's flush
/// => disable it for super fast paced games
/// => enable it for high scale / cpu heavy games
/// </summary>
public static bool batching;
/// <summary>
/// batching from server to client.
/// fewer transport calls give us significantly better performance/scale.
/// if batch interval is 0, then we only batch until the Update() call.
/// </summary>
public static float batchInterval = 0;
/// <summary>
/// Should the server disconnect remote connections that have gone silent for more than Server Idle Timeout?
/// <para>This value is initially set from NetworkManager in SetupServer and can be changed at runtime</para>
@ -470,6 +489,12 @@ public static void Update()
logger.LogWarning("Found 'null' entry in spawned list for netId=" + kvp.Key + ". Please call NetworkServer.Destroy to destroy networked objects. Don't use GameObject.Destroy.");
}
}
// update all connections to send out batched messages in interval
foreach (NetworkConnectionToClient conn in connections.Values)
{
conn.Update();
}
}
static void CheckForInactiveConnections()
@ -525,7 +550,7 @@ static void OnConnected(int connectionId)
if (connections.Count < maxConnections)
{
// add connection
NetworkConnectionToClient conn = new NetworkConnectionToClient(connectionId);
NetworkConnectionToClient conn = new NetworkConnectionToClient(connectionId, batching, batchInterval);
OnConnected(conn);
}
else

View File

@ -197,6 +197,17 @@ public override int GetMaxPacketSize(int channelId = Channels.DefaultReliable)
}
}
// kcp reliable channel max packet size is MTU * WND_RCV
// this allows 144kb messages. but due to head of line blocking, all
// other messages would have to wait until the maxed size one is
// delivered. batching 144kb messages each time would be EXTREMELY slow
// and fill the send queue nearly immediately when using it over the
// network.
// => instead we always use MTU sized batches.
// => people can still send maxed size if needed.
public override int GetMaxBatchSize(int channelId) =>
KcpConnection.UnreliableMaxMessageSize;
public override string ToString()
{
return "KCP";

View File

@ -8,7 +8,7 @@ namespace Mirror
/// </summary>
/// <remarks>
/// <h2>
/// Transport Rules
/// Transport Rules
/// </h2>
/// <list type="bullet">
/// <listheader><description>
@ -206,6 +206,18 @@ public virtual void ClientConnect(Uri uri)
/// <returns>the size in bytes that can be sent via the provided channel</returns>
public abstract int GetMaxPacketSize(int channelId = Channels.DefaultReliable);
/// <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);
/// <summary>
/// Shut down the transport, both as client and server
/// </summary>

View File

@ -4,7 +4,7 @@ namespace Mirror.Tests
{
public class FakeNetworkConnection : NetworkConnectionToClient
{
public FakeNetworkConnection() : base(1)
public FakeNetworkConnection() : base(1, false, 0)
{
}

View File

@ -0,0 +1,132 @@
using System;
using System.Collections.Generic;
using System.Threading;
using NUnit.Framework;
using UnityEngine;
namespace Mirror.Tests
{
public class NetworkConnectionToClientTests
{
GameObject transportGO;
MemoryTransport transport;
List<byte[]> clientReceived = new List<byte[]>();
[SetUp]
public void SetUp()
{
// transport is needed by server and client.
// it needs to be on a gameobject because client.connect enables it,
// which throws a NRE if not on a gameobject
transportGO = new GameObject();
Transport.activeTransport = transport = transportGO.AddComponent<MemoryTransport>();
transport.OnClientDataReceived = (message, channelId) => {
byte[] array = new byte[message.Count];
Buffer.BlockCopy(message.Array, message.Offset, array, 0, message.Count);
clientReceived.Add(array);
};
transport.ServerStart();
transport.ClientConnect("localhost");
Assert.That(transport.ServerActive, Is.True);
Assert.That(transport.ClientConnected, Is.True);
}
[TearDown]
public void TearDown()
{
clientReceived.Clear();
GameObject.DestroyImmediate(transportGO);
}
[Test]
public void Send_WithoutBatching_SendsImmediately()
{
// create connection and send
NetworkConnectionToClient connection = new NetworkConnectionToClient(42, false, 0);
byte[] message = {0x01, 0x02};
connection.Send(new ArraySegment<byte>(message));
// Send() should send immediately, not only in server.update flushing
transport.LateUpdate();
Assert.That(clientReceived.Count, Is.EqualTo(1));
}
[Test]
public void Send_BatchesUntilUpdate()
{
// create connection and send
NetworkConnectionToClient connection = new NetworkConnectionToClient(42, true, 0);
byte[] message = {0x01, 0x02};
connection.Send(new ArraySegment<byte>(message));
// Send() should only add to batch, not send anything yet
transport.LateUpdate();
Assert.That(clientReceived.Count, Is.EqualTo(0));
// updating the connection should now send
connection.Update();
transport.LateUpdate();
Assert.That(clientReceived.Count, Is.EqualTo(1));
}
[Test]
public void Send_BatchesUntilInterval()
{
// create connection and send
int intervalMilliseconds = 10;
float intervalSeconds = intervalMilliseconds / 1000f;
NetworkConnectionToClient connection = new NetworkConnectionToClient(42, true, intervalSeconds);
byte[] message = {0x01, 0x02};
connection.Send(new ArraySegment<byte>(message));
// Send() and update shouldn't send yet until interval elapsed
connection.Update();
transport.LateUpdate();
Assert.That(clientReceived.Count, Is.EqualTo(0));
// wait 'interval'
Thread.Sleep(intervalMilliseconds);
// updating again should flush out the batch
connection.Update();
transport.LateUpdate();
Assert.That(clientReceived.Count, Is.EqualTo(1));
}
// IMPORTANT
//
// there was a bug where batching resets .Position instead of .Length,
// resulting in extremely high bandwidth where if the last message's
// Length was 2, and the current message's Length was 1, then we would
// still send a writer with Length = 2 because we did not reset .Length!
// -> let's try to send a big message, update, then send a small message
[Test]
public void SendBatchingResetsPreviousWriter()
{
// create connection
NetworkConnectionToClient connection = new NetworkConnectionToClient(42, true, 0);
// send and update big message
byte[] message = {0x01, 0x02};
connection.Send(new ArraySegment<byte>(message));
connection.Update();
transport.LateUpdate();
Assert.That(clientReceived.Count, Is.EqualTo(1));
Assert.That(clientReceived[0].Length, Is.EqualTo(2));
Assert.That(clientReceived[0][0], Is.EqualTo(0x01));
Assert.That(clientReceived[0][1], Is.EqualTo(0x02));
// clear previous
clientReceived.Clear();
// send a smaller message
message = new byte[]{0xFF};
connection.Send(new ArraySegment<byte>(message));
connection.Update();
transport.LateUpdate();
Assert.That(clientReceived.Count, Is.EqualTo(1));
Assert.That(clientReceived[0].Length, Is.EqualTo(1));
Assert.That(clientReceived[0][0], Is.EqualTo(0xFF));
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 22aee0cf91224ec9925f7faabc073b09
timeCreated: 1611722538

View File

@ -510,11 +510,11 @@ public void RemoveObserverInternal()
identity.OnStartServer();
// add an observer connection
NetworkConnectionToClient connection = new NetworkConnectionToClient(42);
NetworkConnectionToClient connection = new NetworkConnectionToClient(42, false, 0);
identity.observers[connection.connectionId] = connection;
// RemoveObserverInternal with invalid connection should do nothing
identity.RemoveObserverInternal(new NetworkConnectionToClient(43));
identity.RemoveObserverInternal(new NetworkConnectionToClient(43, false, 0));
Assert.That(identity.observers.Count, Is.EqualTo(1));
// RemoveObserverInternal with existing connection should remove it
@ -711,7 +711,7 @@ public void AssignAndRemoveClientAuthority()
// another connection
// error log is expected
LogAssert.ignoreFailingMessages = true;
result = identity.AssignClientAuthority(new NetworkConnectionToClient(43));
result = identity.AssignClientAuthority(new NetworkConnectionToClient(43, false, 0));
LogAssert.ignoreFailingMessages = false;
Assert.That(result, Is.False);
Assert.That(identity.connectionToClient, Is.EqualTo(owner));
@ -874,7 +874,7 @@ public void OnCheckObserverCatchesException()
// add component
CheckObserverExceptionNetworkBehaviour compExc = gameObject.AddComponent<CheckObserverExceptionNetworkBehaviour>();
NetworkConnection connection = new NetworkConnectionToClient(42);
NetworkConnection connection = new NetworkConnectionToClient(42, false, 0);
// an exception in OnCheckObserver should be caught
// (an error log is expected)
@ -896,7 +896,7 @@ public void OnCheckObserverTrue()
// create a networkidentity with a component that returns true
// result should be true.
CheckObserverTrueNetworkBehaviour compTrue = gameObject.AddComponent<CheckObserverTrueNetworkBehaviour>();
NetworkConnection connection = new NetworkConnectionToClient(42);
NetworkConnection connection = new NetworkConnectionToClient(42, false, 0);
bool result = identity.OnCheckObserver(connection);
Assert.That(result, Is.True);
Assert.That(compTrue.called, Is.EqualTo(1));
@ -908,7 +908,7 @@ public void OnCheckObserverFalse()
// create a networkidentity with a component that returns false
// result should be false.
CheckObserverFalseNetworkBehaviour compFalse = gameObject.AddComponent<CheckObserverFalseNetworkBehaviour>();
NetworkConnection connection = new NetworkConnectionToClient(42);
NetworkConnection connection = new NetworkConnectionToClient(42, false, 0);
bool result = identity.OnCheckObserver(connection);
Assert.That(result, Is.False);
Assert.That(compFalse.called, Is.EqualTo(1));
@ -1142,8 +1142,8 @@ public void OnStopServerEx()
public void AddObserver()
{
// create some connections
NetworkConnectionToClient connection1 = new NetworkConnectionToClient(42);
NetworkConnectionToClient connection2 = new NetworkConnectionToClient(43);
NetworkConnectionToClient connection1 = new NetworkConnectionToClient(42, false, 0);
NetworkConnectionToClient connection2 = new NetworkConnectionToClient(43, false, 0);
// AddObserver should return early if called before .observers was
// created
@ -1167,7 +1167,7 @@ public void AddObserver()
Assert.That(identity.observers[connection2.connectionId], Is.EqualTo(connection2));
// adding a duplicate connectionId shouldn't overwrite the original
NetworkConnectionToClient duplicate = new NetworkConnectionToClient(connection1.connectionId);
NetworkConnectionToClient duplicate = new NetworkConnectionToClient(connection1.connectionId, false, 0);
identity.AddObserver(duplicate);
Assert.That(identity.observers.Count, Is.EqualTo(2));
Assert.That(identity.observers.ContainsKey(connection1.connectionId));
@ -1183,8 +1183,8 @@ public void ClearObservers()
identity.OnStartServer();
// add some observers
identity.observers[42] = new NetworkConnectionToClient(42);
identity.observers[43] = new NetworkConnectionToClient(43);
identity.observers[42] = new NetworkConnectionToClient(42, false, 0);
identity.observers[43] = new NetworkConnectionToClient(43, false, 0);
// call ClearObservers
identity.ClearObservers();
@ -1262,9 +1262,9 @@ public void Reset()
identity.isClient = true;
// creates .observers and generates a netId
identity.OnStartServer();
identity.connectionToClient = new NetworkConnectionToClient(1);
identity.connectionToClient = new NetworkConnectionToClient(1, false, 0);
identity.connectionToServer = new NetworkConnectionToServer();
identity.observers[43] = new NetworkConnectionToClient(2);
identity.observers[43] = new NetworkConnectionToClient(2, false, 0);
// mark for reset and reset
identity.Reset();
@ -1279,7 +1279,7 @@ public void HandleCommand()
{
// add component
CommandTestNetworkBehaviour comp0 = gameObject.AddComponent<CommandTestNetworkBehaviour>();
NetworkConnectionToClient connection = new NetworkConnectionToClient(1);
NetworkConnectionToClient connection = new NetworkConnectionToClient(1, false, 0);
Assert.That(comp0.called, Is.EqualTo(0));
Assert.That(comp0.senderConnectionInCall, Is.Null);
@ -1446,7 +1446,7 @@ public void GetNewObservers()
{
// add components
RebuildObserversNetworkBehaviour comp = gameObject.AddComponent<RebuildObserversNetworkBehaviour>();
comp.observer = new NetworkConnectionToClient(12);
comp.observer = new NetworkConnectionToClient(12, false, 0);
// get new observers
HashSet<NetworkConnection> observers = new HashSet<NetworkConnection>();
@ -1462,7 +1462,7 @@ public void GetNewObserversClearsHashSet()
// get new observers. no observer components so it should just clear
// it and not do anything else
HashSet<NetworkConnection> observers = new HashSet<NetworkConnection>();
observers.Add(new NetworkConnectionToClient(42));
observers.Add(new NetworkConnectionToClient(42, false, 0));
identity.GetNewObservers(observers, true);
Assert.That(observers.Count, Is.EqualTo(0));
}
@ -1480,8 +1480,8 @@ public void GetNewObserversFalseIfNoComponents()
public void AddAllReadyServerConnectionsToObservers()
{
// add some server connections
NetworkServer.connections[12] = new NetworkConnectionToClient(12) { isReady = true };
NetworkServer.connections[13] = new NetworkConnectionToClient(13) { isReady = false };
NetworkServer.connections[12] = new NetworkConnectionToClient(12, false, 0) { isReady = true };
NetworkServer.connections[13] = new NetworkConnectionToClient(13, false, 0) { isReady = false };
// add a host connection
ULocalConnectionToClient localConnection = new ULocalConnectionToClient();
@ -1554,7 +1554,7 @@ public void RebuildObserversAddsReadyConnectionsIfImplemented()
// add a proximity checker
// one with a ready connection, one with no ready connection, one with null connection
RebuildObserversNetworkBehaviour comp = gameObject.AddComponent<RebuildObserversNetworkBehaviour>();
comp.observer = new NetworkConnectionToClient(42) { isReady = true };
comp.observer = new NetworkConnectionToClient(42, false, 0) { isReady = true };
// call OnStartServer so that observers dict is created
identity.OnStartServer();
@ -1575,7 +1575,7 @@ public void RebuildObserversDoesntAddNotReadyConnectionsIfImplemented()
// add a proximity checker
// one with a ready connection, one with no ready connection, one with null connection
RebuildObserversNetworkBehaviour comp = gameObject.AddComponent<RebuildObserversNetworkBehaviour>();
comp.observer = new NetworkConnectionToClient(42) { isReady = false };
comp.observer = new NetworkConnectionToClient(42, false, 0) { isReady = false };
// call OnStartServer so that observers dict is created
identity.OnStartServer();
@ -1592,8 +1592,8 @@ public void RebuildObserversDoesntAddNotReadyConnectionsIfImplemented()
public void RebuildObserversAddsReadyServerConnectionsIfNotImplemented()
{
// add some server connections
NetworkServer.connections[12] = new NetworkConnectionToClient(12) { isReady = true };
NetworkServer.connections[13] = new NetworkConnectionToClient(13) { isReady = false };
NetworkServer.connections[12] = new NetworkConnectionToClient(12, false, 0) { isReady = true };
NetworkServer.connections[13] = new NetworkConnectionToClient(13, false, 0) { isReady = false };
// call OnStartServer so that observers dict is created
identity.OnStartServer();
@ -1612,7 +1612,7 @@ public void RebuildObserversAddsReadyServerConnectionsIfNotImplemented()
public void RebuildObserversDoesNotAddServerConnectionsIfImplemented()
{
// add a server connection
NetworkServer.connections[12] = new NetworkConnectionToClient(12) { isReady = true };
NetworkServer.connections[12] = new NetworkConnectionToClient(12, false, 0) { isReady = true };
// add at least one observers component, otherwise it will just add
// all server connections
@ -1636,7 +1636,7 @@ public void RebuildObserversAddRemoveAndVisListTest()
{
// add observer component with ready observer
RebuildObserversNetworkBehaviour comp = gameObject.AddComponent<RebuildObserversNetworkBehaviour>();
NetworkConnectionToClient observerA = new NetworkConnectionToClient(42) { isReady = true };
NetworkConnectionToClient observerA = new NetworkConnectionToClient(42, false, 0) { isReady = true };
comp.observer = observerA;
// call OnStartServer so that observers dict is created
@ -1652,7 +1652,7 @@ public void RebuildObserversAddRemoveAndVisListTest()
Assert.That(observerA.visList.Contains(identity), Is.True);
// let the component find another observer
NetworkConnectionToClient observerB = new NetworkConnectionToClient(43) { isReady = true };
NetworkConnectionToClient observerB = new NetworkConnectionToClient(43, false, 0) { isReady = true };
comp.observer = observerB;
// rebuild observers should remove the old observer and add the new one
@ -1703,7 +1703,7 @@ public void RebuildObserversSetsHostVisibility()
public void RebuildObserversReturnsIfNull()
{
// add a server connection
NetworkServer.connections[12] = new NetworkConnectionToClient(12) { isReady = true };
NetworkServer.connections[12] = new NetworkConnectionToClient(12, false, 0) { isReady = true };
// call RebuildObservers without calling OnStartServer first.
// .observers will be null and it should simply return early.

View File

@ -50,7 +50,7 @@ static Dictionary<Guid, HashSet<NetworkIdentity>> GetMatchPlayersDictionary()
static NetworkConnection CreateNetworkConnection(GameObject player)
{
NetworkConnectionToClient connection = new NetworkConnectionToClient(++nextConnectionId);
NetworkConnectionToClient connection = new NetworkConnectionToClient(++nextConnectionId, false, 0);
connection.identity = player.GetComponent<NetworkIdentity>();
connection.identity.connectionToClient = connection;
connection.identity.observers = new Dictionary<int, NetworkConnection>();

View File

@ -324,7 +324,7 @@ public void AddConnectionTest()
Assert.That(NetworkServer.connections.Count, Is.EqualTo(0));
// add first connection
NetworkConnectionToClient conn42 = new NetworkConnectionToClient(42);
NetworkConnectionToClient conn42 = new NetworkConnectionToClient(42, false, 0);
bool result42 = NetworkServer.AddConnection(conn42);
Assert.That(result42, Is.True);
Assert.That(NetworkServer.connections.Count, Is.EqualTo(1));
@ -332,7 +332,7 @@ public void AddConnectionTest()
Assert.That(NetworkServer.connections[42], Is.EqualTo(conn42));
// add second connection
NetworkConnectionToClient conn43 = new NetworkConnectionToClient(43);
NetworkConnectionToClient conn43 = new NetworkConnectionToClient(43, false, 0);
bool result43 = NetworkServer.AddConnection(conn43);
Assert.That(result43, Is.True);
Assert.That(NetworkServer.connections.Count, Is.EqualTo(2));
@ -342,7 +342,7 @@ public void AddConnectionTest()
Assert.That(NetworkServer.connections[43], Is.EqualTo(conn43));
// add duplicate connectionId
NetworkConnectionToClient connDup = new NetworkConnectionToClient(42);
NetworkConnectionToClient connDup = new NetworkConnectionToClient(42, false, 0);
bool resultDup = NetworkServer.AddConnection(connDup);
Assert.That(resultDup, Is.False);
Assert.That(NetworkServer.connections.Count, Is.EqualTo(2));
@ -365,7 +365,7 @@ public void RemoveConnectionTest()
Assert.That(NetworkServer.connections.Count, Is.EqualTo(0));
// add connection
NetworkConnectionToClient conn42 = new NetworkConnectionToClient(42);
NetworkConnectionToClient conn42 = new NetworkConnectionToClient(42, false, 0);
bool result42 = NetworkServer.AddConnection(conn42);
Assert.That(result42, Is.True);
Assert.That(NetworkServer.connections.Count, Is.EqualTo(1));
@ -391,7 +391,7 @@ public void DisconnectAllConnectionsTest()
Assert.That(NetworkServer.connections.Count, Is.EqualTo(0));
// add connection
NetworkConnectionToClient conn42 = new NetworkConnectionToClient(42);
NetworkConnectionToClient conn42 = new NetworkConnectionToClient(42, false, 0);
NetworkServer.AddConnection(conn42);
Assert.That(NetworkServer.connections.Count, Is.EqualTo(1));
@ -418,7 +418,7 @@ public void DisconnectAllTest()
Assert.That(NetworkServer.localConnection, Is.EqualTo(localConnection));
// add connection
NetworkConnectionToClient conn42 = new NetworkConnectionToClient(42);
NetworkConnectionToClient conn42 = new NetworkConnectionToClient(42, false, 0);
NetworkServer.AddConnection(conn42);
Assert.That(NetworkServer.connections.Count, Is.EqualTo(1));
@ -452,7 +452,7 @@ public void OnDataReceivedTest()
Assert.That(NetworkServer.connections.Count, Is.EqualTo(0));
// add a connection
NetworkConnectionToClient connection = new NetworkConnectionToClient(42);
NetworkConnectionToClient connection = new NetworkConnectionToClient(42, false, 0);
NetworkServer.AddConnection(connection);
Assert.That(NetworkServer.connections.Count, Is.EqualTo(1));
@ -776,7 +776,7 @@ public void RegisterUnregisterClearHandlerTest()
Assert.That(NetworkServer.connections.Count, Is.EqualTo(0));
// add a connection
NetworkConnectionToClient connection = new NetworkConnectionToClient(42);
NetworkConnectionToClient connection = new NetworkConnectionToClient(42, false, 0);
NetworkServer.AddConnection(connection);
Assert.That(NetworkServer.connections.Count, Is.EqualTo(1));

View File

@ -5,7 +5,7 @@ namespace Mirror.Tests.Performance
{
public class FakeNetworkConnection : NetworkConnectionToClient
{
public FakeNetworkConnection(int networkConnectionId) : base(networkConnectionId)
public FakeNetworkConnection(int networkConnectionId) : base(networkConnectionId, false, 0)
{
}

View File

@ -36,7 +36,7 @@ public void TearDown()
public IEnumerator DestroyPlayerForConnectionTest()
{
GameObject player = new GameObject("testPlayer", typeof(NetworkIdentity));
NetworkConnectionToClient conn = new NetworkConnectionToClient(1);
NetworkConnectionToClient conn = new NetworkConnectionToClient(1, false, 0);
NetworkServer.AddPlayerForConnection(conn, player);
@ -55,7 +55,7 @@ public IEnumerator DestroyPlayerForConnectionTest()
public IEnumerator RemovePlayerForConnectionTest()
{
GameObject player = new GameObject("testPlayer", typeof(NetworkIdentity));
NetworkConnectionToClient conn = new NetworkConnectionToClient(1);
NetworkConnectionToClient conn = new NetworkConnectionToClient(1, false, 0);
NetworkServer.AddPlayerForConnection(conn, player);

View File

@ -55,7 +55,7 @@ public IEnumerator DisconnectTimeoutTest()
GameObject remotePlayer = new GameObject("RemotePlayer", typeof(NetworkIdentity));
const int remoteConnectionId = 1;
const int localConnectionId = 0;
NetworkConnectionToClient remoteConnection = new NetworkConnectionToClient(remoteConnectionId);
NetworkConnectionToClient remoteConnection = new NetworkConnectionToClient(remoteConnectionId, false, 0);
NetworkServer.OnConnected(remoteConnection);
NetworkServer.AddPlayerForConnection(remoteConnection, remotePlayer);