-
-
Notifications
You must be signed in to change notification settings - Fork 65
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: attributes to calculate bit count of a given range (#902)
* feat: attributes to calculate bit count of a given range * weaver tests * generated tests * implementing bitcount from range * updating tests * fixing test * fixing bitcount breaking for no large value
- Loading branch information
1 parent
7cfc6a1
commit 1c22ea6
Showing
48 changed files
with
1,692 additions
and
38 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
89 changes: 89 additions & 0 deletions
89
Assets/Mirage/Weaver/Processors/SyncVars/BitCountFromRangeFinder.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
using System; | ||
using Mirage.Serialization; | ||
using Mono.Cecil; | ||
using Mono.Cecil.Cil; | ||
|
||
namespace Mirage.Weaver.SyncVars | ||
{ | ||
public static class BitCountFromRangeFinder | ||
{ | ||
internal static (int? BitCount, OpCode? BitCountConvert, int? MinValue) GetBitFoundFromRange(FieldDefinition syncVar, bool hasBitCount) | ||
{ | ||
CustomAttribute attribute = syncVar.GetCustomAttribute<BitCountFromRangeAttribute>(); | ||
|
||
if (hasBitCount) | ||
throw new BitCountFromRangeException($"[BitCountFromRange] can't be used with [BitCount]", syncVar); | ||
|
||
int min = (int)attribute.ConstructorArguments[0].Value; | ||
int max = (int)attribute.ConstructorArguments[1].Value; | ||
|
||
if (min >= max) | ||
throw new BitCountFromRangeException("Max must be greater than min", syncVar); | ||
|
||
long minAllowedMin = GetTypeMin(syncVar.FieldType, syncVar); | ||
long maxAllowedMax = GetTypeMax(syncVar.FieldType, syncVar); | ||
|
||
if (min < minAllowedMin) | ||
throw new BitCountException($"Min must be less than types min value, min:{min}, min allowed:{minAllowedMin}, type:{syncVar.FieldType.Name}", syncVar); | ||
|
||
if (max > maxAllowedMax) | ||
throw new BitCountException($"Max must be greater than types max value, max:{max}, max allowed:{maxAllowedMax}, type:{syncVar.FieldType.Name}", syncVar); | ||
|
||
// make sure to cast max to long so incase range is bigger than int value | ||
long range = checked((long)max - min); | ||
int bitCount = (int)Math.Floor(Math.Log(range, 2)) + 1; | ||
if (bitCount < 0 || bitCount > 32) | ||
throw new OverflowException($"Bit Count could not be calcualted, min:{min}, max:{max}, bitCount:{bitCount}"); | ||
|
||
int? minResult; | ||
if (min == 0) minResult = null; | ||
else minResult = min; | ||
|
||
return (bitCount, BitCountFinder.GetConvertType(syncVar.FieldType), minResult); | ||
} | ||
|
||
static int GetTypeMax(TypeReference type, FieldDefinition syncVar) | ||
{ | ||
if (type.Is<byte>()) return byte.MaxValue; | ||
if (type.Is<ushort>()) return ushort.MaxValue; | ||
if (type.Is<short>()) return short.MaxValue; | ||
if (type.Is<int>() | ||
|| type.Is<uint>()) return int.MaxValue; | ||
|
||
if (type.Resolve().IsEnum) | ||
{ | ||
// use underlying enum type for max size | ||
TypeReference enumType = type.Resolve().GetEnumUnderlyingType(); | ||
return GetTypeMax(enumType, syncVar); | ||
} | ||
|
||
// long is not a support type because it is not commonly used and | ||
// would take a lot more code to make it works with all possible | ||
// ranges of values: | ||
// - min and max both negative long values | ||
// - min and max both above long.max | ||
// we would need a type bigger than long in order to easily handle | ||
// both of these, and that is before dealing with IL stuff | ||
|
||
throw new BitCountFromRangeException($"{type.FullName} is not a supported type for [BitCountFromRange]", syncVar); | ||
} | ||
|
||
static int GetTypeMin(TypeReference type, FieldDefinition syncVar) | ||
{ | ||
if (type.Is<byte>()) return byte.MinValue; | ||
if (type.Is<ushort>()) return ushort.MinValue; | ||
if (type.Is<short>()) return short.MinValue; | ||
if (type.Is<int>()) return int.MinValue; | ||
if (type.Is<uint>()) return 0; | ||
|
||
if (type.Resolve().IsEnum) | ||
{ | ||
// use underlying enum type for max size | ||
TypeReference enumType = type.Resolve().GetEnumUnderlyingType(); | ||
return GetTypeMin(enumType, syncVar); | ||
} | ||
|
||
throw new BitCountFromRangeException($"{type.FullName} is not a supported type for [BitCountFromRange]", syncVar); | ||
} | ||
} | ||
} |
11 changes: 11 additions & 0 deletions
11
Assets/Mirage/Weaver/Processors/SyncVars/BitCountFromRangeFinder.cs.meta
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
82 changes: 82 additions & 0 deletions
82
Assets/Tests/Generated/BitCountFromRangeTests/BitCountBehaviour_MyByteEnum_0_3.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
// DO NOT EDIT: GENERATED BY BitCountFromRangeTestGenerator.cs | ||
|
||
using System; | ||
using System.Collections; | ||
using Mirage.Serialization; | ||
using Mirage.Tests.Runtime.ClientServer; | ||
using NUnit.Framework; | ||
using UnityEngine; | ||
using UnityEngine.TestTools; | ||
|
||
namespace Mirage.Tests.Runtime.Generated.BitCountFromRangeAttributeTests | ||
{ | ||
[System.Serializable] | ||
public enum MyByteEnum : byte | ||
{ | ||
None = 0, | ||
Slow = 1, | ||
Fast = 2, | ||
ReallyFast = 3, | ||
} | ||
public class BitCountRangeBehaviour_MyByteEnum_0_3 : NetworkBehaviour | ||
{ | ||
[BitCountFromRange(0, 3)] | ||
[SyncVar] public MyByteEnum myValue; | ||
|
||
public event Action<MyByteEnum> onRpc; | ||
|
||
[ClientRpc] | ||
public void RpcSomeFunction([BitCountFromRange(0, 3)] MyByteEnum myParam) | ||
{ | ||
onRpc?.Invoke(myParam); | ||
} | ||
} | ||
public class BitCountRangeTest_MyByteEnum_0_3 : ClientServerSetup<BitCountRangeBehaviour_MyByteEnum_0_3> | ||
{ | ||
const MyByteEnum value = (MyByteEnum)3; | ||
|
||
[Test] | ||
public void SyncVarIsBitPacked() | ||
{ | ||
serverComponent.myValue = value; | ||
|
||
using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) | ||
{ | ||
serverComponent.SerializeSyncVars(writer, true); | ||
|
||
Assert.That(writer.BitPosition, Is.EqualTo(2)); | ||
|
||
using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment())) | ||
{ | ||
clientComponent.DeserializeSyncVars(reader, true); | ||
Assert.That(reader.BitPosition, Is.EqualTo(2)); | ||
|
||
Assert.That(clientComponent.myValue, Is.EqualTo(value)); | ||
} | ||
} | ||
} | ||
|
||
// [UnityTest] | ||
// [Ignore("Rpc not supported yet")] | ||
public IEnumerator RpcIsBitPacked() | ||
{ | ||
int called = 0; | ||
clientComponent.onRpc += (v) => { called++; Assert.That(v, Is.EqualTo(value)); }; | ||
|
||
client.MessageHandler.UnregisterHandler<RpcMessage>(); | ||
int payloadSize = 0; | ||
client.MessageHandler.RegisterHandler<RpcMessage>((player, msg) => | ||
{ | ||
// store value in variable because assert will throw and be catch by message wrapper | ||
payloadSize = msg.payload.Count; | ||
clientObjectManager.OnRpcMessage(msg); | ||
}); | ||
|
||
|
||
serverComponent.RpcSomeFunction(value); | ||
yield return null; | ||
Assert.That(called, Is.EqualTo(1)); | ||
Assert.That(payloadSize, Is.EqualTo(1), $"%%BIT_COUNT%% bits is 1 bytes in payload"); | ||
} | ||
} | ||
} |
11 changes: 11 additions & 0 deletions
11
Assets/Tests/Generated/BitCountFromRangeTests/BitCountBehaviour_MyByteEnum_0_3.cs.meta
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Oops, something went wrong.