Skip to content

Commit

Permalink
feat: adding attribute that instructs Weaver to write a type as generic
Browse files Browse the repository at this point in the history
Adding [WeaverWriteAsGeneric] that will tell weaver to write a type using the  generic write functions instead of creating functions for it.
  • Loading branch information
James-Frowen committed Jun 19, 2023
1 parent 7eab498 commit 1b1e4e6
Show file tree
Hide file tree
Showing 5 changed files with 97 additions and 8 deletions.
24 changes: 19 additions & 5 deletions Assets/Mirage/Runtime/Serialization/WeaverAttributes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,23 @@ namespace Mirage.Serialization
[AttributeUsage(AttributeTargets.Method)]
public sealed class WeaverIgnoreAttribute : Attribute { }

/// <summary>
/// Tells Weaver to serialize a type as generic instead of creating a custom functions.
/// <para>
/// Use this when you have created and assigned your own Read/Write functions
/// or when you can't use extension methods for types and need to manually assign them.
/// </para>
/// <para>
/// This will cause Weaver to use the <see cref="Writer{T}.Write"/> and <see cref="Reader{T}.Read"/> generic functions instead of creating new ones.
/// You must set these functions manually when using this attribute.
/// </para>
/// </summary>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum | AttributeTargets.Interface)]
public sealed class WeaverWriteAsGenericAttribute : Attribute { }

/// <summary>
/// Tells weaver how many bits to sue for field
/// <para>Only works with interager fields (byte, int, ulong, enums etc)</para>
/// <para>Only works with integer fields (byte, int, ulong, enums etc)</para>
/// <para>
/// 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>
Expand All @@ -39,7 +53,7 @@ public class BitCountFromRangeAttribute : Attribute
}

/// <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
/// Used along size <see cref="BitCountAttribute"/> to encodes a integer value using <see cref="ZigZag"/> so that both positive and negative values can be sent
/// <para>Also See: <see href="https://miragenet.github.io/Mirage/docs/guides/bit-packing/bit-count-from-rangel">Bit Packing Documentation</see></para>
/// </summary>
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter)]
Expand All @@ -56,7 +70,7 @@ public class ZigZagEncodeAttribute : Attribute
public class FloatPackAttribute : Attribute
{
/// <param name="max">Max value of the float</param>
/// <param name="precision">Smallest possible value of the field. Real precision woll be caculated using bitcount but will always be lower than this parameter</param>
/// <param name="precision">Smallest possible value of the field. Real precision will be calculated using bitcount but will always be lower than this parameter</param>
public FloatPackAttribute(float max, float precision) { }

/// <param name="max">Max value of the float</param>
Expand Down Expand Up @@ -99,7 +113,7 @@ public class QuaternionPackAttribute : Attribute
/// <summary>
/// Tells weaver the max range for small, medium and large values.
/// <para>Allows small values to be sent using less bits</para>
/// <para>Only works with interager fields (byte, int, ulong, enums etc)</para>
/// <para>Only works with integer fields (byte, int, ulong, enums etc)</para>
/// </summary>
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter)]
public class VarIntAttribute : Attribute
Expand All @@ -113,7 +127,7 @@ public class VarIntAttribute : Attribute
/// <summary>
/// Tells weaver the block size to use for packing int values
/// <para>Allows small values to be sent using less bits</para>
/// <para>Only works with interager fields (byte, int, ulong, enums etc)</para>
/// <para>Only works with integer fields (byte, int, ulong, enums etc)</para>
/// </summary>
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter)]
public class VarIntBlocksAttribute : Attribute
Expand Down
8 changes: 6 additions & 2 deletions Assets/Mirage/Weaver/SerializeFunctionBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq.Expressions;
using Mirage.Serialization;
using Mono.Cecil;
using Mono.Cecil.Cil;

Expand Down Expand Up @@ -93,7 +94,7 @@ public MethodReference TryGetFunction(TypeReference typeReference, SequencePoint
public MethodReference GetFunction_Throws(TypeReference typeReference)
{
// if is <T> then just return generic write./read with T as the generic argument
if (typeReference.IsGenericParameter)
if (typeReference.IsGenericParameter || HasAsGenericAttribute(typeReference))
{
return CreateGenericFunction(typeReference);
}
Expand All @@ -117,7 +118,10 @@ public MethodReference GetFunction_Throws(TypeReference typeReference)
}
}


private bool HasAsGenericAttribute(TypeReference typeReference)
{
return typeReference.Resolve().HasCustomAttribute<WeaverWriteAsGenericAttribute>();
}

private MethodReference GenerateFunction(TypeReference typeReference)
{
Expand Down
1 change: 0 additions & 1 deletion Assets/Tests/Runtime/Serialization/WeaverIgnoreTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ public void DoesNotUseCustomWriter()
Assert.That(copy.second, Is.EqualTo(data.second));
reader.Dispose();
}

}
public struct MyCustomType
{
Expand Down
61 changes: 61 additions & 0 deletions Assets/Tests/Runtime/Serialization/WeaverWriteAsGenericTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
using Mirage.Serialization;
using NUnit.Framework;

namespace Mirage.Tests.Runtime.Serialization
{
public class WeaverWriteAsGenericTest
{
[Test]
public void WeaverShouldWriteUsingGenericButNotCreateThem()
{
Assert.That(Writer<IMyInterface>.Write, Is.Null, "Weaver should not have set writer");
Assert.That(Reader<IMyInterface>.Read, Is.Null, "Weaver should not have set read");

var writeCalled = 0;
var readCalled = 0;

Writer<IMyInterface>.Write = (writer, value) => writeCalled++;
Reader<IMyInterface>.Read = (reader) => { readCalled++; return null; };

try
{
Assert.That(writeCalled, Is.EqualTo(0));
Assert.That(readCalled, Is.EqualTo(0));

var writer = new NetworkWriter(1300);
writer.Write(new MessageWithInterface());

Assert.That(writeCalled, Is.EqualTo(1));
Assert.That(readCalled, Is.EqualTo(0));

var reader = new NetworkReader();
reader.Reset(writer.ToArraySegment());
var copy = reader.Read<MessageWithInterface>();

Assert.That(writeCalled, Is.EqualTo(1));
Assert.That(readCalled, Is.EqualTo(1));
}
finally
{
// they started null, so we need to reset them to null
// if they dont start null, then the assert checks that the start will throw and stop us from setting them in test
Writer<IMyInterface>.Write = null;
Reader<IMyInterface>.Read = null;
}
}

// a type that weaver can not create a writer for, it should instead write it as a generic
[WeaverWriteAsGeneric]
public interface IMyInterface
{
int Id { get; set; }
}

// use the interface in a message so we can check what methods it calls when writing
[NetworkMessage]
public struct MessageWithInterface
{
public IMyInterface data;
}
}
}

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

0 comments on commit 1b1e4e6

Please sign in to comment.