]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
fix: Block EntityStorage from inserting into mechs (#37942)
authorPerry Fraser <perryprog@users.noreply.github.com>
Tue, 26 Aug 2025 10:29:12 +0000 (06:29 -0400)
committerGitHub <noreply@github.com>
Tue, 26 Aug 2025 10:29:12 +0000 (12:29 +0200)
This additionally moves the hard-coded check for HandsComp that
previously did this, and moves it into an event which now both
HandsSystem and MechSystem subscribe to.

Content.Shared/Hands/EntitySystems/SharedHandsSystem.Drop.cs
Content.Shared/Mech/EntitySystems/SharedMechSystem.cs
Content.Shared/Storage/Components/EntityStorageComponent.cs
Content.Shared/Storage/EntitySystems/SharedEntityStorageSystem.cs

index c1b44efba4740b21ffa61f1e27faa44835285f0e..6512bbd7f15a0244d9016daf8fe323bfe3b927c0 100644 (file)
@@ -3,6 +3,7 @@ using Content.Shared.Database;
 using Content.Shared.Hands.Components;
 using Content.Shared.Interaction;
 using Content.Shared.Inventory.VirtualItem;
+using Content.Shared.Storage.Components;
 using Content.Shared.Tag;
 using Robust.Shared.Containers;
 using Robust.Shared.Map;
@@ -20,6 +21,7 @@ public abstract partial class SharedHandsSystem
     private void InitializeDrop()
     {
         SubscribeLocalEvent<HandsComponent, EntRemovedFromContainerMessage>(HandleEntityRemoved);
+        SubscribeLocalEvent<HandsComponent, EntityStorageIntoContainerAttemptEvent>(OnEntityStorageDump);
     }
 
     protected virtual void HandleEntityRemoved(EntityUid uid, HandsComponent hands, EntRemovedFromContainerMessage args)
@@ -39,6 +41,14 @@ public abstract partial class SharedHandsSystem
             _virtualSystem.DeleteVirtualItem((args.Entity, @virtual), uid);
     }
 
+
+    private void OnEntityStorageDump(Entity<HandsComponent> ent, ref EntityStorageIntoContainerAttemptEvent args)
+    {
+        // If you're physically carrying an EntityStroage which tries to dump its contents out,
+        // we want those contents to fall to the floor.
+        args.Cancelled = true;
+    }
+
     private bool ShouldIgnoreRestrictions(EntityUid user)
     {
         //Checks if the Entity is something that shouldn't care about drop distance or walls ie Aghost
index ab0f658af0900ee191a4ff261862f60d8087e73f..c461b588b1b6e546d62f4f3846fee7f6cf7427e5 100644 (file)
@@ -14,6 +14,7 @@ using Content.Shared.Mech.Equipment.Components;
 using Content.Shared.Movement.Components;
 using Content.Shared.Movement.Systems;
 using Content.Shared.Popups;
+using Content.Shared.Storage.Components;
 using Content.Shared.Weapons.Melee;
 using Content.Shared.Whitelist;
 using Robust.Shared.Containers;
@@ -48,6 +49,7 @@ public abstract partial class SharedMechSystem : EntitySystem
         SubscribeLocalEvent<MechComponent, UserActivateInWorldEvent>(RelayInteractionEvent);
         SubscribeLocalEvent<MechComponent, ComponentStartup>(OnStartup);
         SubscribeLocalEvent<MechComponent, DestructionEventArgs>(OnDestruction);
+        SubscribeLocalEvent<MechComponent, EntityStorageIntoContainerAttemptEvent>(OnEntityStorageDump);
         SubscribeLocalEvent<MechComponent, GetAdditionalAccessEvent>(OnGetAdditionalAccess);
         SubscribeLocalEvent<MechComponent, DragDropTargetEvent>(OnDragDrop);
         SubscribeLocalEvent<MechComponent, CanDropTargetEvent>(OnCanDragDrop);
@@ -104,6 +106,12 @@ public abstract partial class SharedMechSystem : EntitySystem
         BreakMech(uid, component);
     }
 
+    private void OnEntityStorageDump(Entity<MechComponent> entity, ref EntityStorageIntoContainerAttemptEvent args)
+    {
+        // There's no reason we should dump into /any/ of the mech's containers.
+        args.Cancelled = true;
+    }
+
     private void OnGetAdditionalAccess(EntityUid uid, MechComponent component, ref GetAdditionalAccessEvent args)
     {
         var pilot = component.PilotSlot.ContainedEntity;
@@ -147,7 +155,7 @@ public abstract partial class SharedMechSystem : EntitySystem
     }
 
     /// <summary>
-    /// Destroys the mech, removing the user and ejecting all installed equipment.
+    /// Destroys the mech, removing the user and ejecting anything contained.
     /// </summary>
     /// <param name="uid"></param>
     /// <param name="component"></param>
@@ -237,14 +245,19 @@ public abstract partial class SharedMechSystem : EntitySystem
     /// <param name="toRemove"></param>
     /// <param name="component"></param>
     /// <param name="equipmentComponent"></param>
-    /// <param name="forced">Whether or not the removal can be cancelled</param>
+    /// <param name="forced">
+    ///     Whether or not the removal can be cancelled, and if non-mech equipment should be ejected.
+    /// </param>
     public void RemoveEquipment(EntityUid uid, EntityUid toRemove, MechComponent? component = null,
         MechEquipmentComponent? equipmentComponent = null, bool forced = false)
     {
         if (!Resolve(uid, ref component))
             return;
 
-        if (!Resolve(toRemove, ref equipmentComponent))
+        // When forced, we also want to handle the possibility that the "equipment" isn't actually equipment.
+        // This /shouldn't/ be possible thanks to OnEntityStorageDump, but there's been quite a few regressions
+        // with entities being hardlock stuck inside mechs.
+        if (!Resolve(toRemove, ref equipmentComponent) && !forced)
             return;
 
         if (!forced)
@@ -261,7 +274,9 @@ public abstract partial class SharedMechSystem : EntitySystem
         if (component.CurrentSelectedEquipment == toRemove)
             CycleEquipment(uid, component);
 
-        equipmentComponent.EquipmentOwner = null;
+        if (forced && equipmentComponent != null)
+            equipmentComponent.EquipmentOwner = null;
+
         _container.Remove(toRemove, component.EquipmentContainer);
         UpdateUserInterface(uid, component);
     }
index 9d720e4736f3e082976a9876ffc0003479bc098d..628cc85252c7e104d9f15d31455541dd56c099b5 100644 (file)
@@ -166,6 +166,13 @@ public record struct InsertIntoEntityStorageAttemptEvent(EntityUid ItemToInsert,
 [ByRefEvent]
 public record struct EntityStorageInsertedIntoAttemptEvent(EntityUid ItemToInsert, bool Cancelled = false);
 
+/// <summary>
+/// Raised on the Container's owner whenever an entity storage tries to dump its
+/// contents while within a container.
+/// </summary>
+[ByRefEvent]
+public record struct EntityStorageIntoContainerAttemptEvent(BaseContainer Container, bool Cancelled = false);
+
 [ByRefEvent]
 public record struct StorageOpenAttemptEvent(EntityUid User, bool Silent, bool Cancelled = false);
 
index 6039d3b940779320cb11fc9075aba39b5ba293e3..066cd2d88615b9704a75904c92406fc562a7c103 100644 (file)
@@ -305,10 +305,13 @@ public abstract class SharedEntityStorageSystem : EntitySystem
 
         _container.Remove(toRemove, component.Contents);
 
-        if (_container.IsEntityInContainer(container))
+        if (_container.IsEntityInContainer(container)
+            && _container.TryGetOuterContainer(container, Transform(container), out var outerContainer))
         {
-            if (_container.TryGetOuterContainer(container, Transform(container), out var outerContainer) &&
-                !HasComp<HandsComponent>(outerContainer.Owner))
+
+            var attemptEvent = new EntityStorageIntoContainerAttemptEvent(outerContainer);
+            RaiseLocalEvent(outerContainer.Owner, ref attemptEvent);
+            if (!attemptEvent.Cancelled)
             {
                 _container.Insert(toRemove, outerContainer);
                 return true;