diff --git a/Assets/Mirror/Editor/Weaver/Extensions.cs b/Assets/Mirror/Editor/Weaver/Extensions.cs index ff21f9548..874224f3e 100644 --- a/Assets/Mirror/Editor/Weaver/Extensions.cs +++ b/Assets/Mirror/Editor/Weaver/Extensions.cs @@ -253,5 +253,46 @@ public static bool HasMethodInBaseType(this TypeDefinition td, string methodName return false; } + + /// + /// Finds public fields in type and base type + /// + /// + /// + public static IEnumerable FindAllPublicFields(this TypeReference variable) + { + return FindAllPublicFields(variable.Resolve()); + } + + /// + /// Finds public fields in type and base type + /// + /// + /// + public static IEnumerable 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; + } + } + } } } diff --git a/Assets/Mirror/Editor/Weaver/Readers.cs b/Assets/Mirror/Editor/Weaver/Readers.cs index 6d186bef6..11b977bb9 100644 --- a/Assets/Mirror/Editor/Weaver/Readers.cs +++ b/Assets/Mirror/Editor/Weaver/Readers.cs @@ -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)); @@ -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)); @@ -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"); diff --git a/Assets/Mirror/Editor/Weaver/Writers.cs b/Assets/Mirror/Editor/Weaver/Writers.cs index cf7df3021..ece8b13c8 100644 --- a/Assets/Mirror/Editor/Weaver/Writers.cs +++ b/Assets/Mirror/Editor/Weaver/Writers.cs @@ -139,15 +139,25 @@ static MethodDefinition GenerateClassOrStructWriterFunction(TypeReference variab ILProcessor worker = writerFunc.Body.GetILProcessor(); + if (!WriteAllFields(variable, recursionCount, worker)) + return null; + + worker.Append(worker.Create(OpCodes.Ret)); + return writerFunc; + } + + /// + /// Fiends all fields in + /// + /// + /// + /// + /// false if fail + static bool WriteAllFields(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; - MethodReference writeFunc = GetWriteFunc(field.FieldType, recursionCount + 1); if (writeFunc != null) { @@ -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); diff --git a/Assets/Mirror/Tests/Editor/FieldsInBaseClasses.cs b/Assets/Mirror/Tests/Editor/FieldsInBaseClasses.cs new file mode 100644 index 000000000..f72600ed0 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/FieldsInBaseClasses.cs @@ -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 onData; + [Command] + public void CmdSendData(SomeOtherData otherData) + { + onData?.Invoke(otherData); + } + } + + public class FieldsInBaseClasses : RemoteTestBase + { + [Test] + public void WriterShouldIncludeFieldsInBaseClass() + { + DataSenderBehaviour hostBehaviour = CreateHostObject(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)); + } + } +} diff --git a/Assets/Mirror/Tests/Editor/FieldsInBaseClasses.cs.meta b/Assets/Mirror/Tests/Editor/FieldsInBaseClasses.cs.meta new file mode 100644 index 000000000..67a428572 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/FieldsInBaseClasses.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 518288ffe7c7215458a98466131fc7af +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Weaver/.WeaverTests.csproj b/Assets/Mirror/Tests/Editor/Weaver/.WeaverTests.csproj index 53394768a..31c9c8853 100644 --- a/Assets/Mirror/Tests/Editor/Weaver/.WeaverTests.csproj +++ b/Assets/Mirror/Tests/Editor/Weaver/.WeaverTests.csproj @@ -67,6 +67,7 @@ + diff --git a/Assets/Mirror/Tests/Editor/Weaver/GeneratedReaderWriter~/CreatesForClassInherited.cs b/Assets/Mirror/Tests/Editor/Weaver/GeneratedReaderWriter~/CreatesForClassInherited.cs new file mode 100644 index 000000000..81ad2cec1 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Weaver/GeneratedReaderWriter~/CreatesForClassInherited.cs @@ -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; + } +} diff --git a/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests.cs b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests.cs index 184cef56f..8853c5fad 100644 --- a/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests.cs +++ b/Assets/Mirror/Tests/Editor/Weaver/WeaverGeneratedReaderWriterTests.cs @@ -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() {