context: #region Copyright & License Information
  /*
   * Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
   * This file is part of OpenRA, which is free software. It is made
   * available to you under the terms of the GNU General Public License
   * as published by the Free Software Foundation. For more information,
   * see COPYING.
   */
  #endregion
  
  using System;
  using System.Collections.Generic;
  using System.Linq;
  using System.Reflection;
  using System.Reflection.Emit;
  using OpenRA.Primitives;
  using OpenRA.Traits;
  
  namespace OpenRA
  {
  	[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
  	public sealed class SyncAttribute : Attribute { }
  	public interface ISync { } // marker interface
  
  	public static class Sync
  	{
  		static readonly ConcurrentCache<Type, DynamicMethod> HashMethods =
  			new ConcurrentCache<Type, DynamicMethod>(GenerateHashFunc);
  		static readonly ConcurrentCache<Type, Func<object, int>> HashFunctions =
  			new ConcurrentCache<Type, Func<object, int>>(
  				type => (Func<object, int>)HashMethods[type].CreateDelegate(typeof(Func<object, int>)));
  
  		public static int CalculateSyncHash(object obj)
  		{
  			return HashFunctions[obj.GetType()](obj);
  		}
  
  		static readonly Dictionary<Type, MethodInfo> SpecializedHashMethods = new Dictionary<Type, MethodInfo>
  		{
  			{ typeof(int2), ((Func<int2, int>)HashInt2).Method },
  			{ typeof(CPos), ((Func<CPos, int>)HashCPos).Method },
  			{ typeof(CVec), ((Func<CVec, int>)HashCVec).Method },
  			{ typeof(WDist), ((Func<WDist, int>)Hash<WDist>).Method },
  			{ typeof(WPos), ((Func<WPos, int>)Hash<WPos>).Method },
  			{ typeof(WVec), ((Func<WVec, int>)Hash<WVec>).Method },
  			{ typeof(WAngle), ((Func<WAngle, int>)Hash<WAngle>).Method },
  			{ typeof(WRot), ((Func<WRot, int>)Hash<WRot>).Method },
  			{ typeof(TypeDictionary), ((Func<TypeDictionary, int>)HashTDict).Method },
  			{ typeof(Actor), ((Func<Actor, int>)HashActor).Method },
  			{ typeof(Player), ((Func<Player, int>)HashPlayer).Method },
  			{ typeof(Target), ((Func<Target, int>)HashTarget).Method },
  		};
  
  		static void EmitSyncOpcodes(Type type, ILGenerator il)
  		{
  			if (SpecializedHashMethods.ContainsKey(type))
  				il.EmitCall(OpCodes.Call, SpecializedHashMethods[type], null);
  			else if (type == typeof(bool))
  			{
  				var l = il.DefineLabel();
  				il.Emit(OpCodes.Ldc_I4, 0xaaa);
  				il.Emit(OpCodes.Brtrue, l);
  				il.Emit(OpCodes.Pop);
  				il.Emit(OpCodes.Ldc_I4, 0x555);
  				il.MarkLabel(l);
  			}
  			else if (type.HasAttribute<SyncAttribute>())
  				il.EmitCall(OpCodes.Call, ((Func<object, int>)CalculateSyncHash).Method, null);
  			else if (type != typeof(int))
  				throw new NotImplementedException("SyncAttribute on member of unhashable type: {0}".F(type.FullName));
  
  			il.Emit(OpCodes.Xor);
  		}
  
  		static DynamicMethod GenerateHashFunc(Type 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);
  			il.Emit(OpCodes.Castclass, t);
  			il.Emit(OpCodes.Stloc, this_);
  			il.Emit(OpCodes.Ldc_I4_0);
  
  			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<SyncAttribute>()))
  			{
  				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<SyncAttribute>()))
  			{
  				il.Emit(OpCodes.Ldloc, this_);
  				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 neededSync ? d : null;
  		}
  
  		public static int HashInt2(int2 i2)
  		{
  			return ((i2.X * 5) ^ (i2.Y * 3)) / 4;
  		}
  
  		public static int HashCPos(CPos i2)
  		{
  			return ((i2.X * 5) ^ (i2.Y * 3)) / 4;
  		}
  
  		public static int HashCVec(CVec i2)
  		{
  			return ((i2.X * 5) ^ (i2.Y * 3)) / 4;
  		}
  
  		public static int HashTDict(TypeDictionary d)
  		{
  			var ret = 0;
  			foreach (var o in d)
  				ret += CalculateSyncHash(o);
  			return ret;
  		}
  
  		public static int HashActor(Actor a)
  		{
  			if (a != null)
  				return (int)(a.ActorID << 16);
  			return 0;
  		}
  
  		public static int HashPlayer(Player p)
  		{
  			if (p != null)
  				return (int)(p.PlayerActor.ActorID << 16) * 0x567;
  			return 0;
  		}
  
  		public static int HashTarget(Target t)
  		{
  			switch (t.Type)
  			{
  				case TargetType.Actor:
  					return (int)(t.Actor.ActorID << 16) * 0x567;
  
  				case TargetType.FrozenActor:
  					if (t.FrozenActor.Actor == null)
  						return 0;
  
  					return (int)(t.FrozenActor.Actor.ActorID << 16) * 0x567;
  
  				case TargetType.Terrain:
  					return Hash<WPos>(t.CenterPosition);
  
  				default:
  				case TargetType.Invalid:
  					return 0;
  			}
  		}
  
  		public static int Hash<T>(T t)
  		{
  			return t.GetHashCode();
  		}
  
  		public static void CheckSyncUnchanged(World world, Action fn)
  		{
  			CheckSyncUnchanged(world, () => { fn(); return true; });
  		}
  
  		static bool inUnsyncedCode = false;
  
  		public static T CheckSyncUnchanged<T>(World world, Func<T> fn)
  		{
  			if (world == null)
  				return fn();
  
  			var shouldCheckSync = Game.Settings.Debug.SanityCheckUnsyncedCode;
  			var sync = shouldCheckSync ? world.SyncHash() : 0;
  			var prevInUnsyncedCode = inUnsyncedCode;
  			inUnsyncedCode = true;
  
  			try
  			{
  				return fn();
  			}
  			finally
  			{
  				inUnsyncedCode = prevInUnsyncedCode;
  				if (shouldCheckSync && sync != world.SyncHash())
  					throw new InvalidOperationException("CheckSyncUnchanged: sync-changing code may not run here");
  			}
  		}
  
  		public static void AssertUnsynced(string message)
  		{
  			if (!inUnsyncedCode)
  
gt: 				throw new InvalidOperationException(message);