<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Hazel.UnitTests</RootNamespace>
<AssemblyName>Hazel.UnitTests</AssemblyName>
- <TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
+ <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<ProjectTypeGuids>{3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<AllowUnsafeBlocks>false</AllowUnsafeBlocks>
+ <Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
+ <Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
using System;
using System.IO;
+using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace Hazel.UnitTests
msg.StartMessage(1);
msg.Write(Test1);
msg.Write(Test2);
+ msg.Write(string.Empty);
msg.EndMessage();
Assert.AreEqual(msg.Length, msg.Position);
Assert.AreEqual(Test1, reader.ReadString());
Assert.AreEqual(Test2, reader.ReadString());
+ Assert.AreEqual(string.Empty, reader.ReadString());
}
msg.StartMessage(2);
msg.Write("HO");
msg.EndMessage();
+ msg.StartMessage(2);
+ msg.Write("NO");
+ msg.EndMessage();
msg.EndMessage();
Assert.AreEqual(msg.Length, msg.Position);
MessageReader reader = MessageReader.Get(msg.Buffer, 0);
Assert.AreEqual(1, reader.Tag);
Assert.AreEqual(65534, reader.ReadInt32()); // Content
+
var sub = reader.ReadMessage();
+ Assert.AreEqual(3, sub.Length);
Assert.AreEqual(2, sub.Tag);
Assert.AreEqual("HO", sub.ReadString());
+ sub = reader.ReadMessage();
+ Assert.AreEqual(3, sub.Length);
+ Assert.AreEqual(2, sub.Tag);
+ Assert.AreEqual("NO", sub.ReadString());
+ }
+
+ [TestMethod]
+ public void TestMessage()
+ {
+ string test = "5 32 0 0 0 22 0 4 4 2 3 208 52 4 0 1 0 0 0 2 209 52 0 0 1 210 52 0 0 1 40 0 2 208 52 4 36 0 0 128 63 0 0 128 63 0 0 192 63 0 0 112 65 1 0 0 0 1 0 0 0 2 0 0 0 1 0 0 0 1 0 0 0 22 0 4 4 2 3 211 52 4 0 1 0 0 0 2 212 52 0 0 1 213 52 0 0 1 40 0 2 211 52 4 36 0 0 128 63 0 0 128 63 0 0 192 63 0 0 112 65 1 0 0 0 1 0 0 0 2 0 0 0 1 0 0 0 1 0 0 0";
+ byte[] testValues = test.Replace("-", "").Split(' ').Select(b => byte.Parse(b)).ToArray();
+
+
+ MessageWriter dataWriter = new MessageWriter(1024);
+ dataWriter.Write((byte)5);
+ dataWriter.Write(32);
+ dataWriter.StartMessage(4); // Start spawn
+ dataWriter.WritePacked(4); // Spawn Id = Player
+ dataWriter.WritePacked(2); // Owner Id
+ dataWriter.WritePacked(3); // Number children
+
+ dataWriter.Write((byte)208); // NetId (packed)
+ dataWriter.Write((byte)52); // NetId (packed)
+
+ dataWriter.StartMessage(1); // Start data
+ dataWriter.Write(""); // Name
+ dataWriter.Write((byte)0); // Color
+ dataWriter.Write((byte)0); // Important Flags
+ dataWriter.Write((byte)2); // Player Id
+ dataWriter.EndMessage();
+
+ dataWriter.Write((byte)209); // NetId (packed)
+ dataWriter.Write((byte)52); // NetId (packed)
+
+ dataWriter.StartMessage(1); // Start data (None)
+ dataWriter.EndMessage();
+
+ dataWriter.Write((byte)210); // NetId (packed)
+ dataWriter.Write((byte)52); // NetId (packed)
+
+ dataWriter.StartMessage(1); // Start data (None)
+ dataWriter.EndMessage();
+
+ dataWriter.EndMessage();
+
+ Console.WriteLine($"{string.Join(" ", dataWriter.Buffer.Take(dataWriter.Length))}");
+ Console.WriteLine($"{string.Join(" ", testValues.Take(dataWriter.Length))}");
+
+ Assert.AreEqual(22 + 4 + 1 + 3, dataWriter.Length);
+
+
+
+ MessageReader msg = MessageReader.Get(testValues, 0, testValues.Length);
+
+ Assert.AreEqual(5, msg.Tag);
+ Assert.AreEqual(32, msg.ReadInt32());
+
+ while (msg.Position < msg.Length)
+ {
+ var sub = msg.ReadMessage();
+
+ if (sub.Tag == 4) // Spawn
+ {
+ uint spawnId = sub.ReadPackedUInt32();
+ int ownerId = sub.ReadPackedInt32();
+ int numChild = sub.ReadPackedInt32();
+ Console.WriteLine($"Spawning {spawnId} for {ownerId} with {numChild} children");
+ for (int i = 0; i < numChild; ++i)
+ {
+ uint childId = sub.ReadPackedUInt32();
+ var childReader = sub.ReadMessage();
+ if (childId == 6736)
+ {
+ string name = childReader.ReadString();
+ byte color = childReader.ReadByte();
+ byte flags = childReader.ReadByte();
+ uint playerId = childReader.ReadByte();
+ Console.WriteLine($"Child {childId} has name='{name}' {color} {flags} {playerId}");
+ }
+ else
+ {
+ Console.WriteLine($"Child {childId} has data ({childReader.Tag}) len={childReader.Length}");
+ }
+ }
+ }
+ else
+ {
+ Console.WriteLine($"Tag: {sub.Tag}\tLength: {sub.Length}\tData = {string.Join(" ", sub.ReadBytes(sub.Length).Select(s => s.ToString()).ToArray())}");
+ }
+
+ Console.WriteLine($"Position: {msg.Position}/{msg.Length}");
+ }
}
[TestMethod]
var msg = new MessageWriter(2048);
msg.Write(Test1);
msg.Write(Test2);
+ msg.Write(string.Empty);
Assert.AreEqual(msg.Length, msg.Position);
{
Assert.AreEqual(Test1, reader.ReadString());
Assert.AreEqual(Test2, reader.ReadString());
+ Assert.AreEqual(string.Empty, reader.ReadString());
}
}
}
}
+ [TestMethod]
+ public void WritePackedUint()
+ {
+ var msg = new MessageWriter(2048);
+ msg.StartMessage(0);
+ msg.WritePacked(8u);
+ msg.WritePacked(250u);
+ msg.WritePacked(68000u);
+ msg.EndMessage();
+
+ Assert.AreEqual(3 + 1 + 2 + 3, msg.Position);
+ Assert.AreEqual(msg.Length, msg.Position);
+
+ MessageReader reader = MessageReader.Get(msg.Buffer, 0);
+
+ Assert.AreEqual(8u, reader.ReadPackedUInt32());
+ Assert.AreEqual(250u, reader.ReadPackedUInt32());
+ Assert.AreEqual(68000u, reader.ReadPackedUInt32());
+ }
+
+
+ [TestMethod]
+ public void WritePackedInt()
+ {
+ var msg = new MessageWriter(2048);
+ msg.StartMessage(0);
+ msg.WritePacked(8);
+ msg.WritePacked(250);
+ msg.WritePacked(68000);
+ msg.WritePacked(-68000);
+ msg.WritePacked(-250);
+ msg.WritePacked(-8);
+ msg.EndMessage();
+
+ Assert.AreEqual(3 + 1 + 2 + 3 + 5 + 5 + 5, msg.Position);
+ Assert.AreEqual(msg.Length, msg.Position);
+
+ MessageReader reader = MessageReader.Get(msg.Buffer, 0);
+
+ Assert.AreEqual(8, reader.ReadPackedInt32());
+ Assert.AreEqual(250, reader.ReadPackedInt32());
+ Assert.AreEqual(68000, reader.ReadPackedInt32());
+
+
+ Assert.AreEqual(-68000, reader.ReadPackedInt32());
+ Assert.AreEqual(-250, reader.ReadPackedInt32());
+ Assert.AreEqual(-8, reader.ReadPackedInt32());
+ }
+
[TestMethod]
public void WritesMessageLength()
{
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Hazel</RootNamespace>
<AssemblyName>Hazel</AssemblyName>
- <TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
+ <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<TargetFrameworkProfile />
</PropertyGroup>
<DocumentationFile>
</DocumentationFile>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+ <Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<DocumentationFile>
</DocumentationFile>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+ <Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup>
<SignAssembly>true</SignAssembly>
using System;
+using System.Runtime.CompilerServices;
using System.Text;
namespace Hazel
{
private static readonly ObjectPool<MessageReader> objectPool = new ObjectPool<MessageReader>(() => new MessageReader());
- private byte[] Buffer;
+ public byte[] Buffer;
public byte Tag;
- public int End;
- public int Position;
+ public int Length;
+ public int Offset { get; private set; }
+ public int Position
+ {
+ get { return this._position; }
+ set
+ {
+ this._position = value;
+ this.readHead = this._position + Offset;
+ }
+ }
+
+ private int _position;
+
+ private int readHead;
+
public static MessageReader Get(byte[] buffer, int offset, int length)
{
var output = objectPool.GetObject();
output.Buffer = buffer;
- output.Position = offset;
- output.End = length + offset;
+ output.Offset = offset;
+ output.Position = 0;
+ output.Length = length;
output.Tag = output.ReadByte();
-
+
return output;
}
{
var output = objectPool.GetObject();
output.Buffer = buffer;
- output.Position = offset;
- output.End = output.ReadUInt16() + offset;
+ output.Offset = offset;
+ output.Position = 0;
+
+ output.Length = output.ReadUInt16();
output.Tag = output.ReadByte();
+ output.Offset += 3;
+ output.Position = 0;
+
return output;
}
///
public MessageReader ReadMessage()
{
- var output = MessageReader.Get(this.Buffer, this.Position);
- this.Position += output.End;
+ var output = MessageReader.Get(this.Buffer, this.readHead);
+ this.Position += output.Length + 3;
return output;
}
///
public void Recycle()
{
- this.Position = this.End = 0;
+ this.Position = this.Length = 0;
objectPool.PutObject(this);
}
#region Read Methods
public bool ReadBoolean()
{
- byte val = this.Buffer[this.Position++];
+ byte val = this.FastByte();
return val != 0;
}
public sbyte ReadSByte()
{
- return (sbyte)this.Buffer[this.Position++];
+ return (sbyte)this.FastByte();
}
public byte ReadByte()
{
- return this.Buffer[this.Position++];
+ return this.FastByte();
}
public ushort ReadUInt16()
{
- ushort output =
- (ushort)(this.Buffer[Position++]
- | this.Buffer[Position++] << 8);
+ ushort output =
+ (ushort)(this.FastByte()
+ | this.FastByte() << 8);
return output;
}
public short ReadInt16()
{
short output =
- (short)(this.Buffer[Position++]
- | this.Buffer[Position++] << 8);
+ (short)(this.FastByte()
+ | this.FastByte() << 8);
return output;
}
public int ReadInt32()
{
- int output = this.Buffer[Position++]
- | this.Buffer[Position++] << 8
- | this.Buffer[Position++] << 16
- | this.Buffer[Position++] << 24;
+ int output = this.FastByte()
+ | this.FastByte() << 8
+ | this.FastByte() << 16
+ | this.FastByte() << 24;
return output;
}
public unsafe float ReadSingle()
{
float output = 0;
- fixed (byte* bufPtr = &this.Buffer[this.Position])
+ fixed (byte* bufPtr = &this.Buffer[this.readHead])
{
byte* outPtr = (byte*)&output;
public string ReadString()
{
int len = this.ReadPackedInt32();
- string output = UTF8Encoding.UTF8.GetString(this.Buffer, this.Position, len);
+ string output = UTF8Encoding.UTF8.GetString(this.Buffer, this.readHead, len);
this.Position += len;
return output;
public byte[] ReadBytes(int length)
{
byte[] output = new byte[length];
- Array.Copy(this.Buffer, this.Position, output, 0, output.Length);
+ Array.Copy(this.Buffer, this.readHead, output, 0, output.Length);
this.Position += output.Length;
return output;
}
}
#endregion
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private byte FastByte()
+ {
+ this._position++;
+ return this.Buffer[this.readHead++];
+ }
+
public unsafe static bool IsLittleEndian()
{
byte b;
this.Write(bytes);
}
+ public void WriteBytesAndSize(byte[] bytes, int length)
+ {
+ this.WritePacked((uint)length);
+ this.Write(bytes, length);
+ }
+
public void Write(byte[] bytes)
{
Array.Copy(bytes, 0, this.Buffer, this.Position, bytes.Length);
if (this.Position > this.Length) this.Length = this.Position;
}
+ public void Write(byte[] bytes, int length)
+ {
+ Array.Copy(bytes, 0, this.Buffer, this.Position, length);
+ this.Position += length;
+ if (this.Position > this.Length) this.Length = this.Position;
+ }
+
///
public void WritePacked(int value)
{
else
{
//See if we're missing it, else this packet is a duplicate as so we return false
- if (reliableDataPacketsMissing.Contains(id))
- reliableDataPacketsMissing.Remove(id);
- else
+ if (!reliableDataPacketsMissing.Remove(id))
return false;
}
}
lock (reliableDataPacketsSent)
{
//Dispose of timer and remove from dictionary
- if (reliableDataPacketsSent.ContainsKey(id))
- {
- Packet packet = reliableDataPacketsSent[id];
-
+ Packet packet;
+ if (reliableDataPacketsSent.TryGetValue(id, out packet))
+ {
packet.Acknowledged = true;
if (packet.AckCallback != null)