diff --git a/Assets/Mirror/Runtime/LocalConnections.cs b/Assets/Mirror/Runtime/LocalConnections.cs index de4354df9..dd351772b 100644 --- a/Assets/Mirror/Runtime/LocalConnections.cs +++ b/Assets/Mirror/Runtime/LocalConnections.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using UnityEngine; namespace Mirror @@ -18,11 +17,8 @@ public ULocalConnectionToClient() : base(0) internal override bool Send(ArraySegment segment, int channelId = Channels.DefaultReliable) { - // LocalConnection doesn't support allocation-free sends yet. - // previously we allocated in Mirror. now we do it here. - byte[] data = new byte[segment.Count]; - Array.Copy(segment.Array, segment.Offset, data, 0, segment.Count); - connectionToServer.packetQueue.Enqueue(data); + connectionToServer.buffer.Write(segment); + return true; } @@ -44,16 +40,48 @@ public override void Disconnect() } } + internal class LocalConnectionBuffer + { + readonly NetworkWriter writer = new NetworkWriter(); + readonly NetworkReader reader = new NetworkReader(default(ArraySegment)); + // The buffer is atleast 1500 bytes long. So need to keep track of + // packet count to know how many ArraySegments are in the buffer + int packetCount; + + public void Write(ArraySegment segment) + { + writer.WriteBytesAndSizeSegment(segment); + packetCount++; + + // update buffer incase writer's length has changed + reader.buffer = writer.ToArraySegment(); + } + + public bool HasPackets() + { + return packetCount > 0; + } + public ArraySegment GetNextPacket() + { + ArraySegment packet = reader.ReadBytesAndSizeSegment(); + packetCount--; + + return packet; + } + + public void ResetBuffer() + { + writer.SetLength(0); + reader.Position = 0; + } + } + // a localClient's connection TO a server. // send messages on this connection causes the server's handler function to be invoked directly. internal class ULocalConnectionToServer : NetworkConnectionToServer { internal ULocalConnectionToClient connectionToClient; - - // local client in host mode might call Cmds/Rpcs during Update, but we - // want to apply them in LateUpdate like all other Transport messages - // to avoid race conditions. keep packets in Queue until LateUpdate. - internal Queue packetQueue = new Queue(); + internal readonly LocalConnectionBuffer buffer = new LocalConnectionBuffer(); public override string address => "localhost"; @@ -73,13 +101,16 @@ internal override bool Send(ArraySegment segment, int channelId = Channels internal void Update() { // process internal messages so they are applied at the correct time - while (packetQueue.Count > 0) + while (buffer.HasPackets()) { - byte[] packet = packetQueue.Dequeue(); + ArraySegment packet = buffer.GetNextPacket(); + // Treat host player messages exactly like connected client // to avoid deceptive / misleading behavior differences - TransportReceive(new ArraySegment(packet), Channels.DefaultReliable); + TransportReceive(packet, Channels.DefaultReliable); } + + buffer.ResetBuffer(); } /// diff --git a/Assets/Mirror/Tests/Editor/LocalConnectionBufferTest.cs b/Assets/Mirror/Tests/Editor/LocalConnectionBufferTest.cs new file mode 100644 index 000000000..e21ccf6a2 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/LocalConnectionBufferTest.cs @@ -0,0 +1,142 @@ +using System; +using NUnit.Framework; + +namespace Mirror.Tests +{ + public class LocalConnectionBufferTest + { + readonly LocalConnectionBuffer buffer = new LocalConnectionBuffer(); + + [TearDown] + public void TearDown() + { + buffer.ResetBuffer(); + } + + [Test] + public void BufferHasPacketsAfterWriter() + { + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + writer.WriteString("Some Message"); + + buffer.Write(writer.ToArraySegment()); + } + + Assert.IsTrue(buffer.HasPackets()); + } + [Test] + public void BufferHasNoPacketsAfterWriteAndReading() + { + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + writer.WriteString("Some Message"); + + buffer.Write(writer.ToArraySegment()); + } + ArraySegment package = buffer.GetNextPacket(); + + + Assert.IsFalse(buffer.HasPackets()); + } + [Test] + public void BufferCanWriteAndReadPackages() + { + const string expectedMessage = "Some Message"; + const float expectedValue = 46.8f; + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + writer.WriteString(expectedMessage); + writer.WriteSingle(expectedValue); + + buffer.Write(writer.ToArraySegment()); + } + ArraySegment package = buffer.GetNextPacket(); + + string message; + float value; + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(package)) + { + message = reader.ReadString(); + value = reader.ReadSingle(); + } + + Assert.That(message, Is.EqualTo(expectedMessage)); + Assert.That(value, Is.EqualTo(expectedValue)); + } + [Test] + public void BufferReturnsMutliplePacketsInTheOrderTheyWereWriten() + { + const string expectedMessage1 = "first Message"; + const string expectedMessage2 = "second Message"; + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + writer.WriteString(expectedMessage1); + + buffer.Write(writer.ToArraySegment()); + } + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + writer.WriteString(expectedMessage2); + + buffer.Write(writer.ToArraySegment()); + } + + string message1; + string message2; + ArraySegment package1 = buffer.GetNextPacket(); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(package1)) + { + message1 = reader.ReadString(); + } + + Assert.IsTrue(buffer.HasPackets()); + ArraySegment package2 = buffer.GetNextPacket(); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(package2)) + { + message2 = reader.ReadString(); + } + + Assert.That(message1, Is.EqualTo(expectedMessage1)); + Assert.That(message2, Is.EqualTo(expectedMessage2)); + } + [Test] + public void BufferCanWriteReadMorePackageAfterCallingReset() + { + const string expectedMessage = "Some Message"; + const float expectedValue = 46.8f; + + for (int i = 0; i < 5; i++) + { + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + writer.WriteInt32(i); + writer.WriteString(expectedMessage); + writer.WriteSingle(expectedValue); + + buffer.Write(writer.ToArraySegment()); + } + ArraySegment package = buffer.GetNextPacket(); + + int index; + string message; + float value; + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(package)) + { + index = reader.ReadInt32(); + message = reader.ReadString(); + value = reader.ReadSingle(); + } + + Assert.That(index, Is.EqualTo(i)); + Assert.That(message, Is.EqualTo(expectedMessage)); + Assert.That(value, Is.EqualTo(expectedValue)); + + buffer.ResetBuffer(); + } + } + } +} diff --git a/Assets/Mirror/Tests/Editor/LocalConnectionBufferTest.cs.meta b/Assets/Mirror/Tests/Editor/LocalConnectionBufferTest.cs.meta new file mode 100644 index 000000000..1a6ddf912 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/LocalConnectionBufferTest.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e035d8111975b864099f87f21c5518ff +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: