diff --git a/OpenRA.Game/Network/SyncReport.cs b/OpenRA.Game/Network/SyncReport.cs index e500576903..20d244e265 100644 --- a/OpenRA.Game/Network/SyncReport.cs +++ b/OpenRA.Game/Network/SyncReport.cs @@ -17,7 +17,7 @@ using OpenRA.Primitives; namespace OpenRA.Network { - using NamesValuesPair = Pair; + using NamesValuesPair = Pair; class SyncReport { @@ -35,10 +35,10 @@ namespace OpenRA.Network TypeInfo typeInfo; lock (typeInfoCache) typeInfo = typeInfoCache[type]; - var values = new string[typeInfo.Names.Length]; + var values = new object[typeInfo.Names.Length]; var index = 0; - foreach (var func in typeInfo.MemberToStringFunctions) + foreach (var func in typeInfo.SerializableCopyOfMemberFunctions) values[index++] = func(sync); return Pair.New(typeInfo.Names, values); @@ -166,7 +166,7 @@ namespace OpenRA.Network static ParameterExpression syncParam = Expression.Parameter(typeof(ISync), "sync"); static ConstantExpression nullString = Expression.Constant(null, typeof(string)); - public readonly Func[] MemberToStringFunctions; + public readonly Func[] SerializableCopyOfMemberFunctions; public readonly string[] Names; public TypeInfo(Type type) @@ -182,14 +182,25 @@ namespace OpenRA.Network "Invalid Property: " + prop.DeclaringType.FullName + "." + prop.Name); var sync = Expression.Convert(syncParam, type); - MemberToStringFunctions = fields - .Select(fi => MemberToString(Expression.Field(sync, fi), fi.FieldType, fi.Name)) - .Concat(properties.Select(pi => MemberToString(Expression.Property(sync, pi), pi.PropertyType, pi.Name))) + SerializableCopyOfMemberFunctions = fields + .Select(fi => SerializableCopyOfMember(Expression.Field(sync, fi), fi.FieldType, fi.Name)) + .Concat(properties.Select(pi => SerializableCopyOfMember(Expression.Property(sync, pi), pi.PropertyType, pi.Name))) .ToArray(); Names = fields.Select(fi => fi.Name).Concat(properties.Select(pi => pi.Name)).ToArray(); } + static Func SerializableCopyOfMember(MemberExpression getMember, Type memberType, string name) + { + if (memberType.IsValueType) + { + // We can get a copy just by accessing the member since it is a value type. + var boxedCopy = Expression.Convert(getMember, typeof(object)); + return Expression.Lambda>(boxedCopy, name, new[] { syncParam }).Compile(); + } + return MemberToString(getMember, memberType, name); + } + static Func MemberToString(MemberExpression getMember, Type memberType, string name) { // The lambda generated is shown below. @@ -197,10 +208,13 @@ namespace OpenRA.Network var toString = memberType.GetMethod("ToString", Type.EmptyTypes); Expression getString; if (memberType.IsValueType) + { + // (ISync sync) => { return ((TSync)sync).Foo.ToString(); } getString = Expression.Call(getMember, toString); + } else { - // (ISync sync) => { var foo = ((TSync)sync).Foo; return foo == null ? null : foo.ToString()); } + // (ISync sync) => { var foo = ((TSync)sync).Foo; return foo == null ? null : foo.ToString(); } var memberVariable = Expression.Variable(memberType, getMember.Member.Name); var assignMemberVariable = Expression.Assign(memberVariable, getMember); var member = Expression.Block(new[] { memberVariable }, assignMemberVariable);