Remove IContainer and move some functions to the system (#4351)

This commit is contained in:
Leon Friedrich
2023-09-10 14:17:00 +12:00
committed by GitHub
parent 8274623edb
commit 5e21dbdd7f
21 changed files with 309 additions and 487 deletions

View File

@@ -11,7 +11,6 @@ using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Robust.Shared.Physics.Components;
using static Robust.Shared.Containers.ContainerManagerComponent;
namespace Robust.Client.GameObjects
@@ -25,7 +24,7 @@ namespace Robust.Client.GameObjects
private readonly HashSet<EntityUid> _updateQueue = new();
public readonly Dictionary<EntityUid, IContainer> ExpectedEntities = new();
public readonly Dictionary<EntityUid, BaseContainer> ExpectedEntities = new();
public override void Initialize()
{
@@ -43,7 +42,7 @@ namespace Robust.Client.GameObjects
base.Shutdown();
}
protected override void ValidateMissingEntity(EntityUid uid, IContainer cont, EntityUid missing)
protected override void ValidateMissingEntity(EntityUid uid, BaseContainer cont, EntityUid missing)
{
DebugTools.Assert(ExpectedEntities.TryGetValue(missing, out var expectedContainer) && expectedContainer == cont && cont.ExpectedEntities.Contains(missing));
}
@@ -53,9 +52,6 @@ namespace Robust.Client.GameObjects
if (!RemoveExpectedEntity(uid, out var container))
return;
if (container.Deleted)
return;
container.Insert(uid);
}
@@ -72,8 +68,11 @@ namespace Robust.Client.GameObjects
var toDelete = new ValueList<string>();
foreach (var (id, container) in component.Containers)
{
if (cast.Containers.ContainsKey(id))
if (cast.Containers.TryGetValue(id, out var stateContainer)
&& stateContainer.GetType() == container.GetType())
{
continue;
}
foreach (var entity in container.ContainedEntities.ToArray())
{
@@ -98,26 +97,26 @@ namespace Robust.Client.GameObjects
// Add new containers and update existing contents.
foreach (var (containerType, id, showEnts, occludesLight, entityUids) in cast.Containers.Values)
foreach (var (id, stateContainer) in cast.Containers)
{
DebugTools.AssertNotNull(stateContainer.ContainedEntities);
if (!component.Containers.TryGetValue(id, out var container))
{
container = ContainerFactory(component, containerType, id);
container = _dynFactory.CreateInstanceUnchecked<BaseContainer>(stateContainer.GetType(), inject: false);
container.Init(id, uid, component);
component.Containers.Add(id, container);
}
// sync show flag
container.ShowContents = showEnts;
container.OccludesLight = occludesLight;
DebugTools.Assert(container.ID == id);
container.ShowContents = stateContainer.ShowContents;
container.OccludesLight = stateContainer.OccludesLight;
// Remove gone entities.
var toRemove = new ValueList<EntityUid>();
foreach (var entity in container.ContainedEntities)
{
if (!entityUids.Contains(entity))
{
if (!stateContainer.Contains(entity))
toRemove.Add(entity);
}
}
foreach (var entity in toRemove)
@@ -137,10 +136,8 @@ namespace Robust.Client.GameObjects
var removedExpected = new ValueList<EntityUid>();
foreach (var entityUid in container.ExpectedEntities)
{
if (!entityUids.Contains(entityUid))
{
if (!stateContainer.Contains(entityUid))
removedExpected.Add(entityUid);
}
}
foreach (var entityUid in removedExpected)
@@ -149,7 +146,7 @@ namespace Robust.Client.GameObjects
}
// Add new entities.
foreach (var entity in entityUids)
foreach (var entity in stateContainer.ContainedEntities)
{
if (!EntityManager.TryGetComponent(entity, out MetaDataComponent? meta))
{
@@ -208,24 +205,10 @@ namespace Robust.Client.GameObjects
return;
}
if (container.Deleted)
return;
container.Insert(message.Entity, EntityManager);
}
private IContainer ContainerFactory(ContainerManagerComponent component, string containerType, string id)
{
var type = _serializer.FindSerializedType(typeof(IContainer), containerType);
if (type is null) throw new ArgumentException($"Container of type {containerType} for id {id} cannot be found.");
var newContainer = _dynFactory.CreateInstanceUnchecked<BaseContainer>(type);
newContainer.ID = id;
newContainer.Manager = component;
return newContainer;
}
public void AddExpectedEntity(EntityUid uid, IContainer container)
public void AddExpectedEntity(EntityUid uid, BaseContainer container)
{
DebugTools.Assert(!TryComp(uid, out MetaDataComponent? meta) ||
(meta.Flags & ( MetaDataFlags.Detached | MetaDataFlags.InContainer) ) == MetaDataFlags.Detached,
@@ -247,7 +230,7 @@ namespace Robust.Client.GameObjects
container.ExpectedEntities.Add(uid);
}
public bool RemoveExpectedEntity(EntityUid uid, [NotNullWhen(true)] out IContainer? container)
public bool RemoveExpectedEntity(EntityUid uid, [NotNullWhen(true)] out BaseContainer? container)
{
if (!ExpectedEntities.Remove(uid, out container))
return false;

View File

@@ -1000,7 +1000,7 @@ namespace Robust.Client.GameStates
// In some cursed scenarios an entity inside of a container can leave PVS without the container itself leaving PVS.
// In those situations, we need to add the entity back to the list of expected entities after detaching.
IContainer? container = null;
BaseContainer? container = null;
if ((meta.Flags & MetaDataFlags.InContainer) != 0 &&
metas.TryGetComponent(xform.ParentUid, out var containerMeta) &&
(containerMeta.Flags & MetaDataFlags.Detached) == 0 &&
@@ -1238,7 +1238,7 @@ namespace Robust.Client.GameStates
var xform = _entities.GetComponent<TransformComponent>(uid);
if (xform.ParentUid.IsValid())
{
IContainer? container = null;
BaseContainer? container = null;
if ((meta.Flags & MetaDataFlags.InContainer) != 0 &&
_entities.TryGetComponent(xform.ParentUid, out MetaDataComponent? containerMeta) &&
(containerMeta.Flags & MetaDataFlags.Detached) == 0)

View File

@@ -6,7 +6,7 @@ namespace Robust.Server.Containers
{
public sealed class ContainerSystem : SharedContainerSystem
{
protected override void ValidateMissingEntity(EntityUid uid, IContainer 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);

View File

@@ -12,57 +12,81 @@ using Robust.Shared.ViewVariables;
using System;
using System.Collections.Generic;
using System.Numerics;
using Robust.Shared.Map.Components;
using Robust.Shared.Serialization;
namespace Robust.Shared.Containers
{
/// <summary>
/// Base container class that all container inherit from.
/// </summary>
public abstract partial class BaseContainer : IContainer
[ImplicitDataDefinitionForInheritors]
[Serializable, NetSerializable]
public abstract partial class BaseContainer
{
/// <inheritdoc />
/// <summary>
/// Readonly collection of all the entities contained within this specific container
/// </summary>
[ViewVariables]
public abstract IReadOnlyList<EntityUid> ContainedEntities { get; }
[ViewVariables]
public abstract List<EntityUid> ExpectedEntities { get; }
[ViewVariables, NonSerialized]
public List<EntityUid> ExpectedEntities = new();
/// <inheritdoc />
public abstract string ContainerType { get; }
/// <summary>
/// The ID of this container.
/// </summary>
[ViewVariables, NonSerialized, Access(typeof(SharedContainerSystem), typeof(ContainerManagerComponent))]
public string ID = default!;
/// <inheritdoc />
[ViewVariables]
public bool Deleted { get; private set; }
[NonSerialized]
internal ContainerManagerComponent Manager = default!;
/// <inheritdoc />
[ViewVariables]
public string ID { get; internal set; } = default!; // Make sure you set me in init
/// <inheritdoc />
public ContainerManagerComponent Manager { get; internal set; } = default!; // Make sure you set me in init
/// <inheritdoc />
/// <summary>
/// Prevents light from escaping the container, from ex. a flashlight.
/// </summary>
[ViewVariables(VVAccess.ReadWrite)]
[DataField("occludes")]
public bool OccludesLight { get; set; } = true;
/// <inheritdoc />
/// <summary>
/// The entity that owns this container.
/// </summary>
[ViewVariables]
public EntityUid Owner => Manager.Owner;
/// <inheritdoc />
/// <summary>
/// Should the contents of this container be shown? False for closed containers like lockers, true for
/// things like glass display cases.
/// </summary>
[ViewVariables(VVAccess.ReadWrite)]
[DataField("showEnts")]
public bool ShowContents { get; set; }
/// <summary>
/// DO NOT CALL THIS METHOD DIRECTLY!
/// You want <see cref="IContainerManager.MakeContainer{T}(string)" /> instead.
/// </summary>
protected BaseContainer() { }
internal void Init(string id, EntityUid owner, ContainerManagerComponent component)
{
DebugTools.AssertNull(ID);
ID = id;
Manager = component;
/// <inheritdoc />
// TODO fix container init.
// Eventually, we want an owner field, but currently it needs to use component.Owner
// Owner = owner;
}
/// <summary>
/// Attempts to insert the entity into this container.
/// </summary>
/// <remarks>
/// If the insertion is successful, the inserted entity will end up parented to the
/// container entity, and the inserted entity's local position will be set to the zero vector.
/// </remarks>
/// <param name="toinsert">The entity to insert.</param>
/// <param name="entMan"></param>
/// <returns>False if the entity could not be inserted.</returns>
/// <exception cref="InvalidOperationException">
/// Thrown if this container is a child of the entity,
/// which would cause infinite loops.
/// </exception>
public bool Insert(
EntityUid toinsert,
IEntityManager? entMan = null,
@@ -72,7 +96,6 @@ namespace Robust.Shared.Containers
PhysicsComponent? physics = null,
bool force = false)
{
DebugTools.Assert(!Deleted);
DebugTools.Assert(transform == null || transform.Owner == toinsert);
DebugTools.Assert(ownerTransform == null || ownerTransform.Owner == Owner);
DebugTools.Assert(ownerTransform == null || ownerTransform.Owner == Owner);
@@ -80,10 +103,6 @@ namespace Robust.Shared.Containers
DebugTools.Assert(!ExpectedEntities.Contains(toinsert));
IoCManager.Resolve(ref entMan);
//Verify we can insert into this container
if (!force && !CanInsert(toinsert, entMan))
return false;
var physicsQuery = entMan.GetEntityQuery<PhysicsComponent>();
var transformQuery = entMan.GetEntityQuery<TransformComponent>();
var jointQuery = entMan.GetEntityQuery<JointComponent>();
@@ -91,6 +110,11 @@ namespace Robust.Shared.Containers
// ECS containers when
var physicsSys = entMan.EntitySysManager.GetEntitySystem<SharedPhysicsSystem>();
var jointSys = entMan.EntitySysManager.GetEntitySystem<SharedJointSystem>();
var containerSys = entMan.EntitySysManager.GetEntitySystem<SharedContainerSystem>();
//Verify we can insert into this container
if (!force && !containerSys.CanInsert(toinsert, this))
return false;
// Please somebody ecs containers
var lookupSys = entMan.EntitySysManager.GetEntitySystem<EntityLookupSystem>();
@@ -214,45 +238,22 @@ namespace Robust.Shared.Containers
}
}
/// <inheritdoc />
public virtual bool CanInsert(EntityUid toinsert, IEntityManager? entMan = null)
{
DebugTools.Assert(!Deleted);
/// <summary>
/// Whether the given entity can be inserted into this container.
/// </summary>
/// <param name="assumeEmpty">Whether to assume that the container is currently empty.</param>
protected internal virtual bool CanInsert(EntityUid toInsert, bool assumeEmpty, IEntityManager entMan) => true;
// cannot insert into itself.
if (Owner == toinsert)
return false;
IoCManager.Resolve(ref entMan);
// no, you can't put maps or grids into containers
if (entMan.HasComponent<MapComponent>(toinsert) || entMan.HasComponent<MapGridComponent>(toinsert))
return false;
var xformSystem = entMan.EntitySysManager.GetEntitySystem<SharedTransformSystem>();
var xformQuery = entMan.GetEntityQuery<TransformComponent>();
// Crucial, prevent circular insertion.
if (xformSystem.ContainsEntity(xformQuery.GetComponent(toinsert), Owner, xformQuery))
return false;
//Improvement: Traverse the entire tree to make sure we are not creating a loop.
//raise events
var insertAttemptEvent = new ContainerIsInsertingAttemptEvent(this, toinsert);
entMan.EventBus.RaiseLocalEvent(Owner, insertAttemptEvent, true);
if (insertAttemptEvent.Cancelled)
return false;
var gettingInsertedAttemptEvent = new ContainerGettingInsertedAttemptEvent(this, toinsert);
entMan.EventBus.RaiseLocalEvent(toinsert, gettingInsertedAttemptEvent, true);
if (gettingInsertedAttemptEvent.Cancelled)
return false;
return true;
}
/// <inheritdoc />
/// <summary>
/// Attempts to remove the entity from this container.
/// </summary>
/// <param name="reparent">If false, this operation will not rigger a move or parent change event. Ignored if
/// destination is not null</param>
/// <param name="force">If true, this will not perform can-remove checks.</param>
/// <param name="destination">Where to place the entity after removing. Avoids unnecessary broadphase updates.
/// If not specified, and reparent option is true, then the entity will either be inserted into a parent
/// container, the grid, or the map.</param>
/// <param name="localRotation">Optional final local rotation after removal. Avoids redundant move events.</param>
public bool Remove(
EntityUid toRemove,
IEntityManager? entMan = null,
@@ -264,7 +265,6 @@ namespace Robust.Shared.Containers
Angle? localRotation = null)
{
IoCManager.Resolve(ref entMan);
DebugTools.Assert(!Deleted);
DebugTools.AssertNotNull(Manager);
DebugTools.Assert(entMan.EntityExists(toRemove));
DebugTools.Assert(xform == null || xform.Owner == toRemove);
@@ -273,7 +273,8 @@ namespace Robust.Shared.Containers
xform ??= entMan.GetComponent<TransformComponent>(toRemove);
meta ??= entMan.GetComponent<MetaDataComponent>(toRemove);
if (!force && !CanRemove(toRemove, entMan))
var sys = entMan.EntitySysManager.GetEntitySystem<SharedContainerSystem>();
if (!force && !sys.CanRemove(toRemove, this))
return false;
if (force && !Contains(toRemove))
@@ -305,7 +306,7 @@ namespace Robust.Shared.Containers
else if (reparent)
{
// Container ECS when.
entMan.EntitySysManager.GetEntitySystem<SharedContainerSystem>().AttachParentToContainerOrGrid(xform);
sys.AttachParentToContainerOrGrid(xform);
if (localRotation != null)
entMan.EntitySysManager.GetEntitySystem<SharedTransformSystem>().SetLocalRotation(xform, localRotation.Value);
}
@@ -336,40 +337,22 @@ namespace Robust.Shared.Containers
public void ForceRemove(EntityUid toRemove, IEntityManager? entMan = null, MetaDataComponent? meta = null)
=> Remove(toRemove, entMan, meta: meta, reparent: false, force: true);
/// <inheritdoc />
public virtual bool CanRemove(EntityUid toRemove, IEntityManager? entMan = null)
{
DebugTools.Assert(!Deleted);
if (!Contains(toRemove))
return false;
IoCManager.Resolve(ref entMan);
//raise events
var removeAttemptEvent = new ContainerIsRemovingAttemptEvent(this, toRemove);
entMan.EventBus.RaiseLocalEvent(Owner, removeAttemptEvent, true);
if (removeAttemptEvent.Cancelled)
return false;
var gettingRemovedAttemptEvent = new ContainerGettingRemovedAttemptEvent(this, toRemove);
entMan.EventBus.RaiseLocalEvent(toRemove, gettingRemovedAttemptEvent, true);
if (gettingRemovedAttemptEvent.Cancelled)
return false;
return true;
}
/// <inheritdoc />
/// <summary>
/// Checks if the entity is contained in this container.
/// This is not recursive, so containers of children are not checked.
/// </summary>
/// <param name="contained">The entity to check.</param>
/// <returns>True if the entity is immediately contained in this container, false otherwise.</returns>
public abstract bool Contains(EntityUid contained);
/// <inheritdoc />
/// <summary>
/// Clears the container and marks it as deleted.
/// </summary>
public void Shutdown(IEntityManager? entMan = null, INetManager? netMan = null)
{
IoCManager.Resolve(ref entMan, ref netMan);
InternalShutdown(entMan, netMan.IsClient);
Manager.Containers.Remove(ID);
Deleted = true;
}
/// <inheritdoc />

View File

@@ -1,9 +1,11 @@
using System;
using System.Collections.Generic;
using JetBrains.Annotations;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Serialization;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.Timing;
using Robust.Shared.Utility;
namespace Robust.Shared.Containers
@@ -15,27 +17,18 @@ namespace Robust.Shared.Containers
/// For example, inventory containers should be modified only through an inventory component.
/// </summary>
[UsedImplicitly]
[SerializedType(ClassName)]
[Serializable, NetSerializable]
public sealed partial class Container : BaseContainer
{
private const string ClassName = "Container";
/// <summary>
/// The generic container class uses a list of entities
/// </summary>
[DataField("ents")]
private List<EntityUid> _containerList = new();
private readonly List<EntityUid> _expectedEntities = new();
/// <inheritdoc />
public override IReadOnlyList<EntityUid> ContainedEntities => _containerList;
public override List<EntityUid> ExpectedEntities => _expectedEntities;
/// <inheritdoc />
public override string ContainerType => ClassName;
/// <inheritdoc />
protected override void InternalInsert(EntityUid toInsert, IEntityManager entMan)
{
@@ -56,6 +49,9 @@ namespace Robust.Shared.Containers
return false;
#if DEBUG
if (IoCManager.Resolve<IGameTiming>().ApplyingState)
return true;
var entMan = IoCManager.Resolve<IEntityManager>();
var flags = entMan.GetComponent<MetaDataComponent>(contained).Flags;
DebugTools.Assert((flags & MetaDataFlags.InContainer) != 0, $"Entity has bad container flags. Ent: {entMan.ToPrettyString(contained)}. Container: {ID}, Owner: {entMan.ToPrettyString(Owner)}");

View File

@@ -54,7 +54,7 @@ namespace Robust.Shared.Containers
/// <param name="container">The container that this entity is inside of.</param>
/// <returns>If a container was found.</returns>
[Obsolete("Use ContainerSystem.TryGetContainingContainer() instead")]
public static bool TryGetContainer(this EntityUid entity, [NotNullWhen(true)] out IContainer? container, IEntityManager? entMan = null)
public static bool TryGetContainer(this EntityUid entity, [NotNullWhen(true)] out BaseContainer? container, IEntityManager? entMan = null)
{
IoCManager.Resolve(ref entMan);
DebugTools.Assert(entMan.EntityExists(entity));
@@ -100,7 +100,7 @@ namespace Robust.Shared.Containers
/// <see cref="SharedContainerSystem.EmptyContainer"/>
/// </summary>
[Obsolete("Use SharedContainerSystem.EmptyContainer() instead")]
public static void EmptyContainer(this IContainer container, bool force = false, EntityCoordinates? moveTo = null,
public static void EmptyContainer(this BaseContainer container, bool force = false, EntityCoordinates? moveTo = null,
bool attachToGridOrMap = false, IEntityManager? entMan = null)
{
IoCManager.Resolve<IEntitySystemManager>().GetEntitySystem<SharedContainerSystem>()
@@ -112,7 +112,7 @@ namespace Robust.Shared.Containers
/// <see cref="SharedContainerSystem.CleanContainer"/>
/// </summary>
[Obsolete("Use SharedContainerSystem.CleanContainer() instead")]
public static void CleanContainer(this IContainer container, IEntityManager? entMan = null)
public static void CleanContainer(this BaseContainer container, IEntityManager? entMan = null)
{
IoCManager.Resolve<IEntitySystemManager>().GetEntitySystem<SharedContainerSystem>()
.CleanContainer(container);
@@ -147,10 +147,10 @@ namespace Robust.Shared.Containers
/// <param name="containerId"></param>
/// <returns>The new container.</returns>
/// <exception cref="ArgumentException">Thrown if there already is a container with the specified ID.</exception>
/// <seealso cref="IContainerManager.MakeContainer{T}(string)" />
/// <seealso cref="BaseContainerManager.MakeContainer{T}(string)" />
[Obsolete("Use ContainerSystem.MakeContainer() instead")]
public static T CreateContainer<T>(this EntityUid entity, string containerId, IEntityManager? entMan = null)
where T : IContainer
where T : BaseContainer
{
IoCManager.Resolve(ref entMan);
var containermanager = entMan.EnsureComponent<ContainerManagerComponent>(entity);
@@ -159,7 +159,7 @@ namespace Robust.Shared.Containers
[Obsolete("Use ContainerSystem.EnsureContainer() instead")]
public static T EnsureContainer<T>(this EntityUid entity, string containerId, IEntityManager? entMan = null)
where T : IContainer
where T : BaseContainer
{
IoCManager.Resolve(ref entMan);
return EnsureContainer<T>(entity, containerId, out _, entMan);
@@ -167,7 +167,7 @@ namespace Robust.Shared.Containers
[Obsolete("Use ContainerSystem.EnsureContainer() instead")]
public static T EnsureContainer<T>(this EntityUid entity, string containerId, out bool alreadyExisted, IEntityManager? entMan = null)
where T : IContainer
where T : BaseContainer
{
IoCManager.Resolve(ref entMan);
var containerManager = entMan.EnsureComponent<ContainerManagerComponent>(entity);

View File

@@ -25,18 +25,16 @@ namespace Robust.Shared.Containers
[Dependency] private readonly INetManager _netMan = default!;
[DataField("containers")]
public Dictionary<string, IContainer> Containers = new();
public Dictionary<string, BaseContainer> Containers = new();
void ISerializationHooks.AfterDeserialization()
{
// TODO remove ISerializationHooks I guess the IDs can be set by a custom serializer for the dictionary? But
// the component??? Maybe other systems need to stop assuming that containers have been initialized during
// their own init.
// TODO custom type serializer
// TODO set owner uid on init.
foreach (var (id, container) in Containers)
{
var baseContainer = (BaseContainer) container;
baseContainer.Manager = this;
baseContainer.ID = id;
container.Manager = this;
container.ID = id;
}
}
@@ -55,13 +53,20 @@ namespace Robust.Shared.Containers
/// <inheritdoc />
public T MakeContainer<T>(string id)
where T : IContainer
where T : BaseContainer
{
return (T) MakeContainer(id, typeof(T));
if (HasContainer(id))
throw new ArgumentException($"Container with specified ID already exists: '{id}'");
var container = _dynFactory.CreateInstanceUnchecked<T>(typeof(T), inject: false);
container.Init(id, Owner, this);
Containers[id] = container;
_entMan.Dirty(this);
return container;
}
/// <inheritdoc />
public IContainer GetContainer(string id)
public BaseContainer GetContainer(string id)
{
return Containers[id];
}
@@ -73,7 +78,7 @@ namespace Robust.Shared.Containers
}
/// <inheritdoc />
public bool TryGetContainer(string id, [NotNullWhen(true)] out IContainer? container)
public bool TryGetContainer(string id, [NotNullWhen(true)] out BaseContainer? container)
{
var ret = Containers.TryGetValue(id, out var cont);
container = cont!;
@@ -81,11 +86,11 @@ namespace Robust.Shared.Containers
}
/// <inheritdoc />
public bool TryGetContainer(EntityUid entity, [NotNullWhen(true)] out IContainer? container)
public bool TryGetContainer(EntityUid entity, [NotNullWhen(true)] out BaseContainer? container)
{
foreach (var contain in Containers.Values)
{
if (!contain.Deleted && contain.Contains(entity))
if (contain.Contains(entity))
{
container = contain;
return true;
@@ -101,7 +106,7 @@ namespace Robust.Shared.Containers
{
foreach (var container in Containers.Values)
{
if (!container.Deleted && container.Contains(entity)) return true;
if (container.Contains(entity)) return true;
}
return false;
@@ -125,19 +130,6 @@ namespace Robust.Shared.Containers
return true; // If we don't contain the entity, it will always be removed
}
private IContainer MakeContainer(string id, Type type)
{
if (HasContainer(id)) throw new ArgumentException($"Container with specified ID already exists: '{id}'");
var container = _dynFactory.CreateInstanceUnchecked<BaseContainer>(type);
container.ID = id;
container.Manager = this;
Containers[id] = container;
_entMan.Dirty(this);
return container;
}
public AllContainersEnumerable GetAllContainers()
{
return new(this);
@@ -146,60 +138,15 @@ namespace Robust.Shared.Containers
[Serializable, NetSerializable]
internal sealed class ContainerManagerComponentState : ComponentState
{
public Dictionary<string, ContainerData> Containers;
public Dictionary<string, BaseContainer> Containers;
public ContainerManagerComponentState(Dictionary<string, ContainerData> containers)
public ContainerManagerComponentState(Dictionary<string, BaseContainer> containers)
{
Containers = containers;
}
[Serializable, NetSerializable]
public readonly struct ContainerData
{
public readonly string ContainerType;
public readonly string Id;
public readonly bool ShowContents;
public readonly bool OccludesLight;
public readonly EntityUid[] ContainedEntities;
public ContainerData(string containerType, string id, bool showContents, bool occludesLight, EntityUid[] containedEntities)
{
ContainerType = containerType;
Id = id;
ShowContents = showContents;
OccludesLight = occludesLight;
ContainedEntities = containedEntities;
}
public void Deconstruct(out string type, out string id, out bool showEnts, out bool occludesLight, out EntityUid[] ents)
{
type = ContainerType;
id = Id;
showEnts = ShowContents;
occludesLight = OccludesLight;
ents = ContainedEntities;
}
}
}
[DataDefinition]
private partial struct ContainerPrototypeData
{
[DataField("entities")] public List<EntityUid> Entities = new ();
[DataField("type")] public string? Type = null;
// explicit parameterless constructor is required.
public ContainerPrototypeData() { }
public ContainerPrototypeData(List<EntityUid> entities, string type)
{
Entities = entities;
Type = type;
}
}
public readonly struct AllContainersEnumerable : IEnumerable<IContainer>
public readonly struct AllContainersEnumerable : IEnumerable<BaseContainer>
{
private readonly ContainerManagerComponent? _manager;
@@ -213,7 +160,7 @@ namespace Robust.Shared.Containers
return new(_manager);
}
IEnumerator<IContainer> IEnumerable<IContainer>.GetEnumerator()
IEnumerator<BaseContainer> IEnumerable<BaseContainer>.GetEnumerator()
{
return GetEnumerator();
}
@@ -224,9 +171,9 @@ namespace Robust.Shared.Containers
}
}
public struct AllContainersEnumerator : IEnumerator<IContainer>
public struct AllContainersEnumerator : IEnumerator<BaseContainer>
{
private Dictionary<string, IContainer>.ValueCollection.Enumerator _enumerator;
private Dictionary<string, BaseContainer>.ValueCollection.Enumerator _enumerator;
public AllContainersEnumerator(ContainerManagerComponent? manager)
{
@@ -238,11 +185,8 @@ namespace Robust.Shared.Containers
{
while (_enumerator.MoveNext())
{
if (!_enumerator.Current.Deleted)
{
Current = _enumerator.Current;
return true;
}
Current = _enumerator.Current;
return true;
}
return false;
@@ -250,11 +194,11 @@ namespace Robust.Shared.Containers
void IEnumerator.Reset()
{
((IEnumerator<IContainer>) _enumerator).Reset();
((IEnumerator<BaseContainer>) _enumerator).Reset();
}
[AllowNull]
public IContainer Current { get; private set; }
public BaseContainer Current { get; private set; }
object IEnumerator.Current => Current;

View File

@@ -6,24 +6,25 @@ using Robust.Shared.IoC;
using Robust.Shared.Log;
using Robust.Shared.Serialization;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.Timing;
using Robust.Shared.Utility;
namespace Robust.Shared.Containers
{
[UsedImplicitly]
[SerializedType(ClassName)]
[Serializable, NetSerializable]
public sealed partial class ContainerSlot : BaseContainer
{
private const string ClassName = "ContainerSlot";
/// <inheritdoc />
public override IReadOnlyList<EntityUid> ContainedEntities
{
get
{
if (ContainedEntity == null)
if (_containedEntity == null)
return Array.Empty<EntityUid>();
_containedEntityArray ??= new[] { _containedEntity.Value };
DebugTools.Assert(_containedEntityArray[0] == _containedEntity);
return _containedEntityArray;
}
}
@@ -36,39 +37,18 @@ namespace Robust.Shared.Containers
{
_containedEntity = value;
if (value != null)
_containedEntityArray[0] = value!.Value;
{
_containedEntityArray ??= new EntityUid[1];
_containedEntityArray[0] = value.Value;
}
}
}
public override List<EntityUid> ExpectedEntities => _expectedEntities;
private EntityUid? _containedEntity;
private readonly List<EntityUid> _expectedEntities = new();
// Used by ContainedEntities to avoid allocating.
private readonly EntityUid[] _containedEntityArray = new EntityUid[1];
/// <inheritdoc />
public override string ContainerType => ClassName;
/// <inheritdoc />
public override bool CanInsert(EntityUid toinsert, IEntityManager? entMan = null)
{
return (ContainedEntity == null) && CanInsertIfEmpty(toinsert, entMan);
}
/// <summary>
/// Checks if the entity can be inserted into this container, assuming that the container slot is empty.
/// </summary>
/// <remarks>
/// Useful if you need to know whether an item could be inserted into a slot, without having to actually eject
/// the currently contained entity first.
/// </remarks>
/// <param name="toinsert">The entity to attempt to insert.</param>
/// <returns>True if the entity could be inserted into an empty slot, false otherwise.</returns>
public bool CanInsertIfEmpty(EntityUid toinsert, IEntityManager? entMan = null)
{
return base.CanInsert(toinsert, entMan);
}
[NonSerialized]
private EntityUid[]? _containedEntityArray;
/// <inheritdoc />
public override bool Contains(EntityUid contained)
@@ -77,6 +57,9 @@ namespace Robust.Shared.Containers
return false;
#if DEBUG
if (IoCManager.Resolve<IGameTiming>().ApplyingState)
return true;
var entMan = IoCManager.Resolve<IEntityManager>();
var flags = entMan.GetComponent<MetaDataComponent>(contained).Flags;
DebugTools.Assert((flags & MetaDataFlags.InContainer) != 0, $"Entity has bad container flags. Ent: {entMan.ToPrettyString(contained)}. Container: {ID}, Owner: {entMan.ToPrettyString(Owner)}");

View File

@@ -6,10 +6,10 @@ namespace Robust.Shared.Containers;
public abstract class ContainerAttemptEventBase : CancellableEntityEventArgs
{
public readonly IContainer Container;
public readonly BaseContainer Container;
public readonly EntityUid EntityUid;
public ContainerAttemptEventBase(IContainer container, EntityUid entityUid)
public ContainerAttemptEventBase(BaseContainer container, EntityUid entityUid)
{
Container = container;
EntityUid = entityUid;
@@ -18,28 +18,44 @@ public abstract class ContainerAttemptEventBase : CancellableEntityEventArgs
public sealed class ContainerIsInsertingAttemptEvent : ContainerAttemptEventBase
{
public ContainerIsInsertingAttemptEvent(IContainer container, EntityUid entityUid) : base(container, entityUid)
/// <summary>
/// If true, this check should assume that the container is currently empty.
/// I.e., could the entity be inserted if the container doesn't contain anything else?
/// </summary>
public bool AssumeEmpty { get; set; }
public ContainerIsInsertingAttemptEvent(BaseContainer container, EntityUid entityUid, bool assumeEmpty)
: base(container, entityUid)
{
AssumeEmpty = assumeEmpty;
}
}
public sealed class ContainerGettingInsertedAttemptEvent : ContainerAttemptEventBase
{
public ContainerGettingInsertedAttemptEvent(IContainer container, EntityUid entityUid) : base(container, entityUid)
/// <summary>
/// If true, this check should assume that the container is currently empty.
/// I.e., could the entity be inserted if the container doesn't contain anything else?
/// </summary>
public bool AssumeEmpty { get; set; }
public ContainerGettingInsertedAttemptEvent(BaseContainer container, EntityUid entityUid, bool assumeEmpty)
: base(container, entityUid)
{
AssumeEmpty = assumeEmpty;
}
}
public sealed class ContainerIsRemovingAttemptEvent : ContainerAttemptEventBase
{
public ContainerIsRemovingAttemptEvent(IContainer container, EntityUid entityUid) : base(container, entityUid)
public ContainerIsRemovingAttemptEvent(BaseContainer container, EntityUid entityUid) : base(container, entityUid)
{
}
}
public sealed class ContainerGettingRemovedAttemptEvent : ContainerAttemptEventBase
{
public ContainerGettingRemovedAttemptEvent(IContainer container, EntityUid entityUid) : base(container, entityUid)
public ContainerGettingRemovedAttemptEvent(BaseContainer container, EntityUid entityUid) : base(container, entityUid)
{
}
}

View File

@@ -12,14 +12,14 @@ namespace Robust.Shared.Containers
/// <summary>
/// The container being acted upon.
/// </summary>
public IContainer Container { get; }
public BaseContainer Container { get; }
/// <summary>
/// The entity that was removed or inserted from/into the container.
/// </summary>
public EntityUid Entity { get; }
protected ContainerModifiedMessage(EntityUid entity, IContainer container)
protected ContainerModifiedMessage(EntityUid entity, BaseContainer container)
{
Entity = entity;
Container = container;

View File

@@ -9,5 +9,5 @@ namespace Robust.Shared.Containers;
[PublicAPI]
public sealed class EntGotInsertedIntoContainerMessage : ContainerModifiedMessage
{
public EntGotInsertedIntoContainerMessage(EntityUid entity, IContainer container) : base(entity, container) { }
public EntGotInsertedIntoContainerMessage(EntityUid entity, BaseContainer container) : base(entity, container) { }
}

View File

@@ -11,7 +11,7 @@ namespace Robust.Shared.Containers
{
public readonly EntityUid OldParent;
public EntInsertedIntoContainerMessage(EntityUid entity, EntityUid oldParent, IContainer container) : base(entity, container)
public EntInsertedIntoContainerMessage(EntityUid entity, EntityUid oldParent, BaseContainer container) : base(entity, container)
{
OldParent = oldParent;
}

View File

@@ -9,7 +9,7 @@ namespace Robust.Shared.Containers
[PublicAPI]
public sealed class EntRemovedFromContainerMessage : ContainerModifiedMessage
{
public EntRemovedFromContainerMessage(EntityUid entity, IContainer container) : base(entity, container) { }
public EntRemovedFromContainerMessage(EntityUid entity, BaseContainer container) : base(entity, container) { }
}
/// <summary>
@@ -18,6 +18,6 @@ namespace Robust.Shared.Containers
[PublicAPI]
public sealed class EntGotRemovedFromContainerMessage : ContainerModifiedMessage
{
public EntGotRemovedFromContainerMessage(EntityUid entity, IContainer container) : base(entity, container) { }
public EntGotRemovedFromContainerMessage(EntityUid entity, BaseContainer container) : base(entity, container) { }
}
}

View File

@@ -1,147 +0,0 @@
using System;
using System.Collections.Generic;
using JetBrains.Annotations;
using Robust.Shared.GameObjects;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Network;
using Robust.Shared.Physics.Components;
using Robust.Shared.Serialization.Manager.Attributes;
namespace Robust.Shared.Containers
{
/// <summary>
/// A container is a way to "contain" entities inside other entities, in a logical way.
/// This is alike BYOND's <c>contents</c> system, except more advanced.
/// </summary>
/// <remarks>
/// <p>
/// Containers are logical separations of entities contained inside another entity.
/// for example, a crate with two separated compartments would have two separate containers.
/// If an entity inside compartment A drops something,
/// the dropped entity would be placed in compartment A too,
/// and compartment B would be completely untouched.
/// </p>
/// <p>
/// Containers are managed by an entity's <see cref="IContainerManager" />,
/// and have an ID to be referenced by.
/// </p>
/// </remarks>
/// <seealso cref="IContainerManager" />
[PublicAPI]
[ImplicitDataDefinitionForInheritors]
public partial interface IContainer
{
/// <summary>
/// Readonly collection of all the entities contained within this specific container
/// </summary>
IReadOnlyList<EntityUid> ContainedEntities { get; }
List<EntityUid> ExpectedEntities { get; }
/// <summary>
/// The type of this container.
/// </summary>
string ContainerType { get; }
/// <summary>
/// True if the container has been shut down via <see cref="Shutdown" />
/// </summary>
bool Deleted { get; }
/// <summary>
/// The ID of this container.
/// </summary>
string ID { get; }
/// <summary>
/// Prevents light from escaping the container, from ex. a flashlight.
/// </summary>
bool OccludesLight { get; set; }
/// <summary>
/// The entity owning this container.
/// </summary>
EntityUid Owner { get; }
/// <summary>
/// Should the contents of this container be shown? False for closed containers like lockers, true for
/// things like glass display cases.
/// </summary>
bool ShowContents { get; set; }
/// <summary>
/// Checks if the entity can be inserted into this container.
/// </summary>
/// <param name="toinsert">The entity to attempt to insert.</param>
/// <param name="entMan"></param>
/// <returns>True if the entity can be inserted, false otherwise.</returns>
bool CanInsert(EntityUid toinsert, IEntityManager? entMan = null);
/// <summary>
/// Attempts to insert the entity into this container.
/// </summary>
/// <remarks>
/// If the insertion is successful, the inserted entity will end up parented to the
/// container entity, and the inserted entity's local position will be set to the zero vector.
/// </remarks>
/// <param name="toinsert">The entity to insert.</param>
/// <param name="entMan"></param>
/// <returns>False if the entity could not be inserted.</returns>
/// <exception cref="InvalidOperationException">
/// Thrown if this container is a child of the entity,
/// which would cause infinite loops.
/// </exception>
bool Insert(EntityUid toinsert,
IEntityManager? entMan = null,
TransformComponent? transform = null,
TransformComponent? ownerTransform = null,
MetaDataComponent? meta = null,
PhysicsComponent? physics = null,
bool force = false);
/// <summary>
/// Checks if the entity can be removed from this container.
/// </summary>
/// <param name="toremove">The entity to check.</param>
/// <param name="entMan"></param>
/// <returns>True if the entity can be removed, false otherwise.</returns>
bool CanRemove(EntityUid toremove, IEntityManager? entMan = null);
/// <summary>
/// Attempts to remove the entity from this container.
/// </summary>
/// <param name="reparent">If false, this operation will not rigger a move or parent change event. Ignored if
/// destination is not null</param>
/// <param name="force">If true, this will not perform can-remove checks.</param>
/// <param name="destination">Where to place the entity after removing. Avoids unnecessary broadphase updates.
/// If not specified, and reparent option is true, then the entity will either be inserted into a parent
/// container, the grid, or the map.</param>
/// <param name="localRotation">Optional final local rotation after removal. Avoids redundant move events.</param>
bool Remove(
EntityUid toremove,
IEntityManager? entMan = null,
TransformComponent? xform = null,
MetaDataComponent? meta = null,
bool reparent = true,
bool force = false,
EntityCoordinates? destination = null,
Angle? localRotation = null);
[Obsolete("use force option in Remove()")]
void ForceRemove(EntityUid toRemove, IEntityManager? entMan = null, MetaDataComponent? meta = null);
/// <summary>
/// Checks if the entity is contained in this container.
/// This is not recursive, so containers of children are not checked.
/// </summary>
/// <param name="contained">The entity to check.</param>
/// <returns>True if the entity is immediately contained in this container, false otherwise.</returns>
bool Contains(EntityUid contained);
/// <summary>
/// Clears the container and marks it as deleted.
/// </summary>
void Shutdown(IEntityManager? entMan = null, INetManager? netMan = null);
}
}

View File

@@ -0,0 +1,49 @@
using Robust.Shared.GameObjects;
using Robust.Shared.Utility;
namespace Robust.Shared.Containers;
public abstract partial class SharedContainerSystem
{
/// <summary>
/// Checks if the entity can be inserted into the given container.
/// </summary>
/// <param name="assumeEmpty">If true, this will check whether the entity could be inserted if the container were
/// empty.</param>
public bool CanInsert(
EntityUid toInsert,
BaseContainer container,
TransformComponent? toInsertXform = null,
bool assumeEmpty = false)
{
if (container.Owner == toInsert)
return false;
if (!assumeEmpty && container.Contains(toInsert))
return false;
if (!container.CanInsert(toInsert, assumeEmpty, EntityManager))
return false;
if (!_xforms.Resolve(toInsert, ref toInsertXform))
return false;
// no, you can't put maps or grids into containers
if (_mapQuery.HasComponent(toInsert) || _gridQuery.HasComponent(toInsert))
return false;
// Prevent circular insertion.
if (_transform.ContainsEntity(toInsertXform, container.Owner))
return false;
var insertAttemptEvent = new ContainerIsInsertingAttemptEvent(container, toInsert, assumeEmpty);
RaiseLocalEvent(container.Owner, insertAttemptEvent, true);
if (insertAttemptEvent.Cancelled)
return false;
var gettingInsertedAttemptEvent = new ContainerGettingInsertedAttemptEvent(container, toInsert, assumeEmpty);
RaiseLocalEvent(toInsert, gettingInsertedAttemptEvent, true);
return !gettingInsertedAttemptEvent.Cancelled;
}
}

View File

@@ -0,0 +1,26 @@
using Robust.Shared.GameObjects;
namespace Robust.Shared.Containers;
public abstract partial class SharedContainerSystem
{
/// <summary>
/// Checks if the entity can be removed from this container.
/// </summary>
/// <returns>True if the entity can be removed, false otherwise.</returns>
public bool CanRemove(EntityUid toRemove, BaseContainer container)
{
if (!container.Contains(toRemove))
return false;
//raise events
var removeAttemptEvent = new ContainerIsRemovingAttemptEvent(container, toRemove);
RaiseLocalEvent(container.Owner, removeAttemptEvent, true);
if (removeAttemptEvent.Cancelled)
return false;
var gettingRemovedAttemptEvent = new ContainerGettingRemovedAttemptEvent(container, toRemove);
RaiseLocalEvent(toRemove, gettingRemovedAttemptEvent, true);
return !gettingRemovedAttemptEvent.Cancelled;
}
}

View File

@@ -54,7 +54,7 @@ public abstract partial class SharedContainerSystem : EntitySystem
}
}
protected abstract void ValidateMissingEntity(EntityUid uid, IContainer cont, EntityUid missing);
protected abstract void ValidateMissingEntity(EntityUid uid, BaseContainer cont, EntityUid missing);
private void ValidateChildren(TransformComponent xform, EntityQuery<TransformComponent> xformQuery, EntityQuery<PhysicsComponent> physicsQuery)
{

View File

@@ -6,6 +6,7 @@ using Robust.Shared.GameObjects;
using Robust.Shared.GameStates;
using Robust.Shared.IoC;
using Robust.Shared.Map;
using Robust.Shared.Map.Components;
using Robust.Shared.Maths;
using Robust.Shared.Physics.Systems;
using Robust.Shared.Utility;
@@ -16,6 +17,10 @@ namespace Robust.Shared.Containers
{
[Dependency] private readonly SharedPhysicsSystem _physics = default!;
[Dependency] private readonly EntityLookupSystem _lookup = default!;
[Dependency] private readonly SharedTransformSystem _transform = default!;
private EntityQuery<MapGridComponent> _gridQuery;
private EntityQuery<MapComponent> _mapQuery;
private EntityQuery<MetaDataComponent> _metas;
private EntityQuery<TransformComponent> _xforms;
@@ -29,29 +34,15 @@ namespace Robust.Shared.Containers
SubscribeLocalEvent<ContainerManagerComponent, ComponentStartup>(OnStartupValidation);
SubscribeLocalEvent<ContainerManagerComponent, ComponentGetState>(OnContainerGetState);
_gridQuery = GetEntityQuery<MapGridComponent>();
_mapQuery = GetEntityQuery<MapComponent>();
_metas = EntityManager.GetEntityQuery<MetaDataComponent>();
_xforms = EntityManager.GetEntityQuery<TransformComponent>();
}
private void OnContainerGetState(EntityUid uid, ContainerManagerComponent component, ref ComponentGetState args)
{
// naive implementation that just sends the full state of the component
Dictionary<string, ContainerManagerComponent.ContainerManagerComponentState.ContainerData> containerSet = new(component.Containers.Count);
foreach (var container in component.Containers.Values)
{
var uidArr = new EntityUid[container.ContainedEntities.Count];
for (var index = 0; index < container.ContainedEntities.Count; index++)
{
uidArr[index] = container.ContainedEntities[index];
}
var sContainer = new ContainerManagerComponent.ContainerManagerComponentState.ContainerData(container.ContainerType, container.ID, container.ShowContents, container.OccludesLight, uidArr);
containerSet.Add(container.ID, sContainer);
}
args.State = new ContainerManagerComponent.ContainerManagerComponentState(containerSet);
args.State = new ContainerManagerComponent.ContainerManagerComponentState(component.Containers);
}
// TODO: Make ContainerManagerComponent ECS and make these proxy methods the real deal.
@@ -59,7 +50,7 @@ namespace Robust.Shared.Containers
#region Proxy Methods
public T MakeContainer<T>(EntityUid uid, string id, ContainerManagerComponent? containerManager = null)
where T : IContainer
where T : BaseContainer
{
if (!Resolve(uid, ref containerManager, false))
containerManager = EntityManager.AddComponent<ContainerManagerComponent>(uid); // Happy Vera.
@@ -68,7 +59,7 @@ namespace Robust.Shared.Containers
}
public T EnsureContainer<T>(EntityUid uid, string id, out bool alreadyExisted, ContainerManagerComponent? containerManager = null)
where T : IContainer
where T : BaseContainer
{
if (!Resolve(uid, ref containerManager, false))
containerManager = EntityManager.AddComponent<ContainerManagerComponent>(uid);
@@ -88,12 +79,12 @@ namespace Robust.Shared.Containers
}
public T EnsureContainer<T>(EntityUid uid, string id, ContainerManagerComponent? containerManager = null)
where T : IContainer
where T : BaseContainer
{
return EnsureContainer<T>(uid, id, out _, containerManager);
}
public IContainer GetContainer(EntityUid uid, string id, ContainerManagerComponent? containerManager = null)
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));
@@ -109,7 +100,7 @@ namespace Robust.Shared.Containers
return containerManager.HasContainer(id);
}
public bool TryGetContainer(EntityUid uid, string id, [NotNullWhen(true)] out IContainer? container, ContainerManagerComponent? containerManager = null)
public bool TryGetContainer(EntityUid uid, string id, [NotNullWhen(true)] out BaseContainer? container, ContainerManagerComponent? containerManager = null)
{
if (Resolve(uid, ref containerManager, false))
return containerManager.TryGetContainer(id, out container);
@@ -118,7 +109,7 @@ namespace Robust.Shared.Containers
return false;
}
public bool TryGetContainingContainer(EntityUid uid, EntityUid containedUid, [NotNullWhen(true)] out IContainer? container, ContainerManagerComponent? containerManager = null, bool skipExistCheck = false)
public bool TryGetContainingContainer(EntityUid uid, EntityUid containedUid, [NotNullWhen(true)] out BaseContainer? container, ContainerManagerComponent? containerManager = null, bool skipExistCheck = false)
{
if (Resolve(uid, ref containerManager, false) && (skipExistCheck || EntityManager.EntityExists(containedUid)))
return containerManager.TryGetContainer(containedUid, out container);
@@ -164,7 +155,7 @@ namespace Robust.Shared.Containers
#region Container Helpers
public bool TryGetContainingContainer(EntityUid uid, [NotNullWhen(true)] out IContainer? container, MetaDataComponent? meta = null, TransformComponent? transform = null)
public bool TryGetContainingContainer(EntityUid uid, [NotNullWhen(true)] out BaseContainer? container, MetaDataComponent? meta = null, TransformComponent? transform = null)
{
container = null;
@@ -227,7 +218,7 @@ namespace Robust.Shared.Containers
}
/// <summary>
/// Finds the first instance of a component on the recursive parented containers that hold an entity
/// Finds the first instance of a component on the recursive parented containers that hold an entity
/// </summary>
public bool TryFindComponentOnEntityContainerOrParent<T>(
EntityUid uid,
@@ -255,7 +246,7 @@ namespace Robust.Shared.Containers
}
/// <summary>
/// Finds all instances of a component on the recursive parented containers that hold an entity
/// Finds all instances of a component on the recursive parented containers that hold an entity
/// </summary>
public bool TryFindComponentsOnEntityContainerOrParent<T>(
EntityUid uid,
@@ -335,8 +326,8 @@ namespace Robust.Shared.Containers
public bool IsInSameOrTransparentContainer(
EntityUid user,
EntityUid other,
IContainer? userContainer = null,
IContainer? otherContainer = null,
BaseContainer? userContainer = null,
BaseContainer? otherContainer = null,
bool userSeeInsideSelf = false)
{
if (userContainer == null)
@@ -371,14 +362,14 @@ namespace Robust.Shared.Containers
/// <summary>
/// Gets the top-most container in the hierarchy for this entity, if it exists.
/// </summary>
public bool TryGetOuterContainer(EntityUid uid, TransformComponent xform, [NotNullWhen(true)] out IContainer? container)
public bool TryGetOuterContainer(EntityUid uid, TransformComponent xform, [NotNullWhen(true)] out BaseContainer? container)
{
var xformQuery = EntityManager.GetEntityQuery<TransformComponent>();
return TryGetOuterContainer(uid, xform, out container, xformQuery);
}
public bool TryGetOuterContainer(EntityUid uid, TransformComponent xform,
[NotNullWhen(true)] out IContainer? container, EntityQuery<TransformComponent> xformQuery)
[NotNullWhen(true)] out BaseContainer? container, EntityQuery<TransformComponent> xformQuery)
{
container = null;
@@ -448,7 +439,7 @@ namespace Robust.Shared.Containers
/// Attempts to remove all entities in a container. Returns removed entities.
/// </summary>
public List<EntityUid> EmptyContainer(
IContainer container,
BaseContainer container,
bool force = false,
EntityCoordinates? destination = null,
bool reparent = true)
@@ -470,7 +461,7 @@ namespace Robust.Shared.Containers
/// <summary>
/// Attempts to remove and delete all entities in a container.
/// </summary>
public void CleanContainer(IContainer container)
public void CleanContainer(BaseContainer container)
{
foreach (var ent in container.ContainedEntities.ToArray())
{
@@ -491,7 +482,7 @@ namespace Robust.Shared.Containers
transform.AttachToGridOrMap();
}
private bool TryInsertIntoContainer(TransformComponent transform, IContainer container)
private bool TryInsertIntoContainer(TransformComponent transform, BaseContainer container)
{
if (container.Insert(transform.Owner)) return true;

View File

@@ -1,3 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
@@ -8,6 +9,8 @@ using Robust.Shared.GameObjects;
using Robust.Shared.GameStates;
using Robust.Shared.IoC;
using Robust.Shared.Map;
using Robust.Shared.Serialization;
using Robust.Shared.Timing;
using Robust.Shared.Utility;
// ReSharper disable AccessToStaticMemberViaDerivedType
@@ -64,10 +67,6 @@ namespace Robust.UnitTesting.Server.GameObjects.Components
Assert.That(() => manager.GetContainer("dummy3"), Throws.TypeOf<KeyNotFoundException>());
entManager.DeleteEntity(entity);
Assert.That(manager.Deleted, Is.True);
Assert.That(container.Deleted, Is.True);
Assert.That(container2.Deleted, Is.True);
}
[Test]
@@ -172,7 +171,7 @@ namespace Robust.UnitTesting.Server.GameObjects.Components
var container = containerSys.MakeContainer<Container>(entity, "dummy");
Assert.That(container.Insert(entity), Is.False);
Assert.That(container.CanInsert(entity), Is.False);
Assert.That(containerSys.CanInsert(entity, container), Is.False);
}
[Test]
@@ -186,7 +185,7 @@ namespace Robust.UnitTesting.Server.GameObjects.Components
var container = containerSys.MakeContainer<Container>(entity, "dummy");
Assert.That(container.Insert(mapEnt), Is.False);
Assert.That(container.CanInsert(mapEnt), Is.False);
Assert.That(containerSys.CanInsert(mapEnt, container), Is.False);
}
[Test]
@@ -200,7 +199,7 @@ namespace Robust.UnitTesting.Server.GameObjects.Components
var container = containerSys.MakeContainer<Container>(entity, "dummy");
Assert.That(container.Insert(grid), Is.False);
Assert.That(container.CanInsert(grid), Is.False);
Assert.That(containerSys.CanInsert(grid, container), Is.False);
}
[Test]
@@ -284,13 +283,14 @@ namespace Robust.UnitTesting.Server.GameObjects.Components
Assert.That(state.Containers, Has.Count.EqualTo(1));
var cont = state.Containers.Values.First();
Assert.That(cont.Id, Is.EqualTo("dummy"));
Assert.That(cont.ID, Is.EqualTo("dummy"));
Assert.That(cont.OccludesLight, Is.True);
Assert.That(cont.ShowContents, Is.True);
Assert.That(cont.ContainedEntities.Length, Is.EqualTo(1));
Assert.That(cont.ContainedEntities.Count, Is.EqualTo(1));
Assert.That(cont.ContainedEntities[0], Is.EqualTo(childEnt));
}
[Serializable, NetSerializable]
private sealed partial class ContainerOnlyContainer : BaseContainer
{
/// <summary>
@@ -299,13 +299,9 @@ namespace Robust.UnitTesting.Server.GameObjects.Components
private readonly List<EntityUid> _containerList = new();
private readonly List<EntityUid> _expectedEntities = new();
public override string ContainerType => nameof(ContainerOnlyContainer);
/// <inheritdoc />
public override IReadOnlyList<EntityUid> ContainedEntities => _containerList;
public override List<EntityUid> ExpectedEntities => _expectedEntities;
/// <inheritdoc />
protected override void InternalInsert(EntityUid toInsert, IEntityManager entMan)
{
@@ -324,6 +320,9 @@ namespace Robust.UnitTesting.Server.GameObjects.Components
if (!_containerList.Contains(contained))
return false;
if (IoCManager.Resolve<IGameTiming>().ApplyingState)
return true;
var flags = IoCManager.Resolve<IEntityManager>().GetComponent<MetaDataComponent>(contained).Flags;
DebugTools.Assert((flags & MetaDataFlags.InContainer) != 0);
return true;
@@ -341,10 +340,9 @@ namespace Robust.UnitTesting.Server.GameObjects.Components
}
}
public override bool CanInsert(EntityUid toinsert, IEntityManager? entMan = null)
protected internal override bool CanInsert(EntityUid toinsert, bool assumeEmpty, IEntityManager entMan)
{
IoCManager.Resolve(ref entMan);
return entMan.TryGetComponent(toinsert, out ContainerManagerComponent? _);
return entMan.HasComponent<ContainerManagerComponent>(toinsert);
}
}
}

View File

@@ -346,10 +346,10 @@ namespace Robust.UnitTesting.Shared.GameObjects
Assert.That(containerComp.Containers.ContainsKey("testContainer"));
var iContainer = containerComp.GetContainer("testContainer");
Assert.That(iContainer.ContainedEntities.Count, Is.EqualTo(1));
var BaseContainer = containerComp.GetContainer("testContainer");
Assert.That(BaseContainer.ContainedEntities.Count, Is.EqualTo(1));
var containeeEnt = iContainer.ContainedEntities[0];
var containeeEnt = BaseContainer.ContainedEntities[0];
Assert.That(entMan.GetComponent<MetaDataComponent>(containeeEnt).EntityName, Is.EqualTo("ContaineeEnt"));
});
}

View File

@@ -1,3 +1,4 @@
using System;
using System.Collections.Generic;
using System.Numerics;
using System.Threading.Tasks;
@@ -5,6 +6,7 @@ using NUnit.Framework;
using Robust.Shared.Containers;
using Robust.Shared.GameObjects;
using Robust.Shared.Map;
using Robust.Shared.Serialization;
namespace Robust.UnitTesting.Shared.Spawning;
@@ -100,18 +102,16 @@ public abstract partial class EntitySpawnHelpersTest : RobustIntegrationTest
/// <summary>
/// Simple container that can store up to 2 entities.
/// </summary>
[Serializable, NetSerializable]
private sealed partial class TestContainer : BaseContainer
{
private readonly List<EntityUid> _ents = new();
private readonly List<EntityUid> _expected = new();
public override string ContainerType => nameof(TestContainer);
public override IReadOnlyList<EntityUid> ContainedEntities => _ents;
public override List<EntityUid> ExpectedEntities => _expected;
protected override void InternalInsert(EntityUid toInsert, IEntityManager entMan) => _ents.Add(toInsert);
protected override void InternalRemove(EntityUid toRemove, IEntityManager entMan) => _ents.Remove(toRemove);
public override bool Contains(EntityUid contained) => _ents.Contains(contained);
protected override void InternalShutdown(IEntityManager entMan, bool isClient) { }
public override bool CanInsert(EntityUid toinsert, IEntityManager? entMan = null)
protected internal override bool CanInsert(EntityUid toinsert, bool assumeEmpty, IEntityManager entMan)
=> _ents.Count < 2 && !_ents.Contains(toinsert);
}
}