Skip to content

Commit

Permalink
feat: attributes to use zig zag encoding (#897)
Browse files Browse the repository at this point in the history
* feat: attributes to use zig zag encoding

* setting up tests and bitcountfinder

* fixing weaver tests

* fixxing tests

* implementing read/write for zigzagencode

* updating tests

* updating template
  • Loading branch information
James-Frowen committed Aug 24, 2021
1 parent 82d7e51 commit ccef5fb
Show file tree
Hide file tree
Showing 43 changed files with 1,171 additions and 38 deletions.
11 changes: 10 additions & 1 deletion Assets/Mirage/Runtime/Serialization/WeaverAttributes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ public sealed class WeaverIgnoreAttribute : Attribute { }
/// Tells weaver how many bits to sue for field
/// <para>Only works with interager fields (byte, int, ulong, enums etc)</para>
/// <para>
/// NOTE: bits are truncated when using this, so signed values will lose their sign.
/// NOTE: bits are truncated when using this, so signed values will lose their sign. Use <see cref="ZigZagEncodeAttribute"/> as well if value might be negative
/// </para>
/// </summary>
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter)]
Expand All @@ -32,4 +32,13 @@ public BitCountAttribute(int bitCount)
BitCount = bitCount;
}
}

/// <summary>
/// Used along size <see cref="BitCountAttribute"/> to encodes a interager value using <see cref="ZigZag"/> so that both positive and negative values can be sent
/// </summary>
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter)]
public class ZigZagEncodeAttribute : Attribute
{
public ZigZagEncodeAttribute() { }
}
}
29 changes: 29 additions & 0 deletions Assets/Mirage/Weaver/Processors/SyncVarProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -456,10 +456,25 @@ void WriteWithBitCount()
worker.Append(worker.Create(OpCodes.Ldarg, writerParameter));
worker.Append(worker.Create(OpCodes.Ldarg_0));
worker.Append(worker.Create(OpCodes.Ldfld, syncVar.FieldDefinition.MakeHostGenericIfNeeded()));

if (syncVar.UseZigZagEncoding)
{
WriteZigZag();
}

worker.Append(worker.Create(OpCodes.Conv_U8));
worker.Append(worker.Create(OpCodes.Ldc_I4, syncVar.BitCount.Value));
worker.Append(worker.Create(OpCodes.Call, writeWithBitCount));
}
void WriteZigZag()
{
bool useLong = syncVar.FieldDefinition.FieldType.Is<long>();
MethodReference encode = useLong
? module.ImportReference((long v) => ZigZag.Encode(v))
: module.ImportReference((int v) => ZigZag.Encode(v));

worker.Append(worker.Create(OpCodes.Call, encode));
}
}


Expand Down Expand Up @@ -622,8 +637,22 @@ void ReadWithBitCount()
worker.Append(worker.Create(syncVar.BitCountConvert.Value));
}

if (syncVar.UseZigZagEncoding)
{
ReadZigZag();
}

worker.Append(worker.Create(OpCodes.Stfld, syncVar.FieldDefinition.MakeHostGenericIfNeeded()));
}
void ReadZigZag()
{
bool useLong = syncVar.FieldDefinition.FieldType.Is<long>();
MethodReference encode = useLong
? module.ImportReference((ulong v) => ZigZag.Decode(v))
: module.ImportReference((uint v) => ZigZag.Decode(v));

worker.Append(worker.Create(OpCodes.Call, encode));
}
}
}
}
2 changes: 1 addition & 1 deletion Assets/Mirage/Weaver/Processors/SyncVars/BitCountFinder.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using Mirage.Serialization;
using Mirage.Serialization;
using Mono.Cecil;
using Mono.Cecil.Cil;

Expand Down
5 changes: 4 additions & 1 deletion Assets/Mirage/Weaver/Processors/SyncVars/FoundSyncVar.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ public FoundSyncVar(FieldDefinition fieldDefinition, int dirtyIndex)
public MethodDefinition HookMethod { get; private set; }

public int? BitCount { get; private set; }
public OpCode? BitCountConvert { get; internal set; }
public OpCode? BitCountConvert { get; private set; }

public bool UseZigZagEncoding { get; private set; }

public MethodReference WriteFunction { get; private set; }
public MethodReference ReadFunction { get; private set; }
Expand Down Expand Up @@ -82,6 +84,7 @@ public void ProcessAttributes()
HasHookMethod = HookMethod != null;

(BitCount, BitCountConvert) = BitCountFinder.GetBitCount(FieldDefinition);
UseZigZagEncoding = ZigZagFinder.HasZigZag(FieldDefinition, BitCount.HasValue);
}

public void FindSerializeFunctions(Writers writers, Readers readers)
Expand Down
43 changes: 43 additions & 0 deletions Assets/Mirage/Weaver/Processors/SyncVars/ZigZagFinder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
using Mirage.Serialization;
using Mono.Cecil;

namespace Mirage.Weaver.SyncVars
{
public static class ZigZagFinder
{
public static bool HasZigZag(FieldDefinition syncVar, bool hasBitCount)
{
bool hasAttribute = syncVar.HasCustomAttribute<ZigZagEncodeAttribute>();
if (!hasAttribute)
return false;

if (!hasBitCount)
throw new ZigZagException($"[ZigZagEncode] can only be used with [BitCount]", syncVar);

ThrowIfUnsignedType(syncVar.FieldType, syncVar);
return true;
}

/// <summary>
/// Any Int or enum based type is valid
/// </summary>
/// <param name="type"></param>
/// <param name="syncVar"></param>
static void ThrowIfUnsignedType(TypeReference type, FieldDefinition syncVar)
{
// throw if unsigned
if (type.Is<byte>()
|| type.Is<ushort>()
|| type.Is<uint>()
|| type.Is<ulong>())
throw new ZigZagException($"[ZigZagEncode] can only be used on a signed type", syncVar);

if (type.Resolve().IsEnum)
{
// check underlying field is signed
TypeReference enumType = type.Resolve().GetEnumUnderlyingType();
ThrowIfUnsignedType(enumType, syncVar);
}
}
}
}
11 changes: 11 additions & 0 deletions Assets/Mirage/Weaver/Processors/SyncVars/ZigZagFinder.cs.meta

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

4 changes: 4 additions & 0 deletions Assets/Mirage/Weaver/WeaverExceptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,8 @@ internal class BitCountException : SyncVarException
{
public BitCountException(string message, MemberReference memberReference) : base(message, memberReference) { }
}
internal class ZigZagException : SyncVarException
{
public ZigZagException(string message, MemberReference memberReference) : base(message, memberReference) { }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using Mirage.Serialization;
using Mirage.Tests.Runtime.ClientServer;
using NUnit.Framework;
using UnityEngine;
using UnityEngine.TestTools;

namespace Mirage.Tests.Runtime.Generated.BitCountAttributeTests
Expand Down Expand Up @@ -33,24 +34,33 @@ public void RpcSomeFunction([BitCount(4)] MyByteEnum myParam)
}
public class BitCountTest_MyByteEnum_4 : ClientServerSetup<BitCountBehaviour_MyByteEnum_4>
{
const MyByteEnum value = (MyByteEnum)3;

[Test]
public void SyncVarIsBitPacked()
{
var behaviour = new BitCountBehaviour_MyByteEnum_4();
serverComponent.myValue = value;

using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter())
{
behaviour.SerializeSyncVars(writer, true);
serverComponent.SerializeSyncVars(writer, true);

Assert.That(writer.BitPosition, Is.EqualTo(4));

using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment()))
{
clientComponent.DeserializeSyncVars(reader, true);
Assert.That(reader.BitPosition, Is.EqualTo(4));

Assert.That(clientComponent.myValue, Is.EqualTo(value));
}
}
}

// [UnityTest]
// [Ignore("Rpc not supported yet")]
public IEnumerator RpcIsBitPacked()
{
const MyByteEnum value = (MyByteEnum)3;

int called = 0;
clientComponent.onRpc += (v) => { called++; Assert.That(v, Is.EqualTo(value)); };
Expand Down
16 changes: 13 additions & 3 deletions Assets/Tests/Generated/BitCountTests/BitCountBehaviour_MyEnum_4.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using Mirage.Serialization;
using Mirage.Tests.Runtime.ClientServer;
using NUnit.Framework;
using UnityEngine;
using UnityEngine.TestTools;

namespace Mirage.Tests.Runtime.Generated.BitCountAttributeTests
Expand Down Expand Up @@ -33,24 +34,33 @@ public void RpcSomeFunction([BitCount(4)] MyEnum myParam)
}
public class BitCountTest_MyEnum_4 : ClientServerSetup<BitCountBehaviour_MyEnum_4>
{
const MyEnum value = (MyEnum)3;

[Test]
public void SyncVarIsBitPacked()
{
var behaviour = new BitCountBehaviour_MyEnum_4();
serverComponent.myValue = value;

using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter())
{
behaviour.SerializeSyncVars(writer, true);
serverComponent.SerializeSyncVars(writer, true);

Assert.That(writer.BitPosition, Is.EqualTo(4));

using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment()))
{
clientComponent.DeserializeSyncVars(reader, true);
Assert.That(reader.BitPosition, Is.EqualTo(4));

Assert.That(clientComponent.myValue, Is.EqualTo(value));
}
}
}

// [UnityTest]
// [Ignore("Rpc not supported yet")]
public IEnumerator RpcIsBitPacked()
{
const MyEnum value = (MyEnum)3;

int called = 0;
clientComponent.onRpc += (v) => { called++; Assert.That(v, Is.EqualTo(value)); };
Expand Down
16 changes: 13 additions & 3 deletions Assets/Tests/Generated/BitCountTests/BitCountBehaviour_int_10.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using Mirage.Serialization;
using Mirage.Tests.Runtime.ClientServer;
using NUnit.Framework;
using UnityEngine;
using UnityEngine.TestTools;

namespace Mirage.Tests.Runtime.Generated.BitCountAttributeTests
Expand All @@ -25,24 +26,33 @@ public void RpcSomeFunction([BitCount(10)] int myParam)
}
public class BitCountTest_int_10 : ClientServerSetup<BitCountBehaviour_int_10>
{
const int value = 20;

[Test]
public void SyncVarIsBitPacked()
{
var behaviour = new BitCountBehaviour_int_10();
serverComponent.myValue = value;

using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter())
{
behaviour.SerializeSyncVars(writer, true);
serverComponent.SerializeSyncVars(writer, true);

Assert.That(writer.BitPosition, Is.EqualTo(10));

using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment()))
{
clientComponent.DeserializeSyncVars(reader, true);
Assert.That(reader.BitPosition, Is.EqualTo(10));

Assert.That(clientComponent.myValue, Is.EqualTo(value));
}
}
}

// [UnityTest]
// [Ignore("Rpc not supported yet")]
public IEnumerator RpcIsBitPacked()
{
const int value = 20;

int called = 0;
clientComponent.onRpc += (v) => { called++; Assert.That(v, Is.EqualTo(value)); };
Expand Down
16 changes: 13 additions & 3 deletions Assets/Tests/Generated/BitCountTests/BitCountBehaviour_int_17.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using Mirage.Serialization;
using Mirage.Tests.Runtime.ClientServer;
using NUnit.Framework;
using UnityEngine;
using UnityEngine.TestTools;

namespace Mirage.Tests.Runtime.Generated.BitCountAttributeTests
Expand All @@ -25,24 +26,33 @@ public void RpcSomeFunction([BitCount(17)] int myParam)
}
public class BitCountTest_int_17 : ClientServerSetup<BitCountBehaviour_int_17>
{
const int value = 20;

[Test]
public void SyncVarIsBitPacked()
{
var behaviour = new BitCountBehaviour_int_17();
serverComponent.myValue = value;

using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter())
{
behaviour.SerializeSyncVars(writer, true);
serverComponent.SerializeSyncVars(writer, true);

Assert.That(writer.BitPosition, Is.EqualTo(17));

using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment()))
{
clientComponent.DeserializeSyncVars(reader, true);
Assert.That(reader.BitPosition, Is.EqualTo(17));

Assert.That(clientComponent.myValue, Is.EqualTo(value));
}
}
}

// [UnityTest]
// [Ignore("Rpc not supported yet")]
public IEnumerator RpcIsBitPacked()
{
const int value = 20;

int called = 0;
clientComponent.onRpc += (v) => { called++; Assert.That(v, Is.EqualTo(value)); };
Expand Down
16 changes: 13 additions & 3 deletions Assets/Tests/Generated/BitCountTests/BitCountBehaviour_int_32.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using Mirage.Serialization;
using Mirage.Tests.Runtime.ClientServer;
using NUnit.Framework;
using UnityEngine;
using UnityEngine.TestTools;

namespace Mirage.Tests.Runtime.Generated.BitCountAttributeTests
Expand All @@ -25,24 +26,33 @@ public void RpcSomeFunction([BitCount(32)] int myParam)
}
public class BitCountTest_int_32 : ClientServerSetup<BitCountBehaviour_int_32>
{
const int value = 20;

[Test]
public void SyncVarIsBitPacked()
{
var behaviour = new BitCountBehaviour_int_32();
serverComponent.myValue = value;

using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter())
{
behaviour.SerializeSyncVars(writer, true);
serverComponent.SerializeSyncVars(writer, true);

Assert.That(writer.BitPosition, Is.EqualTo(32));

using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment()))
{
clientComponent.DeserializeSyncVars(reader, true);
Assert.That(reader.BitPosition, Is.EqualTo(32));

Assert.That(clientComponent.myValue, Is.EqualTo(value));
}
}
}

// [UnityTest]
// [Ignore("Rpc not supported yet")]
public IEnumerator RpcIsBitPacked()
{
const int value = 20;

int called = 0;
clientComponent.onRpc += (v) => { called++; Assert.That(v, Is.EqualTo(value)); };
Expand Down

0 comments on commit ccef5fb

Please sign in to comment.