From fd4a0a29b4588f000017de2f8708636a0f9376f9 Mon Sep 17 00:00:00 2001 From: Perry Fraser Date: Tue, 26 Aug 2025 06:29:12 -0400 Subject: [PATCH] fix: Block EntityStorage from inserting into mechs (#37942) 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. --- .../EntitySystems/SharedHandsSystem.Drop.cs | 10 ++++++++ .../Mech/EntitySystems/SharedMechSystem.cs | 23 +++++++++++++++---- .../Components/EntityStorageComponent.cs | 7 ++++++ .../SharedEntityStorageSystem.cs | 9 +++++--- 4 files changed, 42 insertions(+), 7 deletions(-) diff --git a/Content.Shared/Hands/EntitySystems/SharedHandsSystem.Drop.cs b/Content.Shared/Hands/EntitySystems/SharedHandsSystem.Drop.cs index c1b44efba4..6512bbd7f1 100644 --- a/Content.Shared/Hands/EntitySystems/SharedHandsSystem.Drop.cs +++ b/Content.Shared/Hands/EntitySystems/SharedHandsSystem.Drop.cs @@ -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(HandleEntityRemoved); + SubscribeLocalEvent(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 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 diff --git a/Content.Shared/Mech/EntitySystems/SharedMechSystem.cs b/Content.Shared/Mech/EntitySystems/SharedMechSystem.cs index ab0f658af0..c461b588b1 100644 --- a/Content.Shared/Mech/EntitySystems/SharedMechSystem.cs +++ b/Content.Shared/Mech/EntitySystems/SharedMechSystem.cs @@ -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(RelayInteractionEvent); SubscribeLocalEvent(OnStartup); SubscribeLocalEvent(OnDestruction); + SubscribeLocalEvent(OnEntityStorageDump); SubscribeLocalEvent(OnGetAdditionalAccess); SubscribeLocalEvent(OnDragDrop); SubscribeLocalEvent(OnCanDragDrop); @@ -104,6 +106,12 @@ public abstract partial class SharedMechSystem : EntitySystem BreakMech(uid, component); } + private void OnEntityStorageDump(Entity 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 } /// - /// Destroys the mech, removing the user and ejecting all installed equipment. + /// Destroys the mech, removing the user and ejecting anything contained. /// /// /// @@ -237,14 +245,19 @@ public abstract partial class SharedMechSystem : EntitySystem /// /// /// - /// Whether or not the removal can be cancelled + /// + /// Whether or not the removal can be cancelled, and if non-mech equipment should be ejected. + /// 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); } diff --git a/Content.Shared/Storage/Components/EntityStorageComponent.cs b/Content.Shared/Storage/Components/EntityStorageComponent.cs index 9d720e4736..628cc85252 100644 --- a/Content.Shared/Storage/Components/EntityStorageComponent.cs +++ b/Content.Shared/Storage/Components/EntityStorageComponent.cs @@ -166,6 +166,13 @@ public record struct InsertIntoEntityStorageAttemptEvent(EntityUid ItemToInsert, [ByRefEvent] public record struct EntityStorageInsertedIntoAttemptEvent(EntityUid ItemToInsert, bool Cancelled = false); +/// +/// Raised on the Container's owner whenever an entity storage tries to dump its +/// contents while within a container. +/// +[ByRefEvent] +public record struct EntityStorageIntoContainerAttemptEvent(BaseContainer Container, bool Cancelled = false); + [ByRefEvent] public record struct StorageOpenAttemptEvent(EntityUid User, bool Silent, bool Cancelled = false); diff --git a/Content.Shared/Storage/EntitySystems/SharedEntityStorageSystem.cs b/Content.Shared/Storage/EntitySystems/SharedEntityStorageSystem.cs index 6039d3b940..066cd2d886 100644 --- a/Content.Shared/Storage/EntitySystems/SharedEntityStorageSystem.cs +++ b/Content.Shared/Storage/EntitySystems/SharedEntityStorageSystem.cs @@ -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(outerContainer.Owner)) + + var attemptEvent = new EntityStorageIntoContainerAttemptEvent(outerContainer); + RaiseLocalEvent(outerContainer.Owner, ref attemptEvent); + if (!attemptEvent.Cancelled) { _container.Insert(toRemove, outerContainer); return true;