From ab2ef0ba03ae3f6ad5a425f4ac769e3cfc989d75 Mon Sep 17 00:00:00 2001 From: RoosterDragon Date: Mon, 30 Mar 2015 23:24:56 +0100 Subject: [PATCH] Improve performance of target validation. The Warhead and WeaponInfo classes now maintain preconstructed sets of valid and invalid targets to speed up validation checks since LINQ Intersect no longer has to be called (which recreates the sets internally each time). Fixed minor bugs in the AI where it was repeating the validation logic but failing to account for invalid targets. --- OpenRA.Game/GameRules/Warhead.cs | 21 +++++++++++++++------ OpenRA.Game/GameRules/WeaponInfo.cs | 21 ++++++++++++++------- OpenRA.Mods.Common/AI/States/AirStates.cs | 4 +++- OpenRA.Mods.Common/AI/States/StateBase.cs | 2 +- 4 files changed, 33 insertions(+), 15 deletions(-) diff --git a/OpenRA.Game/GameRules/Warhead.cs b/OpenRA.Game/GameRules/Warhead.cs index 25ee0e5e9e..7812bbf629 100644 --- a/OpenRA.Game/GameRules/Warhead.cs +++ b/OpenRA.Game/GameRules/Warhead.cs @@ -33,6 +33,18 @@ namespace OpenRA.GameRules [Desc("Delay in ticks before applying the warhead effect.", "0 = instant (old model).")] public readonly int Delay = 0; + HashSet validTargetSet; + HashSet invalidTargetSet; + + public bool IsValidTarget(IEnumerable targetTypes) + { + if (validTargetSet == null) + validTargetSet = new HashSet(ValidTargets); + if (invalidTargetSet == null) + invalidTargetSet = new HashSet(InvalidTargets); + return validTargetSet.Overlaps(targetTypes) && !invalidTargetSet.Overlaps(targetTypes); + } + /// Applies the warhead's effect against the target. public abstract void DoImpact(Target target, Actor firedBy, IEnumerable damageModifiers); @@ -52,8 +64,7 @@ namespace OpenRA.GameRules return false; var cellInfo = world.Map.GetTerrainInfo(cell); - if (!ValidTargets.Intersect(cellInfo.TargetTypes).Any() - || InvalidTargets.Intersect(cellInfo.TargetTypes).Any()) + if (!IsValidTarget(cellInfo.TargetTypes)) return false; return true; @@ -81,8 +92,7 @@ namespace OpenRA.GameRules // A target type is valid if it is in the valid targets list, and not in the invalid targets list. var targetable = victim.TraitOrDefault(); - if (targetable == null || !ValidTargets.Intersect(targetable.TargetTypes).Any() - || InvalidTargets.Intersect(targetable.TargetTypes).Any()) + if (targetable == null || !IsValidTarget(targetable.TargetTypes)) return false; return true; @@ -101,8 +111,7 @@ namespace OpenRA.GameRules // A target type is valid if it is in the valid targets list, and not in the invalid targets list. var targetable = victim.Info.Traits.GetOrDefault(); - if (targetable == null || !ValidTargets.Intersect(targetable.GetTargetTypes()).Any() - || InvalidTargets.Intersect(targetable.GetTargetTypes()).Any()) + if (targetable == null || !IsValidTarget(targetable.GetTargetTypes())) return false; return true; diff --git a/OpenRA.Game/GameRules/WeaponInfo.cs b/OpenRA.Game/GameRules/WeaponInfo.cs index 0c33926e05..83e96eae68 100644 --- a/OpenRA.Game/GameRules/WeaponInfo.cs +++ b/OpenRA.Game/GameRules/WeaponInfo.cs @@ -30,7 +30,7 @@ namespace OpenRA.GameRules public interface IProjectileInfo { IEffect Create(ProjectileArgs args); } - public class WeaponInfo + public sealed class WeaponInfo { [Desc("The maximum range the weapon can fire.")] public readonly WRange Range = WRange.Zero; @@ -64,9 +64,14 @@ namespace OpenRA.GameRules [FieldLoader.LoadUsing("LoadWarheads")] public readonly List Warheads = new List(); + readonly HashSet validTargetSet; + readonly HashSet invalidTargetSet; + public WeaponInfo(string name, MiniYaml content) { FieldLoader.Load(this, content); + validTargetSet = new HashSet(ValidTargets); + invalidTargetSet = new HashSet(InvalidTargets); } static object LoadProjectile(MiniYaml yaml) @@ -92,6 +97,11 @@ namespace OpenRA.GameRules return retList; } + public bool IsValidTarget(IEnumerable targetTypes) + { + return validTargetSet.Overlaps(targetTypes) && !invalidTargetSet.Overlaps(targetTypes); + } + /// Checks if the weapon is valid against (can target) the target. public bool IsValidAgainst(Target target, World world, Actor firedBy) { @@ -108,8 +118,7 @@ namespace OpenRA.GameRules return false; var cellInfo = world.Map.GetTerrainInfo(cell); - if (!ValidTargets.Intersect(cellInfo.TargetTypes).Any() - || InvalidTargets.Intersect(cellInfo.TargetTypes).Any()) + if (!IsValidTarget(cellInfo.TargetTypes)) return false; return true; @@ -122,8 +131,7 @@ namespace OpenRA.GameRules public bool IsValidAgainst(Actor victim, Actor firedBy) { var targetable = victim.TraitOrDefault(); - if (targetable == null || !ValidTargets.Intersect(targetable.TargetTypes).Any() - || InvalidTargets.Intersect(targetable.TargetTypes).Any()) + if (targetable == null || !IsValidTarget(targetable.TargetTypes)) return false; if (!Warheads.Any(w => w.IsValidAgainst(victim, firedBy))) @@ -136,8 +144,7 @@ namespace OpenRA.GameRules public bool IsValidAgainst(FrozenActor victim, Actor firedBy) { var targetable = victim.Info.Traits.GetOrDefault(); - if (targetable == null || !ValidTargets.Intersect(targetable.GetTargetTypes()).Any() - || InvalidTargets.Intersect(targetable.GetTargetTypes()).Any()) + if (targetable == null || !IsValidTarget(targetable.GetTargetTypes())) return false; if (!Warheads.Any(w => w.IsValidAgainst(victim, firedBy))) diff --git a/OpenRA.Mods.Common/AI/States/AirStates.cs b/OpenRA.Mods.Common/AI/States/AirStates.cs index c5d4e95258..095ee9f7f7 100644 --- a/OpenRA.Mods.Common/AI/States/AirStates.cs +++ b/OpenRA.Mods.Common/AI/States/AirStates.cs @@ -18,6 +18,8 @@ namespace OpenRA.Mods.Common.AI { abstract class AirStateBase : StateBase { + static readonly string[] AirTargetTypes = new[] { "Air" }; + protected const int MissileUnitMultiplier = 3; protected static int CountAntiAirUnits(IEnumerable units) @@ -34,7 +36,7 @@ namespace OpenRA.Mods.Common.AI var arms = unit.TraitsImplementing(); foreach (var a in arms) { - if (a.Weapon.ValidTargets.Contains("Air")) + if (a.Weapon.IsValidTarget(AirTargetTypes)) { missileUnitsCount++; break; diff --git a/OpenRA.Mods.Common/AI/States/StateBase.cs b/OpenRA.Mods.Common/AI/States/StateBase.cs index 2f5f07f8c8..a7fe882429 100644 --- a/OpenRA.Mods.Common/AI/States/StateBase.cs +++ b/OpenRA.Mods.Common/AI/States/StateBase.cs @@ -69,7 +69,7 @@ namespace OpenRA.Mods.Common.AI var arms = a.TraitsImplementing(); foreach (var arm in arms) - if (arm.Weapon.ValidTargets.Intersect(targetable.TargetTypes).Any()) + if (arm.Weapon.IsValidTarget(targetable.TargetTypes)) return true; return false;