feat: allowing lists to automatically be sent in Commands, Rpc, and Messages (#2151)

* weaver tests for list read write

* generated tests for list read write

* adding method to check for list type

* temp

* weaver functions for creating read write for list

* generating tests for lists
This commit is contained in:
James Frowen 2020-08-15 17:00:55 +01:00 committed by GitHub
parent 5b179e8917
commit 381e5a115b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 673 additions and 21 deletions

View File

@ -94,6 +94,24 @@ public static bool IsArrayType(this TypeReference tr)
return true;
}
public static bool IsArraySegment(this TypeDefinition td)
{
return td.FullName.StartsWith("System.ArraySegment`1", System.StringComparison.Ordinal);
}
public static bool IsArraySegment(this TypeReference td)
{
return td.FullName.StartsWith("System.ArraySegment`1", System.StringComparison.Ordinal);
}
public static bool IsList(this TypeDefinition td)
{
return td.FullName.StartsWith("System.Collections.Generic.List`1", System.StringComparison.Ordinal);
}
public static bool IsList(this TypeReference td)
{
return td.FullName.StartsWith("System.Collections.Generic.List`1", System.StringComparison.Ordinal);
}
public static bool CanBeResolved(this TypeReference parent)
{
while (parent != null)

View File

@ -43,13 +43,13 @@ public static MethodReference GetReadFunc(TypeReference variable, int recursionC
return newReaderFunc;
}
TypeDefinition td = variable.Resolve();
if (td == null)
TypeDefinition variableType = variable.Resolve();
if (variableType == null)
{
Weaver.Error($"{variable.Name} is not a supported type", variable);
return null;
}
if (td.IsDerivedFrom(WeaverTypes.ComponentType))
if (variableType.IsDerivedFrom(WeaverTypes.ComponentType))
{
Weaver.Error($"Cannot generate reader for component type {variable.Name}. Use a supported type or provide a custom reader", variable);
return null;
@ -70,25 +70,29 @@ public static MethodReference GetReadFunc(TypeReference variable, int recursionC
Weaver.Error($"Cannot pass type {variable.Name} by reference", variable);
return null;
}
if (td.HasGenericParameters && !td.FullName.StartsWith("System.ArraySegment`1", System.StringComparison.Ordinal))
if (variableType.HasGenericParameters && !variableType.IsArraySegment() && !variableType.IsList())
{
Weaver.Error($"Cannot generate reader for generic variable {variable.Name}. Use a supported type or provide a custom reader", variable);
return null;
}
if (td.IsInterface)
if (variableType.IsInterface)
{
Weaver.Error($"Cannot generate reader for interface {variable.Name}. Use a supported type or provide a custom reader", variable);
return null;
}
if (td.IsEnum)
if (variableType.IsEnum)
{
return GetReadFunc(td.GetEnumUnderlyingType(), recursionCount);
return GetReadFunc(variableType.GetEnumUnderlyingType(), recursionCount);
}
else if (variable.FullName.StartsWith("System.ArraySegment`1", System.StringComparison.Ordinal))
else if (variableType.IsArraySegment())
{
newReaderFunc = GenerateArraySegmentReadFunc(variable, recursionCount);
}
else if (variableType.IsList())
{
newReaderFunc = GenerateListReadFunc(variable, recursionCount);
}
else
{
newReaderFunc = GenerateClassOrStructReadFunction(variable, recursionCount);
@ -222,7 +226,7 @@ static MethodDefinition GenerateArraySegmentReadFunc(TypeReference variable, int
return null;
}
string functionName = "_ReadArraySegment_" + variable.GetElementType().Name + "_";
string functionName = "_ReadArraySegment_" + elementType.Name + "_";
if (variable.DeclaringType != null)
{
functionName += variable.DeclaringType.Name;
@ -301,6 +305,111 @@ static MethodDefinition GenerateArraySegmentReadFunc(TypeReference variable, int
return readerFunc;
}
static MethodDefinition GenerateListReadFunc(TypeReference variable, int recursionCount)
{
GenericInstanceType genericInstance = (GenericInstanceType)variable;
TypeReference elementType = genericInstance.GenericArguments[0];
MethodReference elementReadFunc = GetReadFunc(elementType, recursionCount + 1);
if (elementReadFunc == null)
{
Weaver.Error($"Cannot generate reader for List because element {elementType.Name} does not have a reader. Use a supported type or provide a custom reader", variable);
return null;
}
string functionName = "_ReadList_" + elementType.Name + "_";
if (variable.DeclaringType != null)
{
functionName += variable.DeclaringType.Name;
}
else
{
functionName += "None";
}
// create new reader for this type
MethodDefinition readerFunc = new MethodDefinition(functionName,
MethodAttributes.Public |
MethodAttributes.Static |
MethodAttributes.HideBySig,
variable);
readerFunc.Parameters.Add(new ParameterDefinition("reader", ParameterAttributes.None, Weaver.CurrentAssembly.MainModule.ImportReference(WeaverTypes.NetworkReaderType)));
readerFunc.Body.Variables.Add(new VariableDefinition(WeaverTypes.int32Type));
readerFunc.Body.Variables.Add(new VariableDefinition(variable));
readerFunc.Body.Variables.Add(new VariableDefinition(WeaverTypes.int32Type));
readerFunc.Body.InitLocals = true;
ILProcessor worker = readerFunc.Body.GetILProcessor();
// int count = reader.ReadPackedInt32();
worker.Append(worker.Create(OpCodes.Ldarg_0));
worker.Append(worker.Create(OpCodes.Call, GetReadFunc(WeaverTypes.int32Type)));
worker.Append(worker.Create(OpCodes.Stloc_0));
// -1 is null list, so if count is less than 0 return null
// if (count < 0) {
// return null
// }
worker.Append(worker.Create(OpCodes.Ldloc_0));
worker.Append(worker.Create(OpCodes.Ldc_I4_0));
Instruction labelEmptyArray = worker.Create(OpCodes.Nop);
worker.Append(worker.Create(OpCodes.Bge, labelEmptyArray));
// return null
worker.Append(worker.Create(OpCodes.Ldnull));
worker.Append(worker.Create(OpCodes.Ret));
worker.Append(labelEmptyArray);
// List<T> list = new List<T>();
worker.Append(worker.Create(OpCodes.Newobj, WeaverTypes.ListConstructorReference.MakeHostInstanceGeneric(genericInstance)));
worker.Append(worker.Create(OpCodes.Stloc_1));
// loop through array and deserialize each element
// generates code like this
// for (int i=0; i< length ; i++)
// {
// list[i] = reader.ReadXXX();
// }
worker.Append(worker.Create(OpCodes.Ldc_I4_0));
worker.Append(worker.Create(OpCodes.Stloc_2));
Instruction labelHead = worker.Create(OpCodes.Nop);
worker.Append(worker.Create(OpCodes.Br, labelHead));
// loop body
Instruction labelBody = worker.Create(OpCodes.Nop);
worker.Append(labelBody);
MethodReference addItem = WeaverTypes.ListAddReference.MakeHostInstanceGeneric(genericInstance);
// list.Add(reader.ReadT());
worker.Append(worker.Create(OpCodes.Ldloc_1)); // list
worker.Append(worker.Create(OpCodes.Ldarg_0)); // reader
worker.Append(worker.Create(OpCodes.Call, elementReadFunc)); // Read
worker.Append(worker.Create(OpCodes.Call, addItem)); // set_Item
// end for loop
// for loop i++
worker.Append(worker.Create(OpCodes.Ldloc_2));
worker.Append(worker.Create(OpCodes.Ldc_I4_1));
worker.Append(worker.Create(OpCodes.Add));
worker.Append(worker.Create(OpCodes.Stloc_2));
// loop while check
worker.Append(labelHead);
// for loop i < count
worker.Append(worker.Create(OpCodes.Ldloc_2));
worker.Append(worker.Create(OpCodes.Ldloc_0));
worker.Append(worker.Create(OpCodes.Blt, labelBody));
// return value;
worker.Append(worker.Create(OpCodes.Ldloc_1));
worker.Append(worker.Create(OpCodes.Ret));
return readerFunc;
}
static MethodDefinition GenerateClassOrStructReadFunction(TypeReference variable, int recursionCount)
{
if (recursionCount > MaxRecursionCount)

View File

@ -63,6 +63,13 @@ public static class WeaverTypes
public static MethodReference ArraySegmentOffsetReference;
public static MethodReference ArraySegmentCountReference;
// list
public static TypeReference ListType;
public static MethodReference ListConstructorReference;
public static MethodReference ListCountReference;
public static MethodReference ListGetItemReference;
public static MethodReference ListAddReference;
// system types
public static TypeReference voidType;
public static TypeReference singleType;
@ -159,6 +166,12 @@ public static void SetupTargetTypes(AssemblyDefinition unityAssembly, AssemblyDe
ArraySegmentOffsetReference = Resolvers.ResolveProperty(ArraySegmentType, currentAssembly, "Offset");
ArraySegmentConstructorReference = Resolvers.ResolveMethod(ArraySegmentType, currentAssembly, ".ctor");
ListType = ImportSystemModuleType(currentAssembly, systemModule, "System.Collections.Generic.List`1");
ListCountReference = Resolvers.ResolveProperty(ListType, currentAssembly, "Count");
ListGetItemReference = Resolvers.ResolveMethod(ListType, currentAssembly, "get_Item");
ListAddReference = Resolvers.ResolveMethod(ListType, currentAssembly, "Add");
ListConstructorReference = Resolvers.ResolveMethod(ListType, currentAssembly, ".ctor");
NetworkReaderType = mirrorAssembly.MainModule.GetType("Mirror.NetworkReader");
NetworkWriterType = mirrorAssembly.MainModule.GetType("Mirror.NetworkWriter");
TypeReference pooledNetworkWriterTmp = mirrorAssembly.MainModule.GetType("Mirror.PooledNetworkWriter");

View File

@ -50,13 +50,14 @@ public static MethodReference GetWriteFunc(TypeReference variable, int recursion
Weaver.Error($"Cannot pass {variable.Name} by reference", variable);
return null;
}
TypeDefinition td = variable.Resolve();
if (td == null)
TypeDefinition variableType = variable.Resolve();
if (variableType == null)
{
Weaver.Error($"{variable.Name} is not a supported type. Use a supported type or provide a custom writer", variable);
return null;
}
if (td.IsDerivedFrom(WeaverTypes.ComponentType))
if (variableType.IsDerivedFrom(WeaverTypes.ComponentType))
{
Weaver.Error($"Cannot generate writer for component type {variable.Name}. Use a supported type or provide a custom writer", variable);
return null;
@ -71,12 +72,12 @@ public static MethodReference GetWriteFunc(TypeReference variable, int recursion
Weaver.Error($"Cannot generate writer for {variable.Name}. Use a supported type or provide a custom writer", variable);
return null;
}
if (td.HasGenericParameters && !td.FullName.StartsWith("System.ArraySegment`1", System.StringComparison.Ordinal))
if (variableType.HasGenericParameters && !variableType.IsArraySegment() && !variableType.IsList())
{
Weaver.Error($"Cannot generate writer for generic type {variable.Name}. Use a supported type or provide a custom writer", variable);
return null;
}
if (td.IsInterface)
if (variableType.IsInterface)
{
Weaver.Error($"Cannot generate writer for interface {variable.Name}. Use a supported type or provide a custom writer", variable);
return null;
@ -86,10 +87,14 @@ public static MethodReference GetWriteFunc(TypeReference variable, int recursion
{
return GetWriteFunc(variable.Resolve().GetEnumUnderlyingType(), recursionCount);
}
else if (variable.FullName.StartsWith("System.ArraySegment`1", System.StringComparison.Ordinal))
else if (variable.IsArraySegment())
{
newWriterFunc = GenerateArraySegmentWriteFunc(variable, recursionCount);
}
else if (variable.IsList())
{
newWriterFunc = GenerateListWriteFunc(variable, recursionCount);
}
else
{
newWriterFunc = GenerateClassOrStructWriterFunction(variable, recursionCount);
@ -243,8 +248,10 @@ static MethodDefinition GenerateArrayWriteFunc(TypeReference variable, int recur
worker.Append(worker.Create(OpCodes.Call, GetWriteFunc(WeaverTypes.int32Type)));
worker.Append(worker.Create(OpCodes.Ret));
// int length = value.Length;
// else not null
worker.Append(labelNull);
// int length = value.Length;
worker.Append(worker.Create(OpCodes.Ldarg_1));
worker.Append(worker.Create(OpCodes.Ldlen));
worker.Append(worker.Create(OpCodes.Stloc_0));
@ -267,8 +274,8 @@ static MethodDefinition GenerateArrayWriteFunc(TypeReference variable, int recur
worker.Append(worker.Create(OpCodes.Ldarg_0));
worker.Append(worker.Create(OpCodes.Ldarg_1));
worker.Append(worker.Create(OpCodes.Ldloc_1));
worker.Append(worker.Create(OpCodes.Ldelema, variable.GetElementType()));
worker.Append(worker.Create(OpCodes.Ldobj, variable.GetElementType()));
worker.Append(worker.Create(OpCodes.Ldelema, elementType));
worker.Append(worker.Create(OpCodes.Ldobj, elementType));
worker.Append(worker.Create(OpCodes.Call, elementWriteFunc));
worker.Append(worker.Create(OpCodes.Ldloc_1));
@ -384,5 +391,115 @@ static MethodDefinition GenerateArraySegmentWriteFunc(TypeReference variable, in
return writerFunc;
}
static MethodDefinition GenerateListWriteFunc(TypeReference variable, int recursionCount)
{
GenericInstanceType genericInstance = (GenericInstanceType)variable;
TypeReference elementType = genericInstance.GenericArguments[0];
MethodReference elementWriteFunc = GetWriteFunc(elementType, recursionCount + 1);
if (elementWriteFunc == null)
{
Weaver.Error($"Cannot generate writer for List because element {elementType.Name} does not have a writer. Use a supported type or provide a custom writer", variable);
return null;
}
string functionName = "_WriteList_" + elementType.Name + "_";
if (variable.DeclaringType != null)
{
functionName += variable.DeclaringType.Name;
}
else
{
functionName += "None";
}
// create new writer for this type
MethodDefinition writerFunc = new MethodDefinition(functionName,
MethodAttributes.Public |
MethodAttributes.Static |
MethodAttributes.HideBySig,
WeaverTypes.voidType);
writerFunc.Parameters.Add(new ParameterDefinition("writer", ParameterAttributes.None, Weaver.CurrentAssembly.MainModule.ImportReference(WeaverTypes.NetworkWriterType)));
writerFunc.Parameters.Add(new ParameterDefinition("value", ParameterAttributes.None, variable));
writerFunc.Body.Variables.Add(new VariableDefinition(WeaverTypes.int32Type));
writerFunc.Body.Variables.Add(new VariableDefinition(WeaverTypes.int32Type));
writerFunc.Body.InitLocals = true;
ILProcessor worker = writerFunc.Body.GetILProcessor();
// if (value == null)
// {
// writer.WritePackedInt32(-1);
// return;
// }
Instruction labelNull = worker.Create(OpCodes.Nop);
worker.Append(worker.Create(OpCodes.Ldarg_1));
worker.Append(worker.Create(OpCodes.Brtrue, labelNull));
worker.Append(worker.Create(OpCodes.Ldarg_0));
worker.Append(worker.Create(OpCodes.Ldc_I4_M1));
worker.Append(worker.Create(OpCodes.Call, GetWriteFunc(WeaverTypes.int32Type)));
worker.Append(worker.Create(OpCodes.Ret));
// else not null
worker.Append(labelNull);
MethodReference countref = WeaverTypes.ListCountReference.MakeHostInstanceGeneric(genericInstance);
// int count = value.Count;
worker.Append(worker.Create(OpCodes.Ldarg_1));
worker.Append(worker.Create(OpCodes.Call, countref));
worker.Append(worker.Create(OpCodes.Stloc_0));
// writer.WritePackedInt32(count);
worker.Append(worker.Create(OpCodes.Ldarg_0));
worker.Append(worker.Create(OpCodes.Ldloc_0));
worker.Append(worker.Create(OpCodes.Call, GetWriteFunc(WeaverTypes.int32Type)));
// Loop through the List<T> and call the writer for each element.
// generates this:
// for (int i=0; i < count; i++)
// {
// writer.WriteT(value[i]);
// }
worker.Append(worker.Create(OpCodes.Ldc_I4_0));
worker.Append(worker.Create(OpCodes.Stloc_1));
Instruction labelHead = worker.Create(OpCodes.Nop);
worker.Append(worker.Create(OpCodes.Br, labelHead));
// loop body
Instruction labelBody = worker.Create(OpCodes.Nop);
worker.Append(labelBody);
MethodReference getItem = WeaverTypes.ListGetItemReference.MakeHostInstanceGeneric(genericInstance);
// writer.Write(value[i]);
worker.Append(worker.Create(OpCodes.Ldarg_0)); // writer
worker.Append(worker.Create(OpCodes.Ldarg_1)); // value
worker.Append(worker.Create(OpCodes.Ldloc_1)); // i
worker.Append(worker.Create(OpCodes.Call, getItem)); //get_Item
worker.Append(worker.Create(OpCodes.Call, elementWriteFunc)); // Write
// end for loop
// for loop i++
worker.Append(worker.Create(OpCodes.Ldloc_1));
worker.Append(worker.Create(OpCodes.Ldc_I4_1));
worker.Append(worker.Create(OpCodes.Add));
worker.Append(worker.Create(OpCodes.Stloc_1));
worker.Append(labelHead);
// for loop i < count
worker.Append(worker.Create(OpCodes.Ldloc_1));
worker.Append(worker.Create(OpCodes.Ldloc_0));
worker.Append(worker.Create(OpCodes.Blt, labelBody));
// return
worker.Append(worker.Create(OpCodes.Ret));
return writerFunc;
}
}
}

View File

@ -1,5 +1,6 @@
// Generated by CollectionWriterGenerator.cs
using System;
using System.Collections.Generic;
using Mirror.Tests.Generators;
using NUnit.Framework;
using UnityEngine;
@ -716,4 +717,324 @@ public void SendsData()
Assert.That(unpackedCollection.Array[unpackedCollection.Offset + 2].a, Is.EqualTo(new ClassWithNoConstructor { a = 5 }.a));
}
}
public class List_int_Test
{
class Message : MessageBase
{
public List<int> collection;
}
[Test]
public void SendsNull()
{
Message message = new Message
{
collection = default
};
byte[] data = MessagePacker.Pack(message);
Message unpacked = MessagePacker.Unpack<Message>(data);
List<int> unpackedCollection = unpacked.collection;
Assert.That(unpackedCollection, Is.Null.Or.Empty);
}
[Test]
public void SendsEmpty()
{
Message message = new Message
{
collection = new List<int> { }
};
byte[] data = MessagePacker.Pack(message);
Message unpacked = MessagePacker.Unpack<Message>(data);
List<int> unpackedCollection = unpacked.collection;
Assert.IsNotNull(unpackedCollection);
Assert.IsEmpty(unpackedCollection);
}
[Test]
public void SendsData()
{
Message message = new Message
{
collection = new List<int>
{
3, 4, 5
}
};
byte[] data = MessagePacker.Pack(message);
Message unpacked = MessagePacker.Unpack<Message>(data);
List<int> unpackedCollection = unpacked.collection;
Assert.IsNotNull(unpackedCollection);
Assert.IsNotEmpty(unpackedCollection);
Assert.That(unpackedCollection[0], Is.EqualTo(3));
Assert.That(unpackedCollection[1], Is.EqualTo(4));
Assert.That(unpackedCollection[2], Is.EqualTo(5));
}
}
public class List_string_Test
{
class Message : MessageBase
{
public List<string> collection;
}
[Test]
public void SendsNull()
{
Message message = new Message
{
collection = default
};
byte[] data = MessagePacker.Pack(message);
Message unpacked = MessagePacker.Unpack<Message>(data);
List<string> unpackedCollection = unpacked.collection;
Assert.That(unpackedCollection, Is.Null.Or.Empty);
}
[Test]
public void SendsEmpty()
{
Message message = new Message
{
collection = new List<string> { }
};
byte[] data = MessagePacker.Pack(message);
Message unpacked = MessagePacker.Unpack<Message>(data);
List<string> unpackedCollection = unpacked.collection;
Assert.IsNotNull(unpackedCollection);
Assert.IsEmpty(unpackedCollection);
}
[Test]
public void SendsData()
{
Message message = new Message
{
collection = new List<string>
{
"Some", "String", "Value"
}
};
byte[] data = MessagePacker.Pack(message);
Message unpacked = MessagePacker.Unpack<Message>(data);
List<string> unpackedCollection = unpacked.collection;
Assert.IsNotNull(unpackedCollection);
Assert.IsNotEmpty(unpackedCollection);
Assert.That(unpackedCollection[0], Is.EqualTo("Some"));
Assert.That(unpackedCollection[1], Is.EqualTo("String"));
Assert.That(unpackedCollection[2], Is.EqualTo("Value"));
}
}
public class List_Vector3_Test
{
class Message : MessageBase
{
public List<Vector3> collection;
}
[Test]
public void SendsNull()
{
Message message = new Message
{
collection = default
};
byte[] data = MessagePacker.Pack(message);
Message unpacked = MessagePacker.Unpack<Message>(data);
List<Vector3> unpackedCollection = unpacked.collection;
Assert.That(unpackedCollection, Is.Null.Or.Empty);
}
[Test]
public void SendsEmpty()
{
Message message = new Message
{
collection = new List<Vector3> { }
};
byte[] data = MessagePacker.Pack(message);
Message unpacked = MessagePacker.Unpack<Message>(data);
List<Vector3> unpackedCollection = unpacked.collection;
Assert.IsNotNull(unpackedCollection);
Assert.IsEmpty(unpackedCollection);
}
[Test]
public void SendsData()
{
Message message = new Message
{
collection = new List<Vector3>
{
new Vector3(1, 2, 3), new Vector3(4, 5, 6), new Vector3(7, 8, 9)
}
};
byte[] data = MessagePacker.Pack(message);
Message unpacked = MessagePacker.Unpack<Message>(data);
List<Vector3> unpackedCollection = unpacked.collection;
Assert.IsNotNull(unpackedCollection);
Assert.IsNotEmpty(unpackedCollection);
Assert.That(unpackedCollection[0], Is.EqualTo(new Vector3(1, 2, 3)));
Assert.That(unpackedCollection[1], Is.EqualTo(new Vector3(4, 5, 6)));
Assert.That(unpackedCollection[2], Is.EqualTo(new Vector3(7, 8, 9)));
}
}
public class List_FloatStringStruct_Test
{
class Message : MessageBase
{
public List<FloatStringStruct> collection;
}
[Test]
public void SendsNull()
{
Message message = new Message
{
collection = default
};
byte[] data = MessagePacker.Pack(message);
Message unpacked = MessagePacker.Unpack<Message>(data);
List<FloatStringStruct> unpackedCollection = unpacked.collection;
Assert.That(unpackedCollection, Is.Null.Or.Empty);
}
[Test]
public void SendsEmpty()
{
Message message = new Message
{
collection = new List<FloatStringStruct> { }
};
byte[] data = MessagePacker.Pack(message);
Message unpacked = MessagePacker.Unpack<Message>(data);
List<FloatStringStruct> unpackedCollection = unpacked.collection;
Assert.IsNotNull(unpackedCollection);
Assert.IsEmpty(unpackedCollection);
}
[Test]
public void SendsData()
{
Message message = new Message
{
collection = new List<FloatStringStruct>
{
new FloatStringStruct { value = 3, anotherValue = "Some" }, new FloatStringStruct { value = 4, anotherValue = "String" }, new FloatStringStruct { value = 5, anotherValue = "Values" }
}
};
byte[] data = MessagePacker.Pack(message);
Message unpacked = MessagePacker.Unpack<Message>(data);
List<FloatStringStruct> unpackedCollection = unpacked.collection;
Assert.IsNotNull(unpackedCollection);
Assert.IsNotEmpty(unpackedCollection);
Assert.That(unpackedCollection[0], Is.EqualTo(new FloatStringStruct { value = 3, anotherValue = "Some" }));
Assert.That(unpackedCollection[1], Is.EqualTo(new FloatStringStruct { value = 4, anotherValue = "String" }));
Assert.That(unpackedCollection[2], Is.EqualTo(new FloatStringStruct { value = 5, anotherValue = "Values" }));
}
}
public class List_ClassWithNoConstructor_Test
{
class Message : MessageBase
{
public List<ClassWithNoConstructor> collection;
}
[Test]
public void SendsNull()
{
Message message = new Message
{
collection = default
};
byte[] data = MessagePacker.Pack(message);
Message unpacked = MessagePacker.Unpack<Message>(data);
List<ClassWithNoConstructor> unpackedCollection = unpacked.collection;
Assert.That(unpackedCollection, Is.Null.Or.Empty);
}
[Test]
public void SendsEmpty()
{
Message message = new Message
{
collection = new List<ClassWithNoConstructor> { }
};
byte[] data = MessagePacker.Pack(message);
Message unpacked = MessagePacker.Unpack<Message>(data);
List<ClassWithNoConstructor> unpackedCollection = unpacked.collection;
Assert.IsNotNull(unpackedCollection);
Assert.IsEmpty(unpackedCollection);
}
[Test]
public void SendsData()
{
Message message = new Message
{
collection = new List<ClassWithNoConstructor>
{
new ClassWithNoConstructor { a = 3 }, new ClassWithNoConstructor { a = 4 }, new ClassWithNoConstructor { a = 5 }
}
};
byte[] data = MessagePacker.Pack(message);
Message unpacked = MessagePacker.Unpack<Message>(data);
List<ClassWithNoConstructor> unpackedCollection = unpacked.collection;
Assert.IsNotNull(unpackedCollection);
Assert.IsNotEmpty(unpackedCollection);
Assert.That(unpackedCollection[0].a, Is.EqualTo(new ClassWithNoConstructor { a = 3 }.a));
Assert.That(unpackedCollection[1].a, Is.EqualTo(new ClassWithNoConstructor { a = 4 }.a));
Assert.That(unpackedCollection[2].a, Is.EqualTo(new ClassWithNoConstructor { a = 5 }.a));
}
}
}

View File

@ -75,8 +75,8 @@ static IEnumerable<string> valuesForType(string elementType)
static readonly string[] collectionTypes = new string[]
{
ArrayType,
//ListType,
ArraySegmentType
ArraySegmentType,
ListType,
};
const string NameSpace = BaseNameSpace + ".CollectionWriters";
@ -86,6 +86,7 @@ static string Main(IEnumerable<string> classes)
string mergedClasses = Merge(classes);
return $@"// Generated by {nameof(CollectionWriterGenerator)}.cs
using System;
using System.Collections.Generic;
using Mirror.Tests.Generators;
using NUnit.Framework;
using UnityEngine;

View File

@ -1,4 +1,4 @@
<Project>
<Project>
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<RootNamespace>_WeaverTests2.csproj</RootNamespace>
@ -72,13 +72,16 @@
<Compile Include="GeneratedReaderWriter~\CreatesForClassWithValidConstructor.cs" />
<Compile Include="GeneratedReaderWriter~\CreatesForEnums.cs" />
<Compile Include="GeneratedReaderWriter~\CreatesForInheritedFromScriptableObject.cs" />
<Compile Include="GeneratedReaderWriter~\CreatesForList.cs" />
<Compile Include="GeneratedReaderWriter~\CreatesForStructFromDifferentAssemblies.cs" />
<Compile Include="GeneratedReaderWriter~\CreatesForStructs.cs" />
<Compile Include="GeneratedReaderWriter~\CreatesForStuctArraySegment.cs" />
<Compile Include="GeneratedReaderWriter~\CreatesForStuctList.cs" />
<Compile Include="GeneratedReaderWriter~\ExcludesNonSerializedFields.cs" />
<Compile Include="GeneratedReaderWriter~\GivesErrorForClassWithNoValidConstructor.cs" />
<Compile Include="GeneratedReaderWriter~\GivesErrorForInvalidArraySegmentType.cs" />
<Compile Include="GeneratedReaderWriter~\GivesErrorForInvalidArrayType.cs" />
<Compile Include="GeneratedReaderWriter~\GivesErrorForInvalidListType.cs" />
<Compile Include="GeneratedReaderWriter~\GivesErrorForJaggedArray.cs" />
<Compile Include="GeneratedReaderWriter~\GivesErrorForMultidimensionalArray.cs" />
<Compile Include="GeneratedReaderWriter~\GivesErrorWhenUsingInterface.cs" />

View File

@ -0,0 +1,14 @@
using System.Collections.Generic;
using Mirror;
namespace GeneratedReaderWriter.CreatesForList
{
public class CreatesForList : NetworkBehaviour
{
[ClientRpc]
public void RpcDoSomething(List<int> data)
{
// empty
}
}
}

View File

@ -0,0 +1,22 @@
using System.Collections.Generic;
using Mirror;
using UnityEngine;
namespace GeneratedReaderWriter.CreatesForStructList
{
public class CreatesForStructList : NetworkBehaviour
{
[ClientRpc]
public void RpcDoSomething(List<MyStruct> data)
{
// empty
}
}
public struct MyStruct
{
public int someValue;
public Vector3 anotherValue;
}
}

View File

@ -0,0 +1,15 @@
using System.Collections.Generic;
using Mirror;
using UnityEngine;
namespace GeneratedReaderWriter.GivesErrorForInvalidListType
{
public class GivesErrorForInvalidListType : NetworkBehaviour
{
[ClientRpc]
public void RpcDoSomething(List<MonoBehaviour> data)
{
// empty
}
}
}

View File

@ -179,5 +179,24 @@ public void GivesErrorForInvalidArraySegmentType()
Assert.That(weaverErrors, Contains.Item("Cannot generate writer for ArraySegment because element MonoBehaviour does not have a writer. Use a supported type or provide a custom writer (at System.ArraySegment`1<UnityEngine.MonoBehaviour>)"));
Assert.That(weaverErrors, Contains.Item("Cannot generate reader for ArraySegment because element MonoBehaviour does not have a reader. Use a supported type or provide a custom reader (at System.ArraySegment`1<UnityEngine.MonoBehaviour>)"));
}
[Test]
public void CreatesForList()
{
Assert.That(weaverErrors, Is.Empty);
}
[Test]
public void CreatesForStructList()
{
Assert.That(weaverErrors, Is.Empty);
}
[Test]
public void GivesErrorForInvalidListType()
{
Assert.That(weaverErrors, Contains.Item("Cannot generate writer for List because element MonoBehaviour does not have a writer. Use a supported type or provide a custom writer (at System.Collections.Generic.List`1<UnityEngine.MonoBehaviour>)"));
Assert.That(weaverErrors, Contains.Item("Cannot generate reader for List because element MonoBehaviour does not have a reader. Use a supported type or provide a custom reader (at System.Collections.Generic.List`1<UnityEngine.MonoBehaviour>)"));
}
}
}