Fix storage destruction/deletion bug (#24882)
This commit is contained in:
81
Content.IntegrationTests/Tests/Storage/EntityStorageTests.cs
Normal file
81
Content.IntegrationTests/Tests/Storage/EntityStorageTests.cs
Normal file
@@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -198,6 +198,9 @@ public abstract class SharedEntityStorageSystem : EntitySystem
|
|||||||
if (!ResolveStorage(uid, ref component))
|
if (!ResolveStorage(uid, ref component))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (component.Open)
|
||||||
|
return;
|
||||||
|
|
||||||
var beforeev = new StorageBeforeOpenEvent();
|
var beforeev = new StorageBeforeOpenEvent();
|
||||||
RaiseLocalEvent(uid, ref beforeev);
|
RaiseLocalEvent(uid, ref beforeev);
|
||||||
component.Open = true;
|
component.Open = true;
|
||||||
@@ -216,6 +219,16 @@ public abstract class SharedEntityStorageSystem : EntitySystem
|
|||||||
if (!ResolveStorage(uid, ref component))
|
if (!ResolveStorage(uid, ref component))
|
||||||
return;
|
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;
|
component.Open = false;
|
||||||
Dirty(uid, component);
|
Dirty(uid, component);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user