From 6d0fbfa21fe87a9dc6068256453fd4f5d110fba9 Mon Sep 17 00:00:00 2001 From: reaperrr Date: Thu, 28 May 2020 00:15:03 +0200 Subject: [PATCH] Explodes performance optimization Cache armaments on creation, avoid LINQ. Also merge and put first the DamageThreshold == 0 check in Damaged, because the common default IS 0, so most of the time the IsTraitDisabled and IsInWorld checks are redundant. --- OpenRA.Mods.Common/Traits/Explodes.cs | 33 ++++++++++++++++----------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/OpenRA.Mods.Common/Traits/Explodes.cs b/OpenRA.Mods.Common/Traits/Explodes.cs index c5fffc0e9a..6a28712134 100644 --- a/OpenRA.Mods.Common/Traits/Explodes.cs +++ b/OpenRA.Mods.Common/Traits/Explodes.cs @@ -82,6 +82,9 @@ namespace OpenRA.Mods.Common.Traits { readonly IHealth health; BuildingInfo buildingInfo; + Armament[] armaments; + + bool anyArmaments; public Explodes(ExplodesInfo info, Actor self) : base(info) @@ -92,6 +95,9 @@ namespace OpenRA.Mods.Common.Traits protected override void Created(Actor self) { buildingInfo = self.Info.TraitInfoOrDefault(); + armaments = self.TraitsImplementing().ToArray(); + anyArmaments = armaments.Length > 0; + base.Created(self); } @@ -100,13 +106,14 @@ namespace OpenRA.Mods.Common.Traits if (IsTraitDisabled || !self.IsInWorld) return; - if (self.World.SharedRandom.Next(100) > Info.Chance) + var sharedRandom = self.World.SharedRandom.Next(100); + if (sharedRandom > Info.Chance) return; if (!Info.DeathTypes.IsEmpty && !e.Damage.DamageTypes.Overlaps(Info.DeathTypes)) return; - var weapon = ChooseWeaponForExplosion(self); + var weapon = ChooseWeaponForExplosion(self, sharedRandom); if (weapon == null) return; @@ -127,24 +134,24 @@ namespace OpenRA.Mods.Common.Traits weapon.Impact(Target.FromPos(self.CenterPosition), source); } - WeaponInfo ChooseWeaponForExplosion(Actor self) + WeaponInfo ChooseWeaponForExplosion(Actor self, int sharedRandom) { - var armaments = self.TraitsImplementing(); - if (!armaments.Any()) + if (!anyArmaments) return Info.WeaponInfo; + else if (sharedRandom > Info.LoadedChance) + return Info.EmptyWeaponInfo; - // TODO: EmptyWeapon should be removed in favour of conditions - var shouldExplode = !armaments.All(a => a.IsReloading); - var useFullExplosion = self.World.SharedRandom.Next(100) <= Info.LoadedChance; - return (shouldExplode && useFullExplosion) ? Info.WeaponInfo : Info.EmptyWeaponInfo; + // PERF: Avoid LINQ + foreach (var a in armaments) + if (!a.IsReloading) + return Info.WeaponInfo; + + return Info.EmptyWeaponInfo; } void INotifyDamage.Damaged(Actor self, AttackInfo e) { - if (IsTraitDisabled || !self.IsInWorld) - return; - - if (Info.DamageThreshold == 0) + if (Info.DamageThreshold == 0 || IsTraitDisabled || !self.IsInWorld) return; if (!Info.DeathTypes.IsEmpty && !e.Damage.DamageTypes.Overlaps(Info.DeathTypes))