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.
This commit is contained in:
Perry Fraser
2025-08-26 06:29:12 -04:00
committed by GitHub
parent ceda478ae2
commit fd4a0a29b4
4 changed files with 42 additions and 7 deletions

View File

@@ -3,6 +3,7 @@ using Content.Shared.Database;
using Content.Shared.Hands.Components; using Content.Shared.Hands.Components;
using Content.Shared.Interaction; using Content.Shared.Interaction;
using Content.Shared.Inventory.VirtualItem; using Content.Shared.Inventory.VirtualItem;
using Content.Shared.Storage.Components;
using Content.Shared.Tag; using Content.Shared.Tag;
using Robust.Shared.Containers; using Robust.Shared.Containers;
using Robust.Shared.Map; using Robust.Shared.Map;
@@ -20,6 +21,7 @@ public abstract partial class SharedHandsSystem
private void InitializeDrop() private void InitializeDrop()
{ {
SubscribeLocalEvent<HandsComponent, EntRemovedFromContainerMessage>(HandleEntityRemoved); SubscribeLocalEvent<HandsComponent, EntRemovedFromContainerMessage>(HandleEntityRemoved);
SubscribeLocalEvent<HandsComponent, EntityStorageIntoContainerAttemptEvent>(OnEntityStorageDump);
} }
protected virtual void HandleEntityRemoved(EntityUid uid, HandsComponent hands, EntRemovedFromContainerMessage args) 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); _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) private bool ShouldIgnoreRestrictions(EntityUid user)
{ {
//Checks if the Entity is something that shouldn't care about drop distance or walls ie Aghost //Checks if the Entity is something that shouldn't care about drop distance or walls ie Aghost

View File

@@ -14,6 +14,7 @@ using Content.Shared.Mech.Equipment.Components;
using Content.Shared.Movement.Components; using Content.Shared.Movement.Components;
using Content.Shared.Movement.Systems; using Content.Shared.Movement.Systems;
using Content.Shared.Popups; using Content.Shared.Popups;
using Content.Shared.Storage.Components;
using Content.Shared.Weapons.Melee; using Content.Shared.Weapons.Melee;
using Content.Shared.Whitelist; using Content.Shared.Whitelist;
using Robust.Shared.Containers; using Robust.Shared.Containers;
@@ -48,6 +49,7 @@ public abstract partial class SharedMechSystem : EntitySystem
SubscribeLocalEvent<MechComponent, UserActivateInWorldEvent>(RelayInteractionEvent); SubscribeLocalEvent<MechComponent, UserActivateInWorldEvent>(RelayInteractionEvent);
SubscribeLocalEvent<MechComponent, ComponentStartup>(OnStartup); SubscribeLocalEvent<MechComponent, ComponentStartup>(OnStartup);
SubscribeLocalEvent<MechComponent, DestructionEventArgs>(OnDestruction); SubscribeLocalEvent<MechComponent, DestructionEventArgs>(OnDestruction);
SubscribeLocalEvent<MechComponent, EntityStorageIntoContainerAttemptEvent>(OnEntityStorageDump);
SubscribeLocalEvent<MechComponent, GetAdditionalAccessEvent>(OnGetAdditionalAccess); SubscribeLocalEvent<MechComponent, GetAdditionalAccessEvent>(OnGetAdditionalAccess);
SubscribeLocalEvent<MechComponent, DragDropTargetEvent>(OnDragDrop); SubscribeLocalEvent<MechComponent, DragDropTargetEvent>(OnDragDrop);
SubscribeLocalEvent<MechComponent, CanDropTargetEvent>(OnCanDragDrop); SubscribeLocalEvent<MechComponent, CanDropTargetEvent>(OnCanDragDrop);
@@ -104,6 +106,12 @@ public abstract partial class SharedMechSystem : EntitySystem
BreakMech(uid, component); 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) private void OnGetAdditionalAccess(EntityUid uid, MechComponent component, ref GetAdditionalAccessEvent args)
{ {
var pilot = component.PilotSlot.ContainedEntity; var pilot = component.PilotSlot.ContainedEntity;
@@ -147,7 +155,7 @@ public abstract partial class SharedMechSystem : EntitySystem
} }
/// <summary> /// <summary>
/// Destroys the mech, removing the user and ejecting all installed equipment. /// Destroys the mech, removing the user and ejecting anything contained.
/// </summary> /// </summary>
/// <param name="uid"></param> /// <param name="uid"></param>
/// <param name="component"></param> /// <param name="component"></param>
@@ -237,14 +245,19 @@ public abstract partial class SharedMechSystem : EntitySystem
/// <param name="toRemove"></param> /// <param name="toRemove"></param>
/// <param name="component"></param> /// <param name="component"></param>
/// <param name="equipmentComponent"></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, public void RemoveEquipment(EntityUid uid, EntityUid toRemove, MechComponent? component = null,
MechEquipmentComponent? equipmentComponent = null, bool forced = false) MechEquipmentComponent? equipmentComponent = null, bool forced = false)
{ {
if (!Resolve(uid, ref component)) if (!Resolve(uid, ref component))
return; 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; return;
if (!forced) if (!forced)
@@ -261,7 +274,9 @@ public abstract partial class SharedMechSystem : EntitySystem
if (component.CurrentSelectedEquipment == toRemove) if (component.CurrentSelectedEquipment == toRemove)
CycleEquipment(uid, component); CycleEquipment(uid, component);
equipmentComponent.EquipmentOwner = null; if (forced && equipmentComponent != null)
equipmentComponent.EquipmentOwner = null;
_container.Remove(toRemove, component.EquipmentContainer); _container.Remove(toRemove, component.EquipmentContainer);
UpdateUserInterface(uid, component); UpdateUserInterface(uid, component);
} }

View File

@@ -166,6 +166,13 @@ public record struct InsertIntoEntityStorageAttemptEvent(EntityUid ItemToInsert,
[ByRefEvent] [ByRefEvent]
public record struct EntityStorageInsertedIntoAttemptEvent(EntityUid ItemToInsert, bool Cancelled = false); 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] [ByRefEvent]
public record struct StorageOpenAttemptEvent(EntityUid User, bool Silent, bool Cancelled = false); public record struct StorageOpenAttemptEvent(EntityUid User, bool Silent, bool Cancelled = false);

View File

@@ -305,10 +305,13 @@ public abstract class SharedEntityStorageSystem : EntitySystem
_container.Remove(toRemove, component.Contents); _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); _container.Insert(toRemove, outerContainer);
return true; return true;