]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
Fix storage destruction/deletion bug (#24882)
authorLeon Friedrich <60421075+ElectroJr@users.noreply.github.com>
Sat, 3 Feb 2024 00:54:48 +0000 (19:54 -0500)
committerGitHub <noreply@github.com>
Sat, 3 Feb 2024 00:54:48 +0000 (11:54 +1100)
Content.IntegrationTests/Tests/Storage/EntityStorageTests.cs [new file with mode: 0644]
Content.Shared/Storage/EntitySystems/SharedEntityStorageSystem.cs

diff --git a/Content.IntegrationTests/Tests/Storage/EntityStorageTests.cs b/Content.IntegrationTests/Tests/Storage/EntityStorageTests.cs
new file mode 100644 (file)
index 0000000..45ee69a
--- /dev/null
@@ -0,0 +1,81 @@
+using Content.Server.Storage.EntitySystems;
+using Content.Shared.Damage;
+using Robust.Shared.Containers;
+using Robust.Shared.GameObjects;
+
+namespace Content.IntegrationTests.Tests.Storage;
+
+[TestFixture]
+public sealed class EntityStorageTests
+{
+    [TestPrototypes]
+    private const string Prototypes = @"
+- type: entity
+  id: EntityStorageTest
+  name: box
+  components:
+  - type: EntityStorage
+  - type: Damageable
+    damageContainer: Inorganic
+  - type: Destructible
+    thresholds:
+    - trigger:
+        !type:DamageTrigger
+        damage: 10
+      behaviors:
+      - !type:DoActsBehavior
+        acts: [ Destruction ]
+";
+
+    [Test]
+    public async Task TestContainerDestruction()
+    {
+        await using var pair = await PoolManager.GetServerClient();
+        var server = pair.Server;
+        var map = await pair.CreateTestMap();
+
+        EntityUid box = default;
+        EntityUid crowbar = default;
+        await server.WaitPost(() => box = server.EntMan.SpawnEntity("EntityStorageTest", map.GridCoords));
+        await server.WaitPost(() => crowbar = server.EntMan.SpawnEntity("Crowbar", map.GridCoords));
+
+        // Initially the crowbar is not in a contaienr.
+        var sys = server.System<SharedContainerSystem>();
+        Assert.That(sys.IsEntityInContainer(crowbar), Is.False);
+
+        // Open then close the storage entity
+        var storage = server.System<EntityStorageSystem>();
+        await server.WaitPost(() =>
+        {
+            storage.OpenStorage(box);
+            storage.CloseStorage(box);
+        });
+
+        // Crowbar is now in the box
+        Assert.That(sys.IsEntityInContainer(crowbar));
+
+        // Damage the box
+        var damage = new DamageSpecifier();
+        damage.DamageDict.Add("Blunt", 100);
+        await server.WaitPost(() => server.System<DamageableSystem>().TryChangeDamage(box, damage));
+
+        // Box has been destroyed, contents have been emptied. Destruction uses deffered deletion.
+        Assert.That(server.EntMan.IsQueuedForDeletion(box));
+        Assert.That(sys.IsEntityInContainer(crowbar), Is.False);
+
+        // Opening and closing the soon-to-be-deleted box should not re-insert the crowbar
+        await server.WaitPost(() =>
+        {
+            storage.OpenStorage(box);
+            storage.CloseStorage(box);
+        });
+        Assert.That(sys.IsEntityInContainer(crowbar), Is.False);
+
+        // Entity gets deleted after a few ticks
+        await server.WaitRunTicks(5);
+        Assert.That(server.EntMan.Deleted(box));
+        Assert.That(server.EntMan.Deleted(crowbar), Is.False);
+
+        await pair.CleanReturnAsync();
+    }
+}
index 19e41848a1eb8a8520b436f0383710778f55cb71..8f8707ccde8892a352d88ca60ddf32f6edf194ac 100644 (file)
@@ -198,6 +198,9 @@ public abstract class SharedEntityStorageSystem : EntitySystem
         if (!ResolveStorage(uid, ref component))
             return;
 
+        if (component.Open)
+            return;
+
         var beforeev = new StorageBeforeOpenEvent();
         RaiseLocalEvent(uid, ref beforeev);
         component.Open = true;
@@ -216,6 +219,16 @@ public abstract class SharedEntityStorageSystem : EntitySystem
         if (!ResolveStorage(uid, ref component))
             return;
 
+        if (!component.Open)
+            return;
+
+        // Prevent the container from closing if it is queued for deletion. This is so that the container-emptying
+        // behaviour of DestructionEventArgs is respected. This exists because malicious players were using
+        // destructible boxes to delete entities by having two players simultaneously destroy and close the box in
+        // the same tick.
+        if (EntityManager.IsQueuedForDeletion(uid))
+            return;
+
         component.Open = false;
         Dirty(uid, component);