diff --git a/OpenRA.Game/Actor.cs b/OpenRA.Game/Actor.cs index bf211ac472..869cc6ab65 100644 --- a/OpenRA.Game/Actor.cs +++ b/OpenRA.Game/Actor.cs @@ -24,6 +24,13 @@ namespace OpenRA { public sealed class Actor : IScriptBindable, IScriptNotifyBind, ILuaTableBinding, ILuaEqualityBinding, ILuaToStringBinding, IEquatable, IDisposable { + internal struct SyncHash + { + public readonly ISync Trait; + public readonly int Hash; + public SyncHash(ISync trait, int hash) { Trait = trait; Hash = hash; } + } + public readonly ActorInfo Info; public readonly World World; @@ -62,6 +69,8 @@ namespace OpenRA } } + internal IEnumerable SyncHashes { get; private set; } + readonly IFacing facing; readonly IHealth health; readonly IRenderModifier[] renderModifiers; @@ -112,6 +121,12 @@ namespace OpenRA visibilityModifiers = TraitsImplementing().ToArray(); defaultVisibility = Trait(); Targetables = TraitsImplementing().ToArray(); + + SyncHashes = + TraitsImplementing() + .Select(sync => Pair.New(sync, Sync.GetHashFunction(sync))) + .ToArray() + .Select(pair => new SyncHash(pair.First, pair.Second(pair.First))); } Rectangle DetermineBounds() diff --git a/OpenRA.Game/Network/SyncReport.cs b/OpenRA.Game/Network/SyncReport.cs index c3a2f0fbd3..2917ba9b78 100644 --- a/OpenRA.Game/Network/SyncReport.cs +++ b/OpenRA.Game/Network/SyncReport.cs @@ -63,31 +63,29 @@ namespace OpenRA.Network report.SyncedRandom = orderManager.World.SharedRandom.Last; report.TotalCount = orderManager.World.SharedRandom.TotalCount; report.Traits.Clear(); - foreach (var a in orderManager.World.ActorsWithTrait()) - { - var sync = Sync.CalculateSyncHash(a.Trait); - if (sync != 0) - report.Traits.Add(new TraitReport() - { - ActorID = a.Actor.ActorID, - Type = a.Actor.Info.Name, - Owner = (a.Actor.Owner == null) ? "null" : a.Actor.Owner.PlayerName, - Trait = a.Trait.GetType().Name, - Hash = sync, - NamesValues = DumpSyncTrait(a.Trait) - }); - } + foreach (var actor in orderManager.World.ActorsHavingTrait()) + foreach (var syncHash in actor.SyncHashes) + if (syncHash.Hash != 0) + report.Traits.Add(new TraitReport() + { + ActorID = actor.ActorID, + Type = actor.Info.Name, + Owner = (actor.Owner == null) ? "null" : actor.Owner.PlayerName, + Trait = syncHash.Trait.GetType().Name, + Hash = syncHash.Hash, + NamesValues = DumpSyncTrait(syncHash.Trait) + }); foreach (var e in orderManager.World.Effects) { var sync = e as ISync; if (sync != null) { - var hash = Sync.CalculateSyncHash(sync); + var hash = Sync.Hash(sync); if (hash != 0) report.Effects.Add(new EffectReport() { - Name = sync.ToString().Split('.').Last(), + Name = sync.GetType().Name, Hash = hash, NamesValues = DumpSyncTrait(sync) }); diff --git a/OpenRA.Game/Sync.cs b/OpenRA.Game/Sync.cs index 85afe84109..733be00eae 100644 --- a/OpenRA.Game/Sync.cs +++ b/OpenRA.Game/Sync.cs @@ -26,14 +26,20 @@ namespace OpenRA public static class Sync { - static Cache> hashFuncCache = new Cache>(t => GenerateHashFunc(t)); + static readonly ConcurrentCache> HashFunctions = + new ConcurrentCache>(GenerateHashFunc); - public static int CalculateSyncHash(object obj) + internal static Func GetHashFunction(ISync sync) { - return hashFuncCache[obj.GetType()](obj); + return HashFunctions[sync.GetType()]; } - static Dictionary hashFunctions = new Dictionary() + internal static int Hash(ISync sync) + { + return GetHashFunction(sync)(sync); + } + + static readonly Dictionary CustomHashFunctions = new Dictionary() { { typeof(int2), ((Func)HashInt2).Method }, { typeof(CPos), ((Func)HashCPos).Method }, @@ -50,8 +56,8 @@ namespace OpenRA static void EmitSyncOpcodes(Type type, ILGenerator il) { - if (hashFunctions.ContainsKey(type)) - il.EmitCall(OpCodes.Call, hashFunctions[type], null); + if (CustomHashFunctions.ContainsKey(type)) + il.EmitCall(OpCodes.Call, CustomHashFunctions[type], null); else if (type == typeof(bool)) { var l = il.DefineLabel(); diff --git a/OpenRA.Game/World.cs b/OpenRA.Game/World.cs index 1a9149346d..b37f5c4448 100644 --- a/OpenRA.Game/World.cs +++ b/OpenRA.Game/World.cs @@ -341,18 +341,19 @@ namespace OpenRA // hash all the actors foreach (var a in Actors) - ret += n++ * (int)(1 + a.ActorID) * Sync.CalculateSyncHash(a); + ret += n++ * (int)(1 + a.ActorID) * Sync.HashActor(a); // hash all the traits that tick - foreach (var x in ActorsWithTrait()) - ret += n++ * (int)(1 + x.Actor.ActorID) * Sync.CalculateSyncHash(x.Trait); + foreach (var actor in ActorsHavingTrait()) + foreach (var syncHash in actor.SyncHashes) + ret += n++ * (int)(1 + actor.ActorID) * syncHash.Hash; // TODO: don't go over all effects foreach (var e in Effects) { var sync = e as ISync; if (sync != null) - ret += n++ * Sync.CalculateSyncHash(sync); + ret += n++ * Sync.Hash(sync); } // Hash the shared rng