diff --git a/Robust.Server/Containers/ContainerSystem.cs b/Robust.Server/Containers/ContainerSystem.cs index d298fc2b481..4122bd7369d 100644 --- a/Robust.Server/Containers/ContainerSystem.cs +++ b/Robust.Server/Containers/ContainerSystem.cs @@ -1,15 +1,13 @@ using Robust.Shared.Containers; using Robust.Shared.GameObjects; -using Robust.Shared.Log; -namespace Robust.Server.Containers +namespace Robust.Server.Containers; + +public sealed class ContainerSystem : SharedContainerSystem { - public sealed class ContainerSystem : SharedContainerSystem + protected override void ValidateMissingEntity(EntityUid uid, BaseContainer cont, EntityUid missing) { - protected override void ValidateMissingEntity(EntityUid uid, BaseContainer cont, EntityUid missing) - { - Log.Error($"Missing entity for container {ToPrettyString(uid)}. Missing uid: {missing}"); - //cont.InternalRemove(ent); - } + Log.Error($"Missing entity for container {ToPrettyString(uid)}. Missing uid: {missing}"); + //cont.InternalRemove(ent); } } diff --git a/Robust.Shared/Containers/SharedContainerSystem.Validation.cs b/Robust.Shared/Containers/SharedContainerSystem.Validation.cs index 1993c8209a6..bd00f1f90b4 100644 --- a/Robust.Shared/Containers/SharedContainerSystem.Validation.cs +++ b/Robust.Shared/Containers/SharedContainerSystem.Validation.cs @@ -4,7 +4,6 @@ namespace Robust.Shared.Containers; - // This partial class just exists for debug asserts and bug fixing public abstract partial class SharedContainerSystem : EntitySystem { @@ -65,5 +64,4 @@ private void ValidateChildren(TransformComponent xform, EntityQuery _managerQuery; - private EntityQuery _gridQuery; - private EntityQuery _mapQuery; - protected EntityQuery MetaQuery; - protected EntityQuery PhysicsQuery; - protected EntityQuery JointQuery; - protected EntityQuery TransformQuery; - - /// - public override void Initialize() - { - base.Initialize(); - - SubscribeLocalEvent(OnParentChanged); - SubscribeLocalEvent(OnInit); - SubscribeLocalEvent(OnStartupValidation); - SubscribeLocalEvent(OnContainerGetState); - SubscribeLocalEvent(OnContainerManagerRemove); - - _managerQuery = GetEntityQuery(); - _gridQuery = GetEntityQuery(); - _mapQuery = GetEntityQuery(); - MetaQuery = GetEntityQuery(); - PhysicsQuery = GetEntityQuery(); - JointQuery = GetEntityQuery(); - TransformQuery = GetEntityQuery(); - } + [Dependency] private IDynamicTypeFactoryInternal _dynFactory = default!; + [Dependency] private INetManager _net = default!; + [Dependency] private SharedPhysicsSystem _physics = default!; + [Dependency] private EntityLookupSystem _lookup = default!; + [Dependency] private SharedTransformSystem _transform = default!; + [Dependency] private SharedJointSystem _joint = default!; + [Dependency] private IGameTiming _timing = default!; + + [Dependency] private EntityQuery _managerQuery = default!; + [Dependency] private EntityQuery _gridQuery = default!; + [Dependency] private EntityQuery _mapQuery = default!; + [Dependency] protected EntityQuery MetaQuery = default!; + [Dependency] protected EntityQuery PhysicsQuery = default!; + [Dependency] protected EntityQuery JointQuery = default!; + [Dependency] protected EntityQuery TransformQuery = default!; + + /// + public override void Initialize() + { + base.Initialize(); - private void OnInit(Entity ent, ref ComponentInit args) + SubscribeLocalEvent(OnParentChanged); + SubscribeLocalEvent(OnInit); + SubscribeLocalEvent(OnStartupValidation); + SubscribeLocalEvent(OnContainerGetState); + SubscribeLocalEvent(OnContainerManagerRemove); + } + + private void OnInit(Entity ent, ref ComponentInit args) + { + foreach (var (id, container) in ent.Comp.Containers) { - foreach (var (id, container) in ent.Comp.Containers) - { - container.Init(this, id, ent); - } + container.Init(this, id, ent); } + } - private void OnContainerGetState(EntityUid uid, ContainerManagerComponent component, ref ComponentGetState args) + private void OnContainerGetState(EntityUid uid, ContainerManagerComponent component, ref ComponentGetState args) + { + Dictionary containerSet = + new(component.Containers.Count); + + foreach (var container in component.Containers.Values) { - Dictionary containerSet = - new(component.Containers.Count); + var uidArr = new NetEntity[container.ContainedEntities.Count]; - foreach (var container in component.Containers.Values) + for (var index = 0; index < container.ContainedEntities.Count; index++) { - var uidArr = new NetEntity[container.ContainedEntities.Count]; - - for (var index = 0; index < container.ContainedEntities.Count; index++) - { - uidArr[index] = GetNetEntity(container.ContainedEntities[index]); - } - - var sContainer = - new ContainerManagerComponent.ContainerManagerComponentState.ContainerData(container.GetType().Name, - container.ShowContents, - container.OccludesLight, - uidArr); - containerSet.Add(container.ID, sContainer); + uidArr[index] = GetNetEntity(container.ContainedEntities[index]); } - args.State = new ContainerManagerComponent.ContainerManagerComponentState(containerSet); + var sContainer = + new ContainerManagerComponent.ContainerManagerComponentState.ContainerData(container.GetType().Name, + container.ShowContents, + container.OccludesLight, + uidArr); + containerSet.Add(container.ID, sContainer); } - private void OnContainerManagerRemove(EntityUid uid, ContainerManagerComponent component, ComponentRemove args) - { - foreach (var container in component.Containers.Values) - { - ShutdownContainer(container); - } + args.State = new ContainerManagerComponent.ContainerManagerComponentState(containerSet); + } - component.Containers.Clear(); + private void OnContainerManagerRemove(EntityUid uid, ContainerManagerComponent component, ComponentRemove args) + { + foreach (var container in component.Containers.Values) + { + ShutdownContainer(container); } - // TODO: Make ContainerManagerComponent ECS and make these proxy methods the real deal. + component.Containers.Clear(); + } - #region Proxy Methods + // TODO: Make ContainerManagerComponent ECS and make these proxy methods the real deal. - public T MakeContainer(EntityUid uid, string id, ContainerManagerComponent? containerManager = null) - where T : BaseContainer - { - if (!Resolve(uid, ref containerManager, false)) - containerManager = AddComp(uid); // Happy Vera. + #region Proxy Methods - if (HasContainer(uid, id, containerManager)) - throw new ArgumentException($"Container with specified ID already exists: '{id}'"); + public T MakeContainer(EntityUid uid, string id, ContainerManagerComponent? containerManager = null) + where T : BaseContainer + { + if (!Resolve(uid, ref containerManager, false)) + containerManager = AddComp(uid); // Happy Vera. - var container = _dynFactory.CreateInstanceUnchecked(typeof(T), inject: false); - container.Init(this, id, (uid, containerManager)); - containerManager.Containers[id] = container; - Dirty(uid, containerManager); - return container; - } + if (HasContainer(uid, id, containerManager)) + throw new ArgumentException($"Container with specified ID already exists: '{id}'"); - public virtual void ShutdownContainer(BaseContainer container) - { - container.InternalShutdown(EntityManager, this, _net.IsClient); - container.Manager.Containers.Remove(container.ID); - container.ExpectedEntities.Clear(); - } + var container = _dynFactory.CreateInstanceUnchecked(typeof(T), inject: false); + container.Init(this, id, (uid, containerManager)); + containerManager.Containers[id] = container; + Dirty(uid, containerManager); + return container; + } - public T EnsureContainer( - EntityUid uid, - string id, - out bool alreadyExisted, - ContainerManagerComponent? containerManager = null) - where T : BaseContainer - { - if (!Resolve(uid, ref containerManager, false)) - containerManager = AddComp(uid); + public virtual void ShutdownContainer(BaseContainer container) + { + container.InternalShutdown(EntityManager, this, _net.IsClient); + container.Manager.Containers.Remove(container.ID); + container.ExpectedEntities.Clear(); + } - if (TryGetContainer(uid, id, out var container, containerManager)) - { - alreadyExisted = true; - if (container is T cast) - return cast; + public T EnsureContainer( + EntityUid uid, + string id, + out bool alreadyExisted, + ContainerManagerComponent? containerManager = null) + where T : BaseContainer + { + if (!Resolve(uid, ref containerManager, false)) + containerManager = AddComp(uid); - throw new InvalidOperationException( - $"The container exists but is of a different type: {container.GetType()}"); - } + if (TryGetContainer(uid, id, out var container, containerManager)) + { + alreadyExisted = true; + if (container is T cast) + return cast; - alreadyExisted = false; - return MakeContainer(uid, id, containerManager); + throw new InvalidOperationException( + $"The container exists but is of a different type: {container.GetType()}"); } - public T EnsureContainer(EntityUid uid, string id, ContainerManagerComponent? containerManager = null) - where T : BaseContainer - { - return EnsureContainer(uid, id, out _, containerManager); - } + alreadyExisted = false; + return MakeContainer(uid, id, containerManager); + } - public BaseContainer GetContainer(EntityUid uid, string id, ContainerManagerComponent? containerManager = null) - { - if (!Resolve(uid, ref containerManager)) - throw new ArgumentException("Entity does not have a ContainerManagerComponent!", nameof(uid)); + public T EnsureContainer(EntityUid uid, string id, ContainerManagerComponent? containerManager = null) + where T : BaseContainer + { + return EnsureContainer(uid, id, out _, containerManager); + } - return containerManager.Containers[id]; - } + public BaseContainer GetContainer(EntityUid uid, string id, ContainerManagerComponent? containerManager = null) + { + if (!Resolve(uid, ref containerManager)) + throw new ArgumentException("Entity does not have a ContainerManagerComponent!", nameof(uid)); - public bool HasContainer(EntityUid uid, string id, ContainerManagerComponent? containerManager) - { - if (!Resolve(uid, ref containerManager, false)) - return false; + return containerManager.Containers[id]; + } - return containerManager.Containers.ContainsKey(id); - } + public bool HasContainer(EntityUid uid, string id, ContainerManagerComponent? containerManager) + { + if (!Resolve(uid, ref containerManager, false)) + return false; - public bool TryGetContainer( - EntityUid uid, - string id, - [NotNullWhen(true)] out BaseContainer? container, - ContainerManagerComponent? containerManager = null) + return containerManager.Containers.ContainsKey(id); + } + + public bool TryGetContainer( + EntityUid uid, + string id, + [NotNullWhen(true)] out BaseContainer? container, + ContainerManagerComponent? containerManager = null) + { + if (!Resolve(uid, ref containerManager, false)) { - if (!Resolve(uid, ref containerManager, false)) - { - container = null; - return false; - } + container = null; + return false; + } - if (!containerManager.Containers.TryGetValue(id, out container)) - return false; + if (!containerManager.Containers.TryGetValue(id, out container)) + return false; - DebugTools.AssertEqual(container.ID, id); - DebugTools.AssertNotNull(container.Manager); - DebugTools.AssertNotEqual(container.Owner, EntityUid.Invalid); - return true; + DebugTools.AssertEqual(container.ID, id); + DebugTools.AssertNotNull(container.Manager); + DebugTools.AssertNotEqual(container.Owner, EntityUid.Invalid); + return true; + } + + public bool TryGetContainingContainer( + EntityUid uid, + EntityUid containedUid, + [NotNullWhen(true)] out BaseContainer? container, + ContainerManagerComponent? containerManager = null) + { + DebugTools.Assert(Exists(containedUid)); + if (!Resolve(uid, ref containerManager, false)) + { + container = null; + return false; } - public bool TryGetContainingContainer( - EntityUid uid, - EntityUid containedUid, - [NotNullWhen(true)] out BaseContainer? container, - ContainerManagerComponent? containerManager = null) + foreach (var contain in containerManager.Containers.Values) { - DebugTools.Assert(Exists(containedUid)); - if (!Resolve(uid, ref containerManager, false)) + if (contain.Contains(containedUid)) { - container = null; - return false; + container = contain; + return true; } + } - foreach (var contain in containerManager.Containers.Values) - { - if (contain.Contains(containedUid)) - { - container = contain; - return true; - } - } + container = default; + return false; + } - container = default; + public bool ContainsEntity( + EntityUid uid, + EntityUid containedUid, + ContainerManagerComponent? containerManager = null) + { + DebugTools.Assert(Exists(containedUid)); + if (!Resolve(uid, ref containerManager, false)) return false; - } - public bool ContainsEntity( - EntityUid uid, - EntityUid containedUid, - ContainerManagerComponent? containerManager = null) + foreach (var container in containerManager.Containers.Values) { - DebugTools.Assert(Exists(containedUid)); - if (!Resolve(uid, ref containerManager, false)) - return false; + if (container.Contains(containedUid)) + return true; + } - foreach (var container in containerManager.Containers.Values) - { - if (container.Contains(containedUid)) - return true; - } + return false; + } + public bool RemoveEntity( + EntityUid uid, + EntityUid toremove, + ContainerManagerComponent? containerManager = null, + TransformComponent? containedXform = null, + MetaDataComponent? containedMeta = null, + bool reparent = true, + bool force = false, + EntityCoordinates? destination = null, + Angle? localRotation = null) + { + if (!Resolve(uid, ref containerManager) || !Resolve(toremove, ref containedMeta, ref containedXform)) return false; - } - public bool RemoveEntity( - EntityUid uid, - EntityUid toremove, - ContainerManagerComponent? containerManager = null, - TransformComponent? containedXform = null, - MetaDataComponent? containedMeta = null, - bool reparent = true, - bool force = false, - EntityCoordinates? destination = null, - Angle? localRotation = null) + foreach (var containers in containerManager.Containers.Values) { - if (!Resolve(uid, ref containerManager) || !Resolve(toremove, ref containedMeta, ref containedXform)) - return false; - - foreach (var containers in containerManager.Containers.Values) - { - if (containers.Contains(toremove)) - return Remove((toremove, containedXform, containedMeta), - containers, - reparent, - force, - destination, - localRotation); - } - - return true; // If we don't contain the entity, it will always be removed + if (containers.Contains(toremove)) + return Remove((toremove, containedXform, containedMeta), + containers, + reparent, + force, + destination, + localRotation); } - public ContainerManagerComponent.AllContainersEnumerable GetAllContainers( - EntityUid uid, - ContainerManagerComponent? containerManager = null) - { - if (!Resolve(uid, ref containerManager)) - return new ContainerManagerComponent.AllContainersEnumerable(); + return true; // If we don't contain the entity, it will always be removed + } - return new(containerManager); - } + public ContainerManagerComponent.AllContainersEnumerable GetAllContainers( + EntityUid uid, + ContainerManagerComponent? containerManager = null) + { + if (!Resolve(uid, ref containerManager)) + return new ContainerManagerComponent.AllContainersEnumerable(); - #endregion + return new(containerManager); + } - #region Container Helpers + #endregion - public bool TryGetContainingContainer( - Entity ent, - [NotNullWhen(true)] out BaseContainer? container) - { - container = null; + #region Container Helpers - if (!Resolve(ent, ref ent.Comp2, false)) - return false; + public bool TryGetContainingContainer( + Entity ent, + [NotNullWhen(true)] out BaseContainer? container) + { + container = null; - if ((ent.Comp2.Flags & MetaDataFlags.InContainer) == MetaDataFlags.None) - return false; + if (!Resolve(ent, ref ent.Comp2, false)) + return false; - if (!Resolve(ent, ref ent.Comp1, false)) - return false; + if ((ent.Comp2.Flags & MetaDataFlags.InContainer) == MetaDataFlags.None) + return false; - return TryGetContainingContainer(ent.Comp1.ParentUid, ent, out container); - } + if (!Resolve(ent, ref ent.Comp1, false)) + return false; - /// - /// Checks whether the given entity is inside of a container. This will only check if this entity's direct - /// parent is containing it. To recursively if the entity, or any parent, is inside a container, use - /// - /// If the entity is inside of a container. - public bool IsEntityInContainer(EntityUid uid, MetaDataComponent? meta = null) - { - if (!Resolve(uid, ref meta, false)) - return false; + return TryGetContainingContainer(ent.Comp1.ParentUid, ent, out container); + } - return (meta.Flags & MetaDataFlags.InContainer) == MetaDataFlags.InContainer; - } + /// + /// Checks whether the given entity is inside of a container. This will only check if this entity's direct + /// parent is containing it. To recursively if the entity, or any parent, is inside a container, use + /// + /// If the entity is inside of a container. + public bool IsEntityInContainer(EntityUid uid, MetaDataComponent? meta = null) + { + if (!Resolve(uid, ref meta, false)) + return false; - /// - /// Recursively check if the entity or any parent is inside of a container. - /// - /// If the entity is inside of a container. - public bool IsEntityOrParentInContainer( - EntityUid uid, - MetaDataComponent? meta = null, - TransformComponent? xform = null) - { - if (!MetaQuery.Resolve(uid, ref meta)) - return false; + return (meta.Flags & MetaDataFlags.InContainer) == MetaDataFlags.InContainer; + } - if ((meta.Flags & MetaDataFlags.InContainer) == MetaDataFlags.InContainer) - return true; + /// + /// Recursively check if the entity or any parent is inside of a container. + /// + /// If the entity is inside of a container. + public bool IsEntityOrParentInContainer( + EntityUid uid, + MetaDataComponent? meta = null, + TransformComponent? xform = null) + { + if (!MetaQuery.Resolve(uid, ref meta)) + return false; - if (!TransformQuery.Resolve(uid, ref xform)) - return false; + if ((meta.Flags & MetaDataFlags.InContainer) == MetaDataFlags.InContainer) + return true; - if (!xform.ParentUid.Valid) - return false; + if (!TransformQuery.Resolve(uid, ref xform)) + return false; - return IsEntityOrParentInContainer(xform.ParentUid); - } + if (!xform.ParentUid.Valid) + return false; - /// - /// Finds the first instance of a component on the recursive parented containers that hold an entity - /// - public bool TryFindComponentOnEntityContainerOrParent( - EntityUid uid, - EntityQuery entityQuery, - [NotNullWhen(true)] ref T? foundComponent, - MetaDataComponent? meta = null, - TransformComponent? xform = null) where T : IComponent - { - if (!MetaQuery.Resolve(uid, ref meta)) - return false; + return IsEntityOrParentInContainer(xform.ParentUid); + } - if ((meta.Flags & MetaDataFlags.InContainer) != MetaDataFlags.InContainer) - return false; + /// + /// Finds the first instance of a component on the recursive parented containers that hold an entity + /// + public bool TryFindComponentOnEntityContainerOrParent( + EntityUid uid, + EntityQuery entityQuery, + [NotNullWhen(true)] ref T? foundComponent, + MetaDataComponent? meta = null, + TransformComponent? xform = null) where T : IComponent + { + if (!MetaQuery.Resolve(uid, ref meta)) + return false; - if (!TransformQuery.Resolve(uid, ref xform)) - return false; + if ((meta.Flags & MetaDataFlags.InContainer) != MetaDataFlags.InContainer) + return false; - if (!xform.ParentUid.Valid) - return false; + if (!TransformQuery.Resolve(uid, ref xform)) + return false; - if (entityQuery.TryComp(xform.ParentUid, out foundComponent)) - return true; + if (!xform.ParentUid.Valid) + return false; - return TryFindComponentOnEntityContainerOrParent(xform.ParentUid, entityQuery, ref foundComponent); - } + if (entityQuery.TryComp(xform.ParentUid, out foundComponent)) + return true; - /// - /// Finds all instances of a component on the recursive parented containers that hold an entity - /// - public bool TryFindComponentsOnEntityContainerOrParent( - EntityUid uid, - EntityQuery entityQuery, - List foundComponents, - MetaDataComponent? meta = null, - TransformComponent? xform = null) where T : IComponent - { - if (!MetaQuery.Resolve(uid, ref meta)) - return foundComponents.Any(); + return TryFindComponentOnEntityContainerOrParent(xform.ParentUid, entityQuery, ref foundComponent); + } - if ((meta.Flags & MetaDataFlags.InContainer) != MetaDataFlags.InContainer) - return foundComponents.Any(); + /// + /// Finds all instances of a component on the recursive parented containers that hold an entity + /// + public bool TryFindComponentsOnEntityContainerOrParent( + EntityUid uid, + EntityQuery entityQuery, + List foundComponents, + MetaDataComponent? meta = null, + TransformComponent? xform = null) where T : IComponent + { + if (!MetaQuery.Resolve(uid, ref meta)) + return foundComponents.Any(); - if (!TransformQuery.Resolve(uid, ref xform)) - return foundComponents.Any(); + if ((meta.Flags & MetaDataFlags.InContainer) != MetaDataFlags.InContainer) + return foundComponents.Any(); - if (!xform.ParentUid.Valid) - return foundComponents.Any(); + if (!TransformQuery.Resolve(uid, ref xform)) + return foundComponents.Any(); - if (TryComp(xform.ParentUid, out T? foundComponent)) - foundComponents.Add(foundComponent); + if (!xform.ParentUid.Valid) + return foundComponents.Any(); - return TryFindComponentsOnEntityContainerOrParent(xform.ParentUid, entityQuery, foundComponents); - } + if (TryComp(xform.ParentUid, out T? foundComponent)) + foundComponents.Add(foundComponent); - /// - /// Returns true if the two entities are not contained, or are contained in the same container. - /// - public bool IsInSameOrNoContainer( - Entity user, - Entity other) - { - var isUserContained = TryGetContainingContainer(user, out var userContainer); - var isOtherContained = TryGetContainingContainer(other, out var otherContainer); + return TryFindComponentsOnEntityContainerOrParent(xform.ParentUid, entityQuery, foundComponents); + } - // Both entities are not in a container - if (!isUserContained && !isOtherContained) return true; + /// + /// Returns true if the two entities are not contained, or are contained in the same container. + /// + public bool IsInSameOrNoContainer( + Entity user, + Entity other) + { + var isUserContained = TryGetContainingContainer(user, out var userContainer); + var isOtherContained = TryGetContainingContainer(other, out var otherContainer); - // Both entities are in different contained states - if (isUserContained != isOtherContained) return false; + // Both entities are not in a container + if (!isUserContained && !isOtherContained) return true; - // Both entities are in the same container - return userContainer == otherContainer; - } + // Both entities are in different contained states + if (isUserContained != isOtherContained) return false; - /// - /// Returns true if the two entities are not contained, or are contained in the same container, or if one - /// entity contains the other (i.e., is the parent). - /// - public bool IsInSameOrParentContainer( - Entity user, - Entity other) - { - return IsInSameOrParentContainer(user, other, out _, out _); - } + // Both entities are in the same container + return userContainer == otherContainer; + } - /// - public bool IsInSameOrParentContainer( - Entity user, - Entity other, - out BaseContainer? userContainer, - out BaseContainer? otherContainer) - { - var isUserContained = TryGetContainingContainer(user, out userContainer); - var isOtherContained = TryGetContainingContainer(other, out otherContainer); + /// + /// Returns true if the two entities are not contained, or are contained in the same container, or if one + /// entity contains the other (i.e., is the parent). + /// + public bool IsInSameOrParentContainer( + Entity user, + Entity other) + { + return IsInSameOrParentContainer(user, other, out _, out _); + } - // Both entities are not in a container - if (!isUserContained && !isOtherContained) return true; + /// + public bool IsInSameOrParentContainer( + Entity user, + Entity other, + out BaseContainer? userContainer, + out BaseContainer? otherContainer) + { + var isUserContained = TryGetContainingContainer(user, out userContainer); + var isOtherContained = TryGetContainingContainer(other, out otherContainer); - // One contains the other - if (userContainer?.Owner == other || otherContainer?.Owner == user) return true; + // Both entities are not in a container + if (!isUserContained && !isOtherContained) return true; - // Both entities are in different contained states - if (isUserContained != isOtherContained) return false; + // One contains the other + if (userContainer?.Owner == other || otherContainer?.Owner == user) return true; - // Both entities are in the same container - return userContainer == otherContainer; - } + // Both entities are in different contained states + if (isUserContained != isOtherContained) return false; - /// - /// Check whether a given entity can see another entity despite whatever containers they may be in. - /// - /// - /// This is effectively a variant of that also checks whether the - /// containers are transparent. Additionally, an entity can "see" the entity that contains it, but unless - /// otherwise specified the containing entity cannot see into itself. For example, a human in a locker can - /// see the locker and other items in that locker, but the human cannot see their own organs. Note that - /// this means that the two entity arguments are NOT interchangeable. - /// - public bool IsInSameOrTransparentContainer( - Entity user, - Entity other, - BaseContainer? userContainer = null, - BaseContainer? otherContainer = null, - bool userSeeInsideSelf = false) - { - if (userContainer == null) - TryGetContainingContainer(user, out userContainer); + // Both entities are in the same container + return userContainer == otherContainer; + } - if (otherContainer == null) - TryGetContainingContainer(other, out otherContainer); + /// + /// Check whether a given entity can see another entity despite whatever containers they may be in. + /// + /// + /// This is effectively a variant of that also checks whether the + /// containers are transparent. Additionally, an entity can "see" the entity that contains it, but unless + /// otherwise specified the containing entity cannot see into itself. For example, a human in a locker can + /// see the locker and other items in that locker, but the human cannot see their own organs. Note that + /// this means that the two entity arguments are NOT interchangeable. + /// + public bool IsInSameOrTransparentContainer( + Entity user, + Entity other, + BaseContainer? userContainer = null, + BaseContainer? otherContainer = null, + bool userSeeInsideSelf = false) + { + if (userContainer == null) + TryGetContainingContainer(user, out userContainer); - // Are both entities in the same container (or none)? - if (userContainer == otherContainer) return true; + if (otherContainer == null) + TryGetContainingContainer(other, out otherContainer); - // Is the user contained in the other entity? - if (userContainer?.Owner == other) return true; + // Are both entities in the same container (or none)? + if (userContainer == otherContainer) return true; - // Does the user contain the other and can they see through themselves? - if (userSeeInsideSelf && otherContainer?.Owner == user) return true; + // Is the user contained in the other entity? + if (userContainer?.Owner == other) return true; - // Next we check for see-through containers. This uses some recursion, but it should be fine unless people - // start spawning in glass matryoshka dolls. + // Does the user contain the other and can they see through themselves? + if (userSeeInsideSelf && otherContainer?.Owner == user) return true; - // Is the user in a see-through container? - if (userContainer?.ShowContents ?? false) - return IsInSameOrTransparentContainer((userContainer.Owner, null, null), other, otherContainer: otherContainer); + // Next we check for see-through containers. This uses some recursion, but it should be fine unless people + // start spawning in glass matryoshka dolls. - // Is the other entity in a see-through container? - if (otherContainer?.ShowContents ?? false) - return IsInSameOrTransparentContainer(user, (otherContainer.Owner, null, null), userContainer: userContainer, userSeeInsideSelf: userSeeInsideSelf); + // Is the user in a see-through container? + if (userContainer?.ShowContents ?? false) + return IsInSameOrTransparentContainer((userContainer.Owner, null, null), other, otherContainer: otherContainer); - return false; - } + // Is the other entity in a see-through container? + if (otherContainer?.ShowContents ?? false) + return IsInSameOrTransparentContainer(user, (otherContainer.Owner, null, null), userContainer: userContainer, userSeeInsideSelf: userSeeInsideSelf); - /// - /// Returns the full chain of containers containing the entity passed in, from innermost to outermost. - /// - /// - /// The resulting collection includes the container directly containing the entity (if any), - /// the container containing that container, and so on until reaching the outermost container. - /// - public IEnumerable GetContainingContainers(Entity ent) - { - if (!ent.Owner.IsValid()) - yield break; + return false; + } + + /// + /// Returns the full chain of containers containing the entity passed in, from innermost to outermost. + /// + /// + /// The resulting collection includes the container directly containing the entity (if any), + /// the container containing that container, and so on until reaching the outermost container. + /// + public IEnumerable GetContainingContainers(Entity ent) + { + if (!ent.Owner.IsValid()) + yield break; - if (!Resolve(ent, ref ent.Comp)) - yield break; + if (!Resolve(ent, ref ent.Comp)) + yield break; - var child = ent.Owner; - var parent = ent.Comp.ParentUid; + var child = ent.Owner; + var parent = ent.Comp.ParentUid; - while (parent.IsValid()) + while (parent.IsValid()) + { + if (((MetaQuery.GetComponent(child).Flags & MetaDataFlags.InContainer) == MetaDataFlags.InContainer) && + _managerQuery.TryGetComponent(parent, out var conManager) && + TryGetContainingContainer(parent, child, out var parentContainer, conManager)) { - if (((MetaQuery.GetComponent(child).Flags & MetaDataFlags.InContainer) == MetaDataFlags.InContainer) && - _managerQuery.TryGetComponent(parent, out var conManager) && - TryGetContainingContainer(parent, child, out var parentContainer, conManager)) - { - yield return parentContainer; - } - - var parentXform = TransformQuery.GetComponent(parent); - child = parent; - parent = parentXform.ParentUid; + yield return parentContainer; } - } - /// - /// Gets the top-most container in the hierarchy for this entity, if it exists. - /// - public bool TryGetOuterContainer(EntityUid uid, TransformComponent xform, [NotNullWhen(true)] out BaseContainer? container) - { - return TryGetOuterContainer(uid, xform, out container, TransformQuery); + var parentXform = TransformQuery.GetComponent(parent); + child = parent; + parent = parentXform.ParentUid; } + } - public bool TryGetOuterContainer(EntityUid uid, TransformComponent xform, - [NotNullWhen(true)] out BaseContainer? container, EntityQuery xformQuery) - { - container = null; + /// + /// Gets the top-most container in the hierarchy for this entity, if it exists. + /// + public bool TryGetOuterContainer(EntityUid uid, TransformComponent xform, [NotNullWhen(true)] out BaseContainer? container) + { + return TryGetOuterContainer(uid, xform, out container, TransformQuery); + } + + public bool TryGetOuterContainer(EntityUid uid, TransformComponent xform, + [NotNullWhen(true)] out BaseContainer? container, EntityQuery xformQuery) + { + container = null; - if (!uid.IsValid()) - return false; + if (!uid.IsValid()) + return false; - var child = uid; - var parent = xform.ParentUid; + var child = uid; + var parent = xform.ParentUid; - while (parent.IsValid()) + while (parent.IsValid()) + { + if (((MetaQuery.GetComponent(child).Flags & MetaDataFlags.InContainer) == MetaDataFlags.InContainer) && + _managerQuery.TryGetComponent(parent, out var conManager) && + TryGetContainingContainer(parent, child, out var parentContainer, conManager)) { - if (((MetaQuery.GetComponent(child).Flags & MetaDataFlags.InContainer) == MetaDataFlags.InContainer) && - _managerQuery.TryGetComponent(parent, out var conManager) && - TryGetContainingContainer(parent, child, out var parentContainer, conManager)) - { - container = parentContainer; - } - - var parentXform = xformQuery.GetComponent(parent); - child = parent; - parent = parentXform.ParentUid; + container = parentContainer; } - return container != null; + var parentXform = xformQuery.GetComponent(parent); + child = parent; + parent = parentXform.ParentUid; } - /// - /// Attempts to remove an entity from its container, if any. - /// - /// Entity that might be inside a container. - /// Whether to forcibly remove the entity from the container. - /// Whether the entity was actually inside a container or not. - /// If the entity could be removed. Also returns false if it wasn't inside a container. - public bool TryRemoveFromContainer(Entity entity, bool force, out bool wasInContainer) - { - DebugTools.Assert(Exists(entity)); + return container != null; + } - if (TryGetContainingContainer(entity, out var container)) - { - wasInContainer = true; + /// + /// Attempts to remove an entity from its container, if any. + /// + /// Entity that might be inside a container. + /// Whether to forcibly remove the entity from the container. + /// Whether the entity was actually inside a container or not. + /// If the entity could be removed. Also returns false if it wasn't inside a container. + public bool TryRemoveFromContainer(Entity entity, bool force, out bool wasInContainer) + { + DebugTools.Assert(Exists(entity)); - if (!force) - return Remove(entity, container); + if (TryGetContainingContainer(entity, out var container)) + { + wasInContainer = true; - Remove(entity, container, force: true); - return true; - } + if (!force) + return Remove(entity, container); - wasInContainer = false; - return false; + Remove(entity, container, force: true); + return true; } - /// - /// Attempts to remove an entity from its container, if any. - /// - /// Entity that might be inside a container. - /// Whether to forcibly remove the entity from the container. - /// If the entity could be removed. Also returns false if it wasn't inside a container. - public bool TryRemoveFromContainer(Entity entity, bool force = false) - { - return TryRemoveFromContainer(entity, force, out _); - } + wasInContainer = false; + return false; + } - /// - /// Attempts to remove all entities in a container. Returns removed entities. - /// - public List EmptyContainer( - BaseContainer container, - bool force = false, - EntityCoordinates? destination = null, - bool reparent = true) - { - var removed = new List(container.ContainedEntities); - for (var i = removed.Count - 1; i >= 0; i--) - { - if (Remove(removed[i], container, reparent: reparent, force: force, destination: destination)) - continue; + /// + /// Attempts to remove an entity from its container, if any. + /// + /// Entity that might be inside a container. + /// Whether to forcibly remove the entity from the container. + /// If the entity could be removed. Also returns false if it wasn't inside a container. + public bool TryRemoveFromContainer(Entity entity, bool force = false) + { + return TryRemoveFromContainer(entity, force, out _); + } - // failed to remove entity. - DebugTools.Assert(container.Contains(removed[i])); - removed.RemoveSwap(i); - } + /// + /// Attempts to remove all entities in a container. Returns removed entities. + /// + public List EmptyContainer( + BaseContainer container, + bool force = false, + EntityCoordinates? destination = null, + bool reparent = true) + { + var removed = new List(container.ContainedEntities); + for (var i = removed.Count - 1; i >= 0; i--) + { + if (Remove(removed[i], container, reparent: reparent, force: force, destination: destination)) + continue; - return removed; + // failed to remove entity. + DebugTools.Assert(container.Contains(removed[i])); + removed.RemoveSwap(i); } - /// - /// Attempts to remove and delete all entities in a container. - /// - public void CleanContainer(BaseContainer container) + return removed; + } + + /// + /// Attempts to remove and delete all entities in a container. + /// + public void CleanContainer(BaseContainer container) + { + foreach (var ent in container.ContainedEntities.ToArray()) { - foreach (var ent in container.ContainedEntities.ToArray()) - { - if (Deleted(ent)) - continue; + if (Deleted(ent)) + continue; - Remove(ent, container, force: true); - PredictedDel(ent); - } + Remove(ent, container, force: true); + PredictedDel(ent); } + } - public void AttachParentToContainerOrGrid(Entity transform) + public void AttachParentToContainerOrGrid(Entity transform) + { + // TODO make this check upwards for any container, and parent to that. + // Currently this just checks the direct parent, so entities will still teleport through containers. + if (!transform.Comp.ParentUid.IsValid() + || !TryGetContainingContainer((transform.Comp.ParentUid, Transform(transform.Comp.ParentUid)), out var container) + || !TryInsertIntoContainer(transform, container)) { - // TODO make this check upwards for any container, and parent to that. - // Currently this just checks the direct parent, so entities will still teleport through containers. - if (!transform.Comp.ParentUid.IsValid() - || !TryGetContainingContainer((transform.Comp.ParentUid, Transform(transform.Comp.ParentUid)), out var container) - || !TryInsertIntoContainer(transform, container)) - { - _transform.AttachToGridOrMap(transform, transform.Comp); - } + _transform.AttachToGridOrMap(transform, transform.Comp); } + } - private bool TryInsertIntoContainer(Entity transform, BaseContainer container) - { - if (Insert((transform.Owner, transform.Comp, null, null), container)) - return true; + private bool TryInsertIntoContainer(Entity transform, BaseContainer container) + { + if (Insert((transform.Owner, transform.Comp, null, null), container)) + return true; - var ownerXform = Transform(container.Owner); - if (ownerXform.ParentUid.IsValid() - && TryGetContainingContainer((container.Owner, ownerXform), out var newContainer)) - return TryInsertIntoContainer(transform, newContainer); + var ownerXform = Transform(container.Owner); + if (ownerXform.ParentUid.IsValid() + && TryGetContainingContainer((container.Owner, ownerXform), out var newContainer)) + return TryInsertIntoContainer(transform, newContainer); - return false; - } + return false; + } - internal bool TryGetManagerComp(EntityUid entity, [NotNullWhen(true)] out ContainerManagerComponent? manager) - { - DebugTools.Assert(Exists(entity)); + internal bool TryGetManagerComp(EntityUid entity, [NotNullWhen(true)] out ContainerManagerComponent? manager) + { + DebugTools.Assert(Exists(entity)); - if (TryComp(entity, out manager)) - return true; + if (TryComp(entity, out manager)) + return true; - // RECURSION ALERT - var transform = Transform(entity); - if (transform.ParentUid.IsValid()) - return TryGetManagerComp(transform.ParentUid, out manager); + // RECURSION ALERT + var transform = Transform(entity); + if (transform.ParentUid.IsValid()) + return TryGetManagerComp(transform.ParentUid, out manager); - return false; - } + return false; + } - #endregion + #endregion - protected virtual void OnParentChanged(ref EntParentChangedMessage message) - { - var meta = MetaData(message.Entity); - if ((meta.Flags & MetaDataFlags.InContainer) == 0) - return; + protected virtual void OnParentChanged(ref EntParentChangedMessage message) + { + var meta = MetaData(message.Entity); + if ((meta.Flags & MetaDataFlags.InContainer) == 0) + return; - // Eject entities from their parent container if the parent change is done via setting the transform. - if (TryComp(message.OldParent, out ContainerManagerComponent? containerManager)) - RemoveEntity(message.OldParent.Value, message.Entity, containerManager, message.Transform, meta, reparent: false, force: true); - } + // Eject entities from their parent container if the parent change is done via setting the transform. + if (TryComp(message.OldParent, out ContainerManagerComponent? containerManager)) + RemoveEntity(message.OldParent.Value, message.Entity, containerManager, message.Transform, meta, reparent: false, force: true); + } - [Conditional("DEBUG"), Access(typeof(BaseContainer))] - public void AssertInContainer(EntityUid uid, BaseContainer container) - { - if (_timing.ApplyingState) - return; // Entity might not yet have had its state updated. + [Conditional("DEBUG"), Access(typeof(BaseContainer))] + public void AssertInContainer(EntityUid uid, BaseContainer container) + { + if (_timing.ApplyingState) + return; // Entity might not yet have had its state updated. - var flags = MetaData(uid).Flags; - DebugTools.Assert((flags & MetaDataFlags.InContainer) != 0, - $"Entity has bad container flags. Ent: {ToPrettyString(uid)}. Container: {container.ID}, Owner: {ToPrettyString(container.Owner)}"); - } + var flags = MetaData(uid).Flags; + DebugTools.Assert((flags & MetaDataFlags.InContainer) != 0, + $"Entity has bad container flags. Ent: {ToPrettyString(uid)}. Container: {container.ID}, Owner: {ToPrettyString(container.Owner)}"); } }