]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
Reduce explosion airtight cache memory usage (#40912)
authorPieter-Jan Briers <pieterjan.briers+git@gmail.com>
Wed, 3 Dec 2025 15:52:25 +0000 (16:52 +0100)
committerGitHub <noreply@github.com>
Wed, 3 Dec 2025 15:52:25 +0000 (15:52 +0000)
* Reduce explosion airtight cache memory usage

This means you can happily add explosion prototypes again

New approach has the tolerance value data in a shared storage with reference counting.

* Oops fix index removal

* Remove debug code and fix merge conflicts

* Also address my other review

* Oh it's in two places lmao

---------

Co-authored-by: Princess Cheeseballs <66055347+Pronana@users.noreply.github.com>
Content.Server/Explosion/Components/ExplosionAirtightGridComponent.cs [new file with mode: 0644]
Content.Server/Explosion/EntitySystems/ExplosionGridTileFlood.cs
Content.Server/Explosion/EntitySystems/ExplosionSystem.Airtight.cs
Content.Server/Explosion/EntitySystems/ExplosionSystem.GridMap.cs
Content.Server/Explosion/EntitySystems/ExplosionSystem.TileFill.cs
Content.Server/Explosion/EntitySystems/ExplosionSystem.cs
Resources/Prototypes/explosion.yml

diff --git a/Content.Server/Explosion/Components/ExplosionAirtightGridComponent.cs b/Content.Server/Explosion/Components/ExplosionAirtightGridComponent.cs
new file mode 100644 (file)
index 0000000..68f576d
--- /dev/null
@@ -0,0 +1,100 @@
+using Content.Server.Explosion.EntitySystems;
+using Content.Shared.Atmos;
+using Content.Shared.FixedPoint;
+using Robust.Shared.Map.Components;
+using Robust.Shared.Utility;
+
+namespace Content.Server.Explosion.Components;
+
+/// <summary>
+/// Stores data for airtight explosion traversal on a <see cref="MapGridComponent"/> entity.
+/// </summary>
+/// <seealso cref="ExplosionSystem"/>
+[RegisterComponent]
+[Access(typeof(ExplosionSystem), Other = AccessPermissions.None)]
+public sealed partial class ExplosionAirtightGridComponent : Component
+{
+    /// <summary>
+    /// Data for every tile on the current grid.
+    /// </summary>
+    /// <remarks>
+    /// Intentionally not saved.
+    /// </remarks>
+    [ViewVariables]
+    public readonly Dictionary<Vector2i, TileData> Tiles = new();
+
+    /// <summary>
+    ///     Data struct that describes the explosion-blocking airtight entities on a tile.
+    /// </summary>
+    public struct TileData
+    {
+        /// <summary>
+        /// Which index into the tolerance cache of <see cref="ExplosionSystem"/> this tile is using.
+        /// </summary>
+        public required int ToleranceCacheIndex;
+
+        /// <summary>
+        /// Which directions this tile is blocking explosions in. Bitflag field.
+        /// </summary>
+        public required AtmosDirection BlockedDirections;
+    }
+
+    /// <summary>
+    /// A set of tolerance values
+    /// </summary>
+    public struct ToleranceValues : IEquatable<ToleranceValues>
+    {
+        /// <summary>
+        /// Special value that indicates the entity is "invulnerable" against a specific explosion type.
+        /// </summary>
+        /// <remarks>
+        /// Here to deal with the limited range of <see cref="FixedPoint2"/> over typical floats.
+        /// </remarks>
+        public static readonly FixedPoint2 Invulnerable = FixedPoint2.MaxValue;
+
+        /// <summary>
+        /// The intensities at which explosions of each type can instantly break through an entity.
+        /// </summary>
+        /// <remarks>
+        /// <para>
+        /// This is an array, with the index of each value corresponding to the "explosion type ID" cached by
+        /// <see cref="ExplosionSystem"/>.
+        /// </para>
+        /// <para>
+        /// Values are stored as <see cref="FixedPoint2"/> to avoid possible precision issues resulting in
+        /// different-but-almost-identical tolerance values wasting memory.
+        /// </para>
+        /// <para>
+        /// If a value is <see cref="Invulnerable"/>, that indicates the tile is invulnerable.
+        /// </para>
+        /// </remarks>
+        public required FixedPoint2[] Values;
+
+        public bool Equals(ToleranceValues other)
+        {
+            return Values.AsSpan().SequenceEqual(other.Values);
+        }
+
+        public override bool Equals(object? obj)
+        {
+            return obj is ToleranceValues other && Equals(other);
+        }
+
+        public override int GetHashCode()
+        {
+            var hc = new HashCode();
+            hc.AddArray(Values);
+            return hc.ToHashCode();
+        }
+
+        public static bool operator ==(ToleranceValues left, ToleranceValues right)
+        {
+            return left.Equals(right);
+        }
+
+        public static bool operator !=(ToleranceValues left, ToleranceValues right)
+        {
+            return !left.Equals(right);
+        }
+    }
+}
index da3ce635af075046e594654e4f9fb2af5e8e4148..0274979c556e68faea16abe4d34ee7152793e12b 100644 (file)
@@ -1,7 +1,8 @@
 using System.Numerics;
 using Content.Shared.Atmos;
-using Robust.Shared.Map;
+using Content.Shared.FixedPoint;
 using Robust.Shared.Map.Components;
+using static Content.Server.Explosion.Components.ExplosionAirtightGridComponent;
 using static Content.Server.Explosion.EntitySystems.ExplosionSystem;
 
 namespace Content.Server.Explosion.EntitySystems;
@@ -11,6 +12,8 @@ namespace Content.Server.Explosion.EntitySystems;
 /// </summary>
 public sealed class ExplosionGridTileFlood : ExplosionTileFlood
 {
+    private readonly ExplosionSystem _explosionSystem;
+
     public Entity<MapGridComponent> Grid;
     private bool _needToTransform = false;
 
@@ -45,7 +48,8 @@ public sealed class ExplosionGridTileFlood : ExplosionTileFlood
         Dictionary<Vector2i, NeighborFlag> edgeTiles,
         EntityUid? referenceGrid,
         Matrix3x2 spaceMatrix,
-        Angle spaceAngle)
+        Angle spaceAngle,
+        ExplosionSystem explosionSystem)
     {
         Grid = grid;
         _airtightMap = airtightMap;
@@ -53,6 +57,7 @@ public sealed class ExplosionGridTileFlood : ExplosionTileFlood
         _intensityStepSize = intensityStepSize;
         _typeIndex = typeIndex;
         _edgeTiles = edgeTiles;
+        _explosionSystem = explosionSystem;
 
         // initialise SpaceTiles
         foreach (var (tile, spaceNeighbors) in _edgeTiles)
@@ -193,11 +198,11 @@ public sealed class ExplosionGridTileFlood : ExplosionTileFlood
             NewBlockedTiles.Add(tile);
 
             // At what explosion iteration would this blocker be destroyed?
-            var required = tileData.ExplosionTolerance[_typeIndex];
+            var required = _explosionSystem.GetToleranceValues(tileData.ToleranceCacheIndex).Values[_typeIndex];
             if (required > _maxIntensity)
                 return; // blocker is never destroyed.
 
-            var clearIteration = iteration + (int) MathF.Ceiling(required / _intensityStepSize);
+            var clearIteration = iteration + (int) MathF.Ceiling((float)required / _intensityStepSize);
             if (FreedTileLists.TryGetValue(clearIteration, out var list))
                 list.Add(tile);
             else
@@ -261,13 +266,13 @@ public sealed class ExplosionGridTileFlood : ExplosionTileFlood
         foreach (var tile in tiles)
         {
             var blockedDirections = AtmosDirection.Invalid;
-            float sealIntegrity = 0;
+            FixedPoint2 sealIntegrity = 0;
 
             // Note that if (grid, tile) is not a valid key, then airtight.BlockedDirections will default to 0 (no blocked directions)
             if (_airtightMap.TryGetValue(tile, out var tileData))
             {
                 blockedDirections = tileData.BlockedDirections;
-                sealIntegrity = tileData.ExplosionTolerance[_typeIndex];
+                sealIntegrity = _explosionSystem.GetToleranceValues(tileData.ToleranceCacheIndex).Values[_typeIndex];
             }
 
             // First, yield any neighboring tiles that are not blocked by airtight entities on this tile
@@ -290,7 +295,7 @@ public sealed class ExplosionGridTileFlood : ExplosionTileFlood
                 continue;
 
             // At what explosion iteration would this blocker be destroyed?
-            var clearIteration = iteration + (int) MathF.Ceiling(sealIntegrity / _intensityStepSize);
+            var clearIteration = iteration + (int) MathF.Ceiling((float) sealIntegrity / _intensityStepSize);
 
             // Get the delayed neighbours list
             if (!_delayedNeighbors.TryGetValue(clearIteration, out var list))
index 303c4e8cab9859187354dabe482a13d76202fd1c..da2a57190026c11b16f576afa3b45fbf14f82ee0 100644 (file)
@@ -1,44 +1,59 @@
+using System.Linq;
+using System.Runtime.InteropServices;
 using Content.Server.Atmos.Components;
+using Content.Server.Explosion.Components;
 using Content.Shared.Atmos;
 using Content.Shared.Damage.Systems;
 using Content.Shared.Explosion;
 using Content.Shared.FixedPoint;
+using Robust.Shared.Collections;
 using Robust.Shared.Map.Components;
+using Robust.Shared.Prototypes;
+using Robust.Shared.Utility;
+using static Content.Server.Explosion.Components.ExplosionAirtightGridComponent;
 
 namespace Content.Server.Explosion.EntitySystems;
 
 public sealed partial class ExplosionSystem
 {
-    private readonly Dictionary<string, int> _explosionTypes = new();
+    // We keep track of which tiles are airtight, and how much damage from explosions those airtight blockers can take.
+    // This is quite complicated, as the data effectively needs to be tracked *per tile*, *per explosion type*.
+    // To avoid wasting significant memory, we calculate the values and share the actual backing storage of it.
+    // Stored values are reference counted so they can be evicted when no longer needed.
+    // At the time of writing, this compacts the storage for Box Station from ~5500 tolerance value sets to 13,
+    // at round start.
+
+    // Use integers instead of prototype IDs for storage of explosion data.
+    // This allows us to replace a Dictionary<string, FixedPoint2> with just a FixedPoint2[].
+    private readonly Dictionary<ProtoId<ExplosionPrototype>, int> _explosionTypes = new();
+    // Index to look up if we already have an existing set of tolerance values stored, so the data can be shared.
+    private readonly Dictionary<ToleranceValues, int> _toleranceIndex = new();
+    // Storage for tolerance values. Entries form a free linked list when not occupied by a set of real values.
+    private ValueList<CacheEntry> _toleranceData;
+    // First free position in _toleranceData.
+    // -1 indicates there are no free slots left and the storage must be expanded.
+    private int _freeListHead = -1;
 
     private void InitAirtightMap()
     {
-        // Currently explosion prototype hot-reload isn't supported, as it would involve completely re-computing the
-        // airtight map. Could be done, just not yet implemented.
+        _explosionTypes.Clear();
 
-        // for storing airtight entity damage thresholds for all anchored airtight entities, we will use integers in
-        // place of id-strings. This initializes the string <--> id association.
-        // This allows us to replace a Dictionary<string, float> with just a float[].
         int index = 0;
         foreach (var prototype in _prototypeManager.EnumeratePrototypes<ExplosionPrototype>())
         {
-            // TODO EXPLOSION
-            // just make this a field on the prototype
             _explosionTypes.Add(prototype.ID, index);
             index++;
         }
     }
 
-    // The explosion intensity required to break an entity depends on the explosion type. So it is stored in a
-    // Dictionary<string, float>
-    //
-    // Hence, each tile has a tuple (Dictionary<string, float>, AtmosDirection). This specifies what directions are
-    // blocked, and how intense a given explosion type needs to be in order to destroy ALL airtight entities on that
-    // tile. This is the TileData struct.
-    //
-    // We then need this data for every tile on a grid. So this mess of a variable maps the Grid ID and Vector2i grid
-    // indices to this tile-data struct.
-    private Dictionary<EntityUid, Dictionary<Vector2i, TileData>> _airtightMap = new();
+    private void ReloadExplosionPrototypes(PrototypesReloadedEventArgs prototypesReloadedEventArgs)
+    {
+        if (!prototypesReloadedEventArgs.Modified.Contains(typeof(ExplosionPrototype)))
+            return;
+
+        InitAirtightMap();
+        ReloadMap();
+    }
 
     public void UpdateAirtightMap(EntityUid gridId, Vector2i tile, MapGridComponent? grid = null)
     {
@@ -46,6 +61,12 @@ public sealed partial class ExplosionSystem
             UpdateAirtightMap(gridId, grid, tile);
     }
 
+    [Access(typeof(ExplosionGridTileFlood))]
+    public ToleranceValues GetToleranceValues(int idx)
+    {
+        return _toleranceData[idx].Values;
+    }
+
     /// <summary>
     ///     Update the map of explosion blockers.
     /// </summary>
@@ -58,11 +79,12 @@ public sealed partial class ExplosionSystem
     /// </remarks>
     public void UpdateAirtightMap(EntityUid gridId, MapGridComponent grid, Vector2i tile)
     {
-        var tolerance = new float[_explosionTypes.Count];
-        var blockedDirections = AtmosDirection.Invalid;
+        var airtightGrid = EnsureComp<ExplosionAirtightGridComponent>(gridId);
+
+        // Calculate tile new airtight state.
 
-        if (!_airtightMap.ContainsKey(gridId))
-            _airtightMap[gridId] = new();
+        var tolerance = new FixedPoint2[_explosionTypes.Count];
+        var blockedDirections = AtmosDirection.Invalid;
 
         var anchoredEnumerator = _map.GetAnchoredEntitiesEnumerator(gridId, grid, tile);
 
@@ -72,17 +94,97 @@ public sealed partial class ExplosionSystem
                 continue;
 
             blockedDirections |= airtight.AirBlockedDirection;
-            var entityTolerances = GetExplosionTolerance(uid.Value);
-            for (var i = 0; i < tolerance.Length; i++)
+            GetExplosionTolerance(uid.Value, tolerance);
+        }
+
+        // Log.Info($"UPDATE {gridId}/{tile}: {blockedDirections}");
+
+        if (blockedDirections == AtmosDirection.Invalid)
+        {
+            // No longer airtight
+
+            if (!airtightGrid.Tiles.Remove(tile, out var tileData))
             {
-                tolerance[i] = Math.Max(tolerance[i], entityTolerances[i]);
+                // Did not have this tile before and after, nothing to do.
+                return;
             }
+
+            // Removing tile data.
+            DecrementRefCount(tileData.ToleranceCacheIndex);
+            return;
         }
 
-        if (blockedDirections != AtmosDirection.Invalid)
-            _airtightMap[gridId][tile] = new(tolerance, blockedDirections);
+        ref var tileEntry = ref CollectionsMarshal.GetValueRefOrAddDefault(airtightGrid.Tiles, tile, out var existed);
+        var cacheKey = new ToleranceValues { Values = tolerance };
+
+        // Remove previous tolerance reference if necessary.
+        if (existed)
+        {
+            ref var prevEntry = ref _toleranceData[tileEntry.ToleranceCacheIndex];
+            if (prevEntry.Values == cacheKey)
+            {
+                // No change.
+                return;
+            }
+
+            DecrementRefCount(tileEntry.ToleranceCacheIndex);
+        }
+
+        ref var newCacheIndex = ref CollectionsMarshal.GetValueRefOrAddDefault(_toleranceIndex, cacheKey, out existed);
+        if (existed)
+        {
+            _toleranceData[newCacheIndex].RefCount += 1;
+        }
         else
-            _airtightMap[gridId].Remove(tile);
+        {
+            if (_freeListHead < 0)
+                ExpandCache();
+
+            newCacheIndex = _freeListHead;
+            ref var newCacheEntry = ref _toleranceData[newCacheIndex];
+            _freeListHead = newCacheEntry.RefCount;
+
+            newCacheEntry.Values = cacheKey;
+            newCacheEntry.RefCount = 1;
+        }
+
+        tileEntry = new TileData
+        {
+            BlockedDirections = blockedDirections,
+            ToleranceCacheIndex = newCacheIndex,
+        };
+    }
+
+    private void ExpandCache()
+    {
+        var newCacheSize = Math.Max(8, _toleranceData.Count * 2);
+        var curSize = _toleranceData.Count;
+
+        _toleranceData.EnsureLength(newCacheSize);
+        for (var i = curSize; i < newCacheSize; i++)
+        {
+            _toleranceData[i].RefCount = _freeListHead;
+            _freeListHead = i;
+        }
+    }
+
+    private void DecrementRefCount(int index)
+    {
+        ref var cacheEntry = ref _toleranceData[index];
+
+        DebugTools.Assert(cacheEntry.RefCount > 0);
+        cacheEntry.RefCount -= 1;
+
+        if (cacheEntry.RefCount == 0)
+        {
+            var prevValue = cacheEntry.Values;
+            cacheEntry.Values = default;
+            cacheEntry.RefCount = _freeListHead;
+            _freeListHead = index;
+
+            var result = _toleranceIndex.Remove(prevValue);
+            DebugTools.Assert(result, "Failed to removed 0 refcounted index!");
+        }
     }
 
     /// <summary>
@@ -106,7 +208,7 @@ public sealed partial class ExplosionSystem
     /// <summary>
     ///     Return a dictionary that specifies how intense a given explosion type needs to be in order to destroy an entity.
     /// </summary>
-    public float[] GetExplosionTolerance(EntityUid uid)
+    private void GetExplosionTolerance(EntityUid uid, Span<FixedPoint2> explosionTolerance)
     {
         // How much total damage is needed to destroy this entity? This also includes "break" behaviors. This ASSUMES
         // that this will result in a non-airtight entity.Entities that ONLY break via construction graph node changes
@@ -117,14 +219,14 @@ public sealed partial class ExplosionSystem
             totalDamageTarget = _destructibleSystem.DestroyedAt(uid, destructible);
         }
 
-        var explosionTolerance = new float[_explosionTypes.Count];
         if (totalDamageTarget == FixedPoint2.MaxValue || !_damageableQuery.TryGetComponent(uid, out var damageable))
         {
             for (var i = 0; i < explosionTolerance.Length; i++)
             {
-                explosionTolerance[i] = float.MaxValue;
+                explosionTolerance[i] = ToleranceValues.Invulnerable;
             }
-            return explosionTolerance;
+
+            return;
         }
 
         // What multiple of each explosion type damage set will result in the damage exceeding the required amount? This
@@ -157,38 +259,43 @@ public sealed partial class ExplosionSystem
                 damagePerIntensity += value * mod * Math.Max(0, ev.DamageCoefficient);
             }
 
-            explosionTolerance[index] = damagePerIntensity > 0
+            var toleranceValue = damagePerIntensity > 0
                 ? (float) ((totalDamageTarget - damageable.TotalDamage) / damagePerIntensity)
-                : float.MaxValue;
-        }
+                : ToleranceValues.Invulnerable;
 
-        return explosionTolerance;
+            explosionTolerance[index] = toleranceValue;
+        }
     }
 
-    /// <summary>
-    ///     Data struct that describes the explosion-blocking airtight entities on a tile.
-    /// </summary>
-    public struct TileData
+    private void OnAirtightGridRemoved(EntityUid entity)
     {
-        public TileData(float[] explosionTolerance, AtmosDirection blockedDirections)
+        if (!TryComp(entity, out ExplosionAirtightGridComponent? airtightGrid))
+            return;
+
+        foreach (var tile in airtightGrid.Tiles.Values)
         {
-            ExplosionTolerance = explosionTolerance;
-            BlockedDirections = blockedDirections;
+            DecrementRefCount(tile.ToleranceCacheIndex);
         }
 
-        public float[] ExplosionTolerance;
-        public AtmosDirection BlockedDirections = AtmosDirection.Invalid;
+        RemComp<ExplosionAirtightGridComponent>(entity);
     }
 
     public override void ReloadMap()
     {
-        foreach (var(grid, dict) in _airtightMap)
+        var enumerator = EntityQueryEnumerator<ExplosionAirtightGridComponent, MapGridComponent>();
+        while (enumerator.MoveNext(out var uid, out var airtightComp, out var mapGrid))
         {
-            var comp = Comp<MapGridComponent>(grid);
-            foreach (var index in dict.Keys)
+            foreach (var pos in airtightComp.Tiles.Keys)
             {
-                UpdateAirtightMap(grid, comp, index);
+                UpdateAirtightMap(uid, pos, mapGrid);
             }
         }
     }
+
+    private struct CacheEntry
+    {
+        public ToleranceValues Values;
+        public int RefCount; // Doubles as freelist chain
+    }
+
 }
index 5c032d5c82e4e4e9c3ce8c8ffb9db638ea8d8bad..3767d0c2380f21abee327510838816ba97cf6560 100644 (file)
@@ -38,7 +38,7 @@ public sealed partial class ExplosionSystem
 
     private void OnGridRemoved(GridRemovalEvent ev)
     {
-        _airtightMap.Remove(ev.EntityUid);
+        OnAirtightGridRemoved(ev.EntityUid);
         _gridEdges.Remove(ev.EntityUid);
 
         // this should be a small enough set that iterating all of them is fine
index ac539da213d7cfdb1f98bf8e8b8df3b273c1d12a..a274fa866034991f52b6f9cddb4520cd6f36fbc0 100644 (file)
@@ -1,5 +1,6 @@
 using System.Linq;
 using System.Numerics;
+using Content.Server.Explosion.Components;
 using Content.Shared.Administration;
 using Content.Shared.Explosion.Components;
 using Robust.Shared.Map;
@@ -40,11 +41,7 @@ public sealed partial class ExplosionSystem
         if (totalIntensity <= 0 || slope <= 0)
             return null;
 
-        if (!_explosionTypes.TryGetValue(typeID, out var typeIndex))
-        {
-            Log.Error("Attempted to spawn explosion using a prototype that was not defined during initialization. Explosion prototype hot-reload is not currently supported.");
-            return null;
-        }
+        var typeIndex = _explosionTypes[typeID];
 
         Vector2i initialTile;
         EntityUid? epicentreGrid = null;
@@ -103,8 +100,7 @@ public sealed partial class ExplosionSystem
             // set up the initial `gridData` instance
             encounteredGrids.Add(epicentreGrid.Value);
 
-            if (!_airtightMap.TryGetValue(epicentreGrid.Value, out var airtightMap))
-                airtightMap = new();
+            var airtightMap = CompOrNull<ExplosionAirtightGridComponent>(epicentreGrid)?.Tiles ?? new();
 
             var initialGridData = new ExplosionGridTileFlood(
                 (epicentreGrid.Value, Comp<MapGridComponent>(epicentreGrid.Value)),
@@ -115,7 +111,8 @@ public sealed partial class ExplosionSystem
                 _gridEdges[epicentreGrid.Value],
                 referenceGrid,
                 spaceMatrix,
-                spaceAngle);
+                spaceAngle,
+                this);
 
             gridData[epicentreGrid.Value] = initialGridData;
 
@@ -192,8 +189,7 @@ public sealed partial class ExplosionSystem
                 // is this a new grid, for which we must create a new explosion data set
                 if (!gridData.TryGetValue(grid, out var data))
                 {
-                    if (!_airtightMap.TryGetValue(grid, out var airtightMap))
-                        airtightMap = new();
+                    var airtightMap = CompOrNull<ExplosionAirtightGridComponent>(grid)?.Tiles ?? new();
 
                     data = new ExplosionGridTileFlood(
                         (grid, Comp<MapGridComponent>(grid)),
@@ -204,7 +200,8 @@ public sealed partial class ExplosionSystem
                         _gridEdges[grid],
                         referenceGrid,
                         spaceMatrix,
-                        spaceAngle);
+                        spaceAngle,
+                        this);
 
                     gridData[grid] = data;
                 }
index 70863d6f549de5b81ffd49df09128b0a19ae130b..b5163c6157eb65fa36c06e371c2605d1df9f1c17 100644 (file)
@@ -104,6 +104,8 @@ public sealed partial class ExplosionSystem : SharedExplosionSystem
         _destructibleQuery = GetEntityQuery<DestructibleComponent>();
         _damageableQuery = GetEntityQuery<DamageableComponent>();
         _airtightQuery = GetEntityQuery<AirtightComponent>();
+
+        _prototypeManager.PrototypesReloaded += ReloadExplosionPrototypes;
     }
 
     private void OnReset(RoundRestartCleanupEvent ev)
@@ -122,6 +124,7 @@ public sealed partial class ExplosionSystem : SharedExplosionSystem
         base.Shutdown();
         _nodeGroupSystem.PauseUpdating = false;
         _pathfindingSystem.PauseUpdating = false;
+        _prototypeManager.PrototypesReloaded -= ReloadExplosionPrototypes;
     }
 
     private void RelayedResistance(EntityUid uid, ExplosionResistanceComponent component,
index ad0033389281046d05eaf0331b7c648cf81a77ef..4d3febeda7d2ab84a76094948a7ef018c4f0bea5 100644 (file)
@@ -1,11 +1,3 @@
-#  Does not currently support prototype hot-reloading. See comments in c# file.
-
-# Note that for every explosion type you define, explosions & nukes will start performing worse
-# You should only define a new explopsion type if you really need to
-#
-# If you just want to modify properties other than `damagePerIntensity`, it'd be better to
-# split off explosion damage & explosion visuals/effects into their own separate prototypes.
-
 - type: explosion
   id: Default
   damagePerIntensity:
   texturePath: /Textures/Effects/fire.rsi
   fireStates: 3
   fireStacks: 2
-
-# STOP
-# BEFORE YOU ADD MORE EXPLOSION TYPES CONSIDER IF AN EXISTING ONE IS SUITABLE
-# ADDING NEW ONES IS PROHIBITIVELY EXPENSIVE