Skip to content

Commit

Permalink
perf: Adding buffer for local connection (#1621)
Browse files Browse the repository at this point in the history
* Using writer for local connection

LocalConnectionBuffer

* removing assert

* fixing error and cleaning up code

* removing old queue

* tests for LocalConnectionBuffer

* removing field

* removing extra lines

* Update LocalConnections.cs

removing empty line

* adding comment for packet count
  • Loading branch information
James-Frowen committed Apr 2, 2020
1 parent 0ee5d52 commit 4d5cee8
Show file tree
Hide file tree
Showing 3 changed files with 198 additions and 14 deletions.
59 changes: 45 additions & 14 deletions Assets/Mirror/Runtime/LocalConnections.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
using UnityEngine;

namespace Mirror
Expand All @@ -18,11 +17,8 @@ public ULocalConnectionToClient() : base(0)

internal override bool Send(ArraySegment<byte> 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;
}

Expand All @@ -44,16 +40,48 @@ public override void Disconnect()
}
}

internal class LocalConnectionBuffer
{
readonly NetworkWriter writer = new NetworkWriter();
readonly NetworkReader reader = new NetworkReader(default(ArraySegment<byte>));
// 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<byte> 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<byte> GetNextPacket()
{
ArraySegment<byte> 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<byte[]> packetQueue = new Queue<byte[]>();
internal readonly LocalConnectionBuffer buffer = new LocalConnectionBuffer();

public override string address => "localhost";

Expand All @@ -73,13 +101,16 @@ internal override bool Send(ArraySegment<byte> 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<byte> packet = buffer.GetNextPacket();

// Treat host player messages exactly like connected client
// to avoid deceptive / misleading behavior differences
TransportReceive(new ArraySegment<byte>(packet), Channels.DefaultReliable);
TransportReceive(packet, Channels.DefaultReliable);
}

buffer.ResetBuffer();
}

/// <summary>
Expand Down
142 changes: 142 additions & 0 deletions Assets/Mirror/Tests/Editor/LocalConnectionBufferTest.cs
Original file line number Diff line number Diff line change
@@ -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<byte> 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<byte> 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<byte> package1 = buffer.GetNextPacket();

using (PooledNetworkReader reader = NetworkReaderPool.GetReader(package1))
{
message1 = reader.ReadString();
}

Assert.IsTrue(buffer.HasPackets());
ArraySegment<byte> 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<byte> 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();
}
}
}
}
11 changes: 11 additions & 0 deletions Assets/Mirror/Tests/Editor/LocalConnectionBufferTest.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 4d5cee8

Please sign in to comment.