Small ExplosionSystem Cleanup (#20817)

This commit is contained in:
Leon Friedrich
2023-10-15 03:48:25 +11:00
committed by GitHub
parent 0775ab6a14
commit ce76a03d5e
2 changed files with 85 additions and 81 deletions

View File

@@ -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
{
/// <summary>
/// Used to limit explosion processing time. See <see cref="MaxProcessingTime"/>.
@@ -176,12 +178,12 @@ public sealed partial class ExplosionSystem : EntitySystem
/// Used for a variation of <see cref="TurfHelpers.IsBlockedTurf()"/> 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.
/// </remarks>
public bool IsBlockingTurf(EntityUid uid, EntityQuery<PhysicsComponent> 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<EntityUid> processed,
string id,
EntityQuery<TransformComponent> xformQuery,
EntityQuery<DamageableComponent> damageQuery,
EntityQuery<PhysicsComponent> physicsQuery,
EntityQuery<TagComponent> tagQuery,
EntityQuery<ProjectileComponent> 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<TransformComponent> 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<TransformComponent> List, HashSet<EntityUid> Processed, EntityQuery<TransformComponent> XformQuery) state,
ref (List<(EntityUid, TransformComponent)> List, HashSet<EntityUid> Processed, EntityQuery<TransformComponent> 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<TransformComponent> List, HashSet<EntityUid> Processed, EntityQuery<TransformComponent> XformQuery) state,
ref (List<(EntityUid, TransformComponent)> List, HashSet<EntityUid> Processed, EntityQuery<TransformComponent> XformQuery) state,
in FixtureProxy proxy)
{
var owner = proxy.Entity;
@@ -299,17 +296,12 @@ public sealed partial class ExplosionSystem : EntitySystem
DamageSpecifier damage,
MapCoordinates epicenter,
HashSet<EntityUid> processed,
string id,
EntityQuery<TransformComponent> xformQuery,
EntityQuery<DamageableComponent> damageQuery,
EntityQuery<PhysicsComponent> physicsQuery,
EntityQuery<TagComponent> tagQuery,
EntityQuery<ProjectileComponent> projectileQuery)
string id)
{
var gridBox = Box2.FromDimensions(tile * DefaultTileSize, new Vector2(DefaultTileSize, DefaultTileSize));
var worldBox = spaceMatrix.TransformBox(gridBox);
var list = new List<TransformComponent>();
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<TransformComponent> List, HashSet<EntityUid> Processed, Matrix3 InvSpaceMatrix, EntityUid LookupOwner, EntityQuery<TransformComponent> XformQuery, Box2 GridBox) state,
ref (List<(EntityUid, TransformComponent)> List, HashSet<EntityUid> Processed, Matrix3 InvSpaceMatrix, EntityUid LookupOwner, EntityQuery<TransformComponent> 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<TransformComponent> List, HashSet<EntityUid> Processed, Matrix3 InvSpaceMatrix, EntityUid LookupOwner, EntityQuery<TransformComponent> XformQuery, Box2 GridBox) state,
ref (List<(EntityUid, TransformComponent)> List, HashSet<EntityUid> Processed, Matrix3 InvSpaceMatrix, EntityUid LookupOwner, EntityQuery<TransformComponent> 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<DamageableComponent> damageQuery,
EntityQuery<PhysicsComponent> physicsQuery,
EntityQuery<TransformComponent> transformQuery,
EntityQuery<TagComponent> tagQuery,
EntityQuery<ProjectileComponent> 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<MindContainerComponent>(uid) || HasComp<ExplosiveComponent>(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<MindContainerComponent>(uid) || HasComp<ExplosiveComponent>(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())

View File

@@ -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<TransformComponent> _transformQuery;
private EntityQuery<DamageableComponent> _damageQuery;
private EntityQuery<PhysicsComponent> _physicsQuery;
private EntityQuery<ProjectileComponent> _projectileQuery;
private EntityQuery<MindComponent> _mindQuery;
/// <summary>
/// "Tile-size" for space when there are no nearby grids to use as a reference.
/// </summary>
@@ -93,6 +102,12 @@ public sealed partial class ExplosionSystem : EntitySystem
SubscribeCvars();
InitAirtightMap();
InitVisuals();
_transformQuery = GetEntityQuery<TransformComponent>();
_damageQuery = GetEntityQuery<DamageableComponent>();
_physicsQuery = GetEntityQuery<PhysicsComponent>();
_projectileQuery = GetEntityQuery<ProjectileComponent>();
_mindQuery = GetEntityQuery<MindComponent>();
}
private void OnReset(RoundRestartCleanupEvent ev)