feat: Users may provide custom serializers for any type (#1153)

* feat: Users may provide custom serializers for any type

* Relax rules for rpc parameters to support user provided serializers

* Improve error messages

* Fixed component types

* Adjusted tests
This commit is contained in:
Paul Pacheco 2019-10-19 09:00:27 -05:00 committed by vis2k
parent ae5c92b40c
commit 9cb309e5bc
6 changed files with 85 additions and 105 deletions

View File

@ -56,18 +56,6 @@ static void GenerateSerialization(TypeDefinition td)
if (field.IsStatic || field.IsPrivate || field.IsSpecialName)
continue;
if (field.FieldType.Resolve().HasGenericParameters && !field.FieldType.FullName.StartsWith("System.ArraySegment`1", System.StringComparison.Ordinal))
{
Weaver.Error($"{field} cannot have generic type {field.FieldType}. Consider creating a class that derives the generic type");
return;
}
if (field.FieldType.Resolve().IsInterface)
{
Weaver.Error($"{field} has unsupported type. Use a concrete class instead of interface {field.FieldType}");
return;
}
MethodReference writeFunc = Writers.GetWriteFunc(field.FieldType);
if (writeFunc != null)
{

View File

@ -684,16 +684,6 @@ public static bool ProcessMethodsValidateParameters(MethodReference md, CustomAt
Weaver.Error($"{md} cannot have optional parameters");
return false;
}
if (p.ParameterType.Resolve().IsAbstract)
{
Weaver.Error($"{md} has invalid parameter {p}. Use concrete type instead of abstract type {p.ParameterType}");
return false;
}
if (p.ParameterType.IsByReference)
{
Weaver.Error($"{md} has invalid parameter {p}. Use supported type instead of reference type {p.ParameterType}");
return false;
}
// TargetRPC is an exception to this rule and can have a NetworkConnection as first parameter
if (p.ParameterType.FullName == Weaver.NetworkConnectionType.FullName &&
!(ca.AttributeType.FullName == Weaver.TargetRpcType.FullName && i == 0))
@ -701,14 +691,6 @@ public static bool ProcessMethodsValidateParameters(MethodReference md, CustomAt
Weaver.Error($"{md} has invalid parameer {p}. Cannot pass NeworkConnections");
return false;
}
if (p.ParameterType.Resolve().IsDerivedFrom(Weaver.ComponentType))
{
if (p.ParameterType.FullName != Weaver.NetworkIdentityType.FullName)
{
Weaver.Error($"{md} has invalid parameter {p}. Cannot pass components in remote method calls");
return false;
}
}
}
return true;
}

View File

@ -294,36 +294,12 @@ public static void ProcessSyncVars(TypeDefinition td, List<FieldDefinition> sync
{
TypeDefinition resolvedField = fd.FieldType.Resolve();
if (resolvedField.IsDerivedFrom(Weaver.NetworkBehaviourType))
{
Weaver.Error($"{fd} has invalid type. SyncVars cannot be NetworkBehaviours");
return;
}
if (resolvedField.IsDerivedFrom(Weaver.ScriptableObjectType))
{
Weaver.Error($"{fd} has invalid type. SyncVars cannot be scriptable objects");
return;
}
if ((fd.Attributes & FieldAttributes.Static) != 0)
{
Weaver.Error($"{fd} cannot be static");
return;
}
if (resolvedField.HasGenericParameters)
{
Weaver.Error($"{fd} has invalid type. SyncVars cannot have generic parameters");
return;
}
if (resolvedField.IsInterface)
{
Weaver.Error($"{fd} has invalid type. Use a concrete type instead of interface {fd.FieldType}");
return;
}
if (fd.FieldType.IsArray)
{
Weaver.Error($"{fd} has invalid type. Use SyncLists instead of arrays");

View File

@ -34,11 +34,30 @@ public static MethodReference GetReadFunc(TypeReference variable, int recursionC
Weaver.Error($"{variable} is not a supported type");
return null;
}
if (td.IsDerivedFrom(Weaver.ScriptableObjectType))
{
Weaver.Error($"Cannot generate reader for scriptable object {variable}. Use a supported type or provide a custom reader");
return null;
}
if (td.IsDerivedFrom(Weaver.ComponentType))
{
Weaver.Error($"Cannot generate reader for component type {variable}. Use a supported type or provide a custom reader");
return null;
}
if (variable.IsByReference)
{
// error??
Weaver.Error($"{variable} is not a supported reference type");
Weaver.Error($"Cannot pass type {variable} by reference");
return null;
}
if (td.HasGenericParameters && !td.FullName.StartsWith("System.ArraySegment`1", System.StringComparison.Ordinal))
{
Weaver.Error($"Cannot generate reader for generic variable {variable}. Use a concrete type or provide a custom reader");
return null;
}
if (td.IsInterface)
{
Weaver.Error($"Cannot generate reader for interface variable {variable}. Use a concrete type or provide a custom reader");
return null;
}

View File

@ -31,9 +31,36 @@ public static MethodReference GetWriteFunc(TypeReference variable, int recursion
if (variable.IsByReference)
{
// error??
Weaver.Error($"{variable} has unsupported type. Use one of Mirror supported types instead");
Weaver.Error($"Cannot pass {variable} by reference");
return null;
}
TypeDefinition td = variable.Resolve();
if (td == null)
{
Weaver.Error($"{variable} is not a supported type. Use a supported type or provide a custom writer");
return null;
}
if (td.IsDerivedFrom(Weaver.ScriptableObjectType))
{
Weaver.Error($"Cannot generate writer for scriptable object {variable}. Use a supported type or provide a custom writer");
return null;
}
if (td.IsDerivedFrom(Weaver.ComponentType))
{
Weaver.Error($"Cannot generate writer for component type {variable}. Use a supported type or provide a custom writer");
return null;
}
if (td.HasGenericParameters && !td.FullName.StartsWith("System.ArraySegment`1", System.StringComparison.Ordinal))
{
Weaver.Error($"Cannot generate writer for generic type {variable}. Use a concrete type or provide a custom writer");
return null;
}
if (td.IsInterface)
{
Weaver.Error($"Cannot generate writer for interface {variable}. Use a concrete type or provide a custom writer");
return null;
}
MethodDefinition newWriterFunc;
@ -112,18 +139,6 @@ static MethodDefinition GenerateStructWriterFunction(TypeReference variable, int
if (field.IsStatic || field.IsPrivate)
continue;
if (field.FieldType.Resolve().HasGenericParameters)
{
Weaver.Error($"{field} has unsupported type. Create a derived class instead of using generics");
return null;
}
if (field.FieldType.Resolve().IsInterface)
{
Weaver.Error($"{field} has unsupported type. Use a concrete class instead of an interface");
return null;
}
MethodReference writeFunc = GetWriteFunc(field.FieldType, recursionCount + 1);
if (writeFunc != null)
{

View File

@ -177,16 +177,16 @@ public void SyncVarsWrongHookType()
public void SyncVarsDerivedNetworkBehaviour()
{
Assert.That(CompilationFinishedHook.WeaveFailed, Is.True);
Assert.That(m_weaverErrors.Count, Is.EqualTo(1));
Assert.That(m_weaverErrors[0], Is.EqualTo("Mirror.Weaver error: MirrorTest.MirrorTestPlayer/MySyncVar MirrorTest.MirrorTestPlayer::invalidVar has invalid type. SyncVars cannot be NetworkBehaviours"));
Assert.That(m_weaverErrors.Count, Is.EqualTo(2));
Assert.That(m_weaverErrors[0], Is.EqualTo("Mirror.Weaver error: Cannot generate writer for component type MirrorTest.MirrorTestPlayer/MySyncVar. Use a supported type or provide a custom writer"));
}
[Test]
public void SyncVarsDerivedScriptableObject()
{
Assert.That(CompilationFinishedHook.WeaveFailed, Is.True);
Assert.That(m_weaverErrors.Count, Is.EqualTo(1));
Assert.That(m_weaverErrors[0], Is.EqualTo("Mirror.Weaver error: MirrorTest.MirrorTestPlayer/MySyncVar MirrorTest.MirrorTestPlayer::invalidVar has invalid type. SyncVars cannot be scriptable objects"));
Assert.That(m_weaverErrors.Count, Is.EqualTo(2));
Assert.That(m_weaverErrors[0], Is.EqualTo("Mirror.Weaver error: Cannot generate writer for scriptable object MirrorTest.MirrorTestPlayer/MySyncVar. Use a supported type or provide a custom writer"));
}
[Test]
@ -201,24 +201,24 @@ public void SyncVarsStatic()
public void SyncVarsGenericParam()
{
Assert.That(CompilationFinishedHook.WeaveFailed, Is.True);
Assert.That(m_weaverErrors.Count, Is.EqualTo(1));
Assert.That(m_weaverErrors[0], Is.EqualTo("Mirror.Weaver error: MirrorTest.MirrorTestPlayer/MySyncVar`1<System.Int32> MirrorTest.MirrorTestPlayer::invalidVar has invalid type. SyncVars cannot have generic parameters"));
Assert.That(m_weaverErrors.Count, Is.EqualTo(2));
Assert.That(m_weaverErrors[0], Is.EqualTo("Mirror.Weaver error: Cannot generate writer for generic type MirrorTest.MirrorTestPlayer/MySyncVar`1<System.Int32>. Use a concrete type or provide a custom writer"));
}
[Test]
public void SyncVarsInterface()
{
Assert.That(CompilationFinishedHook.WeaveFailed, Is.True);
Assert.That(m_weaverErrors.Count, Is.EqualTo(1));
Assert.That(m_weaverErrors[0], Is.EqualTo("Mirror.Weaver error: MirrorTest.MirrorTestPlayer/MySyncVar MirrorTest.MirrorTestPlayer::invalidVar has invalid type. Use a concrete type instead of interface MirrorTest.MirrorTestPlayer/MySyncVar"));
Assert.That(m_weaverErrors.Count, Is.EqualTo(2));
Assert.That(m_weaverErrors[0], Is.EqualTo("Mirror.Weaver error: Cannot generate writer for interface MirrorTest.MirrorTestPlayer/MySyncVar. Use a concrete type or provide a custom writer"));
}
[Test]
public void SyncVarsDifferentModule()
{
Assert.That(CompilationFinishedHook.WeaveFailed, Is.True);
Assert.That(m_weaverErrors.Count, Is.EqualTo(1));
Assert.That(m_weaverErrors[0], Is.EqualTo("Mirror.Weaver error: UnityEngine.TextMesh MirrorTest.MirrorTestPlayer::invalidVar has unsupported type. Use a supported Mirror type instead"));
Assert.That(m_weaverErrors.Count, Is.EqualTo(2));
Assert.That(m_weaverErrors[0], Is.EqualTo("Mirror.Weaver error: Cannot generate writer for component type UnityEngine.TextMesh. Use a supported type or provide a custom writer"));
}
[Test]
@ -292,16 +292,16 @@ public void SyncListStructGenericGeneric()
public void SyncListStructMemberGeneric()
{
Assert.That(CompilationFinishedHook.WeaveFailed, Is.True);
Assert.That(m_weaverErrors.Count, Is.EqualTo(2));
Assert.That(m_weaverErrors[0], Is.EqualTo("Mirror.Weaver error: MirrorTest.MirrorTestPlayer/MyGenericStruct`1<System.Single> MirrorTest.MirrorTestPlayer/MyStruct::potato has unsupported type. Create a derived class instead of using generics"));
Assert.That(m_weaverErrors.Count, Is.EqualTo(3));
Assert.That(m_weaverErrors[0], Is.EqualTo("Mirror.Weaver error: Cannot generate writer for generic type MirrorTest.MirrorTestPlayer/MyGenericStruct`1<System.Single>. Use a concrete type or provide a custom writer"));
}
[Test]
public void SyncListStructMemberInterface()
{
Assert.That(CompilationFinishedHook.WeaveFailed, Is.True);
Assert.That(m_weaverErrors.Count, Is.EqualTo(2));
Assert.That(m_weaverErrors[0], Is.EqualTo("Mirror.Weaver error: MirrorTest.MirrorTestPlayer/IPotato MirrorTest.MirrorTestPlayer/MyStruct::potato has unsupported type. Use a concrete class instead of an interface"));
Assert.That(m_weaverErrors.Count, Is.EqualTo(3));
Assert.That(m_weaverErrors[0], Is.EqualTo("Mirror.Weaver error: Cannot generate writer for interface MirrorTest.MirrorTestPlayer/IPotato. Use a concrete type or provide a custom writer"));
}
[Test]
@ -404,24 +404,24 @@ public void NetworkBehaviourTargetRpcParamOptional()
public void NetworkBehaviourTargetRpcParamRef()
{
Assert.That(CompilationFinishedHook.WeaveFailed, Is.True);
Assert.That(m_weaverErrors.Count, Is.EqualTo(1));
Assert.That(m_weaverErrors[0], Is.EqualTo("Mirror.Weaver error: System.Void MirrorTest.MirrorTestPlayer::TargetRpcCantHaveParamRef(Mirror.NetworkConnection,System.Int32&) has invalid parameter monkeys. Use supported type instead of reference type System.Int32&"));
Assert.That(m_weaverErrors.Count, Is.EqualTo(4));
Assert.That(m_weaverErrors[0], Is.EqualTo("Mirror.Weaver error: Cannot pass System.Int32& by reference"));
}
[Test]
public void NetworkBehaviourTargetRpcParamAbstract()
{
Assert.That(CompilationFinishedHook.WeaveFailed, Is.True);
Assert.That(m_weaverErrors.Count, Is.EqualTo(1));
Assert.That(m_weaverErrors[0], Is.EqualTo("Mirror.Weaver error: System.Void MirrorTest.MirrorTestPlayer::TargetRpcCantHaveParamAbstract(Mirror.NetworkConnection,MirrorTest.MirrorTestPlayer/AbstractClass) has invalid parameter monkeys. Use concrete type instead of abstract type MirrorTest.MirrorTestPlayer/AbstractClass"));
Assert.That(m_weaverErrors.Count, Is.EqualTo(3));
Assert.That(m_weaverErrors[0], Is.EqualTo("Mirror.Weaver error: MirrorTest.MirrorTestPlayer/AbstractClass can't be deserialized bcause i has no default constructor"));
}
[Test]
public void NetworkBehaviourTargetRpcParamComponent()
{
Assert.That(CompilationFinishedHook.WeaveFailed, Is.True);
Assert.That(m_weaverErrors.Count, Is.EqualTo(1));
Assert.That(m_weaverErrors[0], Is.EqualTo("Mirror.Weaver error: System.Void MirrorTest.MirrorTestPlayer::TargetRpcCantHaveParamComponent(Mirror.NetworkConnection,MirrorTest.MirrorTestPlayer/ComponentClass) has invalid parameter monkeyComp. Cannot pass components in remote method calls"));
Assert.That(m_weaverErrors.Count, Is.EqualTo(4));
Assert.That(m_weaverErrors[0], Is.EqualTo("Mirror.Weaver error: Cannot generate writer for component type MirrorTest.MirrorTestPlayer/ComponentClass. Use a supported type or provide a custom writer"));
}
[Test]
@ -483,24 +483,24 @@ public void NetworkBehaviourClientRpcParamOptional()
public void NetworkBehaviourClientRpcParamRef()
{
Assert.That(CompilationFinishedHook.WeaveFailed, Is.True);
Assert.That(m_weaverErrors.Count, Is.EqualTo(1));
Assert.That(m_weaverErrors[0], Is.EqualTo("Mirror.Weaver error: System.Void MirrorTest.MirrorTestPlayer::RpcCantHaveParamRef(System.Int32&) has invalid parameter monkeys. Use supported type instead of reference type System.Int32&"));
Assert.That(m_weaverErrors.Count, Is.EqualTo(4));
Assert.That(m_weaverErrors[0], Is.EqualTo("Mirror.Weaver error: Cannot pass System.Int32& by reference"));
}
[Test]
public void NetworkBehaviourClientRpcParamAbstract()
{
Assert.That(CompilationFinishedHook.WeaveFailed, Is.True);
Assert.That(m_weaverErrors.Count, Is.EqualTo(1));
Assert.That(m_weaverErrors[0], Is.EqualTo("Mirror.Weaver error: System.Void MirrorTest.MirrorTestPlayer::RpcCantHaveParamAbstract(MirrorTest.MirrorTestPlayer/AbstractClass) has invalid parameter monkeys. Use concrete type instead of abstract type MirrorTest.MirrorTestPlayer/AbstractClass"));
Assert.That(m_weaverErrors.Count, Is.EqualTo(3));
Assert.That(m_weaverErrors[0], Is.EqualTo("Mirror.Weaver error: MirrorTest.MirrorTestPlayer/AbstractClass can't be deserialized bcause i has no default constructor"));
}
[Test]
public void NetworkBehaviourClientRpcParamComponent()
{
Assert.That(CompilationFinishedHook.WeaveFailed, Is.True);
Assert.That(m_weaverErrors.Count, Is.EqualTo(1));
Assert.That(m_weaverErrors[0], Is.EqualTo("Mirror.Weaver error: System.Void MirrorTest.MirrorTestPlayer::RpcCantHaveParamComponent(MirrorTest.MirrorTestPlayer/ComponentClass) has invalid parameter monkeyComp. Cannot pass components in remote method calls"));
Assert.That(m_weaverErrors.Count, Is.EqualTo(4));
Assert.That(m_weaverErrors[0], Is.EqualTo("Mirror.Weaver error: Cannot generate writer for component type MirrorTest.MirrorTestPlayer/ComponentClass. Use a supported type or provide a custom writer"));
}
[Test]
@ -539,24 +539,24 @@ public void NetworkBehaviourCmdParamOptional()
public void NetworkBehaviourCmdParamRef()
{
Assert.That(CompilationFinishedHook.WeaveFailed, Is.True);
Assert.That(m_weaverErrors.Count, Is.EqualTo(1));
Assert.That(m_weaverErrors[0], Is.EqualTo("Mirror.Weaver error: System.Void MirrorTest.MirrorTestPlayer::CmdCantHaveParamRef(System.Int32&) has invalid parameter monkeys. Use supported type instead of reference type System.Int32&"));
Assert.That(m_weaverErrors.Count, Is.EqualTo(4));
Assert.That(m_weaverErrors[0], Is.EqualTo("Mirror.Weaver error: Cannot pass System.Int32& by reference"));
}
[Test]
public void NetworkBehaviourCmdParamAbstract()
{
Assert.That(CompilationFinishedHook.WeaveFailed, Is.True);
Assert.That(m_weaverErrors.Count, Is.EqualTo(1));
Assert.That(m_weaverErrors[0], Is.EqualTo("Mirror.Weaver error: System.Void MirrorTest.MirrorTestPlayer::CmdCantHaveParamAbstract(MirrorTest.MirrorTestPlayer/AbstractClass) has invalid parameter monkeys. Use concrete type instead of abstract type MirrorTest.MirrorTestPlayer/AbstractClass"));
Assert.That(m_weaverErrors.Count, Is.EqualTo(3));
Assert.That(m_weaverErrors[0], Is.EqualTo("Mirror.Weaver error: MirrorTest.MirrorTestPlayer/AbstractClass can't be deserialized bcause i has no default constructor"));
}
[Test]
public void NetworkBehaviourCmdParamComponent()
{
Assert.That(CompilationFinishedHook.WeaveFailed, Is.True);
Assert.That(m_weaverErrors.Count, Is.EqualTo(1));
Assert.That(m_weaverErrors[0], Is.EqualTo("Mirror.Weaver error: System.Void MirrorTest.MirrorTestPlayer::CmdCantHaveParamComponent(MirrorTest.MirrorTestPlayer/ComponentClass) has invalid parameter monkeyComp. Cannot pass components in remote method calls"));
Assert.That(m_weaverErrors.Count, Is.EqualTo(4));
Assert.That(m_weaverErrors[0], Is.EqualTo("Mirror.Weaver error: Cannot generate writer for component type MirrorTest.MirrorTestPlayer/ComponentClass. Use a supported type or provide a custom writer"));
}
[Test]
@ -794,16 +794,16 @@ public void MessageInvalidDeserializeFieldType()
public void MessageMemberGeneric()
{
Assert.That(CompilationFinishedHook.WeaveFailed, Is.True);
Assert.That(m_weaverErrors.Count, Is.EqualTo(1));
Assert.That(m_weaverErrors[0], Is.EqualTo("Mirror.Weaver error: MirrorTest.HasGeneric`1<System.Int32> MirrorTest.PrefabClone::invalidField cannot have generic type MirrorTest.HasGeneric`1<System.Int32>. Consider creating a class that derives the generic type"));
Assert.That(m_weaverErrors.Count, Is.EqualTo(2));
Assert.That(m_weaverErrors[0], Is.EqualTo("Mirror.Weaver error: Cannot generate writer for generic type MirrorTest.HasGeneric`1<System.Int32>. Use a concrete type or provide a custom writer"));
}
[Test]
public void MessageMemberInterface()
{
Assert.That(CompilationFinishedHook.WeaveFailed, Is.True);
Assert.That(m_weaverErrors.Count, Is.EqualTo(1));
Assert.That(m_weaverErrors[0], Is.EqualTo("Mirror.Weaver error: MirrorTest.SuperCoolInterface MirrorTest.PrefabClone::invalidField has unsupported type. Use a concrete class instead of interface MirrorTest.SuperCoolInterface"));
Assert.That(m_weaverErrors.Count, Is.EqualTo(2));
Assert.That(m_weaverErrors[0], Is.EqualTo("Mirror.Weaver error: Cannot generate writer for interface MirrorTest.SuperCoolInterface. Use a concrete type or provide a custom writer"));
}
#endregion
}