From ce76a03d5eb60448b94f3fb75dbdfe40acef1514 Mon Sep 17 00:00:00 2001 From: Leon Friedrich <60421075+ElectroJr@users.noreply.github.com> Date: Sun, 15 Oct 2023 03:48:25 +1100 Subject: [PATCH] Small ExplosionSystem Cleanup (#20817) --- .../ExplosionSystem.Processing.cs | 151 ++++++++---------- .../EntitySystems/ExplosionSystem.cs | 15 ++ 2 files changed, 85 insertions(+), 81 deletions(-) diff --git a/Content.Server/Explosion/EntitySystems/ExplosionSystem.Processing.cs b/Content.Server/Explosion/EntitySystems/ExplosionSystem.Processing.cs index cce7050e3f..102b461fe0 100644 --- a/Content.Server/Explosion/EntitySystems/ExplosionSystem.Processing.cs +++ b/Content.Server/Explosion/EntitySystems/ExplosionSystem.Processing.cs @@ -1,5 +1,6 @@ using System.Linq; using System.Numerics; +using System.Reflection; using Content.Server.Explosion.Components; using Content.Shared.CCVar; using Content.Shared.Damage; @@ -18,11 +19,12 @@ using Robust.Shared.Physics.Components; using Robust.Shared.Physics.Dynamics; using Robust.Shared.Random; using Robust.Shared.Timing; +using Robust.Shared.Utility; using TimedDespawnComponent = Robust.Shared.Spawners.TimedDespawnComponent; namespace Content.Server.Explosion.EntitySystems; -public sealed partial class ExplosionSystem : EntitySystem +public sealed partial class ExplosionSystem { /// /// Used to limit explosion processing time. See . @@ -176,12 +178,12 @@ public sealed partial class ExplosionSystem : EntitySystem /// Used for a variation of that makes use of the fact that we have /// already done an entity lookup on a tile, and don't need to do so again. /// - public bool IsBlockingTurf(EntityUid uid, EntityQuery physicsQuery) + public bool IsBlockingTurf(EntityUid uid) { if (EntityManager.IsQueuedForDeletion(uid)) return false; - if (!physicsQuery.TryGetComponent(uid, out var physics)) + if (!_physicsQuery.TryGetComponent(uid, out var physics)) return false; return physics.CanCollide && physics.Hard && (physics.CollisionLayer & (int) CollisionGroup.Impassable) != 0; @@ -198,19 +200,14 @@ public sealed partial class ExplosionSystem : EntitySystem DamageSpecifier damage, MapCoordinates epicenter, HashSet processed, - string id, - EntityQuery xformQuery, - EntityQuery damageQuery, - EntityQuery physicsQuery, - EntityQuery tagQuery, - EntityQuery projectileQuery) + string id) { var gridBox = new Box2(tile * grid.TileSize, (tile + 1) * grid.TileSize); // get the entities on a tile. Note that we cannot process them directly, or we get // enumerator-changed-while-enumerating errors. - List list = new(); - var state = (list, processed, xformQuery); + List<(EntityUid, TransformComponent)> list = new(); + var state = (list, processed, _transformQuery); // get entities: lookup.DynamicTree.QueryAabb(ref state, GridQueryCallback, gridBox, true); @@ -219,9 +216,9 @@ public sealed partial class ExplosionSystem : EntitySystem lookup.StaticSundriesTree.QueryAabb(ref state, GridQueryCallback, gridBox, true); // process those entities - foreach (var xform in list) + foreach (var (uid, xform) in list) { - ProcessEntity(xform.Owner, epicenter, damage, throwForce, id, xform, damageQuery, physicsQuery, xformQuery, tagQuery, projectileQuery); + ProcessEntity(uid, epicenter, damage, throwForce, id, xform); } // process anchored entities @@ -230,7 +227,7 @@ public sealed partial class ExplosionSystem : EntitySystem foreach (var entity in anchoredList) { processed.Add(entity); - ProcessEntity(entity, epicenter, damage, throwForce, id, null, damageQuery, physicsQuery, xformQuery, tagQuery, projectileQuery); + ProcessEntity(entity, epicenter, damage, throwForce, id, null); } // Walls and reinforced walls will break into girders. These girders will also be considered turf-blocking for @@ -241,7 +238,7 @@ public sealed partial class ExplosionSystem : EntitySystem { foreach (var entity in grid.GetAnchoredEntities(tile)) { - tileBlocked |= IsBlockingTurf(entity, physicsQuery); + tileBlocked |= IsBlockingTurf(entity); } } @@ -260,28 +257,28 @@ public sealed partial class ExplosionSystem : EntitySystem lookup.DynamicTree.QueryAabb(ref state, GridQueryCallback, gridBox, true); lookup.SundriesTree.QueryAabb(ref state, GridQueryCallback, gridBox, true); - foreach (var xform in list) + foreach (var (uid, xform) in list) { // Here we only throw, no dealing damage. Containers n such might drop their entities after being destroyed, but // they should handle their own damage pass-through, with their own damage reduction calculation. - ProcessEntity(xform.Owner, epicenter, null, throwForce, id, xform, damageQuery, physicsQuery, xformQuery, tagQuery, projectileQuery); + ProcessEntity(uid, epicenter, null, throwForce, id, xform); } return !tileBlocked; } private bool GridQueryCallback( - ref (List List, HashSet Processed, EntityQuery XformQuery) state, + ref (List<(EntityUid, TransformComponent)> List, HashSet Processed, EntityQuery XformQuery) state, in EntityUid uid) { if (state.Processed.Add(uid) && state.XformQuery.TryGetComponent(uid, out var xform)) - state.List.Add(xform); + state.List.Add((uid, xform)); return true; } private bool GridQueryCallback( - ref (List List, HashSet Processed, EntityQuery XformQuery) state, + ref (List<(EntityUid, TransformComponent)> List, HashSet Processed, EntityQuery XformQuery) state, in FixtureProxy proxy) { var owner = proxy.Entity; @@ -299,17 +296,12 @@ public sealed partial class ExplosionSystem : EntitySystem DamageSpecifier damage, MapCoordinates epicenter, HashSet processed, - string id, - EntityQuery xformQuery, - EntityQuery damageQuery, - EntityQuery physicsQuery, - EntityQuery tagQuery, - EntityQuery projectileQuery) + string id) { var gridBox = Box2.FromDimensions(tile * DefaultTileSize, new Vector2(DefaultTileSize, DefaultTileSize)); var worldBox = spaceMatrix.TransformBox(gridBox); - var list = new List(); - var state = (list, processed, invSpaceMatrix, lookup.Owner, xformQuery, gridBox); + var list = new List<(EntityUid, TransformComponent)>(); + var state = (list, processed, invSpaceMatrix, lookup.Owner, _transformQuery, gridBox); // get entities: lookup.DynamicTree.QueryAabb(ref state, SpaceQueryCallback, worldBox, true); @@ -317,10 +309,10 @@ public sealed partial class ExplosionSystem : EntitySystem lookup.SundriesTree.QueryAabb(ref state, SpaceQueryCallback, worldBox, true); lookup.StaticSundriesTree.QueryAabb(ref state, SpaceQueryCallback, worldBox, true); - foreach (var xform in state.Item1) + foreach (var (uid, xform) in state.Item1) { - processed.Add(xform.Owner); - ProcessEntity(xform.Owner, epicenter, damage, throwForce, id, xform, damageQuery, physicsQuery, xformQuery, tagQuery, projectileQuery); + processed.Add(uid); + ProcessEntity(uid, epicenter, damage, throwForce, id, xform); } if (throwForce <= 0) @@ -332,14 +324,14 @@ public sealed partial class ExplosionSystem : EntitySystem lookup.DynamicTree.QueryAabb(ref state, SpaceQueryCallback, worldBox, true); lookup.SundriesTree.QueryAabb(ref state, SpaceQueryCallback, worldBox, true); - foreach (var xform in list) + foreach (var (uid, xform) in list) { - ProcessEntity(xform.Owner, epicenter, null, throwForce, id, xform, damageQuery, physicsQuery, xformQuery, tagQuery, projectileQuery); + ProcessEntity(uid, epicenter, null, throwForce, id, xform); } } private bool SpaceQueryCallback( - ref (List List, HashSet Processed, Matrix3 InvSpaceMatrix, EntityUid LookupOwner, EntityQuery XformQuery, Box2 GridBox) state, + ref (List<(EntityUid, TransformComponent)> List, HashSet Processed, Matrix3 InvSpaceMatrix, EntityUid LookupOwner, EntityQuery XformQuery, Box2 GridBox) state, in EntityUid uid) { if (state.Processed.Contains(uid)) @@ -351,20 +343,20 @@ public sealed partial class ExplosionSystem : EntitySystem { // parented directly to the map, use local position if (state.GridBox.Contains(state.InvSpaceMatrix.Transform(xform.LocalPosition))) - state.List.Add(xform); + state.List.Add((uid, xform)); return true; } // finally check if it intersects our tile if (state.GridBox.Contains(state.InvSpaceMatrix.Transform(_transformSystem.GetWorldPosition(xform, state.XformQuery)))) - state.List.Add(xform); + state.List.Add((uid, xform)); return true; } private bool SpaceQueryCallback( - ref (List List, HashSet Processed, Matrix3 InvSpaceMatrix, EntityUid LookupOwner, EntityQuery XformQuery, Box2 GridBox) state, + ref (List<(EntityUid, TransformComponent)> List, HashSet Processed, Matrix3 InvSpaceMatrix, EntityUid LookupOwner, EntityQuery XformQuery, Box2 GridBox) state, in FixtureProxy proxy) { var uid = proxy.Entity; @@ -380,44 +372,38 @@ public sealed partial class ExplosionSystem : EntitySystem DamageSpecifier? damage, float throwForce, string id, - TransformComponent? xform, - EntityQuery damageQuery, - EntityQuery physicsQuery, - EntityQuery transformQuery, - EntityQuery tagQuery, - EntityQuery projectileQuery) + TransformComponent? xform) { // damage - if (damage != null && damageQuery.TryGetComponent(uid, out var damageable)) + if (damage != null && _damageQuery.TryGetComponent(uid, out var damageable)) { + // TODO Explosion Performance + // Cache this? I.e., instead of raising an event, check for a component? var ev = new GetExplosionResistanceEvent(id); - RaiseLocalEvent(uid, ref ev, false); + RaiseLocalEvent(uid, ref ev); ev.DamageCoefficient = Math.Max(0, ev.DamageCoefficient); - //todo need a way to track origin of explosion - if (ev.DamageCoefficient == 1) + // TODO explosion entity + // Move explosion data into the existing explosion visuals entity + // Give each explosion a unique name, include in admin logs. + + // TODO Explosion Performance + // This creates a new dictionary. Maybe we should just re-use a private local damage specifier and update it. + // Though most entities shouldn't have explosion resistance, so maybe its fine. + // ReSharper disable once CompareOfFloatsByEqualityOperator + if (ev.DamageCoefficient != 1) + damage *= ev.DamageCoefficient; + + // Log damage to players. Damage is logged before dealing damage so that the position can be logged before + // the entity gets deleted. + if (_mindQuery.HasComponent(uid)) { - // no damage-dict multiplication required. - _damageableSystem.TryChangeDamage(uid, damage, ignoreResistances: true, damageable: damageable); - if (HasComp(uid) || HasComp(uid)) - { - var damageStr = string.Join(", ", damage.DamageDict.Select(entry => $"{entry.Key}: {entry.Value}")); - _adminLogger.Add(LogType.Explosion, LogImpact.Medium, - $"Explosion caused [{damageStr}] to {ToPrettyString(uid):target} at {Transform(uid).Coordinates}"); - } - } - else - { - var appliedDamage = damage * ev.DamageCoefficient; - _damageableSystem.TryChangeDamage(uid, appliedDamage, ignoreResistances: true, damageable: damageable); - if (HasComp(uid) || HasComp(uid)) - { - var damageStr = string.Join(", ", appliedDamage.DamageDict.Select(entry => $"{entry.Key}: {entry.Value}")); - _adminLogger.Add(LogType.Explosion, LogImpact.Medium, - $"Explosion caused [{damageStr}] to {ToPrettyString(uid):target} at {Transform(uid).Coordinates}"); - } + _adminLogger.Add(LogType.Explosion, LogImpact.Medium, + $"Explosion caused [{damage.Total}] damage to {ToPrettyString(uid):target} at {xform?.Coordinates}"); } + + _damageableSystem.TryChangeDamage(uid, damage, ignoreResistances: true, damageable: damageable); } // throw @@ -425,16 +411,16 @@ public sealed partial class ExplosionSystem : EntitySystem && !xform.Anchored && throwForce > 0 && !EntityManager.IsQueuedForDeletion(uid) - && physicsQuery.TryGetComponent(uid, out var physics) + && _physicsQuery.TryGetComponent(uid, out var physics) && physics.BodyType == BodyType.Dynamic) { - var pos = _transformSystem.GetWorldPosition(xform, transformQuery); + var pos = _transformSystem.GetWorldPosition(xform); _throwingSystem.TryThrow( uid, pos - epicenter.Position, physics, xform, - projectileQuery, + _projectileQuery, throwForce); } @@ -564,6 +550,9 @@ sealed class Explosion // Variables used for enumerating over tiles, grids, etc private DamageSpecifier _currentDamage = default!; +#if DEBUG + private DamageSpecifier? _expectedDamage; +#endif private BroadphaseComponent _currentLookup = default!; private MapGridComponent? _currentGrid; private float _currentIntensity; @@ -683,6 +672,16 @@ sealed class Explosion while (CurrentIteration < _tileSetIntensity.Count) { _currentIntensity = _tileSetIntensity[CurrentIteration]; + + #if DEBUG + if (_expectedDamage != null) + { + // Check that explosion processing hasn't somehow accidentally mutated the damage set. + DebugTools.Assert(_expectedDamage.Equals(_currentDamage)); + _expectedDamage = ExplosionType.DamagePerIntensity * _currentIntensity; + } + #endif + _currentDamage = ExplosionType.DamagePerIntensity * _currentIntensity; // only throw if either the explosion is small, or if this is the outer ring of a large explosion. @@ -780,12 +779,7 @@ sealed class Explosion _currentDamage, Epicenter, ProcessedEntities, - ExplosionType.ID, - _xformQuery, - _damageQuery, - _physicsQuery, - _tagQuery, - _projectileQuery); + ExplosionType.ID); // If the floor is not blocked by some dense object, damage the floor tiles. if (canDamageFloor) @@ -802,12 +796,7 @@ sealed class Explosion _currentDamage, Epicenter, ProcessedEntities, - ExplosionType.ID, - _xformQuery, - _damageQuery, - _physicsQuery, - _tagQuery, - _projectileQuery); + ExplosionType.ID); } if (!MoveNext()) diff --git a/Content.Server/Explosion/EntitySystems/ExplosionSystem.cs b/Content.Server/Explosion/EntitySystems/ExplosionSystem.cs index 5e5af03c17..aa1ad71b16 100644 --- a/Content.Server/Explosion/EntitySystems/ExplosionSystem.cs +++ b/Content.Server/Explosion/EntitySystems/ExplosionSystem.cs @@ -14,12 +14,15 @@ using Content.Shared.Database; using Content.Shared.Explosion; using Content.Shared.GameTicking; using Content.Shared.Inventory; +using Content.Shared.Mind; +using Content.Shared.Projectiles; using Content.Shared.Throwing; using Robust.Server.GameStates; using Robust.Server.Player; using Robust.Shared.Audio; using Robust.Shared.Configuration; using Robust.Shared.Map; +using Robust.Shared.Physics.Components; using Robust.Shared.Player; using Robust.Shared.Prototypes; using Robust.Shared.Random; @@ -47,6 +50,12 @@ public sealed partial class ExplosionSystem : EntitySystem [Dependency] private readonly PvsOverrideSystem _pvsSys = default!; [Dependency] private readonly SharedTransformSystem _transformSystem = default!; + private EntityQuery _transformQuery; + private EntityQuery _damageQuery; + private EntityQuery _physicsQuery; + private EntityQuery _projectileQuery; + private EntityQuery _mindQuery; + /// /// "Tile-size" for space when there are no nearby grids to use as a reference. /// @@ -93,6 +102,12 @@ public sealed partial class ExplosionSystem : EntitySystem SubscribeCvars(); InitAirtightMap(); InitVisuals(); + + _transformQuery = GetEntityQuery(); + _damageQuery = GetEntityQuery(); + _physicsQuery = GetEntityQuery(); + _projectileQuery = GetEntityQuery(); + _mindQuery = GetEntityQuery(); } private void OnReset(RoundRestartCleanupEvent ev)