From: js6pak Date: Sun, 18 Dec 2022 13:59:22 +0000 (+0100) Subject: Get rid of the dependency on Impostor.Api X-Git-Tag: 1.0.0^2~2 X-Git-Url: https://git.deb.at/?a=commitdiff_plain;h=a9a5b408526d54d9b0833eb01a7248b8f3bd2960;p=rhonda%2Fimpostor.hazel.git Get rid of the dependency on Impostor.Api --- diff --git a/Hazel.sln b/Hazel.sln index e18d32f..20d3480 100644 --- a/Hazel.sln +++ b/Hazel.sln @@ -5,7 +5,9 @@ VisualStudioVersion = 16.0.30523.141 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Hazel", "Hazel\Hazel.csproj", "{02CFBD30-D77D-400F-94B2-700F60EFDD7F}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Impostor.Api", "..\Impostor.Api\Impostor.Api.csproj", "{9932ED5B-4B76-451D-84C6-BE060DF1FD20}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Impostor.Hazel.Abstractions", "Impostor.Hazel.Abstractions\Impostor.Hazel.Abstractions.csproj", "{51FD3233-44D5-43BC-A618-A381703EDAD3}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Impostor.Hazel.Tests", "Impostor.Hazel.Tests\Impostor.Hazel.Tests.csproj", "{6CC1050D-4142-454E-BA09-974DF77D2C10}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -17,10 +19,14 @@ Global {02CFBD30-D77D-400F-94B2-700F60EFDD7F}.Debug|Any CPU.Build.0 = Debug|Any CPU {02CFBD30-D77D-400F-94B2-700F60EFDD7F}.Release|Any CPU.ActiveCfg = Release|Any CPU {02CFBD30-D77D-400F-94B2-700F60EFDD7F}.Release|Any CPU.Build.0 = Release|Any CPU - {9932ED5B-4B76-451D-84C6-BE060DF1FD20}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {9932ED5B-4B76-451D-84C6-BE060DF1FD20}.Debug|Any CPU.Build.0 = Debug|Any CPU - {9932ED5B-4B76-451D-84C6-BE060DF1FD20}.Release|Any CPU.ActiveCfg = Release|Any CPU - {9932ED5B-4B76-451D-84C6-BE060DF1FD20}.Release|Any CPU.Build.0 = Release|Any CPU + {51FD3233-44D5-43BC-A618-A381703EDAD3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {51FD3233-44D5-43BC-A618-A381703EDAD3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {51FD3233-44D5-43BC-A618-A381703EDAD3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {51FD3233-44D5-43BC-A618-A381703EDAD3}.Release|Any CPU.Build.0 = Release|Any CPU + {6CC1050D-4142-454E-BA09-974DF77D2C10}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6CC1050D-4142-454E-BA09-974DF77D2C10}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6CC1050D-4142-454E-BA09-974DF77D2C10}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6CC1050D-4142-454E-BA09-974DF77D2C10}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Hazel/Connection.cs b/Hazel/Connection.cs index 4d99a2b..a76878a 100644 --- a/Hazel/Connection.cs +++ b/Hazel/Connection.cs @@ -1,7 +1,7 @@ using System; using System.Net; using System.Threading.Tasks; -using Impostor.Api.Net.Messages; +using Impostor.Hazel.Abstractions; using Serilog; namespace Impostor.Hazel diff --git a/Hazel/ConnectionListener.cs b/Hazel/ConnectionListener.cs index 0e231f9..731fd60 100644 --- a/Hazel/ConnectionListener.cs +++ b/Hazel/ConnectionListener.cs @@ -1,6 +1,6 @@ using System; using System.Threading.Tasks; -using Impostor.Api.Net.Messages; +using Impostor.Hazel.Abstractions; using Serilog; namespace Impostor.Hazel diff --git a/Hazel/DataReceivedEventArgs.cs b/Hazel/DataReceivedEventArgs.cs index 6e7d880..1322c99 100644 --- a/Hazel/DataReceivedEventArgs.cs +++ b/Hazel/DataReceivedEventArgs.cs @@ -1,4 +1,4 @@ -using Impostor.Api.Net.Messages; +using Impostor.Hazel.Abstractions; namespace Impostor.Hazel { diff --git a/Hazel/Hazel.csproj b/Hazel/Hazel.csproj index 08bb33e..e3f1726 100644 --- a/Hazel/Hazel.csproj +++ b/Hazel/Hazel.csproj @@ -5,6 +5,7 @@ net7.0 HAZEL_BAG Impostor.Hazel + Impostor.Hazel @@ -14,12 +15,13 @@ + - + diff --git a/Hazel/MessageReader.cs b/Hazel/MessageReader.cs index 03b0a99..7be34d0 100644 --- a/Hazel/MessageReader.cs +++ b/Hazel/MessageReader.cs @@ -1,13 +1,8 @@ using System; using System.Buffers.Binary; -using System.Numerics; using System.Runtime.CompilerServices; using System.Text; -using Impostor.Api; -using Impostor.Api.Games; -using Impostor.Api.Net.Inner; -using Impostor.Api.Net.Messages; -using Impostor.Api.Unity; +using Impostor.Hazel.Abstractions; using Microsoft.Extensions.ObjectPool; namespace Impostor.Hazel @@ -78,7 +73,7 @@ namespace Impostor.Hazel { if (message.Buffer != Buffer) { - throw new ImpostorProtocolException("Tried to remove message from a message that does not have the same buffer."); + throw new InvalidOperationException("Tried to remove message from a message that does not have the same buffer."); } // Offset of where to start removing. @@ -223,13 +218,11 @@ namespace Impostor.Hazel return output; } - /// public int ReadPackedInt32() { return (int)this.ReadPackedUInt32(); } - /// public uint ReadPackedUInt32() { bool readMore = true; @@ -256,21 +249,6 @@ namespace Impostor.Hazel return output; } - public T ReadNetObject(IGame game) where T : IInnerNetObject - { - return game.FindObjectByNetId(ReadPackedUInt32()); - } - - public Vector2 ReadVector2() - { - const float range = 50f; - - var x = ReadUInt16() / (float)ushort.MaxValue; - var y = ReadUInt16() / (float)ushort.MaxValue; - - return new Vector2(Mathf.Lerp(-range, range, x), Mathf.Lerp(-range, range, y)); - } - #endregion public void CopyTo(IMessageWriter writer) diff --git a/Hazel/MessageWriter.cs b/Hazel/MessageWriter.cs index b0377f3..99bd91d 100644 --- a/Hazel/MessageWriter.cs +++ b/Hazel/MessageWriter.cs @@ -1,12 +1,8 @@ using System; using System.Collections.Generic; using System.Net; -using System.Numerics; using System.Text; -using Impostor.Api.Games; -using Impostor.Api.Net.Inner; -using Impostor.Api.Net.Messages; -using Impostor.Api.Unity; +using Impostor.Hazel.Abstractions; namespace Impostor.Hazel { @@ -18,19 +14,18 @@ namespace Impostor.Hazel public MessageType SendOption { get; private set; } private Stack messageStarts = new Stack(); - + public MessageWriter(byte[] buffer) { this.Buffer = buffer; this.Length = this.Buffer.Length; } - /// public MessageWriter(int bufferSize) { this.Buffer = new byte[bufferSize]; } - + public byte[] Buffer { get; } public int Length { get; set; } public int Position { get; set; } @@ -48,24 +43,23 @@ namespace Impostor.Hazel switch (this.SendOption) { case MessageType.Reliable: - { - byte[] output = new byte[this.Length - 3]; - System.Buffer.BlockCopy(this.Buffer, 3, output, 0, this.Length - 3); - return output; - } + { + byte[] output = new byte[this.Length - 3]; + System.Buffer.BlockCopy(this.Buffer, 3, output, 0, this.Length - 3); + return output; + } case MessageType.Unreliable: - { - byte[] output = new byte[this.Length - 1]; - System.Buffer.BlockCopy(this.Buffer, 1, output, 0, this.Length - 1); - return output; - } + { + byte[] output = new byte[this.Length - 1]; + System.Buffer.BlockCopy(this.Buffer, 1, output, 0, this.Length - 1); + return output; + } } } throw new NotImplementedException(); } - /// /// The option specifying how the message should be sent. public static MessageWriter Get(MessageType sendOption = MessageType.Unreliable) { @@ -85,7 +79,6 @@ namespace Impostor.Hazel return this.Length > 3 + expected; } - /// public void StartMessage(byte typeFlag) { messageStarts.Push(this.Position); @@ -93,7 +86,6 @@ namespace Impostor.Hazel this.Write(typeFlag); } - /// public void EndMessage() { var lastMessageStart = messageStarts.Pop(); @@ -102,7 +94,6 @@ namespace Impostor.Hazel this.Buffer[lastMessageStart + 1] = (byte)(length >> 8); } - /// public void CancelMessage() { this.Position = this.messageStarts.Pop(); @@ -127,7 +118,6 @@ namespace Impostor.Hazel } } - /// public void Recycle() { this.Position = this.Length = 0; @@ -287,13 +277,11 @@ namespace Impostor.Hazel if (this.Position > this.Length) this.Length = this.Position; } - /// public void WritePacked(int value) { this.WritePacked((uint)value); } - /// public void WritePacked(uint value) { do @@ -308,7 +296,7 @@ namespace Impostor.Hazel value >>= 7; } while (value > 0); } - + public void Write(MessageWriter msg, bool includeHeader) { int offset = 0; @@ -333,44 +321,8 @@ namespace Impostor.Hazel this.Write(value.GetAddressBytes()); } - public void Write(GameCode value) - { - this.Write(value.Value); - } - - public void Write(IInnerNetObject innerNetObject) - { - if (innerNetObject == null) - { - this.Write(0); - } - else - { - this.WritePacked(innerNetObject.NetId); - } - } - - public void Write(Vector2 vector) - { - Write((ushort)(Mathf.ReverseLerp(vector.X) * (double) ushort.MaxValue)); - Write((ushort)(Mathf.ReverseLerp(vector.Y) * (double) ushort.MaxValue)); - } - #endregion - public unsafe static bool IsLittleEndian() - { - byte b; - unsafe - { - int i = 1; - byte* bp = (byte*)&i; - b = *bp; - } - - return b == 1; - } - public void Dispose() { Recycle(); diff --git a/Hazel/NewConnectionEventArgs.cs b/Hazel/NewConnectionEventArgs.cs index be9e7a2..a50a29c 100644 --- a/Hazel/NewConnectionEventArgs.cs +++ b/Hazel/NewConnectionEventArgs.cs @@ -1,4 +1,4 @@ -using Impostor.Api.Net.Messages; +using Impostor.Hazel.Abstractions; namespace Impostor.Hazel { diff --git a/Hazel/Udp/UdpClientConnection.cs b/Hazel/Udp/UdpClientConnection.cs index 01f003f..75d7ed8 100644 --- a/Hazel/Udp/UdpClientConnection.cs +++ b/Hazel/Udp/UdpClientConnection.cs @@ -3,7 +3,7 @@ using System.Net; using System.Net.Sockets; using System.Threading; using System.Threading.Tasks; -using Impostor.Api.Net.Messages; +using Impostor.Hazel.Abstractions; using Microsoft.Extensions.ObjectPool; namespace Impostor.Hazel.Udp diff --git a/Hazel/Udp/UdpConnection.Reliable.cs b/Hazel/Udp/UdpConnection.Reliable.cs index 7cf3858..12b6a76 100644 --- a/Hazel/Udp/UdpConnection.Reliable.cs +++ b/Hazel/Udp/UdpConnection.Reliable.cs @@ -4,7 +4,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Threading; using System.Threading.Tasks; -using Impostor.Api.Net.Messages; +using Impostor.Hazel.Abstractions; namespace Impostor.Hazel.Udp { diff --git a/Hazel/Udp/UdpConnection.cs b/Hazel/Udp/UdpConnection.cs index 5221d88..0c8c863 100644 --- a/Hazel/Udp/UdpConnection.cs +++ b/Hazel/Udp/UdpConnection.cs @@ -2,7 +2,7 @@ using System; using System.Threading; using System.Threading.Channels; using System.Threading.Tasks; -using Impostor.Api.Net.Messages; +using Impostor.Hazel.Abstractions; using Microsoft.Extensions.ObjectPool; using Serilog; diff --git a/Hazel/Udp/UdpServerConnection.cs b/Hazel/Udp/UdpServerConnection.cs index 6511375..7cc37db 100644 --- a/Hazel/Udp/UdpServerConnection.cs +++ b/Hazel/Udp/UdpServerConnection.cs @@ -1,7 +1,7 @@ using System; using System.Net; using System.Threading.Tasks; -using Impostor.Api.Net.Messages; +using Impostor.Hazel.Abstractions; using Microsoft.Extensions.ObjectPool; namespace Impostor.Hazel.Udp diff --git a/Impostor.Hazel.Abstractions/IMessageReader.cs b/Impostor.Hazel.Abstractions/IMessageReader.cs new file mode 100644 index 0000000..dc2fdc9 --- /dev/null +++ b/Impostor.Hazel.Abstractions/IMessageReader.cs @@ -0,0 +1,71 @@ +namespace Impostor.Hazel.Abstractions; + +public interface IMessageReader : IDisposable +{ + /// + /// Gets the tag of the message. + /// + byte Tag { get; } + + /// + /// Gets the buffer of the message. + /// + byte[] Buffer { get; } + + /// + /// Gets the offset of our current in the entire . + /// + int Offset { get; } + + /// + /// Gets the current position of the reader. + /// + int Position { get; } + + /// + /// Gets the length of the buffer. + /// + int Length { get; } + + IMessageReader ReadMessage(); + + bool ReadBoolean(); + + sbyte ReadSByte(); + + byte ReadByte(); + + ushort ReadUInt16(); + + short ReadInt16(); + + uint ReadUInt32(); + + int ReadInt32(); + + ulong ReadUInt64(); + + long ReadInt64(); + + float ReadSingle(); + + string ReadString(int length); + + string ReadString(); + + ReadOnlyMemory ReadBytesAndSize(); + + ReadOnlyMemory ReadBytes(int length); + + int ReadPackedInt32(); + + uint ReadPackedUInt32(); + + void CopyTo(IMessageWriter writer); + + void Seek(int position); + + void RemoveMessage(IMessageReader message); + + IMessageReader Copy(int offset = 0); +} \ No newline at end of file diff --git a/Impostor.Hazel.Abstractions/IMessageWriter.cs b/Impostor.Hazel.Abstractions/IMessageWriter.cs new file mode 100644 index 0000000..6b4ce34 --- /dev/null +++ b/Impostor.Hazel.Abstractions/IMessageWriter.cs @@ -0,0 +1,137 @@ +using System.Net; + +namespace Impostor.Hazel.Abstractions; + +/// +/// Base message writer. +/// +public interface IMessageWriter : IDisposable +{ + public byte[] Buffer { get; } + + public int Length { get; set; } + + public int Position { get; set; } + + public MessageType SendOption { get; } + + /// + /// Copies the contents of this writer into a new array. + /// + /// Whether to include message header in the array. + /// An array containing the data in the current writer. + byte[] ToByteArray(bool includeHeader); + + /// + /// Writes a boolean to the message. + /// + /// Value to write. + void Write(bool value); + + /// + /// Writes a sbyte to the message. + /// + /// Value to write. + void Write(sbyte value); + + /// + /// Writes a byte to the message. + /// + /// Value to write. + void Write(byte value); + + /// + /// Writes a short to the message. + /// + /// Value to write. + void Write(short value); + + /// + /// Writes an ushort to the message. + /// + /// Value to write. + void Write(ushort value); + + /// + /// Writes an uint to the message. + /// + /// Value to write. + void Write(uint value); + + /// + /// Writes an int to the message. + /// + /// Value to write. + void Write(int value); + + /// + /// Writes an ulong to the message. + /// + /// Value to write. + void Write(ulong value); + + /// + /// Writes an ulong to the message. + /// + /// Value to write. + void Write(long value); + + /// + /// Writes a float to the message. + /// + /// Value to write. + void Write(float value); + + /// + /// Writes a string to the message. + /// + /// Value to write. + void Write(string value); + + /// + /// Writes a to the message. + /// + /// Value to write. + void Write(IPAddress value); + + /// + /// Writes an packed int to the message. + /// + /// Value to write. + void WritePacked(int value); + + /// + /// Writes an packed uint to the message. + /// + /// Value to write. + void WritePacked(uint value); + + /// + /// Writes raw bytes to the message. + /// + /// Bytes to write. + void Write(ReadOnlyMemory data); + + void WriteBytesAndSize(byte[] bytes); + + void WriteBytesAndSize(byte[] bytes, int length); + + void WriteBytesAndSize(byte[] bytes, int offset, int length); + + /// + /// Starts a new message. + /// + /// Message flag header. + void StartMessage(byte typeFlag); + + /// + /// Mark the end of the message. + /// + void EndMessage(); + + /// + /// Clear the message writer. + /// + /// New type of the message. + void Clear(MessageType type); +} \ No newline at end of file diff --git a/Impostor.Hazel.Abstractions/IMessageWriterProvider.cs b/Impostor.Hazel.Abstractions/IMessageWriterProvider.cs new file mode 100644 index 0000000..cbc252d --- /dev/null +++ b/Impostor.Hazel.Abstractions/IMessageWriterProvider.cs @@ -0,0 +1,15 @@ +namespace Impostor.Hazel.Abstractions; + +public interface IMessageWriterProvider +{ + /// + /// Retrieves a from the internal pool. + /// Make sure to call when you are done!. + /// + /// + /// Whether to send the message as or . + /// Reliable packets will ensure delivery while unreliable packets may be lost. + /// + /// A from the pool. + IMessageWriter Get(MessageType sendOption = MessageType.Unreliable); +} \ No newline at end of file diff --git a/Impostor.Hazel.Abstractions/Impostor.Hazel.Abstractions.csproj b/Impostor.Hazel.Abstractions/Impostor.Hazel.Abstractions.csproj new file mode 100644 index 0000000..6836c68 --- /dev/null +++ b/Impostor.Hazel.Abstractions/Impostor.Hazel.Abstractions.csproj @@ -0,0 +1,9 @@ + + + + net7.0 + enable + enable + + + diff --git a/Impostor.Hazel.Abstractions/MessageType.cs b/Impostor.Hazel.Abstractions/MessageType.cs new file mode 100644 index 0000000..08cd4e5 --- /dev/null +++ b/Impostor.Hazel.Abstractions/MessageType.cs @@ -0,0 +1,29 @@ +namespace Impostor.Hazel.Abstractions; + +/// +/// Specifies how a message should be sent between connections. +/// +[Flags] +public enum MessageType : byte +{ + /// + /// Requests unreliable delivery with no fragmentation. + /// + /// + /// Sending data using unreliable delivery means that data is not guaranteed to arrive at it's destination nor is + /// it guaranteed to arrive only once. However, unreliable delivery can be faster than other methods and it + /// typically requires a smaller number of protocol bytes than other methods. There is also typically less + /// processing involved and less memory needed as packets are not stored once sent. + /// + Unreliable, + + /// + /// Requests data be sent reliably but with no fragmentation. + /// + /// + /// Sending data reliably means that data is guaranteed to arrive and to arrive only once. Reliable delivery + /// typically requires more processing, more memory (as packets need to be stored in case they need resending), + /// a larger number of protocol bytes and can be slower than unreliable delivery. + /// + Reliable, +} \ No newline at end of file diff --git a/Impostor.Hazel.Tests/Impostor.Hazel.Tests.csproj b/Impostor.Hazel.Tests/Impostor.Hazel.Tests.csproj new file mode 100644 index 0000000..a5a0a3b --- /dev/null +++ b/Impostor.Hazel.Tests/Impostor.Hazel.Tests.csproj @@ -0,0 +1,29 @@ + + + + net7.0 + enable + enable + + false + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + + diff --git a/Impostor.Hazel.Tests/MessageReaderTests.cs b/Impostor.Hazel.Tests/MessageReaderTests.cs new file mode 100644 index 0000000..efbf502 --- /dev/null +++ b/Impostor.Hazel.Tests/MessageReaderTests.cs @@ -0,0 +1,360 @@ +using Impostor.Hazel.Extensions; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.ObjectPool; + +namespace Impostor.Hazel.Tests; + +public class MessageReaderTests +{ + private ObjectPool CreateReaderPool() + { + var services = new ServiceCollection(); + services.AddHazel(); + return services.BuildServiceProvider().GetRequiredService>(); + } + + [Fact] + public void ReadProperInt() + { + const int Test1 = int.MaxValue; + const int Test2 = int.MinValue; + + var msg = new MessageWriter(128); + msg.StartMessage(1); + msg.Write(Test1); + msg.Write(Test2); + msg.EndMessage(); + + Assert.Equal(11, msg.Length); + Assert.Equal(msg.Length, msg.Position); + + var readerPool = CreateReaderPool(); + var reader = readerPool.Get(); + reader.Update(msg.Buffer); + Assert.Equal(byte.MaxValue, reader.Tag); + var message = reader.ReadMessage(); + Assert.Equal(1, message.Tag); + Assert.Equal(Test1, message.ReadInt32()); + Assert.Equal(Test2, message.ReadInt32()); + } + + [Fact] + public void ReadProperBool() + { + const bool Test1 = true; + const bool Test2 = false; + + var msg = new MessageWriter(128); + msg.StartMessage(1); + msg.Write(Test1); + msg.Write(Test2); + msg.EndMessage(); + + Assert.Equal(5, msg.Length); + Assert.Equal(msg.Length, msg.Position); + + var readerPool = CreateReaderPool(); + var reader = readerPool.Get(); + reader.Update(msg.Buffer); + Assert.Equal(byte.MaxValue, reader.Tag); + var message = reader.ReadMessage(); + Assert.Equal(1, message.Tag); + Assert.Equal(Test1, message.ReadBoolean()); + Assert.Equal(Test2, message.ReadBoolean()); + } + + [Fact] + public void ReadProperString() + { + const string Test1 = "Hello"; + var Test2 = new string(' ', 1024); + var msg = new MessageWriter(2048); + msg.StartMessage(1); + msg.Write(Test1); + msg.Write(Test2); + msg.Write(string.Empty); + msg.EndMessage(); + + Assert.Equal(msg.Length, msg.Position); + + var readerPool = CreateReaderPool(); + var reader = readerPool.Get(); + reader.Update(msg.Buffer); + Assert.Equal(byte.MaxValue, reader.Tag); + var message = reader.ReadMessage(); + Assert.Equal(1, message.Tag); + Assert.Equal(Test1, message.ReadString()); + Assert.Equal(Test2, message.ReadString()); + Assert.Equal(string.Empty, message.ReadString()); + } + + [Fact] + public void ReadProperFloat() + { + const float Test1 = 12.34f; + + var msg = new MessageWriter(2048); + msg.StartMessage(1); + msg.Write(Test1); + msg.EndMessage(); + + Assert.Equal(7, msg.Length); + Assert.Equal(msg.Length, msg.Position); + + var readerPool = CreateReaderPool(); + var reader = readerPool.Get(); + reader.Update(msg.Buffer); + Assert.Equal(byte.MaxValue, reader.Tag); + var message = reader.ReadMessage(); + Assert.Equal(1, message.Tag); + Assert.Equal(Test1, message.ReadSingle()); + } + + [Fact] + public void CopyMessage() + { + var readerPool = CreateReaderPool(); + + // Create message. + const int msgLength = 18; + const byte Test1 = 12; + const byte Test2 = 146; + + var msg = new MessageWriter(2048); + + msg.StartMessage(1); + msg.StartMessage(2); + msg.Write(Test1); + msg.Write(Test2); + msg.StartMessage(2); + msg.Write(Test1); + msg.Write(Test2); + msg.StartMessage(2); + msg.Write(Test1); + msg.Write(Test2); + msg.EndMessage(); + msg.EndMessage(); + msg.EndMessage(); + msg.EndMessage(); + + // Read message. + using var reader = readerPool.Get(); + + reader.Update(msg.Buffer); + + // Read first message. + using var messageOne = reader.ReadMessage(); + + Assert.Equal(1, messageOne.Tag); + Assert.Equal(0, messageOne.Position); + Assert.Equal(3, messageOne.Offset); + Assert.Equal(msgLength - 3, messageOne.Length); + + using var messageTwo = messageOne.ReadMessage(); + + Assert.Equal(2, messageTwo.Tag); + Assert.Equal(0, messageTwo.Position); + Assert.Equal(6, messageTwo.Offset); + Assert.Equal(msgLength - 6, messageTwo.Length); + Assert.Equal(Test1, messageTwo.ReadByte()); + Assert.Equal(Test2, messageTwo.ReadByte()); + + using var messageThree = messageTwo.ReadMessage(); + + Assert.Equal(2, messageThree.Tag); + Assert.Equal(0, messageThree.Position); + Assert.Equal(11, messageThree.Offset); + Assert.Equal(msgLength - 11, messageThree.Length); + Assert.Equal(Test1, messageThree.ReadByte()); + Assert.Equal(Test2, messageThree.ReadByte()); + } + + [Fact] + public void CopySubMessage() + { + const byte Test1 = 12; + const byte Test2 = 146; + + var msg = new MessageWriter(2048); + msg.StartMessage(1); + + msg.StartMessage(2); + msg.Write(Test1); + msg.Write(Test2); + msg.EndMessage(); + + msg.EndMessage(); + + var readerPool = CreateReaderPool(); + var handleReader = readerPool.Get(); + handleReader.Update(msg.Buffer); + var handleMessage = handleReader.ReadMessage(); + Assert.Equal(1, handleMessage.Tag); + + using var parentReader = handleMessage.Copy(); + + Assert.Equal(1, parentReader.Tag); + + var reader = parentReader.ReadMessage(); + + Assert.Equal(2, reader.Tag); + Assert.Equal(Test1, reader.ReadByte()); + Assert.Equal(Test2, reader.ReadByte()); + } + + [Fact] + public void CopyToMessage() + { + var expected = new byte[] + { + 0x2A, 0x00, 0x01, 0x27, 0x00, 0x02, 0x26, 0x54, + 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x61, + 0x20, 0x6C, 0x6F, 0x6E, 0x67, 0x20, 0x70, 0x61, + 0x63, 0x6B, 0x65, 0x74, 0x20, 0x74, 0x6F, 0x20, + 0x74, 0x65, 0x73, 0x74, 0x20, 0x63, 0x6F, 0x70, + 0x79, 0x69, 0x6E, 0x67, 0x2E, + }; + + var readerPool = CreateReaderPool(); + + // Create packet. + var msg = new MessageWriter(2048); + msg.StartMessage(1); + msg.StartMessage(2); + msg.Write("This is a long packet to test copying."); + msg.EndMessage(); + msg.EndMessage(); + + // Create a reader. + var reader = readerPool.Get(); + + reader.Update(msg.Buffer); + + // Read the initial message. + var message = reader.ReadMessage(); + + // Copy the message to a new writer. + var writer = new MessageWriter(2048); + + message.CopyTo(writer); + + // Compare. + Assert.Equal(expected, writer.ToByteArray(true)); + } + + [Fact] + public void ReadMessageLength() + { + var msg = new MessageWriter(2048); + msg.StartMessage(1); + msg.Write(65534); + msg.StartMessage(2); + msg.Write("HO"); + msg.EndMessage(); + msg.StartMessage(2); + msg.Write("NO"); + msg.EndMessage(); + msg.EndMessage(); + + Assert.Equal(msg.Length, msg.Position); + + var readerPool = CreateReaderPool(); + var reader = readerPool.Get(); + reader.Update(msg.Buffer); + Assert.Equal(byte.MaxValue, reader.Tag); + var message = reader.ReadMessage(); + Assert.Equal(1, message.Tag); + Assert.Equal(65534, message.ReadInt32()); // Content + + var sub = message.ReadMessage(); + Assert.Equal(3, sub.Length); + Assert.Equal(2, sub.Tag); + Assert.Equal("HO", sub.ReadString()); + + sub = message.ReadMessage(); + Assert.Equal(3, sub.Length); + Assert.Equal(2, sub.Tag); + Assert.Equal("NO", sub.ReadString()); + } + + [Fact] + public void RemoveMessage() + { + // Create expected message. + var messageExpected = new MessageWriter(1024); + + messageExpected.StartMessage(0); + messageExpected.StartMessage(1); + messageExpected.Write("HiTest1"); + messageExpected.EndMessage(); + messageExpected.StartMessage(2); + messageExpected.Write("HiTest2"); + messageExpected.EndMessage(); + messageExpected.EndMessage(); + + // Create message. + var messageWriter = new MessageWriter(1024); + + messageWriter.StartMessage(0); + messageWriter.StartMessage(1); + messageWriter.Write("HiTest1"); + messageWriter.StartMessage(2); + messageWriter.Write("RemoveMe!"); + messageWriter.EndMessage(); + messageWriter.EndMessage(); + messageWriter.StartMessage(2); + messageWriter.Write("HiTest2"); + messageWriter.EndMessage(); + messageWriter.EndMessage(); + + // Copy buffer. + var bufferCopy = new byte[messageWriter.Length]; + Buffer.BlockCopy(messageWriter.Buffer, 0, bufferCopy, 0, bufferCopy.Length); + + var bufferCopyTwo = new byte[messageWriter.Length]; + Buffer.BlockCopy(messageWriter.Buffer, 0, bufferCopyTwo, 0, bufferCopyTwo.Length); + + // Do the magic. + var readerPool = CreateReaderPool(); + var reader = readerPool.Get(); + reader.Update(bufferCopy); + var inner = reader.ReadMessage(); + + while (inner.Position < inner.Length) + { + var message = inner.ReadMessage(); + if (message.Tag == 1) + { + Assert.Equal("HiTest1", message.ReadString()); + + var messageSub = message.ReadMessage(); + if (messageSub.Tag == 2) + { + Assert.Equal("RemoveMe!", messageSub.ReadString()); + + // Remove this message. + inner.RemoveMessage(messageSub); + } + } + else if (message.Tag == 2) + { + Assert.Equal("HiTest2", message.ReadString()); + } + else + { + Assert.True(false, "Invalid tag was read."); + } + } + + // Check if the magic was successful. + Assert.Equal(messageExpected.Length, reader.Length); + Assert.Equal(messageExpected.ToByteArray(true), reader.Buffer.Take(reader.Length).ToArray()); + + // Test ownership. + var readerTwo = readerPool.Get(); + + readerTwo.Update(bufferCopyTwo); + + Assert.Throws(() => reader.RemoveMessage(readerTwo.ReadMessage())); + } +} diff --git a/Impostor.Hazel.Tests/MessageWriterTests.cs b/Impostor.Hazel.Tests/MessageWriterTests.cs new file mode 100644 index 0000000..d96891e --- /dev/null +++ b/Impostor.Hazel.Tests/MessageWriterTests.cs @@ -0,0 +1,32 @@ +namespace Impostor.Hazel.Tests; + +public class MessageWriterTests +{ + [Fact] + public void ReadOnlyMemoryWriteWorksTheSameAsArray() + { + var oldVer = new MessageWriter(1024); + var newVer = new MessageWriter(1024); + + var data = Enumerable.Repeat + ( + Enumerable.Range(0, byte.MaxValue) + .Select(x => (byte)x), + 2 + ).SelectMany(x => x).ToArray(); + + WriteSomeData(oldVer); + WriteSomeData(newVer); + + oldVer.Write(data); + newVer.Write(data.AsMemory()); + + Assert.True(oldVer.Buffer.AsSpan().SequenceEqual(newVer.Buffer.AsSpan())); + + static void WriteSomeData(MessageWriter oldVer) + { + oldVer.WritePacked(99); + oldVer.WritePacked(101); + } + } +} diff --git a/Impostor.Hazel.Tests/Usings.cs b/Impostor.Hazel.Tests/Usings.cs new file mode 100644 index 0000000..c802f44 --- /dev/null +++ b/Impostor.Hazel.Tests/Usings.cs @@ -0,0 +1 @@ +global using Xunit;