using Content.Server.GameObjects.EntitySystems; using Content.Server.Interfaces.GameObjects; using Content.Shared.GameObjects.Components.Storage; using SS14.Server.GameObjects; using SS14.Server.GameObjects.Components.Container; using SS14.Server.Interfaces.Player; using SS14.Server.Player; using SS14.Shared.Enums; using SS14.Shared.GameObjects; using SS14.Shared.Interfaces.GameObjects; using SS14.Shared.Interfaces.GameObjects.Components; using SS14.Shared.Interfaces.Network; using SS14.Shared.IoC; using SS14.Shared.Log; using SS14.Shared.Serialization; using System.Collections.Generic; using Content.Shared.Interfaces; using SS14.Shared.GameObjects.EntitySystemMessages; using SS14.Shared.ViewVariables; namespace Content.Server.GameObjects { /// /// Storage component for containing entities within this one, matches a UI on the client which shows stored entities /// public class ServerStorageComponent : SharedStorageComponent, IAttackBy, IUse, IActivate { private Container storage; private bool _storageInitialCalculated = false; private int StorageUsed = 0; private int StorageCapacityMax = 10000; public HashSet SubscribedSessions = new HashSet(); [ViewVariables(VVAccess.ReadWrite)] public bool Open { get => _open; set { if (_open == value) return; _open = value; Dirty(); } } public override void Initialize() { base.Initialize(); storage = ContainerManagerComponent.Ensure("storagebase", Owner); } /// public override ComponentState GetComponentState() { return new StorageComponentState(_open); } public override void ExposeData(ObjectSerializer serializer) { base.ExposeData(serializer); serializer.DataField(ref StorageCapacityMax, "Capacity", 10000); serializer.DataField(ref StorageUsed, "used", 0); } /// /// Removes from the storage container and updates the stored value /// /// /// public bool Remove(IEntity toremove) { _ensureInitialCalculated(); if (storage.Remove(toremove)) { Logger.InfoS("Storage", "Storage (UID {0}) had entity (UID {1}) removed from it.", Owner.Uid, toremove.Uid); StorageUsed -= toremove.GetComponent().ObjectSize; UpdateClientInventories(); return true; } return false; } /// /// Inserts into the storage container /// /// /// public bool Insert(IEntity toinsert) { if (CanInsert(toinsert) && storage.Insert(toinsert)) { Logger.InfoS("Storage", "Storage (UID {0}) had entity (UID {1}) inserted into it.", Owner.Uid, toinsert.Uid); StorageUsed += toinsert.GetComponent().ObjectSize; UpdateClientInventories(); return true; } return false; } /// /// Verifies the object can be inserted by checking if it is storeable and if it keeps under the capacity limit /// /// /// public bool CanInsert(IEntity toinsert) { _ensureInitialCalculated(); if (toinsert.TryGetComponent(out ServerStorageComponent storage)) { if (storage.StorageCapacityMax >= StorageCapacityMax) return false; } if (toinsert.TryGetComponent(out StoreableComponent store)) { if (store.ObjectSize > (StorageCapacityMax - StorageUsed)) return false; } return true; } /// /// Inserts storeable entities into this storage container if possible, otherwise return to the hand of the user /// /// /// /// bool IAttackBy.AttackBy(AttackByEventArgs eventArgs) { _ensureInitialCalculated(); Logger.DebugS("Storage", "Storage (UID {0}) attacked by user (UID {1}) with entity (UID {2}).", Owner.Uid, eventArgs.User.Uid, eventArgs.AttackWith.Uid); if (!eventArgs.User.TryGetComponent(out HandsComponent hands)) return false; //Check that we can drop the item from our hands first otherwise we obviously cant put it inside if (CanInsert(hands.GetActiveHand.Owner) && hands.Drop(hands.ActiveIndex)) { if (Insert(eventArgs.AttackWith)) { return true; } } else { Owner.PopupMessage(eventArgs.User, "Can't insert."); } return false; } /// /// Sends a message to open the storage UI /// /// /// bool IUse.UseEntity(UseEntityEventArgs eventArgs) { _ensureInitialCalculated(); var user_session = eventArgs.User.GetComponent().playerSession; Logger.DebugS("Storage", "Storage (UID {0}) \"used\" by player session (UID {1}).", Owner.Uid, user_session.AttachedEntityUid); SubscribeSession(user_session); SendNetworkMessage(new OpenStorageUIMessage(), user_session.ConnectedClient); UpdateClientInventory(user_session); return false; } /// /// Updates the storage UI on all subscribed actors, informing them of the state of the container. /// private void UpdateClientInventories() { foreach (IPlayerSession session in SubscribedSessions) { UpdateClientInventory(session); } } /// /// Adds actor to the update list. /// /// public void SubscribeSession(IPlayerSession session) { _ensureInitialCalculated(); if (!SubscribedSessions.Contains(session)) { Logger.DebugS("Storage", "Storage (UID {0}) subscribed player session (UID {1}).", Owner.Uid, session.AttachedEntityUid); session.PlayerStatusChanged += HandlePlayerSessionChangeEvent; SubscribedSessions.Add(session); UpdateDoorState(); } } /// /// Removes actor from the update list. /// /// public void UnsubscribeSession(IPlayerSession session) { if(SubscribedSessions.Contains(session)) { Logger.DebugS("Storage", "Storage (UID {0}) unsubscribed player session (UID {1}).", Owner.Uid, session.AttachedEntityUid); SubscribedSessions.Remove(session); SendNetworkMessage(new CloseStorageUIMessage(), session.ConnectedClient); UpdateDoorState(); } } private void UpdateDoorState() { Open = SubscribedSessions.Count != 0; } public void HandlePlayerSessionChangeEvent(object obj, SessionStatusEventArgs SSEA) { Logger.DebugS("Storage", "Storage (UID {0}) handled a status change in player session (UID {1}).", Owner.Uid, SSEA.Session.AttachedEntityUid); if (SSEA.NewStatus != SessionStatus.InGame) { UnsubscribeSession(SSEA.Session); } } /// /// Updates storage UI on a client, informing them of the state of the container. /// private void UpdateClientInventory(IPlayerSession session) { if (session.AttachedEntity == null) { Logger.DebugS("Storage", "Storage (UID {0}) detected no attached entity in player session (UID {1}).", Owner.Uid, session.AttachedEntityUid); UnsubscribeSession(session); return; } Dictionary storedentities = new Dictionary(); foreach (var entities in storage.ContainedEntities) { storedentities.Add(entities.Uid, entities.GetComponent().ObjectSize); } SendNetworkMessage(new StorageHeldItemsMessage(storedentities, StorageUsed, StorageCapacityMax), session.ConnectedClient); } /// /// Receives messages to remove entities from storage, verifies the player can do them, /// and puts the removed entity in hand or on the ground /// /// /// /// public override void HandleMessage(ComponentMessage message, INetChannel netChannel = null, IComponent component = null) { base.HandleMessage(message, netChannel, component); switch (message) { case RemoveEntityMessage _: { _ensureInitialCalculated(); var playerMan = IoCManager.Resolve(); var session = playerMan.GetSessionByChannel(netChannel); var playerentity = session.AttachedEntity; var ourtransform = Owner.GetComponent(); var playertransform = playerentity.GetComponent(); if (playertransform.GridPosition.InRange(ourtransform.GridPosition, 2) && (ourtransform.IsMapTransform || playertransform.ContainsEntity(ourtransform))) { var remove = (RemoveEntityMessage)message; var entity = IoCManager.Resolve().GetEntity(remove.EntityUid); if (entity != null && storage.Contains(entity)) { Remove(entity); var item = entity.GetComponent(); if (item != null && playerentity.TryGetComponent(out HandsComponent hands)) { if (hands.PutInHand(item)) return; } entity.GetComponent().WorldPosition = ourtransform.WorldPosition; } } } break; case CloseStorageUIMessage _: { var playerMan = IoCManager.Resolve(); var session = playerMan.GetSessionByChannel(netChannel); UnsubscribeSession(session); } break; } } /// void IActivate.Activate(ActivateEventArgs eventArgs) { ((IUse) this).UseEntity(new UseEntityEventArgs { User = eventArgs.User }); } private void _ensureInitialCalculated() { if (_storageInitialCalculated) { return; } StorageUsed = 0; foreach (var entity in storage.ContainedEntities) { var item = entity.GetComponent(); StorageUsed += item.ObjectSize; } _storageInitialCalculated = true; } public bool Attackby(AttackByEventArgs eventArgs) { throw new System.NotImplementedException(); } } }