diff --git a/CHANGELOG.md b/CHANGELOG.md index 49da07f..6f722af 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,43 @@ # Change log +## [0.3.0-preview.6] - 2020-02-24 +### New features +### Changes +* Pipelines are now registered by calling `NetworkPipelineStageCollection.RegisterPipelineStage` before creating a `NetworkDriver`. The built-in pipelines do not require explicit registration. The interface for implementing pipelines has been changed to support this. +* NetworkDriver is no longer a generic type. You pass it an interface when creating the `NetworkDriver`, which means you can switch between backends without modifying all usage of the driver. There is a new `NetworkDriver.Create` which creates a driver with the default `NetworkInterface`. It is also possible to create a `new NetworkDriver` by passing a `NetworkInterface` instance as the first argument. +* `NetworkDriver.Send` is replaced by `BeginSend` and `EndSend`. This allows us to do less data copying when sending messages. The interface for implementing new netowrk interfaces has been changed to support this. +* `DataStreamReader` and `DataStreamWriter` no longer owns any memory. They are just reading/writing the data of a `NativeArray`. +* `DataStreamWriter` has explicit types for all Write methods. +* `DataStreamReader.Context` has been removed. +* Error handling for `DataStreamWriter` has been improved, on failure it returns false and sets `DataStreamWriter.HasFailedWrites` to true. `DataStreamReader` returns a default value and sets `DataStreamReader.HasFailedReads` to true. `DataStreamReader` will throw an excpetion instead of returning a default value in the editor. +* IPCManager is no longer public, it is still possible to create a `NetworkDriver` with a `IPCNetworkInterface`. +* Added `NetworkDriver.ScheduleFlushSend` which must be called to guarantee that messages are send before next call to `NetworkDriver.ScheduleUpdate`. +* Added `NetworkDriver.LastUpdateTime` to get the update time the `NetworkDriver` used for the most recent update. +* Removed the IPC address family, use a IPv4 localhost address instead. + +### Fixes +* Fixed a memory overflow in the reliability pipeline. +* Made the soaker report locale independent. + +### Upgrade guide +Creation and type of `NetworkDriver` has changed, use `NetworkDriver.Create` or pass an instance of a `NetworkInterface` to the `NetworkDriver` constructor. + +`NetworkDriver.Send` has been replaced by a pair of `NetworkDriver.BeginSend` and `NetworkDriver.EndSend`. Calling `BeginSend` will return a `DataStreamWriter` to which you write the data. The `DataStreamWriter` is then passed to `EndSend`. + +All write calls in `DataStreamWriter` need an explicit type, for example `Write(0)` should be replaced by `WriteInt(0)`. + +`DataStreamWriter` no longer shares current position between copies, if you call a method which writes you must pass it by ref for the modifications to apply. + +`DataStreamWriter` no longer returns a DeferedWriter, you need to take a copy of the writer at the point you want to make modifications and use the copy to overwrite data later. + +`DataStreamWriter` is no longer disposable. If you use the allocating constructor you need to use `Allocator.Temp`, if you pass a `NativeArray` to the constructor the `NativeArray` owns the memory. + +`DataStreamReader.Context` no longer exists, you need to pass the `DataStreamReader` itself by ref if you read in a different function. + +The interface for network pipelines has been changed. + +The interface for network interfaces has been changed. + ## [0.2.3-preview.0] - 2019-12-12 ### New features ### Changes diff --git a/Documentation~/pipelines-usage.md b/Documentation~/pipelines-usage.md index da47c4a..993a671 100644 --- a/Documentation~/pipelines-usage.md +++ b/Documentation~/pipelines-usage.md @@ -3,7 +3,7 @@ Pipelines are a feature which offers layers of functionality on top of the defau ## How it works -The way it works is that you can add any number of pipeline stages to your transport driver. So when you send a packet it will go to the first stage, then the next and so on until it's sent on the wire. On the receiving side the stages are then processed in reverse order, so the packet is correctly "unpacked" by the stages. +The way it works is that you can add any number of pipeline stages to your transport driver. So when you send a packet it will go to the first stage, then the next and so on until it's sent on the wire. On the receiving side the stages are then processed in reverse order, so the packet is correctly "unpacked" by the stages. For example the first stage might compress a packet and a second stage could add a sequence number (just the packets header). When receiving the packet is first passed through the sequence stage and then decompressed. The sequence stage could drop the packet if it's out of order in which case it leaves the pipeline and doesn't continue to the decompression. @@ -19,13 +19,12 @@ When sending packets the pipeline can then be specified as a parameter, so the p ```c# using Unity.Collections; -using Unity.Collections.LowLevel.Unsafe; using Unity.Networking.Transport; using Unity.Networking.Transport.Utilities; -public class Client { - - UdpNetworkDriver m_DriverHandle; +public class Client +{ + NetworkDriver m_DriverHandle; NetworkPipeline m_Pipeline; const int k_PacketSize = 256; @@ -36,17 +35,17 @@ public class Client { public void Configure() { // Driver can be used as normal - m_DriverHandle = new UdpNetworkDriver(new SimulatorUtility.Parameters {MaxPacketSize = k_PacketSize, MaxPacketCount = 30, PacketDelayMs = 100}); + m_DriverHandle = NetworkDriver.Create(new SimulatorUtility.Parameters {MaxPacketSize = k_PacketSize, MaxPacketCount = 30, PacketDelayMs = 100}); // Driver now knows about this pipeline and can explicitly be asked to send packets through it (by default it sends directly) m_Pipeline = m_DriverHandle.CreatePipeline(typeof(UnreliableSequencedPipelineStage), typeof(SimulatorPipelineStage)); } - public unsafe void SendMessage(NativeArray someData) + public void SendMessage(NativeArray someData) { - var writer = new DataStreamWriter(k_PacketSize, Allocator.Persistent); - writer.WriteBytes((byte*)someData.GetUnsafeReadOnlyPtr(), someData.Length); // Send using the pipeline created in Configure() - m_ConnectionToServer.Send(m_DriverHandle, m_Pipeline, writer); + var writer = m_DriverHandle.BeginSend(m_Pipeline, m_ConnectionToServer); + writer.WriteBytes(someData); + m_DriverHandle.EndSend(writer); } } ``` @@ -59,7 +58,7 @@ The simulator pipeline stage could be added on either the client or server to si Nothing needs to be done after configuring the pipline, it can be set up like this when the driver is created: ```c# -m_DriverHandle = new UdpNetworkDriver(new SimulatorUtility.Parameters {MaxPacketSize = NetworkParameterConstants.MTU, MaxPacketCount = 30, PacketDelayMs = 25, PacketDropPercentage = 10}); +m_DriverHandle = NetworkDriver.Create(new SimulatorUtility.Parameters {MaxPacketSize = NetworkParameterConstants.MTU, MaxPacketCount = 30, PacketDelayMs = 25, PacketDropPercentage = 10}); m_Pipeline = m_DriverHandle.CreatePipeline(typeof(SimulatorPipelineStage)); ``` @@ -72,10 +71,8 @@ To get information about internal state in the simulator you can check the Simul ```c# public unsafe void DumpSimulatorStatistics() { - NativeSlice receiveBuffer = default; - NativeSlice sendBuffer = default; - NativeSlice sharedBuffer = default; - driver.GetPipelineBuffers(pipeline, 0, connection[0], ref receiveBuffer, ref sendBuffer, ref sharedBuffer); + var simulatorStageId = NetworkPipelineStageCollection.GetStageId(typeof(SimulatorPipelineStage)); + driver.GetPipelineBuffers(pipeline, simulatorStageId, connection[0], out var receiveBuffer, out var sendBuffer, out var sharedBuffer); var simCtx = (SimulatorUtility.Context*)sharedBuffer.GetUnsafeReadOnlyPtr(); UnityEngine.Debug.Log("Simulator stats\n" + "PacketCount: " + simCtx->PacketCount + "\n" + @@ -109,7 +106,7 @@ The ack packet type is used when a certain amount of time has passed and nothing ### Using the reliability pipeline ```c# -m_ServerDriver = new UdpNetworkDriver(new ReliableUtility.Parameters { WindowSize = 32 }); +m_ServerDriver = NetworkDriver.Create(new ReliableUtility.Parameters { WindowSize = 32 }); m_Pipeline = m_ServerDriver.CreatePipeline(typeof(ReliableSequencedPipelineStage)); ``` This would create a pipeline with just the reliability pipeline stage present, and initialize it to a window size of 32 (so it can keep track of 32 reliable packets at a one time). The maximum value for this is 32. @@ -118,13 +115,12 @@ Because only 32 packets can be tracked at a time there can't be more than 32 pac ```c# // Get a reference to the internal state or shared context of the reliability -NativeSlice tmpReceiveBuffer = default; -NativeSlice tmpSendBuffer = default; -NativeSlice serverReliableBuffer = default; -m_ServerDriver.GetPipelineBuffers(typeof(ReliableSequencedPipelineStage), serverToClient, ref tmpReceiveBuffer, ref tmpSendBuffer, ref serverReliableBuffer); +var reliableStageId = NetworkPipelineStageCollection.GetStageId(typeof(ReliableSequencedPipelineStage)); +m_ServerDriver.GetPipelineBuffers(serverPipe, reliableStageId, serverToClient, out var tmpReceiveBuffer, out var tmpSendBuffer, out var serverReliableBuffer); var serverReliableCtx = (ReliableUtility.SharedContext*) serverReliableBuffer.GetUnsafePtr(); -m_ServerDriver.Send(serverPipe, serverToClient, strm); +var strm = m_ServerDriver.BeginSend(serverPipe, serverToClient); +m_ServerDriver.EndSend(strm); if (serverReliableCtx->errorCode != 0) { // Failed to send with reliability, error code will be ReliableUtility.ErrorCodes.OutgoingQueueIsFull if no buffer space is left to store the packet diff --git a/Documentation~/samples/clientbehaviour.cs.md b/Documentation~/samples/clientbehaviour.cs.md index 8047aad..549a65f 100644 --- a/Documentation~/samples/clientbehaviour.cs.md +++ b/Documentation~/samples/clientbehaviour.cs.md @@ -1,18 +1,17 @@ ```c# using UnityEngine; -using Unity.Collections; using Unity.Networking.Transport; public class ClientBehaviour : MonoBehaviour { - public UdpNetworkDriver m_Driver; + public NetworkDriver m_Driver; public NetworkConnection m_Connection; public bool m_Done; void Start () - { - m_Driver = new UdpNetworkDriver(new INetworkParameter[0]); + { + m_Driver = NetworkDriver.Create(); m_Connection = default(NetworkConnection); var endpoint = NetworkEndPoint.LoopbackIpv4; @@ -39,24 +38,20 @@ public class ClientBehaviour : MonoBehaviour DataStreamReader stream; NetworkEvent.Type cmd; - while ((cmd = m_Connection.PopEvent(m_Driver, out stream)) != - NetworkEvent.Type.Empty) + while ((cmd = m_Connection.PopEvent(m_Driver, out stream)) != NetworkEvent.Type.Empty) { if (cmd == NetworkEvent.Type.Connect) { Debug.Log("We are now connected to the server"); - var value = 1; - using (var writer = new DataStreamWriter(4, Allocator.Temp)) - { - writer.Write(value); - m_Connection.Send(m_Driver, writer); - } + uint value = 1; + var writer = m_Driver.BeginSend(m_Connection); + writer.WriteUInt(value); + m_Driver.EndSend(writer); } else if (cmd == NetworkEvent.Type.Data) { - var readerCtx = default(DataStreamReader.Context); - uint value = stream.ReadUInt(ref readerCtx); + uint value = stream.ReadUInt(); Debug.Log("Got the value = " + value + " back from the server"); m_Done = true; m_Connection.Disconnect(m_Driver); diff --git a/Documentation~/samples/jobifiedclientbehaviour.cs.md b/Documentation~/samples/jobifiedclientbehaviour.cs.md index 5f56775..5f5d7ce 100644 --- a/Documentation~/samples/jobifiedclientbehaviour.cs.md +++ b/Documentation~/samples/jobifiedclientbehaviour.cs.md @@ -7,66 +7,62 @@ using Unity.Networking.Transport; struct ClientUpdateJob : IJob { - public UdpNetworkDriver driver; - public NativeArray connection; - public NativeArray done; + public NetworkDriver driver; + public NativeArray connection; + public NativeArray done; - public void Execute() - { - if (!connection[0].IsCreated) - { - if (done[0] != 1) - Debug.Log("Something went wrong during connect"); - return; - } + public void Execute() + { + if (!connection[0].IsCreated) + { + if (done[0] != 1) + Debug.Log("Something went wrong during connect"); + return; + } - DataStreamReader stream; - NetworkEvent.Type cmd; + DataStreamReader stream; + NetworkEvent.Type cmd; - while ((cmd = connection[0].PopEvent(driver, out stream)) != - NetworkEvent.Type.Empty) - { - if (cmd == NetworkEvent.Type.Connect) - { - Debug.Log("We are now connected to the server"); + while ((cmd = connection[0].PopEvent(driver, out stream)) != NetworkEvent.Type.Empty) + { + if (cmd == NetworkEvent.Type.Connect) + { + Debug.Log("We are now connected to the server"); - var value = 1; - using (var writer = new DataStreamWriter(4, Allocator.Temp)) - { - writer.Write(value); - connection[0].Send(driver, writer); - } - } - else if (cmd == NetworkEvent.Type.Data) - { - var readerCtx = default(DataStreamReader.Context); - uint value = stream.ReadUInt(ref readerCtx); - Debug.Log("Got the value = " + value + " back from the server"); - // And finally change the `done[0]` to `1` - done[0] = 1; - connection[0].Disconnect(driver); - connection[0] = default(NetworkConnection); - } - else if (cmd == NetworkEvent.Type.Disconnect) - { - Debug.Log("Client got disconnected from server"); - connection[0] = default(NetworkConnection); - } - } - } + uint value = 1; + var writer = driver.BeginSend(connection[0]); + writer.WriteUInt(value); + driver.EndSend(writer); + } + else if (cmd == NetworkEvent.Type.Data) + { + uint value = stream.ReadUInt(); + Debug.Log("Got the value = " + value + " back from the server"); + // And finally change the `done[0]` to `1` + done[0] = 1; + connection[0].Disconnect(driver); + connection[0] = default(NetworkConnection); + } + else if (cmd == NetworkEvent.Type.Disconnect) + { + Debug.Log("Client got disconnected from server"); + connection[0] = default(NetworkConnection); + } + } + } } public class JobifiedClientBehaviour : MonoBehaviour { - public UdpNetworkDriver m_Driver; + public NetworkDriver m_Driver; public NativeArray m_Connection; public NativeArray m_Done; public JobHandle ClientJobHandle; void Start () - { - m_Driver = new UdpNetworkDriver(new INetworkParameter[0]); + { + m_Driver = NetworkDriver.Create(); m_Connection = new NativeArray(1, Allocator.Persistent); m_Done = new NativeArray(1, Allocator.Persistent); diff --git a/Documentation~/samples/jobifiedserverbehaviour.cs.md b/Documentation~/samples/jobifiedserverbehaviour.cs.md index 9d82c37..1d0a3c3 100644 --- a/Documentation~/samples/jobifiedserverbehaviour.cs.md +++ b/Documentation~/samples/jobifiedserverbehaviour.cs.md @@ -8,7 +8,7 @@ using Unity.Networking.Transport; struct ServerUpdateConnectionsJob : IJob { - public UdpNetworkDriver driver; + public NetworkDriver driver; public NativeList connections; public void Execute() @@ -34,32 +34,27 @@ struct ServerUpdateConnectionsJob : IJob struct ServerUpdateJob : IJobParallelForDefer { - public UdpNetworkDriver.Concurrent driver; + public NetworkDriver.Concurrent driver; public NativeArray connections; public void Execute(int index) { DataStreamReader stream; - if (!connections[index].IsCreated) - Assert.IsTrue(true); + Assert.IsTrue(connections[index].IsCreated); NetworkEvent.Type cmd; - while ((cmd = driver.PopEventForConnection(connections[index], out stream)) != - NetworkEvent.Type.Empty) + while ((cmd = driver.PopEventForConnection(connections[index], out stream)) != NetworkEvent.Type.Empty) { if (cmd == NetworkEvent.Type.Data) { - var readerCtx = default(DataStreamReader.Context); - uint number = stream.ReadUInt(ref readerCtx); + uint number = stream.ReadUInt(); Debug.Log("Got " + number + " from the Client adding + 2 to it."); number +=2; - using (var writer = new DataStreamWriter(4, Allocator.Temp)) - { - writer.Write(number); - driver.Send(NetworkPipeline.Null, connections[index], writer); - } + var writer = driver.BeginSend(connections[index]); + writer.WriteUInt(number); + driver.EndSend(writer); } else if (cmd == NetworkEvent.Type.Disconnect) { @@ -72,14 +67,14 @@ struct ServerUpdateJob : IJobParallelForDefer public class JobifiedServerBehaviour : MonoBehaviour { - public UdpNetworkDriver m_Driver; + public NetworkDriver m_Driver; public NativeList m_Connections; private JobHandle ServerJobHandle; void Start () - { + { m_Connections = new NativeList(16, Allocator.Persistent); - m_Driver = new UdpNetworkDriver(new INetworkParameter[0]); + m_Driver = NetworkDriver.Create(); var endpoint = NetworkEndPoint.AnyIpv4; endpoint.Port = 9000; if (m_Driver.Bind(endpoint) != 0) @@ -97,7 +92,7 @@ public class JobifiedServerBehaviour : MonoBehaviour } void Update () - { + { ServerJobHandle.Complete(); var connectionJob = new ServerUpdateConnectionsJob diff --git a/Documentation~/samples/serverbehaviour.cs.md b/Documentation~/samples/serverbehaviour.cs.md index 32ce414..67f8bdd 100644 --- a/Documentation~/samples/serverbehaviour.cs.md +++ b/Documentation~/samples/serverbehaviour.cs.md @@ -7,12 +7,12 @@ using Unity.Networking.Transport; public class ServerBehaviour : MonoBehaviour { - public UdpNetworkDriver m_Driver; + public NetworkDriver m_Driver; private NativeList m_Connections; void Start () { - m_Driver = new UdpNetworkDriver(new INetworkParameter[0]); + m_Driver = NetworkDriver.Create(); var endpoint = NetworkEndPoint.AnyIpv4; endpoint.Port = 9000; if (m_Driver.Bind(endpoint) != 0) @@ -30,7 +30,7 @@ public class ServerBehaviour : MonoBehaviour } void Update () - { + { m_Driver.ScheduleUpdate().Complete(); // CleanUpConnections @@ -53,26 +53,21 @@ public class ServerBehaviour : MonoBehaviour DataStreamReader stream; for (int i = 0; i < m_Connections.Length; i++) { - if (!m_Connections[i].IsCreated) - Assert.IsTrue(true); + Assert.IsTrue(m_Connections[i].IsCreated); NetworkEvent.Type cmd; - while ((cmd = m_Driver.PopEventForConnection(m_Connections[i], out stream)) != - NetworkEvent.Type.Empty) + while ((cmd = m_Driver.PopEventForConnection(m_Connections[i], out stream)) != NetworkEvent.Type.Empty) { if (cmd == NetworkEvent.Type.Data) { - var readerCtx = default(DataStreamReader.Context); - uint number = stream.ReadUInt(ref readerCtx); + uint number = stream.ReadUInt(); Debug.Log("Got " + number + " from the Client adding + 2 to it."); number +=2; - using (var writer = new DataStreamWriter(4, Allocator.Temp)) - { - writer.Write(number); - m_Driver.Send(NetworkPipeline.Null, m_Connections[i], writer); - } + var writer = m_Driver.BeginSend(NetworkPipeline.Null, m_Connections[i]); + writer.WriteUInt(number); + m_Driver.EndSend(writer); } else if (cmd == NetworkEvent.Type.Disconnect) { diff --git a/Documentation~/workflow-client-server-jobs.md b/Documentation~/workflow-client-server-jobs.md index 1391e54..e68b337 100644 --- a/Documentation~/workflow-client-server-jobs.md +++ b/Documentation~/workflow-client-server-jobs.md @@ -1,6 +1,6 @@ # Jobyfiying our Example -In the workflow [Creating a minimal client and server](workflow-client-server.md), our client should look like this [code example](samples/clientbehaviour.cs.md). +In the workflow [Creating a minimal client and server](workflow-client-server.md), our client should look like this [code example](samples/clientbehaviour.cs.md). > **Note**: It is recommended, before reading this workflow, to refresh your memory on how the [C# Job System](https://docs.unity3d.com/Manual/JobSystem.html) works. @@ -12,21 +12,21 @@ Start by creating a client job to handle your inputs from the network. As you on ```c# struct ClientUpdateJob: IJob { - public UdpNetworkDriver driver; - public NativeArray connection; - public NativeArray done; - - public void Execute() { ... } + public NetworkDriver driver; + public NativeArray connection; + public NativeArray done; + + public void Execute() { ... } } ``` -> **Note**: The data inside the ClientUpdateJob is **copied**. If you want to use the data after the job is completed, you need to have your data in a shared container, such as a [NativeContainer](https://docs.unity3d.com/Manual/JobSystemNativeContainer.html). +> **Note**: The data inside the ClientUpdateJob is **copied**. If you want to use the data after the job is completed, you need to have your data in a shared container, such as a [NativeContainer](https://docs.unity3d.com/Manual/JobSystemNativeContainer.html). -Since you might want to update the `NetworkConnection` and the `done` variables inside your job (we might receive a disconnect message), you need to make sure you can share the data between the job and the caller. In this case, you can use a [NativeArray](https://docs.unity3d.com/ScriptReference/Unity.Collections.NativeArray_1.html). +Since you might want to update the `NetworkConnection` and the `done` variables inside your job (we might receive a disconnect message), you need to make sure you can share the data between the job and the caller. In this case, you can use a [NativeArray](https://docs.unity3d.com/ScriptReference/Unity.Collections.NativeArray_1.html). > Note: You can only use [blittable types](https://docs.microsoft.com/en-us/dotnet/framework/interop/blittable-and-non-blittable-types) in a `NativeContainer`. In this case, instead of a `bool` you need to use a `byte`, as its a blittable type. -In your `Execute` method, move over your code from the `Update` method that you have already in place from [_ClientBehaviour.cs_](samples/clientbehaviour.cs.md) and you are done. +In your `Execute` method, move over your code from the `Update` method that you have already in place from [_ClientBehaviour.cs_](samples/clientbehaviour.cs.md) and you are done. You need to change any call to `m_Connection` to `connection[0]` to refer to the first element inside your `NativeArray`. The same goes for your `done` variable, you need to call `done[0]` when you refer to the `done` variable. See the code below: @@ -43,24 +43,20 @@ public void Execute() DataStreamReader stream; NetworkEvent.Type cmd; - while ((cmd = connection[0].PopEvent(driver, out stream)) != - NetworkEvent.Type.Empty) + while ((cmd = connection[0].PopEvent(driver, out stream)) != NetworkEvent.Type.Empty) { if (cmd == NetworkEvent.Type.Connect) { Debug.Log("We are now connected to the server"); var value = 1; - using (var writer = new DataStreamWriter(4, Allocator.Temp)) - { - writer.Write(value); - connection[0].Send(driver, writer); - } + var writer = driver.BeginSend(connection[0]); + writer.WriteUInt(value); + driver.EndSend(writer); } else if (cmd == NetworkEvent.Type.Data) { - var readerCtx = default(DataStreamReader.Context); - uint value = stream.ReadUInt(ref readerCtx); + uint value = stream.ReadUInt(); Debug.Log("Got the value = " + value + " back from the server"); // And finally change the `done[0]` to `1` done[0] = 1; @@ -82,12 +78,13 @@ When you have a job, you need to make sure that you can execute the job. To do t ```c# -public class JobifiedClientBehaviour : MonoBehaviour { - public UdpCNetworkDriver m_Driver; +public class JobifiedClientBehaviour : MonoBehaviour +{ + public NetworkDriver m_Driver; public NativeArray m_Connection; public NativeArray m_Done; public JobHandle ClientJobHandle; - + public void OnDestroy() { ... } public void Start() { ... } public void Update() { ... } @@ -100,10 +97,10 @@ Both `m_Done` and `m_Connection` in the code above, have been changed to type `N ```c# void Start () { - m_Driver = new UdpNetworkDriver(new INetworkParameter[0]); + m_Driver = NetworkDriver.Create(); m_Connection = new NativeArray(1, Allocator.Persistent); m_Done = new NativeArray(1, Allocator.Persistent); - + var endpoint = NetworkEndPoint.LoopbackIpv4; endpoint.Port = 9000; @@ -111,7 +108,7 @@ void Start () { } ``` -The `Start` method looks pretty similar to before, the major update here is to make sure you create your `NativeArray`. +The `Start` method looks pretty similar to before, the major update here is to make sure you create your `NativeArray`. #### OnDestroy method @@ -119,7 +116,7 @@ The `Start` method looks pretty similar to before, the major update here is to m public void OnDestroy() { ClientJobHandle.Complete(); - + m_Connection.Dispose(); m_Driver.Dispose(); m_Done.Dispose(); @@ -147,21 +144,21 @@ To chain your job, start by creating a job struct: ```c# var job = new ClientUpdateJob { - driver = m_Driver, - connection = m_Connection, - done = m_Done + driver = m_Driver, + connection = m_Connection, + done = m_Done }; ``` - To schedule the job, you need to pass the `JobHandle` dependency that was returned from the `m_Driver.ScheduleUpdate` call in the `Schedule` function of your `IJob`. Start by invoking the `m_Driver.ScheduleUpdate` without a call to `Complete`, and pass the returning `JobHandle` to your saved `ClientJobHandle`. + To schedule the job, you need to pass the `JobHandle` dependency that was returned from the `m_Driver.ScheduleUpdate` call in the `Schedule` function of your `IJob`. Start by invoking the `m_Driver.ScheduleUpdate` without a call to `Complete`, and pass the returning `JobHandle` to your saved `ClientJobHandle`. ```c# ClientJobHandle = m_Driver.ScheduleUpdate(); ClientJobHandle = job.Schedule(ClientJobHandle); ``` -As you can see in the code above, you pass the returned `ClientJobHandle` to your own job, returning a newly updated `ClientJobHandle`. +As you can see in the code above, you pass the returned `ClientJobHandle` to your own job, returning a newly updated `ClientJobHandle`. You now have a *JobifiedClientBehaviour* that looks like [this](samples/jobifiedclientbehaviour.cs.md). @@ -185,7 +182,7 @@ struct ServerUpdateJob : IJobParallelForDefer } ``` -However, we can’t run all of our code in parallel. +However, we can’t run all of our code in parallel. In the client example above, we started off by cleaning up closed connections and accepting new ones, this can't be done in parallel. You need to create a connection job as well; @@ -194,9 +191,9 @@ Start by creating a `ServerUpdateConnectionJob` job. You know you need to pass b ```c# struct ServerUpdateConnectionsJob : IJob { - public UdpCNetworkDriver driver; + public NetworkDriver driver; public NativeList connections; - + public void Execute() { // Clean up connections @@ -226,51 +223,47 @@ With the `ServerUpdateConnectionsJob` done, lets look at how to implement the `S ```c# struct ServerUpdateJob : IJobParallelForDefer { - public UdpCNetworkDriver.Concurrent driver; + public NetworkDriver.Concurrent driver; public NativeArray connections; public void Execute(int index) { - ... + ... } } ``` -There are **two** major differences here compared with our other `job`. First off we are using the `UdpNetworkDriver.Concurrent` type, this allows you to call the `NetworkDriver` from multiple threads, precisely what you need for the `IParallelForJobDefer`. Secondly, you are now passing a `NativeArray` of type `NetworkConnection` instead of a `NativeList`. The `IParallelForJobDefer` does not accept any other `Unity.Collections` type than a `NativeArray` (more on this later). +There are **two** major differences here compared with our other `job`. First off we are using the `NetworkDriver.Concurrent` type, this allows you to call the `NetworkDriver` from multiple threads, precisely what you need for the `IParallelForJobDefer`. Secondly, you are now passing a `NativeArray` of type `NetworkConnection` instead of a `NativeList`. The `IParallelForJobDefer` does not accept any other `Unity.Collections` type than a `NativeArray` (more on this later). ### Execute method ```c# public void Execute(int index) { - DataStreamReader stream; - if (!connections[index].IsCreated) - Assert.IsTrue(true); - - NetworkEvent.Type cmd; - while ((cmd = driver.PopEventForConnection(connections[index], out stream)) != - NetworkEvent.Type.Empty) - { - if (cmd == NetworkEvent.Type.Data) - { - var readerCtx = default(DataStreamReader.Context); - uint number = stream.ReadUInt(ref readerCtx); - - Debug.Log("Got " + number + " from the Client adding + 2 to it."); - number +=2; - - using (var writer = new DataStreamWriter(4, Allocator.Temp)) - { - writer.Write(number); - driver.Send(connections[index], writer); - } - } - else if (cmd == NetworkEvent.Type.Disconnect) - { - Debug.Log("Client disconnected from server"); - connections[index] = default(NetworkConnection); - } - } + DataStreamReader stream; + Assert.IsTrue(connections[index].IsCreated); + + NetworkEvent.Type cmd; + while ((cmd = driver.PopEventForConnection(connections[index], out stream)) != + NetworkEvent.Type.Empty) + { + if (cmd == NetworkEvent.Type.Data) + { + uint number = stream.ReadUInt(); + + Debug.Log("Got " + number + " from the Client adding + 2 to it."); + number +=2; + + var writer = driver.BeginSend(connections[index]); + writer.WriteUInt(number); + driver.EndSend(writer); + } + else if (cmd == NetworkEvent.Type.Disconnect) + { + Debug.Log("Client disconnected from server"); + connections[index] = default(NetworkConnection); + } + } } ``` @@ -296,21 +289,21 @@ You now have 2 jobs: With this we can now go back to our [MonoBehaviour](https://docs.unity3d.com/ScriptReference/MonoBehaviour.html) and start updating the server. ```c# -public class JobifiedServerBehaviour : MonoBehaviour +public class JobifiedServerBehaviour : MonoBehaviour { - public UdpNetworkDriver m_Driver; + public NetworkDriver m_Driver; public NativeList m_Connections; private JobHandle ServerJobHandle; void Start () { ... } public void OnDestroy() { ... } - + void Update () { ... } } ``` -The only change made in your variable declaration is that you have once again added a `JobHandle` so you can keep track of your ongoing jobs. +The only change made in your variable declaration is that you have once again added a `JobHandle` so you can keep track of your ongoing jobs. #### Start method @@ -320,8 +313,8 @@ You do not need to change your `Start` method as it should look the same: void Start () { m_Connections = new NativeList(16, Allocator.Persistent); - m_Driver = new UdpCNetworkDriver(new INetworkParameter[0]); - + m_Driver = new NetworkDriver.Create(); + var endpoint = NetworkEndPoint.AnyIpv4; endpoint.Port = 9000; if (m_Driver.Bind(endpoint) != 0) @@ -350,38 +343,38 @@ public void OnDestroy() In your `Update` method, call `Complete`on the `JobHandle`. This will force the jobs to complete before we start a new frame: ```c# -void Update () +void Update () { - ServerJobHandle.Complete(); - - var connectionJob = new ServerUpdateConnectionsJob - { - driver = m_Driver, - connections = m_Connections - }; - - var serverUpdateJob = new ServerUpdateJob - { - driver = m_Driver.ToConcurrent(), - connections = m_Connections.ToDeferredJobArray() - }; - + ServerJobHandle.Complete(); + + var connectionJob = new ServerUpdateConnectionsJob + { + driver = m_Driver, + connections = m_Connections + }; + + var serverUpdateJob = new ServerUpdateJob + { + driver = m_Driver.ToConcurrent(), + connections = m_Connections.ToDeferredJobArray() + }; + ServerJobHandle = m_Driver.ScheduleUpdate(); - ServerJobHandle = connectionJob.Schedule(ServerJobHandle); - ServerJobHandle = serverUpdateJob.Schedule(m_Connections, 1, ServerJobHandle); + ServerJobHandle = connectionJob.Schedule(ServerJobHandle); + ServerJobHandle = serverUpdateJob.Schedule(m_Connections, 1, ServerJobHandle); } ``` To chain the jobs, you want to following to happen: -`NetworkDriver.Update` -> `ServerUpdateConnectionsJob` -> `ServerUpdateJob`. +`NetworkDriver.Update` -> `ServerUpdateConnectionsJob` -> `ServerUpdateJob`. Start by populating your `ServerUpdateConnectionsJob`: ```c# var connectionJob = new ServerUpdateConnectionsJob { - driver = m_Driver, - connections = m_Connections + driver = m_Driver, + connections = m_Connections }; ``` @@ -390,12 +383,12 @@ Then create your `ServerUpdateJob`. Remember to use the `ToConcurrent` call on y ```c# var serverUpdateJob = new ServerUpdateJob { - driver = m_Driver.ToConcurrent(), - connections = m_Connections.ToDeferredJobArray() + driver = m_Driver.ToConcurrent(), + connections = m_Connections.ToDeferredJobArray() }; ``` -The final step is to make sure the `NativeArray` is populated to the correct size. This +The final step is to make sure the `NativeArray` is populated to the correct size. This can be done using a `DeferredJobArray`. It makes sure that, when the job is executed, that the connections array is populated with the correct number of items that you have in your list. Since we will run the `ServerUpdateConnectionsJob` first, this might change the **size** of the list. Create your job chain and call `Scheduele` as follows: @@ -412,7 +405,7 @@ In the code above, you have: - Add the `JobHandle` returned as a dependency on the `ServerUpdateConnectionJob`. - The final link in the chain is the `ServerUpdateJob` that needs to run after the `ServerUpdateConnectionsJob`. In this line of code, there is a trick to invoke the `IJobParallelForDeferExtensions`. As you can see, `m_Connections` `NativeList` is passed to the `Schedule` method, this updates the count of connections before starting the job. It's here that it will fan out and run all the `ServerUpdateConnectionJobs` in parallel. -> **Note**: If you are having trouble with the `serverUpdateJob.Schedule(m_Connections, 1, ServerJobHandle);` call, you might need to add `"com.unity.jobs": "0.0.7-preview.5"` to your `manifest.json` file, inside the _/Packages_ folder. +> **Note**: If you are having trouble with the `serverUpdateJob.Schedule(m_Connections, 1, ServerJobHandle);` call, you might need to add `"com.unity.jobs": "0.0.7-preview.5"` to your `manifest.json` file, inside the _/Packages_ folder. diff --git a/Documentation~/workflow-client-server.md b/Documentation~/workflow-client-server.md index 8d1b897..a3d8131 100644 --- a/Documentation~/workflow-client-server.md +++ b/Documentation~/workflow-client-server.md @@ -22,7 +22,7 @@ This workflow helps you create a sample project that highlights how to use the ` The goal is to make a remote `add` function. The flow will be: a client connects to the server, and sends a number, this number is then received by the server that adds another number to it and sends it back to the client. The client, upon receiving the number, disconnects and quits. -Using the `INetworkDriver` to write client and server code is pretty similar between clients and servers, there are a few subtle differences that you can see demonstrated below. +Using the `NetworkDriver` to write client and server code is pretty similar between clients and servers, there are a few subtle differences that you can see demonstrated below. ## Creating a Server @@ -83,7 +83,7 @@ using ... public class ServerBehaviour : MonoBehaviour { - public UdpNetworkDriver m_Driver; + public NetworkDriver m_Driver; private NativeList m_Connections; void Start () { @@ -100,11 +100,11 @@ public class ServerBehaviour : MonoBehaviour { #### Code walkthrough ``` -public UdpNetworkDriver m_Driver; +public NetworkDriver m_Driver; private NativeList m_Connections; ``` -You need to declare a `INetworkDriver`, in this case you can use the UdpNetworkDriver. You also need to create a [NativeList](http://native-list-info) to hold our connections. +You need to declare a `NetworkDriver`. You also need to create a [NativeList](http://native-list-info) to hold our connections. ### Start method @@ -113,7 +113,7 @@ You need to declare a `INetworkDriver`, in this case you can use the UdpNetworkD ```c# void Start () { - m_Driver = new UdpNetworkDriver(new INetworkParameter[0]); + m_Driver = NetworkDriver.Create(); var endpoint = NetworkEndPoint.AnyIpv4; endpoint.Port = 9000; if (m_Driver.Bind(endpoint) != 0) @@ -127,7 +127,7 @@ You need to declare a `INetworkDriver`, in this case you can use the UdpNetworkD #### Code walkthrough -The first line of code, `m_Driver = new UdpNetworkDriver(new INetworkParameter[0]);` , just makes sure you are creating your driver without any parameters. +The first line of code, `m_Driver = NetworkDriver.Create();` , just makes sure you are creating your driver without any parameters. ```c# if (m_Driver.Bind(endpoint) != 0) @@ -146,7 +146,7 @@ Finally we create a `NativeList` to hold all the connections. ### OnDestroy method -Both `UdpNetworkDriver` and `NativeList` allocate unmanaged memory and need to be disposed. To make sure this happens we can simply call the `Dispose` method when we are done with both of them. +Both `NetworkDriver` and `NativeList` allocate unmanaged memory and need to be disposed. To make sure this happens we can simply call the `Dispose` method when we are done with both of them. Add the following code to the `OnDestroy` method on your [MonoBehaviour](https://docs.unity3d.com/ScriptReference/MonoBehaviour.html): @@ -163,7 +163,7 @@ Add the following code to the `OnDestroy` method on your [MonoBehaviour](https:/ ### Server Update loop -As the `unity.networking.transport` package uses the [Unity C# Job System](https://docs.unity3d.com/Manual/JobSystem.html) internally, the `m_Driver` has a `ScheduleUpdate` method call. Inside our `Update` loop you need to make sure to call the `Complete` method on the [JobHandle](https://docs.unity3d.com/Manual/JobSystemJobDependencies.html) that is returned, in order to know when you are ready to process any updates. +As the `com.unity.transport` package uses the [Unity C# Job System](https://docs.unity3d.com/Manual/JobSystem.html) internally, the `m_Driver` has a `ScheduleUpdate` method call. Inside our `Update` loop you need to make sure to call the `Complete` method on the [JobHandle](https://docs.unity3d.com/Manual/JobSystemJobDependencies.html) that is returned, in order to know when you are ready to process any updates. ```c# void Update () { @@ -215,8 +215,7 @@ For each connection we want to call `PopEventForConnection` while there are more ```c# NetworkEvent.Type cmd; - while ((cmd = m_Driver.PopEventForConnection(m_Connections[i], out stream)) != - NetworkEvent.Type.Empty) + while ((cmd = m_Driver.PopEventForConnection(m_Connections[i], out stream)) != NetworkEvent.Type.Empty) { ``` @@ -227,34 +226,29 @@ We are now ready to process events. Lets start with the `Data` event. ```c# if (cmd == NetworkEvent.Type.Data) { - var readerCtx = default(DataStreamReader.Context); ``` -Inside this block, you start by defining a `readerCtx`, this is a `DataStreamReader.Context` type. This type can be seen as a set of indices into a `DataStreamReader`, to help with knowing where in the stream you are, and how much you have read. - -Next, we use our context and try to read a `uint` from the stream and output what we have received: +Next, we try to read a `uint` from the stream and output what we have received: ```c# - uint number = stream.ReadUInt(ref readerCtx); + uint number = stream.ReadUInt(); Debug.Log("Got " + number + " from the Client adding + 2 to it."); ``` -When this is done we simply add two to the number we received and send it back. To send anything with the `INetworkDriver` we need a instance of a `DataStreamWriter`. A `DataStreamWriter` is a new collection that comes with the `unity.networking.transport` package. It's also a type that needs to be disposed. In this workflow, the `using` statement makes sure that you clean up after yourself. +When this is done we simply add two to the number we received and send it back. To send anything with the `NetworkDriver` we need a instance of a `DataStreamWriter`. A `DataStreamWriter` is a new type that comes with the `com.unity.transport` package. You get a `DataStreamWriter` when you start sending a message by calling `BeginSend`. -After you have written your updated number to your stream, you call the `Send` method on the driver and off it goes: +After you have written your updated number to your stream, you call the `EndSend` method on the driver and off it goes: ```c# number +=2; - using (var writer = new DataStreamWriter(4, Allocator.Temp)) - { - writer.Write(number); - m_Driver.Send(NetworkPipeline.Null, m_Connections[i], writer); - } + var writer = m_Driver.BeginSend(NetworkPipeline.Null, m_Connections[i]); + writer.WriteUInt(number); + m_Driver.EndSend(writer); } ``` -> One thing to note here is that we are `NetworkPipeline.Null`, to the `Send` function. This way we say to the driver to use the unreliable pipeline to send our data. +> One thing to note here is that we are `NetworkPipeline.Null`, to the `BeginSend` function. This way we say to the driver to use the unreliable pipeline to send our data. It is also possible to not specify a pipeline. Finally, you need to handle the disconnect case. This is pretty straight forward, if you receive a disconnect message you need to reset that connection to a `default(NetworkConnection)`. As you might remember, the next time the `Update` loop runs you will clean up after yourself. @@ -278,7 +272,7 @@ The client code looks pretty similar to the server code at first glance, but the ### ClientBehaviour.cs -You still define a `UdpNetworkDriver` but instead of having a list of connections we now only have one. There is a `Done` flag to indicate when we are done, or in case you have issues with a connection, you can exit quick. +You still define a `NetworkDriver` but instead of having a list of connections we now only have one. There is a `Done` flag to indicate when we are done, or in case you have issues with a connection, you can exit quick. **Filename**: [_Assets\Scripts\ClientBehaviour.cs_](samples/clientbehaviour.cs.md) @@ -287,7 +281,7 @@ using ... public class ClientBehaviour : MonoBehaviour { - public UdpNetworkDriver m_Driver; + public NetworkDriver m_Driver; public NetworkConnection m_Connection; public bool Done; @@ -302,7 +296,7 @@ public class ClientBehaviour : MonoBehaviour { Start by creating a driver for the client and an address for the server. ```c# void Start () { - m_Driver = new UdpNetworkDriver(new INetworkParameter[0]); + m_Driver = NetworkDriver.Create(); m_Connection = default(NetworkConnection); var endpoint = NetworkEndPoint.LoopbackIpv4; @@ -343,30 +337,27 @@ You should recognize the code below, but if you look closely you can see that th ```c# DataStreamReader stream; NetworkEvent.Type cmd; - while ((cmd = m_Connection.PopEvent(m_Driver, out stream)) != - NetworkEvent.Type.Empty) + while ((cmd = m_Connection.PopEvent(m_Driver, out stream)) != NetworkEvent.Type.Empty) { ``` Now you encounter a new event you have not seen yet: a `NetworkEvent.Type.Connect` event. This event tells you that you have received a `ConnectionAccept` message and you are now connected to the remote peer. -> **Note**: In this case, the server that is listening on port `9000` on `IPAddress.Loopback` is more commonly known as `127.0.0.1`. +> **Note**: In this case, the server that is listening on port `9000` on `NetworkEndPoint.LoopbackIpv4` is more commonly known as `127.0.0.1`. ``` if (cmd == NetworkEvent.Type.Connect) { Debug.Log("We are now connected to the server"); - var value = 1; - using (var writer = new DataStreamWriter(4, Allocator.Temp)) - { - writer.Write(value); - m_Connection.Send(m_Driver, writer); - } + uint value = 1; + var writer = m_Driver.BeginSend(m_Connection); + writer.WriteUInt(value); + m_Driver.EndSend(writer); } ``` -When you establish a connection between the client and the server, you send a number (that you want the server to increment by two). The use of the `using` pattern together with the `DataStreamWriter`, where we set `value` to one, write it into the stream, and finally send it out on the network. +When you establish a connection between the client and the server, you send a number (that you want the server to increment by two). The use of the `BeginSend` / `EndSend` pattern together with the `DataStreamWriter`, where we set `value` to one, write it into the stream, and finally send it out on the network. When the `NetworkEvent` type is `Data`, as below, you read the `value` back that you received from the server and then call the `Disconnect` method. @@ -375,8 +366,7 @@ When the `NetworkEvent` type is `Data`, as below, you read the `value` back that ```c# else if (cmd == NetworkEvent.Type.Data) { - var readerCtx = default(DataStreamReader.Context); - uint value = stream.ReadUInt(ref readerCtx); + uint value = stream.ReadUInt(); Debug.Log("Got the value = " + value + " back from the server"); Done = true; m_Connection.Disconnect(m_Driver); diff --git a/Editor.meta b/Editor.meta deleted file mode 100644 index a4b0631..0000000 --- a/Editor.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: 234e7fea53ca344ec8293a8a3519d857 -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Editor/PipelineCollectionGeneratorWindow.cs b/Editor/PipelineCollectionGeneratorWindow.cs deleted file mode 100644 index 8faf5f3..0000000 --- a/Editor/PipelineCollectionGeneratorWindow.cs +++ /dev/null @@ -1,200 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Reflection; -using Unity.Networking.Transport; -using UnityEditor; -using UnityEngine; - -public class PipelineCollectionGeneratorWindow : EditorWindow -{ - [MenuItem("Multiplayer/CodeGen/PipelineCollection Generator")] - public static void ShowWindow() - { - GetWindow(false, "PipelineCollection Generator", true); - } - - class PipelineStage - { - public Type type; - public bool generate; - } - private List m_PipelineTypes; - public PipelineCollectionGeneratorWindow() - { - m_PipelineTypes = new List(); - } - - private void OnGUI() - { - if (GUILayout.Button("Scan for Pipelines")) - { - FindAllPipelines(); - } - - for (int i = 0; i < m_PipelineTypes.Count; ++i) - { - m_PipelineTypes[i].generate = GUILayout.Toggle(m_PipelineTypes[i].generate, m_PipelineTypes[i].type.Name); - } - - if (GUILayout.Button("Generate Collection")) - { - var dstFile = EditorUtility.SaveFilePanel("Select file to save", "", "PipelineStageCollection", "cs"); - - var content = @"using System; -using Unity.Collections; -using Unity.Networking.Transport; -public struct PipelineStageCollection : INetworkPipelineStageCollection -{ -"; - for (int i = 0; i < m_PipelineTypes.Count; ++i) - { - if (m_PipelineTypes[i].generate) - content += " private " + m_PipelineTypes[i].type.Name + " m_" + m_PipelineTypes[i].type.Name + ";\n"; - } - - content += " public int GetStageId(Type type)\n {\n"; - int stageIdx = 0; - for (int i = 0; i < m_PipelineTypes.Count; ++i) - { - if (m_PipelineTypes[i].generate) - { - content += " if (type == typeof(" + m_PipelineTypes[i].type.Name + "))\n return " + - stageIdx + ";\n"; - ++stageIdx; - } - } - - content += " return -1;\n }\n"; - - content += @" public void Initialize(params INetworkParameter[] param) - { - for (int i = 0; i < param.Length; ++i) - { -"; - for (int i = 0; i < m_PipelineTypes.Count; ++i) - { - if (m_PipelineTypes[i].generate) - { - var inits = m_PipelineTypes[i].type.GetCustomAttributes(typeof(NetworkPipelineInitilizeAttribute), true); - foreach (var init in inits) - { - var pipelineInit = init as NetworkPipelineInitilizeAttribute; - content += " if (param[i] is " + pipelineInit.ParameterType.FullName.Replace("+", ".") + ")\n"; - content += " m_" + m_PipelineTypes[i].type.Name + ".Initialize((" + pipelineInit.ParameterType.FullName.Replace("+", ".") + ")param[i]);\n"; - } - } - } - content += " }\n }\n"; - - content += GenerateVoidInvoke( - "void InvokeInitialize(int pipelineStageId, NativeSlice sendProcessBuffer, NativeSlice recvProcessBuffer, NativeSlice sharedStateBuffer)", - "InitializeConnection(sendProcessBuffer, recvProcessBuffer, sharedStateBuffer)"); - - content += GenerateInvoke( - "InboundBufferVec InvokeSend(int pipelineStageId, NetworkPipelineContext ctx, InboundBufferVec inboundBuffer, ref bool needsResume, ref bool needsUpdate)", - "Send(ctx, inboundBuffer, ref needsResume, ref needsUpdate)", - "inboundBuffer"); - - content += GenerateInvoke( - "NativeSlice InvokeReceive(int pipelineStageId, NetworkPipelineContext ctx, NativeSlice inboundBuffer, ref bool needsResume, ref bool needsUpdate, ref bool needsSendUpdate)", - "Receive(ctx, inboundBuffer, ref needsResume, ref needsUpdate, ref needsSendUpdate)", - "inboundBuffer"); - - content += GenerateInvoke( - "int GetReceiveCapacity(int pipelineStageId)", - "ReceiveCapacity", - "0"); - - content += GenerateInvoke( - "int GetSendCapacity(int pipelineStageId)", - "SendCapacity", - "0"); - - content += GenerateInvoke( - "int GetHeaderCapacity(int pipelineStageId)", - "HeaderCapacity", - "0"); - - content += GenerateInvoke( - "int GetSharedStateCapacity(int pipelineStageId)", - "SharedStateCapacity", - "0"); - - content += "}\n"; - File.WriteAllText(dstFile, content); - AssetDatabase.Refresh(); - } - } - - string GenerateInvoke(string function, string perTypeInvoke, string fallback) - { - var content = " public " + function + "\n {\n"; - content += " switch (pipelineStageId)\n {\n"; - var stageIdx = 0; - for (int i = 0; i < m_PipelineTypes.Count(); ++i) - { - if (m_PipelineTypes[i].generate) - { - content += " case " + stageIdx + ":\n"; - ++stageIdx; - content += " return m_" + m_PipelineTypes[i].type.Name + "." + perTypeInvoke + ";\n"; - } - } - - content += " }\n return " + fallback + ";\n"; - content += " }\n"; - return content; - } - string GenerateVoidInvoke(string function, string perTypeInvoke) - { - var content = " public " + function + "\n {\n"; - content += " switch (pipelineStageId)\n {\n"; - var stageIdx = 0; - for (int i = 0; i < m_PipelineTypes.Count(); ++i) - { - if (m_PipelineTypes[i].generate) - { - content += " case " + stageIdx + ":\n"; - ++stageIdx; - content += " m_" + m_PipelineTypes[i].type.Name + "." + perTypeInvoke + ";\n break;\n"; - } - } - - content += " }\n }\n"; - return content; - } - - void FindAllPipelines() - { - m_PipelineTypes.Clear(); - foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies()) - { - IEnumerable allTypes; - - try - { - allTypes = assembly.GetTypes(); - - } - catch (ReflectionTypeLoadException e) - { - allTypes = e.Types.Where(t => t != null); - Debug.LogWarning( - $"PipelineCollectionGenerator failed loading assembly: {(assembly.IsDynamic ? assembly.ToString() : assembly.Location)}"); - } - - var pipelineTypes = allTypes.Where(t => - typeof(INetworkPipelineStage).IsAssignableFrom(t) && - !t.IsAbstract && - !t.ContainsGenericParameters); - - foreach (var pt in pipelineTypes) - { - m_PipelineTypes.Add(new PipelineStage {type = pt, generate = !pt.Name.StartsWith("Test")}); - } - } - } -} - diff --git a/Editor/Unity.Networking.Transport.Editor.asmdef b/Editor/Unity.Networking.Transport.Editor.asmdef deleted file mode 100644 index fe00091..0000000 --- a/Editor/Unity.Networking.Transport.Editor.asmdef +++ /dev/null @@ -1,9 +0,0 @@ -{ - "name": "Unity.Networking.Transport.Editor", - "references": [ - "Unity.Networking.Transport" - ], - "includePlatforms": [ "Editor" ], - "excludePlatforms": [], - "allowUnsafeCode": true -} diff --git a/Editor/Unity.Networking.Transport.Editor.asmdef.meta b/Editor/Unity.Networking.Transport.Editor.asmdef.meta deleted file mode 100644 index 59be343..0000000 --- a/Editor/Unity.Networking.Transport.Editor.asmdef.meta +++ /dev/null @@ -1,7 +0,0 @@ -fileFormatVersion: 2 -guid: 80733628b6e334c54b3e794956347a9b -AssemblyDefinitionImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Runtime/AssemblyInfo.cs b/Runtime/AssemblyInfo.cs new file mode 100644 index 0000000..de63956 --- /dev/null +++ b/Runtime/AssemblyInfo.cs @@ -0,0 +1,4 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Unity.Networking.Transport.EditorTests")] + diff --git a/Runtime/IPCNetworkDriver.cs.meta b/Runtime/AssemblyInfo.cs.meta similarity index 83% rename from Runtime/IPCNetworkDriver.cs.meta rename to Runtime/AssemblyInfo.cs.meta index 4daeb48..194d8b0 100644 --- a/Runtime/IPCNetworkDriver.cs.meta +++ b/Runtime/AssemblyInfo.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 3612fb161be744a4090a6c9f588ef0cf +guid: 4231ec648d4054f72a4a9cf3adb42c64 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/Runtime/AtomicFreeList.cs b/Runtime/AtomicFreeList.cs new file mode 100644 index 0000000..f22479d --- /dev/null +++ b/Runtime/AtomicFreeList.cs @@ -0,0 +1,74 @@ +using System; +using System.Threading; +using Unity.Collections; +using Unity.Collections.LowLevel.Unsafe; + +namespace Unity.Networking.Transport.Utilities.LowLevel.Unsafe +{ + internal unsafe struct UnsafeAtomicFreeList : IDisposable + { + // used count + // free list size + // free indices... + [NativeDisableUnsafePtrRestriction] + private int* m_Buffer; + private int m_Length; + private Allocator m_Allocator; + + public int Capacity => m_Length; + public int InUse => m_Buffer[0] - m_Buffer[1]; + + public bool IsCreated => m_Buffer != null; + + public UnsafeAtomicFreeList(int capacity, Allocator allocator) + { + m_Allocator = allocator; + m_Length = capacity; + var size = UnsafeUtility.SizeOf() * (capacity + 2); + m_Buffer = (int*)UnsafeUtility.Malloc(size, UnsafeUtility.AlignOf(), allocator); + UnsafeUtility.MemClear(m_Buffer, size); + } + + public void Dispose() + { + UnsafeUtility.Free(m_Buffer, m_Allocator); + } + + public unsafe void Push(int item) + { + int* buffer = m_Buffer; + int idx = Interlocked.Increment(ref buffer[1]) - 1; + while (Interlocked.CompareExchange(ref buffer[idx + 2], item + 1, 0) != 0) + { + } + } + + public unsafe int Pop() + { + int* buffer = m_Buffer; + int idx = buffer[1] - 1; + while (idx >= 0 && Interlocked.CompareExchange(ref buffer[1], idx, idx + 1) != idx + 1) + idx = buffer[1] - 1; + + if (idx >= 0) + { + int val = 0; + while (val == 0) + { + val = Interlocked.Exchange(ref buffer[2 + idx], 0); + } + + return val - 1; + } + + idx = Interlocked.Increment(ref buffer[0]) - 1; + if (idx >= Capacity) + { + Interlocked.Decrement(ref buffer[0]); + return -1; + } + + return idx; + } + } +} \ No newline at end of file diff --git a/Runtime/UDPNetworkDriver.cs.meta b/Runtime/AtomicFreeList.cs.meta similarity index 83% rename from Runtime/UDPNetworkDriver.cs.meta rename to Runtime/AtomicFreeList.cs.meta index 8b49cfb..277961c 100644 --- a/Runtime/UDPNetworkDriver.cs.meta +++ b/Runtime/AtomicFreeList.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: f1d3e8318e4ee8b468111a082325c13b +guid: babb432cd74d9eb4e8bf3a339ca4418b MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/Runtime/BaselibNetworkArray.cs b/Runtime/BaselibNetworkArray.cs new file mode 100644 index 0000000..c32dd7d --- /dev/null +++ b/Runtime/BaselibNetworkArray.cs @@ -0,0 +1,85 @@ +#if UNITY_2020_1_OR_NEWER +#define UNITY_TRANSPORT_ENABLE_BASELIB +#endif +#if UNITY_TRANSPORT_ENABLE_BASELIB +using System; +using Unity.Baselib; +using Unity.Baselib.LowLevel; +using Unity.Mathematics; +using Unity.Collections; +using Unity.Collections.LowLevel.Unsafe; +using ErrorCode = Unity.Baselib.LowLevel.Binding.Baselib_ErrorCode; + +namespace Unity.Networking.Transport +{ + using size_t = UIntPtr; + + internal unsafe struct UnsafeBaselibNetworkArray : IDisposable + { + [NativeDisableUnsafePtrRestriction] Binding.Baselib_RegisteredNetwork_Buffer* m_Buffer; + + public UnsafeBaselibNetworkArray(int capacity) + { + var totalSize = (long)capacity; +#if ENABLE_UNITY_COLLECTIONS_CHECKS + if (capacity < 0) + throw new ArgumentOutOfRangeException(nameof(capacity), "Capacity must be >= 0"); + + if (totalSize > int.MaxValue) + throw new ArgumentOutOfRangeException(nameof(capacity), $"Capacity * sizeof(T) cannot exceed {int.MaxValue} bytes"); +#endif + var pageInfo = stackalloc Binding.Baselib_Memory_PageSizeInfo[1]; + Binding.Baselib_Memory_GetPageSizeInfo(pageInfo); + var defaultPageSize = (ulong)pageInfo->defaultPageSize; + + var pageCount = (ulong)1; + if ((ulong) totalSize > defaultPageSize) + { + pageCount = (ulong)math.ceil(totalSize / (double) defaultPageSize); + } + + var error = default(ErrorState); + var pageAllocation = Binding.Baselib_Memory_AllocatePages( + pageInfo->defaultPageSize, + pageCount, + 1, + Binding.Baselib_Memory_PageState.ReadWrite, + error.NativeErrorStatePtr); + + if (error.ErrorCode != ErrorCode.Success) + throw new Exception(); + + UnsafeUtility.MemSet((void*)pageAllocation.ptr, 0, (long)(pageAllocation.pageCount * pageAllocation.pageSize)); + + m_Buffer = (Binding.Baselib_RegisteredNetwork_Buffer*)UnsafeUtility.Malloc(UnsafeUtility.SizeOf(), UnsafeUtility.AlignOf(), Allocator.Persistent); + *m_Buffer = Binding.Baselib_RegisteredNetwork_Buffer_Register(pageAllocation, error.NativeErrorStatePtr); + if (error.ErrorCode != (int) ErrorCode.Success) + { + Binding.Baselib_Memory_ReleasePages(pageAllocation, error.NativeErrorStatePtr); + *m_Buffer = default; + throw new Exception(); + } + } + + public void Dispose() + { + var error = default(ErrorState); + var pageAllocation = m_Buffer->allocation; + Binding.Baselib_RegisteredNetwork_Buffer_Deregister(*m_Buffer); + Binding.Baselib_Memory_ReleasePages(pageAllocation, error.NativeErrorStatePtr); + UnsafeUtility.Free(m_Buffer, Allocator.Persistent); + } + + public Binding.Baselib_RegisteredNetwork_BufferSlice AtIndexAsSlice(int index, uint elementSize) + { + var offset = elementSize * (uint)index; + Binding.Baselib_RegisteredNetwork_BufferSlice slice; + slice.id = m_Buffer->id; + slice.data = (IntPtr)((byte*) m_Buffer->allocation.ptr + offset); + slice.offset = offset; + slice.size = elementSize; + return slice; + } + } +} +#endif diff --git a/Editor/PipelineCollectionGeneratorWindow.cs.meta b/Runtime/BaselibNetworkArray.cs.meta similarity index 83% rename from Editor/PipelineCollectionGeneratorWindow.cs.meta rename to Runtime/BaselibNetworkArray.cs.meta index a4dd316..3b6128b 100644 --- a/Editor/PipelineCollectionGeneratorWindow.cs.meta +++ b/Runtime/BaselibNetworkArray.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 4bd04effab94b4a288837c0ae50a3f23 +guid: 699137ce9825846c981a2c997973c26a MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/Runtime/BaselibNetworkInterface.cs b/Runtime/BaselibNetworkInterface.cs new file mode 100644 index 0000000..ee22a4b --- /dev/null +++ b/Runtime/BaselibNetworkInterface.cs @@ -0,0 +1,514 @@ +#if UNITY_2020_1_OR_NEWER +#define UNITY_TRANSPORT_ENABLE_BASELIB +#endif +#if UNITY_TRANSPORT_ENABLE_BASELIB +using System; +using System.Collections.Generic; +using Unity.Baselib; +using Unity.Baselib.LowLevel; +using Unity.Burst; +using Unity.Collections; +using Unity.Collections.LowLevel.Unsafe; +using Unity.Jobs; +using Unity.Networking.Transport.Utilities.LowLevel.Unsafe; +using Unity.Networking.Transport.Protocols; +using ErrorCode = Unity.Baselib.LowLevel.Binding.Baselib_ErrorCode; + +namespace Unity.Networking.Transport +{ + using NetworkRequest = Binding.Baselib_RegisteredNetwork_Request; + using NetworkEndpoint = Binding.Baselib_RegisteredNetwork_Endpoint; + using NetworkSocket = Binding.Baselib_RegisteredNetwork_Socket_UDP; + + public struct BaselibNetworkParameter : INetworkParameter + { + public int receiveQueueCapacity; + public int sendQueueCapacity; + public uint maximumPayloadSize; + } + + [BurstCompile] + public struct BaselibNetworkInterface : INetworkInterface + { +#if ENABLE_UNITY_COLLECTIONS_CHECKS + private class SocketList + { + public struct SocketId + { + public NetworkSocket socket; + } + public HashSet OpenSockets = new HashSet(); + + ~SocketList() + { + foreach (var socket in OpenSockets) + { + Binding.Baselib_RegisteredNetwork_Socket_UDP_Close(socket.socket); + } + } + } + private static SocketList AllSockets = new SocketList(); +#endif + struct Payloads : IDisposable + { + public UnsafeAtomicFreeList m_Handles; + public UnsafeBaselibNetworkArray m_PayloadArray; + public UnsafeBaselibNetworkArray m_EndpointArray; + private uint m_PayloadSize; + + public int InUse => m_Handles.InUse; + public int Capacity => m_Handles.Capacity; + + public Payloads(int capacity, uint maxPayloadSize) + { + m_PayloadSize = maxPayloadSize; + m_Handles = new UnsafeAtomicFreeList(capacity, Allocator.Persistent); + m_PayloadArray = new UnsafeBaselibNetworkArray(capacity * (int)maxPayloadSize); + m_EndpointArray = new UnsafeBaselibNetworkArray(capacity * (int)Binding.Baselib_RegisteredNetwork_Endpoint_MaxSize); + } + + public bool IsCreated => m_Handles.IsCreated; + public void Dispose() + { + m_Handles.Dispose(); + m_PayloadArray.Dispose(); + m_EndpointArray.Dispose(); + } + + public NetworkRequest GetRequestFromHandle(int handle) + { + return new NetworkRequest {payload = m_PayloadArray.AtIndexAsSlice(handle, m_PayloadSize), + remoteEndpoint = new NetworkEndpoint{slice = m_EndpointArray.AtIndexAsSlice(handle, (uint)Binding.Baselib_RegisteredNetwork_Endpoint_MaxSize)}}; + } + + public int AcquireHandle() + { + return m_Handles.Pop(); + } + + public void ReleaseHandle(int handle) + { + m_Handles.Push(handle); + } + } + + private int rxQueueSize; + private int txQueueSize; + private uint maximumPayloadSize; + + private const int k_defaultRxQueueSize = 64; + private const int k_defaultTxQueueSize = 64; + + unsafe struct BaselibData + { + public NetworkSocket m_Socket; + public Payloads m_PayloadsTx; + } + + [ReadOnly] + private NativeArray m_Baselib; + + [NativeDisableContainerSafetyRestriction] + private Payloads m_PayloadsRx; + [NativeDisableContainerSafetyRestriction] + private Payloads m_PayloadsTx; + + private UnsafeBaselibNetworkArray m_LocalAndTempEndpoint; + + public unsafe NetworkInterfaceEndPoint LocalEndPoint + { + get + { + var error = default(ErrorState); + Binding.Baselib_NetworkAddress local; + Binding.Baselib_RegisteredNetwork_Socket_UDP_GetNetworkAddress(m_Baselib[0].m_Socket, &local, error.NativeErrorStatePtr); + var ep = default(NetworkInterfaceEndPoint); + if (error.ErrorCode != ErrorCode.Success) + return ep; + ep.dataLength = UnsafeUtility.SizeOf(); + UnsafeUtility.MemCpy(ep.data, &local, ep.dataLength); + return ep; + } + } + + public bool IsCreated => m_Baselib.IsCreated; + + public unsafe NetworkInterfaceEndPoint CreateInterfaceEndPoint(NetworkEndPoint address) + { + var slice = m_LocalAndTempEndpoint.AtIndexAsSlice(0, (uint)Binding.Baselib_RegisteredNetwork_Endpoint_MaxSize); + var error = default(ErrorState); + NetworkEndpoint local; + + local = Binding.Baselib_RegisteredNetwork_Endpoint_Create( + (Binding.Baselib_NetworkAddress*)&address.rawNetworkAddress, + slice, + error.NativeErrorStatePtr + ); + if (error.ErrorCode != ErrorCode.Success) + return default(NetworkInterfaceEndPoint); + + var endpoint = default(NetworkInterfaceEndPoint); + endpoint.dataLength = (int)local.slice.size; + UnsafeUtility.MemCpy(endpoint.data, (void*)local.slice.data, endpoint.dataLength); + return endpoint; + } + + public unsafe NetworkEndPoint GetGenericEndPoint(NetworkInterfaceEndPoint endpoint) + { + // Set to a valid address so length is set correctly + var address = NetworkEndPoint.LoopbackIpv4; + UnsafeUtility.MemCpy(&address.rawNetworkAddress, endpoint.data, endpoint.dataLength); + return address; + } + + public unsafe void Initialize(params INetworkParameter[] param) + { + //if (!Binding.Baselib_RegisteredNetwork_SupportsNetwork()) + // throw new Exception("Baselib does not support networking"); + + m_Baselib = new NativeArray(1, Allocator.Persistent); + + var baselib = default(BaselibData); + + rxQueueSize = k_defaultRxQueueSize; + txQueueSize = k_defaultTxQueueSize; + maximumPayloadSize = NetworkParameterConstants.MTU; + + for (int i = 0; i < param.Length; ++i) + { + if (param[i] is BaselibNetworkParameter) + { + var config = (BaselibNetworkParameter) param[i]; + rxQueueSize = config.receiveQueueCapacity; + txQueueSize = config.sendQueueCapacity; + maximumPayloadSize = config.maximumPayloadSize; + } + } + + m_PayloadsTx = new Payloads(txQueueSize, maximumPayloadSize); + m_PayloadsRx = new Payloads(rxQueueSize, maximumPayloadSize); + m_LocalAndTempEndpoint = new UnsafeBaselibNetworkArray(2 * (int)Binding.Baselib_RegisteredNetwork_Endpoint_MaxSize); + + baselib.m_PayloadsTx = m_PayloadsTx; + + m_Baselib[0] = baselib; + + // Emulate current interface behavior + NetworkInterfaceEndPoint ep = CreateInterfaceEndPoint(NetworkEndPoint.AnyIpv4); + if (Bind(ep) != 0) + throw new Exception("Could not bind socket"); + } + + public void Dispose() + { + if (m_Baselib[0].m_Socket.handle != IntPtr.Zero) + { + #if ENABLE_UNITY_COLLECTIONS_CHECKS + AllSockets.OpenSockets.Remove(new SocketList.SocketId + {socket = m_Baselib[0].m_Socket}); + #endif + Binding.Baselib_RegisteredNetwork_Socket_UDP_Close(m_Baselib[0].m_Socket); + } + + // FIXME: is created check + m_LocalAndTempEndpoint.Dispose(); + if (m_PayloadsTx.IsCreated) + m_PayloadsTx.Dispose(); + if (m_PayloadsRx.IsCreated) + m_PayloadsRx.Dispose(); + + m_Baselib.Dispose(); + } + + #region ReceiveJob + + [BurstCompile] + struct FlushSendJob : IJob + { + public Payloads Tx; + [NativeDisableContainerSafetyRestriction] + public NativeArray Baselib; + public unsafe void Execute() + { + var error = default(ErrorState); + var pollCount = 0; + while(Binding.Baselib_RegisteredNetwork_Socket_UDP_ProcessSend(Baselib[0].m_Socket, error.NativeErrorStatePtr) == Binding.Baselib_RegisteredNetwork_ProcessStatus.Pending && pollCount++ < k_defaultTxQueueSize){} + int count; + // InUse is not thread safe, needs to be called in a single threaded flush job + var inFlight = Tx.InUse; + if (inFlight > 0) + { + var results = stackalloc Binding.Baselib_RegisteredNetwork_CompletionResult[inFlight]; + count = (int)Binding.Baselib_RegisteredNetwork_Socket_UDP_DequeueSend(Baselib[0].m_Socket, results, (uint)inFlight, error.NativeErrorStatePtr); + if (error.ErrorCode != ErrorCode.Success) + { + return; + } + for (int i = 0; i < count; ++i) + { + Tx.ReleaseHandle((int)results[i].requestUserdata - 1); + } + } + } + } + [BurstCompile] + struct ReceiveJob : IJob + { + public NetworkPacketReceiver Receiver; + public Payloads Rx; + [NativeDisableContainerSafetyRestriction] + public NativeArray Baselib; + + public unsafe void Execute() + { + var count = 0; + var outstanding = Rx.InUse; + var error = default(ErrorState); + var requests = stackalloc Binding.Baselib_RegisteredNetwork_Request[Rx.Capacity]; + + if (outstanding > 0) + { + var pollCount = 0; + while (Binding.Baselib_RegisteredNetwork_Socket_UDP_ProcessRecv(Baselib[0].m_Socket, error.NativeErrorStatePtr) == Binding.Baselib_RegisteredNetwork_ProcessStatus.Pending && pollCount++ < k_defaultRxQueueSize) {} + + var results = stackalloc Binding.Baselib_RegisteredNetwork_CompletionResult[outstanding]; + + // Pop Completed Requests off the CompletionQ + count = (int)Binding.Baselib_RegisteredNetwork_Socket_UDP_DequeueRecv(Baselib[0].m_Socket, results, (uint)outstanding, error.NativeErrorStatePtr); + if (error.ErrorCode != ErrorCode.Success) + { + Receiver.ReceiveErrorCode = (int) error.ErrorCode; + return; + } + + // Copy and run Append on each Packet. + + var stream = Receiver.GetDataStream(); + var headerLength = UnsafeUtility.SizeOf(); + var address = default(NetworkInterfaceEndPoint); + + var indicies = stackalloc int[count]; + for (int i = 0; i < count; i++) + { + if (results[i].status == Binding.Baselib_RegisteredNetwork_CompletionStatus.Failed) + { + // todo: report error? + continue; + } + var receivedBytes = (int) results[i].bytesTransferred; + var index = (int)results[i].requestUserdata - 1; + var packet = Rx.GetRequestFromHandle(index); + + indicies[i] = index; + outstanding--; + + // todo: make sure we avoid this copy + var payloadLen = receivedBytes - headerLength; + + int dataStreamSize = Receiver.GetDataStreamSize(); + if (Receiver.DynamicDataStreamSize()) + { + while (dataStreamSize + payloadLen >= stream.Length) + stream.ResizeUninitialized(stream.Length*2); + } + else if (dataStreamSize + payloadLen > stream.Length) + { + Receiver.ReceiveErrorCode = 10040;//(int)ErrorCode.OutOfMemory; + continue; + } + + UnsafeUtility.MemCpy( + (byte*)stream.GetUnsafePtr() + dataStreamSize, + (byte*)packet.payload.data + headerLength, + payloadLen); + + var remote = packet.remoteEndpoint.slice; + address.dataLength = (int)remote.size; + UnsafeUtility.MemCpy(address.data, (void*)remote.data, (int)remote.size); + Receiver.ReceiveCount += Receiver.AppendPacket(address, *(UdpCHeader*)packet.payload.data, receivedBytes); + } + // Reuse the requests after they have been processed. + for (int i = 0; i < count; i++) + { + requests[i] = Rx.GetRequestFromHandle(indicies[i]); + requests[i].requestUserdata = (IntPtr)indicies[i] + 1; + } + } + + while (Rx.InUse < Rx.Capacity) + { + int handle = Rx.AcquireHandle(); + requests[count] = Rx.GetRequestFromHandle(handle); + requests[count].requestUserdata = (IntPtr)handle + 1; + ++count; + } + if (count > 0) + { + count = (int) Binding.Baselib_RegisteredNetwork_Socket_UDP_ScheduleRecv( + Baselib[0].m_Socket, + requests, + (uint)count, + error.NativeErrorStatePtr); + if (error.ErrorCode != ErrorCode.Success) + Receiver.ReceiveErrorCode = (int) error.ErrorCode; + } + } + } + #endregion + + public JobHandle ScheduleReceive(NetworkPacketReceiver receiver, JobHandle dep) + { + var job = new ReceiveJob + { + Baselib = m_Baselib, + Rx = m_PayloadsRx, + Receiver = receiver + }; + return job.Schedule(dep); + } + public JobHandle ScheduleSend(NativeQueue sendQueue, JobHandle dep) + { + var job = new FlushSendJob + { + Baselib = m_Baselib, + Tx = m_PayloadsTx + }; + return job.Schedule(dep); + } + + public unsafe int Bind(NetworkInterfaceEndPoint endpoint) + { + var baselib = m_Baselib[0]; + if (m_Baselib[0].m_Socket.handle != IntPtr.Zero) + { +#if ENABLE_UNITY_COLLECTIONS_CHECKS + AllSockets.OpenSockets.Remove(new SocketList.SocketId + {socket = m_Baselib[0].m_Socket}); +#endif + Binding.Baselib_RegisteredNetwork_Socket_UDP_Close(m_Baselib[0].m_Socket); + baselib.m_Socket.handle = IntPtr.Zero; + + // Recreate the payloads to make sure we do not loose any items from the queue + m_PayloadsRx.Dispose(); + m_PayloadsRx = new Payloads(rxQueueSize, maximumPayloadSize); + } + + var slice = m_LocalAndTempEndpoint.AtIndexAsSlice(0, (uint)Binding.Baselib_RegisteredNetwork_Endpoint_MaxSize); + UnsafeUtility.MemCpy((void*)slice.data, endpoint.data, endpoint.dataLength); + + var error = default(ErrorState); + + NetworkEndpoint local; + local.slice = slice; + + Binding.Baselib_NetworkAddress localAddress; + Binding.Baselib_RegisteredNetwork_Endpoint_GetNetworkAddress(local, &localAddress, error.NativeErrorStatePtr); + + baselib.m_Socket = Binding.Baselib_RegisteredNetwork_Socket_UDP_Create( + &localAddress, + Binding.Baselib_NetworkAddress_AddressReuse.Allow, + checked((uint)txQueueSize), + checked((uint)rxQueueSize), + error.NativeErrorStatePtr); + if (error.ErrorCode != ErrorCode.Success) + { + m_Baselib[0] = baselib; + return -1; + } + + // Schedule receive right away so we do not loose packets received before the first call to update + int count = 0; + var requests = stackalloc Binding.Baselib_RegisteredNetwork_Request[m_PayloadsRx.Capacity]; + while (m_PayloadsRx.InUse < m_PayloadsRx.Capacity) + { + int handle = m_PayloadsRx.AcquireHandle(); + requests[count] = m_PayloadsRx.GetRequestFromHandle(handle); + requests[count].requestUserdata = (IntPtr)handle + 1; + ++count; + } + if (count > 0) + { + Binding.Baselib_RegisteredNetwork_Socket_UDP_ScheduleRecv( + baselib.m_Socket, + requests, + (uint)count, + error.NativeErrorStatePtr); + } +#if ENABLE_UNITY_COLLECTIONS_CHECKS + AllSockets.OpenSockets.Add(new SocketList.SocketId + {socket = baselib.m_Socket}); +#endif + m_Baselib[0] = baselib; + return 0; + } + + static TransportFunctionPointer BeginSendMessageFunctionPointer = new TransportFunctionPointer(BeginSendMessage); + static TransportFunctionPointer EndSendMessageFunctionPointer = new TransportFunctionPointer(EndSendMessage); + static TransportFunctionPointer AbortSendMessageFunctionPointer = new TransportFunctionPointer(AbortSendMessage); + + public unsafe NetworkSendInterface CreateSendInterface() + { + return new NetworkSendInterface + { + BeginSendMessage = BeginSendMessageFunctionPointer, + EndSendMessage = EndSendMessageFunctionPointer, + AbortSendMessage = AbortSendMessageFunctionPointer, + UserData = (IntPtr)m_Baselib.GetUnsafePtr() + }; + } + + [BurstCompile] + private static unsafe int BeginSendMessage(out NetworkInterfaceSendHandle handle, IntPtr userData) + { + var baselib = (BaselibData*)userData; + handle = default(NetworkInterfaceSendHandle); + int index = baselib->m_PayloadsTx.AcquireHandle(); + if (index < 0) + return -1; + + handle.id = index; + handle.size = 0; + var message = baselib->m_PayloadsTx.GetRequestFromHandle(index); + handle.data = (IntPtr)message.payload.data; + handle.capacity = (int) message.payload.size; + return 0; + } + + [BurstCompile] + private static unsafe int EndSendMessage(ref NetworkInterfaceSendHandle handle, ref NetworkInterfaceEndPoint address, IntPtr userData, ref NetworkSendQueueHandle sendQueueHandle) + { + var baselib = (BaselibData*)userData; + int index = handle.id; + var message = baselib->m_PayloadsTx.GetRequestFromHandle(index); + message.requestUserdata = (IntPtr) (index + 1); + message.payload.size = (uint)handle.size; + + var addr = address; + UnsafeUtility.MemCpy((void*)message.remoteEndpoint.slice.data, addr.data, address.dataLength); + + NetworkRequest* messagePtr = &message; + + var error = default(ErrorState); + var count = (int) Binding.Baselib_RegisteredNetwork_Socket_UDP_ScheduleSend( + baselib->m_Socket, + messagePtr, + 1u, + error.NativeErrorStatePtr); + if (error.ErrorCode != ErrorCode.Success) + { + baselib->m_PayloadsTx.ReleaseHandle(index); + return -1; + } + return handle.size; + } + + [BurstCompile] + private static unsafe void AbortSendMessage(ref NetworkInterfaceSendHandle handle, IntPtr userData) + { + var baselib = (BaselibData*)userData; + var id = handle.id; + baselib->m_PayloadsTx.ReleaseHandle(id); + } + } +} +#endif \ No newline at end of file diff --git a/Runtime/INetworkDriver.cs.meta b/Runtime/BaselibNetworkInterface.cs.meta similarity index 83% rename from Runtime/INetworkDriver.cs.meta rename to Runtime/BaselibNetworkInterface.cs.meta index 0310c89..d70039e 100644 --- a/Runtime/INetworkDriver.cs.meta +++ b/Runtime/BaselibNetworkInterface.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 6250d2765a8fe544b8c6b1ee0c5a580f +guid: 700170a841a1d4c06914f899d021cd3a MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/Runtime/Bindings/network.bindings.bundle b/Runtime/Bindings/network.bindings.bundle index bd0d4f6..a8dc919 100644 Binary files a/Runtime/Bindings/network.bindings.bundle and b/Runtime/Bindings/network.bindings.bundle differ diff --git a/Runtime/Bindings/network.bindings.cs b/Runtime/Bindings/network.bindings.cs index c7213c4..a2fcbbe 100644 --- a/Runtime/Bindings/network.bindings.cs +++ b/Runtime/Bindings/network.bindings.cs @@ -32,6 +32,14 @@ internal unsafe struct in_addr public uint s_addr; } + [StructLayout(LayoutKind.Explicit)] + public unsafe struct network_address + { + internal const int Length = 28; + [FieldOffset(0)] public fixed byte data[28]; + [FieldOffset(28)] public int length; + } + [StructLayout(LayoutKind.Explicit)] internal unsafe struct sockaddr { @@ -87,7 +95,7 @@ public static unsafe class NativeBindings public static extern int network_terminate(); [DllImport(m_DllName, CallingConvention = CallingConvention.Cdecl)] - public static extern int network_create_and_bind(ref long socket_handle, ref NetworkEndPoint address, ref int errorcode); + public static extern int network_create_and_bind(ref long socket_handle, ref network_address address, ref int errorcode); [DllImport(m_DllName, CallingConvention = CallingConvention.Cdecl)] public static extern int network_close(ref long socket_handle, ref int errorcode); @@ -105,14 +113,14 @@ public static unsafe class NativeBindings public static extern int network_set_connection_reset(long socket_handle, int value); [DllImport(m_DllName, CallingConvention = CallingConvention.Cdecl)] - public static extern int network_get_socket_address(long socket_handle, ref NetworkEndPoint own_address, ref int errorcode); + public static extern int network_get_socket_address(long socket_handle, ref network_address own_address, ref int errorcode); [DllImport(m_DllName, CallingConvention = CallingConvention.Cdecl)] public static extern int network_sendmsg(long socket_handle, void* iov, int iov_len, - ref NetworkEndPoint address, ref int errorcode); + ref network_address address, ref int errorcode); [DllImport(m_DllName, CallingConvention = CallingConvention.Cdecl)] public static extern int network_recvmsg(long socket_handle, void* iov, int iov_len, - ref NetworkEndPoint remote, ref int errorcode); + ref network_address remote, ref int errorcode); } } diff --git a/Runtime/Bindings/network.bindings.dll b/Runtime/Bindings/network.bindings.dll index 7b0e816..01014ab 100644 Binary files a/Runtime/Bindings/network.bindings.dll and b/Runtime/Bindings/network.bindings.dll differ diff --git a/Runtime/Bindings/network.bindings.pdb b/Runtime/Bindings/network.bindings.pdb index b67c945..57f487b 100644 Binary files a/Runtime/Bindings/network.bindings.pdb and b/Runtime/Bindings/network.bindings.pdb differ diff --git a/Runtime/DataStream.cs b/Runtime/DataStream.cs index 9cf54ae..f500289 100644 --- a/Runtime/DataStream.cs +++ b/Runtime/DataStream.cs @@ -2,7 +2,6 @@ using Unity.Collections; using Unity.Collections.LowLevel.Unsafe; using System; -using Unity.Networking.Transport.LowLevel.Unsafe; namespace Unity.Networking.Transport { @@ -24,264 +23,107 @@ internal struct UIntFloat /// to serialize data for sending and then to deserialize when receiving. /// /// - /// The reader can be used to deserialize the data from a writer, writing data - /// to a writer and reading it back can be done like this: + /// The reader can be used to deserialize the data from a NativeArray, writing data + /// to a NativeArray and reading it back can be done like this: /// - /// using (var dataWriter = new DataStreamWriter(16, Allocator.Persistent)) + /// using (var data = new NativeArray(16, Allocator.Persistent)) /// { - /// dataWriter.Write(42); - /// dataWriter.Write(1234); + /// var dataWriter = new DataStreamWriter(data); + /// dataWriter.WriteInt(42); + /// dataWriter.WriteInt(1234); /// // Length is the actual amount of data inside the writer, /// // Capacity is the total amount. - /// var dataReader = new DataStreamReader(dataWriter, 0, dataWriter.Length); - /// var context = default(DataStreamReader.Context); - /// var myFirstInt = dataReader.ReadInt(ref context); - /// var mySecondInt = dataReader.ReadInt(ref context); + /// var dataReader = new DataStreamReader(nativeArrayOfBytes.GetSubArray(0, dataWriter.Length)); + /// var myFirstInt = dataReader.ReadInt(); + /// var mySecondInt = dataReader.ReadInt(); /// } /// /// - /// The writer needs to be Disposed (here done by wrapping usage in using statement) - /// because it uses native memory which needs to be freed. - /// - /// There are a number of functions for various data types. Each write call - /// returns a Deferred* variant for that particular type and this can be used - /// as a marker to overwrite the data later on, this is particularly useful when + /// There are a number of functions for various data types. If a copy of the writer + /// is stored it can be used to overwrite the data later on, this is particularly useful when /// the size of the data is written at the start and you want to write it at /// the end when you know the value. /// /// - /// using (var data = new DataStreamWriter(16, Allocator.Persistent)) + /// using (var data = new NativeArray(16, Allocator.Persistent)) /// { + /// var dataWriter = new DataStreamWriter(data); /// // My header data - /// var headerSizeMark = data.Write((ushort)0); - /// var payloadSizeMark = data.Write((ushort)0); - /// data.Write(42); - /// data.Write(1234); + /// var headerSizeMark = dataWriter; + /// dataWriter.WriteUShort((ushort)0); + /// var payloadSizeMark = dataWriter; + /// dataWriter.WriteUShort((ushort)0); + /// dataWriter.WriteInt(42); + /// dataWriter.WriteInt(1234); /// var headerSize = data.Length; /// // Update header size to correct value - /// headerSizeMark.Update((ushort)headerSize); + /// headerSizeMark.WriteUShort((ushort)headerSize); /// // My payload data /// byte[] someBytes = Encoding.ASCII.GetBytes("some string"); - /// data.Write(someBytes, someBytes.Length); + /// dataWriter.Write(someBytes, someBytes.Length); /// // Update payload size to correct value - /// payloadSizeMark.Update((ushort)(data.Length - headerSize)); + /// payloadSizeMark.WriteUShort((ushort)(dataWriter.Length - headerSize)); /// } /// - /// - /// It's possible to get a more direct access to the buffer inside the - /// reader/writer, in an unsafe way. See /// [StructLayout(LayoutKind.Sequential)] - [NativeContainer] - public unsafe struct DataStreamWriter : IDisposable + public unsafe struct DataStreamWriter { - public struct DeferredByte - { - public void Update(byte value) - { -#if ENABLE_UNITY_COLLECTIONS_CHECKS - if (m_writer.m_Data->bitIndex != 0) - throw new InvalidOperationException("Cannot update a deferred writer without flushing packed writes"); -#endif - int oldOffset = m_writer.m_Data->length; - m_writer.m_Data->length = m_offset; - m_writer.Write(value); - m_writer.m_Data->length = oldOffset; - } - - internal DataStreamWriter m_writer; - internal int m_offset; - } - public struct DeferredShort + struct StreamData { - public void Update(short value) - { -#if ENABLE_UNITY_COLLECTIONS_CHECKS - if (m_writer.m_Data->bitIndex != 0) - throw new InvalidOperationException("Cannot update a deferred writer without flushing packed writes"); -#endif - int oldOffset = m_writer.m_Data->length; - m_writer.m_Data->length = m_offset; - m_writer.Write(value); - m_writer.m_Data->length = oldOffset; - } - - internal DataStreamWriter m_writer; - internal int m_offset; - } - public struct DeferredUShort - { - public void Update(ushort value) - { -#if ENABLE_UNITY_COLLECTIONS_CHECKS - if (m_writer.m_Data->bitIndex != 0) - throw new InvalidOperationException("Cannot update a deferred writer without flushing packed writes"); -#endif - int oldOffset = m_writer.m_Data->length; - m_writer.m_Data->length = m_offset; - m_writer.Write(value); - m_writer.m_Data->length = oldOffset; - } - - internal DataStreamWriter m_writer; - internal int m_offset; - } - public struct DeferredInt - { - public void Update(int value) - { -#if ENABLE_UNITY_COLLECTIONS_CHECKS - if (m_writer.m_Data->bitIndex != 0) - throw new InvalidOperationException("Cannot update a deferred writer without flushing packed writes"); -#endif - int oldOffset = m_writer.m_Data->length; - m_writer.m_Data->length = m_offset; - m_writer.Write(value); - m_writer.m_Data->length = oldOffset; - } - - internal DataStreamWriter m_writer; - internal int m_offset; + public byte* buffer; + public int length; + public int capacity; + public ulong bitBuffer; + public int bitIndex; + public int failedWrites; } - public struct DeferredUInt - { - public void Update(uint value) - { -#if ENABLE_UNITY_COLLECTIONS_CHECKS - if (m_writer.m_Data->bitIndex != 0) - throw new InvalidOperationException("Cannot update a deferred writer without flushing packed writes"); -#endif - int oldOffset = m_writer.m_Data->length; - m_writer.m_Data->length = m_offset; - m_writer.Write(value); - m_writer.m_Data->length = oldOffset; - } - internal DataStreamWriter m_writer; - internal int m_offset; - } - public struct DeferredFloat - { - public void Update(float value) - { -#if ENABLE_UNITY_COLLECTIONS_CHECKS - if (m_writer.m_Data->bitIndex != 0) - throw new InvalidOperationException("Cannot update a deferred writer without flushing packed writes"); -#endif - int oldOffset = m_writer.m_Data->length; - m_writer.m_Data->length = m_offset; - m_writer.Write(value); - m_writer.m_Data->length = oldOffset; - } + [NativeDisableUnsafePtrRestriction] StreamData m_Data; + internal IntPtr m_SendHandleData; - internal DataStreamWriter m_writer; - internal int m_offset; - } - public struct DeferredShortNetworkByteOrder - { - public void Update(short value) - { #if ENABLE_UNITY_COLLECTIONS_CHECKS - if (m_writer.m_Data->bitIndex != 0) - throw new InvalidOperationException("Cannot update a deferred writer without flushing packed writes"); + AtomicSafetyHandle m_Safety; #endif - int oldOffset = m_writer.m_Data->length; - m_writer.m_Data->length = m_offset; - m_writer.WriteNetworkByteOrder(value); - m_writer.m_Data->length = oldOffset; - } - - internal DataStreamWriter m_writer; - internal int m_offset; - } - public struct DeferredUShortNetworkByteOrder + public DataStreamWriter(int length, Allocator allocator) { - public void Update(ushort value) - { #if ENABLE_UNITY_COLLECTIONS_CHECKS - if (m_writer.m_Data->bitIndex != 0) - throw new InvalidOperationException("Cannot update a deferred writer without flushing packed writes"); + if (allocator != Allocator.Temp) + throw new InvalidOperationException("DataStreamWriters can only be created with temp memory"); #endif - int oldOffset = m_writer.m_Data->length; - m_writer.m_Data->length = m_offset; - m_writer.WriteNetworkByteOrder(value); - m_writer.m_Data->length = oldOffset; - } - - internal DataStreamWriter m_writer; - internal int m_offset; + Initialize(out this, new NativeArray(length, allocator)); } - public struct DeferredIntNetworkByteOrder + public DataStreamWriter(NativeArray data) { - public void Update(int value) - { -#if ENABLE_UNITY_COLLECTIONS_CHECKS - if (m_writer.m_Data->bitIndex != 0) - throw new InvalidOperationException("Cannot update a deferred writer without flushing packed writes"); -#endif - int oldOffset = m_writer.m_Data->length; - m_writer.m_Data->length = m_offset; - m_writer.WriteNetworkByteOrder(value); - m_writer.m_Data->length = oldOffset; - } - - internal DataStreamWriter m_writer; - internal int m_offset; + Initialize(out this, data); } - public struct DeferredUIntNetworkByteOrder + public NativeArray AsNativeArray() { - public void Update(uint value) - { + var na = NativeArrayUnsafeUtility.ConvertExistingDataToNativeArray(m_Data.buffer, Length, Allocator.Invalid); #if ENABLE_UNITY_COLLECTIONS_CHECKS - if (m_writer.m_Data->bitIndex != 0) - throw new InvalidOperationException("Cannot update a deferred writer without flushing packed writes"); + NativeArrayUnsafeUtility.SetAtomicSafetyHandle(ref na, m_Safety); #endif - int oldOffset = m_writer.m_Data->length; - m_writer.m_Data->length = m_offset; - m_writer.WriteNetworkByteOrder(value); - m_writer.m_Data->length = oldOffset; - } - - internal DataStreamWriter m_writer; - internal int m_offset; + return na; } - - internal struct StreamData + private static void Initialize(out DataStreamWriter self, NativeArray data) { - public byte* buffer; - public int length; - public int capacity; - public ulong bitBuffer; - public int bitIndex; - } + self.m_SendHandleData = IntPtr.Zero; - [NativeDisableUnsafePtrRestriction] internal StreamData* m_Data; + self.m_Data.capacity = data.Length; + self.m_Data.length = 0; + self.m_Data.buffer = (byte*)data.GetUnsafePtr(); + self.m_Data.bitBuffer = 0; + self.m_Data.bitIndex = 0; + self.m_Data.failedWrites = 0; #if ENABLE_UNITY_COLLECTIONS_CHECKS - internal AtomicSafetyHandle m_Safety; - [NativeSetClassTypeToNullOnSchedule] internal DisposeSentinel m_DisposeSentinel; -#endif - - Allocator m_Allocator; - - public DataStreamWriter(int capacity, Allocator allocator) - { - m_Allocator = allocator; - m_Data = (StreamData*)UnsafeUtility.Malloc(UnsafeUtility.SizeOf(), UnsafeUtility.AlignOf(), m_Allocator); - m_Data->capacity = capacity; - m_Data->length = 0; - m_Data->buffer = (byte*) UnsafeUtility.Malloc(capacity, UnsafeUtility.AlignOf(), m_Allocator); - m_Data->bitBuffer = 0; - m_Data->bitIndex = 0; - -#if ENABLE_UNITY_COLLECTIONS_CHECKS - DisposeSentinel.Create(out m_Safety, out m_DisposeSentinel, 1, m_Allocator); + self.m_Safety = NativeArrayUnsafeUtility.GetAtomicSafetyHandle(data); #endif uint test = 1; unsafe { byte* test_b = (byte*) &test; - m_IsLittleEndian = test_b[0] == 1 ? 1 : 0; + self.m_IsLittleEndian = test_b[0] == 1 ? 1 : 0; } } @@ -303,16 +145,15 @@ private static int ByteSwap(int val) /// public bool IsCreated { - get { return m_Data != null; } + get { return m_Data.buffer != null; } } + public bool HasFailedWrites => m_Data.failedWrites > 0; + /// /// The total size of the data buffer, see for - /// the size of space used in the buffer. Capacity can be - /// changed after the writer has been created. + /// the size of space used in the buffer. /// - /// Thrown if the given - /// capacity is smaller than the current buffer usage. public int Capacity { get @@ -320,22 +161,7 @@ public int Capacity #if ENABLE_UNITY_COLLECTIONS_CHECKS AtomicSafetyHandle.CheckReadAndThrow(m_Safety); #endif - return m_Data->capacity; - } - set - { - if (m_Data->capacity == value) - return; -#if ENABLE_UNITY_COLLECTIONS_CHECKS - AtomicSafetyHandle.CheckWriteAndBumpSecondaryVersion(m_Safety); - if (m_Data->length + ((m_Data->bitIndex + 7) >> 3) > value) - throw new InvalidOperationException("Cannot shrink a data stream to be shorter than the current data in it"); -#endif - byte* newbuf = (byte*) UnsafeUtility.Malloc(value, UnsafeUtility.AlignOf(), m_Allocator); - UnsafeUtility.MemCpy(newbuf, m_Data->buffer, m_Data->length); - UnsafeUtility.Free(m_Data->buffer, m_Allocator); - m_Data->buffer = newbuf; - m_Data->capacity = value; + return m_Data.capacity; } } @@ -349,7 +175,8 @@ public int Length #if ENABLE_UNITY_COLLECTIONS_CHECKS AtomicSafetyHandle.CheckReadAndThrow(m_Safety); #endif - return m_Data->length + ((m_Data->bitIndex + 7) >> 3); + SyncBitData(); + return m_Data.length + ((m_Data.bitIndex + 7) >> 3); } } /// @@ -362,226 +189,131 @@ public int LengthInBits #if ENABLE_UNITY_COLLECTIONS_CHECKS AtomicSafetyHandle.CheckReadAndThrow(m_Safety); #endif - return m_Data->length*8 + m_Data->bitIndex; + SyncBitData(); + return m_Data.length*8 + m_Data.bitIndex; } } - /// - /// The writer uses unmanaged memory for its data buffer. Dispose - /// needs to be called to free this resource. - /// - public void Dispose() + private void SyncBitData() { + var bitIndex = m_Data.bitIndex; + if (bitIndex <= 0) + return; #if ENABLE_UNITY_COLLECTIONS_CHECKS - DisposeSentinel.Dispose(ref m_Safety, ref m_DisposeSentinel); + AtomicSafetyHandle.CheckWriteAndThrow(m_Safety); #endif - UnsafeUtility.Free(m_Data->buffer, m_Allocator); - UnsafeUtility.Free(m_Data, m_Allocator); - m_Data = (StreamData*) 0; - } - - public void Flush() - { - while (m_Data->bitIndex > 0) + var bitBuffer = m_Data.bitBuffer; + int offset = 0; + while (bitIndex > 0) { - m_Data->buffer[m_Data->length++] = (byte)m_Data->bitBuffer; - m_Data->bitIndex -= 8; - m_Data->bitBuffer >>= 8; + m_Data.buffer[m_Data.length + offset] = (byte)bitBuffer; + bitIndex -= 8; + bitBuffer >>= 8; + ++offset; } - - m_Data->bitIndex = 0; - } - - /// - /// Create a NativeSlice with access to the raw data in the writer, the data size - /// (start to length) must not exceed the total size of the array or - /// an exception will be thrown. - /// - /// - /// - public NativeSlice GetNativeSlice(int start, int length) - { -#if ENABLE_UNITY_COLLECTIONS_CHECKS - ValidateSizeParameters(start, length, length); -#endif - - var slice = NativeSliceUnsafeUtility.ConvertExistingDataToNativeSlice(m_Data->buffer + start, 1, - length); -#if ENABLE_UNITY_COLLECTIONS_CHECKS - NativeSliceUnsafeUtility.SetAtomicSafetyHandle(ref slice, m_Safety); -#endif - return slice; } - - /// - /// Copy data from the writer to the given NativeArray, the data size - /// (start to length) must not exceed the total size of the array or - /// an exception will be thrown. - /// - /// - /// - /// - public void CopyTo(int start, int length, NativeArray dest) - { -#if ENABLE_UNITY_COLLECTIONS_CHECKS - ValidateSizeParameters(start, length, dest.Length); -#endif - - void* dstPtr = dest.GetUnsafePtr(); - UnsafeUtility.MemCpy(dstPtr, m_Data->buffer + start, length); - } - - /// - /// Copy data from the writer to the given managed byte array, the - /// data size (start to length) must not exceed the total size of the - /// byte array or an exception will be thrown. - /// - /// - /// - /// - public void CopyTo(int start, int length, ref byte[] dest) + public void Flush() { -#if ENABLE_UNITY_COLLECTIONS_CHECKS - ValidateSizeParameters(start, length, dest.Length); -#endif - - fixed (byte* ptr = dest) + while (m_Data.bitIndex > 0) { - UnsafeUtility.MemCpy(ptr, m_Data->buffer + start, length); + m_Data.buffer[m_Data.length++] = (byte)m_Data.bitBuffer; + m_Data.bitIndex -= 8; + m_Data.bitBuffer >>= 8; } - } -#if ENABLE_UNITY_COLLECTIONS_CHECKS - void ValidateSizeParameters(int start, int length, int dstLength) - { - if (start < 0 || length + start > m_Data->length) - throw new ArgumentOutOfRangeException("start+length", - "The sum of start and length can not be larger than the data buffer Length"); - - if (length > dstLength) - throw new ArgumentOutOfRangeException("length", "Length must be <= than the length of the destination"); - - if (m_Data->bitIndex > 0) - throw new InvalidOperationException("Cannot read from a DataStreamWriter when there are pending packed writes, call Flush first"); - - AtomicSafetyHandle.CheckReadAndThrow(m_Safety); + m_Data.bitIndex = 0; } -#endif - public void WriteBytes(byte* data, int bytes) + public bool WriteBytes(byte* data, int bytes) { #if ENABLE_UNITY_COLLECTIONS_CHECKS AtomicSafetyHandle.CheckWriteAndThrow(m_Safety); - if (m_Data->length + ((m_Data->bitIndex + 7) >> 3) + bytes > m_Data->capacity) - throw new System.ArgumentOutOfRangeException(); #endif + if (m_Data.length + ((m_Data.bitIndex + 7) >> 3) + bytes > m_Data.capacity) + { + ++m_Data.failedWrites; + return false; + } Flush(); - UnsafeUtility.MemCpy(m_Data->buffer + m_Data->length, data, bytes); - m_Data->length += bytes; + UnsafeUtility.MemCpy(m_Data.buffer + m_Data.length, data, bytes); + m_Data.length += bytes; + return true; } - public DeferredByte Write(byte value) + public bool WriteByte(byte value) { - var ret = new DeferredByte {m_writer = this, m_offset = m_Data->length + ((m_Data->bitIndex + 7) >> 3)}; - WriteBytes((byte*) &value, sizeof(byte)); - return ret; + return WriteBytes((byte*) &value, sizeof(byte)); } /// - /// Copy byte array into the writers data buffer, up to the - /// given length or the complete size if no length (or -1) is given. + /// Copy NativeArray of bytes into the writers data buffer. /// /// Source byte array - /// Length to copy, omit this to copy all the byte array - public void Write(byte[] value, int length = -1) + public bool WriteBytes(NativeArray value) { - if (length < 0) - length = value.Length; - - fixed (byte* p = value) - { - WriteBytes(p, length); - } + return WriteBytes((byte*)value.GetUnsafeReadOnlyPtr(), value.Length); } - public DeferredShort Write(short value) + public bool WriteShort(short value) { - var ret = new DeferredShort {m_writer = this, m_offset = m_Data->length + ((m_Data->bitIndex + 7) >> 3)}; - WriteBytes((byte*) &value, sizeof(short)); - return ret; + return WriteBytes((byte*) &value, sizeof(short)); } - public DeferredUShort Write(ushort value) + public bool WriteUShort(ushort value) { - var ret = new DeferredUShort {m_writer = this, m_offset = m_Data->length + ((m_Data->bitIndex + 7) >> 3)}; - WriteBytes((byte*) &value, sizeof(ushort)); - return ret; + return WriteBytes((byte*) &value, sizeof(ushort)); } - public DeferredInt Write(int value) + public bool WriteInt(int value) { - var ret = new DeferredInt {m_writer = this, m_offset = m_Data->length + ((m_Data->bitIndex + 7) >> 3)}; - WriteBytes((byte*) &value, sizeof(int)); - return ret; + return WriteBytes((byte*) &value, sizeof(int)); } - public DeferredUInt Write(uint value) + public bool WriteUInt(uint value) { - var ret = new DeferredUInt {m_writer = this, m_offset = m_Data->length + ((m_Data->bitIndex + 7) >> 3)}; - WriteBytes((byte*) &value, sizeof(uint)); - return ret; + return WriteBytes((byte*) &value, sizeof(uint)); } - public void Write(ulong value) + public bool WriteULong(ulong value) { - WriteBytes((byte*) &value, sizeof(ulong)); + return WriteBytes((byte*) &value, sizeof(ulong)); } - public DeferredShortNetworkByteOrder WriteNetworkByteOrder(short value) + public bool WriteShortNetworkByteOrder(short value) { - var ret = new DeferredShortNetworkByteOrder {m_writer = this, m_offset = m_Data->length + ((m_Data->bitIndex + 7) >> 3)}; short netValue = IsLittleEndian ? ByteSwap(value) : value; - WriteBytes((byte*) &netValue, sizeof(short)); - return ret; + return WriteBytes((byte*) &netValue, sizeof(short)); } - public DeferredUShortNetworkByteOrder WriteNetworkByteOrder(ushort value) + public bool WriteUShortNetworkByteOrder(ushort value) { - var ret = new DeferredUShortNetworkByteOrder {m_writer = this, m_offset = m_Data->length + ((m_Data->bitIndex + 7) >> 3)}; - WriteNetworkByteOrder((short) value); - return ret; + return WriteShortNetworkByteOrder((short) value); } - public DeferredIntNetworkByteOrder WriteNetworkByteOrder(int value) + public bool WriteIntNetworkByteOrder(int value) { - var ret = new DeferredIntNetworkByteOrder {m_writer = this, m_offset = m_Data->length + ((m_Data->bitIndex + 7) >> 3)}; int netValue = IsLittleEndian ? ByteSwap(value) : value; - WriteBytes((byte*) &netValue, sizeof(int)); - return ret; + return WriteBytes((byte*) &netValue, sizeof(int)); } - public DeferredUIntNetworkByteOrder WriteNetworkByteOrder(uint value) + public bool WriteUIntNetworkByteOrder(uint value) { - var ret = new DeferredUIntNetworkByteOrder {m_writer = this, m_offset = m_Data->length + ((m_Data->bitIndex + 7) >> 3)}; - WriteNetworkByteOrder((int)value); - return ret; + return WriteIntNetworkByteOrder((int)value); } - public DeferredFloat Write(float value) + public bool WriteFloat(float value) { - var ret = new DeferredFloat {m_writer = this, m_offset = m_Data->length + ((m_Data->bitIndex + 7) >> 3)}; UIntFloat uf = new UIntFloat(); uf.floatValue = value; - Write((int) uf.intValue); - return ret; + return WriteInt((int) uf.intValue); } private void FlushBits() { - while (m_Data->bitIndex >= 8) + while (m_Data.bitIndex >= 8) { - m_Data->buffer[m_Data->length++] = (byte)m_Data->bitBuffer; - m_Data->bitIndex -= 8; - m_Data->bitBuffer >>= 8; + m_Data.buffer[m_Data.length++] = (byte)m_Data.bitBuffer; + m_Data.bitIndex -= 8; + m_Data.bitBuffer >>= 8; } } void WriteRawBitsInternal(uint value, int numbits) @@ -593,11 +325,11 @@ void WriteRawBitsInternal(uint value, int numbits) throw new ArgumentOutOfRangeException("Value does not fit in the specified number of bits"); #endif - m_Data->bitBuffer |= ((ulong)value << m_Data->bitIndex); - m_Data->bitIndex += numbits; + m_Data.bitBuffer |= ((ulong)value << m_Data.bitIndex); + m_Data.bitIndex += numbits; } - public void WritePackedUInt(uint value, NetworkCompressionModel model) + public bool WritePackedUInt(uint value, NetworkCompressionModel model) { int bucket = model.CalculateBucket(value); uint offset = model.bucketOffsets[bucket]; @@ -605,42 +337,49 @@ public void WritePackedUInt(uint value, NetworkCompressionModel model) ushort encodeEntry = model.encodeTable[bucket]; #if ENABLE_UNITY_COLLECTIONS_CHECKS AtomicSafetyHandle.CheckWriteAndThrow(m_Safety); - if (m_Data->length + ((m_Data->bitIndex + encodeEntry&0xff + bits + 7) >> 3) > m_Data->capacity) - throw new System.ArgumentOutOfRangeException(); #endif + if (m_Data.length + ((m_Data.bitIndex + encodeEntry&0xff + bits + 7) >> 3) > m_Data.capacity) + { + ++m_Data.failedWrites; + return false; + } WriteRawBitsInternal((uint)(encodeEntry >> 8), encodeEntry & 0xFF); WriteRawBitsInternal(value - offset, bits); FlushBits(); + return true; } - public void WritePackedInt(int value, NetworkCompressionModel model) + public bool WritePackedInt(int value, NetworkCompressionModel model) { uint interleaved = (uint)((value >> 31) ^ (value << 1)); // interleave negative values between positive values: 0, -1, 1, -2, 2 - WritePackedUInt(interleaved, model); + return WritePackedUInt(interleaved, model); } - public void WritePackedFloat(float value, NetworkCompressionModel model) + public bool WritePackedFloat(float value, NetworkCompressionModel model) { - WritePackedFloatDelta(value, 0, model); + return WritePackedFloatDelta(value, 0, model); } - public void WritePackedUIntDelta(uint value, uint baseline, NetworkCompressionModel model) + public bool WritePackedUIntDelta(uint value, uint baseline, NetworkCompressionModel model) { int diff = (int)(baseline - value); - WritePackedInt(diff, model); + return WritePackedInt(diff, model); } - public void WritePackedIntDelta(int value, int baseline, NetworkCompressionModel model) + public bool WritePackedIntDelta(int value, int baseline, NetworkCompressionModel model) { int diff = (int)(baseline - value); - WritePackedInt(diff, model); + return WritePackedInt(diff, model); } - public void WritePackedFloatDelta(float value, float baseline, NetworkCompressionModel model) + public bool WritePackedFloatDelta(float value, float baseline, NetworkCompressionModel model) { var bits = 0; if (value != baseline) bits = 32; #if ENABLE_UNITY_COLLECTIONS_CHECKS AtomicSafetyHandle.CheckWriteAndThrow(m_Safety); - if (m_Data->length + ((m_Data->bitIndex + 1 + bits + 7) >> 3) > m_Data->capacity) - throw new System.ArgumentOutOfRangeException(); #endif + if (m_Data.length + ((m_Data.bitIndex + 1 + bits + 7) >> 3) > m_Data.capacity) + { + ++m_Data.failedWrites; + return false; + } if (bits == 0) WriteRawBitsInternal(0, 1); else @@ -651,34 +390,45 @@ public void WritePackedFloatDelta(float value, float baseline, NetworkCompressio WriteRawBitsInternal(uf.intValue, bits); } FlushBits(); + return true; } - public unsafe void WriteString(NativeString64 str) + public unsafe bool WriteString(NativeString64 str) { int length = (int)*((ushort*)&str) + 2; byte* data = ((byte*)&str); - WriteBytes(data, length); + return WriteBytes(data, length); } - public unsafe void WritePackedStringDelta(NativeString64 str, NativeString64 baseline, NetworkCompressionModel model) + public unsafe bool WritePackedStringDelta(NativeString64 str, NativeString64 baseline, NetworkCompressionModel model) { ushort length = *((ushort*)&str); byte* data = ((byte*)&str) + 2; ushort baseLength = *((ushort*)&baseline); byte* baseData = ((byte*)&baseline) + 2; - WritePackedUIntDelta(length, baseLength, model); + var oldData = m_Data; + if (!WritePackedUIntDelta(length, baseLength, model)) + return false; + bool didFailWrite = false; if (length <= baseLength) { for (int i = 0; i < length; ++i) - WritePackedUIntDelta(data[i], baseData[i], model); + didFailWrite |= !WritePackedUIntDelta(data[i], baseData[i], model); } else { for (int i = 0; i < baseLength; ++i) - WritePackedUIntDelta(data[i], baseData[i], model); + didFailWrite |= !WritePackedUIntDelta(data[i], baseData[i], model); for (int i = baseLength; i < length; ++i) - WritePackedUInt(data[i], model); + didFailWrite |= !WritePackedUInt(data[i], model); + } + // If anything was not written, rewind to the previous position + if (didFailWrite) + { + m_Data = oldData; + ++m_Data.failedWrites; } + return !didFailWrite; } /// @@ -686,12 +436,10 @@ public unsafe void WritePackedStringDelta(NativeString64 str, NativeString64 bas /// public void Clear() { -#if ENABLE_UNITY_COLLECTIONS_CHECKS - AtomicSafetyHandle.CheckWriteAndBumpSecondaryVersion(m_Safety); -#endif - m_Data->length = 0; - m_Data->bitIndex = 0; - m_Data->bitBuffer = 0; + m_Data.length = 0; + m_Data.bitIndex = 0; + m_Data.bitBuffer = 0; + m_Data.failedWrites = 0; } } @@ -716,70 +464,38 @@ public void Clear() /// } /// /// - /// The DataStreamReader.Context passed to all the Read* functions - /// carries the position of the read pointer inside the buffer inside, this can be - /// used with the job system but normally you start by creating - /// a default context like the example above shows. + /// The DataStreamReader carries the position of the read pointer inside the struct, + /// taking a copy of the reader will also copy the read position. This includes passing the + /// reader to a method by value instead of by ref. /// /// See the class for more information /// and examples. /// public unsafe struct DataStreamReader { - /// - /// The context is the current index to the data buffer used by the - /// reader. This can be used when the reader is used with the job system but normally - /// the same context is passed in for each read invocation so the correct - /// read index is used: - /// - /// var dataReader = new DataStreamReader(someDataWriter, 0, someDataWriter.Length); - /// var ctx = default(DataStreamReader.Context); - /// var someInt = dataReader.ReadInt(ref ctx); - /// var someOtherInt = dataReader.ReadInt(ref ctx); - /// - /// - public struct Context + struct Context { - internal int m_ReadByteIndex; - internal int m_BitIndex; - internal ulong m_BitBuffer; + public int m_ReadByteIndex; + public int m_BitIndex; + public ulong m_BitBuffer; + public int m_FailedReads; } - internal byte* m_bufferPtr; + byte* m_bufferPtr; + Context m_Context; int m_Length; #if ENABLE_UNITY_COLLECTIONS_CHECKS - internal AtomicSafetyHandle m_Safety; + AtomicSafetyHandle m_Safety; #endif - public DataStreamReader(NativeSlice slice) - { -#if ENABLE_UNITY_COLLECTIONS_CHECKS - m_Safety = NativeSliceUnsafeUtility.GetAtomicSafetyHandle(slice); -#endif - m_bufferPtr = (byte*)slice.GetUnsafeReadOnlyPtr(); - m_Length = slice.Length; - - uint test = 1; - unsafe - { - byte* test_b = (byte*) &test; - m_IsLittleEndian = test_b[0] == 1 ? 1 : 0; - } - } - - public DataStreamReader(DataStreamWriter writer, int offset, int length) + public DataStreamReader(NativeArray array) { #if ENABLE_UNITY_COLLECTIONS_CHECKS - if (offset + length > writer.Length) - throw new System.ArgumentOutOfRangeException(); - m_Safety = writer.m_Safety; - AtomicSafetyHandle.UseSecondaryVersion(ref m_Safety); -#if !UNITY_2019_3_OR_NEWER - AtomicSafetyHandle.SetAllowSecondaryVersionWriting(m_Safety, false); + m_Safety = NativeArrayUnsafeUtility.GetAtomicSafetyHandle(array); #endif -#endif - m_bufferPtr = writer.GetUnsafeReadOnlyPtr() + offset; - m_Length = length; + m_bufferPtr = (byte*)array.GetUnsafeReadOnlyPtr(); + m_Length = array.Length; + m_Context = default; uint test = 1; unsafe @@ -801,6 +517,7 @@ private static int ByteSwap(int val) return (int)(((val & 0xff) << 24) |((val&0xff00)<<8) | ((val>>8)&0xff00) | ((val >> 24)&0xff)); } + public bool HasFailedReads => m_Context.m_FailedReads > 0; /// /// The total size of the buffer space this reader is working with. /// @@ -828,225 +545,221 @@ public bool IsCreated /// Read and copy data to the memory location pointed to, an exception will /// be thrown if it does not fit. /// - /// /// /// /// Thrown if the length /// will put the reader out of bounds based on the current read pointer /// position. - public void ReadBytes(ref Context ctx, byte* data, int length) + public void ReadBytes(byte* data, int length) { #if ENABLE_UNITY_COLLECTIONS_CHECKS AtomicSafetyHandle.CheckReadAndThrow(m_Safety); - if (GetBytesRead(ref ctx) + length > m_Length) +#endif + if (GetBytesRead() + length > m_Length) { + ++m_Context.m_FailedReads; +#if ENABLE_UNITY_COLLECTIONS_CHECKS throw new System.ArgumentOutOfRangeException(); - } +#else + UnsafeUtility.MemClear(data, length); + return; #endif + } // Restore the full bytes moved to the bit buffer but no consumed - ctx.m_ReadByteIndex -= (ctx.m_BitIndex >> 3); - ctx.m_BitIndex = 0; - ctx.m_BitBuffer = 0; - UnsafeUtility.MemCpy(data, m_bufferPtr + ctx.m_ReadByteIndex, length); - ctx.m_ReadByteIndex += length; + m_Context.m_ReadByteIndex -= (m_Context.m_BitIndex >> 3); + m_Context.m_BitIndex = 0; + m_Context.m_BitBuffer = 0; + UnsafeUtility.MemCpy(data, m_bufferPtr + m_Context.m_ReadByteIndex, length); + m_Context.m_ReadByteIndex += length; } /// - /// Read and copy data into the given managed byte array, an exception will - /// be thrown if it does not fit. + /// Read and copy data into the given NativeArray of bytes, an exception will + /// be thrown if not enough bytes are available. /// - /// - /// - /// - public void ReadBytesIntoArray(ref Context ctx, ref byte[] dest, int length) + /// + public void ReadBytes(NativeArray array) { - for (var i = 0; i < length; ++i) - dest[i] = ReadByte(ref ctx); + ReadBytes((byte*)array.GetUnsafePtr(), array.Length); } - /// - /// Create a new byte array and read the given length of bytes into it. - /// - /// Current reader context - /// Amount of bytes to read. - /// Newly created byte array with the contents. - public byte[] ReadBytesAsArray(ref Context ctx, int length) + public int GetBytesRead() { - var array = new byte[length]; - for (var i = 0; i < array.Length; ++i) - array[i] = ReadByte(ref ctx); - return array; + return m_Context.m_ReadByteIndex - (m_Context.m_BitIndex >> 3); } - - public int GetBytesRead(ref Context ctx) - { - return ctx.m_ReadByteIndex - (ctx.m_BitIndex >> 3); - } - public int GetBitsRead(ref Context ctx) + public int GetBitsRead() { - return (ctx.m_ReadByteIndex<<3) - ctx.m_BitIndex; + return (m_Context.m_ReadByteIndex<<3) - m_Context.m_BitIndex; } - public byte ReadByte(ref Context ctx) + public byte ReadByte() { byte data; - ReadBytes(ref ctx, (byte*) &data, sizeof(byte)); + ReadBytes((byte*) &data, sizeof(byte)); return data; } - public short ReadShort(ref Context ctx) + public short ReadShort() { short data; - ReadBytes(ref ctx, (byte*) &data, sizeof(short)); + ReadBytes((byte*) &data, sizeof(short)); return data; } - public ushort ReadUShort(ref Context ctx) + public ushort ReadUShort() { ushort data; - ReadBytes(ref ctx, (byte*) &data, sizeof(ushort)); + ReadBytes((byte*) &data, sizeof(ushort)); return data; } - public int ReadInt(ref Context ctx) + public int ReadInt() { int data; - ReadBytes(ref ctx, (byte*) &data, sizeof(int)); + ReadBytes((byte*) &data, sizeof(int)); return data; } - public uint ReadUInt(ref Context ctx) + public uint ReadUInt() { uint data; - ReadBytes(ref ctx, (byte*) &data, sizeof(uint)); + ReadBytes((byte*) &data, sizeof(uint)); return data; } - public ulong ReadULong(ref Context ctx) + public ulong ReadULong() { ulong data; - ReadBytes(ref ctx, (byte*) &data, sizeof(ulong)); + ReadBytes((byte*) &data, sizeof(ulong)); return data; } - public short ReadShortNetworkByteOrder(ref Context ctx) + public short ReadShortNetworkByteOrder() { short data; - ReadBytes(ref ctx, (byte*) &data, sizeof(short)); + ReadBytes((byte*) &data, sizeof(short)); return IsLittleEndian ? ByteSwap(data) : data; } - public ushort ReadUShortNetworkByteOrder(ref Context ctx) + public ushort ReadUShortNetworkByteOrder() { - return (ushort) ReadShortNetworkByteOrder(ref ctx); + return (ushort) ReadShortNetworkByteOrder(); } - public int ReadIntNetworkByteOrder(ref Context ctx) + public int ReadIntNetworkByteOrder() { int data; - ReadBytes(ref ctx, (byte*) &data, sizeof(int)); + ReadBytes((byte*) &data, sizeof(int)); return IsLittleEndian ? ByteSwap(data) : data; } - public uint ReadUIntNetworkByteOrder(ref Context ctx) + public uint ReadUIntNetworkByteOrder() { - return (uint) ReadIntNetworkByteOrder(ref ctx); + return (uint) ReadIntNetworkByteOrder(); } - public float ReadFloat(ref Context ctx) + public float ReadFloat() { UIntFloat uf = new UIntFloat(); - uf.intValue = (uint) ReadInt(ref ctx); + uf.intValue = (uint) ReadInt(); return uf.floatValue; } - public uint ReadPackedUInt(ref Context ctx, NetworkCompressionModel model) + public uint ReadPackedUInt(NetworkCompressionModel model) { #if ENABLE_UNITY_COLLECTIONS_CHECKS AtomicSafetyHandle.CheckReadAndThrow(m_Safety); #endif - FillBitBuffer(ref ctx); + FillBitBuffer(); uint peekMask = (1u << NetworkCompressionModel.k_MaxHuffmanSymbolLength) - 1u; - uint peekBits = (uint)ctx.m_BitBuffer & peekMask; + uint peekBits = (uint)m_Context.m_BitBuffer & peekMask; ushort huffmanEntry = model.decodeTable[(int)peekBits]; int symbol = huffmanEntry >> 8; int length = huffmanEntry & 0xFF; -#if ENABLE_UNITY_COLLECTIONS_CHECKS - if (ctx.m_BitIndex < length) + if (m_Context.m_BitIndex < length) { + ++m_Context.m_FailedReads; +#if ENABLE_UNITY_COLLECTIONS_CHECKS throw new System.ArgumentOutOfRangeException(); - } +#else + return 0; #endif + } // Skip Huffman bits - ctx.m_BitBuffer >>= length; - ctx.m_BitIndex -= length; + m_Context.m_BitBuffer >>= length; + m_Context.m_BitIndex -= length; uint offset = model.bucketOffsets[symbol]; int bits = model.bucketSizes[symbol]; - return ReadRawBitsInternal(ref ctx, bits) + offset; + return ReadRawBitsInternal(bits) + offset; } - void FillBitBuffer(ref Context ctx) + void FillBitBuffer() { - while (ctx.m_BitIndex <= 56 && ctx.m_ReadByteIndex < m_Length) + while (m_Context.m_BitIndex <= 56 && m_Context.m_ReadByteIndex < m_Length) { - ctx.m_BitBuffer |= (ulong)m_bufferPtr[ctx.m_ReadByteIndex++] << ctx.m_BitIndex; - ctx.m_BitIndex += 8; + m_Context.m_BitBuffer |= (ulong)m_bufferPtr[m_Context.m_ReadByteIndex++] << m_Context.m_BitIndex; + m_Context.m_BitIndex += 8; } } - uint ReadRawBitsInternal(ref Context ctx, int numbits) + uint ReadRawBitsInternal(int numbits) { #if ENABLE_UNITY_COLLECTIONS_CHECKS if (numbits < 0 || numbits > 32) throw new ArgumentOutOfRangeException("Invalid number of bits"); - if (ctx.m_BitIndex < numbits) +#endif + if (m_Context.m_BitIndex < numbits) { + ++m_Context.m_FailedReads; +#if ENABLE_UNITY_COLLECTIONS_CHECKS throw new System.ArgumentOutOfRangeException("Not enough bits to read"); - } +#else + return 0; #endif - uint res = (uint)(ctx.m_BitBuffer & ((1UL << numbits) - 1UL)); - ctx.m_BitBuffer >>= numbits; - ctx.m_BitIndex -= numbits; + } + uint res = (uint)(m_Context.m_BitBuffer & ((1UL << numbits) - 1UL)); + m_Context.m_BitBuffer >>= numbits; + m_Context.m_BitIndex -= numbits; return res; } - public int ReadPackedInt(ref Context ctx, NetworkCompressionModel model) + public int ReadPackedInt(NetworkCompressionModel model) { - uint folded = ReadPackedUInt(ref ctx, model); + uint folded = ReadPackedUInt(model); return (int)(folded >> 1) ^ -(int)(folded & 1); // Deinterleave values from [0, -1, 1, -2, 2...] to [..., -2, -1, -0, 1, 2, ...] } - public float ReadPackedFloat(ref Context ctx, NetworkCompressionModel model) + public float ReadPackedFloat(NetworkCompressionModel model) { - return ReadPackedFloatDelta(ref ctx, 0, model); + return ReadPackedFloatDelta(0, model); } - public int ReadPackedIntDelta(ref Context ctx, int baseline, NetworkCompressionModel model) + public int ReadPackedIntDelta(int baseline, NetworkCompressionModel model) { - int delta = ReadPackedInt(ref ctx, model); + int delta = ReadPackedInt(model); return baseline - delta; } - public uint ReadPackedUIntDelta(ref Context ctx, uint baseline, NetworkCompressionModel model) + public uint ReadPackedUIntDelta(uint baseline, NetworkCompressionModel model) { - uint delta = (uint)ReadPackedInt(ref ctx, model); + uint delta = (uint)ReadPackedInt(model); return baseline - delta; } - public float ReadPackedFloatDelta(ref Context ctx, float baseline, NetworkCompressionModel model) + public float ReadPackedFloatDelta(float baseline, NetworkCompressionModel model) { #if ENABLE_UNITY_COLLECTIONS_CHECKS AtomicSafetyHandle.CheckReadAndThrow(m_Safety); #endif - FillBitBuffer(ref ctx); - if (ReadRawBitsInternal(ref ctx, 1) == 0) + FillBitBuffer(); + if (ReadRawBitsInternal(1) == 0) return baseline; var bits = 32; UIntFloat uf = new UIntFloat(); - uf.intValue = ReadRawBitsInternal(ref ctx, bits); + uf.intValue = ReadRawBitsInternal(bits); return uf.floatValue; } - public unsafe NativeString64 ReadString(ref Context ctx) + public unsafe NativeString64 ReadString() { - ushort length = ReadUShort(ref ctx); + ushort length = ReadUShort(); if (length > NativeString64.MaxLength) #if ENABLE_UNITY_COLLECTIONS_CHECKS throw new InvalidOperationException("Invalid string length"); @@ -1055,174 +768,38 @@ public unsafe NativeString64 ReadString(ref Context ctx) #endif NativeString64 str; byte* data = ((byte*)&str) + 2; + ReadBytes(data, length); *(ushort*)&str = length; - ReadBytes(ref ctx, data, length); return str; } - public unsafe NativeString64 ReadPackedStringDelta(ref Context ctx, NativeString64 baseline, NetworkCompressionModel model) + public unsafe NativeString64 ReadPackedStringDelta(NativeString64 baseline, NetworkCompressionModel model) { NativeString64 str; byte* data = ((byte*)&str) + 2; ushort baseLength = *((ushort*)&baseline); byte* baseData = ((byte*)&baseline) + 2; - uint length = ReadPackedUIntDelta(ref ctx, baseLength, model); + uint length = ReadPackedUIntDelta(baseLength, model); if (length > NativeString64.MaxLength) #if ENABLE_UNITY_COLLECTIONS_CHECKS throw new InvalidOperationException("Invalid string length"); #else return default; #endif - *(ushort*)&str = (ushort)length; if (length <= baseLength) { for (int i = 0; i < length; ++i) - data[i] = (byte)ReadPackedUIntDelta(ref ctx, baseData[i], model); + data[i] = (byte)ReadPackedUIntDelta(baseData[i], model); } else { for (int i = 0; i < baseLength; ++i) - data[i] = (byte)ReadPackedUIntDelta(ref ctx, baseData[i], model); + data[i] = (byte)ReadPackedUIntDelta(baseData[i], model); for (int i = baseLength; i < length; ++i) - data[i] = (byte)ReadPackedUInt(ref ctx, model); + data[i] = (byte)ReadPackedUInt(model); } + *(ushort*)&str = (ushort)length; return str; } } } - -namespace Unity.Networking.Transport.LowLevel.Unsafe -{ - /// - /// DataStream (Reader/Writer) unsafe utilities used to do pointer operations on streams. - /// - /// These are added to the DataStreamWriter/DataStreamReader classes as extensions, so - /// you need to add using Unity.Collections.LowLevel.Unsafe at the top - /// of file where you need to access these functions. - /// - /// Since these are unsafe C# operations care must be taken when using them, it can - /// easily crash the editor/player. - /// - /// Every time data is written directly to the data stream buffer you must call - /// WriteBytesWithUnsafePointer afterwards with the length of the data written so - /// that the stream class can internally keep track of how much of the internal - /// buffer has been written to. - /// - /// The functions have read/write access check variants which utilize the job - /// system atomic safety handle. The ENABLE_UNITY_COLLECTIONS_CHECKS define needs - /// to be used for this to work. For more information see - /// Unity.Collections.LowLevel.Unsafe.AtomicSafetyHandle. - /// - /// Example of typical usage: - /// - /// // Manually write some numbers into a data stream from a source buffer. - /// var data = new DataStreamWriter(4, Allocator.Temp); - /// unsafe - /// { - /// var ptr = data.GetUnsafePtr(); - /// var sourceData = new NativeArray<byte>(4, Allocator.Temp); - /// sourceData[0] = 42; - /// sourceData[1] = 42; - /// sourceData[2] = 42; - /// sourceData[3] = 42; - /// UnsafeUtility.MemCpy(ptr, sourceData.GetUnsafePtr(), sourceData.Length); - /// data.WriteBytesWithUnsafePointer(sourceData.Length); - /// } - /// - /// - public static class DataStreamUnsafeUtility - { -#if ENABLE_UNITY_COLLECTIONS_CHECKS - public static AtomicSafetyHandle GetAtomicSafetyHandle(DataStreamWriter strm) - { - return strm.m_Safety; - } - - public static void SetAtomicSafetyHandle(ref DataStreamWriter strm, AtomicSafetyHandle safety) - { - strm.m_Safety = safety; - } - -#endif - /// - /// Get the byte* pointer to the start of the buffer backing the DataStreamWriter. - /// A safety check is done to see if you have write access to the buffer. - /// - /// - /// Pointer to the data stream buffer. - public unsafe static byte* GetUnsafePtr(this DataStreamWriter strm) - { -#if ENABLE_UNITY_COLLECTIONS_CHECKS - AtomicSafetyHandle.CheckWriteAndThrow(strm.m_Safety); -#endif - return strm.m_Data->buffer; - } - - /// - /// Get the byte* pointer to the start of the buffer backing the DataStreamWriter. - /// A safety check is done to make sure you only have read access to the buffer. - /// - /// - /// Pointer to the data stream buffer. - public unsafe static byte* GetUnsafeReadOnlyPtr(this DataStreamWriter strm) - { -#if ENABLE_UNITY_COLLECTIONS_CHECKS - AtomicSafetyHandle.CheckReadAndThrow(strm.m_Safety); - if (strm.m_Data->bitIndex > 0) - throw new InvalidOperationException("Cannot read from a DataStreamWriter when there are pending packed writes, call Flush first"); -#endif - return strm.m_Data->buffer; - } - - /// - /// Get the byte* pointer to the buffer backing the DataStreamWriter. - /// Does not check the safety handle for read/write access. - /// - /// - /// Pointer to the data stream buffer. - unsafe public static byte* GetUnsafeBufferPointerWithoutChecks(this DataStreamWriter strm) - { - return strm.m_Data->buffer; - } - - /// - /// Get the byte* pointer to the start of the buffer backing the DataStreamReader. - /// A safety check is done to make sure you only have read access to the buffer. - /// - /// - /// Pointer to the data stream buffer. - public unsafe static byte* GetUnsafeReadOnlyPtr(this DataStreamReader strm) - { -#if ENABLE_UNITY_COLLECTIONS_CHECKS - AtomicSafetyHandle.CheckReadAndThrow(strm.m_Safety); -#endif - return strm.m_bufferPtr; - } - - /// - /// Signal how many bytes have been written to the buffer used by the data - /// stream using one of the unsafe pointer getters. - /// - /// - /// Amount of data written to the buffer. - /// If the length specified brings the total length to a value higher than the capacity of the buffer. - public unsafe static void WriteBytesWithUnsafePointer(this DataStreamWriter strm, int length) - { -#if ENABLE_UNITY_COLLECTIONS_CHECKS - AtomicSafetyHandle.CheckWriteAndThrow(strm.m_Safety); - if (strm.m_Data->length + length > strm.m_Data->capacity) - throw new ArgumentOutOfRangeException(); -#endif - strm.m_Data->length += length; - } - - public unsafe static DataStreamReader CreateReaderFromExistingData(byte* data, int length) - { - var slice = NativeSliceUnsafeUtility.ConvertExistingDataToNativeSlice(data, 1, length); -#if ENABLE_UNITY_COLLECTIONS_CHECKS - NativeSliceUnsafeUtility.SetAtomicSafetyHandle(ref slice, AtomicSafetyHandle.GetTempUnsafePtrSliceHandle()); -#endif - return new DataStreamReader(slice); - } - } -} diff --git a/Runtime/INetworkDriver.cs b/Runtime/INetworkDriver.cs deleted file mode 100644 index 655368d..0000000 --- a/Runtime/INetworkDriver.cs +++ /dev/null @@ -1,125 +0,0 @@ -using System; -using Unity.Jobs; - -namespace Unity.Networking.Transport -{ - /// - /// The NetworkDriver interface is the main entry point for the transport. - /// The Driver is similar to a UDP socket which can handle many connections. - /// - public interface INetworkDriver : IDisposable - { - // :: Driver Helpers - - bool IsCreated { get; } - - /// - /// Schedule a job to update the state of the NetworkDriver, read messages and events from the underlying - /// network interface and populate the event queues to allow reading from connections concurrently. - /// - /// - /// Used to chain dependencies for jobs. - /// - /// - /// A for the ScheduleUpdate Job. - /// - JobHandle ScheduleUpdate(JobHandle dep = default(JobHandle)); - - // :: Connection Helpers - /// - /// Bind the NetworkDriver to a port locally. This must be called before - /// the socket can listen for incoming connections. - /// - /// - /// A valid , can be implicitly cast using an System.Net.IPEndPoint - /// - /// - /// Returns 0 on Success. - /// - int Bind(NetworkEndPoint endpoint); - - /// - /// Enable listening for incoming connections on this driver. Before calling this - /// all connection attempts will be rejected. - /// - /// - /// Returns 0 on Success. - /// - int Listen(); - bool Listening { get; } - - - /// - /// Accept a pending connection attempt and get the established connection. - /// This should be called until it returns an invalid connection to make sure - /// all connections are accepted. - /// - /// - /// Returns a newly created NetworkConnection if it was Successful and a default(NetworkConnection) - /// if there where no more new NetworkConnections to accept. - /// - NetworkConnection Accept(); - - /// - /// Establish a new connection to a server with a specific address and port. - /// - /// - /// A valid NetworkEndPoint, can be implicitly cast using an System.Net.IPEndPoint - /// - NetworkConnection Connect(NetworkEndPoint endpoint); - - /// - /// Disconnect an existing connection. - /// - /// - /// Returns 0 on Success. - /// - int Disconnect(NetworkConnection con); - - /// - /// Get the state of an existing connection. If called with an invalid connection the call will return the Destroyed state. - /// - NetworkConnection.State GetConnectionState(NetworkConnection con); - - NetworkEndPoint RemoteEndPoint(NetworkConnection con); - NetworkEndPoint LocalEndPoint(); - - /// - /// Create a pipeline which can be used to process data packets sent and received by the transport package. - /// The pipelines must be created in the same order on the client and server since they are identified by - /// an index which is assigned on creation. - /// All pipelines must be created before the first connection is established. - /// - NetworkPipeline CreatePipeline(params Type[] stages); - - // :: Events - /// - /// Send a message to the specific connection. - /// - /// - /// A NetworkConnection to the endpoint you want to send to. - /// - /// - /// A valid DataStreamWriter. - /// - /// - /// Returns the size in bytes that was sent, -1 on failure. - /// - int Send(NetworkPipeline pipe, NetworkConnection con, DataStreamWriter strm); - - /// - /// Send a message to the specific connection. - /// - int Send(NetworkPipeline pipe, NetworkConnection con, IntPtr data, int len); - - /// - /// Receive an event for any connection. - /// - NetworkEvent.Type PopEvent(out NetworkConnection con, out DataStreamReader bs); - - /// - /// Receive an event for a specific connection. Should be called until it returns Empty, even if the socket is disconnected. - /// - NetworkEvent.Type PopEventForConnection(NetworkConnection con, out DataStreamReader bs); - } -} \ No newline at end of file diff --git a/Runtime/INetworkInterface.cs b/Runtime/INetworkInterface.cs index fa46dbf..c4ab5ff 100644 --- a/Runtime/INetworkInterface.cs +++ b/Runtime/INetworkInterface.cs @@ -1,15 +1,18 @@ -using System; -using Unity.Jobs; +using System; using Unity.Networking.Transport.Protocols; +using Unity.Collections; +using Unity.Jobs; +using Unity.Burst; +using Unity.Collections.LowLevel.Unsafe; namespace Unity.Networking.Transport { /// - /// The INetworkPacketReceiver is an interface for handling received packets, needed by the + /// The NetworkPacketReceiver is an interface for handling received packets, needed by the /// - public interface INetworkPacketReceiver + public struct NetworkPacketReceiver { - int ReceiveCount { get; set; } + public int ReceiveCount { get {return m_Driver.ReceiveCount;} set{m_Driver.ReceiveCount = value;} } /// /// AppendPacket is where we parse the data from the network into easy to handle events. /// @@ -17,60 +20,103 @@ public interface INetworkPacketReceiver /// The header data indicating what type of packet it is. for more information. /// The size of the payload, if any. /// - int AppendPacket(NetworkEndPoint address, UdpCHeader header, int dataLen); + public int AppendPacket(NetworkInterfaceEndPoint address, UdpCHeader header, int dataLen) + { + return m_Driver.AppendPacket(address, header, dataLen); + } /// /// Get the datastream associated with this Receiver. /// - /// Returns a - DataStreamWriter GetDataStream(); + /// Returns a NativeList of bytes + public NativeList GetDataStream() + { + return m_Driver.GetDataStream(); + } + public int GetDataStreamSize() + { + return m_Driver.GetDataStreamSize(); + } /// /// Check if the DataStreamWriter uses dynamic allocations to automatically resize the buffers or not. /// /// True if its dynamically resizing the DataStreamWriter - bool DynamicDataStreamSize(); + public bool DynamicDataStreamSize() + { + return m_Driver.DynamicDataStreamSize(); + } - int ReceiveErrorCode { set; } + public int ReceiveErrorCode { set{m_Driver.ReceiveErrorCode = value;} } + internal NetworkDriver m_Driver; } + public struct NetworkInterfaceSendHandle + { + public IntPtr data; + public int capacity; + public int size; + public int id; + } + public struct NetworkSendQueueHandle + { + private IntPtr handle; + internal static unsafe NetworkSendQueueHandle ToTempHandle(NativeQueue.ParallelWriter sendQueue) + { + void* ptr = UnsafeUtility.Malloc(UnsafeUtility.SizeOf.ParallelWriter>(), UnsafeUtility.AlignOf.ParallelWriter>(), Allocator.Temp); + UnsafeUtility.WriteArrayElement(ptr, 0, sendQueue); + return new NetworkSendQueueHandle { handle = (IntPtr)ptr }; + } + public unsafe NativeQueue.ParallelWriter FromHandle() + { + void* ptr = (void*)handle; + return UnsafeUtility.ReadArrayElement.ParallelWriter>(ptr, 0); + } + } + public struct NetworkSendInterface + { + public delegate int BeginSendMessageDelegate(out NetworkInterfaceSendHandle handle, IntPtr userData); + public delegate int EndSendMessageDelegate(ref NetworkInterfaceSendHandle handle, ref NetworkInterfaceEndPoint address, IntPtr userData, ref NetworkSendQueueHandle sendQueue); + public delegate void AbortSendMessageDelegate(ref NetworkInterfaceSendHandle handle, IntPtr userData); + public TransportFunctionPointer BeginSendMessage; + public TransportFunctionPointer EndSendMessage; + public TransportFunctionPointer AbortSendMessage; + [NativeDisableUnsafePtrRestriction] public IntPtr UserData; + } public interface INetworkInterface : IDisposable { - /// - /// The Interface Family is used to indicate what type of medium we are trying to use. See - /// - NetworkFamily Family { get; } + NetworkInterfaceEndPoint LocalEndPoint { get; } - NetworkEndPoint LocalEndPoint { get; } - NetworkEndPoint RemoteEndPoint { get; } - - void Initialize(); + void Initialize(params INetworkParameter[] param); /// /// Schedule a ReceiveJob. This is used to read data from your supported medium and pass it to the AppendData function - /// supplied by the + /// supplied by /// - /// A used to parse the data received. + /// A used to parse the data received. /// A to any dependency we might have. - /// Need to be of type /// A to our newly created ScheduleReceive Job. - JobHandle ScheduleReceive(T receiver, JobHandle dep) where T : struct, INetworkPacketReceiver; + JobHandle ScheduleReceive(NetworkPacketReceiver receiver, JobHandle dep); + + /// + /// Schedule a SendJob. This is used to flush send queues to your supported medium + /// + /// The send queue which can be used to emulate parallel send. + /// A to any dependency we might have. + /// A to our newly created ScheduleSend Job. + JobHandle ScheduleSend(NativeQueue sendQueue, JobHandle dep); /// /// Binds the medium to a specific endpoint. /// /// - /// A valid , can be implicitly cast using an . + /// A valid . /// /// 0 on Success - int Bind(NetworkEndPoint endpoint); + int Bind(NetworkInterfaceEndPoint endpoint); - /// - /// Sends a message using the underlying medium. - /// - /// An array of . - /// Lenght of the iov array passed in. - /// The address of the remote host we want to send data to. - /// - unsafe int SendMessage(network_iovec* iov, int iov_len, ref NetworkEndPoint address); + NetworkSendInterface CreateSendInterface(); + + NetworkInterfaceEndPoint CreateInterfaceEndPoint(NetworkEndPoint endPoint); + NetworkEndPoint GetGenericEndPoint(NetworkInterfaceEndPoint endpoint); } -} \ No newline at end of file +} diff --git a/Runtime/IPCManager.cs b/Runtime/IPCManager.cs index b1fec8f..c1187a1 100644 --- a/Runtime/IPCManager.cs +++ b/Runtime/IPCManager.cs @@ -1,7 +1,5 @@ using System; -#if ENABLE_UNITY_COLLECTIONS_CHECKS -using System.IO; -#endif +using System.Collections.Generic; using System.Runtime.InteropServices; using Unity.Collections; using Unity.Networking.Transport.Utilities; @@ -10,7 +8,7 @@ namespace Unity.Networking.Transport { - public struct IPCManager + internal struct IPCManager { public static IPCManager Instance = new IPCManager(); @@ -21,161 +19,128 @@ internal unsafe struct IPCData [FieldOffset(4)] public int length; [FieldOffset(8)] public fixed byte data[NetworkParameterConstants.MTU]; } - internal struct IPCQueuedMessage - { - public IPCManager.IPCData data; - public int dest; - } - - private NativeQueue m_FreeList; - private NativeList m_IPCEndPoints; private NativeMultiQueue m_IPCQueue; + private NativeHashMap m_IPCChannels; - public int QueueSize { get; private set; } internal static JobHandle ManagerAccessHandle; - public bool IsCreated => m_IPCEndPoints.IsCreated; + public bool IsCreated => m_IPCQueue.IsCreated; + + private int m_RefCount; - public void Initialize(int receiveQueueSize) + public void AddRef() { - m_IPCQueue = new NativeMultiQueue(receiveQueueSize); - m_FreeList = new NativeQueue(Allocator.Persistent); - m_IPCEndPoints = new NativeList(1, Allocator.Persistent); - QueueSize = receiveQueueSize; + if (m_RefCount == 0) + { + m_IPCQueue = new NativeMultiQueue(128); + m_IPCChannels = new NativeHashMap(64, Allocator.Persistent); + } + ++m_RefCount; } - public void Destroy() + public void Release() { - ManagerAccessHandle.Complete(); - m_IPCQueue.Dispose(); - m_FreeList.Dispose(); - m_IPCEndPoints.Dispose(); + --m_RefCount; + if (m_RefCount == 0) + { + ManagerAccessHandle.Complete(); + m_IPCQueue.Dispose(); + m_IPCChannels.Dispose(); + } } - internal void Update(NativeQueue queue) + internal unsafe void Update(NetworkInterfaceEndPoint local, NativeQueue queue) { - IPCQueuedMessage val; + QueuedSendMessage val; while (queue.TryDequeue(out val)) { - m_IPCQueue.Enqueue(val.dest, val.data); + var ipcData = new IPCData(); + UnsafeUtility.MemCpy(ipcData.data, val.Data, val.DataLength); + ipcData.length = val.DataLength; + ipcData.from = *(int*)local.data; + m_IPCQueue.Enqueue(*(int*)val.Dest.data, ipcData); } } - /// - /// Create a NetworkEndPoint for IPC. If the EndPoint is passed to Bind the driver will assume ownership, - /// otherwise the EndPoint must be destroyed by calling ReleaseEndPoint. - /// - public unsafe NetworkEndPoint CreateEndPoint(string name = null) + public unsafe NetworkInterfaceEndPoint CreateEndPoint(ushort port) { ManagerAccessHandle.Complete(); - int id; - if (!m_FreeList.TryDequeue(out id)) + int id = 0; + if (port == 0) { - id = m_IPCEndPoints.Length; - m_IPCEndPoints.Add(1); - } + var rnd = new Random(); + while (id == 0) + { + port = (ushort)rnd.Next(1, 0xffff); + int tmp; + if (!m_IPCChannels.TryGetValue(port, out tmp)) + { + id = m_IPCChannels.Length + 1; + m_IPCChannels.TryAdd(port, id); + } + } - var endpoint = new NetworkEndPoint + } + else { - Family = NetworkFamily.IPC, - ipc_handle = id, - length = 6, - nbo_port = m_IPCEndPoints[id] - }; + if (!m_IPCChannels.TryGetValue(port, out id)) + { + id = m_IPCChannels.Length + 1; + m_IPCChannels.TryAdd(port, id); + } + } + + var endpoint = default(NetworkInterfaceEndPoint); + endpoint.dataLength = 4; + *(int*) endpoint.data = id; return endpoint; } - - public unsafe void ReleaseEndPoint(NetworkEndPoint endpoint) + public unsafe bool GetEndPointPort(NetworkInterfaceEndPoint ep, out ushort port) { ManagerAccessHandle.Complete(); - if (endpoint.Family == NetworkFamily.IPC) + int id = *(int*) ep.data; + var values = m_IPCChannels.GetValueArray(Allocator.Temp); + var keys = m_IPCChannels.GetKeyArray(Allocator.Temp); + port = 0; + for (var i = 0; i < m_IPCChannels.Length; ++i) { - int handle = endpoint.ipc_handle; - m_IPCQueue.Clear(handle); - // Bump the version of the endpoint - ushort version = m_IPCEndPoints[handle]; - ++version; - if (version == 0) - version = 1; - m_IPCEndPoints[handle] = version; - - m_FreeList.Enqueue(handle); + if (values[i] == id) + { + port = keys[i]; + return true; + } } + + return false; } - public unsafe int PeekNext(NetworkEndPoint local, void* slice, out int length, out NetworkEndPoint from) + public unsafe int PeekNext(NetworkInterfaceEndPoint local, void* slice, out int length, out NetworkInterfaceEndPoint from) { ManagerAccessHandle.Complete(); IPCData data; - from = default(NetworkEndPoint); + from = default(NetworkInterfaceEndPoint); length = 0; - if (m_IPCQueue.Peek(local.ipc_handle, out data)) + if (m_IPCQueue.Peek(*(int*)local.data, out data)) { - ushort version = m_IPCEndPoints[data.from]; - UnsafeUtility.MemCpy(slice, data.data, data.length); length = data.length; } - NetworkEndPoint endpoint; - if (!TryGetEndPointByHandle(data.from, out endpoint)) - return -1; - from = endpoint; + GetEndPointByHandle(data.from, out from); return length; } - static internal unsafe int SendMessageEx(NativeQueue.ParallelWriter queue, NetworkEndPoint local, - network_iovec* iov, int iov_len, ref NetworkEndPoint address) - { -#if ENABLE_UNITY_COLLECTIONS_CHECKS - if (address.Family != NetworkFamily.IPC || local.Family != NetworkFamily.IPC || - address.nbo_port == 0 || local.nbo_port == 0) - throw new InvalidOperationException("Sending data over IPC requires both local and remote EndPoint to be valid IPC EndPoints"); -#endif - - var data = new IPCData(); - data.from = local.ipc_handle; - data.length = 0; - - for (int i = 0; i < iov_len; i++) - { -#if ENABLE_UNITY_COLLECTIONS_CHECKS - if (data.length + iov[i].len >= NetworkParameterConstants.MTU) - throw new ArgumentOutOfRangeException("Cannot send more data than an MTU"); -#endif - UnsafeUtility.MemCpy(data.data+data.length, iov[i].buf, iov[i].len); - data.length += iov[i].len; - } - queue.Enqueue(new IPCQueuedMessage {dest = address.ipc_handle, data = data}); - return data.length; - } - - public unsafe int ReceiveMessageEx(NetworkEndPoint local, network_iovec* iov, int iov_len, ref NetworkEndPoint remote) + public unsafe int ReceiveMessageEx(NetworkInterfaceEndPoint local, network_iovec* iov, int iov_len, ref NetworkInterfaceEndPoint remote) { IPCData data; - if (!m_IPCQueue.Peek(local.ipc_handle, out data)) + if (!m_IPCQueue.Peek(*(int*)local.data, out data)) return 0; - NetworkEndPoint endpoint; - if (!TryGetEndPointByHandle(data.from, out endpoint)) - return -1; -#if ENABLE_UNITY_COLLECTIONS_CHECKS - if (endpoint.Family != NetworkFamily.IPC) - throw new InvalidDataException("An incorrect message was pushed to the IPC message queue"); -#endif - -#if (UNITY_EDITOR_OSX || ((UNITY_STANDALONE_OSX || UNITY_IOS) && !UNITY_EDITOR)) - remote.family.sa_family = (byte) NetworkFamily.IPC; -#else - remote.family.sa_family = (ushort) NetworkFamily.IPC; -#endif - remote.ipc_handle = endpoint.ipc_handle; - remote.nbo_port = endpoint.nbo_port; - remote.length = 6; + GetEndPointByHandle(data.from, out remote); int totalLength = 0; for (int i = 0; i < iov_len; i++) @@ -188,25 +153,18 @@ public unsafe int ReceiveMessageEx(NetworkEndPoint local, network_iovec* iov, in if (totalLength < data.length) return -1; - m_IPCQueue.Dequeue(local.ipc_handle, out data); + m_IPCQueue.Dequeue(*(int*)local.data, out data); return totalLength; } - public unsafe bool TryGetEndPointByHandle(int handle, out NetworkEndPoint endpoint) + private unsafe void GetEndPointByHandle(int handle, out NetworkInterfaceEndPoint endpoint) { - endpoint = default(NetworkEndPoint); - if (handle >= m_IPCEndPoints.Length) - return false; - - var temp = new NetworkEndPoint(); - temp.Family = NetworkFamily.IPC; - temp.ipc_handle = handle; + var temp = default(NetworkInterfaceEndPoint); + temp.dataLength = 4; + *(int*)temp.data = handle; - temp.nbo_port = m_IPCEndPoints[handle]; endpoint = temp; - endpoint.length = 6; - return true; } } } \ No newline at end of file diff --git a/Runtime/IPCNetworkDriver.cs b/Runtime/IPCNetworkDriver.cs deleted file mode 100644 index 7a1abb5..0000000 --- a/Runtime/IPCNetworkDriver.cs +++ /dev/null @@ -1,142 +0,0 @@ -using System; -using Unity.Collections; -using Unity.Jobs; - -namespace Unity.Networking.Transport -{ - public struct LocalNetworkDriver : INetworkDriver - { - private GenericNetworkDriver m_genericDriver; - - public struct Concurrent - { - private GenericNetworkDriver.Concurrent m_genericConcurrent; - - internal Concurrent(GenericNetworkDriver.Concurrent genericConcurrent) - { - m_genericConcurrent = genericConcurrent; - } - public NetworkEvent.Type PopEventForConnection(NetworkConnection connectionId, out DataStreamReader slice) - { - return m_genericConcurrent.PopEventForConnection(connectionId, out slice); - } - - public int Send(NetworkPipeline pipe, NetworkConnection id, DataStreamWriter strm) - { - return m_genericConcurrent.Send(pipe, id, strm); - } - - public int Send(NetworkPipeline pipe, NetworkConnection id, IntPtr data, int len) - { - return m_genericConcurrent.Send(pipe, id, data, len); - } - - public NetworkConnection.State GetConnectionState(NetworkConnection id) - { - return m_genericConcurrent.GetConnectionState(id); - } - } - - public Concurrent ToConcurrent() - { - return new Concurrent(m_genericDriver.ToConcurrent()); - } - - public LocalNetworkDriver(params INetworkParameter[] param) - { - m_genericDriver = new GenericNetworkDriver(param); - } - public bool IsCreated => m_genericDriver.IsCreated; - public void Dispose() - { - m_genericDriver.Dispose(); - } - - public JobHandle ScheduleUpdate(JobHandle dep = default(JobHandle)) - { - return m_genericDriver.ScheduleUpdate(dep); - } - - public int ReceiveErrorCode => m_genericDriver.ReceiveErrorCode; - - public int Bind(NetworkEndPoint endpoint) - { - return m_genericDriver.Bind(endpoint); - } - - public int Listen() - { - return m_genericDriver.Listen(); - } - - public bool Listening => m_genericDriver.Listening; - - public NetworkConnection Accept() - { - return m_genericDriver.Accept(); - } - - public NetworkConnection Connect(NetworkEndPoint endpoint) - { - return m_genericDriver.Connect(endpoint); - } - - public int Disconnect(NetworkConnection con) - { - return m_genericDriver.Disconnect(con); - } - - public NetworkConnection.State GetConnectionState(NetworkConnection con) - { - return m_genericDriver.GetConnectionState(con); - } - - public void GetPipelineBuffers(Type pipelineType, NetworkConnection connection, - ref NativeSlice readProcessingBuffer, ref NativeSlice writeProcessingBuffer, - ref NativeSlice sharedBuffer) - { - m_genericDriver.GetPipelineBuffers(pipelineType, connection, ref readProcessingBuffer, - ref writeProcessingBuffer, ref sharedBuffer); - } - - public void GetPipelineBuffers(NetworkPipeline pipeline, int stageId, NetworkConnection connection, ref NativeSlice readProcessingBuffer, ref NativeSlice writeProcessingBuffer, ref NativeSlice sharedBuffer) - { - m_genericDriver.GetPipelineBuffers(pipeline, stageId, connection, ref readProcessingBuffer, ref writeProcessingBuffer, ref sharedBuffer); - } - - public NetworkEndPoint RemoteEndPoint(NetworkConnection con) - { - return m_genericDriver.RemoteEndPoint(con); - } - - public NetworkEndPoint LocalEndPoint() - { - return m_genericDriver.LocalEndPoint(); - } - - public NetworkPipeline CreatePipeline(params Type[] stages) - { - return m_genericDriver.CreatePipeline(stages); - } - - public int Send(NetworkPipeline pipe, NetworkConnection con, DataStreamWriter strm) - { - return m_genericDriver.Send(pipe, con, strm); - } - - public int Send(NetworkPipeline pipe, NetworkConnection con, IntPtr data, int len) - { - return m_genericDriver.Send(pipe, con, data, len); - } - - public NetworkEvent.Type PopEvent(out NetworkConnection con, out DataStreamReader bs) - { - return m_genericDriver.PopEvent(out con, out bs); - } - - public NetworkEvent.Type PopEventForConnection(NetworkConnection con, out DataStreamReader bs) - { - return m_genericDriver.PopEventForConnection(con, out bs); - } - } -} \ No newline at end of file diff --git a/Runtime/IPCNetworkInterface.cs b/Runtime/IPCNetworkInterface.cs index 6cd7780..303a1ab 100644 --- a/Runtime/IPCNetworkInterface.cs +++ b/Runtime/IPCNetworkInterface.cs @@ -3,59 +3,71 @@ using Unity.Collections; using Unity.Collections.LowLevel.Unsafe; using Unity.Jobs; -using Unity.Networking.Transport.LowLevel.Unsafe; using Unity.Networking.Transport.Protocols; namespace Unity.Networking.Transport { - public struct IPCSocket : INetworkInterface + [BurstCompile] + public struct IPCNetworkInterface : INetworkInterface { - [NativeDisableContainerSafetyRestriction] private NativeQueue m_IPCQueue; - private NativeQueue.ParallelWriter m_IPCQueueParallel; - [ReadOnly] private NativeArray m_LocalEndPoint; + [ReadOnly] private NativeArray m_LocalEndPoint; - public NetworkEndPoint LocalEndPoint => m_LocalEndPoint[0]; - public NetworkEndPoint RemoteEndPoint { get; } + public NetworkInterfaceEndPoint LocalEndPoint => m_LocalEndPoint[0]; - public NetworkFamily Family { get; } + public NetworkInterfaceEndPoint CreateInterfaceEndPoint(NetworkEndPoint endPoint) + { + if (!endPoint.IsLoopback && !endPoint.IsAny) +#if ENABLE_UNITY_COLLECTIONS_CHECKS + throw new ArgumentException("IPC network driver can only handle loopback addresses"); +#else + return default; +#endif + var endpoint = IPCManager.Instance.CreateEndPoint(endPoint.Port); + return endpoint; + } - public void Initialize() + public NetworkEndPoint GetGenericEndPoint(NetworkInterfaceEndPoint endpoint) { - m_LocalEndPoint = new NativeArray(1, Allocator.Persistent); - m_LocalEndPoint[0] = IPCManager.Instance.CreateEndPoint(); - m_IPCQueue = new NativeQueue(Allocator.Persistent); - m_IPCQueueParallel = m_IPCQueue.AsParallelWriter(); + if (!IPCManager.Instance.GetEndPointPort(endpoint, out var port)) + return default; + return NetworkEndPoint.LoopbackIpv4.WithPort(port); + } + + public void Initialize(params INetworkParameter[] param) + { + IPCManager.Instance.AddRef(); + m_LocalEndPoint = new NativeArray(1, Allocator.Persistent); + m_LocalEndPoint[0] = CreateInterfaceEndPoint(NetworkEndPoint.LoopbackIpv4); } public void Dispose() { - IPCManager.Instance.ReleaseEndPoint(m_LocalEndPoint[0]); m_LocalEndPoint.Dispose(); - m_IPCQueue.Dispose(); + IPCManager.Instance.Release(); } [BurstCompile] struct SendUpdate : IJob { public IPCManager ipcManager; - public NativeQueue ipcQueue; + public NativeQueue ipcQueue; + [ReadOnly] public NativeArray localEndPoint; public void Execute() { - ipcManager.Update(ipcQueue); + ipcManager.Update(localEndPoint[0], ipcQueue); } } [BurstCompile] - struct ReceiveJob : IJob where T : struct, INetworkPacketReceiver + struct ReceiveJob : IJob { - public T receiver; + public NetworkPacketReceiver receiver; public IPCManager ipcManager; - public NetworkEndPoint localEndPoint; + public NetworkInterfaceEndPoint localEndPoint; public unsafe void Execute() { - var address = new NetworkEndPoint {length = sizeof(NetworkEndPoint)}; var header = new UdpCHeader(); var stream = receiver.GetDataStream(); receiver.ReceiveCount = 0; @@ -63,16 +75,17 @@ public unsafe void Execute() while (true) { + int dataStreamSize = receiver.GetDataStreamSize(); if (receiver.DynamicDataStreamSize()) { - while (stream.Length+NetworkParameterConstants.MTU >= stream.Capacity) - stream.Capacity *= 2; + while (dataStreamSize+NetworkParameterConstants.MTU-UdpCHeader.Length >= stream.Length) + stream.ResizeUninitialized(stream.Length*2); } - else if (stream.Length >= stream.Capacity) + else if (dataStreamSize >= stream.Length) return; - var sliceOffset = stream.Length; - var result = NativeReceive(ref header, stream.GetUnsafePtr() + sliceOffset, - Math.Min(NetworkParameterConstants.MTU, stream.Capacity - stream.Length), ref address); + var endpoint = default(NetworkInterfaceEndPoint); + var result = NativeReceive(ref header, (byte*)stream.GetUnsafePtr() + dataStreamSize, + Math.Min(NetworkParameterConstants.MTU-UdpCHeader.Length, stream.Length - dataStreamSize), ref endpoint); if (result <= 0) { // FIXME: handle error @@ -81,11 +94,11 @@ public unsafe void Execute() return; } - receiver.ReceiveCount += receiver.AppendPacket(address, header, result); + receiver.ReceiveCount += receiver.AppendPacket(endpoint, header, result); } } - unsafe int NativeReceive(ref UdpCHeader header, void* data, int length, ref NetworkEndPoint address) + unsafe int NativeReceive(ref UdpCHeader header, void* data, int length, ref NetworkInterfaceEndPoint address) { #if ENABLE_UNITY_COLLECTIONS_CHECKS if (length <= 0) @@ -102,43 +115,73 @@ unsafe int NativeReceive(ref UdpCHeader header, void* data, int length, ref Netw iov[1].len = length; } -#if ENABLE_UNITY_COLLECTIONS_CHECKS - if (localEndPoint.Family != NetworkFamily.IPC || localEndPoint.nbo_port == 0) - throw new InvalidOperationException(); -#endif return ipcManager.ReceiveMessageEx(localEndPoint, iov, 2, ref address); } } - public JobHandle ScheduleReceive(T receiver, JobHandle dep) where T : struct, INetworkPacketReceiver + public JobHandle ScheduleReceive(NetworkPacketReceiver receiver, JobHandle dep) { - var sendJob = new SendUpdate {ipcManager = IPCManager.Instance, ipcQueue = m_IPCQueue}; - var job = new ReceiveJob - {receiver = receiver, ipcManager = IPCManager.Instance, localEndPoint = m_LocalEndPoint[0]}; + var job = new ReceiveJob + {receiver = receiver, ipcManager = IPCManager.Instance, localEndPoint = LocalEndPoint}; dep = job.Schedule(JobHandle.CombineDependencies(dep, IPCManager.ManagerAccessHandle)); - dep = sendJob.Schedule(dep); + IPCManager.ManagerAccessHandle = dep; + return dep; + } + public JobHandle ScheduleSend(NativeQueue sendQueue, JobHandle dep) + { + var sendJob = new SendUpdate {ipcManager = IPCManager.Instance, ipcQueue = sendQueue, localEndPoint = m_LocalEndPoint}; + dep = sendJob.Schedule(JobHandle.CombineDependencies(dep, IPCManager.ManagerAccessHandle)); IPCManager.ManagerAccessHandle = dep; return dep; } - public int Bind(NetworkEndPoint endpoint) + public unsafe int Bind(NetworkInterfaceEndPoint endpoint) { #if ENABLE_UNITY_COLLECTIONS_CHECKS - if (endpoint.Family != NetworkFamily.IPC || endpoint.nbo_port == 0) + if (endpoint.dataLength != 4 || *(int*)endpoint.data == 0) throw new InvalidOperationException(); #endif - IPCManager.Instance.ReleaseEndPoint(m_LocalEndPoint[0]); m_LocalEndPoint[0] = endpoint; return 0; } - public unsafe int SendMessage(network_iovec* iov, int iov_len, ref NetworkEndPoint address) + static TransportFunctionPointer BeginSendMessageFunctionPointer = new TransportFunctionPointer(BeginSendMessage); + static TransportFunctionPointer EndSendMessageFunctionPointer = new TransportFunctionPointer(EndSendMessage); + static TransportFunctionPointer AbortSendMessageFunctionPointer = new TransportFunctionPointer(AbortSendMessage); + public NetworkSendInterface CreateSendInterface() + { + return new NetworkSendInterface + { + BeginSendMessage = BeginSendMessageFunctionPointer, + EndSendMessage = EndSendMessageFunctionPointer, + AbortSendMessage = AbortSendMessageFunctionPointer, + }; + } + + [BurstCompile] + private static unsafe int BeginSendMessage(out NetworkInterfaceSendHandle handle, IntPtr userData) + { + handle.id = 0; + handle.size = 0; + handle.capacity = NetworkParameterConstants.MTU; + handle.data = (IntPtr)UnsafeUtility.Malloc(handle.capacity, 8, Allocator.Temp); + return 0; + } + + [BurstCompile] + private static unsafe int EndSendMessage(ref NetworkInterfaceSendHandle handle, ref NetworkInterfaceEndPoint address, IntPtr userData, ref NetworkSendQueueHandle sendQueueHandle) + { + var sendQueue = sendQueueHandle.FromHandle(); + var msg = default(QueuedSendMessage); + msg.Dest = address; + msg.DataLength = handle.size; + UnsafeUtility.MemCpy(msg.Data, (void*)handle.data, handle.size); + sendQueue.Enqueue(msg); + return handle.size; + } + [BurstCompile] + private static void AbortSendMessage(ref NetworkInterfaceSendHandle handle, IntPtr userData) { -#if ENABLE_UNITY_COLLECTIONS_CHECKS - if (m_LocalEndPoint[0].Family != NetworkFamily.IPC || m_LocalEndPoint[0].nbo_port == 0) - throw new InvalidOperationException(); -#endif - return IPCManager.SendMessageEx(m_IPCQueueParallel, m_LocalEndPoint[0], iov, iov_len, ref address); } } } \ No newline at end of file diff --git a/Runtime/NetworkConnection.cs b/Runtime/NetworkConnection.cs index f5d883e..c5acb7f 100644 --- a/Runtime/NetworkConnection.cs +++ b/Runtime/NetworkConnection.cs @@ -1,3 +1,5 @@ +using Unity.Collections; + namespace Unity.Networking.Transport { /// @@ -28,7 +30,7 @@ public enum State /// Disconnects a virtual connection and marks it for deletion. This connection will be removed on next the next frame. /// /// The driver that owns the virtual connection. - public int Disconnect(T driver) where T : struct, INetworkDriver + public int Disconnect(NetworkDriver driver) { return driver.Disconnect(this); } @@ -40,31 +42,16 @@ public int Disconnect(T driver) where T : struct, INetworkDriver /// A DataStreamReader, that will only be populated if a /// event was received. /// - public NetworkEvent.Type PopEvent(T driver, out DataStreamReader stream) where T : struct, INetworkDriver + public NetworkEvent.Type PopEvent(NetworkDriver driver, out DataStreamReader stream) { return driver.PopEventForConnection(this, out stream); } - /// - /// Send data to the remote endpoint. - /// - /// The driver that owns the virtual connection. - /// A valid filled with the data you want to send. - public int Send(T driver, DataStreamWriter bs) where T : struct, INetworkDriver - { - return driver.Send(NetworkPipeline.Null, this, bs); - } - - public int Send(T driver, NetworkPipeline pipeline, DataStreamWriter bs) where T : struct, INetworkDriver - { - return driver.Send(pipeline, this, bs); - } - /// /// Close an active NetworkConnection, similar to . /// /// The driver that owns the virtual connection. - public int Close(T driver) where T : struct, INetworkDriver + public int Close(NetworkDriver driver) { if (m_NetworkId >= 0) return driver.Disconnect(this); @@ -75,13 +62,13 @@ public int Close(T driver) where T : struct, INetworkDriver /// Check to see if a NetworkConnection is Created. /// /// `true` if the NetworkConnection has been correctly created by a call to - /// or + /// or public bool IsCreated { get { return m_NetworkVersion != 0; } } - public State GetState(T driver) where T : struct, INetworkDriver + public State GetState(NetworkDriver driver) { return driver.GetConnectionState(this); } diff --git a/Runtime/NetworkDriver.cs b/Runtime/NetworkDriver.cs index 4bf05f4..6bf7979 100644 --- a/Runtime/NetworkDriver.cs +++ b/Runtime/NetworkDriver.cs @@ -1,143 +1,239 @@ +#if UNITY_2020_1_OR_NEWER +#define UNITY_TRANSPORT_ENABLE_BASELIB +#endif using System; using System.Diagnostics; +using System.Collections.Generic; using Unity.Burst; -using Unity.Networking.Transport.LowLevel.Unsafe; using Unity.Collections; using Unity.Collections.LowLevel.Unsafe; using Unity.Networking.Transport.Protocols; using Unity.Jobs; +using Unity.Jobs.LowLevel.Unsafe; using UnityEngine.Assertions; using Random = System.Random; namespace Unity.Networking.Transport { + public unsafe struct QueuedSendMessage + { + public fixed byte Data[NetworkParameterConstants.MTU]; + public NetworkInterfaceEndPoint Dest; + public int DataLength; + } /// - /// The GenericNetworkDriver is an implementation of Virtual Connections over any transport. + /// The NetworkDriver is an implementation of Virtual Connections over any transport. /// /// Basic usage: /// - /// var driver = new GenericNetworkDriver<IPv4UDPSocket>(new INetworkParameter[0]); + /// var driver = new NetworkDriver.Create(); /// /// - /// Should be of type , the currently available NetworkInterfaces - /// are , or . /// - public struct GenericNetworkDriver : INetworkDriver, INetworkPacketReceiver, INetworkPipelineReceiver where T : struct, INetworkInterface where TNetworkPipelineStageCollection : struct, INetworkPipelineStageCollection + public struct NetworkDriver : IDisposable { /// /// Create a Concurrent Copy of the NetworkDriver. /// public Concurrent ToConcurrent() { - Concurrent concurrent; - concurrent.m_EventQueue = m_EventQueue.ToConcurrent(); - concurrent.m_ConnectionList = m_ConnectionList; - concurrent.m_DataStream = m_DataStream; - concurrent.m_NetworkInterface = m_NetworkInterface; - concurrent.m_PipelineProcessor = m_PipelineProcessor.ToConcurrent(); - concurrent.m_DefaultHeaderFlags = m_DefaultHeaderFlags; - concurrent.m_Logger = m_Logger.ToConcurrent(); - return concurrent; + return new Concurrent + { + m_NetworkSendInterface = m_NetworkSendInterface, + m_EventQueue = m_EventQueue.ToConcurrent(), + m_ConnectionList = m_ConnectionList, + m_DataStream = m_DataStream, + m_PipelineProcessor = m_PipelineProcessor.ToConcurrent(), + m_DefaultHeaderFlags = m_DefaultHeaderFlags, + m_Logger = m_Logger.ToConcurrent(), + m_ConcurrentParallelSendQueue = m_ParallelSendQueue.AsParallelWriter(), +#if ENABLE_UNITY_COLLECTIONS_CHECKS + m_ThreadIndex = 0, + m_PendingBeginSend = m_PendingBeginSend +#endif + }; } private Concurrent ToConcurrentSendOnly() { - Concurrent concurrent; - concurrent.m_EventQueue = default(NetworkEventQueue.Concurrent); - concurrent.m_ConnectionList = m_ConnectionList; - concurrent.m_DataStream = default(DataStreamWriter); - concurrent.m_NetworkInterface = m_NetworkInterface; - concurrent.m_PipelineProcessor = m_PipelineProcessor.ToConcurrent(); - concurrent.m_DefaultHeaderFlags = m_DefaultHeaderFlags; - concurrent.m_Logger = m_Logger.ToConcurrent(); - return concurrent; + return new Concurrent + { + m_NetworkSendInterface = m_NetworkSendInterface, + m_EventQueue = default, + m_ConnectionList = m_ConnectionList, + m_DataStream = m_DataStream, + m_PipelineProcessor = m_PipelineProcessor.ToConcurrent(), + m_DefaultHeaderFlags = m_DefaultHeaderFlags, + m_Logger = m_Logger.ToConcurrent(), + m_ConcurrentParallelSendQueue = m_ParallelSendQueue.AsParallelWriter(), +#if ENABLE_UNITY_COLLECTIONS_CHECKS + m_ThreadIndex = 0, + m_PendingBeginSend = m_PendingBeginSend +#endif + }; } /// /// The Concurrent struct is used to create an Concurrent instance of the GenericNetworkDriver. /// - public struct Concurrent : INetworkPipelineSender + public struct Concurrent { public NetworkLogger.Concurrent m_Logger; - public NetworkEvent.Type PopEventForConnection(NetworkConnection connectionId, out DataStreamReader slice) + public NetworkEvent.Type PopEventForConnection(NetworkConnection connectionId, out DataStreamReader reader) { int offset, size; - slice = default(DataStreamReader); + reader = default(DataStreamReader); if (connectionId.m_NetworkId < 0 || connectionId.m_NetworkId >= m_ConnectionList.Length || m_ConnectionList[connectionId.m_NetworkId].Version != connectionId.m_NetworkVersion) return (int) NetworkEvent.Type.Empty; var type = m_EventQueue.PopEventForConnection(connectionId.m_NetworkId, out offset, out size); if (size > 0) - slice = new DataStreamReader(m_DataStream, offset, size); + reader = new DataStreamReader(((NativeArray)m_DataStream).GetSubArray(offset, size)); return type; } - public unsafe int Send(NetworkPipeline pipe, NetworkConnection id, DataStreamWriter strm) + struct PendingSend { - if (strm.IsCreated && strm.Length > 0) - { - return Send(pipe, id, (IntPtr) strm.GetUnsafeReadOnlyPtr(), strm.Length); - } - return 0; + public NetworkPipeline Pipeline; + public NetworkConnection Connection; + public NetworkInterfaceSendHandle SendHandle; + public int headerSize; } - - public unsafe int Send(NetworkPipeline pipe, NetworkConnection id, IntPtr data, int len) + public DataStreamWriter BeginSend(NetworkConnection id) + { + return BeginSend(NetworkPipeline.Null, id); + } + public unsafe DataStreamWriter BeginSend(NetworkPipeline pipe, NetworkConnection id) { - if (pipe.Id > 0) - { - m_DefaultHeaderFlags = UdpCHeader.HeaderFlags.HasPipeline; - var slice = NativeSliceUnsafeUtility.ConvertExistingDataToNativeSlice((void*) data, 1, len); -#if ENABLE_UNITY_COLLECTIONS_CHECKS - var safety = AtomicSafetyHandle.Create(); - AtomicSafetyHandle.SetAllowSecondaryVersionWriting(safety, false); - AtomicSafetyHandle.UseSecondaryVersion(ref safety); - NativeSliceUnsafeUtility.SetAtomicSafetyHandle(ref slice, safety); -#endif - var retval = m_PipelineProcessor.Send(this, pipe, id, slice); -#if ENABLE_UNITY_COLLECTIONS_CHECKS - AtomicSafetyHandle.Release(safety); -#endif - m_DefaultHeaderFlags = 0; - return retval; - } if (id.m_NetworkId < 0 || id.m_NetworkId >= m_ConnectionList.Length) - return 0; + return default; var connection = m_ConnectionList[id.m_NetworkId]; if (connection.Version != id.m_NetworkVersion) - return 0; + return default; #if ENABLE_UNITY_COLLECTIONS_CHECKS if (connection.State == NetworkConnection.State.Connecting) throw new InvalidOperationException("Cannot send data while connecting"); #endif - // update last attempt; - var header = new UdpCHeader + NetworkInterfaceSendHandle sendHandle; + if (m_NetworkSendInterface.BeginSendMessage.Ptr.Invoke(out sendHandle, m_NetworkSendInterface.UserData) != 0) + return default; + var header = (UdpCHeader*) sendHandle.data; + *header = new UdpCHeader { - Type = (int) UdpCProtocol.Data, + Type = (byte) UdpCProtocol.Data, SessionToken = connection.SendToken, Flags = m_DefaultHeaderFlags }; + var headerSize = UnsafeUtility.SizeOf(); + if (pipe.Id > 0) + { + header->Flags |= UdpCHeader.HeaderFlags.HasPipeline; + // All headers plus one byte for pipeline id + headerSize += m_PipelineProcessor.SendHeaderCapacity(pipe) + 1; + } + var footerSize = 0; + if (connection.DidReceiveData == 0) + { + header->Flags |= UdpCHeader.HeaderFlags.HasConnectToken; + footerSize = 2; + } + if (headerSize + footerSize >= sendHandle.capacity) + throw new InvalidOperationException("Package could not fit any data"); - var iov = stackalloc network_iovec[3]; - iov[0].buf = header.Data; - iov[0].len = UdpCHeader.Length; - - iov[1].buf = (void*)data; - iov[1].len = len; - - iov[2].buf = &connection.ReceiveToken; - iov[2].len = 2; + var slice = NativeArrayUnsafeUtility.ConvertExistingDataToNativeArray((byte*) sendHandle.data+headerSize, sendHandle.capacity - headerSize - footerSize, Allocator.Invalid); +#if ENABLE_UNITY_COLLECTIONS_CHECKS + var safety = AtomicSafetyHandle.GetTempMemoryHandle(); + NativeArrayUnsafeUtility.SetAtomicSafetyHandle(ref slice, safety); +#endif + var writer = new DataStreamWriter(slice); + writer.m_SendHandleData = (IntPtr)UnsafeUtility.Malloc(UnsafeUtility.SizeOf(), UnsafeUtility.AlignOf(), Allocator.Temp); + *(PendingSend*)writer.m_SendHandleData = new PendingSend + { + Pipeline = pipe, + Connection = id, + SendHandle = sendHandle, + headerSize = headerSize, + }; +#if ENABLE_UNITY_COLLECTIONS_CHECKS + m_PendingBeginSend[m_ThreadIndex * JobsUtility.CacheLineSize/4] = m_PendingBeginSend[m_ThreadIndex * JobsUtility.CacheLineSize/4] + 1; +#endif + return writer; + } + public unsafe int EndSend(DataStreamWriter writer) + { +#if ENABLE_UNITY_COLLECTIONS_CHECKS + // Just here to trigger a safety check on the writer + if (writer.Capacity == 0) + throw new InvalidOperationException("EndSend without matching BeginSend"); +#endif + PendingSend* pendingSendPtr = (PendingSend*)writer.m_SendHandleData; + if (pendingSendPtr == null || pendingSendPtr->Connection == default) + throw new InvalidOperationException("EndSend without matching BeginSend"); + if (m_ConnectionList[pendingSendPtr->Connection.m_NetworkId].Version != pendingSendPtr->Connection.m_NetworkVersion) + throw new InvalidOperationException("Connection closed between begin and end send"); + + PendingSend pendingSend = *(PendingSend*)writer.m_SendHandleData; + pendingSendPtr->Connection = default; +#if ENABLE_UNITY_COLLECTIONS_CHECKS + m_PendingBeginSend[m_ThreadIndex * JobsUtility.CacheLineSize/4] = m_PendingBeginSend[m_ThreadIndex * JobsUtility.CacheLineSize/4] - 1; +#endif - if (connection.DidReceiveData == 0) + pendingSend.SendHandle.size = pendingSend.headerSize + writer.Length; + int retval = 0; + if (pendingSend.Pipeline.Id > 0) { - header.Flags |= UdpCHeader.HeaderFlags.HasConnectToken; - return m_NetworkInterface.SendMessage(iov, 3, ref connection.Address); + var oldHeaderFlags = m_DefaultHeaderFlags; + m_DefaultHeaderFlags = UdpCHeader.HeaderFlags.HasPipeline; + retval = m_PipelineProcessor.Send(this, pendingSend.Pipeline, pendingSend.Connection, pendingSend.SendHandle, pendingSend.headerSize); + m_DefaultHeaderFlags = oldHeaderFlags; } - return m_NetworkInterface.SendMessage(iov, 2, ref connection.Address); + else + retval = CompleteSend(pendingSend.Connection, pendingSend.SendHandle); + if (retval <= 0) + return retval; + return writer.Length; } - public unsafe int Send(NetworkConnection id, network_iovec* dataIov, int dataIovLen) + public unsafe void AbortSend(DataStreamWriter writer) + { +#if ENABLE_UNITY_COLLECTIONS_CHECKS + // Just here to trigger a safety check on the writer + if (writer.Capacity == 0) + throw new InvalidOperationException("EndSend without matching BeginSend"); +#endif + PendingSend* pendingSendPtr = (PendingSend*)writer.m_SendHandleData; + if (pendingSendPtr == null || pendingSendPtr->Connection == default) + throw new InvalidOperationException("EndSend without matching BeginSend"); + PendingSend pendingSend = *(PendingSend*)writer.m_SendHandleData; + pendingSendPtr->Connection = default; +#if ENABLE_UNITY_COLLECTIONS_CHECKS + m_PendingBeginSend[m_ThreadIndex * JobsUtility.CacheLineSize/4] = m_PendingBeginSend[m_ThreadIndex * JobsUtility.CacheLineSize/4] - 1; +#endif + AbortSend(pendingSend.SendHandle); + } + internal unsafe int CompleteSend(NetworkConnection sendConnection, NetworkInterfaceSendHandle sendHandle) + { + var connection = m_ConnectionList[sendConnection.m_NetworkId]; + if ((((UdpCHeader*)sendHandle.data)->Flags & UdpCHeader.HeaderFlags.HasConnectToken) != 0) + { +#if ENABLE_UNITY_COLLECTIONS_CHECKS + if (sendHandle.size + 2 > sendHandle.capacity) + throw new InvalidOperationException("SendHandle capacity overflow"); +#endif + UnsafeUtility.MemCpy((byte*)sendHandle.data + sendHandle.size, &connection.ReceiveToken, 2); + sendHandle.size += 2; + } + var queueHandle = NetworkSendQueueHandle.ToTempHandle(m_ConcurrentParallelSendQueue); + return m_NetworkSendInterface.EndSendMessage.Ptr.Invoke(ref sendHandle, ref connection.Address, m_NetworkSendInterface.UserData, ref queueHandle); + } + internal void AbortSend(NetworkInterfaceSendHandle sendHandle) + { + m_NetworkSendInterface.AbortSendMessage.Ptr.Invoke(ref sendHandle, m_NetworkSendInterface.UserData); + } + + internal unsafe int Send(NetworkConnection id, network_iovec* dataIov, int dataIovLen) { if (id.m_NetworkId < 0 || id.m_NetworkId >= m_ConnectionList.Length) return 0; @@ -151,29 +247,40 @@ public unsafe int Send(NetworkConnection id, network_iovec* dataIov, int dataIov #endif // update last attempt; - var header = new UdpCHeader + NetworkInterfaceSendHandle sendHandle; + if (m_NetworkSendInterface.BeginSendMessage.Ptr.Invoke(out sendHandle, m_NetworkSendInterface.UserData) != 0) + return -1; + byte* packet = (byte*) sendHandle.data; + sendHandle.size = UdpCHeader.Length; + if (sendHandle.size > sendHandle.capacity) + return -1; + var header = (UdpCHeader*) packet; + *header = new UdpCHeader { - Type = (int) UdpCProtocol.Data, + Type = (byte) UdpCProtocol.Data, SessionToken = connection.SendToken, Flags = m_DefaultHeaderFlags }; - - var iov = stackalloc network_iovec[2+dataIovLen]; - iov[0].buf = header.Data; - iov[0].len = UdpCHeader.Length; - + packet += UdpCHeader.Length; for (int i = 0; i < dataIovLen; ++i) - iov[1 + i] = dataIov[i]; - - iov[1 + dataIovLen].buf = &connection.ReceiveToken; - iov[1 + dataIovLen].len = 2; + { + sendHandle.size += dataIov[i].len; + if (sendHandle.size > sendHandle.capacity) + return -1; + UnsafeUtility.MemCpy(packet, dataIov[i].buf, dataIov[i].len); + packet += dataIov[i].len; + } if (connection.DidReceiveData == 0) { - header.Flags |= UdpCHeader.HeaderFlags.HasConnectToken; - return m_NetworkInterface.SendMessage(iov, 2 + dataIovLen, ref connection.Address); + header->Flags |= UdpCHeader.HeaderFlags.HasConnectToken; + sendHandle.size += 2; + if (sendHandle.size > sendHandle.capacity) + return -1; + UnsafeUtility.MemCpy(packet, &connection.ReceiveToken, 2); } - return m_NetworkInterface.SendMessage(iov, 1 + dataIovLen, ref connection.Address); + var queueHandle = NetworkSendQueueHandle.ToTempHandle(m_ConcurrentParallelSendQueue); + return m_NetworkSendInterface.EndSendMessage.Ptr.Invoke(ref sendHandle, ref connection.Address, m_NetworkSendInterface.UserData, ref queueHandle); } public NetworkConnection.State GetConnectionState(NetworkConnection id) { @@ -185,17 +292,23 @@ public NetworkConnection.State GetConnectionState(NetworkConnection id) return connection.State; } + internal NetworkSendInterface m_NetworkSendInterface; internal NetworkEventQueue.Concurrent m_EventQueue; [ReadOnly] internal NativeList m_ConnectionList; - [ReadOnly] internal DataStreamWriter m_DataStream; - internal T m_NetworkInterface; - internal NetworkPipelineProcessor.Concurrent m_PipelineProcessor; + [ReadOnly] internal NativeList m_DataStream; + internal NetworkPipelineProcessor.Concurrent m_PipelineProcessor; internal UdpCHeader.HeaderFlags m_DefaultHeaderFlags; + internal NativeQueue.ParallelWriter m_ConcurrentParallelSendQueue; + +#if ENABLE_UNITY_COLLECTIONS_CHECKS + [NativeSetThreadIndex] internal int m_ThreadIndex; + [NativeDisableParallelForRestriction] internal NativeArray m_PendingBeginSend; +#endif } internal struct Connection { - public NetworkEndPoint Address; + public NetworkInterfaceEndPoint Address; public long LastAttempt; public int Id; public int Version; @@ -234,11 +347,17 @@ public bool Equals(Connection connection) } // internal variables ::::::::::::::::::::::::::::::::::::::::::::::::: - T m_NetworkInterface; + static List s_NetworkInterfaces = new List(); + int m_NetworkInterfaceIndex; + NetworkSendInterface m_NetworkSendInterface; + NativeQueue m_ParallelSendQueue; +#if ENABLE_UNITY_COLLECTIONS_CHECKS + NativeArray m_PendingBeginSend; +#endif NetworkEventQueue m_EventQueue; - private NetworkLogger m_Logger; + NetworkLogger m_Logger; NativeQueue m_FreeList; NativeQueue m_NetworkAcceptQueue; @@ -281,8 +400,9 @@ public Parameters(params INetworkParameter[] param) #pragma warning restore 649 private Parameters m_NetworkParams; - private DataStreamWriter m_DataStream; - private NetworkPipelineProcessor m_PipelineProcessor; + private NativeList m_DataStream; + private NativeArray m_DataStreamSize; + private NetworkPipelineProcessor m_PipelineProcessor; private UdpCHeader.HeaderFlags m_DefaultHeaderFlags; private long m_updateTime; @@ -313,6 +433,7 @@ enum StringType DisconnectWithPipeline, AcceptWithoutToken, ImplicitAcceptWithoutToken, + MissingEndSend, NumStrings } @@ -331,9 +452,19 @@ enum StringType "Received an invalid Disconnect with pipeline", "Received an invalid ConnectionAccept without a token", "Received an invalid implicit connection accept without a token", + "Missing EndSend, calling BeginSend without calling EndSend will result in a memory leak", }; [ReadOnly] private NativeArray m_StringDB; + + public static NetworkDriver Create(params INetworkParameter[] param) + { +#if UNITY_TRANSPORT_ENABLE_BASELIB + return new NetworkDriver(new BaselibNetworkInterface(), param); +#else + return new NetworkDriver(new UDPNetworkInterface(), param); +#endif + } /// /// Constructor for GenericNetworkDriver. /// @@ -341,8 +472,29 @@ enum StringType /// An array of INetworkParameter. There are currently only two , /// the and the . /// - public GenericNetworkDriver(params INetworkParameter[] param) + public NetworkDriver(INetworkInterface netIf, params INetworkParameter[] param) { + m_NetworkInterfaceIndex = -1; + for (int i = 0; i < s_NetworkInterfaces.Count; ++i) + { + if (s_NetworkInterfaces[i] == null) + { + m_NetworkInterfaceIndex = i; + s_NetworkInterfaces[i] = netIf; + break; + } + } + if (m_NetworkInterfaceIndex < 0) + { + m_NetworkInterfaceIndex = s_NetworkInterfaces.Count; + s_NetworkInterfaces.Add(netIf); + } + netIf.Initialize(param); + m_NetworkSendInterface = netIf.CreateSendInterface(); + m_ParallelSendQueue = new NativeQueue(Allocator.Persistent); +#if ENABLE_UNITY_COLLECTIONS_CHECKS + m_PendingBeginSend = new NativeArray(JobsUtility.MaxJobThreadCount * JobsUtility.CacheLineSize/4, Allocator.Persistent); +#endif m_Logger = new NetworkLogger(NetworkLogger.LogLevel.Debug); m_StringDB = new NativeArray((int)StringType.NumStrings, Allocator.Persistent); if (StringValue.Length != (int)StringType.NumStrings) @@ -351,21 +503,20 @@ public GenericNetworkDriver(params INetworkParameter[] param) { m_StringDB[i] = new NativeString512(StringValue[i]); } - m_updateTime = Stopwatch.GetTimestamp() / TimeSpan.TicksPerMillisecond; - m_updateTimeAdjustment = 0; m_NetworkParams = new Parameters(param); + m_updateTime = m_NetworkParams.config.fixedFrameTimeMS > 0 ? 1 : Stopwatch.GetTimestamp() / TimeSpan.TicksPerMillisecond; + m_updateTimeAdjustment = 0; int initialStreamSize = m_NetworkParams.dataStream.size; if (initialStreamSize == 0) initialStreamSize = NetworkParameterConstants.DriverDataStreamSize; - m_DataStream = new DataStreamWriter(initialStreamSize, Allocator.Persistent); - m_PipelineProcessor = new NetworkPipelineProcessor(param); // Initial capacity might need to be bigger than 0 + m_DataStream = new NativeList(initialStreamSize, Allocator.Persistent); + m_DataStream.ResizeUninitialized(initialStreamSize); + m_DataStreamSize = new NativeArray(1, Allocator.Persistent); + m_PipelineProcessor = new NetworkPipelineProcessor(param); // Initial capacity might need to be bigger than 0 m_DefaultHeaderFlags = 0; - m_NetworkInterface = new T(); - m_NetworkInterface.Initialize(); - m_NetworkAcceptQueue = new NativeQueue(Allocator.Persistent); m_ConnectionList = new NativeList(1, Allocator.Persistent); @@ -387,10 +538,12 @@ public GenericNetworkDriver(params INetworkParameter[] param) // interface implementation ::::::::::::::::::::::::::::::::::::::::::: public void Dispose() { + s_NetworkInterfaces[m_NetworkInterfaceIndex].Dispose(); + s_NetworkInterfaces[m_NetworkInterfaceIndex] = null; m_Logger.Dispose(); m_StringDB.Dispose(); - m_NetworkInterface.Dispose(); m_DataStream.Dispose(); + m_DataStreamSize.Dispose(); m_PipelineProcessor.Dispose(); m_EventQueue.Dispose(); @@ -403,6 +556,10 @@ public void Dispose() m_ReceiveCount.Dispose(); m_SessionIdCounter.Dispose(); m_ErrorCodes.Dispose(); + m_ParallelSendQueue.Dispose(); +#if ENABLE_UNITY_COLLECTIONS_CHECKS + m_PendingBeginSend.Dispose(); +#endif } public bool IsCreated => m_InternalState.IsCreated; @@ -410,7 +567,7 @@ public void Dispose() [BurstCompile] struct UpdateJob : IJob { - public GenericNetworkDriver driver; + public NetworkDriver driver; public void Execute() { @@ -455,11 +612,13 @@ public void Print(ref NativeString512 msg) #endif struct ClearEventQueue : IJob { - public DataStreamWriter dataStream; + public NativeList dataStream; + public NativeArray dataStreamSize; public NetworkEventQueue eventQueue; #if ENABLE_UNITY_COLLECTIONS_CHECKS public NetworkLogger logger; public NativeArray stringDB; + public NativeArray pendingSend; [ReadOnly] public NativeList connectionList; [ReadOnly] public NativeArray internalState; #endif @@ -475,15 +634,31 @@ public void Execute() logger.Log(NetworkLogger.LogLevel.Error, msg); } } + bool didPrint = false; + for (int i = 0; i < JobsUtility.MaxJobThreadCount; ++i) + { + if (pendingSend[i * JobsUtility.CacheLineSize / 4] > 0) + { + pendingSend[i * JobsUtility.CacheLineSize / 4] = 0; + if (!didPrint) + { + logger.Log(NetworkLogger.LogLevel.Error, stringDB[(int)StringType.MissingEndSend]); + didPrint = true; + } + } + } #endif eventQueue.Clear(); - dataStream.Clear(); + dataStream.ResizeUninitialized(dataStream.Length); + dataStreamSize[0] = 0; } } + public long LastUpdateTime => m_updateTime; public JobHandle ScheduleUpdate(JobHandle dep = default(JobHandle)) { - long timeNow = Stopwatch.GetTimestamp() / TimeSpan.TicksPerMillisecond - m_updateTimeAdjustment; + long timeNow = m_NetworkParams.config.fixedFrameTimeMS > 0 ? m_updateTime + m_NetworkParams.config.fixedFrameTimeMS : + Stopwatch.GetTimestamp() / TimeSpan.TicksPerMillisecond - m_updateTimeAdjustment; if (m_NetworkParams.config.maxFrameTimeMS > 0 && (timeNow - m_updateTime) > m_NetworkParams.config.maxFrameTimeMS) { m_updateTimeAdjustment += (timeNow - m_updateTime) - m_NetworkParams.config.maxFrameTimeMS; @@ -495,21 +670,28 @@ public void Execute() var clearJob = new ClearEventQueue { dataStream = m_DataStream, + dataStreamSize = m_DataStreamSize, eventQueue = m_EventQueue, #if ENABLE_UNITY_COLLECTIONS_CHECKS logger = m_Logger, stringDB = m_StringDB, + pendingSend = m_PendingBeginSend, connectionList = m_ConnectionList, internalState = m_InternalState #endif }; handle = clearJob.Schedule(dep); handle = job.Schedule(handle); - handle = m_NetworkInterface.ScheduleReceive(this, handle); + handle = s_NetworkInterfaces[m_NetworkInterfaceIndex].ScheduleReceive(new NetworkPacketReceiver{m_Driver = this}, handle); + handle = s_NetworkInterfaces[m_NetworkInterfaceIndex].ScheduleSend(m_ParallelSendQueue, handle); var logJob = new LogJob {logger = m_Logger}; handle = logJob.Schedule(handle); return handle; } + public JobHandle ScheduleFlushSend(JobHandle dep) + { + return s_NetworkInterfaces[m_NetworkInterfaceIndex].ScheduleSend(m_ParallelSendQueue, dep); + } struct PipelineOverflowMessage : INetworkLogMessage { @@ -541,7 +723,7 @@ void InternalUpdate() m_PipelineProcessor.UpdateReceive(this, out updateCount); // TODO: Find a good way to establish a good limit (connections*pipelines/2?) - if (updateCount > 64) + if (updateCount > (m_ConnectionList.Length - m_FreeList.Count) * 64) { var msg = new PipelineOverflowMessage { stringDB = m_StringDB, count = updateCount }; m_Logger.Log(NetworkLogger.LogLevel.Warning, msg); @@ -549,7 +731,7 @@ void InternalUpdate() m_DefaultHeaderFlags = UdpCHeader.HeaderFlags.HasPipeline; m_PipelineProcessor.UpdateSend(ToConcurrentSendOnly(), out updateCount); - if (updateCount > 64) + if (updateCount > (m_ConnectionList.Length - m_FreeList.Count) * 64) { var msg = new PipelineOverflowMessage { stringDB = m_StringDB, count = updateCount }; m_Logger.Log(NetworkLogger.LogLevel.Warning, msg); @@ -573,6 +755,7 @@ public NetworkPipeline CreatePipeline(params Type[] stages) public int Bind(NetworkEndPoint endpoint) { + var ifEndPoint = s_NetworkInterfaces[m_NetworkInterfaceIndex].CreateInterfaceEndPoint(endpoint); #if ENABLE_UNITY_COLLECTIONS_CHECKS if (!m_InternalState.IsCreated) throw new InvalidOperationException( @@ -584,7 +767,7 @@ public int Bind(NetworkEndPoint endpoint) throw new InvalidOperationException( "Bind cannot be called after establishing connections"); #endif - var ret = m_NetworkInterface.Bind(endpoint); + var ret = s_NetworkInterfaces[m_NetworkInterfaceIndex].Bind(ifEndPoint); if (ret == 0) m_InternalState[InternalStateBound] = 1; return ret; @@ -622,6 +805,7 @@ public NetworkConnection Accept() public NetworkConnection Connect(NetworkEndPoint endpoint) { + var ifEndPoint = s_NetworkInterfaces[m_NetworkInterfaceIndex].CreateInterfaceEndPoint(endpoint); #if ENABLE_UNITY_COLLECTIONS_CHECKS if (!m_InternalState.IsCreated) throw new InvalidOperationException( @@ -640,7 +824,7 @@ public NetworkConnection Connect(NetworkEndPoint endpoint) Id = id, Version = ver, State = NetworkConnection.State.Connecting, - Address = endpoint, + Address = ifEndPoint, Attempts = 1, LastAttempt = m_updateTime, SendToken = 0, @@ -658,22 +842,31 @@ public NetworkConnection Connect(NetworkEndPoint endpoint) void SendConnectionRequest(Connection c) { - var header = new UdpCHeader - { - Type = (int) UdpCProtocol.ConnectionRequest, - SessionToken = c.ReceiveToken, - Flags = m_DefaultHeaderFlags - }; - unsafe { - network_iovec iov; - iov.buf = header.Data; - iov.len = UdpCHeader.Length; - if (m_NetworkInterface.SendMessage(&iov, 1, ref c.Address) <= 0) + NetworkInterfaceSendHandle sendHandle; + if (m_NetworkSendInterface.BeginSendMessage.Ptr.Invoke(out sendHandle, m_NetworkSendInterface.UserData) != 0) { - m_Logger.Log(NetworkLogger.LogLevel.Error, m_StringDB[(int)StringType.ConnectionRequestSendError]); + m_Logger.Log(NetworkLogger.LogLevel.Error, m_StringDB[(int) StringType.ConnectionRequestSendError]); + return; } + + byte* packet = (byte*) sendHandle.data; + sendHandle.size = UdpCHeader.Length; + if (sendHandle.size > sendHandle.capacity) + { + m_Logger.Log(NetworkLogger.LogLevel.Error, m_StringDB[(int) StringType.ConnectionRequestSendError]); + return; + } + var header = (UdpCHeader*) packet; + *header = new UdpCHeader + { + Type = (byte) UdpCProtocol.ConnectionRequest, + SessionToken = c.ReceiveToken, + Flags = m_DefaultHeaderFlags + }; + var queueHandle = NetworkSendQueueHandle.ToTempHandle(m_ParallelSendQueue.AsParallelWriter()); + m_NetworkSendInterface.EndSendMessage.Ptr.Invoke(ref sendHandle, ref c.Address, m_NetworkSendInterface.UserData, ref queueHandle); } } @@ -695,14 +888,12 @@ public int Disconnect(NetworkConnection id) return 0; } - public void GetPipelineBuffers(Type pipelineType, NetworkConnection connection, ref NativeSlice readProcessingBuffer, ref NativeSlice writeProcessingBuffer, ref NativeSlice sharedBuffer) + public void GetPipelineBuffers(NetworkPipeline pipeline, NetworkPipelineStageId stageId, NetworkConnection connection, out NativeArray readProcessingBuffer, out NativeArray writeProcessingBuffer, out NativeArray sharedBuffer) { - m_PipelineProcessor.GetPipelineBuffers(pipelineType, connection, ref readProcessingBuffer, ref writeProcessingBuffer, ref sharedBuffer); - } - - public void GetPipelineBuffers(NetworkPipeline pipeline, int stageId, NetworkConnection connection, ref NativeSlice readProcessingBuffer, ref NativeSlice writeProcessingBuffer, ref NativeSlice sharedBuffer) - { - m_PipelineProcessor.GetPipelineBuffers(pipeline, stageId, connection, ref readProcessingBuffer, ref writeProcessingBuffer, ref sharedBuffer); + if (connection.m_NetworkId < 0 || connection.m_NetworkId >= m_ConnectionList.Length || + m_ConnectionList[connection.m_NetworkId].Version != connection.m_NetworkVersion) + throw new InvalidOperationException("Invalid connection"); + m_PipelineProcessor.GetPipelineBuffers(pipeline, stageId, connection, out readProcessingBuffer, out writeProcessingBuffer, out sharedBuffer); } public NetworkConnection.State GetConnectionState(NetworkConnection con) @@ -716,56 +907,61 @@ public NetworkConnection.State GetConnectionState(NetworkConnection con) public NetworkEndPoint RemoteEndPoint(NetworkConnection id) { if (id == default(NetworkConnection)) - return m_NetworkInterface.RemoteEndPoint; + return default(NetworkEndPoint); Connection connection; if ((connection = GetConnection(id)) == Connection.Null) return default(NetworkEndPoint); - return connection.Address; + return s_NetworkInterfaces[m_NetworkInterfaceIndex].GetGenericEndPoint(connection.Address); } public NetworkEndPoint LocalEndPoint() { - return m_NetworkInterface.LocalEndPoint; + var ep = s_NetworkInterfaces[m_NetworkInterfaceIndex].LocalEndPoint; + return s_NetworkInterfaces[m_NetworkInterfaceIndex].GetGenericEndPoint(ep); } - public int Send(NetworkPipeline pipe, NetworkConnection id, DataStreamWriter strm) + public DataStreamWriter BeginSend(NetworkPipeline pipe, NetworkConnection id) { - unsafe - { - return Send(pipe, id, (IntPtr) strm.GetUnsafeReadOnlyPtr(), strm.Length); - } + return ToConcurrentSendOnly().BeginSend(pipe, id); } - - public unsafe int Send(NetworkPipeline pipe, NetworkConnection id, IntPtr data, int len) + public DataStreamWriter BeginSend(NetworkConnection id) { - return ToConcurrentSendOnly().Send(pipe, id, data, len); + return ToConcurrentSendOnly().BeginSend(NetworkPipeline.Null, id); + } + public int EndSend(DataStreamWriter writer) + { + return ToConcurrentSendOnly().EndSend(writer); + } + public void AbortSend(DataStreamWriter writer) + { + ToConcurrentSendOnly().AbortSend(writer); } - public NetworkEvent.Type PopEvent(out NetworkConnection con, out DataStreamReader slice) + public NetworkEvent.Type PopEvent(out NetworkConnection con, out DataStreamReader reader) { int offset, size; - slice = default(DataStreamReader); + reader = default(DataStreamReader); int id; var type = m_EventQueue.PopEvent(out id, out offset, out size); if (size > 0) - slice = new DataStreamReader(m_DataStream, offset, size); + reader = new DataStreamReader(((NativeArray)m_DataStream).GetSubArray(offset, size)); con = id < 0 ? default(NetworkConnection) : new NetworkConnection {m_NetworkId = id, m_NetworkVersion = m_ConnectionList[id].Version}; return type; } - public NetworkEvent.Type PopEventForConnection(NetworkConnection connectionId, out DataStreamReader slice) + public NetworkEvent.Type PopEventForConnection(NetworkConnection connectionId, out DataStreamReader reader) { int offset, size; - slice = default(DataStreamReader); + reader = default(DataStreamReader); if (connectionId.m_NetworkId < 0 || connectionId.m_NetworkId >= m_ConnectionList.Length || m_ConnectionList[connectionId.m_NetworkId].Version != connectionId.m_NetworkVersion) return (int) NetworkEvent.Type.Empty; var type = m_EventQueue.PopEventForConnection(connectionId.m_NetworkId, out offset, out size); if (size > 0) - slice = new DataStreamReader(m_DataStream, offset, size); + reader = new DataStreamReader(((NativeArray)m_DataStream).GetSubArray(offset, size)); return type; } @@ -788,7 +984,7 @@ Connection GetConnection(NetworkConnection id) return con; } - Connection GetConnection(NetworkEndPoint address, ushort sessionId) + Connection GetConnection(NetworkInterfaceEndPoint address, ushort sessionId) { for (int i = 0; i < m_ConnectionList.Length; i++) { @@ -799,7 +995,7 @@ Connection GetConnection(NetworkEndPoint address, ushort sessionId) return Connection.Null; } - Connection GetNewConnection(NetworkEndPoint address, ushort sessionId) + Connection GetNewConnection(NetworkInterfaceEndPoint address, ushort sessionId) { for (int i = 0; i < m_ConnectionList.Length; i++) { @@ -842,24 +1038,31 @@ bool UpdateConnection(Connection connection) unsafe int SendPacket(UdpCProtocol type, Connection connection) { - var header = new UdpCHeader + NetworkInterfaceSendHandle sendHandle; + if (m_NetworkSendInterface.BeginSendMessage.Ptr.Invoke(out sendHandle, m_NetworkSendInterface.UserData) != 0) + return -1; + byte* packet = (byte*) sendHandle.data; + sendHandle.size = UdpCHeader.Length; + if (sendHandle.size > sendHandle.capacity) + return -1; + var header = (UdpCHeader*) packet; + *header = new UdpCHeader { Type = (byte) type, SessionToken = connection.SendToken, Flags = m_DefaultHeaderFlags }; - - var iov = stackalloc network_iovec[2]; - iov[0].buf = header.Data; - iov[0].len = UdpCHeader.Length; - iov[1].buf = &connection.ReceiveToken; - iov[1].len = 2; if (connection.DidReceiveData == 0) { - header.Flags |= UdpCHeader.HeaderFlags.HasConnectToken; - return m_NetworkInterface.SendMessage(iov, 2, ref connection.Address); + header->Flags |= UdpCHeader.HeaderFlags.HasConnectToken; + packet += sendHandle.size; + sendHandle.size += 2; + if (sendHandle.size > sendHandle.capacity) + return -1; + UnsafeUtility.MemCpy(packet, &connection.ReceiveToken, 2); } - return m_NetworkInterface.SendMessage(iov, 1, ref connection.Address); + var queueHandle = NetworkSendQueueHandle.ToTempHandle(m_ParallelSendQueue.AsParallelWriter()); + return m_NetworkSendInterface.EndSendMessage.Ptr.Invoke(ref sendHandle, ref connection.Address, m_NetworkSendInterface.UserData, ref queueHandle); } int SendPacket(UdpCProtocol type, NetworkConnection id) @@ -917,11 +1120,6 @@ void CheckTimeouts() } } - public DataStreamWriter GetDataStream() - { - return m_DataStream; - } - struct ReceiveErrorMessage : INetworkLogMessage { public NativeArray stringDB; @@ -936,7 +1134,7 @@ public void Print(ref NativeString512 msg) public int ReceiveErrorCode { get { return m_ErrorCodes[(int)ErrorCodeType.ReceiveError]; } - set + internal set { if (value != 0) { @@ -948,18 +1146,28 @@ public int ReceiveErrorCode } } + // Interface for receiving packages from a NetworkInterface + internal NativeList GetDataStream() + { + return m_DataStream; + } + internal int GetDataStreamSize() + { + return m_DataStreamSize[0]; + } + private NativeArray m_ReceiveCount; - public int ReceiveCount { + internal int ReceiveCount { get { return m_ReceiveCount[0]; } set { m_ReceiveCount[0] = value; } } - public bool DynamicDataStreamSize() + internal bool DynamicDataStreamSize() { return m_NetworkParams.dataStream.size == 0; } - public unsafe int AppendPacket(NetworkEndPoint address, UdpCHeader header, int dataLen) + internal int AppendPacket(NetworkInterfaceEndPoint address, UdpCHeader header, int dataLen) { int count = 0; switch ((UdpCProtocol) header.Type) @@ -1048,12 +1256,9 @@ public unsafe int AppendPacket(NetworkEndPoint address, UdpCHeader header, int d if (c.State == NetworkConnection.State.Connecting) { - var sliceOffset = m_DataStream.Length; - m_DataStream.WriteBytesWithUnsafePointer(2); - var dataStreamReader = new DataStreamReader(m_DataStream, sliceOffset, 2); - var context = default(DataStreamReader.Context); - c.SendToken = dataStreamReader.ReadUShort(ref context); - m_DataStream.WriteBytesWithUnsafePointer(-2); + var tokenOffset = m_DataStreamSize[0]; + var dataStreamReader = new DataStreamReader(((NativeArray)m_DataStream).GetSubArray(tokenOffset, 2)); + c.SendToken = dataStreamReader.ReadUShort(); c.State = NetworkConnection.State.Connected; UpdateConnection(c); @@ -1099,12 +1304,9 @@ public unsafe int AppendPacket(NetworkEndPoint address, UdpCHeader header, int d return 0; } - var tokenOffset = m_DataStream.Length + length - 2; - m_DataStream.WriteBytesWithUnsafePointer(length); - var dataStreamReader = new DataStreamReader(m_DataStream, tokenOffset, 2); - var context = default(DataStreamReader.Context); - c.SendToken = dataStreamReader.ReadUShort(ref context); - m_DataStream.WriteBytesWithUnsafePointer(-length); + var tokenOffset = m_DataStreamSize[0] + length - 2; + var dataStreamReader = new DataStreamReader(((NativeArray)m_DataStream).GetSubArray(tokenOffset, 2)); + c.SendToken = dataStreamReader.ReadUShort(); c.State = NetworkConnection.State.Connected; UpdateConnection(c); @@ -1118,13 +1320,13 @@ public unsafe int AppendPacket(NetworkEndPoint address, UdpCHeader header, int d if ((header.Flags & UdpCHeader.HeaderFlags.HasConnectToken) != 0) length -= 2; - var sliceOffset = m_DataStream.Length; - m_DataStream.WriteBytesWithUnsafePointer(length); + var sliceOffset = m_DataStreamSize[0]; + m_DataStreamSize[0] = sliceOffset + length; if ((header.Flags & UdpCHeader.HeaderFlags.HasPipeline) != 0) { var netCon = new NetworkConnection {m_NetworkId = c.Id, m_NetworkVersion = c.Version}; - m_PipelineProcessor.Receive(this, netCon, m_DataStream.GetNativeSlice(sliceOffset, length)); + m_PipelineProcessor.Receive(this, netCon, ((NativeArray)m_DataStream).GetSubArray(sliceOffset, length)); return 0; } @@ -1143,12 +1345,12 @@ public unsafe int AppendPacket(NetworkEndPoint address, UdpCHeader header, int d return count; } - public unsafe void PushDataEvent(NetworkConnection con, NativeSlice data) + // Interface for receiving data from a pipeline + internal unsafe void PushDataEvent(NetworkConnection con, byte* dataPtr, int dataLength) { - byte* dataPtr = (byte*)data.GetUnsafeReadOnlyPtr(); - byte* streamBasePtr = m_DataStream.GetUnsafePtr(); + byte* streamBasePtr = (byte*)m_DataStream.GetUnsafePtr(); int sliceOffset = 0; - if (dataPtr >= streamBasePtr && dataPtr + data.Length <= streamBasePtr + m_DataStream.Length) + if (dataPtr >= streamBasePtr && dataPtr + dataLength <= streamBasePtr + m_DataStreamSize[0]) { // Pointer is a subset of our receive buffer, no need to copy sliceOffset = (int)(dataPtr - streamBasePtr); @@ -1157,15 +1359,15 @@ public unsafe void PushDataEvent(NetworkConnection con, NativeSlice data) { if (DynamicDataStreamSize()) { - while (m_DataStream.Length + data.Length >= m_DataStream.Capacity) - m_DataStream.Capacity *= 2; + while (m_DataStreamSize[0] + dataLength >= m_DataStream.Length) + m_DataStream.ResizeUninitialized(m_DataStream.Length * 2); } - else if (m_DataStream.Length + data.Length >= m_DataStream.Capacity) + else if (m_DataStreamSize[0] + dataLength >= m_DataStream.Length) return; // FIXME: how do we signal this error? - sliceOffset = m_DataStream.Length; - UnsafeUtility.MemCpy(streamBasePtr + sliceOffset, dataPtr, data.Length); - m_DataStream.WriteBytesWithUnsafePointer(data.Length); + sliceOffset = m_DataStreamSize[0]; + UnsafeUtility.MemCpy(streamBasePtr + sliceOffset, dataPtr, dataLength); + m_DataStreamSize[0] = sliceOffset + dataLength; } m_EventQueue.PushEvent(new NetworkEvent @@ -1173,7 +1375,7 @@ public unsafe void PushDataEvent(NetworkConnection con, NativeSlice data) connectionId = con.m_NetworkId, type = NetworkEvent.Type.Data, offset = sliceOffset, - size = data.Length + size = dataLength }); } } diff --git a/Runtime/NetworkEndPoint.cs b/Runtime/NetworkEndPoint.cs index 684e26d..ff78462 100644 --- a/Runtime/NetworkEndPoint.cs +++ b/Runtime/NetworkEndPoint.cs @@ -1,108 +1,204 @@ +#if UNITY_2020_1_OR_NEWER +#define UNITY_TRANSPORT_ENABLE_BASELIB +#endif using System; using System.Runtime.InteropServices; +using Unity.Collections; using Unity.Collections.LowLevel.Unsafe; +using Unity.Mathematics; +#if UNITY_TRANSPORT_ENABLE_BASELIB +using Unity.Baselib; +using Unity.Baselib.LowLevel; +#endif namespace Unity.Networking.Transport { /// /// NetworkFamily indicates what type of underlying medium we are using. - /// Currently supported are and /// public enum NetworkFamily { - UdpIpv4 = 2,//AddressFamily.InterNetwork, - IPC = 0//AddressFamily.Unspecified + Invalid = 0, + Ipv4 = 2, + Ipv6 = 23 } - /// - /// The NetworkEndPoint is our representation of the type. - /// - [StructLayout(LayoutKind.Explicit)] + [StructLayout(LayoutKind.Sequential)] public unsafe struct NetworkEndPoint { - internal const int Length = 28; - [FieldOffset(0)] internal fixed byte data[Length]; - [FieldOffset(0)] internal sa_family_t family; - [FieldOffset(2)] internal ushort nbo_port; - [FieldOffset(4)] internal int ipc_handle; - [FieldOffset(28)] internal int length; - - private static bool IsLittleEndian = true; + enum AddressType { Any = 0, Loopback = 1 } + private const int rawIpv4Length = 4; + private const int rawIpv6Length = 16; + private const int rawDataLength = 16; // Maximum space needed to hold a IPv6 Address + private const int rawLength = rawDataLength + 3; // SizeOf + private static readonly bool IsLittleEndian = true; + +#if UNITY_TRANSPORT_ENABLE_BASELIB + internal Binding.Baselib_NetworkAddress rawNetworkAddress; +#else + [StructLayout(LayoutKind.Explicit)] + public struct RawNetworkAddress + { + [FieldOffset(0)] public fixed byte data[19]; + [FieldOffset(0)] public fixed byte ipv6[16]; + [FieldOffset(0)] public fixed byte ipv4_bytes[4]; + [FieldOffset(0)] public uint ipv4; + [FieldOffset(16)] public ushort port; + [FieldOffset(18)] public byte family; + } + public RawNetworkAddress rawNetworkAddress; +#endif + public int length; + static NetworkEndPoint() { uint test = 1; - unsafe - { - byte* test_b = (byte*) &test; - IsLittleEndian = test_b[0] == 1; - } + byte* test_b = (byte*) &test; + IsLittleEndian = test_b[0] == 1; } - private static ushort ByteSwap(ushort val) +#if UNITY_TRANSPORT_ENABLE_BASELIB + public ushort Port { - return (ushort)(((val & 0xff) << 8) | (val >> 8)); + get => (ushort) (rawNetworkAddress.port1 | (rawNetworkAddress.port0 << 8)); + set + { + rawNetworkAddress.port0 = (byte)((value >> 8) & 0xff); + rawNetworkAddress.port1 = (byte)(value & 0xff); + } } - private static uint ByteSwap(uint val) + + public NetworkFamily Family { - return (uint)(((val & 0xff) << 24) |((val&0xff00)<<8) | ((val>>8)&0xff00) | (val >> 24)); + get => FromBaselibFamily((Binding.Baselib_NetworkAddress_Family)rawNetworkAddress.family); + set => rawNetworkAddress.family = (byte)ToBaselibFamily(value); } - public ushort Port +#else + public ushort Port { - get { return IsLittleEndian ? ByteSwap(nbo_port) : nbo_port; } - set { nbo_port = IsLittleEndian ? ByteSwap(value) : value; } + get => IsLittleEndian ? ByteSwap(rawNetworkAddress.port) : rawNetworkAddress.port; + set => rawNetworkAddress.port = IsLittleEndian ? ByteSwap(value) : value; } public NetworkFamily Family { - get => (NetworkFamily) family.sa_family; -#if (UNITY_EDITOR_OSX || ((UNITY_STANDALONE_OSX || UNITY_IOS) && !UNITY_EDITOR)) - set => family.sa_family = (byte) value; -#else - set => family.sa_family = (ushort)value; + get => (NetworkFamily) rawNetworkAddress.family; + set => rawNetworkAddress.family = (byte) value; + } #endif + + public NativeArray GetRawAddressBytes() + { + if (Family == NetworkFamily.Ipv4) + { + var bytes = new NativeArray(4, Allocator.Temp); + UnsafeUtility.MemCpy(bytes.GetUnsafePtr(), UnsafeUtility.AddressOf(ref rawNetworkAddress), rawIpv4Length); + return bytes; + } + else if (Family == NetworkFamily.Ipv6) + { + var bytes = new NativeArray(16, Allocator.Temp); + UnsafeUtility.MemCpy(bytes.GetUnsafePtr(), UnsafeUtility.AddressOf(ref rawNetworkAddress), rawIpv6Length); + return bytes; + } + return default; } - public bool IsValid => Family != 0; + public void SetRawAddressBytes(NativeArray bytes, NetworkFamily family = NetworkFamily.Ipv4) + { + if (family == NetworkFamily.Ipv4 && bytes.Length== rawIpv4Length) + { + UnsafeUtility.MemCpy(UnsafeUtility.AddressOf(ref rawNetworkAddress), bytes.GetUnsafeReadOnlyPtr(), rawIpv4Length); + } + else if (family == NetworkFamily.Ipv6 && bytes.Length == rawIpv6Length) + { + UnsafeUtility.MemCpy(UnsafeUtility.AddressOf(ref rawNetworkAddress), bytes.GetUnsafeReadOnlyPtr(), rawIpv6Length); + } + else + { + if (family == NetworkFamily.Ipv4 && bytes.Length != rawIpv4Length) + throw new InvalidOperationException("Bad input length, a ipv4 address is 4 bytes long"); + if (family == NetworkFamily.Ipv6 && bytes.Length == rawIpv6Length) + throw new InvalidOperationException("Bad input length, a ipv6 address is 16 bytes long"); + } + } - public static NetworkEndPoint CreateIpv4(uint ip, ushort port) + public ushort RawPort { - if (IsLittleEndian) + get { - port = ByteSwap(port); - ip = ByteSwap(ip); + ushort *port = (ushort*)((byte*) UnsafeUtility.AddressOf(ref rawNetworkAddress) + rawDataLength); + return *port; } + set + { + ushort *port = (ushort*)((byte*) UnsafeUtility.AddressOf(ref rawNetworkAddress) + rawDataLength); + *port = value; + } + } + + public string Address => AddressAsString(); + + public bool IsValid => Family != 0; + + public static NetworkEndPoint AnyIpv4 => CreateAddress(0); + public static NetworkEndPoint LoopbackIpv4 => CreateAddress(0, AddressType.Loopback); - var sai = new sockaddr_in(); +#if UNITY_TRANSPORT_ENABLE_BASELIB + public static NetworkEndPoint AnyIpv6 => CreateAddress(0, AddressType.Any, NetworkFamily.Ipv6); + public static NetworkEndPoint LoopbackIpv6 => CreateAddress(0, AddressType.Loopback, NetworkFamily.Ipv6); +#endif -#if (UNITY_EDITOR_OSX || ((UNITY_STANDALONE_OSX || UNITY_IOS) && !UNITY_EDITOR)) - sai.sin_family.sa_family = (byte) NetworkFamily.UdpIpv4; - sai.sin_family.sa_len = (byte) sizeof(sockaddr_in); + public NetworkEndPoint WithPort(ushort port) + { + var ep = this; + ep.Port = port; + return ep; + } +#if UNITY_TRANSPORT_ENABLE_BASELIB + public bool IsLoopback => (this == LoopbackIpv4.WithPort(Port)) || (this == LoopbackIpv6.WithPort(Port)); + public bool IsAny => (this == AnyIpv4.WithPort(Port)) || (this == AnyIpv6.WithPort(Port)); #else - sai.sin_family.sa_family = (ushort) NetworkFamily.UdpIpv4; + public bool IsLoopback => this == LoopbackIpv4.WithPort(Port); + public bool IsAny => this == AnyIpv4.WithPort(Port); #endif - sai.sin_port = port; - sai.sin_addr.s_addr = ip; - var len = sizeof(sockaddr_in); - var address = new NetworkEndPoint - { - length = len - }; + // Returns true if we can fully parse the input and return a valid endpoint +#if UNITY_TRANSPORT_ENABLE_BASELIB + public static bool TryParse(string address, ushort port, out NetworkEndPoint endpoint, NetworkFamily family = NetworkFamily.Ipv4) + { + UnsafeUtility.SizeOf(); + endpoint = default(NetworkEndPoint); - UnsafeUtility.MemCpy(address.data, sai.data, len); - return address; - } + var errorState = default(ErrorState); + var ipBytes = System.Text.Encoding.UTF8.GetBytes(address + char.MinValue); - public static NetworkEndPoint AnyIpv4 => CreateIpv4(0, 0); - public static NetworkEndPoint LoopbackIpv4 => CreateIpv4((127<<24) | 1, 0); + fixed (byte* ipBytesPtr = ipBytes) + fixed (Binding.Baselib_NetworkAddress* rawAddress = &endpoint.rawNetworkAddress) + { + Binding.Baselib_NetworkAddress_Encode( + rawAddress, + ToBaselibFamily(family), + ipBytesPtr, + (ushort) port, + errorState.NativeErrorStatePtr); + } - // Returns true if we can fully parse the input and return a valid endpoint - public static bool TryParse(string ip, ushort port, out NetworkEndPoint endpoint) + if (errorState.ErrorCode != Binding.Baselib_ErrorCode.Success) + { + return false; + } + return endpoint.IsValid; + } +#else + public static bool TryParse(string address, ushort port, out NetworkEndPoint endpoint, NetworkFamily family = NetworkFamily.Ipv4) { endpoint = default(NetworkEndPoint); + if (family != NetworkFamily.Ipv4) + return false; // Parse failure check - if (ip == null || ip.Length < 7) + if (address == null || address.Length < 7) return false; uint ipaddr = 0; @@ -112,14 +208,14 @@ public static bool TryParse(string ip, ushort port, out NetworkEndPoint endpoint for (var part = 0; part < 4; ++part) { // Parse failure check - if (pos >= ip.Length || ip[pos] < '0' || ip[pos] > '9') + if (pos >= address.Length || address[pos] < '0' || address[pos] > '9') return false; uint byteVal = 0; - while (pos < ip.Length && ip[pos] >= '0' && ip[pos] <= '9') + while (pos < address.Length && address[pos] >= '0' && address[pos] <= '9') { - byteVal = byteVal * 10 + (uint)(ip[pos] - '0'); + byteVal = byteVal * 10 + (uint)(address[pos] - '0'); ++pos; } @@ -129,37 +225,39 @@ public static bool TryParse(string ip, ushort port, out NetworkEndPoint endpoint ipaddr = (ipaddr << 8) | byteVal; - if (pos < ip.Length && ip[pos] == '.') + if (pos < address.Length && address[pos] == '.') ++pos; } - if (pos + 1 < ip.Length && ip[pos] == ':') + if (pos + 1 < address.Length && address[pos] == ':') { ++pos; uint customPort = 0; - while (pos < ip.Length && ip[pos] >= '0' && ip[pos] <= '9') + while (pos < address.Length && address[pos] >= '0' && address[pos] <= '9') { - customPort = customPort * 10 + (uint)(ip[pos] - '0'); + customPort = customPort * 10 + (uint)(address[pos] - '0'); ++pos; } if (customPort > ushort.MaxValue) return false; - + port = (ushort)customPort; } - endpoint = CreateIpv4(ipaddr, port); + endpoint = CreateAddress(port); + endpoint.rawNetworkAddress.ipv4 = IsLittleEndian ? ByteSwap(ipaddr) : ipaddr; + return endpoint.IsValid; } - +#endif // Returns a default address if parsing fails - public static NetworkEndPoint Parse(string ip, ushort port) + public static NetworkEndPoint Parse(string address, ushort port, NetworkFamily family = NetworkFamily.Ipv4) { - if (TryParse(ip, port, out var endpoint)) + if (TryParse(address, port, out var endpoint, family)) return endpoint; - return CreateIpv4(0, port); + return default; } public static bool operator ==(NetworkEndPoint lhs, NetworkEndPoint rhs) @@ -177,6 +275,125 @@ public override bool Equals(object other) return this == (NetworkEndPoint) other; } + public override int GetHashCode() + { + var p = (byte*) UnsafeUtility.AddressOf(ref rawNetworkAddress); + unchecked + { + var result = 0; + + for (int i = 0; i < rawLength; i++) + { + result = (result * 31) ^ (int) (IntPtr) (p + 1); + } + + return result; + } + } + + bool Compare(NetworkEndPoint other) + { + var p = (byte*) UnsafeUtility.AddressOf(ref rawNetworkAddress); + var p1 = (byte*) UnsafeUtility.AddressOf(ref other.rawNetworkAddress); + if (UnsafeUtility.MemCmp(p, p1, rawLength) == 0) + return true; + + return false; + } + + private string AddressAsString() + { + return string.Empty; + } + + private static ushort ByteSwap(ushort val) + { + return (ushort) (((val & 0xff) << 8) | (val >> 8)); + } + + private static uint ByteSwap(uint val) + { + return (uint) (((val & 0xff) << 24) | ((val & 0xff00) << 8) | ((val >> 8) & 0xff00) | (val >> 24)); + } + + static NetworkEndPoint CreateAddress(ushort port, AddressType type = AddressType.Any, NetworkFamily family = NetworkFamily.Ipv4) + { + if (family == NetworkFamily.Invalid) + return default; + + uint ipv4Loopback = (127 << 24) | 1; + + if (IsLittleEndian) + { + port = ByteSwap(port); + ipv4Loopback = ByteSwap(ipv4Loopback); + } + + var ep = new NetworkEndPoint + { + Family = family, + RawPort = port, + length = rawLength + }; + + if (type == AddressType.Loopback) + { + if (family == NetworkFamily.Ipv4) + { + *(uint*) UnsafeUtility.AddressOf(ref ep.rawNetworkAddress) = ipv4Loopback; + } +#if UNITY_TRANSPORT_ENABLE_BASELIB + else if (family == NetworkFamily.Ipv6) + { + ep.rawNetworkAddress.data15 = 1; + } +#endif + } + return ep; + } + +#if UNITY_TRANSPORT_ENABLE_BASELIB + static NetworkFamily FromBaselibFamily(Binding.Baselib_NetworkAddress_Family family) + { + if (family == Binding.Baselib_NetworkAddress_Family.IPv4) + return NetworkFamily.Ipv4; + if (family == Binding.Baselib_NetworkAddress_Family.IPv6) + return NetworkFamily.Ipv6; + return NetworkFamily.Invalid; + } + static Binding.Baselib_NetworkAddress_Family ToBaselibFamily(NetworkFamily family) + { + if (family == NetworkFamily.Ipv4) + return Binding.Baselib_NetworkAddress_Family.IPv4; + if (family == NetworkFamily.Ipv6) + return Binding.Baselib_NetworkAddress_Family.IPv6; + return Binding.Baselib_NetworkAddress_Family.Invalid; + } +#endif + } + + public unsafe struct NetworkInterfaceEndPoint + { + public int dataLength; + public fixed byte data[56]; + + public bool IsValid => dataLength != 0; + + public static bool operator ==(NetworkInterfaceEndPoint lhs, NetworkInterfaceEndPoint rhs) + { + return lhs.Compare(rhs); + } + + public static bool operator !=(NetworkInterfaceEndPoint lhs, NetworkInterfaceEndPoint rhs) + { + return !lhs.Compare(rhs); + } + + public override bool Equals(object other) + { + return this == (NetworkInterfaceEndPoint) other; + } + public override int GetHashCode() { fixed (byte* p = data) @@ -184,7 +401,7 @@ public override int GetHashCode() { var result = 0; - for (int i = 0; i < Length; i++) + for (int i = 0; i < dataLength; i++) { result = (result * 31) ^ (int)(IntPtr) (p + 1); } @@ -193,14 +410,21 @@ public override int GetHashCode() } } - bool Compare(NetworkEndPoint other) + bool Compare(NetworkInterfaceEndPoint other) { - if (length != other.length) +// Workaround for baselib issue on Posix + if (dataLength != other.dataLength) + { +#if UNITY_TRANSPORT_ENABLE_BASELIB + if (dataLength <= 0 || other.dataLength <= 0) + return false; +#else return false; - +#endif + } fixed (void* p = this.data) { - if (UnsafeUtility.MemCmp(p, other.data, length) == 0) + if (UnsafeUtility.MemCmp(p, other.data, math.min(dataLength, other.dataLength)) == 0) return true; } diff --git a/Runtime/NetworkParams.cs b/Runtime/NetworkParams.cs index d007a96..b6a9329 100644 --- a/Runtime/NetworkParams.cs +++ b/Runtime/NetworkParams.cs @@ -31,9 +31,9 @@ public struct NetworkParameterConstants } /// - /// The NetworkDataStreamParameter is used to set the initial data stream size. + /// The NetworkDataStreamParameter is used to set a fixed data stream size. /// - /// The can grow on demand so its not necessary to set this value. + /// The will grow on demand if the size is set to zero. public struct NetworkDataStreamParameter : INetworkParameter { /// Size of the default @@ -55,5 +55,8 @@ public struct NetworkConfigParameter : INetworkParameter /// The maximum amount of time a single frame can advance timeout values. /// The main use for this parameter is to not get disconnects at frame spikes when both endpoints lives in the same process. public int maxFrameTimeMS; + /// A fixed amount of time to use for an interval between ScheduleUpdate. This is used instead of a clock. + /// The main use for this parameter is tests where determinism is more important than correctness. + public int fixedFrameTimeMS; } } \ No newline at end of file diff --git a/Runtime/NetworkPipeline.cs b/Runtime/NetworkPipeline.cs index adcc3bf..008389c 100644 --- a/Runtime/NetworkPipeline.cs +++ b/Runtime/NetworkPipeline.cs @@ -1,70 +1,151 @@ using System; using System.Threading; -using Unity.Networking.Transport.LowLevel.Unsafe; using Unity.Collections; using Unity.Collections.LowLevel.Unsafe; -using UnityEngine; +using Unity.Burst; +using System.Collections.Generic; +using Unity.Networking.Transport.Protocols; namespace Unity.Networking.Transport { - public interface INetworkPipelineSender + public unsafe struct InboundSendBuffer { - unsafe int Send(NetworkConnection con, network_iovec* iov, int iovLen); + public byte* buffer; + public byte* bufferWithHeaders; + public int bufferLength; + public int bufferWithHeadersLength; + public int headerPadding; + + public void SetBufferFrombufferWithHeaders() + { +#if ENABLE_UNITY_COLLECTIONS_CHECKS + if (bufferWithHeadersLength < headerPadding) + throw new IndexOutOfRangeException("Buffer is too small to fit headers"); +#endif + buffer = bufferWithHeaders + headerPadding; + bufferLength = bufferWithHeadersLength - headerPadding; + } } - public interface INetworkPipelineReceiver + public unsafe struct InboundRecvBuffer { - void PushDataEvent(NetworkConnection con, NativeSlice data); - } + public byte* buffer; + public int bufferLength; - public struct InboundBufferVec - { - public NativeSlice buffer1; - public NativeSlice buffer2; + public InboundRecvBuffer Slice(int offset) + { +#if ENABLE_UNITY_COLLECTIONS_CHECKS + if (bufferLength < offset) + throw new ArgumentOutOfRangeException("Buffer does not contain enough data"); +#endif + InboundRecvBuffer slice; + slice.buffer = buffer + offset; + slice.bufferLength = bufferLength - offset; + return slice; + } } - public struct NetworkPipelineContext + public unsafe struct NetworkPipelineContext { - public NativeSlice internalSharedProcessBuffer; - public NativeSlice internalProcessBuffer; + public byte* staticInstanceBuffer; + public byte* internalSharedProcessBuffer; + public byte* internalProcessBuffer; public DataStreamWriter header; public long timestamp; + public int staticInstanceBufferLength; + public int internalSharedProcessBufferLength; + public int internalProcessBufferLength; } - public class NetworkPipelineInitilizeAttribute : Attribute + public unsafe interface INetworkPipelineStage { - public NetworkPipelineInitilizeAttribute(Type t) + NetworkPipelineStage StaticInitialize(byte* staticInstanceBuffer, int staticInstanceBufferLength, INetworkParameter[] param); + int StaticSize { get; } + } + public unsafe struct NetworkPipelineStage + { + public NetworkPipelineStage(TransportFunctionPointer Receive, + TransportFunctionPointer Send, + TransportFunctionPointer InitializeConnection, + int ReceiveCapacity, + int SendCapacity, + int HeaderCapacity, + int SharedStateCapacity) { - m_ParameterType = t; + this.Receive = Receive; + this.Send = Send; + this.InitializeConnection = InitializeConnection; + this.ReceiveCapacity = ReceiveCapacity; + this.SendCapacity = SendCapacity; + this.HeaderCapacity = HeaderCapacity; + this.SharedStateCapacity = SharedStateCapacity; + StaticStateStart = StaticStateCapcity = 0; } - - public Type ParameterType => m_ParameterType; - private Type m_ParameterType; + [Flags] + public enum Requests + { + None = 0, + Resume = 1, + Update = 2, + SendUpdate = 4 + } + public delegate void ReceiveDelegate(ref NetworkPipelineContext ctx, ref InboundRecvBuffer inboundBuffer, ref Requests requests); + public delegate void SendDelegate(ref NetworkPipelineContext ctx, ref InboundSendBuffer inboundBuffer, ref Requests requests); + public delegate void InitializeConnectionDelegate(byte* staticInstanceBuffer, int staticInstanceBufferLength, + byte* sendProcessBuffer, int sendProcessBufferLength, byte* recvProcessBuffer, int recvProcessBufferLength, + byte* sharedProcessBuffer, int sharedProcessBufferLength); + + public TransportFunctionPointer Receive; + public TransportFunctionPointer Send; + public TransportFunctionPointer InitializeConnection; + + public readonly int ReceiveCapacity; + public readonly int SendCapacity; + public readonly int HeaderCapacity; + public readonly int SharedStateCapacity; + + internal int StaticStateStart; + internal int StaticStateCapcity; } - public interface INetworkPipelineStage - { - NativeSlice Receive(NetworkPipelineContext ctx, NativeSlice inboundBuffer, ref bool needsResume, ref bool needsUpdate, ref bool needsSendUpdate); - InboundBufferVec Send(NetworkPipelineContext ctx, InboundBufferVec inboundBuffer, ref bool needsResume, ref bool needsUpdate); - void InitializeConnection(NativeSlice sendProcessBuffer, NativeSlice recvProcessBuffer, NativeSlice sharedProcessBuffer); - - int ReceiveCapacity { get; } - int SendCapacity { get; } - int HeaderCapacity { get; } - int SharedStateCapacity { get; } + public struct NetworkPipelineStageId + { + internal int Index; + internal int IsValid; } - - public interface INetworkPipelineStageCollection + public static class NetworkPipelineStageCollection { - int GetStageId(Type type); - - void Initialize(params INetworkParameter[] param); - void InvokeInitialize(int pipelineStageId, NativeSlice sendProcessBuffer, NativeSlice recvProcessBuffer, NativeSlice sharedStateBuffer); - InboundBufferVec InvokeSend(int pipelineStageId, NetworkPipelineContext ctx, InboundBufferVec inboundBuffer, ref bool needsResume, ref bool needsUpdate); - NativeSlice InvokeReceive(int pipelineStageId, NetworkPipelineContext ctx, NativeSlice inboundBuffer, ref bool needsResume, ref bool needsUpdate, ref bool needsSendUpdate); + static NetworkPipelineStageCollection() + { + m_stages = new List(); + RegisterPipelineStage(new NullPipelineStage()); + RegisterPipelineStage(new ReliableSequencedPipelineStage()); + RegisterPipelineStage(new UnreliableSequencedPipelineStage()); + RegisterPipelineStage(new SimulatorPipelineStage()); + RegisterPipelineStage(new SimulatorPipelineStageInSend()); + } + public static void RegisterPipelineStage(INetworkPipelineStage stage) + { + for (int i = 0; i < m_stages.Count; ++i) + { + if (m_stages[i].GetType() == stage.GetType()) + { + // TODO: should this be an error? + m_stages[i] = stage; + return; + } - int GetReceiveCapacity(int pipelineStageId); - int GetSendCapacity(int pipelineStageId); - int GetHeaderCapacity(int pipelineStageId); - int GetSharedStateCapacity(int pipelineStageId); + } + m_stages.Add(stage); + } + public static NetworkPipelineStageId GetStageId(Type stageType) + { + for (int i = 0; i < m_stages.Count; ++i) + { + if (stageType == m_stages[i].GetType()) + return new NetworkPipelineStageId{Index=i, IsValid = 1}; + } + throw new InvalidOperationException($"Pipeline stage {stageType} is not registered"); + } + internal static List m_stages; } public struct NetworkPipeline @@ -103,13 +184,16 @@ public struct NetworkPipelineParams : INetworkParameter public int initialCapacity; } - internal struct NetworkPipelineProcessor : IDisposable where TNetworkPipelineStageCollection: struct, INetworkPipelineStageCollection + internal struct NetworkPipelineProcessor : IDisposable { + public const int Alignment = 8; + public const int AlignmentMinusOne = Alignment-1; public Concurrent ToConcurrent() { var concurrent = new Concurrent { m_StageCollection = m_StageCollection, + m_StaticInstanceBuffer = m_StaticInstanceBuffer, m_Pipelines = m_Pipelines, m_StageList = m_StageList, m_SendStageNeedsUpdateWrite = m_SendStageNeedsUpdateRead.AsParallelWriter(), @@ -122,8 +206,8 @@ public Concurrent ToConcurrent() } public struct Concurrent { - // FIXME: read-only? - internal TNetworkPipelineStageCollection m_StageCollection; + [ReadOnly] internal NativeArray m_StageCollection; + [ReadOnly] internal NativeArray m_StaticInstanceBuffer; [ReadOnly] internal NativeList m_Pipelines; [ReadOnly] internal NativeList m_StageList; internal NativeQueue.ParallelWriter m_SendStageNeedsUpdateWrite; @@ -133,7 +217,12 @@ public struct Concurrent [ReadOnly] internal NativeList sendBuffer; [ReadOnly] internal NativeArray m_timestamp; - public unsafe int Send(T driver, NetworkPipeline pipeline, NetworkConnection connection, NativeSlice payloadData) where T : struct, INetworkPipelineSender + public unsafe int SendHeaderCapacity(NetworkPipeline pipeline) + { + var p = m_Pipelines[pipeline.Id-1]; + return p.headerCapacity; + } + public unsafe int Send(NetworkDriver.Concurrent driver, NetworkPipeline pipeline, NetworkConnection connection, NetworkInterfaceSendHandle sendHandle, int headerSize) { var p = m_Pipelines[pipeline.Id-1]; @@ -145,26 +234,29 @@ public unsafe int Send(T driver, NetworkPipeline pipeline, NetworkConnection int* sendBufferLock = (int*) tmpBuffer.GetUnsafeReadOnlyPtr(); sendBufferLock += connectionId * sizePerConnection[SendSizeOffset] / 4; - while (Interlocked.CompareExchange(ref *sendBufferLock, 1, 0) != 0) + if (Interlocked.CompareExchange(ref *sendBufferLock, 1, 0) != 0) { #if ENABLE_UNITY_COLLECTIONS_CHECKS throw new InvalidOperationException("The parallel network driver needs to process a single unique connection per job, processing a single connection multiple times in a parallel for is not supported."); +#else + return -1; #endif } NativeList currentUpdates = new NativeList(128, Allocator.Temp); - ProcessPipelineSend(driver, startStage, pipeline, connection, payloadData, currentUpdates); + int retval = ProcessPipelineSend(driver, startStage, pipeline, connection, sendHandle, headerSize, currentUpdates); + Interlocked.Exchange(ref *sendBufferLock, 0); // Move the updates requested in this iteration to the concurrent queue so it can be read/parsed in update routine for (int i = 0; i < currentUpdates.Length; ++i) m_SendStageNeedsUpdateWrite.Enqueue(currentUpdates[i]); - Interlocked.Exchange(ref *sendBufferLock, 0); - return payloadData.Length; + return retval; } - internal unsafe void ProcessPipelineSend(T driver, int startStage, NetworkPipeline pipeline, NetworkConnection connection, - NativeSlice payloadBuffer, NativeList currentUpdates) where T : struct, INetworkPipelineSender + internal unsafe int ProcessPipelineSend(NetworkDriver.Concurrent driver, int startStage, NetworkPipeline pipeline, NetworkConnection connection, + NetworkInterfaceSendHandle sendHandle, int headerSize, NativeList currentUpdates) { + int retval = sendHandle.size; NetworkPipelineContext ctx = default(NetworkPipelineContext); ctx.timestamp = m_timestamp[0]; var p = m_Pipelines[pipeline.Id-1]; @@ -172,15 +264,24 @@ internal unsafe void ProcessPipelineSend(T driver, int startStage, NetworkPip var resumeQ = new NativeList(16, Allocator.Temp); int resumeQStart = 0; - ctx.header = new DataStreamWriter(p.headerCapacity, Allocator.Temp); - var inboundBuffer = default(InboundBufferVec); - inboundBuffer.buffer1 = payloadBuffer; - - var prevHeader = new DataStreamWriter(p.headerCapacity, Allocator.Temp); +#if ENABLE_UNITY_COLLECTIONS_CHECKS + if (headerSize != p.headerCapacity+UnsafeUtility.SizeOf()+1 && sendHandle.data != IntPtr.Zero) + throw new InvalidOperationException("Invalid header size."); +#endif + var inboundBuffer = default(InboundSendBuffer); + if (sendHandle.data != IntPtr.Zero) + { + inboundBuffer.bufferWithHeaders = (byte*)sendHandle.data + UnsafeUtility.SizeOf() + 1; + inboundBuffer.bufferWithHeadersLength = sendHandle.size - UnsafeUtility.SizeOf() - 1; + inboundBuffer.buffer = inboundBuffer.bufferWithHeaders + p.headerCapacity; + inboundBuffer.bufferLength = inboundBuffer.bufferWithHeadersLength - p.headerCapacity; + } while (true) { + headerSize = p.headerCapacity; + int internalBufferOffset = p.sendBufferOffset + sizePerConnection[SendSizeOffset] * connectionId; int internalSharedBufferOffset = p.sharedBufferOffset + sizePerConnection[SharedSizeOffset] * connectionId; @@ -188,81 +289,117 @@ internal unsafe void ProcessPipelineSend(T driver, int startStage, NetworkPip // If this is not the first stage we need to fast forward the buffer offset to the correct place if (startStage > 0) { +#if ENABLE_UNITY_COLLECTIONS_CHECKS + if (inboundBuffer.bufferWithHeadersLength > 0) + throw new InvalidOperationException("Can't start from a stage with a buffer"); +#endif for (int i = 0; i < startStage; ++i) { - internalBufferOffset += m_StageCollection.GetSendCapacity(m_StageList[p.FirstStageIndex + i]); - internalSharedBufferOffset += m_StageCollection.GetSharedStateCapacity(m_StageList[p.FirstStageIndex + i]); + internalBufferOffset += (m_StageCollection[m_StageList[p.FirstStageIndex + i]].SendCapacity + AlignmentMinusOne) & (~AlignmentMinusOne); + internalSharedBufferOffset += (m_StageCollection[m_StageList[p.FirstStageIndex + i]].SharedStateCapacity + AlignmentMinusOne) & (~AlignmentMinusOne); + headerSize -= m_StageCollection[m_StageList[p.FirstStageIndex + i]].HeaderCapacity; } } for (int i = startStage; i < p.NumStages; ++i) { + int stageHeaderCapacity = m_StageCollection[m_StageList[p.FirstStageIndex + i]].HeaderCapacity; +#if ENABLE_UNITY_COLLECTIONS_CHECKS + if (stageHeaderCapacity > headerSize) + throw new InvalidOperationException("Not enough header space"); +#endif + inboundBuffer.headerPadding = headerSize; + headerSize -= stageHeaderCapacity; + if (stageHeaderCapacity > 0 && inboundBuffer.bufferWithHeadersLength > 0) + { + var headerArray = NativeArrayUnsafeUtility.ConvertExistingDataToNativeArray(inboundBuffer.bufferWithHeaders + headerSize, stageHeaderCapacity, Allocator.Invalid); +#if ENABLE_UNITY_COLLECTIONS_CHECKS + NativeArrayUnsafeUtility.SetAtomicSafetyHandle(ref headerArray, AtomicSafetyHandle.GetTempMemoryHandle()); +#endif + ctx.header = new DataStreamWriter(headerArray); + + } + else + ctx.header = new DataStreamWriter(stageHeaderCapacity, Allocator.Temp); var prevInbound = inboundBuffer; ProcessSendStage(i, internalBufferOffset, internalSharedBufferOffset, p, ref resumeQ, ref ctx, ref inboundBuffer, ref needsUpdate); - if (inboundBuffer.buffer1 == prevInbound.buffer1 && - inboundBuffer.buffer2 == prevInbound.buffer2) + + if (needsUpdate) + AddSendUpdate(connection, i, pipeline, currentUpdates); + if (inboundBuffer.bufferWithHeadersLength == 0) + break; + +#if ENABLE_UNITY_COLLECTIONS_CHECKS + if (inboundBuffer.headerPadding != prevInbound.headerPadding) + throw new InvalidOperationException("Changing the header padding in a pipeline is not supported"); +#endif + if (inboundBuffer.buffer != prevInbound.buffer) { - if (ctx.header.Length > 0) - { - if (prevHeader.Length > 0) - ctx.header.WriteBytes(prevHeader.GetUnsafeReadOnlyPtr(), prevHeader.Length); - prevHeader.Clear(); - var tempHeader = ctx.header; - ctx.header = prevHeader; - prevHeader = tempHeader; - if (inboundBuffer.buffer2.Length == 0) - inboundBuffer.buffer2 = inboundBuffer.buffer1; - inboundBuffer.buffer1 = prevHeader.GetNativeSlice(0, prevHeader.Length); - } +#if ENABLE_UNITY_COLLECTIONS_CHECKS + if (inboundBuffer.buffer != inboundBuffer.bufferWithHeaders + inboundBuffer.headerPadding || + inboundBuffer.bufferLength + inboundBuffer.headerPadding > inboundBuffer.bufferWithHeadersLength) + throw new InvalidOperationException("When creating an internal buffer in piplines the buffer must be a subset of the buffer width headers"); +#endif + // Copy header to new buffer so it is part of the payload + UnsafeUtility.MemCpy(inboundBuffer.bufferWithHeaders + headerSize, ctx.header.AsNativeArray().GetUnsafeReadOnlyPtr(), ctx.header.Length); } +#if ENABLE_UNITY_COLLECTIONS_CHECKS else { -#if ENABLE_UNITY_COLLECTIONS_CHECKS - if (inboundBuffer.buffer2.Length > 0) - throw new InvalidOperationException("Pipeline send stages must return either the unmodified inbound buffers or a consolidated version with a single buffer"); + if (inboundBuffer.bufferWithHeaders != prevInbound.bufferWithHeaders) + throw new InvalidOperationException("Changing the send buffer with headers without changing the buffer is not supported"); + } #endif - // Prev header is now part of payload - prevHeader.Clear(); - if (ctx.header.Length > 0) - { - var tempHeader = ctx.header; - ctx.header = prevHeader; - prevHeader = tempHeader; - inboundBuffer.buffer2 = inboundBuffer.buffer1; - inboundBuffer.buffer1 = prevHeader.GetNativeSlice(0, prevHeader.Length); - } + if (ctx.header.Length < stageHeaderCapacity) + { + int wastedSpace = stageHeaderCapacity - ctx.header.Length; + // Remove wasted space in the header + UnsafeUtility.MemMove(inboundBuffer.buffer - wastedSpace, inboundBuffer.buffer, inboundBuffer.bufferLength); } - if (needsUpdate) - AddSendUpdate(connection, i, pipeline, currentUpdates); - if (inboundBuffer.buffer1.Length == 0) - break; + + // Update the inbound buffer for next iteration + inboundBuffer.buffer = inboundBuffer.bufferWithHeaders + headerSize; + inboundBuffer.bufferLength = ctx.header.Length + inboundBuffer.bufferLength; + needsUpdate = false; - internalBufferOffset += ctx.internalProcessBuffer.Length; - internalSharedBufferOffset += ctx.internalSharedProcessBuffer.Length; + internalBufferOffset += (ctx.internalProcessBufferLength + AlignmentMinusOne) & (~AlignmentMinusOne); + internalSharedBufferOffset += (ctx.internalSharedProcessBufferLength + AlignmentMinusOne) & (~AlignmentMinusOne); } - if (inboundBuffer.buffer1.Length != 0) + if (inboundBuffer.bufferLength != 0) { - var iov = stackalloc network_iovec[4]; - var pipelineId = pipeline.Id; - iov[0].buf = &pipelineId; - iov[0].len = 1; - iov[1].buf = ctx.header.GetUnsafePtr(); - iov[1].len = ctx.header.Length; - iov[2].buf = inboundBuffer.buffer1.GetUnsafeReadOnlyPtr(); - iov[2].len = inboundBuffer.buffer1.Length; - if (inboundBuffer.buffer2.Length > 0) + if (sendHandle.data != IntPtr.Zero && inboundBuffer.bufferWithHeaders == (byte*)sendHandle.data + UnsafeUtility.SizeOf() + 1) { - iov[3].buf = inboundBuffer.buffer2.GetUnsafeReadOnlyPtr(); - iov[3].len = inboundBuffer.buffer2.Length; - // FIXME: handle send errors - driver.Send(connection, iov, 4); + // Actually send the data - after collapsing it again + if (inboundBuffer.buffer != inboundBuffer.bufferWithHeaders) + { + UnsafeUtility.MemMove(inboundBuffer.bufferWithHeaders, inboundBuffer.buffer, inboundBuffer.bufferLength); + inboundBuffer.buffer = inboundBuffer.bufferWithHeaders; + } + ((byte*)sendHandle.data)[UnsafeUtility.SizeOf()] = (byte)pipeline.Id; + int sendSize = UnsafeUtility.SizeOf() + 1 + inboundBuffer.bufferLength; +#if ENABLE_UNITY_COLLECTIONS_CHECKS + if (sendSize > sendHandle.size) + throw new InvalidOperationException("Pipeline increased the data in the buffer, this is not allowed"); +#endif + sendHandle.size = sendSize; + retval = driver.CompleteSend(connection, sendHandle); + sendHandle = default; } else - driver.Send(connection, iov, 3); + { + // Sending without pipeline, the correct pipeline will be added by the default flags when this is called + var writer = driver.BeginSend(connection); + writer.WriteByte((byte)pipeline.Id); + writer.WriteBytes(inboundBuffer.buffer, inboundBuffer.bufferLength); + if (writer.HasFailedWrites) + driver.AbortSend(writer); + else + driver.EndSend(writer); + } } if (resumeQStart >= resumeQ.Length) @@ -272,43 +409,34 @@ internal unsafe void ProcessPipelineSend(T driver, int startStage, NetworkPip startStage = resumeQ[resumeQStart++]; - prevHeader.Clear(); - inboundBuffer = default(InboundBufferVec); + inboundBuffer = default(InboundSendBuffer); } + if (sendHandle.data != IntPtr.Zero) + driver.AbortSend(sendHandle); + return retval; } - internal static unsafe NativeSlice Unsafe_GetSliceFromReadOnlyArray(NativeArray array, int offset, int length) - { -#if ENABLE_UNITY_COLLECTIONS_CHECKS - var handle = AtomicSafetyHandle.Create(); -#endif - var ptr = (void*)((byte*) array.GetUnsafeReadOnlyPtr() + offset); - var buffer = - NativeArrayUnsafeUtility.ConvertExistingDataToNativeArray(ptr, length, Allocator.None); -#if ENABLE_UNITY_COLLECTIONS_CHECKS - NativeArrayUnsafeUtility.SetAtomicSafetyHandle(ref buffer, handle); -#endif - return buffer.Slice(); - } - - private void ProcessSendStage(int startStage, int internalBufferOffset, int internalSharedBufferOffset, - PipelineImpl p, ref NativeList resumeQ, ref NetworkPipelineContext ctx, ref InboundBufferVec inboundBuffer, ref bool needsUpdate) + private unsafe void ProcessSendStage(int startStage, int internalBufferOffset, int internalSharedBufferOffset, + PipelineImpl p, ref NativeList resumeQ, ref NetworkPipelineContext ctx, ref InboundSendBuffer inboundBuffer, ref bool needsUpdate) { - bool needsResume = false; - ctx.internalProcessBuffer = Unsafe_GetSliceFromReadOnlyArray(sendBuffer, internalBufferOffset, - m_StageCollection.GetSendCapacity(m_StageList[p.FirstStageIndex + startStage])); - - ctx.internalSharedProcessBuffer = - Unsafe_GetSliceFromReadOnlyArray(sharedBuffer, internalSharedBufferOffset, - m_StageCollection.GetSharedStateCapacity(m_StageList[p.FirstStageIndex + startStage])); - - inboundBuffer = m_StageCollection.InvokeSend(m_StageList[p.FirstStageIndex + startStage], ctx, - inboundBuffer, ref needsResume, ref needsUpdate); - if (needsResume) + var pipelineStage = m_StageCollection[m_StageList[p.FirstStageIndex + startStage]]; + ctx.staticInstanceBuffer = (byte*)m_StaticInstanceBuffer.GetUnsafeReadOnlyPtr() + pipelineStage.StaticStateStart; + ctx.staticInstanceBufferLength = pipelineStage.StaticStateCapcity; + ctx.internalProcessBuffer = (byte*)sendBuffer.GetUnsafeReadOnlyPtr() + internalBufferOffset; + ctx.internalProcessBufferLength = pipelineStage.SendCapacity; + + ctx.internalSharedProcessBuffer = (byte*)sharedBuffer.GetUnsafeReadOnlyPtr() + internalSharedBufferOffset; + ctx.internalSharedProcessBufferLength = pipelineStage.SharedStateCapacity; + + NetworkPipelineStage.Requests requests = NetworkPipelineStage.Requests.None; + pipelineStage.Send.Ptr.Invoke(ref ctx, ref inboundBuffer, ref requests); + if ((requests & NetworkPipelineStage.Requests.Resume) != 0) resumeQ.Add(startStage); + needsUpdate = (requests & NetworkPipelineStage.Requests.Update) != 0; } } - private TNetworkPipelineStageCollection m_StageCollection; + private NativeArray m_StageCollection; + private NativeArray m_StaticInstanceBuffer; private NativeList m_StageList; private NativeList m_Pipelines; private NativeList m_ReceiveBuffer; @@ -337,7 +465,7 @@ internal struct PipelineImpl public int headerCapacity; } - public NetworkPipelineProcessor(params INetworkParameter[] param) + public unsafe NetworkPipelineProcessor(params INetworkParameter[] param) { NetworkPipelineParams config = default(NetworkPipelineParams); for (int i = 0; i < param.Length; ++i) @@ -345,16 +473,34 @@ public NetworkPipelineProcessor(params INetworkParameter[] param) if (param[i] is NetworkPipelineParams) config = (NetworkPipelineParams)param[i]; } - m_StageCollection = new TNetworkPipelineStageCollection(); - m_StageCollection.Initialize(param); + + int staticBufferSize = 0; + for (int i = 0; i < NetworkPipelineStageCollection.m_stages.Count; ++i) + { + staticBufferSize += NetworkPipelineStageCollection.m_stages[i].StaticSize; + staticBufferSize = (staticBufferSize+15)&(~15); + } + m_StaticInstanceBuffer = new NativeArray(staticBufferSize, Allocator.Persistent); + m_StageCollection = new NativeArray(NetworkPipelineStageCollection.m_stages.Count, Allocator.Persistent); + staticBufferSize = 0; + for (int i = 0; i < NetworkPipelineStageCollection.m_stages.Count; ++i) + { + var stageStruct = NetworkPipelineStageCollection.m_stages[i].StaticInitialize((byte*)m_StaticInstanceBuffer.GetUnsafePtr() + staticBufferSize, NetworkPipelineStageCollection.m_stages[i].StaticSize, param); + stageStruct.StaticStateStart = staticBufferSize; + stageStruct.StaticStateCapcity = NetworkPipelineStageCollection.m_stages[i].StaticSize; + m_StageCollection[i] = stageStruct; + staticBufferSize += NetworkPipelineStageCollection.m_stages[i].StaticSize; + staticBufferSize = (staticBufferSize+15)&(~15); + } + m_StageList = new NativeList(16, Allocator.Persistent); m_Pipelines = new NativeList(16, Allocator.Persistent); m_ReceiveBuffer = new NativeList(config.initialCapacity, Allocator.Persistent); m_SendBuffer = new NativeList(config.initialCapacity, Allocator.Persistent); m_SharedBuffer = new NativeList(config.initialCapacity, Allocator.Persistent); sizePerConnection = new NativeArray(3, Allocator.Persistent); - // Store an int for the spinlock first in each connections send buffer - sizePerConnection[SendSizeOffset] = 4; + // Store an int for the spinlock first in each connections send buffer, round up to alignment of 8 + sizePerConnection[SendSizeOffset] = Alignment; m_ReceiveStageNeedsUpdate = new NativeList(128, Allocator.Persistent); m_SendStageNeedsUpdate = new NativeList(128, Allocator.Persistent); m_SendStageNeedsUpdateRead = new NativeQueue(Allocator.Persistent); @@ -373,6 +519,8 @@ public void Dispose() m_SendStageNeedsUpdate.Dispose(); m_SendStageNeedsUpdateRead.Dispose(); m_timestamp.Dispose(); + m_StageCollection.Dispose(); + m_StaticInstanceBuffer.Dispose(); } public long Timestamp @@ -392,7 +540,7 @@ public unsafe void initializeConnection(NetworkConnection con) m_SendBuffer.ResizeUninitialized(requiredSendSize); if (m_SharedBuffer.Length < requiredSharedSize) m_SharedBuffer.ResizeUninitialized(requiredSharedSize); - + UnsafeUtility.MemClear((byte*)m_ReceiveBuffer.GetUnsafePtr() + con.m_NetworkId * sizePerConnection[RecveiveSizeOffset], sizePerConnection[RecveiveSizeOffset]); UnsafeUtility.MemClear((byte*)m_SendBuffer.GetUnsafePtr() + con.m_NetworkId * sizePerConnection[SendSizeOffset], sizePerConnection[SendSizeOffset]); UnsafeUtility.MemClear((byte*)m_SharedBuffer.GetUnsafePtr() + con.m_NetworkId * sizePerConnection[SharedSizeOffset], sizePerConnection[SharedSizeOffset]); @@ -400,7 +548,7 @@ public unsafe void initializeConnection(NetworkConnection con) InitializeStages(con.m_NetworkId); } - void InitializeStages(int networkId) + unsafe void InitializeStages(int networkId) { var connectionId = networkId; @@ -416,18 +564,23 @@ void InitializeStages(int networkId) stage < pipeline.FirstStageIndex + pipeline.NumStages; stage++) { - var sendProcessBuffer = - new NativeSlice(m_SendBuffer, sendBufferOffset, m_StageCollection.GetSendCapacity(m_StageList[stage])); - var recvProcessBuffer = - new NativeSlice(m_ReceiveBuffer, recvBufferOffset, m_StageCollection.GetReceiveCapacity(m_StageList[stage])); - var sharedProcessBuffer = - new NativeSlice(m_SharedBuffer, sharedBufferOffset, m_StageCollection.GetSharedStateCapacity(m_StageList[stage])); - - m_StageCollection.InvokeInitialize(m_StageList[stage], sendProcessBuffer, recvProcessBuffer, sharedProcessBuffer); - - sendBufferOffset += sendProcessBuffer.Length; - recvBufferOffset += recvProcessBuffer.Length; - sharedBufferOffset += sharedProcessBuffer.Length; + var pipelineStage = m_StageCollection[m_StageList[stage]]; + var sendProcessBuffer = (byte*)m_SendBuffer.GetUnsafePtr() + sendBufferOffset; + var sendProcessBufferLength = pipelineStage.SendCapacity; + var recvProcessBuffer = (byte*)m_ReceiveBuffer.GetUnsafePtr() + recvBufferOffset; + var recvProcessBufferLength = pipelineStage.ReceiveCapacity; + var sharedProcessBuffer = (byte*)m_SharedBuffer.GetUnsafePtr() + sharedBufferOffset; + var sharedProcessBufferLength = pipelineStage.SharedStateCapacity; + + var staticInstanceBuffer = (byte*)m_StaticInstanceBuffer.GetUnsafePtr() + pipelineStage.StaticStateStart; + var staticInstanceBufferLength = pipelineStage.StaticStateCapcity; + pipelineStage.InitializeConnection.Ptr.Invoke(staticInstanceBuffer, staticInstanceBufferLength, + sendProcessBuffer, sendProcessBufferLength, recvProcessBuffer, recvProcessBufferLength, + sharedProcessBuffer, sharedProcessBufferLength); + + sendBufferOffset += (sendProcessBufferLength + AlignmentMinusOne) & (~AlignmentMinusOne); + recvBufferOffset += (recvProcessBufferLength + AlignmentMinusOne) & (~AlignmentMinusOne); + sharedBufferOffset += (sharedProcessBufferLength + AlignmentMinusOne) & (~AlignmentMinusOne); } } } @@ -447,23 +600,19 @@ public NetworkPipeline CreatePipeline(params Type[] stages) pipeline.NumStages = stages.Length; for (int i = 0; i < stages.Length; i++) { - var stageId = m_StageCollection.GetStageId(stages[i]); + var stageId = NetworkPipelineStageCollection.GetStageId(stages[i]).Index; #if ENABLE_UNITY_COLLECTIONS_CHECKS if (stageId < 0) throw new InvalidOperationException("Trying to create pipeline with invalid stage " + stages[i]); #endif m_StageList.Add(stageId); - receiveCap += m_StageCollection.GetReceiveCapacity(stageId); - sendCap += m_StageCollection.GetSendCapacity(stageId); - headerCap += m_StageCollection.GetHeaderCapacity(stageId); - sharedCap += m_StageCollection.GetSharedStateCapacity(stageId); + // Make sure all data buffers are aligned + receiveCap += (m_StageCollection[stageId].ReceiveCapacity + AlignmentMinusOne) & (~AlignmentMinusOne); + sendCap += (m_StageCollection[stageId].SendCapacity + AlignmentMinusOne) & (~AlignmentMinusOne); + headerCap += m_StageCollection[stageId].HeaderCapacity; + sharedCap += (m_StageCollection[stageId].SharedStateCapacity + AlignmentMinusOne) & (~AlignmentMinusOne); } - // Make sure all data buffers are 4-byte aligned - receiveCap = (receiveCap + 3) & (~3); - sendCap = (sendCap + 3) & (~3); - sharedCap = (sharedCap + 3) & (~3); - pipeline.receiveBufferOffset = sizePerConnection[RecveiveSizeOffset]; sizePerConnection[RecveiveSizeOffset] = sizePerConnection[RecveiveSizeOffset] + receiveCap; @@ -479,31 +628,14 @@ public NetworkPipeline CreatePipeline(params Type[] stages) return new NetworkPipeline {Id = m_Pipelines.Length}; } - public void GetPipelineBuffers(Type pipelineType, NetworkConnection connection, ref NativeSlice readProcessingBuffer, ref NativeSlice writeProcessingBuffer, ref NativeSlice sharedBuffer) - { - var stageId = m_StageCollection.GetStageId(pipelineType); - - int pipelineId = 0; - int stageIndexInList = 0; - for (pipelineId = 0; pipelineId < m_Pipelines.Length; pipelineId++) - { - var pipelineImpl = m_Pipelines[pipelineId]; - for (stageIndexInList = pipelineImpl.FirstStageIndex; - stageIndexInList < pipelineImpl.FirstStageIndex + pipelineImpl.NumStages; - stageIndexInList++) - { - if (m_StageList[stageIndexInList] == stageId) - break; - } - } - - GetPipelineBuffers(new NetworkPipeline{Id = pipelineId}, stageId, connection, ref readProcessingBuffer, ref writeProcessingBuffer, ref sharedBuffer); - } - - public void GetPipelineBuffers(NetworkPipeline pipelineId, int stageId, NetworkConnection connection, - ref NativeSlice readProcessingBuffer, ref NativeSlice writeProcessingBuffer, - ref NativeSlice sharedBuffer) + public void GetPipelineBuffers(NetworkPipeline pipelineId, NetworkPipelineStageId stageId, NetworkConnection connection, + out NativeArray readProcessingBuffer, out NativeArray writeProcessingBuffer, + out NativeArray sharedBuffer) { + if (pipelineId.Id < 1) + throw new InvalidOperationException("The specified pipeline is not valid"); + if (stageId.IsValid == 0) + throw new InvalidOperationException("The specified pipeline stage is not valid"); var pipeline = m_Pipelines[pipelineId.Id-1]; int recvBufferOffset = pipeline.receiveBufferOffset + sizePerConnection[RecveiveSizeOffset] * connection.InternalId; @@ -516,30 +648,32 @@ public void GetPipelineBuffers(NetworkPipeline pipelineId, int stageId, NetworkC stageIndexInList < pipeline.FirstStageIndex + pipeline.NumStages; stageIndexInList++) { - if (m_StageList[stageIndexInList] == stageId) + if (m_StageList[stageIndexInList] == stageId.Index) { stageNotFound = false; break; } - sendBufferOffset += m_StageCollection.GetSendCapacity(m_StageList[stageIndexInList]); - recvBufferOffset += m_StageCollection.GetReceiveCapacity(m_StageList[stageIndexInList]); - sharedBufferOffset += m_StageCollection.GetSharedStateCapacity(m_StageList[stageIndexInList]); + sendBufferOffset += (m_StageCollection[m_StageList[stageIndexInList]].SendCapacity + AlignmentMinusOne) & (~AlignmentMinusOne); + recvBufferOffset += (m_StageCollection[m_StageList[stageIndexInList]].ReceiveCapacity + AlignmentMinusOne) & (~AlignmentMinusOne); + sharedBufferOffset += (m_StageCollection[m_StageList[stageIndexInList]].SharedStateCapacity + AlignmentMinusOne) & (~AlignmentMinusOne); } if (stageNotFound) + { #if ENABLE_UNITY_COLLECTIONS_CHECKS throw new InvalidOperationException("Could not find stage ID " + stageId + " make sure the type for this stage ID is added when the pipeline is created."); #else + writeProcessingBuffer = default; + readProcessingBuffer = default; + sharedBuffer = default; return; #endif + } - writeProcessingBuffer = - new NativeSlice(m_SendBuffer, sendBufferOffset, m_StageCollection.GetSendCapacity(m_StageList[stageIndexInList])); - readProcessingBuffer = - new NativeSlice(m_ReceiveBuffer, recvBufferOffset, m_StageCollection.GetReceiveCapacity(m_StageList[stageIndexInList])); - sharedBuffer = - new NativeSlice(m_SharedBuffer, sharedBufferOffset, m_StageCollection.GetSharedStateCapacity(m_StageList[stageIndexInList])); + writeProcessingBuffer = ((NativeArray)m_SendBuffer).GetSubArray(sendBufferOffset, m_StageCollection[m_StageList[stageIndexInList]].SendCapacity); + readProcessingBuffer = ((NativeArray)m_ReceiveBuffer).GetSubArray(recvBufferOffset, m_StageCollection[m_StageList[stageIndexInList]].ReceiveCapacity); + sharedBuffer = ((NativeArray)m_SharedBuffer).GetSubArray(sharedBufferOffset, m_StageCollection[m_StageList[stageIndexInList]].SharedStateCapacity); } internal struct UpdatePipeline @@ -549,22 +683,29 @@ internal struct UpdatePipeline public NetworkConnection connection; } - internal void UpdateSend(T driver, out int updateCount) where T : struct, INetworkPipelineSender + internal unsafe void UpdateSend(NetworkDriver.Concurrent driver, out int updateCount) { + // Clear the send lock since it cannot be kept here and can be lost if there are exceptions in send + NativeArray tmpBuffer = m_SendBuffer; + int* sendBufferLock = (int*) tmpBuffer.GetUnsafePtr(); + for (int connectionOffset = 0; connectionOffset < m_SendBuffer.Length; connectionOffset += sizePerConnection[SendSizeOffset]) + sendBufferLock[connectionOffset / 4] = 0; + NativeArray sendUpdates = new NativeArray(m_SendStageNeedsUpdateRead.Count + m_SendStageNeedsUpdate.Length, Allocator.Temp); UpdatePipeline updateItem; updateCount = 0; while (m_SendStageNeedsUpdateRead.TryDequeue(out updateItem)) { - sendUpdates[updateCount++] = updateItem; + if (driver.GetConnectionState(updateItem.connection) == NetworkConnection.State.Connected) + sendUpdates[updateCount++] = updateItem; } int startLength = updateCount; for (int i = 0; i < m_SendStageNeedsUpdate.Length; i++) { - sendUpdates[startLength + i] = m_SendStageNeedsUpdate[i]; - updateCount++; + if (driver.GetConnectionState(m_SendStageNeedsUpdate[i].connection) == NetworkConnection.State.Connected) + sendUpdates[updateCount++] = m_SendStageNeedsUpdate[i]; } NativeList currentUpdates = new NativeList(128, Allocator.Temp); @@ -572,8 +713,7 @@ internal void UpdateSend(T driver, out int updateCount) where T : struct, INe for (int i = 0; i < updateCount; ++i) { updateItem = sendUpdates[i]; - var inboundBuffer = default(NativeSlice); - ToConcurrent().ProcessPipelineSend(driver, updateItem.stage, updateItem.pipeline, updateItem.connection, inboundBuffer, currentUpdates); + ToConcurrent().ProcessPipelineSend(driver, updateItem.stage, updateItem.pipeline, updateItem.connection, default, 0, currentUpdates); } for (int i = 0; i < currentUpdates.Length; ++i) m_SendStageNeedsUpdateRead.Enqueue(currentUpdates[i]); @@ -595,34 +735,40 @@ private static void AddSendUpdate(NetworkConnection connection, int stageId, Net currentUpdates.Add(newUpdate); } - public void UpdateReceive(T driver, out int updateCount) where T: struct, INetworkPipelineReceiver + public void UpdateReceive(NetworkDriver driver, out int updateCount) { - updateCount = m_ReceiveStageNeedsUpdate.Length; - NativeArray receiveUpdates = new NativeArray(updateCount, Allocator.Temp); + NativeArray receiveUpdates = new NativeArray(m_ReceiveStageNeedsUpdate.Length, Allocator.Temp); // Move current update requests to a new queue - for (int i = 0; i < updateCount; ++i) - receiveUpdates[i] = m_ReceiveStageNeedsUpdate[i]; + updateCount = 0; + for (int i = 0; i < m_ReceiveStageNeedsUpdate.Length; ++i) + { + if (driver.GetConnectionState(m_ReceiveStageNeedsUpdate[i].connection) == NetworkConnection.State.Connected) + receiveUpdates[updateCount++] = m_ReceiveStageNeedsUpdate[i]; + } m_ReceiveStageNeedsUpdate.Clear(); // Process all current requested updates, new update requests will (possibly) be generated from the pipeline stages - for (int i = 0; i < receiveUpdates.Length; ++i) + for (int i = 0; i < updateCount; ++i) { UpdatePipeline updateItem = receiveUpdates[i]; - ProcessReceiveStagesFrom(driver, updateItem.stage, updateItem.pipeline, updateItem.connection, default(NativeSlice)); + ProcessReceiveStagesFrom(driver, updateItem.stage, updateItem.pipeline, updateItem.connection, default); } } - public void Receive(T driver, NetworkConnection connection, NativeSlice buffer) where T: struct, INetworkPipelineReceiver + public unsafe void Receive(NetworkDriver driver, NetworkConnection connection, NativeArray buffer) { byte pipelineId = buffer[0]; var p = m_Pipelines[pipelineId-1]; int startStage = p.NumStages - 1; - ProcessReceiveStagesFrom(driver, startStage, new NetworkPipeline{Id = pipelineId}, connection, new NativeSlice(buffer, 1, buffer.Length -1)); + InboundRecvBuffer inBuffer; + inBuffer.buffer = (byte*)buffer.GetUnsafePtr() + 1; + inBuffer.bufferLength = buffer.Length - 1; + ProcessReceiveStagesFrom(driver, startStage, new NetworkPipeline{Id = pipelineId}, connection, inBuffer); } - private void ProcessReceiveStagesFrom(T driver, int startStage, NetworkPipeline pipeline, NetworkConnection connection, NativeSlice buffer) where T: struct, INetworkPipelineReceiver + private unsafe void ProcessReceiveStagesFrom(NetworkDriver driver, int startStage, NetworkPipeline pipeline, NetworkConnection connection, InboundRecvBuffer buffer) { var p = m_Pipelines[pipeline.Id-1]; var connectionId = connection.m_NetworkId; @@ -631,7 +777,7 @@ private void ProcessReceiveStagesFrom(T driver, int startStage, NetworkPipeli NetworkPipelineContext ctx = default(NetworkPipelineContext); ctx.timestamp = Timestamp; - var inboundBuffer = new NativeSlice(buffer, 0, buffer.Length); + var inboundBuffer = buffer; ctx.header = default(DataStreamWriter); NativeList sendUpdates = new NativeList(128, Allocator.Temp); @@ -645,8 +791,8 @@ private void ProcessReceiveStagesFrom(T driver, int startStage, NetworkPipeli // Adjust offset accounting for stages in front of the starting stage, since we're parsing the stages in reverse order for (int st = 0; st < startStage; ++st) { - internalBufferOffset += m_StageCollection.GetReceiveCapacity(m_StageList[p.FirstStageIndex+st]); - internalSharedBufferOffset += m_StageCollection.GetSharedStateCapacity(m_StageList[p.FirstStageIndex+st]); + internalBufferOffset += (m_StageCollection[m_StageList[p.FirstStageIndex+st]].ReceiveCapacity + AlignmentMinusOne) & (~AlignmentMinusOne); + internalSharedBufferOffset += (m_StageCollection[m_StageList[p.FirstStageIndex+st]].SharedStateCapacity + AlignmentMinusOne) & (~AlignmentMinusOne); } for (int i = startStage; i >= 0; --i) @@ -671,23 +817,23 @@ private void ProcessReceiveStagesFrom(T driver, int startStage, NetworkPipeli if (needsSendUpdate) AddSendUpdate(connection, i, pipeline, m_SendStageNeedsUpdate); - if (inboundBuffer.Length == 0) + if (inboundBuffer.bufferLength == 0) break; // Offset needs to be adjusted for the next pipeline (the one in front of this one) if (i > 0) { internalBufferOffset -= - m_StageCollection.GetReceiveCapacity(m_StageList[p.FirstStageIndex + i - 1]); + (m_StageCollection[m_StageList[p.FirstStageIndex + i - 1]].ReceiveCapacity + AlignmentMinusOne) & (~AlignmentMinusOne); internalSharedBufferOffset -= - m_StageCollection.GetSharedStateCapacity(m_StageList[p.FirstStageIndex + i - 1]); + (m_StageCollection[m_StageList[p.FirstStageIndex + i - 1]].SharedStateCapacity + AlignmentMinusOne) & (~AlignmentMinusOne); } needsUpdate = false; } - if (inboundBuffer.Length != 0) - driver.PushDataEvent(connection, inboundBuffer); + if (inboundBuffer.bufferLength != 0) + driver.PushDataEvent(connection, inboundBuffer.buffer, inboundBuffer.bufferLength); if (resumeQStart >= resumeQ.Length) { @@ -695,24 +841,29 @@ private void ProcessReceiveStagesFrom(T driver, int startStage, NetworkPipeli } startStage = resumeQ[resumeQStart++]; - inboundBuffer = default(NativeSlice); + inboundBuffer = default; } } - private void ProcessReceiveStage(int stage, NetworkPipeline pipeline, int internalBufferOffset, int internalSharedBufferOffset, ref NetworkPipelineContext ctx, ref NativeSlice inboundBuffer, ref NativeList resumeQ, ref bool needsUpdate, ref bool needsSendUpdate) + private unsafe void ProcessReceiveStage(int stage, NetworkPipeline pipeline, int internalBufferOffset, int internalSharedBufferOffset, ref NetworkPipelineContext ctx, ref InboundRecvBuffer inboundBuffer, ref NativeList resumeQ, ref bool needsUpdate, ref bool needsSendUpdate) { - bool needsResume = false; var p = m_Pipelines[pipeline.Id-1]; - ctx.internalProcessBuffer = - new NativeSlice(m_ReceiveBuffer, internalBufferOffset, m_StageCollection.GetReceiveCapacity(m_StageList[p.FirstStageIndex+stage])); - ctx.internalSharedProcessBuffer = - new NativeSlice(m_SharedBuffer, internalSharedBufferOffset, m_StageCollection.GetSharedStateCapacity(m_StageList[p.FirstStageIndex+stage])); var stageId = m_StageList[p.FirstStageIndex + stage]; - inboundBuffer = m_StageCollection.InvokeReceive(stageId, ctx, inboundBuffer, ref needsResume, ref needsUpdate, ref needsSendUpdate); - - if (needsResume) + var pipelineStage = m_StageCollection[stageId]; + ctx.staticInstanceBuffer = (byte*)m_StaticInstanceBuffer.GetUnsafePtr() + pipelineStage.StaticStateStart; + ctx.staticInstanceBufferLength = pipelineStage.StaticStateCapcity; + ctx.internalProcessBuffer = (byte*)m_ReceiveBuffer.GetUnsafePtr() + internalBufferOffset; + ctx.internalProcessBufferLength = pipelineStage.ReceiveCapacity; + ctx.internalSharedProcessBuffer = (byte*)m_SharedBuffer.GetUnsafePtr() + internalSharedBufferOffset; + ctx.internalSharedProcessBufferLength = pipelineStage.SharedStateCapacity; + NetworkPipelineStage.Requests requests = NetworkPipelineStage.Requests.None; + pipelineStage.Receive.Ptr.Invoke(ref ctx, ref inboundBuffer, ref requests); + + if ((requests & NetworkPipelineStage.Requests.Resume) != 0) resumeQ.Add(stage); + needsUpdate = (requests & NetworkPipelineStage.Requests.Update) != 0; + needsSendUpdate = (requests & NetworkPipelineStage.Requests.SendUpdate) != 0; } } } diff --git a/Runtime/Pipelines/DefaultPipelineStageCollection.cs b/Runtime/Pipelines/DefaultPipelineStageCollection.cs deleted file mode 100644 index e7d841c..0000000 --- a/Runtime/Pipelines/DefaultPipelineStageCollection.cs +++ /dev/null @@ -1,237 +0,0 @@ -using System; -using Unity.Collections; -using Unity.Networking.Transport.Utilities; - -namespace Unity.Networking.Transport -{ - public struct NullPipelineStageCollection : INetworkPipelineStageCollection - { - public int GetStageId(Type type) - { - return -1; - } - - public void Initialize(params INetworkParameter[] param) - { - } - - public void InvokeInitialize(int pipelineStageId, NativeSlice sendProcessBuffer, NativeSlice recvProcessBuffer, - NativeSlice sharedStateBuffer) - { - throw new NotImplementedException(); - } - - public InboundBufferVec InvokeSend(int pipelineStageId, NetworkPipelineContext ctx, InboundBufferVec inboundBuffer, ref bool needsResume, ref bool needsUpdate) - { - return inboundBuffer; - } - - public NativeSlice InvokeReceive(int pipelineStageId, NetworkPipelineContext ctx, NativeSlice inboundBuffer, ref bool needsResume, ref bool needsUpdate, ref bool needsSendUpdate) - { - return inboundBuffer; - } - - public int GetReceiveCapacity(int pipelineStageId) - { - return 0; - } - - public int GetSendCapacity(int pipelineStageId) - { - return 0; - } - - public int GetHeaderCapacity(int pipelineStageId) - { - return 0; - } - - public int GetSharedStateCapacity(int pipelineStageId) - { - return 0; - } - } - - public struct DefaultPipelineStageCollection : INetworkPipelineStageCollection - { - private SimulatorPipelineStage m_SimulatorPipelineStage; - private SimulatorPipelineStageInSend m_SimulatorPipelineStageInSend; - private NullPipelineStage m_NullPipelineStage; - private UnreliableSequencedPipelineStage m_UnreliableSequencedPipelineStage; - private ReliableSequencedPipelineStage m_ReliableSequencedPipelineStage; - public int GetStageId(Type type) - { - if (type == typeof(SimulatorPipelineStage)) - return 0; - if (type == typeof(SimulatorPipelineStageInSend)) - return 1; - if (type == typeof(NullPipelineStage)) - return 2; - if (type == typeof(UnreliableSequencedPipelineStage)) - return 3; - if (type == typeof(ReliableSequencedPipelineStage)) - return 4; - return -1; - } - public void Initialize(params INetworkParameter[] param) - { - for (int i = 0; i < param.Length; ++i) - { - if (param[i] is SimulatorUtility.Parameters) - m_SimulatorPipelineStage.Initialize((SimulatorUtility.Parameters)param[i]); - if (param[i] is SimulatorUtility.Parameters) - m_SimulatorPipelineStageInSend.Initialize((SimulatorUtility.Parameters)param[i]); - if (param[i] is ReliableUtility.Parameters) - m_ReliableSequencedPipelineStage.Initialize((ReliableUtility.Parameters)param[i]); - } - } - public void InvokeInitialize(int pipelineStageId, NativeSlice sendProcessBuffer, NativeSlice recvProcessBuffer, NativeSlice sharedStateBuffer) - { - switch (pipelineStageId) - { - case 0: - m_SimulatorPipelineStage.InitializeConnection(sendProcessBuffer, recvProcessBuffer, sharedStateBuffer); - break; - case 1: - m_SimulatorPipelineStageInSend.InitializeConnection(sendProcessBuffer, recvProcessBuffer, sharedStateBuffer); - break; - case 2: - m_NullPipelineStage.InitializeConnection(sendProcessBuffer, recvProcessBuffer, sharedStateBuffer); - break; - case 3: - m_UnreliableSequencedPipelineStage.InitializeConnection(sendProcessBuffer, recvProcessBuffer, sharedStateBuffer); - break; - case 4: - m_ReliableSequencedPipelineStage.InitializeConnection(sendProcessBuffer, recvProcessBuffer, sharedStateBuffer); - break; - } - } - public InboundBufferVec InvokeSend(int pipelineStageId, NetworkPipelineContext ctx, InboundBufferVec inboundBuffer, ref bool needsResume, ref bool needsUpdate) - { - switch (pipelineStageId) - { - case 0: - return m_SimulatorPipelineStage.Send(ctx, inboundBuffer, ref needsResume, ref needsUpdate); - case 1: - return m_SimulatorPipelineStageInSend.Send(ctx, inboundBuffer, ref needsResume, ref needsUpdate); - case 2: - return m_NullPipelineStage.Send(ctx, inboundBuffer, ref needsResume, ref needsUpdate); - case 3: - return m_UnreliableSequencedPipelineStage.Send(ctx, inboundBuffer, ref needsResume, ref needsUpdate); - case 4: - return m_ReliableSequencedPipelineStage.Send(ctx, inboundBuffer, ref needsResume, ref needsUpdate); - } - return inboundBuffer; - } - public NativeSlice InvokeReceive(int pipelineStageId, NetworkPipelineContext ctx, NativeSlice inboundBuffer, ref bool needsResume, ref bool needsUpdate, ref bool needsSendUpdate) - { - switch (pipelineStageId) - { - case 0: - return m_SimulatorPipelineStage.Receive(ctx, inboundBuffer, ref needsResume, ref needsUpdate, ref needsSendUpdate); - case 1: - return m_SimulatorPipelineStageInSend.Receive(ctx, inboundBuffer, ref needsResume, ref needsUpdate, ref needsSendUpdate); - case 2: - return m_NullPipelineStage.Receive(ctx, inboundBuffer, ref needsResume, ref needsUpdate, ref needsSendUpdate); - case 3: - return m_UnreliableSequencedPipelineStage.Receive(ctx, inboundBuffer, ref needsResume, ref needsUpdate, ref needsSendUpdate); - case 4: - return m_ReliableSequencedPipelineStage.Receive(ctx, inboundBuffer, ref needsResume, ref needsUpdate, ref needsSendUpdate); - } - return inboundBuffer; - } - public int GetReceiveCapacity(int pipelineStageId) - { - switch (pipelineStageId) - { - case 0: - return m_SimulatorPipelineStage.ReceiveCapacity; - case 1: - return m_SimulatorPipelineStageInSend.ReceiveCapacity; - case 2: - return m_NullPipelineStage.ReceiveCapacity; - case 3: - return m_UnreliableSequencedPipelineStage.ReceiveCapacity; - case 4: - return m_ReliableSequencedPipelineStage.ReceiveCapacity; - } - return 0; - } - public int GetSendCapacity(int pipelineStageId) - { - switch (pipelineStageId) - { - case 0: - return m_SimulatorPipelineStage.SendCapacity; - case 1: - return m_SimulatorPipelineStageInSend.SendCapacity; - case 2: - return m_NullPipelineStage.SendCapacity; - case 3: - return m_UnreliableSequencedPipelineStage.SendCapacity; - case 4: - return m_ReliableSequencedPipelineStage.SendCapacity; - } - return 0; - } - public int GetHeaderCapacity(int pipelineStageId) - { - switch (pipelineStageId) - { - case 0: - return m_SimulatorPipelineStage.HeaderCapacity; - case 1: - return m_SimulatorPipelineStageInSend.HeaderCapacity; - case 2: - return m_NullPipelineStage.HeaderCapacity; - case 3: - return m_UnreliableSequencedPipelineStage.HeaderCapacity; - case 4: - return m_ReliableSequencedPipelineStage.HeaderCapacity; - } - return 0; - } - public int GetSharedStateCapacity(int pipelineStageId) - { - switch (pipelineStageId) - { - case 0: - return m_SimulatorPipelineStage.SharedStateCapacity; - case 1: - return m_SimulatorPipelineStageInSend.SharedStateCapacity; - case 2: - return m_NullPipelineStage.SharedStateCapacity; - case 3: - return m_UnreliableSequencedPipelineStage.SharedStateCapacity; - case 4: - return m_ReliableSequencedPipelineStage.SharedStateCapacity; - } - return 0; - } - } - - public struct NullPipelineStage : INetworkPipelineStage - { - public NativeSlice Receive(NetworkPipelineContext ctx, NativeSlice inboundBuffer, - ref bool needsResume, ref bool needsUpdate, ref bool needsSendUpdate) - { - return inboundBuffer; - } - - public InboundBufferVec Send(NetworkPipelineContext ctx, InboundBufferVec inboundBuffer, ref bool needsResume, - ref bool needsUpdate) - { - return inboundBuffer; - } - - public void InitializeConnection(NativeSlice sendProcessBuffer, NativeSlice recvProcessBuffer, - NativeSlice sharedProcessBuffer) - { - } - - public int ReceiveCapacity => 0; - public int SendCapacity => 0; - public int HeaderCapacity => 0; - public int SharedStateCapacity => 0; - } -} diff --git a/Runtime/Pipelines/NullPipelineStage.cs b/Runtime/Pipelines/NullPipelineStage.cs new file mode 100644 index 0000000..173e4f6 --- /dev/null +++ b/Runtime/Pipelines/NullPipelineStage.cs @@ -0,0 +1,42 @@ +using Unity.Burst; + +namespace Unity.Networking.Transport +{ + [BurstCompile] + public unsafe struct NullPipelineStage : INetworkPipelineStage + { + [BurstCompile] + private static void Send(ref NetworkPipelineContext ctx, ref InboundSendBuffer inboundBuffer, ref NetworkPipelineStage.Requests requests) + { + } + + [BurstCompile] + private static void Receive(ref NetworkPipelineContext ctx, ref InboundRecvBuffer inboundBuffer, ref NetworkPipelineStage.Requests requests) + { + } + + [BurstCompile] + private static void InitializeConnection(byte* staticInstanceBuffer, int staticInstanceBufferLength, + byte* sendProcessBuffer, int sendProcessBufferLength, byte* recvProcessBuffer, int recvProcessBufferLength, + byte* sharedProcessBuffer, int sharedProcessBufferLength) + { + } + static TransportFunctionPointer ReceiveFunctionPointer = new TransportFunctionPointer(Receive); + static TransportFunctionPointer SendFunctionPointer = new TransportFunctionPointer(Send); + static TransportFunctionPointer InitializeConnectionFunctionPointer = new TransportFunctionPointer(InitializeConnection); + public NetworkPipelineStage StaticInitialize(byte* staticInstanceBuffer, int staticInstanceBufferLength, INetworkParameter[] netParams) + { + return new NetworkPipelineStage( + Receive: ReceiveFunctionPointer, + Send: SendFunctionPointer, + InitializeConnection: InitializeConnectionFunctionPointer, + ReceiveCapacity: 0, + SendCapacity: 0, + HeaderCapacity: 0, + SharedStateCapacity: 0 + ); + } + + public int StaticSize => 0; + } +} diff --git a/Runtime/Pipelines/DefaultPipelineStageCollection.cs.meta b/Runtime/Pipelines/NullPipelineStage.cs.meta similarity index 100% rename from Runtime/Pipelines/DefaultPipelineStageCollection.cs.meta rename to Runtime/Pipelines/NullPipelineStage.cs.meta diff --git a/Runtime/Pipelines/ReliableSequencedPipelineStage.cs b/Runtime/Pipelines/ReliableSequencedPipelineStage.cs index c65d83a..1f5bc6f 100644 --- a/Runtime/Pipelines/ReliableSequencedPipelineStage.cs +++ b/Runtime/Pipelines/ReliableSequencedPipelineStage.cs @@ -1,155 +1,173 @@ using Unity.Collections; using Unity.Collections.LowLevel.Unsafe; using Unity.Networking.Transport.Utilities; +using Unity.Burst; namespace Unity.Networking.Transport { - [NetworkPipelineInitilize(typeof(ReliableUtility.Parameters))] - public struct ReliableSequencedPipelineStage : INetworkPipelineStage + [BurstCompile] + public unsafe struct ReliableSequencedPipelineStage : INetworkPipelineStage { - private ReliableUtility.Parameters m_ReliableParams; + static TransportFunctionPointer ReceiveFunctionPointer = new TransportFunctionPointer(Receive); + static TransportFunctionPointer SendFunctionPointer = new TransportFunctionPointer(Send); + static TransportFunctionPointer InitializeConnectionFunctionPointer = new TransportFunctionPointer(InitializeConnection); + public NetworkPipelineStage StaticInitialize(byte* staticInstanceBuffer, int staticInstanceBufferLength, INetworkParameter[] netParams) + { + ReliableUtility.Parameters param = default; + foreach (var netParam in netParams) + { + if (netParam.GetType() == typeof(ReliableUtility.Parameters)) + param = (ReliableUtility.Parameters)netParam; + } + if (param.WindowSize == 0) + param = new ReliableUtility.Parameters{WindowSize = ReliableUtility.ParameterConstants.WindowSize}; + UnsafeUtility.MemCpy(staticInstanceBuffer, ¶m, UnsafeUtility.SizeOf()); + return new NetworkPipelineStage( + Receive: ReceiveFunctionPointer, + Send: SendFunctionPointer, + InitializeConnection: InitializeConnectionFunctionPointer, + ReceiveCapacity: ReliableUtility.ProcessCapacityNeeded(param), + SendCapacity: ReliableUtility.ProcessCapacityNeeded(param), + HeaderCapacity: UnsafeUtility.SizeOf(), + SharedStateCapacity: ReliableUtility.SharedCapacityNeeded(param) + ); + } + public int StaticSize => UnsafeUtility.SizeOf(); - public NativeSlice Receive(NetworkPipelineContext ctx, NativeSlice inboundBuffer, ref bool needsResume, ref bool needsUpdate, ref bool needsSendUpdate) + [BurstCompile] + private static void Receive(ref NetworkPipelineContext ctx, ref InboundRecvBuffer inboundBuffer, ref NetworkPipelineStage.Requests requests) { - needsResume = false; // Request a send update to see if a queued packet needs to be resent later or if an ack packet should be sent - needsSendUpdate = true; + requests = NetworkPipelineStage.Requests.SendUpdate; + bool needsResume = false; - var context = default(DataStreamReader.Context); var header = default(ReliableUtility.PacketHeader); - var slice = default(NativeSlice); - unsafe + var slice = default(InboundRecvBuffer); + ReliableUtility.Context* reliable = (ReliableUtility.Context*) ctx.internalProcessBuffer; + ReliableUtility.SharedContext* shared = (ReliableUtility.SharedContext*) ctx.internalSharedProcessBuffer; + shared->errorCode = 0; + if (reliable->Resume == ReliableUtility.NullEntry) { - ReliableUtility.Context* reliable = (ReliableUtility.Context*) ctx.internalProcessBuffer.GetUnsafePtr(); - ReliableUtility.SharedContext* shared = (ReliableUtility.SharedContext*) ctx.internalSharedProcessBuffer.GetUnsafePtr(); - shared->errorCode = 0; - if (reliable->Resume == ReliableUtility.NullEntry) + if (inboundBuffer.bufferLength <= 0) { - if (inboundBuffer.Length <= 0) - return slice; - var reader = new DataStreamReader(inboundBuffer); - reader.ReadBytes(ref context, (byte*)&header, UnsafeUtility.SizeOf()); - - if (header.Type == (ushort)ReliableUtility.PacketType.Ack) - { - ReliableUtility.ReadAckPacket(ctx, header); - inboundBuffer = new NativeSlice(); - return inboundBuffer; - } + inboundBuffer = slice; + return; + } + var inboundArray = NativeArrayUnsafeUtility.ConvertExistingDataToNativeArray(inboundBuffer.buffer, inboundBuffer.bufferLength, Allocator.Invalid); +#if ENABLE_UNITY_COLLECTIONS_CHECKS + var safetyHandle = AtomicSafetyHandle.GetTempMemoryHandle(); + NativeArrayUnsafeUtility.SetAtomicSafetyHandle(ref inboundArray, safetyHandle); +#endif + var reader = new DataStreamReader(inboundArray); + reader.ReadBytes((byte*)&header, UnsafeUtility.SizeOf()); + + if (header.Type == (ushort)ReliableUtility.PacketType.Ack) + { + ReliableUtility.ReadAckPacket(ctx, header); + inboundBuffer = default; + return; + } - var result = ReliableUtility.Read(ctx, header); + var result = ReliableUtility.Read(ctx, header); - if (result >= 0) + if (result >= 0) + { + var nextExpectedSequenceId = (ushort) (reliable->Delivered + 1); + if (result == nextExpectedSequenceId) { - var nextExpectedSequenceId = (ushort) (reliable->Delivered + 1); - if (result == nextExpectedSequenceId) - { - reliable->Delivered = result; - slice = inboundBuffer.Slice(UnsafeUtility.SizeOf()); - - if (needsResume = SequenceHelpers.GreaterThan16((ushort) shared->ReceivedPackets.Sequence, - (ushort) result)) - { - reliable->Resume = (ushort)(result + 1); - } - } - else + reliable->Delivered = result; + slice = inboundBuffer.Slice(UnsafeUtility.SizeOf()); + + if (needsResume = SequenceHelpers.GreaterThan16((ushort) shared->ReceivedPackets.Sequence, + (ushort) result)) { - ReliableUtility.SetPacket(ctx.internalProcessBuffer, result, inboundBuffer.Slice(UnsafeUtility.SizeOf())); - slice = ReliableUtility.ResumeReceive(ctx, reliable->Delivered + 1, ref needsResume); + reliable->Resume = (ushort)(result + 1); } } - } - else - { - slice = ReliableUtility.ResumeReceive(ctx, reliable->Resume, ref needsResume); + else + { + ReliableUtility.SetPacket(ctx.internalProcessBuffer, result, inboundBuffer.Slice(UnsafeUtility.SizeOf())); + slice = ReliableUtility.ResumeReceive(ctx, reliable->Delivered + 1, ref needsResume); + } } } - return slice; + else + { + slice = ReliableUtility.ResumeReceive(ctx, reliable->Resume, ref needsResume); + } + if (needsResume) + requests |= NetworkPipelineStage.Requests.Resume; + inboundBuffer = slice; } - public InboundBufferVec Send(NetworkPipelineContext ctx, InboundBufferVec inboundBuffer, ref bool needsResume, ref bool needsUpdate) + [BurstCompile] + private static void Send(ref NetworkPipelineContext ctx, ref InboundSendBuffer inboundBuffer, ref NetworkPipelineStage.Requests requests) { // Request an update to see if a queued packet needs to be resent later or if an ack packet should be sent - needsUpdate = true; + requests = NetworkPipelineStage.Requests.Update; + bool needsResume = false; var header = new ReliableUtility.PacketHeader(); - unsafe - { - var reliable = (ReliableUtility.Context*) ctx.internalProcessBuffer.GetUnsafePtr(); - - needsResume = ReliableUtility.ReleaseOrResumePackets(ctx); - - if (inboundBuffer.buffer1.Length > 0) - { - reliable->LastSentTime = ctx.timestamp; + var reliable = (ReliableUtility.Context*) ctx.internalProcessBuffer; - ReliableUtility.Write(ctx, inboundBuffer, ref header); - ctx.header.WriteBytes((byte*)&header, UnsafeUtility.SizeOf()); - if (reliable->Resume != ReliableUtility.NullEntry) - needsResume = true; + needsResume = ReliableUtility.ReleaseOrResumePackets(ctx); + if (needsResume) + requests |= NetworkPipelineStage.Requests.Resume; - reliable->PreviousTimestamp = ctx.timestamp; - return inboundBuffer; - } + if (inboundBuffer.bufferLength > 0) + { + reliable->LastSentTime = ctx.timestamp; + ReliableUtility.Write(ctx, inboundBuffer, ref header); + ctx.header.WriteBytes((byte*)&header, UnsafeUtility.SizeOf()); if (reliable->Resume != ReliableUtility.NullEntry) - { - reliable->LastSentTime = ctx.timestamp; - var slice = ReliableUtility.ResumeSend(ctx, out header, ref needsResume); - ctx.header.Clear(); - ctx.header.WriteBytes((byte*)&header, UnsafeUtility.SizeOf()); - inboundBuffer.buffer1 = slice; - inboundBuffer.buffer2 = default(NativeSlice); - reliable->PreviousTimestamp = ctx.timestamp; - return inboundBuffer; - } - - if (ReliableUtility.ShouldSendAck(ctx)) - { - reliable->LastSentTime = ctx.timestamp; + requests |= NetworkPipelineStage.Requests.Resume; - ReliableUtility.WriteAckPacket(ctx, ref header); - ctx.header.WriteBytes((byte*)&header, UnsafeUtility.SizeOf()); - reliable->PreviousTimestamp = ctx.timestamp; - - // TODO: Sending dummy byte over since the pipeline won't send an empty payload (ignored on receive) - inboundBuffer.buffer1 = new NativeSlice(ctx.internalProcessBuffer, 0, 1); - return inboundBuffer; - } reliable->PreviousTimestamp = ctx.timestamp; - return inboundBuffer; + return; } - } - public void InitializeConnection(NativeSlice sendProcessBuffer, NativeSlice recvProcessBuffer, - NativeSlice sharedProcessBuffer) - { - if (sharedProcessBuffer.Length >= ReliableUtility.SharedCapacityNeeded(m_ReliableParams) && - (sendProcessBuffer.Length + recvProcessBuffer.Length) >= ReliableUtility.ProcessCapacityNeeded(m_ReliableParams) * 2) + if (reliable->Resume != ReliableUtility.NullEntry) { - ReliableUtility.InitializeContext(sharedProcessBuffer, sendProcessBuffer, recvProcessBuffer, m_ReliableParams); + reliable->LastSentTime = ctx.timestamp; + inboundBuffer = ReliableUtility.ResumeSend(ctx, out header, ref needsResume); + if (needsResume) + requests |= NetworkPipelineStage.Requests.Resume; + ctx.header.Clear(); + ctx.header.WriteBytes((byte*)&header, UnsafeUtility.SizeOf()); + reliable->PreviousTimestamp = ctx.timestamp; + return; } - } - public void Initialize(ReliableUtility.Parameters param) - { - m_ReliableParams = param; - } + if (ReliableUtility.ShouldSendAck(ctx)) + { + reliable->LastSentTime = ctx.timestamp; - public int HeaderCapacity => UnsafeUtility.SizeOf(); + ReliableUtility.WriteAckPacket(ctx, ref header); + ctx.header.WriteBytes((byte*)&header, UnsafeUtility.SizeOf()); + reliable->PreviousTimestamp = ctx.timestamp; - public int SharedStateCapacity - { - get { if (m_ReliableParams.WindowSize == 0) m_ReliableParams = new ReliableUtility.Parameters{WindowSize = ReliableUtility.ParameterConstants.WindowSize}; return ReliableUtility.SharedCapacityNeeded(m_ReliableParams);} - } - public int ReceiveCapacity - { - get { if (m_ReliableParams.WindowSize == 0) m_ReliableParams = new ReliableUtility.Parameters{WindowSize = ReliableUtility.ParameterConstants.WindowSize}; return ReliableUtility.ProcessCapacityNeeded(m_ReliableParams);} + // TODO: Sending dummy byte over since the pipeline won't send an empty payload (ignored on receive) + inboundBuffer.bufferWithHeadersLength = inboundBuffer.headerPadding + 1; + inboundBuffer.bufferWithHeaders = (byte*)UnsafeUtility.Malloc(inboundBuffer.bufferWithHeadersLength, 8, Allocator.Temp); + inboundBuffer.SetBufferFrombufferWithHeaders(); + return; + } + reliable->PreviousTimestamp = ctx.timestamp; } - public int SendCapacity + + [BurstCompile] + private static void InitializeConnection(byte* staticInstanceBuffer, int staticInstanceBufferLength, + byte* sendProcessBuffer, int sendProcessBufferLength, byte* recvProcessBuffer, int recvProcessBufferLength, + byte* sharedProcessBuffer, int sharedProcessBufferLength) { - get { if (m_ReliableParams.WindowSize == 0) m_ReliableParams = new ReliableUtility.Parameters{WindowSize = ReliableUtility.ParameterConstants.WindowSize}; return ReliableUtility.ProcessCapacityNeeded(m_ReliableParams);} + ReliableUtility.Parameters param; + UnsafeUtility.MemCpy(¶m, staticInstanceBuffer, UnsafeUtility.SizeOf()); + if (sharedProcessBufferLength >= ReliableUtility.SharedCapacityNeeded(param) && + (sendProcessBufferLength + recvProcessBufferLength) >= ReliableUtility.ProcessCapacityNeeded(param) * 2) + { + ReliableUtility.InitializeContext(sharedProcessBuffer, sharedProcessBufferLength, sendProcessBuffer, sendProcessBufferLength, recvProcessBuffer, recvProcessBufferLength, param); + } } } } diff --git a/Runtime/Pipelines/ReliableUtility.cs b/Runtime/Pipelines/ReliableUtility.cs index b986644..e7c8401 100644 --- a/Runtime/Pipelines/ReliableUtility.cs +++ b/Runtime/Pipelines/ReliableUtility.cs @@ -118,7 +118,8 @@ public struct PacketHeader public struct PacketInformation { public int SequenceId; - public int Size; + public ushort Size; + public ushort HeaderPadding; public long SendTime; } @@ -141,6 +142,10 @@ public struct PacketTimers public long ReceiveTime; } + private static int AlignedSizeOf() where T: struct + { + return (UnsafeUtility.SizeOf() + NetworkPipelineProcessor.AlignmentMinusOne) & (~NetworkPipelineProcessor.AlignmentMinusOne); + } public static int SharedCapacityNeeded(Parameters param) { int capacityNeeded; @@ -148,8 +153,8 @@ public static int SharedCapacityNeeded(Parameters param) { // Timers are stored for both remote packets (processing time) and local packets (round trip time) // The amount of timestamps needed in the queues is the same as the window size capacity - var timerDataSize = sizeof(PacketTimers) * param.WindowSize * 2; - capacityNeeded = sizeof(SharedContext) + timerDataSize; + var timerDataSize = AlignedSizeOf() * param.WindowSize * 2; + capacityNeeded = AlignedSizeOf() + timerDataSize; } return capacityNeeded; } @@ -159,20 +164,23 @@ public static int ProcessCapacityNeeded(Parameters param) int capacityNeeded; unsafe { - var infoSize = param.WindowSize * UnsafeUtility.SizeOf(); - var dataSize = param.WindowSize * Packet.Length; + var infoSize = AlignedSizeOf(); + var dataSize = (Packet.Length + UnsafeUtility.SizeOf() + NetworkPipelineProcessor.AlignmentMinusOne) & (~NetworkPipelineProcessor.AlignmentMinusOne); + infoSize *= param.WindowSize; + dataSize *= param.WindowSize; - capacityNeeded = sizeof(Context)+ infoSize + dataSize; + capacityNeeded = AlignedSizeOf() + infoSize + dataSize; } return capacityNeeded; } - public static unsafe SharedContext InitializeContext(NativeSlice sharedBuffer, NativeSlice sendBuffer, NativeSlice recvBuffer, Parameters param) + public static unsafe SharedContext InitializeContext(byte* sharedBuffer, int sharedBufferLength, + byte* sendBuffer, int sendBufferLength, byte* recvBuffer, int recvBufferLength, Parameters param) { - InitializeProcessContext(sendBuffer, param); - InitializeProcessContext(recvBuffer, param); + InitializeProcessContext(sendBuffer, sendBufferLength, param); + InitializeProcessContext(recvBuffer, recvBufferLength, param); - SharedContext* notifier = (SharedContext*) sharedBuffer.GetUnsafePtr(); + SharedContext* notifier = (SharedContext*) sharedBuffer; *notifier = new SharedContext { WindowSize = param.WindowSize, @@ -180,44 +188,43 @@ public static unsafe SharedContext InitializeContext(NativeSlice sharedBuf MinimumResendTime = DefaultMinimumResendTime, ReceivedPackets = new SequenceBufferContext { Sequence = NullEntry }, RttInfo = new RTTInfo { SmoothedVariance = 5, SmoothedRtt = 50, ResendTimeout = 50, LastRtt = 50}, - TimerDataOffset = UnsafeUtility.SizeOf(), - TimerDataStride = UnsafeUtility.SizeOf(), - RemoteTimerDataOffset = UnsafeUtility.SizeOf() + UnsafeUtility.SizeOf() * param.WindowSize, - RemoteTimerDataStride = UnsafeUtility.SizeOf() + TimerDataOffset = AlignedSizeOf(), + TimerDataStride = AlignedSizeOf(), + RemoteTimerDataOffset = AlignedSizeOf() + AlignedSizeOf() * param.WindowSize, + RemoteTimerDataStride = AlignedSizeOf() }; return *notifier; } - public static unsafe int InitializeProcessContext(NativeSlice self, Parameters param) + public static unsafe int InitializeProcessContext(byte* buffer, int bufferLength, Parameters param) { int totalCapacity = ProcessCapacityNeeded(param); - if (self.Length != totalCapacity) + if (bufferLength != totalCapacity) { return (int) ErrorCodes.InsufficientMemory; } - Context* ctx = (Context*) self.GetUnsafePtr(); + Context* ctx = (Context*) buffer; ctx->Capacity = param.WindowSize; - ctx->IndexStride = (UnsafeUtility.SizeOf() + 3) & ~3; - ctx->IndexPtrOffset = sizeof(Context); - ctx->DataStride = (Packet.Length + 3) & ~3; + ctx->IndexStride = AlignedSizeOf(); + ctx->IndexPtrOffset = AlignedSizeOf(); + ctx->DataStride = (Packet.Length + UnsafeUtility.SizeOf() + NetworkPipelineProcessor.AlignmentMinusOne) & (~NetworkPipelineProcessor.AlignmentMinusOne); ctx->DataPtrOffset = ctx->IndexPtrOffset + (ctx->IndexStride * ctx->Capacity); ctx->Resume = NullEntry; ctx->Delivered = NullEntry; - Release(self, 0, param.WindowSize); + Release(buffer, 0, param.WindowSize); return 0; } - public static unsafe void SetPacket(NativeSlice self, int sequence, NativeSlice data) + public static unsafe void SetPacket(byte* self, int sequence, InboundRecvBuffer data) { - SetPacket(self, sequence, data.GetUnsafeReadOnlyPtr(), data.Length); + SetPacket(self, sequence, data.buffer, data.bufferLength); } - public static unsafe void SetPacket(NativeSlice self, int sequence, void* data, int length) + public static unsafe void SetPacket(byte* self, int sequence, void* data, int length) { - byte *ptr = (byte*)self.GetUnsafePtr(); - Context* ctx = (Context*) ptr; + Context* ctx = (Context*) self; if (length > ctx->DataStride) #if ENABLE_UNITY_COLLECTIONS_CHECKS @@ -230,11 +237,12 @@ public static unsafe void SetPacket(NativeSlice self, int sequence, void* PacketInformation* info = GetPacketInformation(self, sequence); info->SequenceId = sequence; - info->Size = length; + info->Size = (ushort)length; + info->HeaderPadding = 0; // Not used for packets queued for resume receive info->SendTime = -1; // Not used for packets queued for resume receive var offset = ctx->DataPtrOffset + (index * ctx->DataStride); - void* dataPtr = (ptr + offset); + void* dataPtr = (self + offset); UnsafeUtility.MemCpy(dataPtr, data, length); } @@ -249,13 +257,12 @@ public static unsafe void SetPacket(NativeSlice self, int sequence, void* /// The packet header which we'll store with the packet payload. /// The packet data which we're storing. /// - public static unsafe void SetHeaderAndPacket(NativeSlice self, int sequence, PacketHeader header, InboundBufferVec data, long timestamp) + public static unsafe void SetHeaderAndPacket(byte* self, int sequence, PacketHeader header, InboundSendBuffer data, long timestamp) { - byte *ptr = (byte*)self.GetUnsafePtr(); - Context* ctx = (Context*) ptr; - int totalSize = data.buffer1.Length + data.buffer2.Length; + Context* ctx = (Context*) self; + int totalSize = data.bufferLength + data.headerPadding; - if (totalSize > ctx->DataStride) + if (totalSize + UnsafeUtility.SizeOf() > ctx->DataStride) #if ENABLE_UNITY_COLLECTIONS_CHECKS throw new OverflowException(); #else @@ -265,42 +272,39 @@ public static unsafe void SetHeaderAndPacket(NativeSlice self, int sequenc PacketInformation* info = GetPacketInformation(self, sequence); info->SequenceId = sequence; - info->Size = totalSize; + info->Size = (ushort)totalSize; + info->HeaderPadding = (ushort)data.headerPadding; info->SendTime = timestamp; Packet* packet = GetPacket(self, sequence); packet->Header = header; var offset = (ctx->DataPtrOffset + (index * ctx->DataStride)) + UnsafeUtility.SizeOf(); - void* dataPtr = (ptr + offset); + void* dataPtr = (self + offset); - if (data.buffer1.Length > 0) - UnsafeUtility.MemCpy(dataPtr, data.buffer1.GetUnsafeReadOnlyPtr(), data.buffer1.Length); - if (data.buffer2.Length > 0) - UnsafeUtility.MemCpy(&dataPtr + data.buffer1.Length, data.buffer2.GetUnsafeReadOnlyPtr(), data.buffer2.Length); + if (data.bufferLength > 0) + UnsafeUtility.MemCpy((byte*)dataPtr + data.headerPadding, data.buffer, data.bufferLength); } - public static unsafe PacketInformation* GetPacketInformation(NativeSlice self, int sequence) + public static unsafe PacketInformation* GetPacketInformation(byte* self, int sequence) { - byte *ptr = (byte*)self.GetUnsafePtr(); - Context* ctx = (Context*) ptr; + Context* ctx = (Context*) self; var index = sequence % ctx->Capacity; - return (PacketInformation*) ((ptr + ctx->IndexPtrOffset) + (index * ctx->IndexStride)); + return (PacketInformation*) ((self + ctx->IndexPtrOffset) + (index * ctx->IndexStride)); } - public static unsafe Packet* GetPacket(NativeSlice self, int sequence) + public static unsafe Packet* GetPacket(byte* self, int sequence) { - byte *ptr = (byte*)self.GetUnsafePtr(); - Context* ctx = (Context*) ptr; + Context* ctx = (Context*) self; var index = sequence % ctx->Capacity; var offset = ctx->DataPtrOffset + (index * ctx->DataStride); - return (Packet*) (ptr + offset); + return (Packet*) (self + offset); } - public static unsafe bool TryAquire(NativeSlice self, int sequence) + public static unsafe bool TryAquire(byte* self, int sequence) { - Context* ctx = (Context*) self.GetUnsafePtr(); + Context* ctx = (Context*) self; var index = sequence % ctx->Capacity; @@ -313,35 +317,33 @@ public static unsafe bool TryAquire(NativeSlice self, int sequence) return false; } - public static unsafe void Release(NativeSlice self, int sequence) + public static unsafe void Release(byte* self, int sequence) { Release(self, sequence, 1); } - public static unsafe void Release(NativeSlice self, int start_sequence, int count) + public static unsafe void Release(byte* self, int start_sequence, int count) { - Context* ctx = (Context*) self.GetUnsafePtr(); + Context* ctx = (Context*) self; for (int i = 0; i < count; i++) { SetIndex(self, (start_sequence + i) % ctx->Capacity, NullEntry); } } - static unsafe void SetIndex(NativeSlice self, int index, int sequence) + static unsafe void SetIndex(byte* self, int index, int sequence) { - byte *ptr = (byte*)self.GetUnsafePtr(); - Context* ctx = (Context*) ptr; + Context* ctx = (Context*) self; - int* value = (int*) ((ptr + ctx->IndexPtrOffset) + (index * ctx->IndexStride)); + int* value = (int*) ((self + ctx->IndexPtrOffset) + (index * ctx->IndexStride)); *value = sequence; } - static unsafe int GetIndex(NativeSlice self, int index) + static unsafe int GetIndex(byte* self, int index) { - byte *ptr = (byte*)self.GetUnsafePtr(); - Context* ctx = (Context*) ptr; + Context* ctx = (Context*) self; - int* value = (int*) ((ptr + ctx->IndexPtrOffset) + (index * ctx->IndexStride)); + int* value = (int*) ((self + ctx->IndexPtrOffset) + (index * ctx->IndexStride)); return *value; } @@ -357,8 +359,8 @@ static unsafe int GetIndex(NativeSlice self, int index) /// public static unsafe bool ReleaseOrResumePackets(NetworkPipelineContext context) { - SharedContext* reliable = (SharedContext*) context.internalSharedProcessBuffer.GetUnsafePtr(); - Context* ctx = (Context*) context.internalProcessBuffer.GetUnsafePtr(); + SharedContext* reliable = (SharedContext*) context.internalSharedProcessBuffer; + Context* ctx = (Context*) context.internalProcessBuffer; // Last sequence ID and ackmask we received from the remote peer, these are confirmed delivered packets var lastReceivedAckMask = reliable->SentPackets.AckMask; @@ -410,12 +412,12 @@ public static unsafe bool ReleaseOrResumePackets(NetworkPipelineContext context) /// The first packet which we need to retrieve now, there could be more after that. /// Indicates if we need the pipeline to resume again. /// - public static unsafe NativeSlice ResumeReceive(NetworkPipelineContext context, int startSequence, ref bool needsResume) + public static unsafe InboundRecvBuffer ResumeReceive(NetworkPipelineContext context, int startSequence, ref bool needsResume) { - if (startSequence == NullEntry) return default(NativeSlice); + if (startSequence == NullEntry) return default; - SharedContext* shared = (SharedContext*) context.internalSharedProcessBuffer.GetUnsafePtr(); - Context* reliable = (Context*)context.internalProcessBuffer.GetUnsafePtr(); + SharedContext* shared = (SharedContext*) context.internalSharedProcessBuffer; + Context* reliable = (Context*)context.internalProcessBuffer; reliable->Resume = NullEntry; @@ -424,7 +426,9 @@ public static unsafe NativeSlice ResumeReceive(NetworkPipelineContext cont if (info->SequenceId == startSequence) { var offset = reliable->DataPtrOffset + ((startSequence % reliable->Capacity) * reliable->DataStride); - NativeSlice slice = new NativeSlice(context.internalProcessBuffer, offset, info->Size); + InboundRecvBuffer inBuffer; + inBuffer.buffer = context.internalProcessBuffer + offset; + inBuffer.bufferLength = info->Size; reliable->Delivered = startSequence; if ((ushort)(startSequence + 1) <= latestReceivedPacket) @@ -432,9 +436,9 @@ public static unsafe NativeSlice ResumeReceive(NetworkPipelineContext cont reliable->Resume = (ushort)(startSequence + 1); needsResume = true; } - return slice; + return inBuffer; } - return default(NativeSlice); + return default; } /// @@ -447,10 +451,10 @@ public static unsafe NativeSlice ResumeReceive(NetworkPipelineContext cont /// Indicates if a pipeline resume is needed again. /// Buffer slice to packet payload. /// - public static unsafe NativeSlice ResumeSend(NetworkPipelineContext context, out PacketHeader header, ref bool needsResume) + public static unsafe InboundSendBuffer ResumeSend(NetworkPipelineContext context, out PacketHeader header, ref bool needsResume) { - SharedContext* reliable = (SharedContext*) context.internalSharedProcessBuffer.GetUnsafePtr(); - Context* ctx = (Context*)context.internalProcessBuffer.GetUnsafePtr(); + SharedContext* reliable = (SharedContext*) context.internalSharedProcessBuffer; + Context* ctx = (Context*)context.internalProcessBuffer; #if ENABLE_UNITY_COLLECTIONS_CHECKS if (ctx->Resume == NullEntry) @@ -473,7 +477,11 @@ public static unsafe NativeSlice ResumeSend(NetworkPipelineContext context var offset = (ctx->DataPtrOffset + ((sequence % ctx->Capacity) * ctx->DataStride)) + UnsafeUtility.SizeOf(); - NativeSlice slice = new NativeSlice(context.internalProcessBuffer, offset, information->Size); + var inbound = default(InboundSendBuffer); + inbound.bufferWithHeaders = context.internalProcessBuffer + offset; + inbound.bufferWithHeadersLength = information->Size; + inbound.headerPadding = information->HeaderPadding; + inbound.SetBufferFrombufferWithHeaders(); reliable->stats.PacketsResent++; needsResume = false; @@ -490,7 +498,7 @@ public static unsafe NativeSlice ResumeSend(NetworkPipelineContext context ctx->Resume = i; } } - return slice; + return inbound; } /// @@ -501,9 +509,9 @@ public static unsafe NativeSlice ResumeSend(NetworkPipelineContext context /// Buffer with packet data. /// Packet header which will be populated. /// Sequence ID assigned to this packet. - public static unsafe int Write(NetworkPipelineContext context, InboundBufferVec inboundBuffer, ref PacketHeader header) + public static unsafe int Write(NetworkPipelineContext context, InboundSendBuffer inboundBuffer, ref PacketHeader header) { - SharedContext* reliable = (SharedContext*) context.internalSharedProcessBuffer.GetUnsafePtr(); + SharedContext* reliable = (SharedContext*) context.internalSharedProcessBuffer; var sequence = (ushort) reliable->SentPackets.Sequence; @@ -541,7 +549,7 @@ public static unsafe int Write(NetworkPipelineContext context, InboundBufferVec /// public static unsafe void WriteAckPacket(NetworkPipelineContext context, ref PacketHeader header) { - SharedContext* reliable = (SharedContext*) context.internalSharedProcessBuffer.GetUnsafePtr(); + SharedContext* reliable = (SharedContext*) context.internalSharedProcessBuffer; header.Type = (ushort)PacketType.Ack; header.AckedSequenceId = (ushort) reliable->ReceivedPackets.Sequence; @@ -551,7 +559,7 @@ public static unsafe void WriteAckPacket(NetworkPipelineContext context, ref Pac reliable->ReceivedPackets.Acked = reliable->ReceivedPackets.Sequence; } - public static unsafe void StoreTimestamp(NativeSlice sharedBuffer, ushort sequenceId, long timestamp) + public static unsafe void StoreTimestamp(byte* sharedBuffer, ushort sequenceId, long timestamp) { var timerData = GetLocalPacketTimer(sharedBuffer, sequenceId); timerData->SequenceId = sequenceId; @@ -560,9 +568,9 @@ public static unsafe void StoreTimestamp(NativeSlice sharedBuffer, ushort timerData->ReceiveTime = 0; } - public static unsafe void StoreReceiveTimestamp(NativeSlice sharedBuffer, ushort sequenceId, long timestamp, ushort processingTime) + public static unsafe void StoreReceiveTimestamp(byte* sharedBuffer, ushort sequenceId, long timestamp, ushort processingTime) { - var sharedCtx = (SharedContext*) sharedBuffer.GetUnsafePtr(); + var sharedCtx = (SharedContext*) sharedBuffer; var rttInfo = sharedCtx->RttInfo; var timerData = GetLocalPacketTimer(sharedBuffer, sequenceId); if (timerData != null && timerData->SequenceId == sequenceId) @@ -582,22 +590,22 @@ public static unsafe void StoreReceiveTimestamp(NativeSlice sharedBuffer, } } - public static unsafe void StoreRemoteReceiveTimestamp(NativeSlice sharedBuffer, ushort sequenceId, long timestamp) + public static unsafe void StoreRemoteReceiveTimestamp(byte* sharedBuffer, ushort sequenceId, long timestamp) { var timerData = GetRemotePacketTimer(sharedBuffer, sequenceId); timerData->SequenceId = sequenceId; timerData->ReceiveTime = timestamp; } - static unsafe int CurrentResendTime(NativeSlice sharedBuffer) + static unsafe int CurrentResendTime(byte* sharedBuffer) { - var sharedCtx = (SharedContext*) sharedBuffer.GetUnsafePtr(); + var sharedCtx = (SharedContext*) sharedBuffer; if (sharedCtx->RttInfo.ResendTimeout > MaximumResendTime) return MaximumResendTime; return Math.Max(sharedCtx->RttInfo.ResendTimeout, sharedCtx->MinimumResendTime); } - public static unsafe ushort CalculateProcessingTime(NativeSlice sharedBuffer, ushort sequenceId, long timestamp) + public static unsafe ushort CalculateProcessingTime(byte* sharedBuffer, ushort sequenceId, long timestamp) { // Look up previously recorded receive timestamp, subtract that from current timestamp and return as processing time var timerData = GetRemotePacketTimer(sharedBuffer, sequenceId); @@ -606,19 +614,19 @@ public static unsafe ushort CalculateProcessingTime(NativeSlice sharedBuff return 0; } - public static unsafe PacketTimers* GetLocalPacketTimer(NativeSlice sharedBuffer, ushort sequenceId) + public static unsafe PacketTimers* GetLocalPacketTimer(byte* sharedBuffer, ushort sequenceId) { - var sharedCtx = (SharedContext*) sharedBuffer.GetUnsafePtr(); + var sharedCtx = (SharedContext*) sharedBuffer; var index = sequenceId % sharedCtx->WindowSize; - var timerPtr = (long)sharedBuffer.GetUnsafePtr() + sharedCtx->TimerDataOffset + sharedCtx->TimerDataStride * index; + var timerPtr = (long)sharedBuffer + sharedCtx->TimerDataOffset + sharedCtx->TimerDataStride * index; return (PacketTimers*) timerPtr; } - public static unsafe PacketTimers* GetRemotePacketTimer(NativeSlice sharedBuffer, ushort sequenceId) + public static unsafe PacketTimers* GetRemotePacketTimer(byte* sharedBuffer, ushort sequenceId) { - var sharedCtx = (SharedContext*) sharedBuffer.GetUnsafePtr(); + var sharedCtx = (SharedContext*) sharedBuffer; var index = sequenceId % sharedCtx->WindowSize; - var timerPtr = (long)sharedBuffer.GetUnsafePtr() + sharedCtx->RemoteTimerDataOffset + sharedCtx->RemoteTimerDataStride * index; + var timerPtr = (long)sharedBuffer + sharedCtx->RemoteTimerDataOffset + sharedCtx->RemoteTimerDataStride * index; return (PacketTimers*) timerPtr; } @@ -632,7 +640,7 @@ public static unsafe ushort CalculateProcessingTime(NativeSlice sharedBuff /// Sequence ID of the received packet. public static unsafe int Read(NetworkPipelineContext context, PacketHeader header) { - SharedContext* reliable = (SharedContext*) context.internalSharedProcessBuffer.GetUnsafePtr(); + SharedContext* reliable = (SharedContext*) context.internalSharedProcessBuffer; reliable->stats.PacketsReceived++; if (SequenceHelpers.StalePacket( @@ -698,7 +706,7 @@ public static unsafe int Read(NetworkPipelineContext context, PacketHeader heade public static unsafe void ReadAckPacket(NetworkPipelineContext context, PacketHeader header) { - SharedContext* reliable = (SharedContext*) context.internalSharedProcessBuffer.GetUnsafePtr(); + SharedContext* reliable = (SharedContext*) context.internalSharedProcessBuffer; // Store receive timestamp for our acked sequence ID with remote processing time StoreReceiveTimestamp(context.internalSharedProcessBuffer, header.AckedSequenceId, context.timestamp, header.ProcessingTime); @@ -717,8 +725,8 @@ public static unsafe void ReadAckPacket(NetworkPipelineContext context, PacketHe public static unsafe bool ShouldSendAck(NetworkPipelineContext ctx) { - var reliable = (Context*) ctx.internalProcessBuffer.GetUnsafePtr(); - var shared = (SharedContext*) ctx.internalSharedProcessBuffer.GetUnsafePtr(); + var reliable = (Context*) ctx.internalProcessBuffer; + var shared = (SharedContext*) ctx.internalSharedProcessBuffer; // If more than one full frame (timestamp - prevTimestamp = one frame) has elapsed then send ack packet // and if the last received sequence ID has not been acked yet @@ -728,24 +736,10 @@ public static unsafe bool ShouldSendAck(NetworkPipelineContext ctx) return false; } - public static unsafe void SetMinimumResendTime(int value, UdpNetworkDriver driver, - NetworkPipeline pipeline, int stageId, NetworkConnection con) - { - NativeSlice receiveBuffer = default(NativeSlice); - NativeSlice sendBuffer = default(NativeSlice); - NativeSlice sharedBuffer = default(NativeSlice); - driver.GetPipelineBuffers(pipeline, stageId, con, ref receiveBuffer, ref sendBuffer, ref sharedBuffer); - var sharedCtx = (ReliableUtility.SharedContext*) sharedBuffer.GetUnsafePtr(); - sharedCtx->MinimumResendTime = value; - } - - public static unsafe void SetMinimumResendTime(int value, LocalNetworkDriver driver, - NetworkPipeline pipeline, int stageId, NetworkConnection con) + public static unsafe void SetMinimumResendTime(int value, NetworkDriver driver, + NetworkPipeline pipeline, NetworkConnection con) { - NativeSlice receiveBuffer = default(NativeSlice); - NativeSlice sendBuffer = default(NativeSlice); - NativeSlice sharedBuffer = default(NativeSlice); - driver.GetPipelineBuffers(pipeline, stageId, con, ref receiveBuffer, ref sendBuffer, ref sharedBuffer); + driver.GetPipelineBuffers(pipeline, NetworkPipelineStageCollection.GetStageId(typeof(ReliableSequencedPipelineStage)), con, out var receiveBuffer, out var sendBuffer, out var sharedBuffer); var sharedCtx = (ReliableUtility.SharedContext*) sharedBuffer.GetUnsafePtr(); sharedCtx->MinimumResendTime = value; } diff --git a/Runtime/Pipelines/SimulatorPipelineStage.cs b/Runtime/Pipelines/SimulatorPipelineStage.cs index 7954e9b..9fb20e7 100644 --- a/Runtime/Pipelines/SimulatorPipelineStage.cs +++ b/Runtime/Pipelines/SimulatorPipelineStage.cs @@ -1,144 +1,205 @@ using Unity.Collections; using Unity.Collections.LowLevel.Unsafe; using Unity.Networking.Transport.Utilities; +using Unity.Burst; namespace Unity.Networking.Transport { - [NetworkPipelineInitilize(typeof(SimulatorUtility.Parameters))] - public struct SimulatorPipelineStage : INetworkPipelineStage + [BurstCompile] + public unsafe struct SimulatorPipelineStage : INetworkPipelineStage { - private SimulatorUtility.Parameters m_SimulatorParams; + static TransportFunctionPointer ReceiveFunctionPointer = new TransportFunctionPointer(Receive); + static TransportFunctionPointer SendFunctionPointer = new TransportFunctionPointer(Send); + static TransportFunctionPointer InitializeConnectionFunctionPointer = new TransportFunctionPointer(InitializeConnection); + public NetworkPipelineStage StaticInitialize(byte* staticInstanceBuffer, int staticInstanceBufferLength, INetworkParameter[] netParams) + { + SimulatorUtility.Parameters param = default; + foreach (var netParam in netParams) + { + if (netParam is SimulatorUtility.Parameters) + param = (SimulatorUtility.Parameters)netParam; + } + + UnsafeUtility.MemCpy(staticInstanceBuffer, ¶m, UnsafeUtility.SizeOf()); + + return new NetworkPipelineStage( + Receive: ReceiveFunctionPointer, + Send: SendFunctionPointer, + InitializeConnection: InitializeConnectionFunctionPointer, + ReceiveCapacity: param.MaxPacketCount * (param.MaxPacketSize+UnsafeUtility.SizeOf()), + SendCapacity: 0, + HeaderCapacity: 0, + SharedStateCapacity: UnsafeUtility.SizeOf() + ); + } - // Setup simulation parameters which get capacity function depends on, so buffer size can be correctly allocated - public void Initialize(SimulatorUtility.Parameters param) + [BurstCompile] + private static void InitializeConnection(byte* staticInstanceBuffer, int staticInstanceBufferLength, + byte* sendProcessBuffer, int sendProcessBufferLength, byte* recvProcessBuffer, int recvProcessBufferLength, + byte* sharedProcessBuffer, int sharedProcessBufferLength) { - m_SimulatorParams = param; + SimulatorUtility.Parameters param = default; + + UnsafeUtility.MemCpy(¶m, staticInstanceBuffer, UnsafeUtility.SizeOf()); + if (sharedProcessBufferLength >= UnsafeUtility.SizeOf()) + { + SimulatorUtility.InitializeContext(param, sharedProcessBuffer); + } } - public unsafe NativeSlice Receive(NetworkPipelineContext ctx, NativeSlice inboundBuffer, ref bool needsResume, ref bool needsUpdate, ref bool needsSendUpdate) + [BurstCompile] + private static void Send(ref NetworkPipelineContext ctx, ref InboundSendBuffer inboundBuffer, ref NetworkPipelineStage.Requests requests) { - var param = (SimulatorUtility.Context*) ctx.internalSharedProcessBuffer.GetUnsafePtr(); - var simulator = new SimulatorUtility(m_SimulatorParams.MaxPacketCount, m_SimulatorParams.MaxPacketSize, m_SimulatorParams.PacketDelayMs, m_SimulatorParams.PacketJitterMs); - if (inboundBuffer.Length > m_SimulatorParams.MaxPacketSize) + } + + [BurstCompile] + private static void Receive(ref NetworkPipelineContext ctx, ref InboundRecvBuffer inboundBuffer, ref NetworkPipelineStage.Requests requests) + { + var context = (SimulatorUtility.Context*) ctx.internalSharedProcessBuffer; + var param = *(SimulatorUtility.Parameters*) ctx.staticInstanceBuffer; + var simulator = new SimulatorUtility(param.MaxPacketCount, param.MaxPacketSize, param.PacketDelayMs, param.PacketJitterMs); + + if (inboundBuffer.bufferLength > param.MaxPacketSize) { //UnityEngine.Debug.LogWarning("Incoming packet too large for internal storage buffer. Passing through. [buffer=" + inboundBuffer.Length + " packet=" + param->MaxPacketSize + "]"); // TODO: Add error code for this - return inboundBuffer; + return; } var timestamp = ctx.timestamp; // Inbound buffer is empty if this is a resumed receive - if (inboundBuffer.Length > 0) + if (inboundBuffer.bufferLength > 0) { - param->PacketCount++; + context->PacketCount++; - if (simulator.ShouldDropPacket(param, m_SimulatorParams, timestamp)) + if (simulator.ShouldDropPacket(context, param, timestamp)) { - param->PacketDropCount++; - return default; + context->PacketDropCount++; + inboundBuffer = default; + return; } - var bufferVec = default(InboundBufferVec); - bufferVec.buffer1 = inboundBuffer; - if (param->PacketDelayMs == 0 || - !simulator.DelayPacket(ref ctx, bufferVec, ref needsUpdate, timestamp)) + var bufferVec = default(InboundSendBuffer); + bufferVec.bufferWithHeaders = inboundBuffer.buffer; + bufferVec.bufferWithHeadersLength = inboundBuffer.bufferLength; + bufferVec.buffer = inboundBuffer.buffer; + bufferVec.bufferLength = inboundBuffer.bufferLength; + bufferVec.headerPadding = 0; + if (context->PacketDelayMs == 0 || + !simulator.DelayPacket(ref ctx, bufferVec, ref requests, timestamp)) { - return inboundBuffer; + return; } } - NativeSlice returnPacket = default(NativeSlice); - if (simulator.GetDelayedPacket(ref ctx, ref returnPacket, ref needsResume, ref needsUpdate, timestamp)) - return returnPacket; - - return default; - } - - public InboundBufferVec Send(NetworkPipelineContext ctx, InboundBufferVec inboundBuffer, ref bool needsResume, ref bool needsUpdate) - { - return inboundBuffer; - } + InboundSendBuffer returnPacket = default; + if (simulator.GetDelayedPacket(ref ctx, ref returnPacket, ref requests, timestamp)) + { + inboundBuffer.buffer = returnPacket.bufferWithHeaders; + inboundBuffer.bufferLength = returnPacket.bufferWithHeadersLength; + return; + } - public unsafe void InitializeConnection(NativeSlice sendProcessBuffer, NativeSlice recvProcessBuffer, - NativeSlice sharedProcessBuffer) - { - if (sharedProcessBuffer.Length >= UnsafeUtility.SizeOf()) - SimulatorUtility.InitializeContext(m_SimulatorParams, sharedProcessBuffer); + inboundBuffer = default; } - - public int ReceiveCapacity => m_SimulatorParams.MaxPacketCount * (m_SimulatorParams.MaxPacketSize+UnsafeUtility.SizeOf()); - public int SendCapacity => 0; - public int HeaderCapacity => 0; - public int SharedStateCapacity => UnsafeUtility.SizeOf(); + public int StaticSize => UnsafeUtility.SizeOf(); } - [NetworkPipelineInitilize(typeof(SimulatorUtility.Parameters))] - public struct SimulatorPipelineStageInSend : INetworkPipelineStage + [BurstCompile] + public unsafe struct SimulatorPipelineStageInSend : INetworkPipelineStage { - private SimulatorUtility.Parameters m_SimulatorParams; - - // Setup simulation parameters which get capacity function depends on, so buffer size can be correctly allocated - public void Initialize(SimulatorUtility.Parameters param) + static TransportFunctionPointer ReceiveFunctionPointer = new TransportFunctionPointer(Receive); + static TransportFunctionPointer SendFunctionPointer = new TransportFunctionPointer(Send); + static TransportFunctionPointer InitializeConnectionFunctionPointer = new TransportFunctionPointer(InitializeConnection); + public NetworkPipelineStage StaticInitialize(byte* staticInstanceBuffer, int staticInstanceBufferLength, INetworkParameter[] netParams) { - m_SimulatorParams = param; + SimulatorUtility.Parameters param = default; + foreach (var netParam in netParams) + { + if (netParam is SimulatorUtility.Parameters parameters) + param = parameters; + } + + UnsafeUtility.MemCpy(staticInstanceBuffer, ¶m, UnsafeUtility.SizeOf()); + + return new NetworkPipelineStage( + Receive: ReceiveFunctionPointer, + Send: SendFunctionPointer, + InitializeConnection: InitializeConnectionFunctionPointer, + ReceiveCapacity: 0, + SendCapacity: param.MaxPacketCount * (param.MaxPacketSize+UnsafeUtility.SizeOf()), + HeaderCapacity: 0, + SharedStateCapacity: UnsafeUtility.SizeOf() + ); } - public NativeSlice Receive(NetworkPipelineContext ctx, NativeSlice inboundBuffer, ref bool needsResume, ref bool needsUpdate, ref bool needsSendUpdate) + [BurstCompile] + private static void InitializeConnection(byte* staticInstanceBuffer, int staticInstanceBufferLength, + byte* sendProcessBuffer, int sendProcessBufferLength, byte* recvProcessBuffer, int recvProcessBufferLength, + byte* sharedProcessBuffer, int sharedProcessBufferLength) { - return new NativeSlice(inboundBuffer, 0, inboundBuffer.Length); + SimulatorUtility.Parameters param = default; + + UnsafeUtility.MemCpy(¶m, staticInstanceBuffer, UnsafeUtility.SizeOf()); + if (sharedProcessBufferLength >= UnsafeUtility.SizeOf()) + { + SimulatorUtility.InitializeContext(param, sharedProcessBuffer); + } } - public unsafe InboundBufferVec Send(NetworkPipelineContext ctx, InboundBufferVec inboundBuffer, ref bool needsResume, ref bool needsUpdate) + [BurstCompile] + private static void Send(ref NetworkPipelineContext ctx, ref InboundSendBuffer inboundBuffer, ref NetworkPipelineStage.Requests requests) { - var param = (SimulatorUtility.Context*) ctx.internalSharedProcessBuffer.GetUnsafePtr(); - var simulator = new SimulatorUtility(m_SimulatorParams.MaxPacketCount, m_SimulatorParams.MaxPacketSize, m_SimulatorParams.PacketDelayMs, m_SimulatorParams.PacketJitterMs); - if (inboundBuffer.buffer1.Length+inboundBuffer.buffer2.Length > m_SimulatorParams.MaxPacketSize) + var context = (SimulatorUtility.Context*) ctx.internalSharedProcessBuffer; + var param = *(SimulatorUtility.Parameters*) ctx.staticInstanceBuffer; + + var simulator = new SimulatorUtility(param.MaxPacketCount, param.MaxPacketSize, param.PacketDelayMs, param.PacketJitterMs); + if (inboundBuffer.headerPadding+inboundBuffer.bufferLength > param.MaxPacketSize) { - //UnityEngine.Debug.LogWarning("Incoming packet too large for internal storage buffer. Passing through. [buffer=" + (inboundBuffer.buffer1.Length+inboundBuffer.buffer2.Length) + " packet=" + param->MaxPacketSize + "]"); - return inboundBuffer; + //UnityEngine.Debug.LogWarning("Incoming packet too large for internal storage buffer. Passing through. [buffer=" + (inboundBuffer.headerPadding+inboundBuffer.buffer.Length) + " packet=" + param.MaxPacketSize + "]"); + return; } var timestamp = ctx.timestamp; - if (inboundBuffer.buffer1.Length > 0) + if (inboundBuffer.bufferLength > 0) { - param->PacketCount++; + context->PacketCount++; + + if (simulator.ShouldDropPacket(context, param, timestamp)) + { + context->PacketDropCount++; + inboundBuffer = default; + return; + } - if (simulator.ShouldDropPacket(param, m_SimulatorParams, timestamp)) + if (context->FuzzFactor > 0) { - param->PacketDropCount++; - return default; + simulator.FuzzPacket(context, ref inboundBuffer); } - if (param->PacketDelayMs == 0 || - !simulator.DelayPacket(ref ctx, inboundBuffer, ref needsUpdate, timestamp)) + if (context->PacketDelayMs == 0 || + !simulator.DelayPacket(ref ctx, inboundBuffer, ref requests, timestamp)) { - return inboundBuffer; + return; } } - NativeSlice returnPacket = default; - if (simulator.GetDelayedPacket(ref ctx, ref returnPacket, ref needsResume, ref needsUpdate, timestamp)) + InboundSendBuffer returnPacket = default; + if (simulator.GetDelayedPacket(ref ctx, ref returnPacket, ref requests, timestamp)) { - inboundBuffer.buffer1 = returnPacket; - inboundBuffer.buffer2 = default; - return inboundBuffer; + inboundBuffer = returnPacket; + return; } - - return default; + inboundBuffer = default; } - public void InitializeConnection(NativeSlice sendProcessBuffer, NativeSlice recvProcessBuffer, - NativeSlice sharedProcessBuffer) + [BurstCompile] + private static void Receive(ref NetworkPipelineContext ctx, ref InboundRecvBuffer inboundBuffer, + ref NetworkPipelineStage.Requests requests) { - if (sharedProcessBuffer.Length >= UnsafeUtility.SizeOf()) - SimulatorUtility.InitializeContext(m_SimulatorParams, sharedProcessBuffer); } - - public int ReceiveCapacity => 0; - public int SendCapacity => m_SimulatorParams.MaxPacketCount * (m_SimulatorParams.MaxPacketSize+UnsafeUtility.SizeOf()); - public int HeaderCapacity => 0; - public int SharedStateCapacity => UnsafeUtility.SizeOf(); + public int StaticSize => UnsafeUtility.SizeOf(); } } diff --git a/Runtime/Pipelines/SimulatorUtility.cs b/Runtime/Pipelines/SimulatorUtility.cs index 8b39899..0bd528e 100644 --- a/Runtime/Pipelines/SimulatorUtility.cs +++ b/Runtime/Pipelines/SimulatorUtility.cs @@ -1,5 +1,4 @@ using System.Runtime.InteropServices; -using Unity.Collections; using Unity.Collections.LowLevel.Unsafe; using Random = Unity.Mathematics.Random; @@ -49,6 +48,23 @@ public struct Parameters : INetworkParameter /// A percentage of 5 means approximately every 20th packet will be dropped. /// public int PacketDropPercentage; + /// + /// Use the fuzz factor when you want to fuzz a packet. For every packet + /// a random number generator is used to determine if the packet should have the internal bits flipped. + /// A percentage of 5 means approximately every 20th packet will be fuzzed, and that each bit in the packet + /// has a 5 percent chance to get flipped. + /// + public int FuzzFactor; + /// + /// Use the fuzz offset in conjunction with the fuzz factor, the fuzz offset will offset where we start + /// flipping bits. This is useful if you want to only fuzz a part of the packet. + /// + public int FuzzOffset; + /// + /// The random seed is used to set the initial seed of the random number generator. This is useful to get + /// deterministic runs in tests for example that are dependant on the random number generator. + /// + public uint RandomSeed; } [StructLayout(LayoutKind.Sequential)] @@ -59,7 +75,10 @@ public struct Context public int PacketDelayMs; public int PacketJitterMs; public int PacketDrop; + public int FuzzOffset; + public int FuzzFactor; + public uint RandomSeed; public Random Random; // Statistics @@ -75,7 +94,8 @@ public struct Context public struct DelayedPacket { public int processBufferOffset; - public int packetSize; + public ushort packetSize; + public ushort packetHeaderPadding; public long delayUntil; } @@ -87,19 +107,27 @@ public SimulatorUtility(int packetCount, int maxPacketSize, int packetDelayMs, i m_PacketJitterMs = packetJitterMs; } - public static unsafe void InitializeContext(Parameters param, NativeSlice sharedProcessBuffer) + public static unsafe void InitializeContext(Parameters param, byte* sharedProcessBuffer) { // Store parameters in the shared buffer space - Context* ctx = (Context*) sharedProcessBuffer.GetUnsafePtr(); + Context* ctx = (Context*) sharedProcessBuffer; ctx->MaxPacketCount = param.MaxPacketCount; ctx->MaxPacketSize = param.MaxPacketSize; ctx->PacketDelayMs = param.PacketDelayMs; ctx->PacketJitterMs = param.PacketJitterMs; ctx->PacketDrop = param.PacketDropInterval; + ctx->FuzzFactor = param.FuzzFactor; + ctx->FuzzOffset = param.FuzzOffset; ctx->PacketCount = 0; ctx->PacketDropCount = 0; ctx->Random = new Random(); - ctx->Random.InitState(); + if (param.RandomSeed > 0) + { + ctx->Random.InitState(param.RandomSeed); + ctx->RandomSeed = param.RandomSeed; + } + else + ctx->Random.InitState(); } public unsafe bool GetEmptyDataSlot(byte* processBufferPtr, ref int packetPayloadOffset, @@ -126,24 +154,14 @@ public unsafe bool GetEmptyDataSlot(byte* processBufferPtr, ref int packetPayloa return foundSlot; } - public void StorePacketPayload(NativeSlice destinationSlice, NativeSlice sourceSlice1, - NativeSlice sourceSlice2) - { - int position; - for (position = 0; position < sourceSlice1.Length; ++position) - destinationSlice[position] = sourceSlice1[position]; - for (int i = 0; i < sourceSlice2.Length; ++i) - destinationSlice[position++] = sourceSlice2[i]; - } - - public unsafe bool GetDelayedPacket(ref NetworkPipelineContext ctx, ref NativeSlice delayedPacket, - ref bool needsResume, ref bool needsUpdate, long currentTimestamp) + public unsafe bool GetDelayedPacket(ref NetworkPipelineContext ctx, ref InboundSendBuffer delayedPacket, + ref NetworkPipelineStage.Requests requests, long currentTimestamp) { - needsUpdate = needsResume = false; + requests = NetworkPipelineStage.Requests.None; var dataSize = UnsafeUtility.SizeOf(); - byte* processBufferPtr = (byte*) ctx.internalProcessBuffer.GetUnsafePtr(); - var simCtx = (Context*) ctx.internalSharedProcessBuffer.GetUnsafePtr(); + byte* processBufferPtr = (byte*) ctx.internalProcessBuffer; + var simCtx = (Context*) ctx.internalSharedProcessBuffer; int oldestPacketIndex = -1; long oldestTime = long.MaxValue; int readyPackets = 0; @@ -170,15 +188,13 @@ public unsafe bool GetDelayedPacket(ref NetworkPipelineContext ctx, ref NativeSl // If more than one item has expired timer we need to resume this pipeline stage if (readyPackets > 1) { - needsUpdate = false; - needsResume = true; + requests |= NetworkPipelineStage.Requests.Resume; } // If more than one item is present (but doesn't have expired timer) we need to re-run the pipeline // in a later update call else if (packetsInQueue > 0) { - needsUpdate = true; - needsResume = false; + requests |= NetworkPipelineStage.Requests.Update; } if (oldestPacketIndex >= 0) @@ -186,22 +202,45 @@ public unsafe bool GetDelayedPacket(ref NetworkPipelineContext ctx, ref NativeSl DelayedPacket* packet = (DelayedPacket*) (processBufferPtr + dataSize * oldestPacketIndex); packet->delayUntil = 0; - delayedPacket = new NativeSlice(ctx.internalProcessBuffer, packet->processBufferOffset, - packet->packetSize); + delayedPacket.bufferWithHeaders = ctx.internalProcessBuffer + packet->processBufferOffset; + delayedPacket.bufferWithHeadersLength = packet->packetSize; + delayedPacket.headerPadding = packet->packetHeaderPadding; + delayedPacket.SetBufferFrombufferWithHeaders(); return true; } return false; } - public unsafe bool DelayPacket(ref NetworkPipelineContext ctx, InboundBufferVec inboundBuffer, - ref bool needsUpdate, + public unsafe void FuzzPacket(Context *ctx, ref InboundSendBuffer inboundBuffer) + { + int fuzzFactor = ctx->FuzzFactor; + int fuzzOffset = ctx->FuzzOffset; + int rand = ctx->Random.NextInt(0, 100); + if (rand > fuzzFactor) + return; + + var length = inboundBuffer.bufferLength; + for (int i = fuzzOffset; i < length; ++i) + { + for (int j = 0; j < 8; ++j) + { + if (fuzzFactor > ctx->Random.NextInt(0, 100)) + { + inboundBuffer.buffer[i] ^= (byte)(1 << j); + } + } + } + } + + public unsafe bool DelayPacket(ref NetworkPipelineContext ctx, InboundSendBuffer inboundBuffer, + ref NetworkPipelineStage.Requests requests, long timestamp) { // Find empty slot in bookkeeping data space to track this packet int packetPayloadOffset = 0; int packetDataOffset = 0; - var processBufferPtr = (byte*) ctx.internalProcessBuffer.GetUnsafePtr(); + var processBufferPtr = (byte*) ctx.internalProcessBuffer; bool foundSlot = GetEmptyDataSlot(processBufferPtr, ref packetPayloadOffset, ref packetDataOffset); if (!foundSlot) @@ -210,24 +249,20 @@ public unsafe bool DelayPacket(ref NetworkPipelineContext ctx, InboundBufferVec return false; } - NativeSlice packetPayload = - new NativeSlice(ctx.internalProcessBuffer, packetPayloadOffset, - inboundBuffer.buffer1.Length + inboundBuffer.buffer2.Length); + UnsafeUtility.MemCpy(ctx.internalProcessBuffer + packetPayloadOffset + inboundBuffer.headerPadding, inboundBuffer.buffer, inboundBuffer.bufferLength); - StorePacketPayload(packetPayload, inboundBuffer.buffer1, inboundBuffer.buffer2); - - var param = (SimulatorUtility.Context*) ctx.internalSharedProcessBuffer.GetUnsafePtr(); + var param = (SimulatorUtility.Context*) ctx.internalSharedProcessBuffer; // Add tracking for this packet so we can resurrect later DelayedPacket packet; packet.delayUntil = timestamp + m_PacketDelayMs + param->Random.NextInt(m_PacketJitterMs*2) - m_PacketJitterMs; packet.processBufferOffset = packetPayloadOffset; - packet.packetSize = inboundBuffer.buffer1.Length + inboundBuffer.buffer2.Length; + packet.packetSize = (ushort)(inboundBuffer.headerPadding + inboundBuffer.bufferLength); + packet.packetHeaderPadding = (ushort)inboundBuffer.headerPadding; byte* packetPtr = (byte*) &packet; UnsafeUtility.MemCpy(processBufferPtr + packetDataOffset, packetPtr, UnsafeUtility.SizeOf()); // Schedule an update call so packet can be resurrected later - needsUpdate = true; - + requests |= NetworkPipelineStage.Requests.Update; return true; } @@ -238,7 +273,7 @@ public unsafe bool ShouldDropPacket(Context* ctx, Parameters param, long timesta if (param.PacketDropPercentage > 0) { //var packetLoss = new System.Random().NextDouble() * 100; - var packetLoss = ctx->Random.NextDouble() * 100; + var packetLoss = ctx->Random.NextInt(0, 100); if (packetLoss < param.PacketDropPercentage) return true; } diff --git a/Runtime/Pipelines/UnreliableSequencedPipelineStage.cs b/Runtime/Pipelines/UnreliableSequencedPipelineStage.cs index 790bbb5..749cf7d 100644 --- a/Runtime/Pipelines/UnreliableSequencedPipelineStage.cs +++ b/Runtime/Pipelines/UnreliableSequencedPipelineStage.cs @@ -1,4 +1,3 @@ -using System; using Unity.Burst; using Unity.Collections; using Unity.Collections.LowLevel.Unsafe; @@ -6,55 +5,66 @@ namespace Unity.Networking.Transport { - public struct UnreliableSequencedPipelineStage : INetworkPipelineStage + [BurstCompile] + public unsafe struct UnreliableSequencedPipelineStage : INetworkPipelineStage { - public NativeSlice Receive(NetworkPipelineContext ctx, NativeSlice inboundBuffer, ref bool needsResume, ref bool needsUpdate, ref bool needsSendUpdate) + static TransportFunctionPointer ReceiveFunctionPointer = new TransportFunctionPointer(Receive); + static TransportFunctionPointer SendFunctionPointer = new TransportFunctionPointer(Send); + static TransportFunctionPointer InitializeConnectionFunctionPointer = new TransportFunctionPointer(InitializeConnection); + public NetworkPipelineStage StaticInitialize(byte* staticInstanceBuffer, int staticInstanceBufferLength, INetworkParameter[] netParams) { - needsResume = false; - var reader = new DataStreamReader(inboundBuffer); - var context = default(DataStreamReader.Context); - unsafe - { - var oldSequenceId = (int*) ctx.internalProcessBuffer.GetUnsafePtr(); - ushort sequenceId = reader.ReadUShort(ref context); - - if (SequenceHelpers.GreaterThan16(sequenceId, (ushort)*oldSequenceId)) - { - *oldSequenceId = sequenceId; - // Skip over the part of the buffer which contains the header - return inboundBuffer.Slice(sizeof(ushort)); - } - } - return default(NativeSlice); + return new NetworkPipelineStage( + Receive: ReceiveFunctionPointer, + Send: SendFunctionPointer, + InitializeConnection: InitializeConnectionFunctionPointer, + ReceiveCapacity: UnsafeUtility.SizeOf(), + SendCapacity: UnsafeUtility.SizeOf(), + HeaderCapacity: UnsafeUtility.SizeOf(), + SharedStateCapacity: 0 + ); } + public int StaticSize => 0; - public InboundBufferVec Send(NetworkPipelineContext ctx, InboundBufferVec inboundBuffer, ref bool needsResume, ref bool needsUpdate) + [BurstCompile] + private static void Receive(ref NetworkPipelineContext ctx, ref InboundRecvBuffer inboundBuffer, ref NetworkPipelineStage.Requests requests) { - needsResume = false; - unsafe + var inboundArray = NativeArrayUnsafeUtility.ConvertExistingDataToNativeArray(inboundBuffer.buffer, inboundBuffer.bufferLength, Allocator.Invalid); +#if ENABLE_UNITY_COLLECTIONS_CHECKS + var safetyHandle = AtomicSafetyHandle.GetTempMemoryHandle(); + NativeArrayUnsafeUtility.SetAtomicSafetyHandle(ref inboundArray, safetyHandle); +#endif + var reader = new DataStreamReader(inboundArray); + var oldSequenceId = (int*) ctx.internalProcessBuffer; + ushort sequenceId = reader.ReadUShort(); + + if (SequenceHelpers.GreaterThan16(sequenceId, (ushort)*oldSequenceId)) { - var sequenceId = (int*) ctx.internalProcessBuffer.GetUnsafePtr(); - ctx.header.Write((ushort)*sequenceId); - *sequenceId = (ushort)(*sequenceId + 1); + *oldSequenceId = sequenceId; + // Skip over the part of the buffer which contains the header + inboundBuffer = inboundBuffer.Slice(sizeof(ushort)); + return; } - return inboundBuffer; + inboundBuffer = default; } - public void InitializeConnection(NativeSlice sendProcessBuffer, NativeSlice recvProcessBuffer, NativeSlice sharedProcessBuffer) + [BurstCompile] + private static void Send(ref NetworkPipelineContext ctx, ref InboundSendBuffer inboundBuffer, ref NetworkPipelineStage.Requests requests) { - unsafe + var sequenceId = (int*) ctx.internalProcessBuffer; + ctx.header.WriteUShort((ushort)*sequenceId); + *sequenceId = (ushort)(*sequenceId + 1); + } + + [BurstCompile] + private static void InitializeConnection(byte* staticInstanceBuffer, int staticInstanceBufferLength, + byte* sendProcessBuffer, int sendProcessBufferLength, byte* recvProcessBuffer, int recvProcessBufferLength, + byte* sharedProcessBuffer, int sharedProcessBufferLength) + { + if (recvProcessBufferLength > 0) { - if (recvProcessBuffer.Length > 0) - { - // The receive processing buffer contains the current sequence ID, initialize it to -1 as it will be incremented when used. - *(int*) recvProcessBuffer.GetUnsafePtr() = -1; - } + // The receive processing buffer contains the current sequence ID, initialize it to -1 as it will be incremented when used. + *(int*) recvProcessBuffer = -1; } } - - public int ReceiveCapacity => sizeof(int); - public int SendCapacity => sizeof(int); - public int HeaderCapacity => sizeof(ushort); - public int SharedStateCapacity { get; } } } \ No newline at end of file diff --git a/Runtime/TransportFunctionPointer.cs b/Runtime/TransportFunctionPointer.cs new file mode 100644 index 0000000..dc3427e --- /dev/null +++ b/Runtime/TransportFunctionPointer.cs @@ -0,0 +1,23 @@ +using System; +using Unity.Burst; + +namespace Unity.Networking.Transport +{ + public struct TransportFunctionPointer where T : Delegate + { + public TransportFunctionPointer(T executeDelegate) + { +#if !UNITY_DOTSPLAYER + Ptr = BurstCompiler.CompileFunctionPointer(executeDelegate); +#else + Ptr = executeDelegate; +#endif + } + +#if !UNITY_DOTSPLAYER + internal readonly FunctionPointer Ptr; +#else + internal readonly T Ptr; +#endif + } +} \ No newline at end of file diff --git a/Runtime/TransportFunctionPointer.cs.meta b/Runtime/TransportFunctionPointer.cs.meta new file mode 100644 index 0000000..0815c83 --- /dev/null +++ b/Runtime/TransportFunctionPointer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 324237658e8e2405986a266f8919ae4f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/UDPNetworkDriver.cs b/Runtime/UDPNetworkDriver.cs deleted file mode 100644 index f18442e..0000000 --- a/Runtime/UDPNetworkDriver.cs +++ /dev/null @@ -1,141 +0,0 @@ -using System; -using Unity.Collections; -using Unity.Jobs; - -namespace Unity.Networking.Transport -{ - public struct UdpNetworkDriver : INetworkDriver - { - private GenericNetworkDriver m_genericDriver; - - public struct Concurrent - { - private GenericNetworkDriver.Concurrent m_genericConcurrent; - - internal Concurrent(GenericNetworkDriver.Concurrent genericConcurrent) - { - m_genericConcurrent = genericConcurrent; - } - public NetworkEvent.Type PopEventForConnection(NetworkConnection connectionId, out DataStreamReader slice) - { - return m_genericConcurrent.PopEventForConnection(connectionId, out slice); - } - - public int Send(NetworkPipeline pipe, NetworkConnection id, DataStreamWriter strm) - { - return m_genericConcurrent.Send(pipe, id, strm); - } - - public int Send(NetworkPipeline pipe, NetworkConnection id, IntPtr data, int len) - { - return m_genericConcurrent.Send(pipe, id, data, len); - } - - public NetworkConnection.State GetConnectionState(NetworkConnection id) - { - return m_genericConcurrent.GetConnectionState(id); - } - } - - public Concurrent ToConcurrent() - { - return new Concurrent(m_genericDriver.ToConcurrent()); - } - - public UdpNetworkDriver(params INetworkParameter[] param) - { - m_genericDriver = new GenericNetworkDriver(param); - } - public bool IsCreated => m_genericDriver.IsCreated; - public void Dispose() - { - m_genericDriver.Dispose(); - } - - public JobHandle ScheduleUpdate(JobHandle dep = default(JobHandle)) - { - return m_genericDriver.ScheduleUpdate(dep); - } - - public int ReceiveErrorCode => m_genericDriver.ReceiveErrorCode; - - public int Bind(NetworkEndPoint endpoint) - { - return m_genericDriver.Bind(endpoint); - } - - public int Listen() - { - return m_genericDriver.Listen(); - } - - public bool Listening => m_genericDriver.Listening; - - public NetworkConnection Accept() - { - return m_genericDriver.Accept(); - } - - public NetworkConnection Connect(NetworkEndPoint endpoint) - { - return m_genericDriver.Connect(endpoint); - } - - public int Disconnect(NetworkConnection con) - { - return m_genericDriver.Disconnect(con); - } - - public NetworkConnection.State GetConnectionState(NetworkConnection con) - { - return m_genericDriver.GetConnectionState(con); - } - - public void GetPipelineBuffers(Type pipelineType, NetworkConnection connection, ref NativeSlice readProcessingBuffer, ref NativeSlice writeProcessingBuffer, ref NativeSlice sharedBuffer) - { - m_genericDriver.GetPipelineBuffers(pipelineType, connection, ref readProcessingBuffer, ref writeProcessingBuffer, ref sharedBuffer); - } - - public void GetPipelineBuffers(NetworkPipeline pipeline, int stageId, NetworkConnection connection, - ref NativeSlice readProcessingBuffer, ref NativeSlice writeProcessingBuffer, - ref NativeSlice sharedBuffer) - { - m_genericDriver.GetPipelineBuffers(pipeline, stageId, connection, ref readProcessingBuffer, ref writeProcessingBuffer, ref sharedBuffer); - } - - public NetworkEndPoint RemoteEndPoint(NetworkConnection con) - { - return m_genericDriver.RemoteEndPoint(con); - } - - public NetworkEndPoint LocalEndPoint() - { - return m_genericDriver.LocalEndPoint(); - } - - public NetworkPipeline CreatePipeline(params Type[] stages) - { - return m_genericDriver.CreatePipeline(stages); - } - - public int Send(NetworkPipeline pipe, NetworkConnection con, DataStreamWriter strm) - { - return m_genericDriver.Send(pipe, con, strm); - } - - public int Send(NetworkPipeline pipe, NetworkConnection con, IntPtr data, int len) - { - return m_genericDriver.Send(pipe, con, data, len); - } - - public NetworkEvent.Type PopEvent(out NetworkConnection con, out DataStreamReader bs) - { - return m_genericDriver.PopEvent(out con, out bs); - } - - public NetworkEvent.Type PopEventForConnection(NetworkConnection con, out DataStreamReader bs) - { - return m_genericDriver.PopEventForConnection(con, out bs); - } - } -} \ No newline at end of file diff --git a/Runtime/UDPNetworkInterface.cs b/Runtime/UDPNetworkInterface.cs index feecab9..9aebe9c 100644 --- a/Runtime/UDPNetworkInterface.cs +++ b/Runtime/UDPNetworkInterface.cs @@ -1,8 +1,9 @@ using System; using System.Collections.Generic; using Unity.Burst; +using Unity.Collections; +using Unity.Collections.LowLevel.Unsafe; using Unity.Jobs; -using Unity.Networking.Transport.LowLevel.Unsafe; using Unity.Networking.Transport.Protocols; namespace Unity.Networking.Transport @@ -25,7 +26,8 @@ public NetworkInterfaceException(int err) } #endif - public struct IPv4UDPSocket : INetworkInterface + [BurstCompile] + public struct UDPNetworkInterface : INetworkInterface { #if ENABLE_UNITY_COLLECTIONS_CHECKS private class SocketList @@ -45,53 +47,97 @@ private class SocketList private static SocketList AllSockets = new SocketList(); #endif - private long m_SocketHandle; - private NetworkEndPoint m_RemoteEndPoint; + NativeArray m_UserData; - public NetworkFamily Family => NetworkFamily.UdpIpv4; - - public unsafe NetworkEndPoint LocalEndPoint + public unsafe NetworkInterfaceEndPoint LocalEndPoint { get { - var localEndPoint = new NetworkEndPoint {length = sizeof(NetworkEndPoint)}; + var localEndPoint = new network_address {length = network_address.Length}; int errorcode = 0; - var result = NativeBindings.network_get_socket_address(m_SocketHandle, ref localEndPoint, ref errorcode); + var result = NativeBindings.network_get_socket_address(m_UserData[0], ref localEndPoint, ref errorcode); if (result != 0) { throw new NetworkInterfaceException(errorcode); } - return localEndPoint; + + var endpoint = default(NetworkInterfaceEndPoint); + endpoint.dataLength = UnsafeUtility.SizeOf(); + UnsafeUtility.MemCpy(endpoint.data, &localEndPoint, endpoint.dataLength); + return endpoint; } } - public NetworkEndPoint RemoteEndPoint { - get { return m_RemoteEndPoint; } + private static unsafe NetworkInterfaceEndPoint ParseNetworkAddress(NetworkEndPoint endPoint) + { + NetworkInterfaceEndPoint ep = default(NetworkInterfaceEndPoint); + var addr = (network_address*) ep.data; + var sai = (sockaddr_in*) addr->data; +#if (UNITY_EDITOR_OSX || ((UNITY_STANDALONE_OSX || UNITY_IOS) && !UNITY_EDITOR)) + sai->sin_family.sa_family = (byte) NetworkFamily.Ipv4; + sai->sin_family.sa_len = (byte) sizeof(sockaddr_in); +#else + sai->sin_family.sa_family = (ushort) NetworkFamily.Ipv4; +#endif + sai->sin_port = endPoint.RawPort; + var bytes = endPoint.GetRawAddressBytes(); + sai->sin_addr.s_addr = *(uint*) bytes.GetUnsafeReadOnlyPtr(); + + addr->length = sizeof(sockaddr_in); + ep.dataLength = sizeof(network_address); + return ep; + } + public unsafe NetworkInterfaceEndPoint CreateInterfaceEndPoint(NetworkEndPoint endPoint) + { + if (endPoint.Family != NetworkFamily.Ipv4) + throw new ArgumentException("Invalid family type"); + + return ParseNetworkAddress(endPoint); } - public void Initialize() + public unsafe NetworkEndPoint GetGenericEndPoint(NetworkInterfaceEndPoint endpoint) + { + var address = NetworkEndPoint.AnyIpv4; + var addr = (network_address*) endpoint.data; + var sai = (sockaddr_in*) addr->data; + address.RawPort = sai->sin_port; + if (sai->sin_addr.s_addr != 0) + { + var bytes = new NativeArray(4, Allocator.Temp); + UnsafeUtility.MemCpy(bytes.GetUnsafePtr(), UnsafeUtility.AddressOf(ref sai->sin_addr.s_addr), 4); + address.SetRawAddressBytes(bytes); + } + return address; + } + + public unsafe void Initialize(params INetworkParameter[] param) { NativeBindings.network_initialize(); - int ret = CreateAndBindSocket(out m_SocketHandle, NetworkEndPoint.AnyIpv4); + var ep = CreateInterfaceEndPoint(NetworkEndPoint.AnyIpv4); + long sockHand; + int ret = CreateAndBindSocket(out sockHand, *(network_address*)ep.data); if (ret != 0) throw new NetworkInterfaceException(ret); + m_UserData = new NativeArray(1, Allocator.Persistent); + m_UserData[0] = sockHand; } public void Dispose() { Close(); NativeBindings.network_terminate(); + m_UserData.Dispose(); } [BurstCompile] - struct ReceiveJob : IJob where T : struct, INetworkPacketReceiver + struct ReceiveJob : IJob { - public T receiver; + public NetworkPacketReceiver receiver; public long socket; public unsafe void Execute() { - var address = new NetworkEndPoint {length = sizeof(NetworkEndPoint)}; + var address = new network_address {length = network_address.Length}; var header = new UdpCHeader(); var stream = receiver.GetDataStream(); receiver.ReceiveCount = 0; @@ -99,24 +145,28 @@ public unsafe void Execute() while (true) { + int dataStreamSize = receiver.GetDataStreamSize(); if (receiver.DynamicDataStreamSize()) { - while (stream.Length+NetworkParameterConstants.MTU >= stream.Capacity) - stream.Capacity *= 2; + while (dataStreamSize+NetworkParameterConstants.MTU-UdpCHeader.Length >= stream.Length) + stream.ResizeUninitialized(stream.Length * 2); } - else if (stream.Length >= stream.Capacity) + else if (dataStreamSize >= stream.Length) return; - var sliceOffset = stream.Length; - var result = NativeReceive(ref header, stream.GetUnsafePtr() + sliceOffset, - Math.Min(NetworkParameterConstants.MTU, stream.Capacity - stream.Length), ref address); + var result = NativeReceive(ref header, (byte*)stream.GetUnsafePtr() + dataStreamSize, + Math.Min(NetworkParameterConstants.MTU-UdpCHeader.Length, stream.Length - dataStreamSize), ref address); if (result <= 0) return; - receiver.ReceiveCount += receiver.AppendPacket(address, header, result); + + var endpoint = default(NetworkInterfaceEndPoint); + endpoint.dataLength = UnsafeUtility.SizeOf(); + UnsafeUtility.MemCpy(endpoint.data, &address, endpoint.dataLength); + receiver.ReceiveCount += receiver.AppendPacket(endpoint, header, result); } } - unsafe int NativeReceive(ref UdpCHeader header, void* data, int length, ref NetworkEndPoint address) + unsafe int NativeReceive(ref UdpCHeader header, void* data, int length, ref network_address address) { #if ENABLE_UNITY_COLLECTIONS_CHECKS if (length <= 0) @@ -146,51 +196,82 @@ unsafe int NativeReceive(ref UdpCHeader header, void* data, int length, ref Netw } } - public JobHandle ScheduleReceive(T receiver, JobHandle dep) where T : struct, INetworkPacketReceiver + public JobHandle ScheduleReceive(NetworkPacketReceiver receiver, JobHandle dep) { - var job = new ReceiveJob {receiver = receiver, socket = m_SocketHandle}; + var job = new ReceiveJob {receiver = receiver, socket = m_UserData[0]}; return job.Schedule(dep); } - - public int Bind(NetworkEndPoint endpoint) + public JobHandle ScheduleSend(NativeQueue sendQueue, JobHandle dep) { -#if ENABLE_UNITY_COLLECTIONS_CHECKS - if (endpoint.Family != NetworkFamily.UdpIpv4) - throw new InvalidOperationException(); -#endif + return dep; + } + public unsafe int Bind(NetworkInterfaceEndPoint endpoint) + { long newSocket; - int ret = CreateAndBindSocket(out newSocket, endpoint); + int ret = CreateAndBindSocket(out newSocket, *(network_address*)endpoint.data); if (ret != 0) return ret; Close(); - m_RemoteEndPoint = endpoint; - m_SocketHandle = newSocket; + m_UserData[0] = newSocket; return 0; } private void Close() { - if (m_SocketHandle < 0) + if (m_UserData[0] < 0) return; #if ENABLE_UNITY_COLLECTIONS_CHECKS - AllSockets.OpenSockets.Remove(m_SocketHandle); + AllSockets.OpenSockets.Remove(m_UserData[0]); #endif int errorcode = 0; - NativeBindings.network_close(ref m_SocketHandle, ref errorcode); - m_RemoteEndPoint = default(NetworkEndPoint); - m_SocketHandle = -1; + long sockHand = m_UserData[0]; + NativeBindings.network_close(ref sockHand, ref errorcode); + + m_UserData[0] = -1; + } + + static TransportFunctionPointer BeginSendMessageFunctionPointer = new TransportFunctionPointer(BeginSendMessage); + static TransportFunctionPointer EndSendMessageFunctionPointer = new TransportFunctionPointer(EndSendMessage); + static TransportFunctionPointer AbortSendMessageFunctionPointer = new TransportFunctionPointer(AbortSendMessage); + public unsafe NetworkSendInterface CreateSendInterface() + { + return new NetworkSendInterface + { + BeginSendMessage = BeginSendMessageFunctionPointer, + EndSendMessage = EndSendMessageFunctionPointer, + AbortSendMessage = AbortSendMessageFunctionPointer, + UserData = (IntPtr)m_UserData.GetUnsafePtr() + }; + } + [BurstCompile] + public static unsafe int BeginSendMessage(out NetworkInterfaceSendHandle handle, IntPtr userData) + { + handle.id = 0; + handle.size = 0; + handle.capacity = NetworkParameterConstants.MTU; + handle.data = (IntPtr)UnsafeUtility.Malloc(handle.capacity, 8, Allocator.Temp); + return 0; } - public unsafe int SendMessage(network_iovec* iov, int iov_len, ref NetworkEndPoint address) + [BurstCompile] + public static unsafe int EndSendMessage(ref NetworkInterfaceSendHandle handle, ref NetworkInterfaceEndPoint address, IntPtr userData, ref NetworkSendQueueHandle sendQueue) { + network_iovec iov; + iov.buf = (void*)handle.data; + iov.len = handle.size; int errorcode = 0; - return NativeBindings.network_sendmsg(m_SocketHandle, iov, iov_len, ref address, ref errorcode); + var addr = address; + return NativeBindings.network_sendmsg(*(long*)userData, &iov, 1, ref *(network_address*)addr.data, ref errorcode); + } + [BurstCompile] + private static void AbortSendMessage(ref NetworkInterfaceSendHandle handle, IntPtr userData) + { } - int CreateAndBindSocket(out long socket, NetworkEndPoint address) + int CreateAndBindSocket(out long socket, network_address address) { socket = -1; int errorcode = 0; diff --git a/Runtime/Utilities.cs b/Runtime/Utilities.cs index ce79808..06f2280 100644 --- a/Runtime/Utilities.cs +++ b/Runtime/Utilities.cs @@ -14,6 +14,8 @@ public struct NativeMultiQueue : IDisposable where T : struct private NativeList m_QueueHeadTail; private NativeArray m_MaxItems; + public bool IsCreated => m_Queue.IsCreated; + /// /// New NativeMultiQueue has a single bucket and the specified number /// of items for that bucket. Accessing buckets out of range will grow @@ -138,21 +140,6 @@ public void Clear(int bucket) } } - /// - /// A simple timer used to measure the number of milliseconds since it was created. - /// - public class Timer - { - private System.Diagnostics.Stopwatch stopwatch = new System.Diagnostics.Stopwatch(); - - public Timer() - { - stopwatch.Start(); - } - - public long ElapsedMilliseconds => stopwatch.ElapsedMilliseconds; - } - public struct SequenceHelpers { // Calculate difference between the sequence IDs taking into account wrapping, so when you go from 65535 to 0 the distance is 1 @@ -165,7 +152,7 @@ public static int AbsDistance(ushort lhs, ushort rhs) distance = lhs - rhs; return distance; } - + public static bool IsNewer(uint current, uint old) { // Invert the check so same does not count as newer diff --git a/Tests/Editor/BeginEndSendTests.cs b/Tests/Editor/BeginEndSendTests.cs new file mode 100644 index 0000000..dabed6d --- /dev/null +++ b/Tests/Editor/BeginEndSendTests.cs @@ -0,0 +1,192 @@ +using System; +using Unity.Collections; +using NUnit.Framework; +using Unity.Networking.Transport.Protocols; +using UnityEngine; +using UnityEngine.TestTools; +using Unity.Jobs; + +namespace Unity.Networking.Transport.Tests +{ + public class BeginEndSendTests + { + private NetworkDriver Driver; + private NetworkDriver RemoteDriver; + private NetworkConnection ToRemoteConnection; + private NetworkConnection ToLocalConnection; + + [SetUp] + public void IPC_Setup() + { + Driver = TestNetworkDriver.Create(new NetworkDataStreamParameter {size = 64}); + RemoteDriver = TestNetworkDriver.Create(new NetworkDataStreamParameter {size = 64}); + + RemoteDriver.Bind(NetworkEndPoint.LoopbackIpv4); + RemoteDriver.Listen(); + ToRemoteConnection = Driver.Connect(RemoteDriver.LocalEndPoint()); + Driver.ScheduleFlushSend(default).Complete(); + RemoteDriver.ScheduleUpdate().Complete(); + ToLocalConnection = RemoteDriver.Accept(); + Assert.AreNotEqual(default, ToLocalConnection); + Driver.ScheduleUpdate().Complete(); + var evt = Driver.PopEventForConnection(ToRemoteConnection, out var reader); + Assert.AreEqual(NetworkEvent.Type.Connect, evt); + } + + [TearDown] + public void IPC_TearDown() + { + Driver.Dispose(); + RemoteDriver.Dispose(); + } + [Test] + public void BeginEndSimple() + { + var writer = Driver.BeginSend(ToRemoteConnection); + Assert.AreEqual(NetworkParameterConstants.MTU - UdpCHeader.Length, writer.Capacity); + writer.WriteInt(42); + Driver.EndSend(writer); + Driver.ScheduleFlushSend(default).Complete(); + RemoteDriver.ScheduleUpdate().Complete(); + var evt = RemoteDriver.PopEventForConnection(ToLocalConnection, out var reader); + Assert.AreEqual(NetworkEvent.Type.Data, evt); + Assert.AreEqual(4, reader.Length); + Assert.AreEqual(42, reader.ReadInt()); + } + [Test] + public void NestedBeginEndSend() + { + var writer = Driver.BeginSend(ToRemoteConnection); + var writer2 = Driver.BeginSend(ToRemoteConnection); + writer.WriteInt(42); + writer2.WriteInt(4242); + Driver.EndSend(writer2); + Driver.EndSend(writer); + Driver.ScheduleFlushSend(default).Complete(); + RemoteDriver.ScheduleUpdate().Complete(); + var evt = RemoteDriver.PopEventForConnection(ToLocalConnection, out var reader); + Assert.AreEqual(NetworkEvent.Type.Data, evt); + Assert.AreEqual(4, reader.Length); + Assert.AreEqual(4242, reader.ReadInt()); + evt = RemoteDriver.PopEventForConnection(ToLocalConnection, out reader); + Assert.AreEqual(NetworkEvent.Type.Data, evt); + Assert.AreEqual(4, reader.Length); + Assert.AreEqual(42, reader.ReadInt()); + } + [Test] + public void OverlappingBeginEndSend() + { + var writer = Driver.BeginSend(ToRemoteConnection); + var writer2 = Driver.BeginSend(ToRemoteConnection); + writer.WriteInt(42); + writer2.WriteInt(4242); + Driver.EndSend(writer); + Driver.EndSend(writer2); + Driver.ScheduleFlushSend(default).Complete(); + RemoteDriver.ScheduleUpdate().Complete(); + var evt = RemoteDriver.PopEventForConnection(ToLocalConnection, out var reader); + Assert.AreEqual(NetworkEvent.Type.Data, evt); + Assert.AreEqual(4, reader.Length); + Assert.AreEqual(42, reader.ReadInt()); + evt = RemoteDriver.PopEventForConnection(ToLocalConnection, out reader); + Assert.AreEqual(NetworkEvent.Type.Data, evt); + Assert.AreEqual(4, reader.Length); + Assert.AreEqual(4242, reader.ReadInt()); + } + [Test] + public void MissingEndSend() + { + var writer = Driver.BeginSend(ToRemoteConnection); + writer.WriteInt(42); + LogAssert.Expect(LogType.Error, "Missing EndSend, calling BeginSend without calling EndSend will result in a memory leak"); + Driver.ScheduleUpdate().Complete(); + } + [Test] + public void DuplicateEndSend() + { + var writer = Driver.BeginSend(ToRemoteConnection); + writer.WriteInt(42); + Driver.EndSend(writer); + Assert.Throws(()=>{Driver.EndSend(writer);}); + + Driver.ScheduleFlushSend(default).Complete(); + RemoteDriver.ScheduleUpdate().Complete(); + var evt = RemoteDriver.PopEventForConnection(ToLocalConnection, out var reader); + Assert.AreEqual(NetworkEvent.Type.Data, evt); + Assert.AreEqual(4, reader.Length); + Assert.AreEqual(42, reader.ReadInt()); + } + [Test] + public void DuplicateAbortSend() + { + var writer = Driver.BeginSend(ToRemoteConnection); + writer.WriteInt(42); + Driver.AbortSend(writer); + Assert.Throws(()=>{Driver.AbortSend(writer);}); + + Driver.ScheduleFlushSend(default).Complete(); + RemoteDriver.ScheduleUpdate().Complete(); + var evt = RemoteDriver.PopEventForConnection(ToLocalConnection, out var reader); + Assert.AreEqual(NetworkEvent.Type.Empty, evt); + } + [Test] + public void AbortBeforeEndSend() + { + var writer = Driver.BeginSend(ToRemoteConnection); + writer.WriteInt(42); + Driver.AbortSend(writer); + Assert.Throws(()=>{Driver.EndSend(writer);}); + + Driver.ScheduleFlushSend(default).Complete(); + RemoteDriver.ScheduleUpdate().Complete(); + var evt = RemoteDriver.PopEventForConnection(ToLocalConnection, out var reader); + Assert.AreEqual(NetworkEvent.Type.Empty, evt); + } + [Test] + public void EndBeforeAbortSend() + { + var writer = Driver.BeginSend(ToRemoteConnection); + writer.WriteInt(42); + Driver.EndSend(writer); + Assert.Throws(()=>{Driver.AbortSend(writer);}); + + Driver.ScheduleFlushSend(default).Complete(); + RemoteDriver.ScheduleUpdate().Complete(); + var evt = RemoteDriver.PopEventForConnection(ToLocalConnection, out var reader); + Assert.AreEqual(NetworkEvent.Type.Data, evt); + Assert.AreEqual(4, reader.Length); + Assert.AreEqual(42, reader.ReadInt()); + } + struct BeginSendJob : IJob + { + public static DataStreamWriter writer = default; + public NetworkDriver Driver; + public NetworkConnection ToRemoteConnection; + public void Execute() + { + writer = Driver.BeginSend(ToRemoteConnection); + writer.WriteInt(42); + } + } + [Test] + public void EndSendAfterDispose() + { + var beginJob = new BeginSendJob + { + Driver = Driver, + ToRemoteConnection = ToRemoteConnection + }; + beginJob.Schedule().Complete(); + Assert.Throws(()=>{Driver.EndSend(BeginSendJob.writer);}); + + LogAssert.Expect(LogType.Error, "Missing EndSend, calling BeginSend without calling EndSend will result in a memory leak"); + Driver.ScheduleUpdate().Complete(); + } + [Test] + public void EndSendWithoutBeginSend() + { + var writer = new DataStreamWriter(16, Allocator.Temp); + Assert.Throws(()=>{Driver.EndSend(writer);}); + } + } +} \ No newline at end of file diff --git a/Tests/Editor/BeginEndSendTests.cs.meta b/Tests/Editor/BeginEndSendTests.cs.meta new file mode 100644 index 0000000..0ee2da7 --- /dev/null +++ b/Tests/Editor/BeginEndSendTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d6d15d28914b448bea75363d260e43c3 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Tests/Editor/DataStreamTests.cs b/Tests/Editor/DataStreamTests.cs index 20ac77e..31bf0d3 100644 --- a/Tests/Editor/DataStreamTests.cs +++ b/Tests/Editor/DataStreamTests.cs @@ -1,7 +1,5 @@ using System; -using Unity.Networking.Transport.Utilities; using NUnit.Framework; -using UnityEngine; using Unity.Collections; // using FsCheck; @@ -20,19 +18,16 @@ public void CreateStreamWithPartOfSourceByteArray() }; DataStreamWriter dataStream; - using (dataStream = new DataStreamWriter(4, Allocator.Persistent)) + dataStream = new DataStreamWriter(4, Allocator.Temp); + dataStream.WriteBytes(new NativeArray(byteArray, Allocator.Temp).GetSubArray(0, 4)); + Assert.AreEqual(dataStream.Length, 4); + var reader = new DataStreamReader(dataStream.AsNativeArray()); + for (int i = 0; i < dataStream.Length; ++i) { - dataStream.Write(byteArray, 4); - Assert.AreEqual(dataStream.Length, 4); - var reader = new DataStreamReader(dataStream, 0, dataStream.Length); - var readerCtx = default(DataStreamReader.Context); - for (int i = 0; i < reader.Length; ++i) - { - Assert.AreEqual(byteArray[i], reader.ReadByte(ref readerCtx)); - } - - Assert.Throws(() => { reader.ReadByte(ref readerCtx); }); + Assert.AreEqual(byteArray[i], reader.ReadByte()); } + + Assert.Throws(() => { reader.ReadByte(); }); } [Test] @@ -44,36 +39,32 @@ public void CreateStreamWithSourceByteArray() byteArray[2] = (byte) 'c'; DataStreamWriter dataStream; - using (dataStream = new DataStreamWriter(byteArray.Length, Allocator.Persistent)) + dataStream = new DataStreamWriter(byteArray.Length, Allocator.Temp); + dataStream.WriteBytes(new NativeArray(byteArray, Allocator.Temp)); + var reader = new DataStreamReader(dataStream.AsNativeArray()); + for (int i = 0; i < byteArray.Length; ++i) { - dataStream.Write(byteArray, byteArray.Length); - var reader = new DataStreamReader(dataStream, 0, byteArray.Length); - var readerCtx = default(DataStreamReader.Context); - for (int i = 0; i < reader.Length; ++i) - { - Assert.AreEqual(byteArray[i], reader.ReadByte(ref readerCtx)); - } + Assert.AreEqual(byteArray[i], reader.ReadByte()); } } [Test] public void ReadIntoExistingByteArray() { - byte[] byteArray = new byte[100]; + var byteArray = new NativeArray(100, Allocator.Temp); DataStreamWriter dataStream; - using (dataStream = new DataStreamWriter(3, Allocator.Persistent)) + dataStream = new DataStreamWriter(3, Allocator.Temp); { - dataStream.Write((byte) 'a'); - dataStream.Write((byte) 'b'); - dataStream.Write((byte) 'c'); - var reader = new DataStreamReader(dataStream, 0, dataStream.Length); - var readerCtx = default(DataStreamReader.Context); - reader.ReadBytesIntoArray(ref readerCtx, ref byteArray, dataStream.Length); - readerCtx = default(DataStreamReader.Context); + dataStream.WriteByte((byte) 'a'); + dataStream.WriteByte((byte) 'b'); + dataStream.WriteByte((byte) 'c'); + var reader = new DataStreamReader(dataStream.AsNativeArray()); + reader.ReadBytes(byteArray.GetSubArray(0, dataStream.Length)); + reader = new DataStreamReader(dataStream.AsNativeArray()); for (int i = 0; i < reader.Length; ++i) { - Assert.AreEqual(byteArray[i], reader.ReadByte(ref readerCtx)); + Assert.AreEqual(byteArray[i], reader.ReadByte()); } } } @@ -81,140 +72,117 @@ public void ReadIntoExistingByteArray() [Test] public void ReadingDataFromStreamWithSliceOffset() { - using (var dataStream = new DataStreamWriter(100, Allocator.Persistent)) - { - dataStream.Write((byte) 'a'); - dataStream.Write((byte) 'b'); - dataStream.Write((byte) 'c'); - dataStream.Write((byte) 'd'); - dataStream.Write((byte) 'e'); - dataStream.Write((byte) 'f'); - var reader = new DataStreamReader(dataStream, 3, 3); - var readerCtx = default(DataStreamReader.Context); - Assert.AreEqual('d', reader.ReadByte(ref readerCtx)); - Assert.AreEqual('e', reader.ReadByte(ref readerCtx)); - Assert.AreEqual('f', reader.ReadByte(ref readerCtx)); - } - } - - [Test] - public void CopyToByteArrayWithOffset() - { - byte[] byteArray = new byte[100]; - - using (var dataStream = new DataStreamWriter(100, Allocator.Persistent)) - { - dataStream.Write((byte) 'a'); - dataStream.Write((byte) 'b'); - dataStream.Write((byte) 'c'); - dataStream.Write((byte) 'd'); - dataStream.Write((byte) 'e'); - dataStream.Write((byte) 'f'); - - dataStream.CopyTo(2, 3, ref byteArray); - Assert.AreEqual(byteArray[0], (byte) 'c'); - Assert.AreEqual(byteArray[1], (byte) 'd'); - Assert.AreEqual(byteArray[2], (byte) 'e'); - Assert.AreNotEqual(byteArray[3], (byte) 'f'); - } - } - - [Test] - public void CopyToNativeArrayWithOffset() - { - using (var dataStream = new DataStreamWriter(100, Allocator.Persistent)) - using (var nativeArray = new NativeArray(100, Allocator.Persistent)) - { - dataStream.Write((byte) 'a'); - dataStream.Write((byte) 'b'); - dataStream.Write((byte) 'c'); - dataStream.Write((byte) 'd'); - dataStream.Write((byte) 'e'); - dataStream.Write((byte) 'f'); - - dataStream.CopyTo(2, 3, nativeArray); - Assert.AreEqual(nativeArray[0], (byte) 'c'); - Assert.AreEqual(nativeArray[1], (byte) 'd'); - Assert.AreEqual(nativeArray[2], (byte) 'e'); - Assert.AreNotEqual(nativeArray[3], (byte) 'f'); - } + var dataStream = new DataStreamWriter(100, Allocator.Temp); + dataStream.WriteByte((byte) 'a'); + dataStream.WriteByte((byte) 'b'); + dataStream.WriteByte((byte) 'c'); + dataStream.WriteByte((byte) 'd'); + dataStream.WriteByte((byte) 'e'); + dataStream.WriteByte((byte) 'f'); + var reader = new DataStreamReader(dataStream.AsNativeArray().GetSubArray(3, 3)); + Assert.AreEqual('d', reader.ReadByte()); + Assert.AreEqual('e', reader.ReadByte()); + Assert.AreEqual('f', reader.ReadByte()); } [Test] public void ReadWritePackedUInt() { - using (var dataStream = new DataStreamWriter(300 * 4, Allocator.Persistent)) using (var compressionModel = new NetworkCompressionModel(Allocator.Persistent)) { + var dataStream = new DataStreamWriter(300 * 4, Allocator.Temp); uint base_val = 2000; uint count = 277; for (uint i = 0; i < count; ++i) dataStream.WritePackedUInt(base_val + i, compressionModel); - dataStream.Write((int) 1979); + dataStream.WriteInt((int) 1979); dataStream.Flush(); - var reader = new DataStreamReader(dataStream, 0, dataStream.Length); - var ctx = default(DataStreamReader.Context); + var reader = new DataStreamReader(dataStream.AsNativeArray()); for (uint i = 0; i < count; ++i) { - var val = reader.ReadPackedUInt(ref ctx, compressionModel); + var val = reader.ReadPackedUInt(compressionModel); Assert.AreEqual(base_val + i, val); } - Assert.AreEqual(1979, reader.ReadInt(ref ctx)); + Assert.AreEqual(1979, reader.ReadInt()); } } [Test] public void ReadWritePackedInt() { - using (var dataStream = new DataStreamWriter(300 * 4, Allocator.Persistent)) using (var compressionModel = new NetworkCompressionModel(Allocator.Persistent)) { + var dataStream = new DataStreamWriter(300 * 4, Allocator.Temp); int base_val = -10; int count = 20; for (int i = 0; i < count; ++i) dataStream.WritePackedInt(base_val + i, compressionModel); - dataStream.Write((int) 1979); + dataStream.WriteInt((int) 1979); dataStream.Flush(); - var reader = new DataStreamReader(dataStream, 0, dataStream.Length); - var ctx = default(DataStreamReader.Context); + var reader = new DataStreamReader(dataStream.AsNativeArray()); for (int i = 0; i < count; ++i) { - var val = reader.ReadPackedInt(ref ctx, compressionModel); + var val = reader.ReadPackedInt(compressionModel); Assert.AreEqual(base_val + i, val); } - Assert.AreEqual(1979, reader.ReadInt(ref ctx)); + Assert.AreEqual(1979, reader.ReadInt()); } } [Test] public void ReadWritePackedUIntWithDeferred() { - using (var dataStream = new DataStreamWriter(300 * 4, Allocator.Persistent)) using (var compressionModel = new NetworkCompressionModel(Allocator.Persistent)) { + var dataStream = new DataStreamWriter(300 * 4, Allocator.Temp); uint base_val = 2000; uint count = 277; - var def = dataStream.Write((int) 0); + var def = dataStream; + dataStream.WriteInt((int) 0); for (uint i = 0; i < count; ++i) dataStream.WritePackedUInt(base_val + i, compressionModel); dataStream.Flush(); - def.Update(1979); - def = dataStream.Write((int) 0); - def.Update(1979); + def.WriteInt(1979); + def = dataStream; + dataStream.WriteInt((int) 0); + def.WriteInt(1979); dataStream.Flush(); - var reader = new DataStreamReader(dataStream, 0, dataStream.Length); - var ctx = default(DataStreamReader.Context); - Assert.AreEqual(1979, reader.ReadInt(ref ctx)); + var reader = new DataStreamReader(dataStream.AsNativeArray()); + Assert.AreEqual(1979, reader.ReadInt()); for (uint i = 0; i < count; ++i) { - var val = reader.ReadPackedUInt(ref ctx, compressionModel); + var val = reader.ReadPackedUInt(compressionModel); Assert.AreEqual(base_val + i, val); } - Assert.AreEqual(1979, reader.ReadInt(ref ctx)); + Assert.AreEqual(1979, reader.ReadInt()); } } - + [Test] + public void WriteOutOfBounds() + { + var dataStream = new DataStreamWriter(9, Allocator.Temp); + Assert.IsTrue(dataStream.WriteInt(42)); + Assert.AreEqual(4, dataStream.Length); + Assert.IsTrue(dataStream.WriteInt(42)); + Assert.AreEqual(8, dataStream.Length); + Assert.IsFalse(dataStream.HasFailedWrites); + Assert.IsFalse(dataStream.WriteInt(42)); + Assert.AreEqual(8, dataStream.Length); + Assert.IsTrue(dataStream.HasFailedWrites); + + Assert.IsFalse(dataStream.WriteShort(42)); + Assert.AreEqual(8, dataStream.Length); + Assert.IsTrue(dataStream.HasFailedWrites); + + Assert.IsTrue(dataStream.WriteByte(42)); + Assert.AreEqual(9, dataStream.Length); + Assert.IsTrue(dataStream.HasFailedWrites); + + Assert.IsFalse(dataStream.WriteByte(42)); + Assert.AreEqual(9, dataStream.Length); + Assert.IsTrue(dataStream.HasFailedWrites); + } [Test] public void ReadWriteString() { @@ -225,11 +193,9 @@ public void ReadWriteString() //Assert.AreEqual(src.LengthInBytes+2, dataStream.Length); - var reader = new DataStreamReader(dataStream, 0, dataStream.Length); - DataStreamReader.Context ctx = default; - var dst = reader.ReadString(ref ctx); + var reader = new DataStreamReader(dataStream.AsNativeArray()); + var dst = reader.ReadString(); Assert.AreEqual(src, dst); - ctx = default; } [Test] public void ReadWritePackedStringDelta() @@ -244,11 +210,9 @@ public void ReadWritePackedStringDelta() //Assert.LessOrEqual(dataStream.Length, src.LengthInBytes+2); - var reader = new DataStreamReader(dataStream, 0, dataStream.Length); - DataStreamReader.Context ctx = default; - var dst = reader.ReadPackedStringDelta(ref ctx, baseline, compressionModel); + var reader = new DataStreamReader(dataStream.AsNativeArray()); + var dst = reader.ReadPackedStringDelta(baseline, compressionModel); Assert.AreEqual(src, dst); - ctx = default; } } } \ No newline at end of file diff --git a/Tests/Editor/NetworkAddressTests.cs b/Tests/Editor/NetworkAddressTests.cs new file mode 100644 index 0000000..8763cfd --- /dev/null +++ b/Tests/Editor/NetworkAddressTests.cs @@ -0,0 +1,88 @@ +using NUnit.Framework; + +namespace Unity.Networking.Transport.Tests +{ + [TestFixture] + public class NetworkAddressTests + { + [Test] + public unsafe void NetworkAddress_CastingTests() + { + var endpoint = new NetworkEndPoint(); + endpoint.Port = 1; + Assert.True(1 == endpoint.Port); + + Assert.True(256 == endpoint.RawPort); + } + + [Test] + public unsafe void NetworkAddress_ParseAddress_CompareToBaselibParse() + { + // 19 == SizeOf + //Assert.True(19 == UnsafeUtility.SizeOf()); + + string[] addresses = { + "127.0.0.1", + "192.168.1.134", + "53BF:009C:0000:0000:120A:09D5:000D:CD29", + "2001:0db8::0370:7334", + "2001:db8::123.123.123.123", + "1200:0000:AB00:1234:0000:2552:7777:1313", + "21DA:D3:0:2F3B:2AA:FF:FE28:9C5A", + "FE80:0000:0000:0000:0202:B3FF:FE1E:8329", + "53BF:009C:0000:0000:120A:09D5:000D:CD29", + "0.0.0.0", + "9.255.255.255", + "11.0.0.0", + "126.255.255.255", + "129.0.0.0", + "169.253.255.255", + "169.255.0.0", + "172.15.255.255", + "172.32.0.0", + "191.0.1.255", + "192.88.98.255", + "192.88.100.0", + "192.167.255.255", + "192.169.0.0", + "198.17.255.255", + "223.255.255.255", + "[2001:db8:0:1]:80", + "http://[2001:db8:0:1]:80", + "1200:0000:AB00:1234:O000:2552:7777:1313" + }; + + NetworkFamily[] families = + { + NetworkFamily.Ipv4, + NetworkFamily.Ipv4, + NetworkFamily.Ipv6, + NetworkFamily.Ipv6, + NetworkFamily.Ipv6, + NetworkFamily.Ipv6, + NetworkFamily.Ipv6, + NetworkFamily.Ipv6, + NetworkFamily.Ipv6, + NetworkFamily.Ipv4, + NetworkFamily.Ipv4, + NetworkFamily.Ipv4, + NetworkFamily.Ipv4, + NetworkFamily.Ipv4, + NetworkFamily.Ipv4, + NetworkFamily.Ipv4, + NetworkFamily.Ipv4, + NetworkFamily.Ipv4, + NetworkFamily.Ipv4, + NetworkFamily.Ipv4, + NetworkFamily.Ipv4, + NetworkFamily.Ipv4, + NetworkFamily.Ipv4, + NetworkFamily.Ipv4, + NetworkFamily.Ipv4, + NetworkFamily.Ipv6, + NetworkFamily.Ipv6, + NetworkFamily.Ipv6 + }; + } + } +} diff --git a/Tests/Editor/NetworkAddressTests.cs.meta b/Tests/Editor/NetworkAddressTests.cs.meta new file mode 100644 index 0000000..9ed2173 --- /dev/null +++ b/Tests/Editor/NetworkAddressTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: da44f9825ea0440e9ab582a9902fa401 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Tests/Editor/NetworkConnectionUnitTests.cs b/Tests/Editor/NetworkConnectionUnitTests.cs index b9e258a..afe9c43 100644 --- a/Tests/Editor/NetworkConnectionUnitTests.cs +++ b/Tests/Editor/NetworkConnectionUnitTests.cs @@ -24,20 +24,16 @@ public static class SharedConstants public class NetworkConnectionUnitTests { - private LocalNetworkDriver Driver; - private LocalNetworkDriver RemoteDriver; - private DataStreamWriter Stream; + private NetworkDriver Driver; + private NetworkDriver RemoteDriver; [SetUp] public void IPC_Setup() { - IPCManager.Instance.Initialize(100); + Driver = TestNetworkDriver.Create(new NetworkDataStreamParameter {size = 64}); + RemoteDriver = TestNetworkDriver.Create(new NetworkDataStreamParameter {size = 64}); - Stream = new DataStreamWriter(64, Allocator.Persistent); - Driver = new LocalNetworkDriver(new NetworkDataStreamParameter {size = 64}); - RemoteDriver = new LocalNetworkDriver(new NetworkDataStreamParameter {size = 64}); - - RemoteDriver.Bind(IPCManager.Instance.CreateEndPoint("remote_host")); + RemoteDriver.Bind(NetworkEndPoint.LoopbackIpv4); RemoteDriver.Listen(); } @@ -46,9 +42,6 @@ public void IPC_TearDown() { Driver.Dispose(); RemoteDriver.Dispose(); - Stream.Dispose(); - - IPCManager.Instance.Destroy(); } [Test] @@ -108,8 +101,9 @@ public void Connection_SetupSendAndReceive() Assert.That(connection.PopEvent(Driver, out reader) == NetworkEvent.Type.Connect); // Send to endpoint - Stream.Write(SharedConstants.ping); - connection.Send(Driver, Stream); + var Stream = Driver.BeginSend(NetworkPipeline.Null, connection); + Stream.WriteBytes(new NativeArray(SharedConstants.ping, Allocator.Temp)); + Driver.EndSend(Stream); Driver.ScheduleUpdate().Complete(); RemoteDriver.ScheduleUpdate().Complete(); diff --git a/Tests/Editor/NetworkDriverFlowTest.cs b/Tests/Editor/NetworkDriverFlowTest.cs index 28052bf..0e7c7b8 100644 --- a/Tests/Editor/NetworkDriverFlowTest.cs +++ b/Tests/Editor/NetworkDriverFlowTest.cs @@ -24,13 +24,13 @@ public void Tick() public class UnreliableServer : IDisposable { - private LocalNetworkDriver m_Driver; + private NetworkDriver m_Driver; //private List m_Connections; public UnreliableServer() { - m_Driver = new LocalNetworkDriver(new NetworkDataStreamParameter + m_Driver = TestNetworkDriver.Create(new NetworkDataStreamParameter {size = NetworkParameterConstants.MTU}); } diff --git a/Tests/Editor/NetworkDriverUnitTests.cs b/Tests/Editor/NetworkDriverUnitTests.cs index 13f1660..088680e 100644 --- a/Tests/Editor/NetworkDriverUnitTests.cs +++ b/Tests/Editor/NetworkDriverUnitTests.cs @@ -1,9 +1,9 @@ using System; using System.Collections.Generic; using System.Text; -using Unity.Networking.Transport.LowLevel.Unsafe; using NUnit.Framework; using Unity.Collections; +using Unity.Collections.LowLevel.Unsafe; using UnityEngine; using Unity.Networking.Transport.Protocols; using Unity.Networking.Transport.Utilities; @@ -29,28 +29,28 @@ namespace Unity.Networking.Transport.Tests { public struct LocalDriverHelper : IDisposable { - public NetworkEndPoint Address { get; } - public LocalNetworkDriver m_LocalDriver; - private DataStreamWriter m_LocalDataStream; + public NetworkEndPoint EndPoint { get; } + public NetworkDriver m_LocalDriver; + private NativeArray m_LocalData; public NetworkConnection Connection { get; internal set; } public List ClientConnections; public LocalDriverHelper(NetworkEndPoint endpoint, params INetworkParameter[] networkParams) { if (networkParams.Length == 0) - m_LocalDriver = new LocalNetworkDriver(new NetworkDataStreamParameter + m_LocalDriver = new NetworkDriver(new IPCNetworkInterface(), new NetworkDataStreamParameter {size = NetworkParameterConstants.MTU}); else - m_LocalDriver = new LocalNetworkDriver(networkParams); - m_LocalDataStream = new DataStreamWriter(NetworkParameterConstants.MTU, Allocator.Persistent); + m_LocalDriver = new NetworkDriver(new IPCNetworkInterface(), networkParams); + m_LocalData = new NativeArray(NetworkParameterConstants.MTU, Allocator.Persistent); - if (endpoint.Family == NetworkFamily.IPC && endpoint.Port != 0) + if (endpoint.IsValid) { - Address = endpoint; + EndPoint = endpoint; } else { - Address = IPCManager.Instance.CreateEndPoint(Utilities.Random.String(32)); + EndPoint = NetworkEndPoint.LoopbackIpv4.WithPort(1); } Connection = default(NetworkConnection); @@ -60,7 +60,7 @@ public LocalDriverHelper(NetworkEndPoint endpoint, params INetworkParameter[] ne public void Dispose() { m_LocalDriver.Dispose(); - m_LocalDataStream.Dispose(); + m_LocalData.Dispose(); } public void Update() @@ -75,13 +75,13 @@ public NetworkConnection Accept() public void Host() { - m_LocalDriver.Bind(Address); + m_LocalDriver.Bind(EndPoint); m_LocalDriver.Listen(); } public void Connect(NetworkEndPoint endpoint) { - Assert.True(endpoint.Family == NetworkFamily.IPC); + Assert.True(endpoint.IsValid); Connection = m_LocalDriver.Connect(endpoint); m_LocalDriver.ScheduleUpdate().Complete(); } @@ -89,23 +89,22 @@ public void Connect(NetworkEndPoint endpoint) public unsafe void Assert_GotConnectionRequest(NetworkEndPoint from, bool accept = false) { int length; - NetworkEndPoint remote; - m_LocalDataStream.Clear(); + NetworkInterfaceEndPoint remote; + Assert.True(EndPoint.IsLoopback || EndPoint.IsAny); + Assert.True(from.IsLoopback || from.IsAny); + var localEndPoint = IPCManager.Instance.CreateEndPoint(EndPoint.Port); + var fromEndPoint = IPCManager.Instance.CreateEndPoint(from.Port); Assert.True( - IPCManager.Instance.PeekNext(Address, m_LocalDataStream.GetUnsafePtr(), out length, out remote) >= + IPCManager.Instance.PeekNext(localEndPoint, m_LocalData.GetUnsafePtr(), out length, out remote) >= sizeof(UdpCHeader)); - m_LocalDataStream.WriteBytesWithUnsafePointer(length); UdpCHeader header = new UdpCHeader(); - var reader = new DataStreamReader(m_LocalDataStream, 0, sizeof(UdpCHeader)); - var readerCtx = default(DataStreamReader.Context); + var reader = new DataStreamReader(m_LocalData.GetSubArray(0, sizeof(UdpCHeader))); Assert.True(reader.IsCreated); - reader.ReadBytes(ref readerCtx, header.Data, sizeof(UdpCHeader)); + reader.ReadBytes(header.Data, sizeof(UdpCHeader)); Assert.True(header.Type == (int) UdpCProtocol.ConnectionRequest); - Assert.True(remote.Family == NetworkFamily.IPC); - //Assert.True(remote.ipc_handle == from.ipc_handle); - Assert.True(remote.Port == from.Port); + Assert.True(remote == fromEndPoint); if (accept) { @@ -119,38 +118,40 @@ public unsafe void Assert_GotConnectionRequest(NetworkEndPoint from, bool accept public unsafe void Assert_GotDisconnectionRequest(NetworkEndPoint from) { int length; - NetworkEndPoint remote; - m_LocalDataStream.Clear(); + NetworkInterfaceEndPoint remote; + Assert.True(EndPoint.IsLoopback || EndPoint.IsAny); + Assert.True(from.IsLoopback || from.IsAny); + var localEndPoint = IPCManager.Instance.CreateEndPoint(EndPoint.Port); + var fromEndPoint = IPCManager.Instance.CreateEndPoint(from.Port); Assert.True( - IPCManager.Instance.PeekNext(Address, m_LocalDataStream.GetUnsafePtr(), out length, out remote) >= + IPCManager.Instance.PeekNext(localEndPoint, m_LocalData.GetUnsafePtr(), out length, out remote) >= sizeof(UdpCHeader)); - m_LocalDataStream.WriteBytesWithUnsafePointer(length); UdpCHeader header = new UdpCHeader(); - var reader = new DataStreamReader(m_LocalDataStream, 0, sizeof(UdpCHeader)); - var readerCtx = default(DataStreamReader.Context); + var reader = new DataStreamReader(m_LocalData.GetSubArray(0, sizeof(UdpCHeader))); Assert.True(reader.IsCreated); - reader.ReadBytes(ref readerCtx, header.Data, sizeof(UdpCHeader)); + reader.ReadBytes(header.Data, sizeof(UdpCHeader)); Assert.True(header.Type == (int) UdpCProtocol.Disconnect); - Assert.True(remote.Family == NetworkFamily.IPC); - //Assert.True(remote.ipc_handle == from.ipc_handle); - Assert.True(remote.Port == from.Port); + Assert.True(remote == fromEndPoint); } public unsafe void Assert_GotDataRequest(NetworkEndPoint from, byte[] dataToCompare) { - NetworkEndPoint remote = default(NetworkEndPoint); - m_LocalDataStream.Clear(); + NetworkInterfaceEndPoint remote = default; network_iovec[] iovecs = new network_iovec[2]; - iovecs[0].buf = m_LocalDataStream.GetUnsafePtr(); + iovecs[0].buf = m_LocalData.GetUnsafePtr(); iovecs[0].len = sizeof(UdpCHeader); - iovecs[1].buf = m_LocalDataStream.GetUnsafePtr() + sizeof(UdpCHeader); + iovecs[1].buf = (byte*)m_LocalData.GetUnsafePtr() + sizeof(UdpCHeader); iovecs[1].len = NetworkParameterConstants.MTU; int dataLen = 0; + Assert.True(EndPoint.IsLoopback || EndPoint.IsAny); + Assert.True(from.IsLoopback || from.IsAny); + var localEndPoint = IPCManager.Instance.CreateEndPoint(EndPoint.Port); + var fromEndPoint = IPCManager.Instance.CreateEndPoint(from.Port); fixed (network_iovec* iovptr = &iovecs[0]) { - dataLen = IPCManager.Instance.ReceiveMessageEx(Address, iovptr, 2, ref remote); + dataLen = IPCManager.Instance.ReceiveMessageEx(localEndPoint, iovptr, 2, ref remote); } if (dataLen <= 0) @@ -160,25 +161,20 @@ public unsafe void Assert_GotDataRequest(NetworkEndPoint from, byte[] dataToComp Assert.True(iovecs[0].len+iovecs[1].len == dataLen); Assert.True(iovecs[0].len == sizeof(UdpCHeader)); - m_LocalDataStream.WriteBytesWithUnsafePointer(iovecs[0].len); UdpCHeader header = new UdpCHeader(); - var reader = new DataStreamReader(m_LocalDataStream, 0, sizeof(UdpCHeader)); - var readerCtx = default(DataStreamReader.Context); + var reader = new DataStreamReader(m_LocalData.GetSubArray(0, sizeof(UdpCHeader))); Assert.True(reader.IsCreated); - reader.ReadBytes(ref readerCtx, header.Data, sizeof(UdpCHeader)); + reader.ReadBytes(header.Data, sizeof(UdpCHeader)); Assert.True(header.Type == (int) UdpCProtocol.Data); - Assert.True(remote.Family == NetworkFamily.IPC); - //Assert.True(remote.ipc_handle == from.ipc_handle); - Assert.True(remote.Port == from.Port); + Assert.True(remote == fromEndPoint); Assert.True(iovecs[1].len == dataToCompare.Length); - m_LocalDataStream.WriteBytesWithUnsafePointer(iovecs[1].len); - reader = new DataStreamReader(m_LocalDataStream, iovecs[0].len, dataToCompare.Length); - readerCtx = default(DataStreamReader.Context); - var received = reader.ReadBytesAsArray(ref readerCtx, dataToCompare.Length); + reader = new DataStreamReader(m_LocalData.GetSubArray(iovecs[0].len, dataToCompare.Length)); + var received = new NativeArray(dataToCompare.Length, Allocator.Temp); + reader.ReadBytes(received); for (int i = 0, n = dataToCompare.Length; i < n; ++i) Assert.True(received[i] == dataToCompare[i]); @@ -202,43 +198,29 @@ public unsafe void Assert_PopEvent(out NetworkConnection connection, NetworkEven public class NetworkDriverUnitTests { - private Timer m_timer; - [SetUp] - public void IPC_Setup() - { - m_timer = new Timer(); - IPCManager.Instance.Initialize(100); - } - - [TearDown] - public void IPC_TearDown() - { - IPCManager.Instance.Destroy(); - } - [Test] public void InitializeAndDestroyDriver() { - var driver = new LocalNetworkDriver(new NetworkDataStreamParameter {size = 64}); + var driver = TestNetworkDriver.Create(new NetworkDataStreamParameter {size = 64}); driver.Dispose(); } [Test] public void BindDriverToAEndPoint() { - var driver = new LocalNetworkDriver(new NetworkDataStreamParameter {size = 64}); + var driver = TestNetworkDriver.Create(new NetworkDataStreamParameter {size = 64}); - driver.Bind(IPCManager.Instance.CreateEndPoint("host")); + driver.Bind(NetworkEndPoint.LoopbackIpv4); driver.Dispose(); } [Test] public void ListenOnDriver() { - var driver = new LocalNetworkDriver(new NetworkDataStreamParameter {size = 64}); + var driver = TestNetworkDriver.Create(new NetworkDataStreamParameter {size = 64}); // Make sure we Bind before we Listen. - driver.Bind(IPCManager.Instance.CreateEndPoint("host")); + driver.Bind(NetworkEndPoint.LoopbackIpv4); driver.Listen(); Assert.True(driver.Listening); @@ -248,10 +230,10 @@ public void ListenOnDriver() [Test] public void AcceptNewConnectionsOnDriver() { - var driver = new LocalNetworkDriver(new NetworkDataStreamParameter {size = 64}); + var driver = TestNetworkDriver.Create(new NetworkDataStreamParameter {size = 64}); // Make sure we Bind before we Listen. - driver.Bind(IPCManager.Instance.CreateEndPoint("host")); + driver.Bind(NetworkEndPoint.LoopbackIpv4); driver.Listen(); Assert.True(driver.Listening); @@ -271,9 +253,9 @@ public void ConnectToARemoteEndPoint() using (var host = new LocalDriverHelper(default(NetworkEndPoint))) { host.Host(); - var driver = new LocalNetworkDriver(new NetworkDataStreamParameter {size = 64}); + var driver = new NetworkDriver(new IPCNetworkInterface(), new NetworkDataStreamParameter {size = 64}); - NetworkConnection connectionId = driver.Connect(host.Address); + NetworkConnection connectionId = driver.Connect(host.EndPoint); Assert.True(connectionId != default(NetworkConnection)); driver.ScheduleUpdate().Complete(); @@ -295,18 +277,17 @@ public void ConnectAttemptWithRetriesToARemoteEndPoint() NetworkConnection connection; NetworkEvent.Type eventType = 0; DataStreamReader reader; - var hostAddress = IPCManager.Instance.CreateEndPoint(Utilities.Random.String(32)); // Tiny connect timeout for this test to be quicker - using (var client = new LocalNetworkDriver(new NetworkDataStreamParameter {size = 64}, - new NetworkConfigParameter {connectTimeoutMS = 1, maxConnectAttempts = 10})) + using (var client = new NetworkDriver(new IPCNetworkInterface(), new NetworkDataStreamParameter {size = 64}, + new NetworkConfigParameter {connectTimeoutMS = 15, maxConnectAttempts = 10, fixedFrameTimeMS = 10})) { + var hostAddress = NetworkEndPoint.LoopbackIpv4.WithPort(1); client.Connect(hostAddress); // Wait past the connect timeout so there will be unanswered connect requests - long timeout = m_timer.ElapsedMilliseconds + 2; - while (m_timer.ElapsedMilliseconds < timeout) - client.ScheduleUpdate().Complete(); + client.ScheduleUpdate().Complete(); + client.ScheduleUpdate().Complete(); using (var host = new LocalDriverHelper(hostAddress)) { @@ -314,15 +295,12 @@ public void ConnectAttemptWithRetriesToARemoteEndPoint() // Now give the next connect attempt time to happen // TODO: Would be better to be able to see internal state here and explicitly wait until next connect attempt happens - timeout = m_timer.ElapsedMilliseconds + 10; - while (m_timer.ElapsedMilliseconds < timeout) - client.ScheduleUpdate().Complete(); + //client.ScheduleUpdate().Complete(); host.Assert_GotConnectionRequest(client.LocalEndPoint(), true); // Wait for the client to get the connect event back - timeout = m_timer.ElapsedMilliseconds + 2; - while (m_timer.ElapsedMilliseconds < timeout) + for (int i = 0; i < 2; ++i) { client.ScheduleUpdate().Complete(); eventType = client.PopEvent(out connection, out reader); @@ -341,10 +319,10 @@ public void DisconnectFromARemoteEndPoint() using (var host = new LocalDriverHelper(default(NetworkEndPoint))) { host.Host(); - var driver = new LocalNetworkDriver(new NetworkDataStreamParameter {size = 64}); + var driver = new NetworkDriver(new IPCNetworkInterface(), new NetworkDataStreamParameter {size = 64}); // Need to be connected in order to be able to send a disconnect packet. - NetworkConnection connectionId = driver.Connect(host.Address); + NetworkConnection connectionId = driver.Connect(host.EndPoint); Assert.True(connectionId != default(NetworkConnection)); driver.ScheduleUpdate().Complete(); @@ -369,8 +347,8 @@ public void DisconnectFromARemoteEndPoint() public void DisconnectTimeoutOnServer() { using (var host = new LocalDriverHelper(default(NetworkEndPoint), - new NetworkConfigParameter {disconnectTimeoutMS = 40})) - using (var client = new LocalNetworkDriver(new NetworkConfigParameter {disconnectTimeoutMS = 40})) + new NetworkConfigParameter {disconnectTimeoutMS = 40, fixedFrameTimeMS = 10})) + using (var client = new NetworkDriver(new IPCNetworkInterface(), new NetworkConfigParameter {disconnectTimeoutMS = 40, fixedFrameTimeMS = 10})) { NetworkConnection id; NetworkEvent.Type popEvent = NetworkEvent.Type.Empty; @@ -378,26 +356,24 @@ public void DisconnectTimeoutOnServer() host.Host(); - client.Connect(host.Address); + client.Connect(host.EndPoint); client.ScheduleUpdate().Complete(); host.Assert_GotConnectionRequest(client.LocalEndPoint(), true); - var stream = new DataStreamWriter(100, Allocator.Persistent); - for (int i = 0; i < 100; i++) - stream.Write((byte) i); - // Host sends stuff but gets nothing back, until disconnect timeout happens - var timeout = m_timer.ElapsedMilliseconds + 100; - while (m_timer.ElapsedMilliseconds < timeout) + for (int frm = 0; frm < 10; ++frm) { - host.m_LocalDriver.Send(NetworkPipeline.Null, host.ClientConnections[0], stream); + var stream = host.m_LocalDriver.BeginSend(NetworkPipeline.Null, host.ClientConnections[0]); + for (int i = 0; i < 100; i++) + stream.WriteByte((byte) i); + + host.m_LocalDriver.EndSend(stream); popEvent = host.m_LocalDriver.PopEvent(out id, out reader); if (popEvent != NetworkEvent.Type.Empty) break; host.Update(); } - stream.Dispose(); Assert.AreEqual(NetworkEvent.Type.Disconnect, popEvent); } } @@ -405,14 +381,13 @@ public void DisconnectTimeoutOnServer() [Test] public void SendDataToRemoteEndPoint() { - using (var host = new LocalDriverHelper(default(NetworkEndPoint))) - using (var stream = new DataStreamWriter(64, Allocator.Persistent)) + using (var host = new LocalDriverHelper(default)) { host.Host(); - var driver = new LocalNetworkDriver(new NetworkDataStreamParameter {size = 64}); + var driver = new NetworkDriver(new IPCNetworkInterface(), new NetworkDataStreamParameter {size = 64}); // Need to be connected in order to be able to send a disconnect packet. - NetworkConnection connectionId = driver.Connect(host.Address); + NetworkConnection connectionId = driver.Connect(host.EndPoint); Assert.True(connectionId != default(NetworkConnection)); driver.ScheduleUpdate().Complete(); var local = driver.LocalEndPoint(); @@ -424,10 +399,10 @@ public void SendDataToRemoteEndPoint() driver.ScheduleUpdate().Complete(); Assert.AreEqual(NetworkEvent.Type.Connect, driver.PopEvent(out con, out slice)); - stream.Clear(); + var stream = driver.BeginSend(NetworkPipeline.Null, connectionId); var data = Encoding.ASCII.GetBytes("data to send"); - stream.Write(data); - driver.Send(NetworkPipeline.Null, connectionId, stream); + stream.WriteBytes(new NativeArray(data, Allocator.Temp)); + driver.EndSend(stream); driver.ScheduleUpdate().Complete(); host.Assert_GotDataRequest(local, data); @@ -439,13 +414,13 @@ public void SendDataToRemoteEndPoint() [Test] public void HandleEventsFromSpecificEndPoint() { - using (var host = new LocalDriverHelper(default(NetworkEndPoint))) - using (var client0 = new LocalDriverHelper(default(NetworkEndPoint))) - using (var client1 = new LocalDriverHelper(default(NetworkEndPoint))) + using (var host = new LocalDriverHelper(default)) + using (var client0 = new LocalDriverHelper(default)) + using (var client1 = new LocalDriverHelper(default)) { host.Host(); - client0.Connect(host.Address); - client1.Connect(host.Address); + client0.Connect(host.EndPoint); + client1.Connect(host.EndPoint); host.Assert_PopEventForConnection(client0.Connection, NetworkEvent.Type.Empty); host.Assert_PopEventForConnection(client1.Connection, NetworkEvent.Type.Empty); @@ -468,13 +443,13 @@ public void HandleEventsFromSpecificEndPoint() [Test] public void HandleEventsFromAnyEndPoint() { - using (var host = new LocalDriverHelper(default(NetworkEndPoint))) - using (var client0 = new LocalDriverHelper(default(NetworkEndPoint))) - using (var client1 = new LocalDriverHelper(default(NetworkEndPoint))) + using (var host = new LocalDriverHelper(default)) + using (var client0 = new LocalDriverHelper(default)) + using (var client1 = new LocalDriverHelper(default)) { host.Host(); - client0.Connect(host.Address); - client1.Connect(host.Address); + client0.Connect(host.EndPoint); + client1.Connect(host.EndPoint); host.Assert_PopEventForConnection(client0.Connection, NetworkEvent.Type.Empty); host.Assert_PopEventForConnection(client1.Connection, NetworkEvent.Type.Empty); @@ -505,11 +480,11 @@ public void FillInternalBitStreamBuffer() const int k_PacketCount = 21; // Exactly enough to fill the receive buffer + 1 too much const int k_PacketSize = 50; - using (var host = new LocalNetworkDriver(new NetworkDataStreamParameter {size = k_InternalBufferSize})) - using (var client = new LocalNetworkDriver(new NetworkDataStreamParameter {size = 64})) - using (var stream = new DataStreamWriter(64, Allocator.Persistent)) + using (var host = new NetworkDriver(new IPCNetworkInterface(), new NetworkDataStreamParameter {size = k_InternalBufferSize})) + using (var client = new NetworkDriver(new IPCNetworkInterface(), new NetworkDataStreamParameter {size = 64})) { - host.Bind(IPCManager.Instance.CreateEndPoint(Utilities.Random.String(32))); + host.Bind(NetworkEndPoint.LoopbackIpv4); + host.Listen(); NetworkConnection connectionId = client.Connect(host.LocalEndPoint()); @@ -535,9 +510,9 @@ public void FillInternalBitStreamBuffer() for (int i = 0; i < k_PacketCount; ++i) { - stream.Clear(); - stream.Write(dataBlob[i]); - client.Send(NetworkPipeline.Null, connectionId, stream); + var stream = client.BeginSend(NetworkPipeline.Null, connectionId); + stream.WriteBytes(new NativeArray(dataBlob[i], Allocator.Temp)); + client.EndSend(stream); } // Process the pending events @@ -559,10 +534,9 @@ public void FillInternalBitStreamBuffer() Assert.AreEqual(retval, NetworkEvent.Type.Data); Assert.AreEqual(k_PacketSize, reader.Length); - var readerCtx = default(DataStreamReader.Context); for (int j = 0; j < k_PacketSize; ++j) { - Assert.AreEqual(dataBlob[i][j], reader.ReadByte(ref readerCtx)); + Assert.AreEqual(dataBlob[i][j], reader.ReadByte()); } } } @@ -571,21 +545,17 @@ public void FillInternalBitStreamBuffer() [Test] public void SendAndReceiveMessage_RealNetwork() { - using (var clientSendData = new DataStreamWriter(64, Allocator.Persistent)) + using (var serverDriver = NetworkDriver.Create(new NetworkDataStreamParameter {size = 64})) + using (var clientDriver = NetworkDriver.Create(new NetworkDataStreamParameter {size = 64})) { DataStreamReader stream; - var serverEndpoint = NetworkEndPoint.LoopbackIpv4; - serverEndpoint.Port = (ushort)Random.Range(2000, 65000); - var serverDriver = new UdpNetworkDriver(new NetworkDataStreamParameter {size = 64}); + var serverEndpoint = NetworkEndPoint.Parse("127.0.0.1", (ushort)Random.Range(2000, 65000)); serverDriver.Bind(serverEndpoint); - serverDriver.Listen(); - var clientDriver = new UdpNetworkDriver(new NetworkDataStreamParameter {size = 64}); - clientDriver.Bind(NetworkEndPoint.LoopbackIpv4); - var clientToServerId = clientDriver.Connect(serverEndpoint); + clientDriver.ScheduleFlushSend(default).Complete(); NetworkConnection serverToClientId = default(NetworkConnection); // Retry a few times since the network might need some time to process @@ -607,99 +577,93 @@ public void SendAndReceiveMessage_RealNetwork() int testInt = 100; float testFloat = 555.5f; byte[] testByteArray = Encoding.ASCII.GetBytes("Some bytes blablabla 1111111111111111111"); - clientSendData.Write(testInt); - clientSendData.Write(testFloat); - clientSendData.Write(testByteArray.Length); - clientSendData.Write(testByteArray); - var sentBytes = clientDriver.Send(NetworkPipeline.Null, clientToServerId, clientSendData); + var clientSendData = clientDriver.BeginSend(NetworkPipeline.Null, clientToServerId); + clientSendData.WriteInt(testInt); + clientSendData.WriteFloat(testFloat); + clientSendData.WriteInt(testByteArray.Length); + clientSendData.WriteBytes(new NativeArray(testByteArray, Allocator.Temp)); + var sentBytes = clientDriver.EndSend(clientSendData); - // Header size is included in the sent bytes count (4 bytes overhead) - Assert.AreEqual(clientSendData.Length + 4, sentBytes); + Assert.AreEqual(clientSendData.Length, sentBytes); clientDriver.ScheduleUpdate().Complete(); serverDriver.ScheduleUpdate().Complete(); DataStreamReader serverReceiveStream; eventId = serverDriver.PopEventForConnection(serverToClientId, out serverReceiveStream); - var readerCtx = default(DataStreamReader.Context); Assert.True(eventId == NetworkEvent.Type.Data); - var receivedInt = serverReceiveStream.ReadInt(ref readerCtx); - var receivedFloat = serverReceiveStream.ReadFloat(ref readerCtx); - var byteArrayLength = serverReceiveStream.ReadInt(ref readerCtx); - var receivedBytes = serverReceiveStream.ReadBytesAsArray(ref readerCtx, byteArrayLength); + var receivedInt = serverReceiveStream.ReadInt(); + var receivedFloat = serverReceiveStream.ReadFloat(); + var byteArrayLength = serverReceiveStream.ReadInt(); + var receivedBytes = new NativeArray(byteArrayLength, Allocator.Temp); + serverReceiveStream.ReadBytes(receivedBytes); Assert.True(testInt == receivedInt); Assert.That(Mathf.Approximately(testFloat, receivedFloat)); Assert.AreEqual(testByteArray, receivedBytes); - - clientDriver.Dispose(); - serverDriver.Dispose(); } } [Test] public void SendAndReceiveMessage() { - using (var clientSendData = new DataStreamWriter(64, Allocator.Persistent)) - { - DataStreamReader stream; - var serverEndpoint = IPCManager.Instance.CreateEndPoint("server"); + DataStreamReader stream; - var serverDriver = new LocalNetworkDriver(new NetworkDataStreamParameter {size = 64}); - serverDriver.Bind(serverEndpoint); + var serverDriver = new NetworkDriver(new IPCNetworkInterface(), new NetworkDataStreamParameter {size = 64}); + var serverEndpoint = NetworkEndPoint.LoopbackIpv4.WithPort(1); + serverDriver.Bind(serverEndpoint); - serverDriver.Listen(); + serverDriver.Listen(); - var clientDriver = new LocalNetworkDriver(new NetworkDataStreamParameter {size = 64}); - clientDriver.Bind(IPCManager.Instance.CreateEndPoint("client")); + var clientDriver = new NetworkDriver(new IPCNetworkInterface(), new NetworkDataStreamParameter {size = 64}); + clientDriver.Bind(NetworkEndPoint.LoopbackIpv4); - var clientToServerId = clientDriver.Connect(serverEndpoint); - clientDriver.ScheduleUpdate().Complete(); + var clientToServerId = clientDriver.Connect(serverEndpoint); + clientDriver.ScheduleUpdate().Complete(); - serverDriver.ScheduleUpdate().Complete(); + serverDriver.ScheduleUpdate().Complete(); - NetworkConnection serverToClientId = serverDriver.Accept(); - Assert.That(serverToClientId != default(NetworkConnection)); + NetworkConnection serverToClientId = serverDriver.Accept(); + Assert.That(serverToClientId != default(NetworkConnection)); - clientDriver.ScheduleUpdate().Complete(); + clientDriver.ScheduleUpdate().Complete(); - var eventId = clientDriver.PopEventForConnection(clientToServerId, out stream); - Assert.That(eventId == NetworkEvent.Type.Connect); + var eventId = clientDriver.PopEventForConnection(clientToServerId, out stream); + Assert.That(eventId == NetworkEvent.Type.Connect); - int testInt = 100; - float testFloat = 555.5f; - byte[] testByteArray = Encoding.ASCII.GetBytes("Some bytes blablabla 1111111111111111111"); - clientSendData.Write(testInt); - clientSendData.Write(testFloat); - clientSendData.Write(testByteArray.Length); - clientSendData.Write(testByteArray); - var sentBytes = clientDriver.Send(NetworkPipeline.Null, clientToServerId, clientSendData); + int testInt = 100; + float testFloat = 555.5f; + byte[] testByteArray = Encoding.ASCII.GetBytes("Some bytes blablabla 1111111111111111111"); + var clientSendData = clientDriver.BeginSend(NetworkPipeline.Null, clientToServerId); + clientSendData.WriteInt(testInt); + clientSendData.WriteFloat(testFloat); + clientSendData.WriteInt(testByteArray.Length); + clientSendData.WriteBytes(new NativeArray(testByteArray, Allocator.Temp)); + var sentBytes = clientDriver.EndSend(clientSendData); - // Header size is included in the sent bytes count (4 bytes overhead) - Assert.AreEqual(clientSendData.Length + 4, sentBytes); + Assert.AreEqual(clientSendData.Length, sentBytes); - clientDriver.ScheduleUpdate().Complete(); - serverDriver.ScheduleUpdate().Complete(); + clientDriver.ScheduleUpdate().Complete(); + serverDriver.ScheduleUpdate().Complete(); - DataStreamReader serverReceiveStream; - eventId = serverDriver.PopEventForConnection(serverToClientId, out serverReceiveStream); - var readerCtx = default(DataStreamReader.Context); + DataStreamReader serverReceiveStream; + eventId = serverDriver.PopEventForConnection(serverToClientId, out serverReceiveStream); - Assert.True(eventId == NetworkEvent.Type.Data); - var receivedInt = serverReceiveStream.ReadInt(ref readerCtx); - var receivedFloat = serverReceiveStream.ReadFloat(ref readerCtx); - var byteArrayLength = serverReceiveStream.ReadInt(ref readerCtx); - var receivedBytes = serverReceiveStream.ReadBytesAsArray(ref readerCtx, byteArrayLength); + Assert.True(eventId == NetworkEvent.Type.Data); + var receivedInt = serverReceiveStream.ReadInt(); + var receivedFloat = serverReceiveStream.ReadFloat(); + var byteArrayLength = serverReceiveStream.ReadInt(); + var receivedBytes = new NativeArray(byteArrayLength, Allocator.Temp); + serverReceiveStream.ReadBytes(receivedBytes); - Assert.True(testInt == receivedInt); - Assert.That(Mathf.Approximately(testFloat, receivedFloat)); - Assert.AreEqual(testByteArray, receivedBytes); + Assert.True(testInt == receivedInt); + Assert.That(Mathf.Approximately(testFloat, receivedFloat)); + Assert.AreEqual(testByteArray, receivedBytes); - clientDriver.Dispose(); - serverDriver.Dispose(); - } + clientDriver.Dispose(); + serverDriver.Dispose(); } } } \ No newline at end of file diff --git a/Tests/Editor/NetworkEndPointUnitTests.cs b/Tests/Editor/NetworkEndPointUnitTests.cs deleted file mode 100644 index f6cb251..0000000 --- a/Tests/Editor/NetworkEndPointUnitTests.cs +++ /dev/null @@ -1,53 +0,0 @@ -using NUnit.Framework; - -namespace Unity.Networking.Transport.Tests -{ - public class NetworkEndPointUnitTests - { - [Test] - public void NetworkEndPoint_Parse_WorksAsExpected() - { - ushort port = 12345; - NetworkEndPoint nep = NetworkEndPoint.LoopbackIpv4; - nep.Port = port; - - Assert.That(nep.Family == NetworkFamily.UdpIpv4); - Assert.That(nep.Port == port); - - NetworkEndPoint iep = NetworkEndPoint.Parse("127.0.0.1", port); - - Assert.That(nep == iep); - } - - [Test] - public void NetworkEndPoint_Parse_ExtractsPortIfPresent() - { - ushort defaultPort = 12345; - ushort customPort = 6789; - NetworkEndPoint nep = NetworkEndPoint.LoopbackIpv4; - nep.Port = customPort; - - Assert.That(nep.Family == NetworkFamily.UdpIpv4); - Assert.That(nep.Port == customPort); - - NetworkEndPoint iep = NetworkEndPoint.Parse("127.0.0.1:6789", defaultPort); - - Assert.That(nep == iep); - } - - [Test] - public void NetworkEndPoint_Parse_WhenEmptyPort_UsesDefaultPort() - { - ushort defaultPort = 12345; - NetworkEndPoint nep = NetworkEndPoint.LoopbackIpv4; - nep.Port = defaultPort; - - Assert.That(nep.Family == NetworkFamily.UdpIpv4); - Assert.That(nep.Port == defaultPort); - - NetworkEndPoint iep = NetworkEndPoint.Parse("127.0.0.1:", defaultPort); - - Assert.That(nep == iep); - } - } -} \ No newline at end of file diff --git a/Tests/Editor/NetworkEndPointUnitTests.cs.meta b/Tests/Editor/NetworkEndPointUnitTests.cs.meta deleted file mode 100644 index abe30fe..0000000 --- a/Tests/Editor/NetworkEndPointUnitTests.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 7c312b56290b9d14e9149e1d16a253eb -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Tests/Editor/NetworkHostUnitTests.cs b/Tests/Editor/NetworkHostUnitTests.cs index ebd3c38..73a7c8b 100644 --- a/Tests/Editor/NetworkHostUnitTests.cs +++ b/Tests/Editor/NetworkHostUnitTests.cs @@ -4,16 +4,14 @@ namespace Unity.Networking.Transport.Tests { public class NetworkHostUnitTests { - private LocalNetworkDriver Driver; - private LocalNetworkDriver RemoteDriver; + private NetworkDriver Driver; + private NetworkDriver RemoteDriver; [SetUp] public void IPC_Setup() { - IPCManager.Instance.Initialize(100); - - Driver = new LocalNetworkDriver(new NetworkDataStreamParameter {size = 64}); - RemoteDriver = new LocalNetworkDriver(new NetworkDataStreamParameter {size = 64}); + Driver = TestNetworkDriver.Create(new NetworkDataStreamParameter {size = 64}); + RemoteDriver = TestNetworkDriver.Create(new NetworkDataStreamParameter {size = 64}); } [TearDown] @@ -21,13 +19,12 @@ public void IPC_TearDown() { Driver.Dispose(); RemoteDriver.Dispose(); - IPCManager.Instance.Destroy(); } [Test] public void Listen() { - Driver.Bind(IPCManager.Instance.CreateEndPoint("network_host")); + Driver.Bind(NetworkEndPoint.LoopbackIpv4); Driver.Listen(); Assert.That(Driver.Listening); } @@ -35,7 +32,7 @@ public void Listen() [Test] public void Accept() { - Driver.Bind(IPCManager.Instance.CreateEndPoint("network_host")); + Driver.Bind(NetworkEndPoint.LoopbackIpv4); Driver.Listen(); Assert.That(Driver.Listening); diff --git a/Tests/Editor/NetworkJobTests.cs b/Tests/Editor/NetworkJobTests.cs index 1ef8b76..0011136 100644 --- a/Tests/Editor/NetworkJobTests.cs +++ b/Tests/Editor/NetworkJobTests.cs @@ -1,3 +1,6 @@ +#if UNITY_2020_1_OR_NEWER +#define UNITY_TRANSPORT_ENABLE_BASELIB +#endif using System; using NUnit.Framework; using Unity.Burst; @@ -9,23 +12,11 @@ namespace Unity.Networking.Transport.Tests { public class NetworkJobTests { - [SetUp] - public void IPC_Setup() - { - IPCManager.Instance.Initialize(100); - } - - [TearDown] - public void IPC_TearDown() - { - IPCManager.Instance.Destroy(); - } - - void WaitForConnected(LocalNetworkDriver clientDriver, LocalNetworkDriver serverDriver, + void WaitForConnected(NetworkDriver clientDriver, NetworkDriver serverDriver, NetworkConnection clientToServer) { // Make sure connect message is sent - clientDriver.ScheduleUpdate().Complete(); + clientDriver.ScheduleFlushSend(default).Complete(); // Make sure connection accept message is sent back serverDriver.ScheduleUpdate().Complete(); // Handle the connection accept message @@ -38,7 +29,7 @@ void WaitForConnected(LocalNetworkDriver clientDriver, LocalNetworkDriver server [Test] public void ScheduleUpdateWorks() { - var driver = new LocalNetworkDriver(new NetworkDataStreamParameter {size = 64}); + var driver = TestNetworkDriver.Create(new NetworkDataStreamParameter {size = 64}); var updateHandle = driver.ScheduleUpdate(); updateHandle.Complete(); driver.Dispose(); @@ -46,47 +37,44 @@ public void ScheduleUpdateWorks() [Test] public void ScheduleUpdateWithMissingDependencyThrowsException() { - var driver = new LocalNetworkDriver(new NetworkDataStreamParameter {size = 64}); + var driver = TestNetworkDriver.Create(new NetworkDataStreamParameter {size = 64}); var updateHandle = driver.ScheduleUpdate(); Assert.Throws(() => { driver.ScheduleUpdate().Complete(); }); updateHandle.Complete(); driver.Dispose(); } -#if UNITY_2019_3_OR_NEWER - [Ignore("Safety check temporarily disabled in 2019.3+")] -#endif [Test] public void DataStremReaderIsOnlyUsableUntilUpdate() { - var serverDriver = new LocalNetworkDriver(new NetworkDataStreamParameter {size = 64}); - serverDriver.Bind(IPCManager.Instance.CreateEndPoint()); + var serverDriver = TestNetworkDriver.Create(new NetworkDataStreamParameter {size = 64}); + serverDriver.Bind(NetworkEndPoint.LoopbackIpv4); serverDriver.Listen(); - var clientDriver = new LocalNetworkDriver(new NetworkDataStreamParameter {size = 64}); + var clientDriver = TestNetworkDriver.Create(new NetworkDataStreamParameter {size = 64}); var clientToServer = clientDriver.Connect(serverDriver.LocalEndPoint()); WaitForConnected(clientDriver, serverDriver, clientToServer); - var strmWriter = new DataStreamWriter(4, Allocator.Temp); - strmWriter.Write(42); - clientToServer.Send(clientDriver, strmWriter); + var strmWriter = clientDriver.BeginSend(clientToServer); + strmWriter.WriteInt(42); + clientDriver.EndSend(strmWriter); clientDriver.ScheduleUpdate().Complete(); var serverToClient = serverDriver.Accept(); serverDriver.ScheduleUpdate().Complete(); DataStreamReader strmReader; Assert.AreEqual(NetworkEvent.Type.Data, serverToClient.PopEvent(serverDriver, out strmReader)); - var ctx = default(DataStreamReader.Context); - Assert.AreEqual(42, strmReader.ReadInt(ref ctx)); - ctx = default(DataStreamReader.Context); - Assert.AreEqual(42, strmReader.ReadInt(ref ctx)); + var ctx = strmReader; + Assert.AreEqual(42, strmReader.ReadInt()); + strmReader = ctx; + Assert.AreEqual(42, strmReader.ReadInt()); serverDriver.ScheduleUpdate().Complete(); - ctx = default(DataStreamReader.Context); - Assert.Throws(() => { strmReader.ReadInt(ref ctx); }); + strmReader = ctx; + Assert.Throws(() => { strmReader.ReadInt(); }); clientDriver.Dispose(); serverDriver.Dispose(); } struct AcceptJob : IJob { - public LocalNetworkDriver driver; + public NetworkDriver driver; public NativeArray connections; public void Execute() { @@ -97,10 +85,10 @@ public void Execute() [Test] public void AcceptInJobWorks() { - var serverDriver = new LocalNetworkDriver(new NetworkDataStreamParameter {size = 64}); - serverDriver.Bind(IPCManager.Instance.CreateEndPoint()); + var serverDriver = TestNetworkDriver.Create(new NetworkDataStreamParameter {size = 64}); + serverDriver.Bind(NetworkEndPoint.LoopbackIpv4); serverDriver.Listen(); - var clientDriver = new LocalNetworkDriver(new NetworkDataStreamParameter {size = 64}); + var clientDriver = TestNetworkDriver.Create(new NetworkDataStreamParameter {size = 64}); /*var clientToServer =*/ clientDriver.Connect(serverDriver.LocalEndPoint()); clientDriver.ScheduleUpdate().Complete(); @@ -116,7 +104,7 @@ public void AcceptInJobWorks() } struct ReceiveJob : IJob { - public LocalNetworkDriver driver; + public NetworkDriver driver; public NativeArray connections; public NativeArray result; public void Execute() @@ -124,22 +112,21 @@ public void Execute() DataStreamReader strmReader; // Data connections[0].PopEvent(driver, out strmReader); - var ctx = default(DataStreamReader.Context); - result[0] = strmReader.ReadInt(ref ctx); + result[0] = strmReader.ReadInt(); } } [Test] public void ReceiveInJobWorks() { - var serverDriver = new LocalNetworkDriver(new NetworkDataStreamParameter {size = 64}); - serverDriver.Bind(IPCManager.Instance.CreateEndPoint()); + var serverDriver = TestNetworkDriver.Create(new NetworkDataStreamParameter {size = 64}); + serverDriver.Bind(NetworkEndPoint.LoopbackIpv4); serverDriver.Listen(); - var clientDriver = new LocalNetworkDriver(new NetworkDataStreamParameter {size = 64}); + var clientDriver = TestNetworkDriver.Create(new NetworkDataStreamParameter {size = 64}); var clientToServer = clientDriver.Connect(serverDriver.LocalEndPoint()); WaitForConnected(clientDriver, serverDriver, clientToServer); - var strmWriter = new DataStreamWriter(4, Allocator.Temp); - strmWriter.Write(42); - clientToServer.Send(clientDriver, strmWriter); + var strmWriter = clientDriver.BeginSend(clientToServer); + strmWriter.WriteInt(42); + clientDriver.EndSend(strmWriter); clientDriver.ScheduleUpdate().Complete(); var serverToClient = new NativeArray(1, Allocator.TempJob); @@ -158,22 +145,22 @@ public void ReceiveInJobWorks() struct SendJob : IJob { - public LocalNetworkDriver driver; + public NetworkDriver driver; public NetworkConnection connection; public void Execute() { - var strmWriter = new DataStreamWriter(4, Allocator.Temp); - strmWriter.Write(42); - connection.Send(driver, strmWriter); + var strmWriter = driver.BeginSend(connection); + strmWriter.WriteInt(42); + driver.EndSend(strmWriter); } } [Test] public void SendInJobWorks() { - var serverDriver = new LocalNetworkDriver(new NetworkDataStreamParameter {size = 64}); - serverDriver.Bind(IPCManager.Instance.CreateEndPoint()); + var serverDriver = TestNetworkDriver.Create(new NetworkDataStreamParameter {size = 64}); + serverDriver.Bind(NetworkEndPoint.LoopbackIpv4); serverDriver.Listen(); - var clientDriver = new LocalNetworkDriver(new NetworkDataStreamParameter {size = 64}); + var clientDriver = TestNetworkDriver.Create(new NetworkDataStreamParameter {size = 64}); var clientToServer = clientDriver.Connect(serverDriver.LocalEndPoint()); WaitForConnected(clientDriver, serverDriver, clientToServer); var sendJob = new SendJob {driver = clientDriver, connection = clientToServer}; @@ -182,14 +169,13 @@ public void SendInJobWorks() serverDriver.ScheduleUpdate().Complete(); DataStreamReader strmReader; Assert.AreEqual(NetworkEvent.Type.Data, serverToClient.PopEvent(serverDriver, out strmReader)); - var ctx = default(DataStreamReader.Context); - Assert.AreEqual(42, strmReader.ReadInt(ref ctx)); + Assert.AreEqual(42, strmReader.ReadInt()); clientDriver.Dispose(); serverDriver.Dispose(); } struct SendReceiveParallelJob : IJobParallelFor { - public LocalNetworkDriver.Concurrent driver; + public NetworkDriver.Concurrent driver; public NativeArray connections; public void Execute(int i) { @@ -197,36 +183,37 @@ public void Execute(int i) // Data if (driver.PopEventForConnection(connections[i], out strmReader) != NetworkEvent.Type.Data) throw new InvalidOperationException("Expected data: " + i); - var ctx = default(DataStreamReader.Context); - int result = strmReader.ReadInt(ref ctx); - var strmWriter = new DataStreamWriter(4, Allocator.Temp); - strmWriter.Write(result + 1); - driver.Send(NetworkPipeline.Null, connections[i], strmWriter); + int result = strmReader.ReadInt(); + var strmWriter = driver.BeginSend(connections[i]); + strmWriter.WriteInt(result + 1); + driver.EndSend(strmWriter); } } [Test] public void SendReceiveInParallelJobWorks() { NativeArray serverToClient; - using (var serverDriver = new LocalNetworkDriver(new NetworkDataStreamParameter {size = 64})) - using (var clientDriver0 = new LocalNetworkDriver(new NetworkDataStreamParameter {size = 64})) - using (var clientDriver1 = new LocalNetworkDriver(new NetworkDataStreamParameter {size = 64})) + using (var serverDriver = TestNetworkDriver.Create(new NetworkDataStreamParameter {size = 64})) + using (var clientDriver0 = TestNetworkDriver.Create(new NetworkDataStreamParameter {size = 64})) + using (var clientDriver1 = TestNetworkDriver.Create(new NetworkDataStreamParameter {size = 64})) using (serverToClient = new NativeArray(2, Allocator.Persistent)) { - serverDriver.Bind(IPCManager.Instance.CreateEndPoint()); + serverDriver.Bind(NetworkEndPoint.LoopbackIpv4); serverDriver.Listen(); - var strmWriter = new DataStreamWriter(4, Allocator.Temp); - strmWriter.Write(42); var clientToServer0 = clientDriver0.Connect(serverDriver.LocalEndPoint()); var clientToServer1 = clientDriver1.Connect(serverDriver.LocalEndPoint()); WaitForConnected(clientDriver0, serverDriver, clientToServer0); + var strmWriter = clientDriver0.BeginSend(clientToServer0); + strmWriter.WriteInt(42); serverToClient[0] = serverDriver.Accept(); Assert.IsTrue(serverToClient[0].IsCreated); WaitForConnected(clientDriver1, serverDriver, clientToServer1); serverToClient[1] = serverDriver.Accept(); Assert.IsTrue(serverToClient[1].IsCreated); - clientToServer0.Send(clientDriver0, strmWriter); - clientToServer1.Send(clientDriver1, strmWriter); + clientDriver0.EndSend(strmWriter); + var strmWriter2 = clientDriver1.BeginSend(clientToServer1); + strmWriter2.WriteBytes(strmWriter.AsNativeArray()); + clientDriver1.EndSend(strmWriter2); clientDriver0.ScheduleUpdate().Complete(); clientDriver1.ScheduleUpdate().Complete(); @@ -242,7 +229,7 @@ public void SendReceiveInParallelJobWorks() [BurstCompile/*(CompileSynchronously = true)*/] // FIXME: sync compilation makes tests timeout struct SendReceiveWithPipelineParallelJob : IJobParallelFor { - public LocalNetworkDriver.Concurrent driver; + public NetworkDriver.Concurrent driver; public NativeArray connections; public NetworkPipeline pipeline; public void Execute(int i) @@ -251,11 +238,10 @@ public void Execute(int i) // Data if (driver.PopEventForConnection(connections[i], out strmReader) != NetworkEvent.Type.Data) throw new InvalidOperationException("Expected data: " + i); - var ctx = default(DataStreamReader.Context); - int result = strmReader.ReadInt(ref ctx); - var strmWriter = new DataStreamWriter(4, Allocator.Temp); - strmWriter.Write(result + 1); - driver.Send(pipeline, connections[i], strmWriter); + int result = strmReader.ReadInt(); + var strmWriter = driver.BeginSend(connections[i]); + strmWriter.WriteInt(result + 1); + driver.EndSend(strmWriter); } } [Test] @@ -269,18 +255,16 @@ public void SendReceiveWithPipelineInParallelJobWorks() maxFrameTimeMS = 16 }; NativeArray serverToClient; - using (var serverDriver = new LocalNetworkDriver(new NetworkDataStreamParameter {size = 64}, timeoutParam)) - using (var clientDriver0 = new LocalNetworkDriver(new NetworkDataStreamParameter {size = 64}, timeoutParam)) - using (var clientDriver1 = new LocalNetworkDriver(new NetworkDataStreamParameter {size = 64}, timeoutParam)) + using (var serverDriver = TestNetworkDriver.Create(new NetworkDataStreamParameter {size = 64}, timeoutParam)) + using (var clientDriver0 = TestNetworkDriver.Create(new NetworkDataStreamParameter {size = 64}, timeoutParam)) + using (var clientDriver1 = TestNetworkDriver.Create(new NetworkDataStreamParameter {size = 64}, timeoutParam)) using (serverToClient = new NativeArray(2, Allocator.Persistent)) { var serverPipeline = serverDriver.CreatePipeline(typeof(ReliableSequencedPipelineStage)); - serverDriver.Bind(IPCManager.Instance.CreateEndPoint()); + serverDriver.Bind(NetworkEndPoint.LoopbackIpv4); serverDriver.Listen(); var client0Pipeline = clientDriver0.CreatePipeline(typeof(ReliableSequencedPipelineStage)); var client1Pipeline = clientDriver1.CreatePipeline(typeof(ReliableSequencedPipelineStage)); - var strmWriter = new DataStreamWriter(4, Allocator.Temp); - strmWriter.Write(42); var clientToServer0 = clientDriver0.Connect(serverDriver.LocalEndPoint()); var clientToServer1 = clientDriver1.Connect(serverDriver.LocalEndPoint()); WaitForConnected(clientDriver0, serverDriver, clientToServer0); @@ -289,8 +273,12 @@ public void SendReceiveWithPipelineInParallelJobWorks() WaitForConnected(clientDriver1, serverDriver, clientToServer1); serverToClient[1] = serverDriver.Accept(); Assert.IsTrue(serverToClient[1].IsCreated); - clientToServer0.Send(clientDriver0, client0Pipeline, strmWriter); - clientToServer1.Send(clientDriver1, client1Pipeline, strmWriter); + var strmWriter0 = clientDriver0.BeginSend(clientToServer0); + var strmWriter1 = clientDriver1.BeginSend(clientToServer1); + strmWriter0.WriteInt(42); + strmWriter1.WriteInt(42); + clientDriver0.EndSend(strmWriter0); + clientDriver1.EndSend(strmWriter1); clientDriver0.ScheduleUpdate().Complete(); clientDriver1.ScheduleUpdate().Complete(); @@ -316,24 +304,26 @@ public void ParallelSendReceiveStressTest() maxFrameTimeMS = 16 }; NativeArray serverToClient; - var clientDrivers = new List(); + var clientDrivers = new List(); var clientPipelines = new List(); var clientToServer = new List(); try { for (int i = 0; i < 250; ++i) { - clientDrivers.Add(new LocalNetworkDriver(new NetworkDataStreamParameter {size = 64}, timeoutParam)); + clientDrivers.Add(TestNetworkDriver.Create(new NetworkDataStreamParameter {size = 64}, timeoutParam)); clientPipelines.Add(clientDrivers[i].CreatePipeline(typeof(ReliableSequencedPipelineStage))); } - using (var serverDriver = new LocalNetworkDriver(new NetworkDataStreamParameter {size = 17*clientDrivers.Count}, timeoutParam)) +#if UNITY_TRANSPORT_ENABLE_BASELIB + using (var serverDriver = TestNetworkDriver.Create(new BaselibNetworkParameter {maximumPayloadSize = 64, receiveQueueCapacity = clientDrivers.Count, sendQueueCapacity = clientDrivers.Count }, timeoutParam)) +#else + using (var serverDriver = TestNetworkDriver.Create(new NetworkDataStreamParameter {size = 17*clientDrivers.Count}, timeoutParam)) +#endif using (serverToClient = new NativeArray(clientDrivers.Count, Allocator.Persistent)) { var serverPipeline = serverDriver.CreatePipeline(typeof(ReliableSequencedPipelineStage)); - serverDriver.Bind(IPCManager.Instance.CreateEndPoint()); + serverDriver.Bind(NetworkEndPoint.LoopbackIpv4); serverDriver.Listen(); - var strmWriter = new DataStreamWriter(4, Allocator.Temp); - strmWriter.Write(42); for (var i = 0; i < clientDrivers.Count; ++i) { var drv = clientDrivers[i]; @@ -345,8 +335,10 @@ public void ParallelSendReceiveStressTest() } for (var i = 0; i < clientDrivers.Count; ++i) { - clientToServer[i].Send(clientDrivers[i], clientPipelines[i], strmWriter); - clientDrivers[i].ScheduleUpdate().Complete(); + var strmWriter = clientDrivers[i].BeginSend(clientPipelines[i], clientToServer[i]); + strmWriter.WriteInt(42); + clientDrivers[i].EndSend(strmWriter); + clientDrivers[i].ScheduleFlushSend(default).Complete(); } var sendRecvJob = new SendReceiveWithPipelineParallelJob @@ -365,7 +357,7 @@ public void ParallelSendReceiveStressTest() drv.Dispose(); } } - void AssertDataReceived(LocalNetworkDriver serverDriver, NativeArray serverConnections, LocalNetworkDriver clientDriver, NetworkConnection clientToServerConnection, int assertValue, bool serverResend) + void AssertDataReceived(NetworkDriver serverDriver, NativeArray serverConnections, NetworkDriver clientDriver, NetworkConnection clientToServerConnection, int assertValue, bool serverResend) { DataStreamReader strmReader; clientDriver.ScheduleUpdate().Complete(); @@ -381,15 +373,16 @@ void AssertDataReceived(LocalNetworkDriver serverDriver, NativeArray Receive(NetworkPipelineContext ctx, NativeSlice inboundBuffer, ref bool needsResume, ref bool needsUpdate, ref bool needsSendUpdate) - { - unsafe - { - var headerData = (int*)inboundBuffer.GetUnsafeReadOnlyPtr(); - if (*headerData != 1) - throw new InvalidOperationException("Header data invalid, got " + *headerData); - } - return new NativeSlice(inboundBuffer, 4, inboundBuffer.Length - 4); + static TransportFunctionPointer ReceiveFunctionPointer = new TransportFunctionPointer(Receive); + static TransportFunctionPointer SendFunctionPointer = new TransportFunctionPointer(Send); + static TransportFunctionPointer InitializeConnectionFunctionPointer = new TransportFunctionPointer(InitializeConnection); + public NetworkPipelineStage StaticInitialize(byte* staticInstanceBuffer, int staticInstanceBufferLength, INetworkParameter[] netParams) + { + return new NetworkPipelineStage( + Receive: ReceiveFunctionPointer, + Send: SendFunctionPointer, + InitializeConnection: InitializeConnectionFunctionPointer, + ReceiveCapacity: 0, + SendCapacity: 0, + HeaderCapacity: 4, + SharedStateCapacity: 0 + ); + } + public int StaticSize => 0; + + [BurstCompile] + private static void Receive(ref NetworkPipelineContext ctx, ref InboundRecvBuffer inboundBuffer, ref NetworkPipelineStage.Requests request) + { + var headerData = (int*)inboundBuffer.buffer; + if (*headerData != 1) + throw new InvalidOperationException("Header data invalid, got " + *headerData); + inboundBuffer = inboundBuffer.Slice(4); } - public InboundBufferVec Send(NetworkPipelineContext ctx, InboundBufferVec inboundBuffer, ref bool needsResume, ref bool needsUpdate) + [BurstCompile] + private static void Send(ref NetworkPipelineContext ctx, ref InboundSendBuffer inboundBuffer, ref NetworkPipelineStage.Requests request) { - ctx.header.Write((int) 1); - return inboundBuffer; + ctx.header.WriteInt((int) 1); } - public void InitializeConnection(NativeSlice sendProcessBuffer, NativeSlice recvProcessBuffer, NativeSlice sharedProcessBuffer) + [BurstCompile] + private static void InitializeConnection(byte* staticInstanceBuffer, int staticInstanceBufferLength, + byte* sendProcessBuffer, int sendProcessBufferLength, byte* recvProcessBuffer, int recvProcessBufferLength, + byte* sharedProcessBuffer, int sharedProcessBufferLength) { } - - public int ReceiveCapacity => 0; - public int SendCapacity => 0; - public int HeaderCapacity => 4; - public int SharedStateCapacity { get; } } - public struct TestPipelineStageWithHeaderTwo : INetworkPipelineStage + [BurstCompile] + public unsafe struct TestPipelineStageWithHeaderTwo : INetworkPipelineStage { - public unsafe NativeSlice Receive(NetworkPipelineContext ctx, NativeSlice inboundBuffer, ref bool needsResume, ref bool needsUpdate, ref bool needsSendUpdate) - { - var headerData = (int*)inboundBuffer.GetUnsafeReadOnlyPtr(); + static TransportFunctionPointer ReceiveFunctionPointer = new TransportFunctionPointer(Receive); + static TransportFunctionPointer SendFunctionPointer = new TransportFunctionPointer(Send); + static TransportFunctionPointer InitializeConnectionFunctionPointer = new TransportFunctionPointer(InitializeConnection); + public NetworkPipelineStage StaticInitialize(byte* staticInstanceBuffer, int staticInstanceBufferLength, INetworkParameter[] netParams) + { + return new NetworkPipelineStage( + Receive: ReceiveFunctionPointer, + Send: SendFunctionPointer, + InitializeConnection: InitializeConnectionFunctionPointer, + ReceiveCapacity: 0, + SendCapacity: 0, + HeaderCapacity: 4, + SharedStateCapacity: 0 + ); + } + public int StaticSize => 0; + + [BurstCompile] + private static void Receive(ref NetworkPipelineContext ctx, ref InboundRecvBuffer inboundBuffer, ref NetworkPipelineStage.Requests request) + { + var headerData = (int*)inboundBuffer.buffer; if (*headerData != 2) throw new InvalidOperationException("Header data invalid, got " + *headerData); - return new NativeSlice(inboundBuffer, 4, inboundBuffer.Length - 4); + inboundBuffer = inboundBuffer.Slice(4); } - public InboundBufferVec Send(NetworkPipelineContext ctx, InboundBufferVec inboundBuffer, ref bool needsResume, ref bool needsUpdate) + [BurstCompile] + private static void Send(ref NetworkPipelineContext ctx, ref InboundSendBuffer inboundBuffer, ref NetworkPipelineStage.Requests request) { - ctx.header.Write((int) 2); - return inboundBuffer; + ctx.header.WriteInt((int) 2); } - public void InitializeConnection(NativeSlice sendProcessBuffer, NativeSlice recvProcessBuffer, NativeSlice sharedProcessBuffer) + [BurstCompile] + private static void InitializeConnection(byte* staticInstanceBuffer, int staticInstanceBufferLength, + byte* sendProcessBuffer, int sendProcessBufferLength, byte* recvProcessBuffer, int recvProcessBufferLength, + byte* sharedProcessBuffer, int sharedProcessBufferLength) { } - - public int ReceiveCapacity => 0; - public int SendCapacity => 0; - public int HeaderCapacity => 4; - public int SharedStateCapacity { get; } } - public struct TestEncryptPipelineStage : INetworkPipelineStage + [BurstCompile] + public unsafe struct TestEncryptPipelineStage : INetworkPipelineStage { private const int k_MaxPacketSize = 64; - - public NativeSlice Receive(NetworkPipelineContext ctx, NativeSlice inboundBuffer, ref bool needsResume, ref bool needsUpdate, ref bool needsSendUpdate) + static TransportFunctionPointer ReceiveFunctionPointer = new TransportFunctionPointer(Receive); + static TransportFunctionPointer SendFunctionPointer = new TransportFunctionPointer(Send); + static TransportFunctionPointer InitializeConnectionFunctionPointer = new TransportFunctionPointer(InitializeConnection); + public NetworkPipelineStage StaticInitialize(byte* staticInstanceBuffer, int staticInstanceBufferLength, INetworkParameter[] netParams) { - for (int i = 0; i < inboundBuffer.Length; ++i) - ctx.internalProcessBuffer[i] = (byte)(inboundBuffer[i] ^ 0xff); - return new NativeSlice(ctx.internalProcessBuffer, 0, inboundBuffer.Length); + return new NetworkPipelineStage( + Receive: ReceiveFunctionPointer, + Send: SendFunctionPointer, + InitializeConnection: InitializeConnectionFunctionPointer, + ReceiveCapacity: k_MaxPacketSize, + SendCapacity: k_MaxPacketSize, + HeaderCapacity: 0, + SharedStateCapacity: 0 + ); } + public int StaticSize => 0; - public InboundBufferVec Send(NetworkPipelineContext ctx, InboundBufferVec inboundBuffer, ref bool needsResume, ref bool needsUpdate) + [BurstCompile] + private static void Receive(ref NetworkPipelineContext ctx, ref InboundRecvBuffer inboundBuffer, ref NetworkPipelineStage.Requests request) { - var len1 = inboundBuffer.buffer1.Length; - var len2 = inboundBuffer.buffer2.Length; - for (int i = 0; i < len1; ++i) - ctx.internalProcessBuffer[i] = (byte)(inboundBuffer.buffer1[i] ^ 0xff); - for (int i = 0; i < len2; ++i) - ctx.internalProcessBuffer[len1+i] = (byte)(inboundBuffer.buffer2[i] ^ 0xff); - var nextInbound = default(InboundBufferVec); - nextInbound.buffer1 = new NativeSlice(ctx.internalProcessBuffer, 0, len1+len2); - return nextInbound; + for (int i = 0; i < inboundBuffer.bufferLength; ++i) + ctx.internalProcessBuffer[i] = (byte)(inboundBuffer.buffer[i] ^ 0xff); + inboundBuffer.buffer = ctx.internalProcessBuffer; } - public void InitializeConnection(NativeSlice sendProcessBuffer, NativeSlice recvProcessBuffer, NativeSlice sharedProcessBuffer) + [BurstCompile] + private static void Send(ref NetworkPipelineContext ctx, ref InboundSendBuffer inboundBuffer, ref NetworkPipelineStage.Requests request) { + var len = inboundBuffer.bufferLength; + for (int i = 0; i < len; ++i) + ctx.internalProcessBuffer[inboundBuffer.headerPadding + i] = (byte)(inboundBuffer.buffer[i] ^ 0xff); + var nextInbound = default(InboundSendBuffer); + nextInbound.bufferWithHeaders = ctx.internalProcessBuffer; + nextInbound.bufferWithHeadersLength = len + inboundBuffer.headerPadding; + nextInbound.SetBufferFrombufferWithHeaders(); + inboundBuffer = nextInbound; } - public int ReceiveCapacity => k_MaxPacketSize; - public int SendCapacity => k_MaxPacketSize; - public int HeaderCapacity => 0; - public int SharedStateCapacity { get; } + [BurstCompile] + private static void InitializeConnection(byte* staticInstanceBuffer, int staticInstanceBufferLength, + byte* sendProcessBuffer, int sendProcessBufferLength, byte* recvProcessBuffer, int recvProcessBufferLength, + byte* sharedProcessBuffer, int sharedProcessBufferLength) + { + } } - public struct TestEncryptInPlacePipelineStage : INetworkPipelineStage + [BurstCompile] + public unsafe struct TestEncryptInPlacePipelineStage : INetworkPipelineStage { - public NativeSlice Receive(NetworkPipelineContext ctx, NativeSlice inboundBuffer, ref bool needsResume, ref bool needsUpdate, ref bool needsSendUpdate) + static TransportFunctionPointer ReceiveFunctionPointer = new TransportFunctionPointer(Receive); + static TransportFunctionPointer SendFunctionPointer = new TransportFunctionPointer(Send); + static TransportFunctionPointer InitializeConnectionFunctionPointer = new TransportFunctionPointer(InitializeConnection); + public NetworkPipelineStage StaticInitialize(byte* staticInstanceBuffer, int staticInstanceBufferLength, INetworkParameter[] netParams) { - for (int i = 0; i < inboundBuffer.Length; ++i) - inboundBuffer[i] = (byte)(inboundBuffer[i] ^ 0xff); - return inboundBuffer; + return new NetworkPipelineStage( + Receive: ReceiveFunctionPointer, + Send: SendFunctionPointer, + InitializeConnection: InitializeConnectionFunctionPointer, + ReceiveCapacity: 0, + SendCapacity: NetworkParameterConstants.MTU, + HeaderCapacity: 0, + SharedStateCapacity: 0 + ); } + public int StaticSize => 0; - public InboundBufferVec Send(NetworkPipelineContext ctx, InboundBufferVec inboundBuffer, ref bool needsResume, ref bool needsUpdate) + [BurstCompile] + private static void Receive(ref NetworkPipelineContext ctx, ref InboundRecvBuffer inboundBuffer, ref NetworkPipelineStage.Requests request) { - var len1 = inboundBuffer.buffer1.Length; - var len2 = inboundBuffer.buffer2.Length; - for (int i = 0; i < len1; ++i) - ctx.internalProcessBuffer[i] = (byte)(inboundBuffer.buffer1[i] ^ 0xff); - for (int i = 0; i < len2; ++i) - ctx.internalProcessBuffer[len1+i] = (byte)(inboundBuffer.buffer2[i] ^ 0xff); - var nextInbound = default(InboundBufferVec); - nextInbound.buffer1 = new NativeSlice(ctx.internalProcessBuffer, 0, len1+len2); - return nextInbound; + for (int i = 0; i < inboundBuffer.bufferLength; ++i) + inboundBuffer.buffer[i] = (byte)(inboundBuffer.buffer[i] ^ 0xff); } - public void InitializeConnection(NativeSlice sendProcessBuffer, NativeSlice recvProcessBuffer, NativeSlice sharedProcessBuffer) + [BurstCompile] + private static void Send(ref NetworkPipelineContext ctx, ref InboundSendBuffer inboundBuffer, ref NetworkPipelineStage.Requests request) { + var len = inboundBuffer.bufferLength; + for (int i = 0; i < len; ++i) + ctx.internalProcessBuffer[inboundBuffer.headerPadding + i] = (byte)(inboundBuffer.buffer[i] ^ 0xff); + var nextInbound = default(InboundSendBuffer); + nextInbound.bufferWithHeaders = ctx.internalProcessBuffer; + nextInbound.bufferWithHeadersLength = len + inboundBuffer.headerPadding; + nextInbound.SetBufferFrombufferWithHeaders(); + inboundBuffer = nextInbound; } - public int ReceiveCapacity => 0; - public int SendCapacity => NetworkParameterConstants.MTU; - public int HeaderCapacity => 0; - public int SharedStateCapacity { get; } + [BurstCompile] + private static void InitializeConnection(byte* staticInstanceBuffer, int staticInstanceBufferLength, + byte* sendProcessBuffer, int sendProcessBufferLength, byte* recvProcessBuffer, int recvProcessBufferLength, + byte* sharedProcessBuffer, int sharedProcessBufferLength) + { + } } - public struct TestInvertPipelineStage : INetworkPipelineStage + [BurstCompile] + public unsafe struct TestInvertPipelineStage : INetworkPipelineStage { - public NativeSlice Receive(NetworkPipelineContext ctx, NativeSlice inboundBuffer, ref bool needsResume, ref bool needsUpdate, ref bool needsSendUpdate) + static TransportFunctionPointer ReceiveFunctionPointer = new TransportFunctionPointer(Receive); + static TransportFunctionPointer SendFunctionPointer = new TransportFunctionPointer(Send); + static TransportFunctionPointer InitializeConnectionFunctionPointer = new TransportFunctionPointer(InitializeConnection); + public NetworkPipelineStage StaticInitialize(byte* staticInstanceBuffer, int staticInstanceBufferLength, INetworkParameter[] netParams) { - return inboundBuffer; + return new NetworkPipelineStage( + Receive: ReceiveFunctionPointer, + Send: SendFunctionPointer, + InitializeConnection: InitializeConnectionFunctionPointer, + ReceiveCapacity: 0, + SendCapacity: NetworkParameterConstants.MTU, + HeaderCapacity: 0, + SharedStateCapacity: 0 + ); } + public int StaticSize => 0; - public unsafe InboundBufferVec Send(NetworkPipelineContext ctx, InboundBufferVec inboundBuffer, ref bool needsResume, ref bool needsUpdate) + [BurstCompile] + private static void Receive(ref NetworkPipelineContext ctx, ref InboundRecvBuffer inboundBuffer, ref NetworkPipelineStage.Requests request) { - var len1 = inboundBuffer.buffer1.Length; - var len2 = inboundBuffer.buffer2.Length; - for (int i = 0; i < len1; ++i) - ctx.internalProcessBuffer[i] = (byte)(inboundBuffer.buffer1[i] ^ 0xff); - for (int i = 0; i < len2; ++i) - ctx.internalProcessBuffer[len1+i] = (byte)(inboundBuffer.buffer2[i] ^ 0xff); - var nextInbound = default(InboundBufferVec); - nextInbound.buffer1 = new NativeSlice(ctx.internalProcessBuffer, 0, len1+len2); - return nextInbound; } - public void InitializeConnection(NativeSlice sendProcessBuffer, NativeSlice recvProcessBuffer, NativeSlice sharedProcessBuffer) + [BurstCompile] + private static unsafe void Send(ref NetworkPipelineContext ctx, ref InboundSendBuffer inboundBuffer, ref NetworkPipelineStage.Requests request) { + var len = inboundBuffer.bufferLength; + for (int i = 0; i < len; ++i) + ctx.internalProcessBuffer[inboundBuffer.headerPadding + i] = (byte)(inboundBuffer.buffer[i] ^ 0xff); + var nextInbound = default(InboundSendBuffer); + nextInbound.bufferWithHeaders = ctx.internalProcessBuffer; + nextInbound.bufferWithHeadersLength = len + inboundBuffer.headerPadding; + nextInbound.SetBufferFrombufferWithHeaders(); + inboundBuffer = nextInbound; } - public int ReceiveCapacity => 0; - public int SendCapacity => NetworkParameterConstants.MTU; - public int HeaderCapacity => 0; - public int SharedStateCapacity { get; } + [BurstCompile] + private static void InitializeConnection(byte* staticInstanceBuffer, int staticInstanceBufferLength, + byte* sendProcessBuffer, int sendProcessBufferLength, byte* recvProcessBuffer, int recvProcessBufferLength, + byte* sharedProcessBuffer, int sharedProcessBufferLength) + { + } } + [BurstCompile] public unsafe struct TestPipelineWithInitializers : INetworkPipelineStage { - public NativeSlice Receive(NetworkPipelineContext ctx, NativeSlice inboundBuffer, ref bool needsResume, ref bool needsUpdate, ref bool needsSendUpdate) - { - var receiveData = (int*)ctx.internalProcessBuffer.GetUnsafePtr(); + static TransportFunctionPointer ReceiveFunctionPointer = new TransportFunctionPointer(Receive); + static TransportFunctionPointer SendFunctionPointer = new TransportFunctionPointer(Send); + static TransportFunctionPointer InitializeConnectionFunctionPointer = new TransportFunctionPointer(InitializeConnection); + public NetworkPipelineStage StaticInitialize(byte* staticInstanceBuffer, int staticInstanceBufferLength, INetworkParameter[] netParams) + { + return new NetworkPipelineStage( + Receive: ReceiveFunctionPointer, + Send: SendFunctionPointer, + InitializeConnection: InitializeConnectionFunctionPointer, + ReceiveCapacity: 3*UnsafeUtility.SizeOf(), + SendCapacity: 3*UnsafeUtility.SizeOf(), + HeaderCapacity: 0, + SharedStateCapacity: 3*UnsafeUtility.SizeOf() + ); + } + public int StaticSize => 0; + + [BurstCompile] + private static void Receive(ref NetworkPipelineContext ctx, ref InboundRecvBuffer inboundBuffer, ref NetworkPipelineStage.Requests request) + { + var receiveData = (int*)ctx.internalProcessBuffer; for (int i = 4; i <= 6; ++i) { Assert.AreEqual(*receiveData, i); receiveData++; } - var sharedData = (int*)ctx.internalSharedProcessBuffer.GetUnsafePtr(); + var sharedData = (int*)ctx.internalSharedProcessBuffer; for (int i = 7; i <= 8; ++i) { Assert.AreEqual(*sharedData, i); sharedData++; } - return inboundBuffer; } - public unsafe InboundBufferVec Send(NetworkPipelineContext ctx, InboundBufferVec inboundBuffer, ref bool needsResume, ref bool needsUpdate) + [BurstCompile] + private static void Send(ref NetworkPipelineContext ctx, ref InboundSendBuffer inboundBuffer, ref NetworkPipelineStage.Requests request) { - var sendData = (int*)ctx.internalProcessBuffer.GetUnsafePtr(); + var sendData = (int*)ctx.internalProcessBuffer; for (int i = 1; i <= 3; ++i) { Assert.AreEqual(*sendData, i); sendData++; } - var sharedData = (int*)ctx.internalSharedProcessBuffer.GetUnsafePtr(); + var sharedData = (int*)ctx.internalSharedProcessBuffer; for (int i = 7; i <= 8; ++i) { Assert.AreEqual(*sharedData, i); sharedData++; } - return inboundBuffer; } - public unsafe void InitializeConnection(NativeSlice sendProcessBuffer, NativeSlice recvProcessBuffer, NativeSlice sharedProcessBuffer) + [BurstCompile] + private static void InitializeConnection(byte* staticInstanceBuffer, int staticInstanceBufferLength, + byte* sendProcessBuffer, int sendProcessBufferLength, byte* recvProcessBuffer, int recvProcessBufferLength, + byte* sharedProcessBuffer, int sharedProcessBufferLength) { - var sendData = (int*)sendProcessBuffer.GetUnsafePtr(); + var sendData = (int*)sendProcessBuffer; *sendData = 1; sendData++; *sendData = 2; sendData++; *sendData = 3; - var receiveData = (int*)recvProcessBuffer.GetUnsafePtr(); + var receiveData = (int*)recvProcessBuffer; *receiveData = 4; receiveData++; *receiveData = 5; receiveData++; *receiveData = 6; - var sharedData = (int*) sharedProcessBuffer.GetUnsafePtr(); + var sharedData = (int*) sharedProcessBuffer; *sharedData = 7; sharedData++; *sharedData = 8; sharedData++; *sharedData = 9; } - - public int ReceiveCapacity => 3*sizeof(int); - public int SendCapacity => 3*sizeof(int); - public int HeaderCapacity => 0; - public int SharedStateCapacity => 3*sizeof(int); } + [BurstCompile] public unsafe struct TestPipelineWithInitializersTwo : INetworkPipelineStage { - public NativeSlice Receive(NetworkPipelineContext ctx, NativeSlice inboundBuffer, ref bool needsResume, ref bool needsUpdate, ref bool needsSendUpdate) - { - var receiveData = (int*)ctx.internalProcessBuffer.GetUnsafePtr(); + static TransportFunctionPointer ReceiveFunctionPointer = new TransportFunctionPointer(Receive); + static TransportFunctionPointer SendFunctionPointer = new TransportFunctionPointer(Send); + static TransportFunctionPointer InitializeConnectionFunctionPointer = new TransportFunctionPointer(InitializeConnection); + public NetworkPipelineStage StaticInitialize(byte* staticInstanceBuffer, int staticInstanceBufferLength, INetworkParameter[] netParams) + { + return new NetworkPipelineStage( + Receive: ReceiveFunctionPointer, + Send: SendFunctionPointer, + InitializeConnection: InitializeConnectionFunctionPointer, + ReceiveCapacity: 3*UnsafeUtility.SizeOf(), + SendCapacity: 3*UnsafeUtility.SizeOf(), + HeaderCapacity: 0, + SharedStateCapacity: 3*UnsafeUtility.SizeOf() + ); + } + public int StaticSize => 0; + + [BurstCompile] + private static void Receive(ref NetworkPipelineContext ctx, ref InboundRecvBuffer inboundBuffer, ref NetworkPipelineStage.Requests request) + { + var receiveData = (int*)ctx.internalProcessBuffer; for (int i = 4; i <= 6; ++i) { Assert.AreEqual(*receiveData, i*10); receiveData++; } - var sharedData = (int*)ctx.internalSharedProcessBuffer.GetUnsafePtr(); + var sharedData = (int*)ctx.internalSharedProcessBuffer; for (int i = 7; i <= 8; ++i) { Assert.AreEqual(*sharedData, i*10); sharedData++; } - return inboundBuffer; } - public unsafe InboundBufferVec Send(NetworkPipelineContext ctx, InboundBufferVec inboundBuffer, ref bool needsResume, ref bool needsUpdate) + [BurstCompile] + private static void Send(ref NetworkPipelineContext ctx, ref InboundSendBuffer inboundBuffer, ref NetworkPipelineStage.Requests request) { - var sendData = (int*)ctx.internalProcessBuffer.GetUnsafePtr(); + var sendData = (int*)ctx.internalProcessBuffer; for (int i = 1; i <= 3; ++i) { Assert.AreEqual(*sendData, i*10); sendData++; } - var sharedData = (int*)ctx.internalSharedProcessBuffer.GetUnsafePtr(); + var sharedData = (int*)ctx.internalSharedProcessBuffer; for (int i = 7; i <= 8; ++i) { Assert.AreEqual(*sharedData, i*10); sharedData++; } - return inboundBuffer; } - public unsafe void InitializeConnection(NativeSlice sendProcessBuffer, NativeSlice recvProcessBuffer, NativeSlice sharedProcessBuffer) + [BurstCompile] + private static void InitializeConnection(byte* staticInstanceBuffer, int staticInstanceBufferLength, + byte* sendProcessBuffer, int sendProcessBufferLength, byte* recvProcessBuffer, int recvProcessBufferLength, + byte* sharedProcessBuffer, int sharedProcessBufferLength) { - var sendData = (int*)sendProcessBuffer.GetUnsafePtr(); + var sendData = (int*)sendProcessBuffer; *sendData = 10; sendData++; *sendData = 20; sendData++; *sendData = 30; - var receiveData = (int*)recvProcessBuffer.GetUnsafePtr(); + var receiveData = (int*)recvProcessBuffer; *receiveData = 40; receiveData++; *receiveData = 50; receiveData++; *receiveData = 60; - var sharedData = (int*) sharedProcessBuffer.GetUnsafePtr(); + var sharedData = (int*) sharedProcessBuffer; *sharedData = 70; sharedData++; *sharedData = 80; sharedData++; *sharedData = 90; } - - public int ReceiveCapacity => 3*sizeof(int); - public int SendCapacity => 3*sizeof(int); - public int HeaderCapacity => 0; - public int SharedStateCapacity => 3*sizeof(int); } - public struct TestNetworkPipelineStageCollection : INetworkPipelineStageCollection + public struct TestNetworkPipelineStageCollection { - private TestPipelineStageWithHeader testPipelineStageWithHeader; - private TestPipelineStageWithHeaderTwo testPipelineStageWithHeaderTwo; - private TestEncryptPipelineStage testEncryptPipelineStage; - private TestEncryptInPlacePipelineStage testEncryptInPlacePipelineStage; - private TestInvertPipelineStage testInvertPipelineStage; - private TestPipelineWithInitializers testPipelineWithInitializers; - private TestPipelineWithInitializersTwo testPipelineWithInitializersTwo; - private SimulatorPipelineStage testDelayedReadPipelineStage; - private SimulatorPipelineStageInSend testDelayedSendPipelineStage; - private UnreliableSequencedPipelineStage testUnreliableSequencedPipelineStage; - - public int GetStageId(Type type) - { - if (type == typeof(TestPipelineStageWithHeader)) - return 0; - if (type == typeof(TestEncryptPipelineStage)) - return 1; - if (type == typeof(TestEncryptInPlacePipelineStage)) - return 2; - if (type == typeof(TestInvertPipelineStage)) - return 3; - if (type == typeof(SimulatorPipelineStage)) - return 4; - if (type == typeof(SimulatorPipelineStageInSend)) - return 5; - if (type == typeof(UnreliableSequencedPipelineStage)) - return 6; - if (type == typeof(TestPipelineStageWithHeaderTwo)) - return 7; - if (type == typeof(TestPipelineWithInitializers)) - return 8; - if (type == typeof(TestPipelineWithInitializersTwo)) - return 9; - - return -1; - } - - public void Initialize(params INetworkParameter[] param) - { - for (int i = 0; i < param.Length; ++i) - { - if (param[i] is SimulatorUtility.Parameters) - { - testDelayedReadPipelineStage.Initialize((SimulatorUtility.Parameters)param[i]); - testDelayedSendPipelineStage.Initialize((SimulatorUtility.Parameters)param[i]); - } - } - } - - public void InvokeInitialize(int pipelineStageId, NativeSlice sendProcessBuffer, NativeSlice recvProcessBuffer, - NativeSlice sharedStateBuffer) - { - switch (pipelineStageId) - { - case 4: - testDelayedReadPipelineStage.InitializeConnection(sendProcessBuffer, recvProcessBuffer, sharedStateBuffer); - break; - case 5: - testDelayedSendPipelineStage.InitializeConnection(sendProcessBuffer, recvProcessBuffer, sharedStateBuffer); - break; - case 6: - testUnreliableSequencedPipelineStage.InitializeConnection(sendProcessBuffer, recvProcessBuffer, sharedStateBuffer); - break; - case 8: - testPipelineWithInitializers.InitializeConnection(sendProcessBuffer, recvProcessBuffer, sharedStateBuffer); - break; - case 9: - testPipelineWithInitializersTwo.InitializeConnection(sendProcessBuffer, recvProcessBuffer, sharedStateBuffer); - break; - } - } - - public InboundBufferVec InvokeSend(int pipelineStageId, NetworkPipelineContext ctx, InboundBufferVec inboundBuffer, ref bool needsResume, ref bool needsUpdate) - { - switch (pipelineStageId) - { - case 0: - return testPipelineStageWithHeader.Send(ctx, inboundBuffer, ref needsResume, ref needsUpdate); - case 1: - return testEncryptPipelineStage.Send(ctx, inboundBuffer, ref needsResume, ref needsUpdate); - case 2: - return testEncryptInPlacePipelineStage.Send(ctx, inboundBuffer, ref needsResume, ref needsUpdate); - case 3: - return testInvertPipelineStage.Send(ctx, inboundBuffer, ref needsResume, ref needsUpdate); - case 4: - return testDelayedReadPipelineStage.Send(ctx, inboundBuffer, ref needsResume, ref needsUpdate); - case 5: - return testDelayedSendPipelineStage.Send(ctx, inboundBuffer, ref needsResume, ref needsUpdate); - case 6: - return testUnreliableSequencedPipelineStage.Send(ctx, inboundBuffer, ref needsResume, ref needsUpdate); - case 7: - return testPipelineStageWithHeaderTwo.Send(ctx, inboundBuffer, ref needsResume, ref needsUpdate); - case 8: - return testPipelineWithInitializers.Send(ctx, inboundBuffer, ref needsResume, ref needsUpdate); - case 9: - return testPipelineWithInitializersTwo.Send(ctx, inboundBuffer, ref needsResume, ref needsUpdate); - } - return inboundBuffer; - } - - public NativeSlice InvokeReceive(int pipelineStageId, NetworkPipelineContext ctx, NativeSlice inboundBuffer, ref bool needsResume, ref bool needsUpdate, ref bool needsSendUpdate) - { - switch (pipelineStageId) - { - case 0: - return testPipelineStageWithHeader.Receive(ctx, inboundBuffer, ref needsResume, ref needsUpdate, ref needsSendUpdate); - case 1: - return testEncryptPipelineStage.Receive(ctx, inboundBuffer, ref needsResume, ref needsUpdate, ref needsSendUpdate); - case 2: - return testEncryptInPlacePipelineStage.Receive(ctx, inboundBuffer, ref needsResume, ref needsUpdate, ref needsSendUpdate); - case 3: - return testInvertPipelineStage.Receive(ctx, inboundBuffer, ref needsResume, ref needsUpdate, ref needsSendUpdate); - case 4: - return testDelayedReadPipelineStage.Receive(ctx, inboundBuffer, ref needsResume, ref needsUpdate, ref needsSendUpdate); - case 5: - return testDelayedSendPipelineStage.Receive(ctx, inboundBuffer, ref needsResume, ref needsUpdate, ref needsSendUpdate); - case 6: - return testUnreliableSequencedPipelineStage.Receive(ctx, inboundBuffer, ref needsResume, ref needsUpdate, ref needsSendUpdate); - case 7: - return testPipelineStageWithHeaderTwo.Receive(ctx, inboundBuffer, ref needsResume, ref needsUpdate, ref needsSendUpdate); - case 8: - return testPipelineWithInitializers.Receive(ctx, inboundBuffer, ref needsResume, ref needsUpdate, ref needsSendUpdate); - case 9: - return testPipelineWithInitializersTwo.Receive(ctx, inboundBuffer, ref needsResume, ref needsUpdate, ref needsSendUpdate); - } - return inboundBuffer; - } - - public int GetReceiveCapacity(int pipelineStageId) - { - switch (pipelineStageId) - { - case 0: - return testPipelineStageWithHeader.ReceiveCapacity; - case 1: - return testEncryptPipelineStage.ReceiveCapacity; - case 2: - return testEncryptInPlacePipelineStage.ReceiveCapacity; - case 3: - return testInvertPipelineStage.ReceiveCapacity; - case 4: - return testDelayedReadPipelineStage.ReceiveCapacity; - case 5: - return testDelayedSendPipelineStage.ReceiveCapacity; - case 6: - return testUnreliableSequencedPipelineStage.ReceiveCapacity; - case 7: - return testPipelineStageWithHeaderTwo.ReceiveCapacity; - case 8: - return testPipelineWithInitializers.ReceiveCapacity; - case 9: - return testPipelineWithInitializersTwo.ReceiveCapacity; - } - return 0; - } - - public int GetSendCapacity(int pipelineStageId) - { - switch (pipelineStageId) - { - case 0: - return testPipelineStageWithHeader.SendCapacity; - case 1: - return testEncryptPipelineStage.SendCapacity; - case 2: - return testEncryptInPlacePipelineStage.SendCapacity; - case 3: - return testInvertPipelineStage.SendCapacity; - case 4: - return testDelayedReadPipelineStage.SendCapacity; - case 5: - return testDelayedSendPipelineStage.SendCapacity; - case 6: - return testUnreliableSequencedPipelineStage.SendCapacity; - case 7: - return testPipelineStageWithHeaderTwo.SendCapacity; - case 8: - return testPipelineWithInitializers.SendCapacity; - case 9: - return testPipelineWithInitializersTwo.SendCapacity; - } - return 0; - } - - public int GetHeaderCapacity(int pipelineStageId) - { - switch (pipelineStageId) - { - case 0: - return testPipelineStageWithHeader.HeaderCapacity; - case 1: - return testEncryptPipelineStage.HeaderCapacity; - case 2: - return testEncryptInPlacePipelineStage.HeaderCapacity; - case 3: - return testInvertPipelineStage.HeaderCapacity; - case 4: - return testDelayedReadPipelineStage.HeaderCapacity; - case 5: - return testDelayedSendPipelineStage.HeaderCapacity; - case 6: - return testUnreliableSequencedPipelineStage.HeaderCapacity; - case 7: - return testPipelineStageWithHeaderTwo.HeaderCapacity; - case 8: - return testPipelineWithInitializers.HeaderCapacity; - case 9: - return testPipelineWithInitializersTwo.HeaderCapacity; - } - return 0; - } - - public int GetSharedStateCapacity(int pipelineStageId) + public static void Register() { - switch (pipelineStageId) - { - case 0: - return testPipelineStageWithHeader.SharedStateCapacity; - case 1: - return testEncryptPipelineStage.SharedStateCapacity; - case 2: - return testEncryptInPlacePipelineStage.SharedStateCapacity; - case 3: - return testInvertPipelineStage.SharedStateCapacity; - case 4: - return testDelayedReadPipelineStage.SharedStateCapacity; - case 5: - return testDelayedSendPipelineStage.SharedStateCapacity; - case 6: - return testUnreliableSequencedPipelineStage.SharedStateCapacity; - case 7: - return testPipelineStageWithHeaderTwo.SharedStateCapacity; - case 8: - return testPipelineWithInitializers.SharedStateCapacity; - case 9: - return testPipelineWithInitializersTwo.SharedStateCapacity; - } - return 0; + NetworkPipelineStageCollection.RegisterPipelineStage(new TestPipelineStageWithHeader()); + NetworkPipelineStageCollection.RegisterPipelineStage(new TestPipelineStageWithHeaderTwo()); + NetworkPipelineStageCollection.RegisterPipelineStage(new TestEncryptPipelineStage()); + NetworkPipelineStageCollection.RegisterPipelineStage(new TestEncryptInPlacePipelineStage()); + NetworkPipelineStageCollection.RegisterPipelineStage(new TestInvertPipelineStage()); + NetworkPipelineStageCollection.RegisterPipelineStage(new TestPipelineWithInitializers()); + NetworkPipelineStageCollection.RegisterPipelineStage(new TestPipelineWithInitializersTwo()); } } public class NetworkPipelineTest { - private GenericNetworkDriver m_ServerDriver; - private GenericNetworkDriver m_ClientDriver; - private GenericNetworkDriver m_ClientDriver2; + private NetworkDriver m_ServerDriver; + private NetworkDriver m_ClientDriver; + private NetworkDriver m_ClientDriver2; [SetUp] public void IPC_Setup() { - IPCManager.Instance.Initialize(100); + var timeoutParam = new NetworkConfigParameter + { + connectTimeoutMS = NetworkParameterConstants.ConnectTimeoutMS, + maxConnectAttempts = NetworkParameterConstants.MaxConnectAttempts, + disconnectTimeoutMS = NetworkParameterConstants.DisconnectTimeoutMS, + fixedFrameTimeMS = 16 + }; // NOTE: MaxPacketSize should be 64 for all the tests using simulator except needs to account for header size as well (one test has 2x2B headers) var simulatorParams = new SimulatorUtility.Parameters() - {MaxPacketSize = 68, MaxPacketCount = 30, PacketDelayMs = 100}; - m_ServerDriver = new GenericNetworkDriver(simulatorParams); - m_ServerDriver.Bind(IPCManager.Instance.CreateEndPoint()); + {MaxPacketSize = 72, MaxPacketCount = 30, PacketDelayMs = 100}; + TestNetworkPipelineStageCollection.Register(); + m_ServerDriver = TestNetworkDriver.Create(timeoutParam, simulatorParams); + m_ServerDriver.Bind(NetworkEndPoint.LoopbackIpv4); m_ServerDriver.Listen(); - m_ClientDriver = new GenericNetworkDriver(simulatorParams); - m_ClientDriver2 = new GenericNetworkDriver(simulatorParams); + m_ClientDriver = TestNetworkDriver.Create(timeoutParam, simulatorParams); + m_ClientDriver2 = TestNetworkDriver.Create(timeoutParam, simulatorParams); } [TearDown] @@ -554,7 +442,6 @@ public void IPC_TearDown() m_ClientDriver.Dispose(); m_ClientDriver2.Dispose(); m_ServerDriver.Dispose(); - IPCManager.Instance.Destroy(); } [Test] public void NetworkPipeline_CreatePipelineIsSymetrical() @@ -594,9 +481,9 @@ public void NetworkPipeline_CanExtendHeader() Assert.AreNotEqual(default(NetworkConnection), serverToClient); // Send message to client - var strm = new DataStreamWriter(4, Allocator.Temp); - strm.Write((int) 42); - m_ServerDriver.Send(serverPipe, serverToClient, strm); + var strm = m_ServerDriver.BeginSend(serverPipe, serverToClient); + strm.WriteInt((int) 42); + m_ServerDriver.EndSend(strm); m_ServerDriver.ScheduleUpdate().Complete(); // Receive incoming message from server @@ -605,8 +492,7 @@ public void NetworkPipeline_CanExtendHeader() Assert.AreEqual(NetworkEvent.Type.Connect, clientToServer.PopEvent(m_ClientDriver, out readStrm)); Assert.AreEqual(NetworkEvent.Type.Data, clientToServer.PopEvent(m_ClientDriver, out readStrm)); Assert.AreEqual(4, readStrm.Length); - var readCtx = default(DataStreamReader.Context); - Assert.AreEqual(42, readStrm.ReadInt(ref readCtx)); + Assert.AreEqual(42, readStrm.ReadInt()); } [Test] public void NetworkPipeline_CanModifyAndRestoreData() @@ -627,9 +513,9 @@ public void NetworkPipeline_CanModifyAndRestoreData() Assert.AreNotEqual(default(NetworkConnection), serverToClient); // Send message to client - var strm = new DataStreamWriter(4, Allocator.Temp); - strm.Write((int) 42); - m_ServerDriver.Send(serverPipe, serverToClient, strm); + var strm = m_ServerDriver.BeginSend(serverPipe, serverToClient); + strm.WriteInt((int) 42); + m_ServerDriver.EndSend(strm); m_ServerDriver.ScheduleUpdate().Complete(); // Receive incoming message from server @@ -638,8 +524,7 @@ public void NetworkPipeline_CanModifyAndRestoreData() Assert.AreEqual(NetworkEvent.Type.Connect, clientToServer.PopEvent(m_ClientDriver, out readStrm)); Assert.AreEqual(NetworkEvent.Type.Data, clientToServer.PopEvent(m_ClientDriver, out readStrm)); Assert.AreEqual(4, readStrm.Length); - var readCtx = default(DataStreamReader.Context); - Assert.AreEqual(42, readStrm.ReadInt(ref readCtx)); + Assert.AreEqual(42, readStrm.ReadInt()); } [Test] public void NetworkPipeline_CanModifyAndRestoreDataInPlace() @@ -660,9 +545,9 @@ public void NetworkPipeline_CanModifyAndRestoreDataInPlace() Assert.AreNotEqual(default(NetworkConnection), serverToClient); // Send message to client - var strm = new DataStreamWriter(4, Allocator.Temp); - strm.Write((int) 42); - m_ServerDriver.Send(serverPipe, serverToClient, strm); + var strm = m_ServerDriver.BeginSend(serverPipe, serverToClient); + strm.WriteInt((int) 42); + m_ServerDriver.EndSend(strm); m_ServerDriver.ScheduleUpdate().Complete(); // Receive incoming message from server @@ -671,8 +556,7 @@ public void NetworkPipeline_CanModifyAndRestoreDataInPlace() Assert.AreEqual(NetworkEvent.Type.Connect, clientToServer.PopEvent(m_ClientDriver, out readStrm)); Assert.AreEqual(NetworkEvent.Type.Data, clientToServer.PopEvent(m_ClientDriver, out readStrm)); Assert.AreEqual(4, readStrm.Length); - var readCtx = default(DataStreamReader.Context); - Assert.AreEqual(42, readStrm.ReadInt(ref readCtx)); + Assert.AreEqual(42, readStrm.ReadInt()); } [Test] public void NetworkPipeline_CanModifyData() @@ -693,9 +577,9 @@ public void NetworkPipeline_CanModifyData() Assert.AreNotEqual(default(NetworkConnection), serverToClient); // Send message to client - var strm = new DataStreamWriter(4, Allocator.Temp); - strm.Write((int) 42); - m_ServerDriver.Send(serverPipe, serverToClient, strm); + var strm = m_ServerDriver.BeginSend(serverPipe, serverToClient); + strm.WriteInt((int) 42); + m_ServerDriver.EndSend(strm); m_ServerDriver.ScheduleUpdate().Complete(); // Receive incoming message from server @@ -704,8 +588,7 @@ public void NetworkPipeline_CanModifyData() Assert.AreEqual(NetworkEvent.Type.Connect, clientToServer.PopEvent(m_ClientDriver, out readStrm)); Assert.AreEqual(NetworkEvent.Type.Data, clientToServer.PopEvent(m_ClientDriver, out readStrm)); Assert.AreEqual(4, readStrm.Length); - var readCtx = default(DataStreamReader.Context); - Assert.AreEqual(-1^42, readStrm.ReadInt(ref readCtx)); + Assert.AreEqual(-1^42, readStrm.ReadInt()); } [Test] @@ -726,9 +609,9 @@ public void NetworkPipeline_MultiplePipelinesWork() Assert.AreNotEqual(default(NetworkConnection), serverToClient); // Send message to client - var strm = new DataStreamWriter(4, Allocator.Temp); - strm.Write((int) 42); - m_ServerDriver.Send(serverPipe, serverToClient, strm); + var strm = m_ServerDriver.BeginSend(serverPipe, serverToClient); + strm.WriteInt((int) 42); + m_ServerDriver.EndSend(strm); m_ServerDriver.ScheduleUpdate().Complete(); // Receive incoming message from server @@ -737,8 +620,7 @@ public void NetworkPipeline_MultiplePipelinesWork() Assert.AreEqual(NetworkEvent.Type.Connect, clientToServer.PopEvent(m_ClientDriver, out readStrm)); Assert.AreEqual(NetworkEvent.Type.Data, clientToServer.PopEvent(m_ClientDriver, out readStrm)); Assert.AreEqual(4, readStrm.Length); - var readCtx = default(DataStreamReader.Context); - Assert.AreEqual(42, readStrm.ReadInt(ref readCtx)); + Assert.AreEqual(42, readStrm.ReadInt()); } [Test] @@ -888,7 +770,7 @@ private void TestPipeline(int packetCount, NetworkPipeline serverPipe, int packe m_ClientDriver2.ScheduleUpdate().Complete(); // Driver only updates time in update, so must read start time before update - var startTime = Stopwatch.GetTimestamp() / TimeSpan.TicksPerMillisecond; + var startTime = m_ServerDriver.LastUpdateTime; // Handle incoming connection from client m_ServerDriver.ScheduleUpdate().Complete(); var serverToClient = m_ServerDriver.Accept(); @@ -898,14 +780,17 @@ private void TestPipeline(int packetCount, NetworkPipeline serverPipe, int packe // Send given packetCount number of packets in a row in one update // Write 1's for packet 1, 2's for packet 2 and so on and verify they're received in same order - var strm = new DataStreamWriter(64, Allocator.Temp); for (int i = 0; i < packetCount; i++) { - strm.Clear(); + var strm = m_ServerDriver.BeginSend(serverPipe, serverToClient); + var strm2 = m_ServerDriver.BeginSend(serverPipe, serverToClient2); for (int j = 0; j < 16; j++) - strm.Write((int) i + 1); - m_ServerDriver.Send(serverPipe, serverToClient, strm); - m_ServerDriver.Send(serverPipe, serverToClient2, strm); + { + strm.WriteInt((int) i + 1); + strm2.WriteInt((int) i + 1); + } + m_ServerDriver.EndSend(strm); + m_ServerDriver.EndSend(strm2); } m_ServerDriver.ScheduleUpdate().Complete(); @@ -921,14 +806,14 @@ private void TestPipeline(int packetCount, NetworkPipeline serverPipe, int packe ClientReceivePackets(m_ClientDriver2, packetCount, clientToServer2, startTime, packetDelay); } - private void ClientReceivePackets(GenericNetworkDriver clientDriver, int packetCount, NetworkConnection clientToServer, long startTime, int minDelay) + private void ClientReceivePackets(NetworkDriver clientDriver, int packetCount, NetworkConnection clientToServer, long startTime, int minDelay) { DataStreamReader readStrm; NetworkEvent.Type netEvent; - var abortTimer = new Timer(); + var abortFrame = 0; while (true) { - if (abortTimer.ElapsedMilliseconds > 2000) + if (abortFrame++ > 125) Assert.Fail("Did not receive first delayed packet"); netEvent = clientToServer.PopEvent(clientDriver, out readStrm); if (netEvent == NetworkEvent.Type.Data) @@ -940,14 +825,13 @@ private void ClientReceivePackets(GenericNetworkDriver= minDelay, "Delay too low on packet " + i + ": " + delay); + Assert.GreaterOrEqual(delay, minDelay, $"Delay too low on packet {i}"); Assert.AreEqual(64, readStrm.Length); - var readCtx = default(DataStreamReader.Context); for (int j = 0; j < 16; j++) { - var read = readStrm.ReadInt(ref readCtx); + var read = readStrm.ReadInt(); Assert.AreEqual(i + 1, read); Assert.True(read > 0 && read <= packetCount, "read incorrect value: " + read); } @@ -957,10 +841,10 @@ private void ClientReceivePackets(GenericNetworkDriver 1000) + if (abortFrame++ > 75) Assert.Fail("Didn't receive all delayed packets"); clientDriver.ScheduleUpdate().Complete(); m_ServerDriver.ScheduleUpdate().Complete(); diff --git a/Tests/Editor/ReliablePipelineTests.cs b/Tests/Editor/ReliablePipelineTests.cs index afd04eb..555b4df 100644 --- a/Tests/Editor/ReliablePipelineTests.cs +++ b/Tests/Editor/ReliablePipelineTests.cs @@ -39,15 +39,23 @@ public unsafe void ReliableUtility_ValidationScenarios() var packet = new NativeArray(UnsafeUtility.SizeOf(), Allocator.Persistent); packet[0] = 100; - var header = new DataStreamWriter(UnsafeUtility.SizeOf(), Allocator.Persistent); + var header = new DataStreamWriter(UnsafeUtility.SizeOf(), Allocator.Temp); - ReliableSequencedPipelineStage ep1 = new ReliableSequencedPipelineStage(); - ReliableSequencedPipelineStage ep2 = new ReliableSequencedPipelineStage(); + ReliableSequencedPipelineStage ep1Owner = new ReliableSequencedPipelineStage(); + ReliableSequencedPipelineStage ep2Owner = new ReliableSequencedPipelineStage(); - ep1.Initialize(parameters); - ep2.Initialize(parameters); - ep1.InitializeConnection(ep1SendBuffer, ep1RecvBuffer, ep1SharedBuffer); - ep2.InitializeConnection(ep2SendBuffer, ep2RecvBuffer, ep2SharedBuffer); + var ep1Buffer = new NativeArray(ep1Owner.StaticSize, Allocator.Persistent); + var ep2Buffer = new NativeArray(ep2Owner.StaticSize, Allocator.Persistent); + + var paramList = new INetworkParameter[]{parameters}; + var ep1 = ep1Owner.StaticInitialize((byte*)ep1Buffer.GetUnsafePtr(), ep1Buffer.Length, paramList); + var ep2 = ep1Owner.StaticInitialize((byte*)ep2Buffer.GetUnsafePtr(), ep2Buffer.Length, paramList); + ep1.InitializeConnection.Ptr.Invoke((byte*)ep1Buffer.GetUnsafePtr(), ep1Buffer.Length, + (byte*)ep1SendBuffer.GetUnsafePtr(), ep1SendBuffer.Length, (byte*)ep1RecvBuffer.GetUnsafePtr(), ep1RecvBuffer.Length, + (byte*)ep1SharedBuffer.GetUnsafePtr(), ep1SharedBuffer.Length); + ep2.InitializeConnection.Ptr.Invoke((byte*)ep2Buffer.GetUnsafePtr(), ep2Buffer.Length, + (byte*)ep2SendBuffer.GetUnsafePtr(), ep2SendBuffer.Length, (byte*)ep2RecvBuffer.GetUnsafePtr(), ep2RecvBuffer.Length, + (byte*)ep2SharedBuffer.GetUnsafePtr(), ep2SharedBuffer.Length); var ep1sendContext = (ReliableUtility.Context*) ep1SendBuffer.GetUnsafePtr(); //var ep1recvContext = (ReliableUtility.Context*) ep1RecvBuffer.GetUnsafePtr(); @@ -60,40 +68,54 @@ public unsafe void ReliableUtility_ValidationScenarios() // Send a Packet - Receive a Packet var currentId = 0; - var inboundSend = default(InboundBufferVec); - inboundSend.buffer1 = packet; + var inboundSend = default(InboundSendBuffer); + inboundSend.buffer = (byte*)packet.GetUnsafePtr(); + inboundSend.bufferLength = packet.Length; + inboundSend.bufferWithHeaders = (byte*)packet.GetUnsafePtr(); + inboundSend.bufferWithHeadersLength = packet.Length; - bool needsUpdate = false; - bool needsResume = false; - bool needsSendUpdate = false; - var slice = default(NativeSlice); - var output = default(InboundBufferVec); + NetworkPipelineStage.Requests stageRequest = NetworkPipelineStage.Requests.None; + var slice = default(InboundRecvBuffer); + var output = default(InboundSendBuffer); { - output = ep1.Send(new NetworkPipelineContext + var ctx = new NetworkPipelineContext { header = header, - internalProcessBuffer = ep1SendBuffer, - internalSharedProcessBuffer = ep1SharedBuffer - }, inboundSend, ref needsResume, ref needsUpdate); - Assert.True(output.buffer1[0] == packet[0]); - Assert.True(!needsResume); + internalProcessBuffer = (byte*)ep1SendBuffer.GetUnsafePtr(), + internalProcessBufferLength = ep1SendBuffer.Length, + internalSharedProcessBuffer = (byte*)ep1SharedBuffer.GetUnsafePtr(), + internalSharedProcessBufferLength = ep1SharedBuffer.Length, + staticInstanceBuffer = (byte*)ep1Buffer.GetUnsafePtr(), + staticInstanceBufferLength = ep1Buffer.Length + }; + output = inboundSend; + ep1.Send.Ptr.Invoke(ref ctx, ref output, ref stageRequest); + Assert.True(output.buffer[0] == packet[0]); + Assert.True((stageRequest&NetworkPipelineStage.Requests.Resume)==0); } { - var info = ReliableUtility.GetPacketInformation(ep1SendBuffer, currentId); + var info = ReliableUtility.GetPacketInformation((byte*)ep1SendBuffer.GetUnsafeReadOnlyPtr(), currentId); var offset = ep1sendContext->DataPtrOffset; // + (index * ctx->DataStride); - NativeSlice data = new NativeSlice(ep1SendBuffer, offset, info->Size); - + InboundRecvBuffer data; + data.buffer = (byte*)ep1SendBuffer.GetUnsafeReadOnlyPtr() + offset; + data.bufferLength = info->Size; - slice = ep2.Receive(new NetworkPipelineContext + var ctx = new NetworkPipelineContext { - internalProcessBuffer = ep2RecvBuffer, - internalSharedProcessBuffer = ep2SharedBuffer - }, data, ref needsResume, ref needsUpdate, ref needsSendUpdate); - - if (slice.Length > 0) - Assert.True(slice[0] == packet[0]); + internalProcessBuffer = (byte*)ep2RecvBuffer.GetUnsafePtr(), + internalProcessBufferLength = ep2RecvBuffer.Length, + internalSharedProcessBuffer = (byte*)ep2SharedBuffer.GetUnsafePtr(), + internalSharedProcessBufferLength = ep2SharedBuffer.Length, + staticInstanceBuffer = (byte*)ep2Buffer.GetUnsafePtr(), + staticInstanceBufferLength = ep2Buffer.Length + }; + slice = data; + ep2.Receive.Ptr.Invoke(ref ctx, ref slice, ref stageRequest); + + if (slice.bufferLength > 0) + Assert.True(slice.buffer[0] == packet[0]); } - Assert.True(!needsResume); + Assert.True((stageRequest&NetworkPipelineStage.Requests.Resume)==0); Assert.True(ep2recvContext->Delivered == currentId); // Scenario: Receive a Packet Newer then expected [0, 1, Lost, 3] @@ -104,15 +126,21 @@ public unsafe void ReliableUtility_ValidationScenarios() packet[0] = (byte) (100 + seq); header.Clear(); - output = ep1.Send(new NetworkPipelineContext + var ctx = new NetworkPipelineContext { header = header, - internalProcessBuffer = ep1SendBuffer, - internalSharedProcessBuffer = ep1SharedBuffer - }, inboundSend, ref needsResume, ref needsUpdate); - - Assert.True(!needsResume); - Assert.True(output.buffer1[0] == packet[0]); + internalProcessBuffer = (byte*)ep1SendBuffer.GetUnsafePtr(), + internalProcessBufferLength = ep1SendBuffer.Length, + internalSharedProcessBuffer = (byte*)ep1SharedBuffer.GetUnsafePtr(), + internalSharedProcessBufferLength = ep1SharedBuffer.Length, + staticInstanceBuffer = (byte*)ep1Buffer.GetUnsafePtr(), + staticInstanceBufferLength = ep1Buffer.Length + }; + output = inboundSend; + ep1.Send.Ptr.Invoke(ref ctx, ref output, ref stageRequest); + + Assert.True((stageRequest&NetworkPipelineStage.Requests.Resume)==0); + Assert.True(output.buffer[0] == packet[0]); } for (int seq = currentId + 1; seq < 4; seq++) @@ -120,24 +148,33 @@ public unsafe void ReliableUtility_ValidationScenarios() if (seq == 2) continue; - var info = ReliableUtility.GetPacketInformation(ep1SendBuffer, seq); + var info = ReliableUtility.GetPacketInformation((byte*)ep1SendBuffer.GetUnsafeReadOnlyPtr(), seq); var offset = ep1sendContext->DataPtrOffset + ((seq % ep1sendContext->Capacity) * ep1sendContext->DataStride); - var inspectPacket = ReliableUtility.GetPacket(ep1SendBuffer, seq); + var inspectPacket = ReliableUtility.GetPacket((byte*)ep1SendBuffer.GetUnsafeReadOnlyPtr(), seq); - NativeSlice data = new NativeSlice(ep1SendBuffer, offset, info->Size); + InboundRecvBuffer data; + data.buffer = (byte*)ep1SendBuffer.GetUnsafeReadOnlyPtr() + offset; + data.bufferLength = info->Size; Assert.True(inspectPacket->Header.SequenceId == info->SequenceId); header.Clear(); - slice = ep2.Receive(new NetworkPipelineContext + var ctx = new NetworkPipelineContext { header = header, - internalProcessBuffer = ep2RecvBuffer, - internalSharedProcessBuffer = ep2SharedBuffer - }, data, ref needsResume, ref needsUpdate, ref needsSendUpdate); - - if (slice.Length > 0) + internalProcessBuffer = (byte*)ep2RecvBuffer.GetUnsafePtr(), + internalProcessBufferLength = ep2RecvBuffer.Length, + internalSharedProcessBuffer = (byte*)ep2SharedBuffer.GetUnsafePtr(), + internalSharedProcessBufferLength = ep2SharedBuffer.Length, + staticInstanceBuffer = (byte*)ep2Buffer.GetUnsafePtr(), + staticInstanceBufferLength = ep2Buffer.Length + }; + stageRequest = NetworkPipelineStage.Requests.None; + slice = data; + ep2.Receive.Ptr.Invoke(ref ctx, ref slice, ref stageRequest); + + if (slice.bufferLength > 0) { - Assert.True(slice[0] == seq + 100); + Assert.True(slice.buffer[0] == seq + 100); } } @@ -145,42 +182,48 @@ public unsafe void ReliableUtility_ValidationScenarios() bool first = true; do { - var data = default(NativeSlice); + var data = default(InboundRecvBuffer); if (first) { var seq = 2; - var info = ReliableUtility.GetPacketInformation(ep1SendBuffer, seq); + var info = ReliableUtility.GetPacketInformation((byte*)ep1SendBuffer.GetUnsafeReadOnlyPtr(), seq); var offset = ep1sendContext->DataPtrOffset + ((seq % ep1sendContext->Capacity) * ep1sendContext->DataStride); - var inspectPacket = ReliableUtility.GetPacket(ep1SendBuffer, seq); + var inspectPacket = ReliableUtility.GetPacket((byte*)ep1SendBuffer.GetUnsafeReadOnlyPtr(), seq); - data = new NativeSlice(ep1SendBuffer, offset, info->Size); + data.buffer = (byte*)ep1SendBuffer.GetUnsafeReadOnlyPtr() + offset; + data.bufferLength = info->Size; Assert.True(inspectPacket->Header.SequenceId == info->SequenceId); first = false; } - slice = ep2.Receive(new NetworkPipelineContext + var ctx = new NetworkPipelineContext { - internalProcessBuffer = ep2RecvBuffer, - internalSharedProcessBuffer = ep2SharedBuffer - }, data, ref needsResume, ref needsUpdate, ref needsSendUpdate); - - if (slice.Length > 0) + internalProcessBuffer = (byte*)ep2RecvBuffer.GetUnsafeReadOnlyPtr(), + internalProcessBufferLength = ep2RecvBuffer.Length, + internalSharedProcessBuffer = (byte*)ep2SharedBuffer.GetUnsafeReadOnlyPtr(), + internalSharedProcessBufferLength = ep2SharedBuffer.Length + }; + slice = data; + ep2.Receive.Ptr.Invoke(ref ctx, ref slice, ref stageRequest); + + if (slice.bufferLength > 0) { - Assert.True(slice[0] == ep2recvContext->Delivered + 100); + Assert.True(slice.buffer[0] == ep2recvContext->Delivered + 100); } - } while (needsResume); + } while ((stageRequest&NetworkPipelineStage.Requests.Resume)!=0); packet.Dispose(); - header.Dispose(); ep1SharedBuffer.Dispose(); ep1SendBuffer.Dispose(); ep1RecvBuffer.Dispose(); ep2SharedBuffer.Dispose(); ep2SendBuffer.Dispose(); ep2RecvBuffer.Dispose(); + ep1Buffer.Dispose(); + ep2Buffer.Dispose(); } @@ -197,38 +240,40 @@ public unsafe void ReliableUtility_Validation() int result = ReliableUtility.ProcessCapacityNeeded(parameters); NativeArray processBuffer = new NativeArray(result, Allocator.Persistent); - ReliableUtility.InitializeProcessContext(processBuffer, parameters); + var processBufferPtr = (byte*)processBuffer.GetUnsafePtr(); - Assert.IsTrue(ReliableUtility.TryAquire(processBuffer, 0)); - Assert.IsTrue(ReliableUtility.TryAquire(processBuffer, 1)); - Assert.IsTrue(ReliableUtility.TryAquire(processBuffer, 2)); - Assert.IsTrue(ReliableUtility.TryAquire(processBuffer, 3)); - Assert.IsTrue(ReliableUtility.TryAquire(processBuffer, 4)); - Assert.IsFalse(ReliableUtility.TryAquire(processBuffer, 5)); + ReliableUtility.InitializeProcessContext(processBufferPtr, processBuffer.Length, parameters); - ReliableUtility.Release(processBuffer, 0, 5); + Assert.IsTrue(ReliableUtility.TryAquire(processBufferPtr, 0)); + Assert.IsTrue(ReliableUtility.TryAquire(processBufferPtr, 1)); + Assert.IsTrue(ReliableUtility.TryAquire(processBufferPtr, 2)); + Assert.IsTrue(ReliableUtility.TryAquire(processBufferPtr, 3)); + Assert.IsTrue(ReliableUtility.TryAquire(processBufferPtr, 4)); + Assert.IsFalse(ReliableUtility.TryAquire(processBufferPtr, 5)); - Assert.IsTrue(ReliableUtility.TryAquire(processBuffer, 0)); - Assert.IsTrue(ReliableUtility.TryAquire(processBuffer, 1)); - Assert.IsTrue(ReliableUtility.TryAquire(processBuffer, 2)); - Assert.IsTrue(ReliableUtility.TryAquire(processBuffer, 3)); - Assert.IsTrue(ReliableUtility.TryAquire(processBuffer, 4)); + ReliableUtility.Release(processBufferPtr, 0, 5); + + Assert.IsTrue(ReliableUtility.TryAquire(processBufferPtr, 0)); + Assert.IsTrue(ReliableUtility.TryAquire(processBufferPtr, 1)); + Assert.IsTrue(ReliableUtility.TryAquire(processBufferPtr, 2)); + Assert.IsTrue(ReliableUtility.TryAquire(processBufferPtr, 3)); + Assert.IsTrue(ReliableUtility.TryAquire(processBufferPtr, 4)); buffer[0] = (byte)(1); - ReliableUtility.SetPacket(processBuffer, 0, buffer); + ReliableUtility.SetPacket(processBufferPtr, 0, (byte*)buffer.GetUnsafeReadOnlyPtr(), buffer.Length); - var slice = ReliableUtility.GetPacket(processBuffer, 0); + var slice = ReliableUtility.GetPacket(processBufferPtr, 0); Assert.IsTrue(slice->Buffer[0] == buffer[0]); for (int i = 0; i < capacity * 5; i++) { - ReliableUtility.SetPacket(processBuffer, i, buffer); - slice = ReliableUtility.GetPacket(processBuffer, i); + ReliableUtility.SetPacket(processBufferPtr, i, (byte*)buffer.GetUnsafeReadOnlyPtr(), buffer.Length); + slice = ReliableUtility.GetPacket(processBufferPtr, i); Assert.IsTrue(slice->Buffer[0] == buffer[0]); } - ReliableUtility.Release(processBuffer, 0, 5); + ReliableUtility.Release(processBufferPtr, 0, 5); processBuffer.Dispose(); buffer.Dispose(); @@ -248,10 +293,14 @@ public unsafe void ReliableUtility_AckPackets_SeqIdBeginAt0() NativeArray sendBuffer = new NativeArray(processCapacity, Allocator.Persistent); NativeArray sharedBuffer = new NativeArray(sharedCapacity, Allocator.Persistent); - ReliableUtility.InitializeContext(sharedBuffer, sendBuffer, recvBuffer, parameters); + var sendBufferPtr = (byte*)sendBuffer.GetUnsafePtr(); + + ReliableUtility.InitializeContext((byte*)sharedBuffer.GetUnsafePtr(), sharedBuffer.Length, + sendBufferPtr, sendBuffer.Length, (byte*)recvBuffer.GetUnsafePtr(), recvBuffer.Length, parameters); var pipelineContext = new NetworkPipelineContext - {internalProcessBuffer = sendBuffer, internalSharedProcessBuffer = sharedBuffer, timestamp = 1000}; + {internalProcessBuffer = sendBufferPtr, internalProcessBufferLength = sendBuffer.Length, + internalSharedProcessBuffer = (byte*)sharedBuffer.GetUnsafePtr(), internalSharedProcessBufferLength = sharedBuffer.Length, timestamp = 1000}; // Sending seqId 3, last received ID 0 (1 is not yet acked, 2 was dropped) var sharedContext = (ReliableUtility.SharedContext*) sharedBuffer.GetUnsafePtr(); @@ -263,26 +312,26 @@ public unsafe void ReliableUtility_AckPackets_SeqIdBeginAt0() var receiveContext = (ReliableUtility.Context*) recvBuffer.GetUnsafePtr(); receiveContext->Delivered = sharedContext->SentPackets.Acked; - using (var stream = new DataStreamWriter(4, Allocator.Temp)) + var stream = new DataStreamWriter(4, Allocator.Temp); { // Add buffers in resend queue - stream.Write((int) 10); - ReliableUtility.SetPacket(sendBuffer, 65535, stream.GetNativeSlice(0, stream.Length)); - ReliableUtility.GetPacketInformation(sendBuffer, 65535)->SendTime = 980; + stream.WriteInt((int) 10); + ReliableUtility.SetPacket(sendBufferPtr, 65535, (byte*)stream.AsNativeArray().GetUnsafeReadOnlyPtr(), stream.Length); + ReliableUtility.GetPacketInformation(sendBufferPtr, 65535)->SendTime = 980; ReliableUtility.StoreTimestamp(pipelineContext.internalSharedProcessBuffer, 65535, 980); ReliableUtility.StoreReceiveTimestamp(pipelineContext.internalSharedProcessBuffer, 65535, 990, 16); stream.Clear(); - stream.Write((int) 11); - ReliableUtility.SetPacket(sendBuffer, 0, stream.GetNativeSlice(0, stream.Length)); - ReliableUtility.GetPacketInformation(sendBuffer, 0)->SendTime = 990; + stream.WriteInt((int) 11); + ReliableUtility.SetPacket(sendBufferPtr, 0, (byte*)stream.AsNativeArray().GetUnsafeReadOnlyPtr(), stream.Length); + ReliableUtility.GetPacketInformation(sendBufferPtr, 0)->SendTime = 990; ReliableUtility.StoreTimestamp(pipelineContext.internalSharedProcessBuffer, 0, 990); ReliableUtility.ReleaseOrResumePackets(pipelineContext); Assert.AreEqual((ReliableUtility.ErrorCodes)0, sharedContext->errorCode); // Validate that packet tracking state is correct, 65535 should be released, 0 should still be there - Assert.AreEqual(-1, ReliableUtility.GetPacketInformation(sendBuffer, 65535)->SequenceId); - Assert.AreEqual(0, ReliableUtility.GetPacketInformation(sendBuffer, 0)->SequenceId); + Assert.AreEqual(-1, ReliableUtility.GetPacketInformation(sendBufferPtr, 65535)->SequenceId); + Assert.AreEqual(0, ReliableUtility.GetPacketInformation(sendBufferPtr, 0)->SequenceId); } recvBuffer.Dispose(); sendBuffer.Dispose(); @@ -303,10 +352,14 @@ public unsafe void ReliableUtility_AckPackets_SeqIdWrap1() NativeArray sendBuffer = new NativeArray(processCapacity, Allocator.Persistent); NativeArray sharedBuffer = new NativeArray(sharedCapacity, Allocator.Persistent); - ReliableUtility.InitializeContext(sharedBuffer, sendBuffer, recvBuffer, parameters); + var sendBufferPtr = (byte*)sendBuffer.GetUnsafePtr(); + + ReliableUtility.InitializeContext((byte*)sharedBuffer.GetUnsafePtr(), sharedBuffer.Length, + sendBufferPtr, sendBuffer.Length, (byte*)recvBuffer.GetUnsafePtr(), recvBuffer.Length, parameters); var pipelineContext = new NetworkPipelineContext - {internalProcessBuffer = sendBuffer, internalSharedProcessBuffer = sharedBuffer, timestamp = 1000}; + {internalProcessBuffer = sendBufferPtr, internalProcessBufferLength = sendBuffer.Length, + internalSharedProcessBuffer = (byte*)sharedBuffer.GetUnsafePtr(), internalSharedProcessBufferLength = sharedBuffer.Length, timestamp = 1000}; // Sending seqId 3, last received ID 2 (same as last sent packet) var sharedContext = (ReliableUtility.SharedContext*) sharedBuffer.GetUnsafePtr(); @@ -318,18 +371,18 @@ public unsafe void ReliableUtility_AckPackets_SeqIdWrap1() var receiveContext = (ReliableUtility.Context*) recvBuffer.GetUnsafePtr(); receiveContext->Delivered = sharedContext->SentPackets.Acked; - using (var stream = new DataStreamWriter(4, Allocator.Temp)) + var stream = new DataStreamWriter(4, Allocator.Temp); { // Add buffers in resend queue - stream.Write((int) 10); - ReliableUtility.SetPacket(sendBuffer, 1, stream.GetNativeSlice(0, stream.Length)); - ReliableUtility.GetPacketInformation(sendBuffer, 1)->SendTime = 980; + stream.WriteInt((int) 10); + ReliableUtility.SetPacket(sendBufferPtr, 1, (byte*)stream.AsNativeArray().GetUnsafeReadOnlyPtr(), stream.Length); + ReliableUtility.GetPacketInformation(sendBufferPtr, 1)->SendTime = 980; ReliableUtility.StoreTimestamp(pipelineContext.internalSharedProcessBuffer, 1, 980); ReliableUtility.StoreReceiveTimestamp(pipelineContext.internalSharedProcessBuffer, 1, 990, 16); stream.Clear(); - stream.Write((int) 11); - ReliableUtility.SetPacket(sendBuffer, 2, stream.GetNativeSlice(0, stream.Length)); - ReliableUtility.GetPacketInformation(sendBuffer, 2)->SendTime = 990; + stream.WriteInt((int) 11); + ReliableUtility.SetPacket(sendBufferPtr, 2, (byte*)stream.AsNativeArray().GetUnsafeReadOnlyPtr(), stream.Length); + ReliableUtility.GetPacketInformation(sendBufferPtr, 2)->SendTime = 990; ReliableUtility.StoreTimestamp(pipelineContext.internalSharedProcessBuffer, 2, 990); ReliableUtility.StoreReceiveTimestamp(pipelineContext.internalSharedProcessBuffer, 2, 1000, 16); @@ -337,8 +390,8 @@ public unsafe void ReliableUtility_AckPackets_SeqIdWrap1() Assert.AreEqual((ReliableUtility.ErrorCodes)0, sharedContext->errorCode); // Validate that packet tracking state is correct, both packets should be released - Assert.AreEqual(-1, ReliableUtility.GetPacketInformation(sendBuffer, 1)->SequenceId); - Assert.AreEqual(-1, ReliableUtility.GetPacketInformation(sendBuffer, 2)->SequenceId); + Assert.AreEqual(-1, ReliableUtility.GetPacketInformation(sendBufferPtr, 1)->SequenceId); + Assert.AreEqual(-1, ReliableUtility.GetPacketInformation(sendBufferPtr, 2)->SequenceId); } recvBuffer.Dispose(); sendBuffer.Dispose(); @@ -359,10 +412,14 @@ public unsafe void ReliableUtility_AckPackets_SeqIdWrap2() NativeArray sendBuffer = new NativeArray(processCapacity, Allocator.Persistent); NativeArray sharedBuffer = new NativeArray(sharedCapacity, Allocator.Persistent); - ReliableUtility.InitializeContext(sharedBuffer, sendBuffer, recvBuffer, parameters); + var sendBufferPtr = (byte*)sendBuffer.GetUnsafePtr(); + + ReliableUtility.InitializeContext((byte*)sharedBuffer.GetUnsafePtr(), sharedBuffer.Length, + sendBufferPtr, sendBuffer.Length, (byte*)recvBuffer.GetUnsafePtr(), recvBuffer.Length, parameters); var pipelineContext = new NetworkPipelineContext - {internalProcessBuffer = sendBuffer, internalSharedProcessBuffer = sharedBuffer}; + {internalProcessBuffer = sendBufferPtr, internalProcessBufferLength = sendBuffer.Length, + internalSharedProcessBuffer = (byte*)sharedBuffer.GetUnsafePtr(), internalSharedProcessBufferLength = sharedBuffer.Length}; // Sending seqId 3, last received ID 65535 (same as last sent) var sharedContext = (ReliableUtility.SharedContext*) sharedBuffer.GetUnsafePtr(); @@ -374,12 +431,12 @@ public unsafe void ReliableUtility_AckPackets_SeqIdWrap2() var receiveContext = (ReliableUtility.Context*) recvBuffer.GetUnsafePtr(); receiveContext->Delivered = sharedContext->SentPackets.Acked; - using (var stream = new DataStreamWriter(4, Allocator.Temp)) + var stream = new DataStreamWriter(4, Allocator.Temp); { // Add buffers in resend queue - stream.Write((int) 10); - ReliableUtility.SetPacket(sendBuffer, 65535, stream.GetNativeSlice(0, stream.Length)); - ReliableUtility.GetPacketInformation(sendBuffer, 65535)->SendTime = 980; + stream.WriteInt((int) 10); + ReliableUtility.SetPacket(sendBufferPtr, 65535, (byte*)stream.AsNativeArray().GetUnsafeReadOnlyPtr(), stream.Length); + ReliableUtility.GetPacketInformation(sendBufferPtr, 65535)->SendTime = 980; ReliableUtility.StoreTimestamp(pipelineContext.internalSharedProcessBuffer, 65535, 980); ReliableUtility.StoreReceiveTimestamp(pipelineContext.internalSharedProcessBuffer, 65535, 990, 16); @@ -387,7 +444,7 @@ public unsafe void ReliableUtility_AckPackets_SeqIdWrap2() Assert.AreEqual((ReliableUtility.ErrorCodes)0, sharedContext->errorCode); // Validate that packet tracking state is correct, 65535 should be released - Assert.AreEqual(-1, ReliableUtility.GetPacketInformation(sendBuffer, 65535)->SequenceId); + Assert.AreEqual(-1, ReliableUtility.GetPacketInformation(sendBufferPtr, 65535)->SequenceId); } recvBuffer.Dispose(); sendBuffer.Dispose(); @@ -408,10 +465,14 @@ public unsafe void ReliableUtility_AckPackets_SeqIdWrap3() NativeArray sendBuffer = new NativeArray(processCapacity, Allocator.Persistent); NativeArray sharedBuffer = new NativeArray(sharedCapacity, Allocator.Persistent); - ReliableUtility.InitializeContext(sharedBuffer, sendBuffer, recvBuffer, parameters); + var sendBufferPtr = (byte*)sendBuffer.GetUnsafePtr(); + + ReliableUtility.InitializeContext((byte*)sharedBuffer.GetUnsafePtr(), sharedBuffer.Length, + sendBufferPtr, sendBuffer.Length, (byte*)recvBuffer.GetUnsafePtr(), recvBuffer.Length, parameters); var pipelineContext = new NetworkPipelineContext - {internalProcessBuffer = sendBuffer, internalSharedProcessBuffer = sharedBuffer}; + {internalProcessBuffer = sendBufferPtr, internalProcessBufferLength = sendBuffer.Length, + internalSharedProcessBuffer = (byte*)sharedBuffer.GetUnsafePtr(), internalSharedProcessBufferLength = sharedBuffer.Length}; // Sending seqId 3, last received ID 0 (1 is not yet acked, 2 was dropped) var sharedContext = (ReliableUtility.SharedContext*) sharedBuffer.GetUnsafePtr(); @@ -423,17 +484,17 @@ public unsafe void ReliableUtility_AckPackets_SeqIdWrap3() var receiveContext = (ReliableUtility.Context*) recvBuffer.GetUnsafePtr(); receiveContext->Delivered = sharedContext->SentPackets.Acked; - using (var stream = new DataStreamWriter(4, Allocator.Temp)) + var stream = new DataStreamWriter(4, Allocator.Temp); { // Add buffers in resend queue - stream.Write((int) 10); - ReliableUtility.SetPacket(sendBuffer, 16, stream.GetNativeSlice(0, stream.Length)); + stream.WriteInt((int) 10); + ReliableUtility.SetPacket(sendBufferPtr, 16, (byte*)stream.AsNativeArray().GetUnsafeReadOnlyPtr(), stream.Length); ReliableUtility.ReleaseOrResumePackets(pipelineContext); Assert.AreEqual((ReliableUtility.ErrorCodes)0, sharedContext->errorCode); // Validate that packet tracking state is correct, packet 16 should be released - Assert.AreEqual(-1, ReliableUtility.GetPacketInformation(sendBuffer, 16)->SequenceId); + Assert.AreEqual(-1, ReliableUtility.GetPacketInformation(sendBufferPtr, 16)->SequenceId); } recvBuffer.Dispose(); sendBuffer.Dispose(); @@ -454,10 +515,14 @@ public unsafe void ReliableUtility_AckPackets_ReleaseSlotWithWrappedSeqId() NativeArray sendBuffer = new NativeArray(processCapacity, Allocator.Persistent); NativeArray sharedBuffer = new NativeArray(sharedCapacity, Allocator.Persistent); - ReliableUtility.InitializeContext(sharedBuffer, sendBuffer, recvBuffer, parameters); + var sendBufferPtr = (byte*)sendBuffer.GetUnsafePtr(); + + ReliableUtility.InitializeContext((byte*)sharedBuffer.GetUnsafePtr(), sharedBuffer.Length, + sendBufferPtr, sendBuffer.Length, (byte*)recvBuffer.GetUnsafePtr(), recvBuffer.Length, parameters); var pipelineContext = new NetworkPipelineContext - {internalProcessBuffer = sendBuffer, internalSharedProcessBuffer = sharedBuffer}; + {internalProcessBuffer = sendBufferPtr, internalProcessBufferLength = sendBuffer.Length, + internalSharedProcessBuffer = (byte*)sharedBuffer.GetUnsafePtr(), internalSharedProcessBufferLength = sharedBuffer.Length}; // Sending seqId 3, last received ID 0 (1 is not yet acked, 2 was dropped) var sharedContext = (ReliableUtility.SharedContext*) sharedBuffer.GetUnsafePtr(); @@ -469,21 +534,21 @@ public unsafe void ReliableUtility_AckPackets_ReleaseSlotWithWrappedSeqId() var receiveContext = (ReliableUtility.Context*) recvBuffer.GetUnsafePtr(); receiveContext->Delivered = sharedContext->SentPackets.Acked; - using (var stream = new DataStreamWriter(4, Allocator.Temp)) + var stream = new DataStreamWriter(4, Allocator.Temp); { // Add buffers in resend queue - stream.Write((int) 10); - ReliableUtility.SetPacket(sendBuffer, 0, stream.GetNativeSlice(0, stream.Length)); + stream.WriteInt((int) 10); + ReliableUtility.SetPacket(sendBufferPtr, 0, (byte*)stream.AsNativeArray().GetUnsafeReadOnlyPtr(), stream.Length); stream.Clear(); - stream.Write((int) 11); - ReliableUtility.SetPacket(sendBuffer, 65535, stream.GetNativeSlice(0, stream.Length)); + stream.WriteInt((int) 11); + ReliableUtility.SetPacket(sendBufferPtr, 65535, (byte*)stream.AsNativeArray().GetUnsafeReadOnlyPtr(), stream.Length); ReliableUtility.ReleaseOrResumePackets(pipelineContext); Assert.AreEqual((ReliableUtility.ErrorCodes)0, sharedContext->errorCode); // Validate that packet tracking state is correct, slot with seqId 0 and 65535 should have been released - Assert.AreEqual(-1, ReliableUtility.GetPacketInformation(sendBuffer, 0)->SequenceId); - Assert.AreEqual(-1, ReliableUtility.GetPacketInformation(sendBuffer, 65535)->SequenceId); + Assert.AreEqual(-1, ReliableUtility.GetPacketInformation(sendBufferPtr, 0)->SequenceId); + Assert.AreEqual(-1, ReliableUtility.GetPacketInformation(sendBufferPtr, 65535)->SequenceId); } recvBuffer.Dispose(); sendBuffer.Dispose(); @@ -504,10 +569,14 @@ public unsafe void ReliableUtility_AckPackets_AckMaskShiftsProperly1() NativeArray sendBuffer = new NativeArray(processCapacity, Allocator.Persistent); NativeArray sharedBuffer = new NativeArray(sharedCapacity, Allocator.Persistent); - ReliableUtility.InitializeContext(sharedBuffer, sendBuffer, recvBuffer, parameters); + var sendBufferPtr = (byte*)sendBuffer.GetUnsafePtr(); + + ReliableUtility.InitializeContext((byte*)sharedBuffer.GetUnsafePtr(), sharedBuffer.Length, + sendBufferPtr, sendBuffer.Length, (byte*)recvBuffer.GetUnsafePtr(), recvBuffer.Length, parameters); var pipelineContext = new NetworkPipelineContext - {internalProcessBuffer = sendBuffer, internalSharedProcessBuffer = sharedBuffer, timestamp = 1000}; + {internalProcessBuffer = sendBufferPtr, internalProcessBufferLength = sendBuffer.Length, + internalSharedProcessBuffer = (byte*)sharedBuffer.GetUnsafePtr(), internalSharedProcessBufferLength = sharedBuffer.Length, timestamp = 1000}; // Sending seqId 3, last received ID 0 (1 is not yet acked, 2 was dropped) var sharedContext = (ReliableUtility.SharedContext*) sharedBuffer.GetUnsafePtr(); @@ -519,28 +588,28 @@ public unsafe void ReliableUtility_AckPackets_AckMaskShiftsProperly1() var receiveContext = (ReliableUtility.Context*) recvBuffer.GetUnsafePtr(); receiveContext->Delivered = sharedContext->SentPackets.Acked; - using (var stream = new DataStreamWriter(4, Allocator.Temp)) + var stream = new DataStreamWriter(4, Allocator.Temp); { // Add buffers in resend queue // SeqId 3 is received and ready to be released - stream.Write((int) 10); - ReliableUtility.SetPacket(sendBuffer, 3, stream.GetNativeSlice(0, stream.Length)); - ReliableUtility.GetPacketInformation(sendBuffer, 3)->SendTime = 990; + stream.WriteInt((int) 10); + ReliableUtility.SetPacket(sendBufferPtr, 3, (byte*)stream.AsNativeArray().GetUnsafeReadOnlyPtr(), stream.Length); + ReliableUtility.GetPacketInformation(sendBufferPtr, 3)->SendTime = 990; ReliableUtility.StoreTimestamp(pipelineContext.internalSharedProcessBuffer, 3, 980); ReliableUtility.StoreReceiveTimestamp(pipelineContext.internalSharedProcessBuffer, 3, 990, 16); stream.Clear(); // SeqId 2 is not yet received so it should stick around - stream.Write((int) 11); - ReliableUtility.SetPacket(sendBuffer, 2, stream.GetNativeSlice(0, stream.Length)); - ReliableUtility.GetPacketInformation(sendBuffer, 2)->SendTime = 1000; + stream.WriteInt((int) 11); + ReliableUtility.SetPacket(sendBufferPtr, 2, (byte*)stream.AsNativeArray().GetUnsafeReadOnlyPtr(), stream.Length); + ReliableUtility.GetPacketInformation(sendBufferPtr, 2)->SendTime = 1000; ReliableUtility.StoreTimestamp(pipelineContext.internalSharedProcessBuffer, 2, 1000); ReliableUtility.ReleaseOrResumePackets(pipelineContext); Assert.AreEqual((ReliableUtility.ErrorCodes)0, sharedContext->errorCode); // Validate that packet tracking state is correct, packet 3 should be released (has been acked), 2 should stick around - Assert.AreEqual(-1, ReliableUtility.GetPacketInformation(sendBuffer, 3)->SequenceId); - Assert.AreEqual(2, ReliableUtility.GetPacketInformation(sendBuffer, 2)->SequenceId); + Assert.AreEqual(-1, ReliableUtility.GetPacketInformation(sendBufferPtr, 3)->SequenceId); + Assert.AreEqual(2, ReliableUtility.GetPacketInformation(sendBufferPtr, 2)->SequenceId); } recvBuffer.Dispose(); sendBuffer.Dispose(); @@ -561,10 +630,14 @@ public unsafe void ReliableUtility_AckPackets_AckMaskShiftsProperly2() NativeArray sendBuffer = new NativeArray(processCapacity, Allocator.Persistent); NativeArray sharedBuffer = new NativeArray(sharedCapacity, Allocator.Persistent); - ReliableUtility.InitializeContext(sharedBuffer, sendBuffer, recvBuffer, parameters); + var sendBufferPtr = (byte*)sendBuffer.GetUnsafePtr(); + + ReliableUtility.InitializeContext((byte*)sharedBuffer.GetUnsafePtr(), sharedBuffer.Length, + sendBufferPtr, sendBuffer.Length, (byte*)recvBuffer.GetUnsafePtr(), recvBuffer.Length, parameters); var pipelineContext = new NetworkPipelineContext - {internalProcessBuffer = sendBuffer, internalSharedProcessBuffer = sharedBuffer, timestamp = 1000}; + {internalProcessBuffer = sendBufferPtr, internalProcessBufferLength = sendBuffer.Length, + internalSharedProcessBuffer = (byte*)sharedBuffer.GetUnsafePtr(), internalSharedProcessBufferLength = sharedBuffer.Length, timestamp = 1000}; // Sending seqId 3, last received ID 0 (1 is not yet acked, 2 was dropped) var sharedContext = (ReliableUtility.SharedContext*) sharedBuffer.GetUnsafePtr(); @@ -576,27 +649,27 @@ public unsafe void ReliableUtility_AckPackets_AckMaskShiftsProperly2() var receiveContext = (ReliableUtility.Context*) recvBuffer.GetUnsafePtr(); receiveContext->Delivered = sharedContext->SentPackets.Acked; - using (var stream = new DataStreamWriter(4, Allocator.Temp)) + var stream = new DataStreamWriter(4, Allocator.Temp); { // Add buffers in resend queue // SeqId 4 is received and ready to be released - stream.Write((int) 10); - ReliableUtility.SetPacket(sendBuffer, 4, stream.GetNativeSlice(0, stream.Length)); - ReliableUtility.GetPacketInformation(sendBuffer, 4)->SendTime = 980; + stream.WriteInt((int) 10); + ReliableUtility.SetPacket(sendBufferPtr, 4, (byte*)stream.AsNativeArray().GetUnsafeReadOnlyPtr(), stream.Length); + ReliableUtility.GetPacketInformation(sendBufferPtr, 4)->SendTime = 980; ReliableUtility.StoreTimestamp(pipelineContext.internalSharedProcessBuffer, 4, 980); ReliableUtility.StoreReceiveTimestamp(pipelineContext.internalSharedProcessBuffer, 4, 990, 16); stream.Clear(); - stream.Write((int) 11); - ReliableUtility.SetPacket(sendBuffer, 3, stream.GetNativeSlice(0, stream.Length)); - ReliableUtility.GetPacketInformation(sendBuffer, 3)->SendTime = 1000; + stream.WriteInt((int) 11); + ReliableUtility.SetPacket(sendBufferPtr, 3, (byte*)stream.AsNativeArray().GetUnsafeReadOnlyPtr(), stream.Length); + ReliableUtility.GetPacketInformation(sendBufferPtr, 3)->SendTime = 1000; ReliableUtility.StoreTimestamp(pipelineContext.internalSharedProcessBuffer, 3, 1000); ReliableUtility.ReleaseOrResumePackets(pipelineContext); Assert.AreEqual((ReliableUtility.ErrorCodes)0, sharedContext->errorCode); // Validate that packet tracking state is correct, packet 3 should be released (has been acked), 2 should stick around - Assert.AreEqual(-1, ReliableUtility.GetPacketInformation(sendBuffer, 4)->SequenceId); - Assert.AreEqual(3, ReliableUtility.GetPacketInformation(sendBuffer, 3)->SequenceId); + Assert.AreEqual(-1, ReliableUtility.GetPacketInformation(sendBufferPtr, 4)->SequenceId); + Assert.AreEqual(3, ReliableUtility.GetPacketInformation(sendBufferPtr, 3)->SequenceId); } recvBuffer.Dispose(); sendBuffer.Dispose(); @@ -620,31 +693,35 @@ public unsafe void ReliableUtility_TimestampHandling() NativeArray ep2SendBuffer = new NativeArray(processCapacity, Allocator.Persistent); NativeArray ep2SharedBuffer = new NativeArray(sharedCapacity, Allocator.Persistent); - ReliableUtility.InitializeContext(ep1SharedBuffer, ep1SendBuffer, ep1RecvBuffer, parameters); - ReliableUtility.InitializeContext(ep2SharedBuffer, ep2SendBuffer, ep2RecvBuffer, parameters); + ReliableUtility.InitializeContext((byte*)ep1SharedBuffer.GetUnsafePtr(), ep1SharedBuffer.Length, + (byte*)ep1SendBuffer.GetUnsafePtr(), ep1SendBuffer.Length, + (byte*)ep1RecvBuffer.GetUnsafePtr(), ep1RecvBuffer.Length, parameters); + ReliableUtility.InitializeContext((byte*)ep2SharedBuffer.GetUnsafePtr(), ep2SharedBuffer.Length, + (byte*)ep2SendBuffer.GetUnsafePtr(), ep2SendBuffer.Length, + (byte*)ep2RecvBuffer.GetUnsafePtr(), ep2RecvBuffer.Length, parameters); // When sending we store the send timestamp of the sequence ID (EP1 -> EP2) ushort ep1SeqId = 10; - ReliableUtility.StoreTimestamp(ep1SharedBuffer, ep1SeqId, 900); + ReliableUtility.StoreTimestamp((byte*)ep1SharedBuffer.GetUnsafePtr(), ep1SeqId, 900); // EP2 also sends something to EP1 ushort ep2SeqId = 100; - ReliableUtility.StoreTimestamp(ep2SharedBuffer, ep2SeqId, 910); + ReliableUtility.StoreTimestamp((byte*)ep2SharedBuffer.GetUnsafePtr(), ep2SeqId, 910); // When EP2 receives the packet the receive time is stored - ReliableUtility.StoreRemoteReceiveTimestamp(ep2SharedBuffer, ep1SeqId, 920); + ReliableUtility.StoreRemoteReceiveTimestamp((byte*)ep2SharedBuffer.GetUnsafePtr(), ep1SeqId, 920); // EP2 also stores the timing information in the EP1 packet (processing time for the packet it sent earlier) - ReliableUtility.StoreReceiveTimestamp(ep2SharedBuffer, ep2SeqId, 920, 10); + ReliableUtility.StoreReceiveTimestamp((byte*)ep2SharedBuffer.GetUnsafePtr(), ep2SeqId, 920, 10); // When EP2 sends another packet to EP1 it calculates ep1SeqId processing time - int processTime = ReliableUtility.CalculateProcessingTime(ep2SharedBuffer, ep1SeqId, 930); + int processTime = ReliableUtility.CalculateProcessingTime((byte*)ep2SharedBuffer.GetUnsafePtr(), ep1SeqId, 930); // ep1SeqId processing time should be 10 ms (930 - 920) Assert.AreEqual(10, processTime); // Verify information written so far (send/receive times + processing time) - var timerData = ReliableUtility.GetLocalPacketTimer(ep2SharedBuffer, ep2SeqId); + var timerData = ReliableUtility.GetLocalPacketTimer((byte*)ep2SharedBuffer.GetUnsafePtr(), ep2SeqId); Assert.IsTrue(timerData != null, "Packet timing data not found"); Assert.AreEqual(ep2SeqId, timerData->SequenceId); Assert.AreEqual(10, timerData->ProcessingTime); @@ -679,10 +756,14 @@ public unsafe void Receive_ResumesMultipleStoredPacketsAroundWrapPoint1() NativeArray sendBuffer = new NativeArray(processCapacity, Allocator.Persistent); NativeArray sharedBuffer = new NativeArray(sharedCapacity, Allocator.Persistent); - ReliableUtility.InitializeContext(new NativeSlice(sharedBuffer), new NativeSlice(sendBuffer), new NativeSlice(recvBuffer), parameters); + var recvBufferPtr = (byte*)recvBuffer.GetUnsafePtr(); + + ReliableUtility.InitializeContext((byte*)sharedBuffer.GetUnsafePtr(), sharedBuffer.Length, + (byte*)sendBuffer.GetUnsafePtr(), sendBuffer.Length, recvBufferPtr, recvBuffer.Length, parameters); var pipelineContext = new NetworkPipelineContext - {internalProcessBuffer = recvBuffer, internalSharedProcessBuffer = sharedBuffer}; + {internalProcessBuffer = recvBufferPtr, internalProcessBufferLength = recvBuffer.Length, + internalSharedProcessBuffer = (byte*)sharedBuffer.GetUnsafePtr(), internalSharedProcessBufferLength = sharedBuffer.Length}; var sharedContext = (ReliableUtility.SharedContext*) sharedBuffer.GetUnsafePtr(); sharedContext->SentPackets.Sequence = 3; // what was last sent doesn't matter here @@ -693,49 +774,54 @@ public unsafe void Receive_ResumesMultipleStoredPacketsAroundWrapPoint1() var receiveContext = (ReliableUtility.Context*) recvBuffer.GetUnsafePtr(); receiveContext->Delivered = 65534; // latest in sequence delivered packet, one less than what unclogs the packet jam - var reliablePipeline = new ReliableSequencedPipelineStage(); + var reliablePipelineStage = new ReliableSequencedPipelineStage(); + var staticBuffer = new NativeArray(reliablePipelineStage.StaticSize, Allocator.Temp); + pipelineContext.staticInstanceBuffer = (byte*)staticBuffer.GetUnsafePtr(); + pipelineContext.staticInstanceBufferLength = staticBuffer.Length; + var reliablePipeline = reliablePipelineStage.StaticInitialize((byte*)staticBuffer.GetUnsafePtr(), staticBuffer.Length, new INetworkParameter[0]); - using (var stream = new DataStreamWriter(4, Allocator.Temp)) - using (var inboundStream = new DataStreamWriter(4, Allocator.Temp)) + var stream = new DataStreamWriter(4, Allocator.Temp); + var inboundStream = new DataStreamWriter(4, Allocator.Temp); { // Add buffers to receive queue, packets which should be resume received after packet jam is unclogged stream.Clear(); - stream.Write((int) 100); - ReliableUtility.SetPacket(recvBuffer, 0, stream.GetNativeSlice(0, stream.Length)); + stream.WriteInt((int) 100); + ReliableUtility.SetPacket(recvBufferPtr, 0, stream.AsNativeArray().GetUnsafeReadOnlyPtr(), stream.Length); stream.Clear(); - stream.Write((int) 200); - ReliableUtility.SetPacket(recvBuffer, 1, stream.GetNativeSlice(0, stream.Length)); + stream.WriteInt((int) 200); + ReliableUtility.SetPacket(recvBufferPtr, 1, stream.AsNativeArray().GetUnsafeReadOnlyPtr(), stream.Length); stream.Clear(); - stream.Write((int) 300); - ReliableUtility.SetPacket(recvBuffer, 2, stream.GetNativeSlice(0, stream.Length)); + stream.WriteInt((int) 300); + ReliableUtility.SetPacket(recvBufferPtr, 2, stream.AsNativeArray().GetUnsafeReadOnlyPtr(), stream.Length); // Generate the packet which will be handled in receive - NativeSlice packet = default; + InboundRecvBuffer packet = default; GeneratePacket(9000, 2, 0xFFFFFFFF, 65535, ref sendBuffer, out packet); - bool needsResume = false; - bool needsUpdate = false; - bool needsSendUpdate = false; // Process 65535, 0 should then be next in line on the resume field - reliablePipeline.Receive(pipelineContext, packet, ref needsResume, ref needsUpdate, ref needsSendUpdate); + var stageRequest = NetworkPipelineStage.Requests.None; + reliablePipeline.Receive.Ptr.Invoke(ref pipelineContext, ref packet, ref stageRequest); Assert.AreEqual((ReliableUtility.ErrorCodes)0, sharedContext->errorCode); Assert.AreEqual(0, receiveContext->Resume); - Assert.IsTrue(needsResume); + Assert.AreNotEqual(NetworkPipelineStage.Requests.None, stageRequest&NetworkPipelineStage.Requests.Resume); // Process 0, after that 1 is up - reliablePipeline.Receive(pipelineContext, packet, ref needsResume, ref needsUpdate, ref needsSendUpdate); + stageRequest = NetworkPipelineStage.Requests.None; + reliablePipeline.Receive.Ptr.Invoke(ref pipelineContext, ref packet, ref stageRequest); Assert.AreEqual((ReliableUtility.ErrorCodes)0, sharedContext->errorCode); Assert.AreEqual(1, receiveContext->Resume); - Assert.IsTrue(needsResume); + Assert.AreNotEqual(NetworkPipelineStage.Requests.None, stageRequest&NetworkPipelineStage.Requests.Resume); // Process 1, after that 2 is up - reliablePipeline.Receive(pipelineContext, packet, ref needsResume, ref needsUpdate, ref needsSendUpdate); + stageRequest = NetworkPipelineStage.Requests.None; + reliablePipeline.Receive.Ptr.Invoke(ref pipelineContext, ref packet, ref stageRequest); Assert.AreEqual((ReliableUtility.ErrorCodes)0, sharedContext->errorCode); Assert.AreEqual(2, receiveContext->Resume); - Assert.IsTrue(needsResume); + Assert.AreNotEqual(NetworkPipelineStage.Requests.None, stageRequest&NetworkPipelineStage.Requests.Resume); // Process 2, and we are done - reliablePipeline.Receive(pipelineContext, packet, ref needsResume, ref needsUpdate, ref needsSendUpdate); + stageRequest = NetworkPipelineStage.Requests.None; + reliablePipeline.Receive.Ptr.Invoke(ref pipelineContext, ref packet, ref stageRequest); Assert.AreEqual((ReliableUtility.ErrorCodes)0, sharedContext->errorCode); Assert.AreEqual(-1, receiveContext->Resume); - Assert.IsFalse(needsResume); + Assert.AreEqual(NetworkPipelineStage.Requests.None, stageRequest&NetworkPipelineStage.Requests.Resume); } recvBuffer.Dispose(); sendBuffer.Dispose(); @@ -756,10 +842,14 @@ public unsafe void Receive_ResumesMultipleStoredPacketsAroundWrapPoint2() NativeArray sendBuffer = new NativeArray(processCapacity, Allocator.Persistent); NativeArray sharedBuffer = new NativeArray(sharedCapacity, Allocator.Persistent); - ReliableUtility.InitializeContext(new NativeSlice(sharedBuffer), new NativeSlice(sendBuffer), new NativeSlice(recvBuffer), parameters); + var recvBufferPtr = (byte*)recvBuffer.GetUnsafePtr(); + + ReliableUtility.InitializeContext((byte*)sharedBuffer.GetUnsafePtr(), sharedBuffer.Length, + (byte*)sendBuffer.GetUnsafePtr(), sendBuffer.Length, recvBufferPtr, recvBuffer.Length, parameters); var pipelineContext = new NetworkPipelineContext - {internalProcessBuffer = recvBuffer, internalSharedProcessBuffer = sharedBuffer}; + {internalProcessBuffer = recvBufferPtr, internalProcessBufferLength = recvBuffer.Length, + internalSharedProcessBuffer = (byte*)sharedBuffer.GetUnsafePtr(), internalSharedProcessBufferLength = sharedBuffer.Length}; var sharedContext = (ReliableUtility.SharedContext*) sharedBuffer.GetUnsafePtr(); sharedContext->SentPackets.Sequence = 2; // what was last sent doesn't matter here @@ -770,48 +860,53 @@ public unsafe void Receive_ResumesMultipleStoredPacketsAroundWrapPoint2() var receiveContext = (ReliableUtility.Context*) recvBuffer.GetUnsafePtr(); receiveContext->Delivered = 65533; // latest in sequence delivered packet, one less than what unclogs the packet jam - var reliablePipeline = new ReliableSequencedPipelineStage(); + var reliablePipelineStage = new ReliableSequencedPipelineStage(); + var staticBuffer = new NativeArray(reliablePipelineStage.StaticSize, Allocator.Temp); + pipelineContext.staticInstanceBuffer = (byte*)staticBuffer.GetUnsafePtr(); + pipelineContext.staticInstanceBufferLength = staticBuffer.Length; + var reliablePipeline = reliablePipelineStage.StaticInitialize((byte*)staticBuffer.GetUnsafePtr(), staticBuffer.Length, new INetworkParameter[0]); - using (var stream = new DataStreamWriter(4, Allocator.Temp)) + var stream = new DataStreamWriter(4, Allocator.Temp); { // Add buffers to receive queue, packets which should be resume received after packet jam is unclogged stream.Clear(); - stream.Write((int) 100); - ReliableUtility.SetPacket(recvBuffer, 65535, stream.GetNativeSlice(0, stream.Length)); + stream.WriteInt((int) 100); + ReliableUtility.SetPacket(recvBufferPtr, 65535, stream.AsNativeArray().GetUnsafeReadOnlyPtr(), stream.Length); stream.Clear(); - stream.Write((int) 200); - ReliableUtility.SetPacket(recvBuffer, 0, stream.GetNativeSlice(0, stream.Length)); + stream.WriteInt((int) 200); + ReliableUtility.SetPacket(recvBufferPtr, 0, stream.AsNativeArray().GetUnsafeReadOnlyPtr(), stream.Length); stream.Clear(); - stream.Write((int) 300); - ReliableUtility.SetPacket(recvBuffer, 1, stream.GetNativeSlice(0, stream.Length)); + stream.WriteInt((int) 300); + ReliableUtility.SetPacket(recvBufferPtr, 1, stream.AsNativeArray().GetUnsafeReadOnlyPtr(), stream.Length); // Generate the packet which will be handled in receive - NativeSlice packet = default; + InboundRecvBuffer packet = default; GeneratePacket(9000, 65533, 0xFFFFFFFF, 65534, ref sendBuffer, out packet); - bool needsResume = false; - bool needsUpdate = false; - bool needsSendUpdate = false; // Process 65534, 65535 should then be next in line on the resume field - reliablePipeline.Receive(pipelineContext, packet, ref needsResume, ref needsUpdate, ref needsSendUpdate); + var stageRequest = NetworkPipelineStage.Requests.None; + reliablePipeline.Receive.Ptr.Invoke(ref pipelineContext, ref packet, ref stageRequest); Assert.AreEqual((ReliableUtility.ErrorCodes)0, sharedContext->errorCode); Assert.AreEqual(65535, receiveContext->Resume); - Assert.IsTrue(needsResume); + Assert.AreNotEqual(NetworkPipelineStage.Requests.None, stageRequest&NetworkPipelineStage.Requests.Resume); // Process 65535, after that 0 is up - reliablePipeline.Receive(pipelineContext, packet, ref needsResume, ref needsUpdate, ref needsSendUpdate); + stageRequest = NetworkPipelineStage.Requests.None; + reliablePipeline.Receive.Ptr.Invoke(ref pipelineContext, ref packet, ref stageRequest); Assert.AreEqual((ReliableUtility.ErrorCodes)0, sharedContext->errorCode); Assert.AreEqual(0, receiveContext->Resume); - Assert.IsTrue(needsResume); + Assert.AreNotEqual(NetworkPipelineStage.Requests.None, stageRequest&NetworkPipelineStage.Requests.Resume); // Process 0, after that 1 is up - reliablePipeline.Receive(pipelineContext, packet, ref needsResume, ref needsUpdate, ref needsSendUpdate); + stageRequest = NetworkPipelineStage.Requests.None; + reliablePipeline.Receive.Ptr.Invoke(ref pipelineContext, ref packet, ref stageRequest); Assert.AreEqual((ReliableUtility.ErrorCodes)0, sharedContext->errorCode); Assert.AreEqual(1, receiveContext->Resume); - Assert.IsTrue(needsResume); + Assert.AreNotEqual(NetworkPipelineStage.Requests.None, stageRequest&NetworkPipelineStage.Requests.Resume); // Process 1, and we are done - reliablePipeline.Receive(pipelineContext, packet, ref needsResume, ref needsUpdate, ref needsSendUpdate); + stageRequest = NetworkPipelineStage.Requests.None; + reliablePipeline.Receive.Ptr.Invoke(ref pipelineContext, ref packet, ref stageRequest); Assert.AreEqual((ReliableUtility.ErrorCodes)0, sharedContext->errorCode); Assert.AreEqual(-1, receiveContext->Resume); - Assert.IsFalse(needsResume); + Assert.AreEqual(NetworkPipelineStage.Requests.None, stageRequest&NetworkPipelineStage.Requests.Resume); } recvBuffer.Dispose(); sendBuffer.Dispose(); @@ -832,10 +927,14 @@ public unsafe void Receive_ResumesMultipleStoredPacketsAndSetsAckedAckMaskProper NativeArray sendBuffer = new NativeArray(processCapacity, Allocator.Persistent); NativeArray sharedBuffer = new NativeArray(sharedCapacity, Allocator.Persistent); - ReliableUtility.InitializeContext(new NativeSlice(sharedBuffer), new NativeSlice(sendBuffer), new NativeSlice(recvBuffer), parameters); + var recvBufferPtr = (byte*)recvBuffer.GetUnsafePtr(); + + ReliableUtility.InitializeContext((byte*)sharedBuffer.GetUnsafePtr(), sharedBuffer.Length, + (byte*)sendBuffer.GetUnsafePtr(), sendBuffer.Length, recvBufferPtr, recvBuffer.Length, parameters); var pipelineContext = new NetworkPipelineContext - {internalProcessBuffer = recvBuffer, internalSharedProcessBuffer = sharedBuffer}; + {internalProcessBuffer = recvBufferPtr, internalProcessBufferLength = recvBuffer.Length, + internalSharedProcessBuffer = (byte*)sharedBuffer.GetUnsafePtr(), internalSharedProcessBufferLength = sharedBuffer.Length}; var sharedContext = (ReliableUtility.SharedContext*) sharedBuffer.GetUnsafePtr(); sharedContext->SentPackets.Sequence = 99; // what was last sent doesn't matter here @@ -846,56 +945,63 @@ public unsafe void Receive_ResumesMultipleStoredPacketsAndSetsAckedAckMaskProper var receiveContext = (ReliableUtility.Context*) recvBuffer.GetUnsafePtr(); receiveContext->Delivered = 94; // latest in sequence delivered packet, one less than what unclogs the packet jam - var reliablePipeline = new ReliableSequencedPipelineStage(); + var reliablePipelineStage = new ReliableSequencedPipelineStage(); + var staticBuffer = new NativeArray(reliablePipelineStage.StaticSize, Allocator.Temp); + pipelineContext.staticInstanceBuffer = (byte*)staticBuffer.GetUnsafePtr(); + pipelineContext.staticInstanceBufferLength = staticBuffer.Length; + var reliablePipeline = reliablePipelineStage.StaticInitialize((byte*)staticBuffer.GetUnsafePtr(), staticBuffer.Length, new INetworkParameter[0]); - using (var stream = new DataStreamWriter(4, Allocator.Temp)) + var stream = new DataStreamWriter(4, Allocator.Temp); { // Add buffers to receive queue, packets which should be resume received after packet jam is unclogged stream.Clear(); - stream.Write((int) 200); - ReliableUtility.SetPacket(recvBuffer, 96, stream.GetNativeSlice(0, stream.Length)); + stream.WriteInt((int) 200); + ReliableUtility.SetPacket(recvBufferPtr, 96, stream.AsNativeArray().GetUnsafeReadOnlyPtr(), stream.Length); stream.Clear(); - stream.Write((int) 300); - ReliableUtility.SetPacket(recvBuffer, 97, stream.GetNativeSlice(0, stream.Length)); + stream.WriteInt((int) 300); + ReliableUtility.SetPacket(recvBufferPtr, 97, stream.AsNativeArray().GetUnsafeReadOnlyPtr(), stream.Length); stream.Clear(); - stream.Write((int) 300); - ReliableUtility.SetPacket(recvBuffer, 98, stream.GetNativeSlice(0, stream.Length)); + stream.WriteInt((int) 300); + ReliableUtility.SetPacket(recvBufferPtr, 98, stream.AsNativeArray().GetUnsafeReadOnlyPtr(), stream.Length); - bool needsResume = false; - bool needsUpdate = false; - bool needsSendUpdate = false; - NativeSlice packet = default; + InboundRecvBuffer packet = default; GeneratePacket(9000, 98, 0xFFFFFFFF, 99, ref sendBuffer, out packet); // Receive 99, it's out of order so should be queued for later (waiting for 95) - reliablePipeline.Receive(pipelineContext, packet, ref needsResume, ref needsUpdate, ref needsSendUpdate); + var stageRequest = NetworkPipelineStage.Requests.None; + reliablePipeline.Receive.Ptr.Invoke(ref pipelineContext, ref packet, ref stageRequest); Assert.AreEqual((ReliableUtility.ErrorCodes)0, sharedContext->errorCode); Assert.AreEqual(-1, receiveContext->Resume); - Assert.IsFalse(needsResume); + Assert.AreEqual(NetworkPipelineStage.Requests.None, stageRequest&NetworkPipelineStage.Requests.Resume); GeneratePacket(10000, 98, 0xFFFFFFFF, 95, ref sendBuffer, out packet); // First 95 is received and then receive resume runs up to 99 - reliablePipeline.Receive(pipelineContext, packet, ref needsResume, ref needsUpdate, ref needsSendUpdate); + stageRequest = NetworkPipelineStage.Requests.None; + reliablePipeline.Receive.Ptr.Invoke(ref pipelineContext, ref packet, ref stageRequest); Assert.AreEqual((ReliableUtility.ErrorCodes)0, sharedContext->errorCode); Assert.AreEqual(96, receiveContext->Resume); - Assert.IsTrue(needsResume); - reliablePipeline.Receive(pipelineContext, packet, ref needsResume, ref needsUpdate, ref needsSendUpdate); + Assert.AreNotEqual(NetworkPipelineStage.Requests.None, stageRequest&NetworkPipelineStage.Requests.Resume); + stageRequest = NetworkPipelineStage.Requests.None; + reliablePipeline.Receive.Ptr.Invoke(ref pipelineContext, ref packet, ref stageRequest); Assert.AreEqual((ReliableUtility.ErrorCodes)0, sharedContext->errorCode); Assert.AreEqual(97, receiveContext->Resume); - Assert.IsTrue(needsResume); - reliablePipeline.Receive(pipelineContext, packet, ref needsResume, ref needsUpdate, ref needsSendUpdate); + Assert.AreNotEqual(NetworkPipelineStage.Requests.None, stageRequest&NetworkPipelineStage.Requests.Resume); + stageRequest = NetworkPipelineStage.Requests.None; + reliablePipeline.Receive.Ptr.Invoke(ref pipelineContext, ref packet, ref stageRequest); Assert.AreEqual((ReliableUtility.ErrorCodes)0, sharedContext->errorCode); Assert.AreEqual(98, receiveContext->Resume); - Assert.IsTrue(needsResume); - reliablePipeline.Receive(pipelineContext, packet, ref needsResume, ref needsUpdate, ref needsSendUpdate); + Assert.AreNotEqual(NetworkPipelineStage.Requests.None, stageRequest&NetworkPipelineStage.Requests.Resume); + stageRequest = NetworkPipelineStage.Requests.None; + reliablePipeline.Receive.Ptr.Invoke(ref pipelineContext, ref packet, ref stageRequest); Assert.AreEqual((ReliableUtility.ErrorCodes)0, sharedContext->errorCode); Assert.AreEqual(99, receiveContext->Resume); - Assert.IsTrue(needsResume); - reliablePipeline.Receive(pipelineContext, packet, ref needsResume, ref needsUpdate, ref needsSendUpdate); + Assert.AreNotEqual(NetworkPipelineStage.Requests.None, stageRequest&NetworkPipelineStage.Requests.Resume); + stageRequest = NetworkPipelineStage.Requests.None; + reliablePipeline.Receive.Ptr.Invoke(ref pipelineContext, ref packet, ref stageRequest); Assert.AreEqual((ReliableUtility.ErrorCodes)0, sharedContext->errorCode); Assert.AreEqual(-1, receiveContext->Resume); - Assert.IsFalse(needsResume); + Assert.AreEqual(NetworkPipelineStage.Requests.None, stageRequest&NetworkPipelineStage.Requests.Resume); // Verify that the ReceivePackets state is correct, 99 should be latest received and ackmask 0xFFFFF Assert.AreEqual(99, sharedContext->ReceivedPackets.Sequence); @@ -920,10 +1026,14 @@ public unsafe void Send_PacketsAreAcked_SendingPacket() NativeArray sendBuffer = new NativeArray(processCapacity, Allocator.Persistent); NativeArray sharedBuffer = new NativeArray(sharedCapacity, Allocator.Persistent); - ReliableUtility.InitializeContext(new NativeSlice(sharedBuffer), new NativeSlice(sendBuffer), new NativeSlice(recvBuffer), parameters); + var sendBufferPtr = (byte*)sendBuffer.GetUnsafePtr(); + + ReliableUtility.InitializeContext((byte*)sharedBuffer.GetUnsafePtr(), sharedBuffer.Length, + sendBufferPtr, sendBuffer.Length, (byte*)recvBuffer.GetUnsafePtr(), recvBuffer.Length, parameters); var pipelineContext = new NetworkPipelineContext - {internalProcessBuffer = sendBuffer, internalSharedProcessBuffer = sharedBuffer}; + {internalProcessBuffer = sendBufferPtr, internalProcessBufferLength = recvBuffer.Length, + internalSharedProcessBuffer = (byte*)sharedBuffer.GetUnsafePtr(), internalSharedProcessBufferLength = sharedBuffer.Length}; var sharedContext = (ReliableUtility.SharedContext*) sharedBuffer.GetUnsafePtr(); sharedContext->SentPackets.Sequence = 3; @@ -934,42 +1044,45 @@ public unsafe void Send_PacketsAreAcked_SendingPacket() var receiveContext = (ReliableUtility.Context*) recvBuffer.GetUnsafePtr(); receiveContext->Delivered = 1; - var reliablePipeline = new ReliableSequencedPipelineStage(); + var reliablePipelineStage = new ReliableSequencedPipelineStage(); + var staticBuffer = new NativeArray(reliablePipelineStage.StaticSize, Allocator.Temp); + pipelineContext.staticInstanceBuffer = (byte*)staticBuffer.GetUnsafePtr(); + pipelineContext.staticInstanceBufferLength = staticBuffer.Length; + var reliablePipeline = reliablePipelineStage.StaticInitialize((byte*)staticBuffer.GetUnsafePtr(), staticBuffer.Length, new INetworkParameter[0]); - using (var stream = new DataStreamWriter(4, Allocator.Temp)) - using (pipelineContext.header = new DataStreamWriter(UnsafeUtility.SizeOf(), Allocator.Persistent)) + var stream = new DataStreamWriter(4, Allocator.Temp); + pipelineContext.header = new DataStreamWriter(UnsafeUtility.SizeOf(), Allocator.Temp); { // Fill window capacity, next send should then clear everything stream.Clear(); - stream.Write((int) 100); - ReliableUtility.SetPacket(sendBuffer, 0, stream.GetNativeSlice(0, stream.Length)); + stream.WriteInt((int) 100); + ReliableUtility.SetPacket(sendBufferPtr, 0, stream.AsNativeArray().GetUnsafeReadOnlyPtr(), stream.Length); stream.Clear(); - stream.Write((int) 200); - ReliableUtility.SetPacket(sendBuffer, 1, stream.GetNativeSlice(0, stream.Length)); + stream.WriteInt((int) 200); + ReliableUtility.SetPacket(sendBufferPtr, 1, stream.AsNativeArray().GetUnsafeReadOnlyPtr(), stream.Length); stream.Clear(); - stream.Write((int) 300); - ReliableUtility.SetPacket(sendBuffer, 2, stream.GetNativeSlice(0, stream.Length)); + stream.WriteInt((int) 300); + ReliableUtility.SetPacket(sendBufferPtr, 2, stream.AsNativeArray().GetUnsafeReadOnlyPtr(), stream.Length); // Set input buffer and send, this will be seqId 3 stream.Clear(); - stream.Write((int) 9000); - var inboundBuffer = new InboundBufferVec(); - inboundBuffer.buffer1 = stream.GetNativeSlice(0, stream.Length); - inboundBuffer.buffer2 = default; + stream.WriteInt((int) 9000); + var inboundBuffer = new InboundSendBuffer(); + inboundBuffer.bufferWithHeaders = (byte*)stream.AsNativeArray().GetUnsafeReadOnlyPtr(); + inboundBuffer.bufferWithHeadersLength = stream.Length; + inboundBuffer.SetBufferFrombufferWithHeaders(); - bool needsResume = false; - bool needsUpdate = false; - reliablePipeline.Send(pipelineContext, inboundBuffer, ref needsResume, ref needsUpdate); + var stageRequest = NetworkPipelineStage.Requests.None; + reliablePipeline.Send.Ptr.Invoke(ref pipelineContext, ref inboundBuffer, ref stageRequest); // seqId 3 should now be stored in slot 0 - Assert.AreEqual(3, ReliableUtility.GetPacketInformation(sendBuffer, 3)->SequenceId); + Assert.AreEqual(3, ReliableUtility.GetPacketInformation(sendBufferPtr, 3)->SequenceId); // slots 1 and 2 should be cleared - Assert.AreEqual(-1, ReliableUtility.GetPacketInformation(sendBuffer, 1)->SequenceId); - Assert.AreEqual(-1, ReliableUtility.GetPacketInformation(sendBuffer, 2)->SequenceId); + Assert.AreEqual(-1, ReliableUtility.GetPacketInformation(sendBufferPtr, 1)->SequenceId); + Assert.AreEqual(-1, ReliableUtility.GetPacketInformation(sendBufferPtr, 2)->SequenceId); - Assert.IsFalse(needsResume); - Assert.IsTrue(needsUpdate); + Assert.AreEqual(NetworkPipelineStage.Requests.Update, stageRequest); // Verify ack packet is written correctly ReliableUtility.PacketHeader header = default; @@ -996,10 +1109,12 @@ public unsafe void Send_PacketsAreAcked_UpdateAckState() NativeArray sendBuffer = new NativeArray(processCapacity, Allocator.Persistent); NativeArray sharedBuffer = new NativeArray(sharedCapacity, Allocator.Persistent); - ReliableUtility.InitializeContext(new NativeSlice(sharedBuffer), new NativeSlice(sendBuffer), new NativeSlice(recvBuffer), parameters); + ReliableUtility.InitializeContext((byte*)sharedBuffer.GetUnsafePtr(), sharedBuffer.Length, + (byte*)sendBuffer.GetUnsafePtr(), sendBuffer.Length, (byte*)recvBuffer.GetUnsafePtr(), recvBuffer.Length, parameters); var pipelineContext = new NetworkPipelineContext - {internalProcessBuffer = sendBuffer, internalSharedProcessBuffer = sharedBuffer, timestamp = 1000}; + {internalProcessBuffer = (byte*)sendBuffer.GetUnsafePtr(), internalProcessBufferLength = sendBuffer.Length, + internalSharedProcessBuffer = (byte*)sharedBuffer.GetUnsafePtr(), internalSharedProcessBufferLength = sharedBuffer.Length, timestamp = 1000}; var sharedContext = (ReliableUtility.SharedContext*) sharedBuffer.GetUnsafePtr(); sharedContext->SentPackets.Sequence = 3; @@ -1015,70 +1130,74 @@ public unsafe void Send_PacketsAreAcked_UpdateAckState() sendContext->LastSentTime = 500; sendContext->PreviousTimestamp = 980; // 20 ms ago - var reliablePipeline = new ReliableSequencedPipelineStage(); + var reliablePipelineStage = new ReliableSequencedPipelineStage(); + var staticBuffer = new NativeArray(reliablePipelineStage.StaticSize, Allocator.Temp); + pipelineContext.staticInstanceBuffer = (byte*)staticBuffer.GetUnsafeReadOnlyPtr(); + pipelineContext.staticInstanceBufferLength = staticBuffer.Length; + var reliablePipeline = reliablePipelineStage.StaticInitialize((byte*)staticBuffer.GetUnsafeReadOnlyPtr(), staticBuffer.Length, new INetworkParameter[0]); - using (var stream = new DataStreamWriter(4, Allocator.Temp)) - using (pipelineContext.header = new DataStreamWriter(UnsafeUtility.SizeOf(), Allocator.Persistent)) + var stream = new DataStreamWriter(4, Allocator.Temp); + pipelineContext.header = new DataStreamWriter(UnsafeUtility.SizeOf(), Allocator.Temp); { // Fill window capacity, next send should then clear everything stream.Clear(); - stream.Write((int) 100); - ReliableUtility.SetPacket(sendBuffer, 0, stream.GetNativeSlice(0, stream.Length)); + stream.WriteInt((int) 100); + ReliableUtility.SetPacket((byte*)sendBuffer.GetUnsafePtr(), 0, stream.AsNativeArray().GetUnsafeReadOnlyPtr(), stream.Length); stream.Clear(); - stream.Write((int) 200); - ReliableUtility.SetPacket(sendBuffer, 1, stream.GetNativeSlice(0, stream.Length)); + stream.WriteInt((int) 200); + ReliableUtility.SetPacket((byte*)sendBuffer.GetUnsafePtr(), 1, stream.AsNativeArray().GetUnsafeReadOnlyPtr(), stream.Length); stream.Clear(); - stream.Write((int) 300); - ReliableUtility.SetPacket(sendBuffer, 2, stream.GetNativeSlice(0, stream.Length)); + stream.WriteInt((int) 300); + ReliableUtility.SetPacket((byte*)sendBuffer.GetUnsafePtr(), 2, stream.AsNativeArray().GetUnsafeReadOnlyPtr(), stream.Length); - var inboundBuffer = new InboundBufferVec(); - inboundBuffer.buffer1 = default; - inboundBuffer.buffer2 = default; + var inboundBuffer = new InboundSendBuffer(); - bool needsResume = false; - bool needsUpdate = false; - reliablePipeline.Send(pipelineContext, inboundBuffer, ref needsResume, ref needsUpdate); + var stageRequest = NetworkPipelineStage.Requests.None; + reliablePipeline.Send.Ptr.Invoke(ref pipelineContext, ref inboundBuffer, ref stageRequest); - Assert.AreEqual(-1, ReliableUtility.GetPacketInformation(sendBuffer, 0)->SequenceId); - Assert.AreEqual(-1, ReliableUtility.GetPacketInformation(sendBuffer, 1)->SequenceId); - Assert.AreEqual(-1, ReliableUtility.GetPacketInformation(sendBuffer, 2)->SequenceId); + Assert.AreEqual(-1, ReliableUtility.GetPacketInformation((byte*)sendBuffer.GetUnsafeReadOnlyPtr(), 0)->SequenceId); + Assert.AreEqual(-1, ReliableUtility.GetPacketInformation((byte*)sendBuffer.GetUnsafeReadOnlyPtr(), 1)->SequenceId); + Assert.AreEqual(-1, ReliableUtility.GetPacketInformation((byte*)sendBuffer.GetUnsafeReadOnlyPtr(), 2)->SequenceId); - Assert.IsFalse(needsResume); - Assert.IsTrue(needsUpdate); + Assert.AreEqual(NetworkPipelineStage.Requests.Update, stageRequest); } recvBuffer.Dispose(); sendBuffer.Dispose(); sharedBuffer.Dispose(); } - unsafe void GeneratePacket(int payload, ushort headerAckedId, uint headerAckMask, ushort headerSeqId, ref NativeArray sendBuffer, out NativeSlice packet) + unsafe void GeneratePacket(int payload, ushort headerAckedId, uint headerAckMask, ushort headerSeqId, ref NativeArray sendBuffer, out InboundRecvBuffer packet) { DataStreamWriter inboundStream = new DataStreamWriter(4, Allocator.Temp); - inboundStream.Write((int) payload); - InboundBufferVec data = default; - data.buffer1 = inboundStream.GetNativeSlice(0, inboundStream.Length); + inboundStream.WriteInt((int) payload); + InboundSendBuffer data = default; + data.bufferWithHeaders = (byte*)inboundStream.AsNativeArray().GetUnsafePtr(); + data.bufferWithHeadersLength = inboundStream.Length; + data.SetBufferFrombufferWithHeaders(); ReliableUtility.PacketHeader header = new ReliableUtility.PacketHeader() { AckedSequenceId = headerAckedId, AckMask = headerAckMask, SequenceId = headerSeqId }; - ReliableUtility.SetHeaderAndPacket(sendBuffer, headerSeqId, header, data, 1000); + ReliableUtility.SetHeaderAndPacket((byte*)sendBuffer.GetUnsafePtr(), headerSeqId, header, data, 1000); // Extract raw packet from the send buffer so it can be passed directly to receive var sendCtx = (ReliableUtility.Context*) sendBuffer.GetUnsafePtr(); var index = headerSeqId % sendCtx->Capacity; var offset = sendCtx->DataPtrOffset + (index * sendCtx->DataStride); - packet = new NativeSlice(sendBuffer, offset, sendCtx->DataStride); - inboundStream.Dispose(); + packet.buffer = (byte*)sendBuffer.GetUnsafeReadOnlyPtr() + offset; + packet.bufferLength = sendCtx->DataStride; } } public class QoSNetworkPipelineTest { - private LocalNetworkDriver m_ServerDriver; - private LocalNetworkDriver m_ClientDriver; + private NetworkDriver m_ServerDriver; + private NetworkDriver m_ClientDriver; + private NetworkPipelineStageId m_ReliableStageId; + private NetworkPipelineStageId m_SimulatorStageId; [SetUp] public void IPC_Setup() @@ -1088,20 +1207,21 @@ public void IPC_Setup() connectTimeoutMS = NetworkParameterConstants.ConnectTimeoutMS, maxConnectAttempts = NetworkParameterConstants.MaxConnectAttempts, disconnectTimeoutMS = 90 * 1000, - maxFrameTimeMS = 16 + fixedFrameTimeMS = 16 }; - IPCManager.Instance.Initialize(100); m_ServerDriver = - new LocalNetworkDriver(new NetworkDataStreamParameter + TestNetworkDriver.Create(new NetworkDataStreamParameter {size = 0}, timeoutParam, new ReliableUtility.Parameters { WindowSize = 32}); - m_ServerDriver.Bind(IPCManager.Instance.CreateEndPoint()); + m_ServerDriver.Bind(NetworkEndPoint.LoopbackIpv4); m_ServerDriver.Listen(); m_ClientDriver = - new LocalNetworkDriver(new NetworkDataStreamParameter + TestNetworkDriver.Create(new NetworkDataStreamParameter {size = 0}, timeoutParam, new ReliableUtility.Parameters { WindowSize = 32}, new SimulatorUtility.Parameters { MaxPacketCount = 30, MaxPacketSize = 16, PacketDelayMs = 0, /*PacketDropInterval = 8,*/ PacketDropPercentage = 10}); + m_ReliableStageId = NetworkPipelineStageCollection.GetStageId(typeof(ReliableSequencedPipelineStage)); + m_SimulatorStageId = NetworkPipelineStageCollection.GetStageId(typeof(SimulatorPipelineStage)); } [TearDown] @@ -1109,7 +1229,6 @@ public void IPC_TearDown() { m_ClientDriver.Dispose(); m_ServerDriver.Dispose(); - IPCManager.Instance.Destroy(); } [Test] @@ -1130,9 +1249,9 @@ public void NetworkPipeline_ReliableSequenced_SendRecvOnce() Assert.AreNotEqual(default(NetworkConnection), serverToClient); // Send message to client - var strm = new DataStreamWriter(4, Allocator.Temp); - strm.Write((int) 42); - m_ServerDriver.Send(serverPipe, serverToClient, strm); + var strm = m_ServerDriver.BeginSend(serverPipe, serverToClient); + strm.WriteInt((int) 42); + m_ServerDriver.EndSend(strm); m_ServerDriver.ScheduleUpdate().Complete(); // Receive incoming message from server @@ -1141,8 +1260,7 @@ public void NetworkPipeline_ReliableSequenced_SendRecvOnce() Assert.AreEqual(NetworkEvent.Type.Connect, clientToServer.PopEvent(m_ClientDriver, out readStrm)); Assert.AreEqual(NetworkEvent.Type.Data, clientToServer.PopEvent(m_ClientDriver, out readStrm)); Assert.AreEqual(4, readStrm.Length); - var readCtx = default(DataStreamReader.Context); - Assert.AreEqual(42, readStrm.ReadInt(ref readCtx)); + Assert.AreEqual(42, readStrm.ReadInt()); } [Test] @@ -1155,50 +1273,41 @@ public unsafe void NetworkPipeline_ReliableSequenced_SendRecvWithRTTCalculation( m_ServerDriver.ScheduleUpdate().Complete(); var serverToClient = m_ServerDriver.Accept(); - NativeSlice serverReceiveBuffer = default; - NativeSlice serverSendBuffer = default; - NativeSlice serverSharedBuffer = default; - m_ServerDriver.GetPipelineBuffers(serverPipe, 4, serverToClient, ref serverReceiveBuffer, ref serverSendBuffer, ref serverSharedBuffer); + m_ServerDriver.GetPipelineBuffers(serverPipe, m_ReliableStageId, serverToClient, out var serverReceiveBuffer, out var serverSendBuffer, out var serverSharedBuffer); var sharedContext = (ReliableUtility.SharedContext*) serverSharedBuffer.GetUnsafePtr(); - NativeSlice clientReceiveBuffer = default; - NativeSlice clientSendBuffer = default; - NativeSlice clientSharedBuffer = default; - m_ClientDriver.GetPipelineBuffers(clientPipe, 4, clientToServer, ref clientReceiveBuffer, ref clientSendBuffer, ref clientSharedBuffer); + m_ClientDriver.GetPipelineBuffers(clientPipe, m_ReliableStageId, clientToServer, out var clientReceiveBuffer, out var clientSendBuffer, out var clientSharedBuffer); // First the server sends a packet to the client - var strm = new DataStreamWriter(4, Allocator.Temp); - strm.Write((int) 42); - m_ServerDriver.Send(serverPipe, serverToClient, strm); + var strm = m_ServerDriver.BeginSend(serverPipe, serverToClient); + strm.WriteInt((int) 42); + m_ServerDriver.EndSend(strm); m_ServerDriver.ScheduleUpdate().Complete(); // Server sent time for the packet with seqId=0 is set - m_ServerDriver.GetPipelineBuffers(serverPipe, 4, serverToClient, ref serverReceiveBuffer, ref serverSendBuffer, ref serverSharedBuffer); - var serverPacketTimer = ReliableUtility.GetLocalPacketTimer(serverSharedBuffer, 0); + m_ServerDriver.GetPipelineBuffers(serverPipe, m_ReliableStageId, serverToClient, out serverReceiveBuffer, out serverSendBuffer, out serverSharedBuffer); + var serverPacketTimer = ReliableUtility.GetLocalPacketTimer((byte*)serverSharedBuffer.GetUnsafeReadOnlyPtr(), 0); Assert.IsTrue(serverPacketTimer->SentTime > 0); m_ClientDriver.ScheduleUpdate().Complete(); // Client received seqId=0 from server and sets the receive time - m_ClientDriver.GetPipelineBuffers(clientPipe, 4, clientToServer, ref clientReceiveBuffer, ref clientSendBuffer, ref clientSharedBuffer); - var clientPacketTimer = ReliableUtility.GetRemotePacketTimer(clientSharedBuffer, 0); + m_ClientDriver.GetPipelineBuffers(clientPipe, m_ReliableStageId, clientToServer, out clientReceiveBuffer, out clientSendBuffer, out clientSharedBuffer); + var clientPacketTimer = ReliableUtility.GetRemotePacketTimer((byte*)clientSharedBuffer.GetUnsafeReadOnlyPtr(), 0); Assert.IsTrue(clientPacketTimer->ReceiveTime >= serverPacketTimer->SentTime); DataStreamReader readStrm; Assert.AreEqual(NetworkEvent.Type.Connect, clientToServer.PopEvent(m_ClientDriver, out readStrm)); Assert.AreEqual(NetworkEvent.Type.Data, clientToServer.PopEvent(m_ClientDriver, out readStrm)); - // Force processing time to be at least 16 ms, - var timer = new Timer(); - while (timer.ElapsedMilliseconds < 16) { } // Now update client, if it's updated in the while loop it will automatically send ack packets to the server // so processing time will actually be recorded as almost 0 m_ClientDriver.ScheduleUpdate().Complete(); // Now client sends packet to the server, this should contain the ackedSeqId=0 for the servers initial packet - strm.Clear(); - strm.Write((int) 9000); - m_ClientDriver.Send(clientPipe, clientToServer, strm); + strm = m_ClientDriver.BeginSend(clientPipe, clientToServer); + strm.WriteInt((int) 9000); + m_ClientDriver.EndSend(strm); m_ClientDriver.ScheduleUpdate().Complete(); // Receive time for the server packet is 0 at this point @@ -1208,8 +1317,8 @@ public unsafe void NetworkPipeline_ReliableSequenced_SendRecvWithRTTCalculation( m_ServerDriver.ScheduleUpdate().Complete(); // Server has now received a packet from the client with ackedSeqId=0 in the header and timing info for that - Assert.IsTrue(serverPacketTimer->ReceiveTime >= clientPacketTimer->ReceiveTime); - Assert.IsTrue(serverPacketTimer->ProcessingTime >= 16); + Assert.GreaterOrEqual(serverPacketTimer->ReceiveTime, clientPacketTimer->ReceiveTime); + Assert.GreaterOrEqual(serverPacketTimer->ProcessingTime, 16); } [Test] @@ -1236,43 +1345,38 @@ public void NetworkPipeline_ReliableSequenced_SendRecvMany() for (int i = 0; i < 30; ++i) { // Send message to client - var strm = new DataStreamWriter(4, Allocator.Temp); - strm.Write((int) i); - m_ServerDriver.Send(serverPipe, serverToClient, strm); + var strm = m_ServerDriver.BeginSend(serverPipe, serverToClient); + strm.WriteInt((int) i); + m_ServerDriver.EndSend(strm); m_ServerDriver.ScheduleUpdate().Complete(); // Receive incoming message from server m_ClientDriver.ScheduleUpdate().Complete(); - var readCtx = default(DataStreamReader.Context); var result = clientToServer.PopEvent(m_ClientDriver, out readStrm); Assert.AreEqual(NetworkEvent.Type.Data, result); Assert.AreEqual(4, readStrm.Length); - Assert.AreEqual(i, readStrm.ReadInt(ref readCtx)); + Assert.AreEqual(i, readStrm.ReadInt()); // Send back a message to server - strm.Clear(); - strm.Write((int) i*100); - m_ClientDriver.Send(clientPipe, clientToServer, strm); + strm = m_ClientDriver.BeginSend(clientPipe, clientToServer); + strm.WriteInt((int) i*100); + m_ClientDriver.EndSend(strm); m_ClientDriver.ScheduleUpdate().Complete(); // Receive incoming message from client - var timer = new Timer(); - while (true) + // 100 frames = 1600ms + for (int frame = 0; frame < 100; ++frame) { m_ServerDriver.ScheduleUpdate().Complete(); - readCtx = default(DataStreamReader.Context); result = serverToClient.PopEvent(m_ServerDriver, out readStrm); if (result != NetworkEvent.Type.Empty) break; - if (timer.ElapsedMilliseconds > 1000) - break; } Assert.AreEqual(NetworkEvent.Type.Data, result); Assert.AreEqual(4, readStrm.Length); - Assert.AreEqual(i*100, readStrm.ReadInt(ref readCtx)); - strm.Dispose(); + Assert.AreEqual(i*100, readStrm.ReadInt()); } } @@ -1290,10 +1394,7 @@ public unsafe void NetworkPipeline_ReliableSequenced_SendRecvManyWithPacketDropH // Set sequence ID to a value just below wrapping over 0, also need to set last received seqId value to one // less or the first packet will be considered out of order and stored for later use - NativeSlice receiveBuffer = default; - NativeSlice sendBuffer = default; - NativeSlice sharedBuffer = default; - m_ClientDriver.GetPipelineBuffers(typeof(ReliableSequencedPipelineStage), clientToServer, ref receiveBuffer, ref sendBuffer, ref sharedBuffer); + m_ClientDriver.GetPipelineBuffers(clientPipe, m_ReliableStageId, clientToServer, out var receiveBuffer, out var sendBuffer, out var sharedBuffer); var sharedContext = (ReliableUtility.SharedContext*) sharedBuffer.GetUnsafePtr(); sharedContext->SentPackets.Sequence = ushort.MaxValue - 1; sharedContext->SentPackets.Acked = ushort.MaxValue - 2; @@ -1309,10 +1410,10 @@ public unsafe void NetworkPipeline_ReliableSequenced_SendRecvManyWithPacketDropH Assert.AreNotEqual(default(NetworkConnection), serverToClient); // This test runs fast so the minimum resend times needs to be lower (assumes 1 ms update rate) - ReliableUtility.SetMinimumResendTime(4, m_ClientDriver, clientPipe, 4, clientToServer); - ReliableUtility.SetMinimumResendTime(4, m_ServerDriver, serverPipe, 4, serverToClient); + ReliableUtility.SetMinimumResendTime(4, m_ClientDriver, clientPipe, clientToServer); + ReliableUtility.SetMinimumResendTime(4, m_ServerDriver, serverPipe, serverToClient); - m_ServerDriver.GetPipelineBuffers(typeof(ReliableSequencedPipelineStage), serverToClient, ref receiveBuffer, ref sendBuffer, ref sharedBuffer); + m_ServerDriver.GetPipelineBuffers(serverPipe, m_ReliableStageId, serverToClient, out receiveBuffer, out sendBuffer, out sharedBuffer); sharedContext = (ReliableUtility.SharedContext*) sharedBuffer.GetUnsafePtr(); sharedContext->SentPackets.Sequence = ushort.MaxValue - 1; sharedContext->SentPackets.Acked = ushort.MaxValue - 2; @@ -1346,8 +1447,8 @@ public void NetworkPipeline_ReliableSequenced_SendRecvManyWithPacketDrop() Assert.AreNotEqual(default(NetworkConnection), serverToClient); // This test runs fast so the minimum resend times needs to be lower (assumes 1 ms update rate) - ReliableUtility.SetMinimumResendTime(4, m_ClientDriver, clientPipe, 4, clientToServer); - ReliableUtility.SetMinimumResendTime(4, m_ServerDriver, serverPipe, 4, serverToClient); + ReliableUtility.SetMinimumResendTime(4, m_ClientDriver, clientPipe, clientToServer); + ReliableUtility.SetMinimumResendTime(4, m_ServerDriver, serverPipe, serverToClient); // Receive incoming message from server m_ClientDriver.ScheduleUpdate().Complete(); @@ -1367,26 +1468,21 @@ unsafe void SendAndReceiveMessages(NetworkConnection clientToServer, NetworkConn var sendMessageCount = 0; var lastClientReceivedNumber = 0; var lastServerReceivedNumber = 0; - var timer = new Timer(); - NativeSlice tmpReceiveBuffer = default; - NativeSlice tmpSendBuffer = default; - NativeSlice serverReliableBuffer = default; - NativeSlice clientReliableBuffer = default; - NativeSlice clientSimulatorBuffer = default; - m_ServerDriver.GetPipelineBuffers(typeof(ReliableSequencedPipelineStage), serverToClient, ref tmpReceiveBuffer, ref tmpSendBuffer, ref serverReliableBuffer); + int frame = 0; + m_ServerDriver.GetPipelineBuffers(serverPipe, m_ReliableStageId, serverToClient, out var tmpReceiveBuffer, out var tmpSendBuffer, out var serverReliableBuffer); var serverReliableCtx = (ReliableUtility.SharedContext*) serverReliableBuffer.GetUnsafePtr(); - m_ClientDriver.GetPipelineBuffers(typeof(ReliableSequencedPipelineStage), clientToServer, ref tmpReceiveBuffer, ref tmpSendBuffer, ref clientReliableBuffer); + m_ClientDriver.GetPipelineBuffers(clientPipe, m_ReliableStageId, clientToServer, out tmpReceiveBuffer, out tmpSendBuffer, out var clientReliableBuffer); var clientReliableCtx = (ReliableUtility.SharedContext*) clientReliableBuffer.GetUnsafePtr(); - m_ClientDriver.GetPipelineBuffers(typeof(SimulatorPipelineStage), clientToServer, ref tmpReceiveBuffer, ref tmpSendBuffer, ref clientSimulatorBuffer); + m_ClientDriver.GetPipelineBuffers(clientPipe, m_SimulatorStageId, clientToServer, out tmpReceiveBuffer, out tmpSendBuffer, out var clientSimulatorBuffer); var clientSimulatorCtx = (SimulatorUtility.Context*) clientSimulatorBuffer.GetUnsafePtr(); // Client is the one dropping packets, so wait for that count to reach total, server receive count will be higher while (lastClientReceivedNumber < totalMessageCount) { // Send message to client sendMessageCount++; - var strm = new DataStreamWriter(4, Allocator.Temp); - strm.Write((int) sendMessageCount); - m_ServerDriver.Send(serverPipe, serverToClient, strm); + var strm = m_ServerDriver.BeginSend(serverPipe, serverToClient); + strm.WriteInt((int) sendMessageCount); + m_ServerDriver.EndSend(strm); if (serverReliableCtx->errorCode != 0) { UnityEngine.Debug.Log("Reliability stats\nPacketsDropped: " + serverReliableCtx->stats.PacketsDropped + "\n" + @@ -1400,48 +1496,43 @@ unsafe void SendAndReceiveMessages(NetworkConnection clientToServer, NetworkConn } m_ServerDriver.ScheduleUpdate().Complete(); - var readCtx = default(DataStreamReader.Context); NetworkEvent.Type result; // Receive incoming message from server, might be empty but we still need to keep // sending or else a resend for a dropped packet will not happen m_ClientDriver.ScheduleUpdate().Complete(); - readCtx = default(DataStreamReader.Context); result = clientToServer.PopEvent(m_ClientDriver, out readStrm); Assert.AreEqual(m_ClientDriver.ReceiveErrorCode, 0); Assert.AreEqual((ReliableUtility.ErrorCodes)0, clientReliableCtx->errorCode); while (result != NetworkEvent.Type.Empty) { Assert.AreEqual(4, readStrm.Length); - var read = readStrm.ReadInt(ref readCtx); + var read = readStrm.ReadInt(); // We should be receiving in order, so last payload should be one more than the current receive count Assert.AreEqual(lastClientReceivedNumber + 1, read); lastClientReceivedNumber = read; // Pop all events which might be pending (in case of dropped packet it should contain all the other packets already up to latest) - readCtx = default(DataStreamReader.Context); result = clientToServer.PopEvent(m_ClientDriver, out readStrm); Assert.AreEqual((ReliableUtility.ErrorCodes)0, clientReliableCtx->errorCode); } // Send back a message to server - strm.Clear(); - strm.Write((int) sendMessageCount * 100); - m_ClientDriver.Send(clientPipe, clientToServer, strm); + strm = m_ClientDriver.BeginSend(clientPipe, clientToServer); + strm.WriteInt((int) sendMessageCount * 100); + m_ClientDriver.EndSend(strm); Assert.AreEqual((ReliableUtility.ErrorCodes)0, clientReliableCtx->errorCode); m_ClientDriver.ScheduleUpdate().Complete(); // Receive incoming message from client m_ServerDriver.ScheduleUpdate().Complete(); - readCtx = default(DataStreamReader.Context); result = serverToClient.PopEvent(m_ServerDriver, out readStrm); Assert.AreEqual(m_ServerDriver.ReceiveErrorCode, 0); Assert.AreEqual((ReliableUtility.ErrorCodes)0, serverReliableCtx->errorCode); while (result != NetworkEvent.Type.Empty) { Assert.AreEqual(4, readStrm.Length); - var read = readStrm.ReadInt(ref readCtx); + var read = readStrm.ReadInt(); Assert.AreEqual(lastServerReceivedNumber + 100, read); lastServerReceivedNumber = read; - readCtx = default(DataStreamReader.Context); result = clientToServer.PopEvent(m_ClientDriver, out readStrm); Assert.AreEqual((ReliableUtility.ErrorCodes)0, serverReliableCtx->errorCode); } @@ -1451,10 +1542,9 @@ unsafe void SendAndReceiveMessages(NetworkConnection clientToServer, NetworkConn //Assert.AreEqual(0, clientReliableCtx->stats.PacketsDuplicated); Assert.AreEqual(0, clientReliableCtx->stats.PacketsStale); - if (timer.ElapsedMilliseconds > 1000) + if (frame > 100) Assert.Fail("Test timeout, didn't receive all messages (" + totalMessageCount + ")"); - - strm.Dispose(); + ++frame; } var stats = serverReliableCtx->stats; @@ -1499,9 +1589,9 @@ public void NetworkPipeline_UnreliableSequenced_SendRecvOnce() Assert.AreNotEqual(default(NetworkConnection), serverToClient); // Send message to client - var strm = new DataStreamWriter(4, Allocator.Temp); - strm.Write((int) 42); - m_ServerDriver.Send(serverPipe, serverToClient, strm); + var strm = m_ServerDriver.BeginSend(serverPipe, serverToClient); + strm.WriteInt((int) 42); + m_ServerDriver.EndSend(strm); m_ServerDriver.ScheduleUpdate().Complete(); // Receive incoming message from server @@ -1510,8 +1600,7 @@ public void NetworkPipeline_UnreliableSequenced_SendRecvOnce() Assert.AreEqual(NetworkEvent.Type.Connect, clientToServer.PopEvent(m_ClientDriver, out readStrm)); Assert.AreEqual(NetworkEvent.Type.Data, clientToServer.PopEvent(m_ClientDriver, out readStrm)); Assert.AreEqual(4, readStrm.Length); - var readCtx = default(DataStreamReader.Context); - Assert.AreEqual(42, readStrm.ReadInt(ref readCtx)); + Assert.AreEqual(42, readStrm.ReadInt()); } [Test] @@ -1541,15 +1630,11 @@ public unsafe void NetworkPipeline_ReliableSequenced_ClientSendsNothing() var totalMessageCount = 100; var sendMessageCount = 0; var lastClientReceivedNumber = 0; - var timer = new Timer(); - NativeSlice tmpReceiveBuffer = default; - NativeSlice tmpSendBuffer = default; - NativeSlice serverReliableBuffer = default; - NativeSlice clientReliableBuffer = default; + int frame = 0; - m_ServerDriver.GetPipelineBuffers(typeof(ReliableSequencedPipelineStage), serverToClient, ref tmpReceiveBuffer, ref tmpSendBuffer, ref serverReliableBuffer); + m_ServerDriver.GetPipelineBuffers(serverPipe, m_ReliableStageId, serverToClient, out var tmpReceiveBuffer, out var tmpSendBuffer, out var serverReliableBuffer); var serverReliableCtx = (ReliableUtility.SharedContext*) serverReliableBuffer.GetUnsafePtr(); - m_ClientDriver.GetPipelineBuffers(typeof(ReliableSequencedPipelineStage), clientToServer, ref tmpReceiveBuffer, ref tmpSendBuffer, ref clientReliableBuffer); + m_ClientDriver.GetPipelineBuffers(clientPipe, m_ReliableStageId, clientToServer, out tmpReceiveBuffer, out tmpSendBuffer, out var clientReliableBuffer); var clientReliableCtx = (ReliableUtility.SharedContext*) clientReliableBuffer.GetUnsafePtr(); // Finish when client has received all messages from server without errors @@ -1557,29 +1642,26 @@ public unsafe void NetworkPipeline_ReliableSequenced_ClientSendsNothing() { // Send message to client sendMessageCount++; - var strm = new DataStreamWriter(4, Allocator.Temp); - strm.Write((int) sendMessageCount); - m_ServerDriver.Send(serverPipe, serverToClient, strm); + var strm = m_ServerDriver.BeginSend(serverPipe, serverToClient); + strm.WriteInt((int) sendMessageCount); + m_ServerDriver.EndSend(strm); Assert.AreEqual((ReliableUtility.ErrorCodes)0, serverReliableCtx->errorCode); m_ServerDriver.ScheduleUpdate().Complete(); - var readCtx = default(DataStreamReader.Context); NetworkEvent.Type result; // Receive incoming message from server, might be empty or might be more than one message m_ClientDriver.ScheduleUpdate().Complete(); - readCtx = default(DataStreamReader.Context); result = clientToServer.PopEvent(m_ClientDriver, out readStrm); Assert.AreEqual(m_ClientDriver.ReceiveErrorCode, 0); Assert.AreEqual((ReliableUtility.ErrorCodes)0, clientReliableCtx->errorCode); while (result != NetworkEvent.Type.Empty) { Assert.AreEqual(4, readStrm.Length); - var read = readStrm.ReadInt(ref readCtx); + var read = readStrm.ReadInt(); // We should be receiving in order, so last payload should be one more than the current receive count Assert.AreEqual(lastClientReceivedNumber + 1, read); lastClientReceivedNumber = read; // Pop all events which might be pending (in case of dropped packet it should contain all the other packets already up to latest) - readCtx = default(DataStreamReader.Context); result = clientToServer.PopEvent(m_ClientDriver, out readStrm); Assert.AreEqual((ReliableUtility.ErrorCodes)0, clientReliableCtx->errorCode); } @@ -1589,15 +1671,13 @@ public unsafe void NetworkPipeline_ReliableSequenced_ClientSendsNothing() // Make sure no event has arrived on server and no errors seen m_ServerDriver.ScheduleUpdate().Complete(); - readCtx = default(DataStreamReader.Context); Assert.AreEqual(serverToClient.PopEvent(m_ServerDriver, out readStrm), NetworkEvent.Type.Empty); Assert.AreEqual(m_ServerDriver.ReceiveErrorCode, 0); Assert.AreEqual((ReliableUtility.ErrorCodes)0, serverReliableCtx->errorCode); - if (timer.ElapsedMilliseconds > 1000) + if (frame > 100) Assert.Fail("Test timeout, didn't receive all messages (" + totalMessageCount + ")"); - - strm.Dispose(); + ++frame; } // The empty ack packets will bump the PacketsSent count, also in this test it can happen that a duplicate @@ -1616,17 +1696,17 @@ public unsafe void NetworkPipeline_ReliableSequenced_NothingIsSentAfterPingPong( connectTimeoutMS = NetworkParameterConstants.ConnectTimeoutMS, maxConnectAttempts = NetworkParameterConstants.MaxConnectAttempts, disconnectTimeoutMS = 90 * 1000, - maxFrameTimeMS = 16 + fixedFrameTimeMS = 16 }; m_ServerDriver = - new LocalNetworkDriver(new NetworkDataStreamParameter + TestNetworkDriver.Create(new NetworkDataStreamParameter {size = 0}, timeoutParam, new ReliableUtility.Parameters { WindowSize = 32}, new SimulatorUtility.Parameters { MaxPacketCount = 30, MaxPacketSize = 16, PacketDelayMs = 0, PacketDropPercentage = 0}); - m_ServerDriver.Bind(IPCManager.Instance.CreateEndPoint()); + m_ServerDriver.Bind(NetworkEndPoint.LoopbackIpv4); m_ServerDriver.Listen(); m_ClientDriver = - new LocalNetworkDriver(new NetworkDataStreamParameter + TestNetworkDriver.Create(new NetworkDataStreamParameter {size = 0}, timeoutParam, new ReliableUtility.Parameters { WindowSize = 32}, new SimulatorUtility.Parameters { MaxPacketCount = 30, MaxPacketSize = 16, PacketDelayMs = 0, PacketDropPercentage = 0}); @@ -1643,36 +1723,34 @@ public unsafe void NetworkPipeline_ReliableSequenced_NothingIsSentAfterPingPong( Assert.AreEqual(NetworkEvent.Type.Connect, clientToServer.PopEvent(m_ClientDriver, out readStrm)); // Perform ping pong transmision - var strm = new DataStreamWriter(4, Allocator.Temp); - strm.Write((int) 100); + var strm = m_ServerDriver.BeginSend(serverPipe, serverToClient); + strm.WriteInt((int) 100); Console.WriteLine("Server send"); - m_ServerDriver.Send(serverPipe, serverToClient, strm); + m_ServerDriver.EndSend(strm); m_ServerDriver.ScheduleUpdate().Complete(); Console.WriteLine("Client update"); m_ClientDriver.ScheduleUpdate().Complete(); Assert.AreEqual(NetworkEvent.Type.Data, clientToServer.PopEvent(m_ClientDriver, out readStrm)); - strm.Clear(); - strm.Write((int) 200); + strm = m_ClientDriver.BeginSend(clientPipe, clientToServer); + strm.WriteInt((int) 200); Console.WriteLine("Client send"); - m_ClientDriver.Send(clientPipe, clientToServer, strm); + m_ClientDriver.EndSend(strm); m_ClientDriver.ScheduleUpdate().Complete(); Console.WriteLine("Server update"); m_ServerDriver.ScheduleUpdate().Complete(); Assert.AreEqual(NetworkEvent.Type.Data, serverToClient.PopEvent(m_ServerDriver, out readStrm)); // Check how many packets have been sent so far - NativeSlice tmpReceiveBuffer = default; - NativeSlice tmpSendBuffer = default; - NativeSlice simulatorBuffer = default; - m_ClientDriver.GetPipelineBuffers(typeof(SimulatorPipelineStage), clientToServer, ref tmpReceiveBuffer, ref tmpSendBuffer, ref simulatorBuffer); + m_ClientDriver.GetPipelineBuffers(clientPipe, m_SimulatorStageId, clientToServer, out var tmpReceiveBuffer, out var tmpSendBuffer, out var simulatorBuffer); var simulatorCtx = (SimulatorUtility.Context*) simulatorBuffer.GetUnsafePtr(); - // Do a loop and make sure nothing is being sent between client and server - var timer = new Timer(); - while (timer.ElapsedMilliseconds < 1000) + // Do a loop and make sure nothing is being sent between client and server - 100 frames at 16ms = 1600ms + for (int iter = 0; iter < 100; ++iter) { m_ServerDriver.ScheduleUpdate().Complete(); m_ClientDriver.ScheduleUpdate().Complete(); + Assert.AreEqual(NetworkEvent.Type.Empty, serverToClient.PopEvent(m_ServerDriver, out readStrm)); + Assert.AreEqual(NetworkEvent.Type.Empty, clientToServer.PopEvent(m_ClientDriver, out readStrm)); } // The client simulator counts all packets which pass through the pipeline so will catch anything the @@ -1680,7 +1758,7 @@ public unsafe void NetworkPipeline_ReliableSequenced_NothingIsSentAfterPingPong( Assert.AreEqual(2, simulatorCtx->PacketCount); // Check server side as well, server only has one packet as the client included it's ack in the pong packet it sent - m_ServerDriver.GetPipelineBuffers(typeof(SimulatorPipelineStage), clientToServer, ref tmpReceiveBuffer, ref tmpSendBuffer, ref simulatorBuffer); + m_ServerDriver.GetPipelineBuffers(serverPipe, m_SimulatorStageId, serverToClient, out tmpReceiveBuffer, out tmpSendBuffer, out simulatorBuffer); simulatorCtx = (SimulatorUtility.Context*) simulatorBuffer.GetUnsafePtr(); Assert.AreEqual(1, simulatorCtx->PacketCount); } @@ -1695,15 +1773,15 @@ public unsafe void NetworkPipeline_ReliableSequenced_IdleAfterPacketDrop() connectTimeoutMS = NetworkParameterConstants.ConnectTimeoutMS, maxConnectAttempts = NetworkParameterConstants.MaxConnectAttempts, disconnectTimeoutMS = 90 * 1000, - maxFrameTimeMS = 16 + fixedFrameTimeMS = 16 }; m_ClientDriver = - new LocalNetworkDriver(new NetworkDataStreamParameter + TestNetworkDriver.Create(new NetworkDataStreamParameter {size = 0}, timeoutParam, new ReliableUtility.Parameters { WindowSize = 32}, new SimulatorUtility.Parameters { MaxPacketCount = 30, MaxPacketSize = 16, PacketDelayMs = 0, PacketDropInterval = 10}); - m_ClientDriver.CreatePipeline(typeof(ReliableSequencedPipelineStage), typeof(SimulatorPipelineStage)); + var clientPipe = m_ClientDriver.CreatePipeline(typeof(ReliableSequencedPipelineStage), typeof(SimulatorPipelineStage)); var serverPipe = m_ServerDriver.CreatePipeline(typeof(ReliableSequencedPipelineStage), typeof(SimulatorPipelineStage)); var clientToServer = m_ClientDriver.Connect(m_ServerDriver.LocalEndPoint()); m_ClientDriver.ScheduleUpdate().Complete(); @@ -1715,17 +1793,17 @@ public unsafe void NetworkPipeline_ReliableSequenced_IdleAfterPacketDrop() Assert.AreEqual(NetworkEvent.Type.Connect, clientToServer.PopEvent(m_ClientDriver, out readStrm)); // Server sends one packet, this will be dropped, client has empty event - var strm = new DataStreamWriter(4, Allocator.Temp); - strm.Write((int) 100); - m_ServerDriver.Send(serverPipe, serverToClient, strm); + var strm = m_ServerDriver.BeginSend(serverPipe, serverToClient); + strm.WriteInt((int) 100); + m_ServerDriver.EndSend(strm); m_ServerDriver.ScheduleUpdate().Complete(); m_ClientDriver.ScheduleUpdate().Complete(); Assert.AreEqual(NetworkEvent.Type.Empty, clientToServer.PopEvent(m_ClientDriver, out readStrm)); // Wait until client receives the server packet resend - var timer = new Timer(); var clientEvent = NetworkEvent.Type.Empty; - while (timer.ElapsedMilliseconds < 1000) + // 100 frames = 1600ms + for (int frame = 0; frame < 100; ++frame) { m_ClientDriver.ScheduleUpdate().Complete(); m_ServerDriver.ScheduleUpdate().Complete(); @@ -1736,10 +1814,7 @@ public unsafe void NetworkPipeline_ReliableSequenced_IdleAfterPacketDrop() Assert.AreEqual(NetworkEvent.Type.Data, clientEvent); // Verify exactly one packet has been dropped - NativeSlice tmpReceiveBuffer = default; - NativeSlice tmpSendBuffer = default; - NativeSlice simulatorBuffer = default; - m_ClientDriver.GetPipelineBuffers(typeof(SimulatorPipelineStage), clientToServer, ref tmpReceiveBuffer, ref tmpSendBuffer, ref simulatorBuffer); + m_ClientDriver.GetPipelineBuffers(clientPipe, m_SimulatorStageId, clientToServer, out var tmpReceiveBuffer, out var tmpSendBuffer, out var simulatorBuffer); var simulatorCtx = (SimulatorUtility.Context*) simulatorBuffer.GetUnsafePtr(); Assert.AreEqual(simulatorCtx->PacketDropCount, 1); } diff --git a/Tests/Editor/TestNetworkDriver.cs b/Tests/Editor/TestNetworkDriver.cs new file mode 100644 index 0000000..8b6e13c --- /dev/null +++ b/Tests/Editor/TestNetworkDriver.cs @@ -0,0 +1,10 @@ +namespace Unity.Networking.Transport.Tests +{ + public static class TestNetworkDriver + { + public static NetworkDriver Create(params INetworkParameter[] param) + { + return new NetworkDriver(new IPCNetworkInterface(), param); + } + } +} \ No newline at end of file diff --git a/Tests/Editor/TestNetworkDriver.cs.meta b/Tests/Editor/TestNetworkDriver.cs.meta new file mode 100644 index 0000000..431eae0 --- /dev/null +++ b/Tests/Editor/TestNetworkDriver.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4a770922f872a4df396192d6c2a77ab6 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Tests/Editor/UtilitiesTests.cs b/Tests/Editor/UtilitiesTests.cs index 848e05d..c863681 100644 --- a/Tests/Editor/UtilitiesTests.cs +++ b/Tests/Editor/UtilitiesTests.cs @@ -1,21 +1,14 @@ -using System; using NUnit.Framework; using Unity.Networking.Transport.Utilities; +using Unity.Networking.Transport.Utilities.LowLevel.Unsafe; +using Unity.Jobs; +using Unity.Collections; +using System.Collections.Generic; namespace Unity.Networking.Transport.Tests { - public class NativeMultiQueue_Tests + public class NetworkUtilities_Tests { - [SetUp] - public void Setup() - { - } - - [TearDown] - public void TearDown() - { - } - [Test] public void NativeMultiQueue_SimpleScenarios() { @@ -47,5 +40,40 @@ public void NativeMultiQueue_SimpleScenarios() } } } + + struct FreeJob : IJobParallelFor + { + public UnsafeAtomicFreeList freeList; + public void Execute(int i) + { + var indices = new NativeArray(100, Allocator.Temp); + for (int asd = 0; asd < indices.Length; ++asd) + indices[asd] = freeList.Pop(); + for (int asd = 0; asd < indices.Length; ++asd) + { + if (indices[asd] >= 0) + freeList.Push(indices[asd]); + } + } + } + + [Test] + //[Repeat( 25 )] + public void AtomicFreeList() + { + using (var freeList = new UnsafeAtomicFreeList(1024, Allocator.Persistent)) + { + var job = new FreeJob {freeList = freeList}; + job.Schedule(1024*100, 1).Complete(); + var foo = new HashSet(); + for (int i = 0; i < freeList.Capacity; ++i) + { + var idx = freeList.Pop(); + Assert.IsTrue(idx < 1024 && idx >= 0); + Assert.IsFalse(foo.Contains(idx)); + } + Assert.AreEqual(-1, freeList.Pop()); + } + } } } \ No newline at end of file diff --git a/Tests/Runtime/ServerAndClientTests.cs b/Tests/Runtime/ServerAndClientTests.cs index f429a6f..30663b7 100644 --- a/Tests/Runtime/ServerAndClientTests.cs +++ b/Tests/Runtime/ServerAndClientTests.cs @@ -1,19 +1,19 @@ using System.Collections; using Unity.Collections; using NUnit.Framework; -using System; using UnityEngine.TestTools; using UnityEngine; using Unity.Networking.Transport; +using Unity.Networking.Transport.Protocols; namespace Tests { public class ServerAndClientTests { - UdpNetworkDriver server_driver; + NetworkDriver server_driver; NetworkConnection connectionToClient; - UdpNetworkDriver client_driver; + NetworkDriver client_driver; NetworkConnection clientToServerConnection; NetworkEvent.Type ev; @@ -22,14 +22,14 @@ public class ServerAndClientTests public void SetupServerAndClientAndConnectThem(int bufferSize) { //setup server - server_driver = new UdpNetworkDriver(new NetworkDataStreamParameter { size = bufferSize }); + server_driver = NetworkDriver.Create(new NetworkDataStreamParameter { size = bufferSize }); NetworkEndPoint server_endpoint = NetworkEndPoint.LoopbackIpv4; server_endpoint.Port = 1337; server_driver.Bind(server_endpoint); server_driver.Listen(); //setup client - client_driver = new UdpNetworkDriver(new NetworkDataStreamParameter { size = bufferSize }); + client_driver = NetworkDriver.Create(new NetworkDataStreamParameter { size = bufferSize }); clientToServerConnection = client_driver.Connect(server_endpoint); //update drivers @@ -45,7 +45,7 @@ public void SetupServerAndClientAndConnectThem(int bufferSize) client_driver.ScheduleUpdate().Complete(); ev = clientToServerConnection.PopEvent(client_driver, out stream); - Assert.IsTrue(ev == NetworkEvent.Type.Connect, "NetworkEvent should have Type.Connect on the client"); + Assert.IsTrue(ev == NetworkEvent.Type.Connect, "NetworkEvent should have Type.Connect on the client"); } public void DisconnectAndCleanup() @@ -76,12 +76,12 @@ public IEnumerator ServerAnd5Clients_Connect_Successfully() { int numberOfClients = 5; NativeArray connectionToClientArray; - UdpNetworkDriver[] client_driversArray = new UdpNetworkDriver[numberOfClients]; + NetworkDriver[] client_driversArray = new NetworkDriver[numberOfClients]; NativeArray clientToServerConnectionsArray; //setup server connectionToClientArray = new NativeArray(numberOfClients, Allocator.Persistent); - server_driver = new UdpNetworkDriver(new NetworkDataStreamParameter { size = 0 }); + server_driver = NetworkDriver.Create(new NetworkDataStreamParameter { size = 0 }); NetworkEndPoint server_endpoint = NetworkEndPoint.LoopbackIpv4; server_endpoint.Port = 1337; server_driver.Bind(server_endpoint); @@ -92,7 +92,7 @@ public IEnumerator ServerAnd5Clients_Connect_Successfully() for (int i = 0; i < numberOfClients; i++) { - client_driversArray[i] = new UdpNetworkDriver(new NetworkDataStreamParameter { size = 0 }); + client_driversArray[i] = NetworkDriver.Create(new NetworkDataStreamParameter { size = 0 }); clientToServerConnectionsArray[i] = client_driversArray[i].Connect(server_endpoint); } @@ -144,17 +144,18 @@ public IEnumerator ServerAndClient_PingPong_Successfully() SetupServerAndClientAndConnectThem(0); //send data from client - DataStreamWriter m_OutStream = new DataStreamWriter(16, Allocator.Persistent); + DataStreamWriter m_OutStream = client_driver.BeginSend(clientToServerConnection); m_OutStream.Clear(); - m_OutStream.Write(SharedConstants.ping); - clientToServerConnection.Send(client_driver, m_OutStream); + m_OutStream.WriteBytes(new NativeArray(SharedConstants.ping, Allocator.Temp)); + client_driver.EndSend(m_OutStream); + client_driver.ScheduleFlushSend(default).Complete(); //handle sent data server_driver.ScheduleUpdate().Complete(); ev = server_driver.PopEventForConnection(connectionToClient, out stream); Assert.IsTrue(ev == NetworkEvent.Type.Data, "Expected to get Type.Data"); - var readerCtx = default(DataStreamReader.Context); - var msg = stream.ReadBytesAsArray(ref readerCtx, stream.Length); + var msg = new NativeArray(stream.Length, Allocator.Temp); + stream.ReadBytes(msg); if (msg.Length == SharedConstants.ping.Length) { for (var i = 0; i < msg.Length; i++) @@ -169,18 +170,17 @@ public IEnumerator ServerAndClient_PingPong_Successfully() client_driver.ScheduleUpdate().Complete(); //send data from server - m_OutStream.Clear(); - m_OutStream.Write(SharedConstants.pong); - connectionToClient.Send(server_driver, m_OutStream); - m_OutStream.Dispose(); + m_OutStream = server_driver.BeginSend(connectionToClient); + m_OutStream.WriteBytes(new NativeArray(SharedConstants.pong, Allocator.Temp)); + server_driver.EndSend(m_OutStream); //handle sent data server_driver.ScheduleUpdate().Complete(); client_driver.ScheduleUpdate().Complete(); ev = clientToServerConnection.PopEvent(client_driver, out stream); Assert.IsTrue(ev == NetworkEvent.Type.Data, "Expected to get Type.Data"); - readerCtx = default(DataStreamReader.Context); - msg = stream.ReadBytesAsArray(ref readerCtx, stream.Length); + msg = new NativeArray(stream.Length, Allocator.Temp); + stream.ReadBytes(msg); if (msg.Length == SharedConstants.pong.Length) { for (var i = 0; i < msg.Length; i++) @@ -203,13 +203,13 @@ public IEnumerator ServerAndClient_SendBigMessage_OverflowsIncomingDriverBuffer( SetupServerAndClientAndConnectThem(8); //send data from client - DataStreamWriter m_OutStream = new DataStreamWriter(16, Allocator.Persistent); + DataStreamWriter m_OutStream = client_driver.BeginSend(clientToServerConnection); m_OutStream.Clear(); - m_OutStream.Write(SharedConstants.ping); - clientToServerConnection.Send(client_driver, m_OutStream); - - LogAssert.Expect(LogType.Error, "Error on receive 10040"); - m_OutStream.Dispose(); + m_OutStream.WriteBytes(new NativeArray(SharedConstants.ping, Allocator.Temp)); + client_driver.EndSend(m_OutStream); + client_driver.ScheduleFlushSend(default).Complete(); + + LogAssert.Expect(LogType.Error, "Error on receive 10040"); //handle sent data server_driver.ScheduleUpdate().Complete(); @@ -227,24 +227,23 @@ public IEnumerator ServerAndClient_SendMessageWithMaxLength_SentAndReceivedWitho SetupServerAndClientAndConnectThem(0); //send data from client - DataStreamWriter m_OutStream = new DataStreamWriter(1500, Allocator.Persistent); - m_OutStream.Clear(); - int messageLength = 1400; - byte[] messageToSend = new byte[messageLength]; + DataStreamWriter m_OutStream = client_driver.BeginSend(clientToServerConnection); + int messageLength = 1400-UdpCHeader.Length; + var messageToSend = new NativeArray(messageLength, Allocator.Temp); for (int i = 0; i < messageLength; i++) { messageToSend[i] = (byte)(33 + (i % 93)); } - m_OutStream.Write(messageToSend); - clientToServerConnection.Send(client_driver, m_OutStream); - m_OutStream.Dispose(); + m_OutStream.WriteBytes(messageToSend); + client_driver.EndSend(m_OutStream); + client_driver.ScheduleFlushSend(default).Complete(); server_driver.ScheduleUpdate().Complete(); ev = server_driver.PopEventForConnection(connectionToClient, out stream); Assert.IsTrue(ev == NetworkEvent.Type.Data, "Expected to get Type.Data"); - var readerCtx = default(DataStreamReader.Context); - var msg = stream.ReadBytesAsArray(ref readerCtx, stream.Length); + var msg = new NativeArray(stream.Length, Allocator.Temp); + stream.ReadBytes(msg); Assert.IsTrue(msg.Length == messageLength, "Lenghts of sent and received messages are different"); DisconnectAndCleanup(); @@ -257,25 +256,26 @@ public IEnumerator ServerAndClient_SendMessageWithMoreThenMaxLength_OverflowsInc SetupServerAndClientAndConnectThem(0); //send data from client - DataStreamWriter m_OutStream = new DataStreamWriter(1500, Allocator.Persistent); + DataStreamWriter m_OutStream = client_driver.BeginSend(clientToServerConnection); m_OutStream.Clear(); - int messageLength = 1401; - byte[] messageToSend = new byte[messageLength]; + int messageLength = 1401-UdpCHeader.Length; + var messageToSend = new NativeArray(messageLength, Allocator.Temp); for (int i = 0; i < messageLength; i++) { messageToSend[i] = (byte)(33 + (i % 93)); } - m_OutStream.Write(messageToSend); - clientToServerConnection.Send(client_driver, m_OutStream); - LogAssert.Expect(LogType.Error, "Error on receive 10040"); - m_OutStream.Dispose(); + Assert.IsFalse(m_OutStream.WriteBytes(messageToSend)); + Assert.AreEqual(0, client_driver.EndSend(m_OutStream)); + client_driver.ScheduleFlushSend(default).Complete(); //handle sent data server_driver.ScheduleUpdate().Complete(); client_driver.ScheduleUpdate().Complete(); - Assert.AreEqual(10040, server_driver.ReceiveErrorCode); + ev = server_driver.PopEventForConnection(connectionToClient, out stream); + Assert.IsTrue(ev == NetworkEvent.Type.Data, "Expected to get Type.Empty"); + Assert.IsFalse(stream.IsCreated); DisconnectAndCleanup(); yield return null; @@ -287,11 +287,10 @@ public IEnumerator ServerAndClient_SendMessageWithoutReadingIt_GivesErrorOnDrive SetupServerAndClientAndConnectThem(0); //send data from client - DataStreamWriter m_OutStream = new DataStreamWriter(16, Allocator.Persistent); - m_OutStream.Clear(); - m_OutStream.Write(SharedConstants.ping); - clientToServerConnection.Send(client_driver, m_OutStream); - m_OutStream.Dispose(); + DataStreamWriter m_OutStream = client_driver.BeginSend(clientToServerConnection); + m_OutStream.WriteBytes(new NativeArray(SharedConstants.ping, Allocator.Temp)); + client_driver.EndSend(m_OutStream); + client_driver.ScheduleFlushSend(default).Complete(); server_driver.ScheduleUpdate().Complete(); client_driver.ScheduleUpdate().Complete(); diff --git a/package.json b/package.json index 261e9a8..f984afc 100644 --- a/package.json +++ b/package.json @@ -1,18 +1,19 @@ { "name": "com.unity.transport", "displayName": "Unity Transport", - "version": "0.2.3-preview.0", + "version": "0.3.0-preview.6", "unity": "2019.3", "unityRelease": "0b11", "description": "Unity network transport layer - the low-level interface for sending UDP data", "dependencies": { - "com.unity.burst": "1.2.0-preview.10", - "com.unity.collections": "0.3.0-preview.0", + "com.unity.burst": "1.3.0-preview.3", + "com.unity.collections": "0.5.2-preview.8", "com.unity.mathematics": "1.1.0" }, "repository": { + "footprint": "d877cabe46cdf275d2e2a771121f8b4a15c740e7", "type": "git", - "url": "git@github.com:Unity-Technologies/netcode.git", - "revision": "2c437c67f30b8dd7b9a6848a979abded4b5ffd3b" + "url": "https://github.com/Unity-Technologies/netcode.git", + "revision": "e057ccf4fb0b19bbd5e6ea8615a121abcfc49fbb" } }