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.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

View 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);
if (forced && equipmentComponent != null)
equipmentComponent.EquipmentOwner = null;
_container.Remove(toRemove, component.EquipmentContainer);
UpdateUserInterface(uid, component);
}

View 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);

View 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;