diff --git a/OpenRA.Game/Sync.cs b/OpenRA.Game/Sync.cs index 905c2bd3ab..1551b5fdd4 100644 --- a/OpenRA.Game/Sync.cs +++ b/OpenRA.Game/Sync.cs @@ -20,18 +20,22 @@ namespace OpenRA { [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] public sealed class SyncAttribute : Attribute { } - public interface ISync { } /* marker interface */ + public interface ISync { } // marker interface public static class Sync { - static Cache> hashFuncCache = new Cache>(t => GenerateHashFunc(t)); + static readonly ConcurrentCache HashMethods = + new ConcurrentCache(GenerateHashFunc); + static readonly ConcurrentCache> HashFunctions = + new ConcurrentCache>( + type => (Func)HashMethods[type].CreateDelegate(typeof(Func))); public static int CalculateSyncHash(object obj) { - return hashFuncCache[obj.GetType()](obj); + return HashFunctions[obj.GetType()](obj); } - static Dictionary hashFunctions = new Dictionary() + static readonly Dictionary SpecializedHashMethods = new Dictionary { { typeof(int2), ((Func)HashInt2).Method }, { typeof(CPos), ((Func)HashCPos).Method }, @@ -49,8 +53,8 @@ namespace OpenRA static void EmitSyncOpcodes(Type type, ILGenerator il) { - if (hashFunctions.ContainsKey(type)) - il.EmitCall(OpCodes.Call, hashFunctions[type], null); + if (SpecializedHashMethods.ContainsKey(type)) + il.EmitCall(OpCodes.Call, SpecializedHashMethods[type], null); else if (type == typeof(bool)) { var l = il.DefineLabel(); @@ -68,9 +72,9 @@ namespace OpenRA il.Emit(OpCodes.Xor); } - public static Func GenerateHashFunc(Type t) + static DynamicMethod GenerateHashFunc(Type t) { - var d = new DynamicMethod("hash_{0}".F(t.Name), typeof(int), new Type[] { typeof(object) }, t); + var d = new DynamicMethod("Hash_{0}".F(t.Name), typeof(int), new Type[] { typeof(object) }, t); var il = d.GetILGenerator(); var this_ = il.DeclareLocal(t).LocalIndex; il.Emit(OpCodes.Ldarg_0); @@ -78,13 +82,17 @@ namespace OpenRA il.Emit(OpCodes.Stloc, this_); il.Emit(OpCodes.Ldc_I4_0); - const BindingFlags Binding = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance; + var neededSync = false; + + const BindingFlags Binding = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly; + foreach (var field in t.GetFields(Binding).Where(x => x.HasAttribute())) { il.Emit(OpCodes.Ldloc, this_); il.Emit(OpCodes.Ldfld, field); EmitSyncOpcodes(field.FieldType, il); + neededSync = true; } foreach (var prop in t.GetProperties(Binding).Where(x => x.HasAttribute())) @@ -93,10 +101,23 @@ namespace OpenRA il.EmitCall(OpCodes.Call, prop.GetGetMethod(), null); EmitSyncOpcodes(prop.PropertyType, il); + neededSync = true; + } + + if (t.BaseType != null) + { + var baseHashFunc = HashMethods[t.BaseType]; + if (baseHashFunc != null) + { + il.Emit(OpCodes.Ldloc, this_); + il.EmitCall(OpCodes.Call, baseHashFunc, null); + il.Emit(OpCodes.Xor); + neededSync = true; + } } il.Emit(OpCodes.Ret); - return (Func)d.CreateDelegate(typeof(Func)); + return neededSync ? d : null; } public static int HashInt2(int2 i2)