mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-09-07 02:08:17 +02:00
Added some debug assertions to Container. These should help verify that a deleted container is not being accessed.
245 lines
7.9 KiB
C#
245 lines
7.9 KiB
C#
using System.Collections.Generic;
|
|
using JetBrains.Annotations;
|
|
using Robust.Server.GameObjects.EntitySystemMessages;
|
|
using Robust.Shared.Interfaces.GameObjects;
|
|
using Robust.Server.Interfaces.GameObjects;
|
|
using Robust.Shared.GameObjects;
|
|
using Robust.Shared.Utility;
|
|
using Robust.Shared.ViewVariables;
|
|
|
|
namespace Robust.Server.GameObjects.Components.Container
|
|
{
|
|
/// <summary>
|
|
/// Default implementation for containers,
|
|
/// cannot be inherited. If additional logic is needed,
|
|
/// this logic should go on the systems that are holding this container.
|
|
/// For example, inventory containers should be modified only through an inventory component.
|
|
/// </summary>
|
|
[UsedImplicitly]
|
|
public sealed class Container : BaseContainer
|
|
{
|
|
/// <summary>
|
|
/// The generic container class uses a list of entities
|
|
/// </summary>
|
|
private readonly List<IEntity> _containerList = new List<IEntity>();
|
|
|
|
/// <inheritdoc />
|
|
public Container(string id, IContainerManager manager) : base(id, manager) { }
|
|
|
|
/// <inheritdoc />
|
|
public override IReadOnlyCollection<IEntity> ContainedEntities => _containerList.AsReadOnly();
|
|
|
|
/// <inheritdoc />
|
|
protected override void InternalInsert(IEntity toinsert)
|
|
{
|
|
_containerList.Add(toinsert);
|
|
base.InternalInsert(toinsert);
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
protected override void InternalRemove(IEntity toremove)
|
|
{
|
|
_containerList.Remove(toremove);
|
|
base.InternalRemove(toremove);
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public override bool Contains(IEntity contained)
|
|
{
|
|
return _containerList.Contains(contained);
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public override void Shutdown()
|
|
{
|
|
base.Shutdown();
|
|
|
|
foreach (var entity in _containerList)
|
|
{
|
|
entity.Delete();
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Base container class that all container inherit from.
|
|
/// </summary>
|
|
public abstract class BaseContainer : IContainer
|
|
{
|
|
/// <inheritdoc />
|
|
public IContainerManager Manager { get; private set; }
|
|
|
|
/// <inheritdoc />
|
|
[ViewVariables]
|
|
public string ID { get; }
|
|
|
|
/// <inheritdoc />
|
|
[ViewVariables]
|
|
public IEntity Owner => Manager?.Owner;
|
|
|
|
/// <inheritdoc />
|
|
[ViewVariables]
|
|
public bool Deleted { get; private set; }
|
|
|
|
/// <inheritdoc />
|
|
public abstract IReadOnlyCollection<IEntity> ContainedEntities { get; }
|
|
|
|
/// <summary>
|
|
/// DO NOT CALL THIS METHOD DIRECTLY!
|
|
/// You want <see cref="IContainerManager.MakeContainer{T}(string)" /> instead.
|
|
/// </summary>
|
|
protected BaseContainer(string id, IContainerManager manager)
|
|
{
|
|
DebugTools.Assert(!string.IsNullOrEmpty(id));
|
|
DebugTools.Assert(manager != null);
|
|
|
|
ID = id;
|
|
Manager = manager;
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public bool Insert(IEntity toinsert)
|
|
{
|
|
DebugTools.Assert(!Deleted);
|
|
|
|
//Verify we can insert and that the object got properly removed from its current location
|
|
if (!CanInsert(toinsert))
|
|
return false;
|
|
|
|
var transform = toinsert.Transform;
|
|
// The transform.Parent.Owner != Owner is there because map deserialization of containers still uses Insert()
|
|
// In which case the child is already parented. To us. Don't reject him hand him to the orphanage.
|
|
// Perhaps making it not use Insert() is a good idea but eh.
|
|
if (!transform.IsMapTransform && transform.Parent.Owner != Owner && !transform.Parent.Owner.GetComponent<IContainerManager>().Remove(toinsert))
|
|
{
|
|
// Can't detach the entity from its parent, can't insert.
|
|
return false;
|
|
}
|
|
InternalInsert(toinsert);
|
|
transform.AttachParent(Owner.Transform);
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Implement to store the reference in whatever form you want
|
|
/// </summary>
|
|
/// <param name="toinsert"></param>
|
|
protected virtual void InternalInsert(IEntity toinsert)
|
|
{
|
|
DebugTools.Assert(!Deleted);
|
|
|
|
Owner.EntityManager.RaiseEvent(Owner, new EntInsertedIntoContainerMessage(toinsert, this));
|
|
Manager.Owner.SendMessage(Manager, new ContainerContentsModifiedMessage(this, toinsert, false));
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public virtual bool CanInsert(IEntity toinsert)
|
|
{
|
|
DebugTools.Assert(!Deleted);
|
|
|
|
// cannot insert into itself.
|
|
if (Owner == toinsert)
|
|
return false;
|
|
|
|
// Crucial, prevent circular insertion.
|
|
return !toinsert.Transform.ContainsEntity(Owner.Transform);
|
|
|
|
//Improvement: Traverse the entire tree to make sure we are not creating a loop.
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public bool Remove(IEntity toremove)
|
|
{
|
|
DebugTools.Assert(!Deleted);
|
|
|
|
if (toremove == null)
|
|
return true;
|
|
|
|
if (!CanRemove(toremove))
|
|
{
|
|
return false;
|
|
}
|
|
InternalRemove(toremove);
|
|
|
|
if (!toremove.IsValid())
|
|
return true;
|
|
|
|
toremove.Transform.DetachParent();
|
|
return true;
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public void ForceRemove(IEntity toRemove)
|
|
{
|
|
DebugTools.Assert(!Deleted);
|
|
|
|
InternalRemove(toRemove);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Implement to remove the reference you used to store the entity
|
|
/// </summary>
|
|
/// <param name="toremove"></param>
|
|
protected virtual void InternalRemove(IEntity toremove)
|
|
{
|
|
DebugTools.Assert(!Deleted);
|
|
DebugTools.Assert(Manager != null);
|
|
DebugTools.Assert(toremove != null && toremove.IsValid());
|
|
|
|
Owner?.EntityManager.RaiseEvent(Owner, new EntRemovedFromContainerMessage(toremove, this));
|
|
Manager.Owner.SendMessage(Manager, new ContainerContentsModifiedMessage(this, toremove, true));
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public virtual bool CanRemove(IEntity toremove)
|
|
{
|
|
DebugTools.Assert(!Deleted);
|
|
return Contains(toremove);
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public abstract bool Contains(IEntity contained);
|
|
|
|
/// <inheritdoc />
|
|
public virtual void Shutdown()
|
|
{
|
|
Deleted = true;
|
|
Manager = null;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// The contents of this container have been changed.
|
|
/// </summary>
|
|
public class ContainerContentsModifiedMessage : ComponentMessage
|
|
{
|
|
/// <summary>
|
|
/// Container whose contents were modified.
|
|
/// </summary>
|
|
public IContainer Container { get; }
|
|
|
|
/// <summary>
|
|
/// Entity that was added or removed from the container.
|
|
/// </summary>
|
|
public IEntity Entity { get; }
|
|
|
|
/// <summary>
|
|
/// If true, the entity was removed. If false, it was added to the container.
|
|
/// </summary>
|
|
public bool Removed { get; }
|
|
|
|
/// <summary>
|
|
/// Constructs a new instance of <see cref="ContainerContentsModifiedMessage"/>.
|
|
/// </summary>
|
|
/// <param name="container">Container whose contents were modified.</param>
|
|
/// <param name="entity">Entity that was added or removed in the container.</param>
|
|
/// <param name="removed">If true, the entity was removed. If false, it was added to the container.</param>
|
|
public ContainerContentsModifiedMessage(IContainer container, IEntity entity, bool removed)
|
|
{
|
|
Container = container;
|
|
Entity = entity;
|
|
Removed = removed;
|
|
}
|
|
}
|
|
}
|