Skip to content

Commit

Permalink
fix: making weaver include public fields in base classes in auto gene…
Browse files Browse the repository at this point in the history
…rated Read/Write (#1977)

* adding test to check if data is sent

* test for generating writer for inherited class

* adding extension method for FindAllPublicFields
  • Loading branch information
James-Frowen committed Jun 10, 2020
1 parent df76cb6 commit 3db57e5
Show file tree
Hide file tree
Showing 8 changed files with 161 additions and 21 deletions.
41 changes: 41 additions & 0 deletions Assets/Mirror/Editor/Weaver/Extensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -253,5 +253,46 @@ public static bool HasMethodInBaseType(this TypeDefinition td, string methodName

return false;
}

/// <summary>
/// Finds public fields in type and base type
/// </summary>
/// <param name="variable"></param>
/// <returns></returns>
public static IEnumerable<FieldDefinition> FindAllPublicFields(this TypeReference variable)
{
return FindAllPublicFields(variable.Resolve());
}

/// <summary>
/// Finds public fields in type and base type
/// </summary>
/// <param name="variable"></param>
/// <returns></returns>
public static IEnumerable<FieldDefinition> FindAllPublicFields(this TypeDefinition typeDefinition)
{
while (typeDefinition != null)
{
foreach (FieldDefinition field in typeDefinition.Fields)
{
if (field.IsStatic || field.IsPrivate)
continue;

if (field.IsNotSerialized)
continue;

yield return field;
}

try
{
typeDefinition = typeDefinition.BaseType.Resolve();
}
catch (System.Exception e)
{
break;
}
}
}
}
}
13 changes: 4 additions & 9 deletions Assets/Mirror/Editor/Weaver/Readers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -332,7 +332,7 @@ static MethodDefinition GenerateClassOrStructReadFunction(TypeReference variable
TypeDefinition td = variable.Resolve();

CreateNew(variable, worker, td);
DeserializeFields(variable, recursionCount, worker);
ReadAllFields(variable, recursionCount, worker);

worker.Append(worker.Create(OpCodes.Ldloc_0));
worker.Append(worker.Create(OpCodes.Ret));
Expand Down Expand Up @@ -373,17 +373,11 @@ static void CreateNew(TypeReference variable, ILProcessor worker, TypeDefinition
}
}

static void DeserializeFields(TypeReference variable, int recursionCount, ILProcessor worker)
static void ReadAllFields(TypeReference variable, int recursionCount, ILProcessor worker)
{
uint fields = 0;
foreach (FieldDefinition field in variable.Resolve().Fields)
foreach (FieldDefinition field in variable.FindAllPublicFields())
{
if (field.IsStatic || field.IsPrivate)
continue;

if (field.IsNotSerialized)
continue;

// mismatched ldloca/ldloc for struct/class combinations is invalid IL, which causes crash at runtime
OpCode opcode = variable.IsValueType ? OpCodes.Ldloca : OpCodes.Ldloc;
worker.Append(worker.Create(opcode, 0));
Expand All @@ -403,6 +397,7 @@ static void DeserializeFields(TypeReference variable, int recursionCount, ILProc
worker.Append(worker.Create(OpCodes.Stfld, fieldRef));
fields++;
}

if (fields == 0)
{
Log.Warning($"{variable} has no public or non-static fields to deserialize");
Expand Down
34 changes: 22 additions & 12 deletions Assets/Mirror/Editor/Weaver/Writers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -139,15 +139,25 @@ static MethodDefinition GenerateClassOrStructWriterFunction(TypeReference variab

ILProcessor worker = writerFunc.Body.GetILProcessor();

uint fields = 0;
foreach (FieldDefinition field in variable.Resolve().Fields)
{
if (field.IsStatic || field.IsPrivate)
continue;
if (!WriteAllFields(variable, recursionCount, worker))
return null;

if (field.IsNotSerialized)
continue;
worker.Append(worker.Create(OpCodes.Ret));
return writerFunc;
}

/// <summary>
/// Fiends all fields in
/// </summary>
/// <param name="variable"></param>
/// <param name="recursionCount"></param>
/// <param name="worker"></param>
/// <returns>false if fail</returns>
static bool WriteAllFields(TypeReference variable, int recursionCount, ILProcessor worker)
{
uint fields = 0;
foreach (FieldDefinition field in variable.FindAllPublicFields())
{
MethodReference writeFunc = GetWriteFunc(field.FieldType, recursionCount + 1);
if (writeFunc != null)
{
Expand All @@ -162,20 +172,20 @@ static MethodDefinition GenerateClassOrStructWriterFunction(TypeReference variab
else
{
Weaver.Error($"{field.Name} has unsupported type. Use a type supported by Mirror instead", field);
return null;
return false;
}
}

if (fields == 0)
{
Log.Warning($" {variable} has no no public or non-static fields to serialize");
Log.Warning($"{variable} has no no public or non-static fields to serialize");
}
worker.Append(worker.Create(OpCodes.Ret));
return writerFunc;

return true;
}

static MethodDefinition GenerateArrayWriteFunc(TypeReference variable, int recursionCount)
{

if (!variable.IsArrayType())
{
Weaver.Error($"{variable.Name} is an unsupported type. Jagged and multidimensional arrays are not supported", variable);
Expand Down
53 changes: 53 additions & 0 deletions Assets/Mirror/Tests/Editor/FieldsInBaseClasses.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
using System;
using Mirror.Tests.RemoteAttrributeTest;
using NUnit.Framework;

namespace Mirror.Tests.GeneratedWriterTests
{
public class BaseData
{
public bool toggle;
}
public class SomeOtherData : BaseData
{
public int usefulNumber;
}

public class DataSenderBehaviour : NetworkBehaviour
{
public event Action<SomeOtherData> onData;
[Command]
public void CmdSendData(SomeOtherData otherData)
{
onData?.Invoke(otherData);
}
}

public class FieldsInBaseClasses : RemoteTestBase
{
[Test]
public void WriterShouldIncludeFieldsInBaseClass()
{
DataSenderBehaviour hostBehaviour = CreateHostObject<DataSenderBehaviour>(true);

const bool toggle = true;
const int usefulNumber = 10;

int callCount = 0;
hostBehaviour.onData += data =>
{
callCount++;
Assert.That(data.usefulNumber, Is.EqualTo(usefulNumber));
Assert.That(data.toggle, Is.EqualTo(toggle));
};
hostBehaviour.CmdSendData(new SomeOtherData
{
usefulNumber = usefulNumber,
toggle = toggle
});

ProcessMessages();
Assert.That(callCount, Is.EqualTo(1));
}
}
}
11 changes: 11 additions & 0 deletions Assets/Mirror/Tests/Editor/FieldsInBaseClasses.cs.meta

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

1 change: 1 addition & 0 deletions Assets/Mirror/Tests/Editor/Weaver/.WeaverTests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@
<Compile Include="GeneratedReaderWriter~\CreatesForClass.cs" />
<Compile Include="GeneratedReaderWriter~\CreatesForClassFromDifferentAssemblies.cs" />
<Compile Include="GeneratedReaderWriter~\CreatesForClassFromDifferentAssembliesWithValidConstructor.cs" />
<Compile Include="GeneratedReaderWriter~\CreatesForClassInherited.cs" />
<Compile Include="GeneratedReaderWriter~\CreatesForClassWithValidConstructor.cs" />
<Compile Include="GeneratedReaderWriter~\CreatesForEnums.cs" />
<Compile Include="GeneratedReaderWriter~\CreatesForInheritedFromScriptableObject.cs" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using Mirror;


namespace GeneratedReaderWriter.CreatesForClassInherited
{
public class CreatesForClassInherited : NetworkBehaviour
{
[ClientRpc]
public void RpcDoSomething(SomeOtherData data)
{
// empty
}
}

public class BaseData
{
public bool yes;
}
public class SomeOtherData : BaseData
{
public int usefulNumber;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,12 @@ public void CreatesForClass()
Assert.That(weaverErrors, Is.Empty);
}

[Test]
public void CreatesForClassInherited()
{
Assert.That(weaverErrors, Is.Empty);
}

[Test]
public void CreatesForClassWithValidConstructor()
{
Expand Down

0 comments on commit 3db57e5

Please sign in to comment.