using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; using Robust.Shared.GameObjects; using Robust.Shared.GameStates; using Robust.Shared.IoC; using Robust.Shared.Serialization; using Robust.Shared.Serialization.Manager; using Robust.Shared.Serialization.Manager.Attributes; using Robust.Shared.ViewVariables; namespace Robust.Shared.Containers { /// /// Holds data about a set of entity containers on this entity. /// [ComponentReference(typeof(IContainerManager))] [NetworkedComponent] [ComponentProtoName("ContainerContainer")] public sealed class ContainerManagerComponent : Component, IContainerManager, ISerializationHooks { [Dependency] private readonly IDynamicTypeFactoryInternal _dynFactory = default!; [ViewVariables] [DataField("containers")] public Dictionary Containers = new(); void ISerializationHooks.AfterDeserialization() { foreach (var (_, container) in Containers) { var baseContainer = (BaseContainer) container; baseContainer.Manager = this; } } /// protected override void OnRemove() { base.OnRemove(); // IContainer.Shutdown modifies the _containers collection foreach (var container in Containers.Values.ToArray()) { container.Shutdown(); } Containers.Clear(); } /// protected override void Initialize() { base.Initialize(); foreach (var container in Containers) { var baseContainer = (BaseContainer)container.Value; baseContainer.Manager = this; baseContainer.ID = container.Key; } } /// public override ComponentState GetComponentState() { // naive implementation that just sends the full state of the component List containerSet = new(Containers.Count); foreach (var container in 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 ContainerManagerComponentState.ContainerData(container.ContainerType, container.ID, container.ShowContents, container.OccludesLight, uidArr); containerSet.Add(sContainer); } return new ContainerManagerComponentState(containerSet); } /// public T MakeContainer(string id) where T : IContainer { return (T) MakeContainer(id, typeof(T)); } /// public IContainer GetContainer(string id) { return Containers[id]; } /// public bool HasContainer(string id) { return Containers.ContainsKey(id); } /// public bool TryGetContainer(string id, [NotNullWhen(true)] out IContainer? container) { var ret = Containers.TryGetValue(id, out var cont); container = cont!; return ret; } /// public bool TryGetContainer(EntityUid entity, [NotNullWhen(true)] out IContainer? container) { foreach (var contain in Containers.Values) { if (!contain.Deleted && contain.Contains(entity)) { container = contain; return true; } } container = default; return false; } /// public bool ContainsEntity(EntityUid entity) { foreach (var container in Containers.Values) { if (!container.Deleted && container.Contains(entity)) return true; } return false; } /// public void ForceRemove(EntityUid entity) { foreach (var container in Containers.Values) { if (container.Contains(entity)) container.ForceRemove(entity); } } /// public void InternalContainerShutdown(IContainer container) { Containers.Remove(container.ID); } /// public bool Remove(EntityUid entity) { foreach (var containers in Containers.Values) { if (containers.Contains(entity)) return containers.Remove(entity); } return true; // If we don't contain the entity, it will always be removed } /// protected override void Shutdown() { base.Shutdown(); // On shutdown we won't get to process remove events in the containers so this has to be manually done. var entMan = IoCManager.Resolve(); foreach (var container in Containers.Values) { foreach (var containerEntity in container.ContainedEntities) { entMan.EventBus.RaiseEvent(EventSource.Local, new UpdateContainerOcclusionMessage(containerEntity)); } } } private IContainer MakeContainer(string id, Type type) { if (HasContainer(id)) throw new ArgumentException($"Container with specified ID already exists: '{id}'"); var container = _dynFactory.CreateInstanceUnchecked(type); container.ID = id; container.Manager = this; Containers[id] = container; Dirty(); return container; } public AllContainersEnumerable GetAllContainers() { return new(this); } [Serializable, NetSerializable] internal sealed class ContainerManagerComponentState : ComponentState { public List ContainerSet; public ContainerManagerComponentState(List containers) { ContainerSet = 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 struct ContainerPrototypeData : IPopulateDefaultValues { [DataField("entities")] public List Entities; [DataField("type")] public string? Type; public ContainerPrototypeData(List entities, string type) { Entities = entities; Type = type; } public void PopulateDefaultValues() { Entities = new List(); } } public readonly struct AllContainersEnumerable : IEnumerable { private readonly ContainerManagerComponent? _manager; public AllContainersEnumerable(ContainerManagerComponent? manager) { _manager = manager; } public AllContainersEnumerator GetEnumerator() { return new(_manager); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } } public struct AllContainersEnumerator : IEnumerator { private Dictionary.ValueCollection.Enumerator _enumerator; public AllContainersEnumerator(ContainerManagerComponent? manager) { _enumerator = manager?.Containers.Values.GetEnumerator() ?? new(); Current = default; } public bool MoveNext() { while (_enumerator.MoveNext()) { if (!_enumerator.Current.Deleted) { Current = _enumerator.Current; return true; } } return false; } void IEnumerator.Reset() { ((IEnumerator) _enumerator).Reset(); } [AllowNull] public IContainer Current { get; private set; } object IEnumerator.Current => Current; public void Dispose() { } } } }