2020-07-17 11:03:07 +02:00
#nullable enable
using System ;
2020-04-20 10:36:02 +01:00
using System.Collections.Generic ;
2019-07-31 15:02:36 +02:00
using System.Linq ;
2020-08-18 14:39:08 +02:00
using System.Threading.Tasks ;
2020-07-25 15:11:16 +02:00
using Content.Server.GameObjects.Components.GUI ;
using Content.Server.Interfaces.GameObjects.Components.Items ;
2018-04-22 06:11:38 -05:00
using Content.Shared.GameObjects.Components.Storage ;
2020-07-23 01:41:22 +02:00
using Content.Shared.GameObjects.EntitySystems ;
2019-07-31 15:02:36 +02:00
using Content.Shared.Interfaces ;
2020-07-18 22:51:56 -07:00
using Content.Shared.Interfaces.GameObjects.Components ;
2020-08-30 11:37:06 +02:00
using Content.Shared.Utility ;
2019-04-15 21:11:38 -06:00
using Robust.Server.GameObjects ;
using Robust.Server.GameObjects.Components.Container ;
2019-07-31 15:02:36 +02:00
using Robust.Server.GameObjects.EntitySystemMessages ;
2019-04-15 21:11:38 -06:00
using Robust.Server.Interfaces.Player ;
using Robust.Server.Player ;
using Robust.Shared.Enums ;
using Robust.Shared.GameObjects ;
using Robust.Shared.Interfaces.GameObjects ;
2019-07-31 15:02:36 +02:00
using Robust.Shared.Interfaces.Map ;
2019-04-15 21:11:38 -06:00
using Robust.Shared.Interfaces.Network ;
using Robust.Shared.IoC ;
using Robust.Shared.Log ;
2020-04-20 10:36:02 +01:00
using Robust.Shared.Players ;
2019-04-15 21:11:38 -06:00
using Robust.Shared.Serialization ;
2020-09-08 13:30:22 +02:00
using Robust.Shared.ViewVariables ;
2018-04-22 06:11:38 -05:00
2020-07-17 11:03:07 +02:00
namespace Content.Server.GameObjects.Components.Items.Storage
2018-04-22 06:11:38 -05:00
{
/// <summary>
/// Storage component for containing entities within this one, matches a UI on the client which shows stored entities
/// </summary>
2019-07-31 15:02:36 +02:00
[RegisterComponent]
[ComponentReference(typeof(IActivate))]
[ComponentReference(typeof(IStorageComponent))]
2020-10-14 15:24:07 +02:00
public class ServerStorageComponent : SharedStorageComponent , IInteractUsing , IUse , IActivate , IStorageComponent , IDestroyAct , IExAct
2018-04-22 06:11:38 -05:00
{
2020-07-17 11:03:07 +02:00
[Dependency] private readonly IEntityManager _entityManager = default ! ;
2019-04-20 16:20:18 -07:00
2020-07-17 11:03:07 +02:00
private const string LoggerName = "Storage" ;
2018-04-22 06:11:38 -05:00
2020-07-17 11:03:07 +02:00
private Container ? _storage ;
2018-04-22 06:11:38 -05:00
2020-07-26 08:25:53 -04:00
private bool _occludesLight ;
2020-07-17 11:03:07 +02:00
private bool _storageInitialCalculated ;
private int _storageUsed ;
2020-07-18 19:37:17 -06:00
private int _storageCapacityMax ;
2020-07-17 11:03:07 +02:00
public readonly HashSet < IPlayerSession > SubscribedSessions = new HashSet < IPlayerSession > ( ) ;
2020-05-05 00:39:15 +02:00
2020-09-08 13:30:22 +02:00
[ViewVariables]
2020-10-14 15:24:07 +02:00
public override IReadOnlyList < IEntity > ? StoredEntities = > _storage ? . ContainedEntities ;
2020-07-17 11:03:07 +02:00
2020-09-14 00:04:00 +12:00
[ViewVariables(VVAccess.ReadWrite)]
2020-07-26 08:25:53 -04:00
public bool OccludesLight
{
get = > _occludesLight ;
set
{
_occludesLight = value ;
if ( _storage ! = null ) _storage . OccludesLight = value ;
}
}
2020-07-17 11:03:07 +02:00
private void EnsureInitialCalculated ( )
2018-04-22 06:11:38 -05:00
{
2020-07-17 11:03:07 +02:00
if ( _storageInitialCalculated )
{
return ;
}
2018-04-22 06:11:38 -05:00
2020-07-17 11:03:07 +02:00
RecalculateStorageUsed ( ) ;
_storageInitialCalculated = true ;
2018-04-22 06:11:38 -05:00
}
2018-11-11 20:04:52 +01:00
2020-07-17 11:03:07 +02:00
private void RecalculateStorageUsed ( )
2018-04-22 06:11:38 -05:00
{
2020-07-17 11:03:07 +02:00
_storageUsed = 0 ;
2018-04-22 06:11:38 -05:00
2020-07-17 11:03:07 +02:00
if ( _storage = = null )
{
return ;
}
foreach ( var entity in _storage . ContainedEntities )
{
var item = entity . GetComponent < StorableComponent > ( ) ;
2020-10-14 15:24:07 +02:00
_storageUsed + = item . Size ;
2020-07-17 11:03:07 +02:00
}
2018-04-22 06:11:38 -05:00
}
/// <summary>
2020-07-17 11:03:07 +02:00
/// Verifies if an entity can be stored and if it fits
2018-04-22 06:11:38 -05:00
/// </summary>
2020-07-17 11:03:07 +02:00
/// <param name="entity">The entity to check</param>
/// <returns>true if it can be inserted, false otherwise</returns>
public bool CanInsert ( IEntity entity )
2018-04-22 06:11:38 -05:00
{
2020-07-17 11:03:07 +02:00
EnsureInitialCalculated ( ) ;
2019-05-05 13:09:21 +02:00
2020-08-20 16:48:00 +02:00
if ( entity . TryGetComponent ( out ServerStorageComponent ? storage ) & &
2020-07-17 11:03:07 +02:00
storage . _storageCapacityMax > = _storageCapacityMax )
2018-04-22 06:11:38 -05:00
{
2020-07-17 11:03:07 +02:00
return false ;
2018-04-22 06:11:38 -05:00
}
2019-05-05 13:09:21 +02:00
2020-08-20 16:48:00 +02:00
if ( entity . TryGetComponent ( out StorableComponent ? store ) & &
2020-10-14 15:24:07 +02:00
store . Size > _storageCapacityMax - _storageUsed )
2020-07-17 11:03:07 +02:00
{
return false ;
}
return true ;
2018-04-22 06:11:38 -05:00
}
/// <summary>
2020-07-17 11:03:07 +02:00
/// Inserts into the storage container
2018-04-22 06:11:38 -05:00
/// </summary>
2020-07-17 11:03:07 +02:00
/// <param name="entity">The entity to insert</param>
/// <returns>true if the entity was inserted, false otherwise</returns>
public bool Insert ( IEntity entity )
{
return CanInsert ( entity ) & & _storage ? . Insert ( entity ) = = true ;
}
2020-10-14 15:24:07 +02:00
public override bool Remove ( IEntity entity )
2018-04-22 06:11:38 -05:00
{
2020-07-17 11:03:07 +02:00
EnsureInitialCalculated ( ) ;
return _storage ? . Remove ( entity ) = = true ;
2019-05-05 13:09:21 +02:00
}
2020-07-17 11:03:07 +02:00
public void HandleEntityMaybeInserted ( EntInsertedIntoContainerMessage message )
2019-05-05 13:09:21 +02:00
{
2020-07-17 11:03:07 +02:00
if ( message . Container ! = _storage )
2018-04-22 06:11:38 -05:00
{
2019-05-05 13:09:21 +02:00
return ;
2018-04-22 06:11:38 -05:00
}
2019-05-05 13:09:21 +02:00
2020-07-17 11:03:07 +02:00
EnsureInitialCalculated ( ) ;
Logger . DebugS ( LoggerName , $"Storage (UID {Owner.Uid}) had entity (UID {message.Entity.Uid}) inserted into it." ) ;
2020-10-14 15:24:07 +02:00
_storageUsed + = message . Entity . GetComponent < StorableComponent > ( ) . Size ;
2020-07-17 11:03:07 +02:00
2019-05-05 13:09:21 +02:00
UpdateClientInventories ( ) ;
2018-04-22 06:11:38 -05:00
}
2020-07-17 11:03:07 +02:00
public void HandleEntityMaybeRemoved ( EntRemovedFromContainerMessage message )
2018-04-22 06:11:38 -05:00
{
2020-07-17 11:03:07 +02:00
if ( message . Container ! = _storage )
2019-03-30 07:56:29 -05:00
{
2020-07-17 11:03:07 +02:00
return ;
2019-03-30 07:56:29 -05:00
}
2020-07-17 11:03:07 +02:00
EnsureInitialCalculated ( ) ;
Logger . DebugS ( LoggerName , $"Storage (UID {Owner.Uid}) had entity (UID {message.Entity.Uid}) removed from it." ) ;
2020-08-20 16:48:00 +02:00
if ( ! message . Entity . TryGetComponent ( out StorableComponent ? storable ) )
2018-04-22 06:11:38 -05:00
{
2020-07-17 11:03:07 +02:00
Logger . WarningS ( LoggerName , $"Removed entity {message.Entity.Uid} without a StorableComponent from storage {Owner.Uid} at {Owner.Transform.MapPosition}" ) ;
RecalculateStorageUsed ( ) ;
return ;
2018-04-22 06:11:38 -05:00
}
2020-07-17 11:03:07 +02:00
2020-10-14 15:24:07 +02:00
_storageUsed - = storable . Size ;
2020-07-17 11:03:07 +02:00
UpdateClientInventories ( ) ;
2018-04-22 06:11:38 -05:00
}
/// <summary>
2020-07-17 11:03:07 +02:00
/// Inserts an entity into storage from the player's active hand
2018-04-22 06:11:38 -05:00
/// </summary>
2020-07-17 11:03:07 +02:00
/// <param name="player">The player to insert an entity from</param>
/// <returns>true if inserted, false otherwise</returns>
public bool PlayerInsertEntity ( IEntity player )
2018-04-22 06:11:38 -05:00
{
2020-07-17 11:03:07 +02:00
EnsureInitialCalculated ( ) ;
2018-12-13 05:49:05 -08:00
2020-08-20 16:48:00 +02:00
if ( ! player . TryGetComponent ( out IHandsComponent ? hands ) | |
2020-07-17 11:03:07 +02:00
hands . GetActiveHand = = null )
2019-04-17 23:26:00 +02:00
{
return false ;
}
2020-07-17 11:03:07 +02:00
var toInsert = hands . GetActiveHand ;
2020-05-23 02:27:31 -07:00
2020-07-17 11:03:07 +02:00
if ( ! hands . Drop ( toInsert . Owner ) )
{
Owner . PopupMessage ( player , "Can't insert." ) ;
return false ;
}
2020-05-23 02:27:31 -07:00
2020-07-17 11:03:07 +02:00
if ( ! Insert ( toInsert . Owner ) )
{
hands . PutInHand ( toInsert ) ;
Owner . PopupMessage ( player , "Can't insert." ) ;
return false ;
}
return true ;
}
2018-04-22 06:11:38 -05:00
/// <summary>
2020-07-17 11:03:07 +02:00
/// Opens the storage UI for an entity
2018-04-22 06:11:38 -05:00
/// </summary>
2020-07-17 11:03:07 +02:00
/// <param name="entity">The entity to open the UI for</param>
public void OpenStorageUI ( IEntity entity )
2018-04-22 06:11:38 -05:00
{
2020-07-17 11:03:07 +02:00
EnsureInitialCalculated ( ) ;
2019-07-31 16:38:24 -07:00
2020-07-17 11:03:07 +02:00
var userSession = entity . GetComponent < BasicActorComponent > ( ) . playerSession ;
Logger . DebugS ( LoggerName , $"Storage (UID {Owner.Uid}) \" used \ " by player session (UID {userSession.AttachedEntityUid})." ) ;
SubscribeSession ( userSession ) ;
SendNetworkMessage ( new OpenStorageUIMessage ( ) , userSession . ConnectedClient ) ;
UpdateClientInventory ( userSession ) ;
2018-04-22 06:11:38 -05:00
}
/// <summary>
2020-07-17 11:03:07 +02:00
/// Updates the storage UI on all subscribed actors, informing them of the state of the container.
2018-04-22 06:11:38 -05:00
/// </summary>
2018-06-20 16:49:13 -04:00
private void UpdateClientInventories ( )
2018-04-22 06:11:38 -05:00
{
2020-07-17 11:03:07 +02:00
foreach ( var session in SubscribedSessions )
2018-06-20 16:49:13 -04:00
{
UpdateClientInventory ( session ) ;
}
}
/// <summary>
2020-07-17 11:03:07 +02:00
/// Updates storage UI on a client, informing them of the state of the container.
/// </summary>
/// <param name="session">The client to be updated</param>
private void UpdateClientInventory ( IPlayerSession session )
{
if ( session . AttachedEntity = = null )
{
Logger . DebugS ( LoggerName , $"Storage (UID {Owner.Uid}) detected no attached entity in player session (UID {session.AttachedEntityUid})." ) ;
UnsubscribeSession ( session ) ;
return ;
}
if ( _storage = = null )
{
Logger . WarningS ( LoggerName , $"{nameof(UpdateClientInventory)} called with null {nameof(_storage)}" ) ;
return ;
}
2020-10-14 15:24:07 +02:00
if ( StoredEntities = = null )
2020-07-17 11:03:07 +02:00
{
2020-10-14 15:24:07 +02:00
Logger . WarningS ( LoggerName , $"{nameof(UpdateClientInventory)} called with null {nameof(StoredEntities)}" ) ;
return ;
2020-07-17 11:03:07 +02:00
}
2020-10-14 15:24:07 +02:00
var stored = StoredEntities . Select ( e = > e . Uid ) . ToArray ( ) ;
SendNetworkMessage ( new StorageHeldItemsMessage ( stored , _storageUsed , _storageCapacityMax ) , session . ConnectedClient ) ;
2020-07-17 11:03:07 +02:00
}
/// <summary>
/// Adds a session to the update list.
2018-06-20 16:49:13 -04:00
/// </summary>
2020-07-17 11:03:07 +02:00
/// <param name="session">The session to add</param>
private void SubscribeSession ( IPlayerSession session )
2018-06-20 16:49:13 -04:00
{
2020-07-17 11:03:07 +02:00
EnsureInitialCalculated ( ) ;
2018-06-20 16:49:13 -04:00
if ( ! SubscribedSessions . Contains ( session ) )
{
2020-07-17 11:03:07 +02:00
Logger . DebugS ( LoggerName , $"Storage (UID {Owner.Uid}) subscribed player session (UID {session.AttachedEntityUid})." ) ;
2018-06-20 16:49:13 -04:00
session . PlayerStatusChanged + = HandlePlayerSessionChangeEvent ;
SubscribedSessions . Add ( session ) ;
2020-07-17 11:03:07 +02:00
2018-10-25 17:35:34 -07:00
UpdateDoorState ( ) ;
2018-06-20 16:49:13 -04:00
}
}
/// <summary>
2020-07-17 11:03:07 +02:00
/// Removes a session from the update list.
2018-06-20 16:49:13 -04:00
/// </summary>
2020-07-17 11:03:07 +02:00
/// <param name="session">The session to remove</param>
2018-06-20 16:49:13 -04:00
public void UnsubscribeSession ( IPlayerSession session )
{
2020-07-17 11:03:07 +02:00
if ( SubscribedSessions . Contains ( session ) )
2018-10-25 17:35:34 -07:00
{
2020-07-17 11:03:07 +02:00
Logger . DebugS ( LoggerName , $"Storage (UID {Owner.Uid}) unsubscribed player session (UID {session.AttachedEntityUid})." ) ;
2018-10-25 17:35:34 -07:00
SubscribedSessions . Remove ( session ) ;
SendNetworkMessage ( new CloseStorageUIMessage ( ) , session . ConnectedClient ) ;
2020-07-17 11:03:07 +02:00
2018-10-25 17:35:34 -07:00
UpdateDoorState ( ) ;
}
}
2020-07-17 11:03:07 +02:00
private void HandlePlayerSessionChangeEvent ( object? obj , SessionStatusEventArgs sessionStatus )
{
Logger . DebugS ( LoggerName , $"Storage (UID {Owner.Uid}) handled a status change in player session (UID {sessionStatus.Session.AttachedEntityUid})." ) ;
if ( sessionStatus . NewStatus ! = SessionStatus . InGame )
{
UnsubscribeSession ( sessionStatus . Session ) ;
}
}
2018-10-25 17:35:34 -07:00
private void UpdateDoorState ( )
{
2020-08-20 16:48:00 +02:00
if ( Owner . TryGetComponent ( out AppearanceComponent ? appearance ) )
2019-05-05 18:52:06 +02:00
{
appearance . SetData ( StorageVisuals . Open , SubscribedSessions . Count ! = 0 ) ;
}
2018-06-20 16:49:13 -04:00
}
2020-07-17 11:03:07 +02:00
public override void Initialize ( )
2018-06-20 16:49:13 -04:00
{
2020-07-17 11:03:07 +02:00
base . Initialize ( ) ;
// ReSharper disable once StringLiteralTypo
_storage = ContainerManagerComponent . Ensure < Container > ( "storagebase" , Owner ) ;
2020-07-26 08:25:53 -04:00
_storage . OccludesLight = _occludesLight ;
2018-06-20 16:49:13 -04:00
}
2020-07-17 11:03:07 +02:00
public override void ExposeData ( ObjectSerializer serializer )
2018-06-20 16:49:13 -04:00
{
2020-07-17 11:03:07 +02:00
base . ExposeData ( serializer ) ;
2020-07-18 19:37:17 -06:00
serializer . DataField ( ref _storageCapacityMax , "capacity" , 10000 ) ;
2020-07-26 08:25:53 -04:00
serializer . DataField ( ref _occludesLight , "occludesLight" , true ) ;
2020-07-17 11:03:07 +02:00
//serializer.DataField(ref StorageUsed, "used", 0);
2018-04-22 06:11:38 -05:00
}
2020-07-17 11:03:07 +02:00
public override void HandleNetworkMessage ( ComponentMessage message , INetChannel channel , ICommonSession ? session = null )
2018-04-22 06:11:38 -05:00
{
2020-04-20 10:36:02 +01:00
base . HandleNetworkMessage ( message , channel , session ) ;
if ( session = = null )
{
throw new ArgumentException ( nameof ( session ) ) ;
}
2018-04-22 06:11:38 -05:00
switch ( message )
{
2020-07-17 11:03:07 +02:00
case RemoveEntityMessage remove :
2018-10-25 17:35:34 -07:00
{
2020-07-17 11:03:07 +02:00
EnsureInitialCalculated ( ) ;
var player = session . AttachedEntity ;
if ( player = = null )
{
break ;
}
var ownerTransform = Owner . Transform ;
var playerTransform = player . Transform ;
2020-09-06 16:11:53 +02:00
if ( ! playerTransform . Coordinates . InRange ( _entityManager , ownerTransform . Coordinates , 2 ) | |
2020-07-17 11:03:07 +02:00
! ownerTransform . IsMapTransform & &
! playerTransform . ContainsEntity ( ownerTransform ) )
{
break ;
}
var entity = _entityManager . GetEntity ( remove . EntityUid ) ;
2018-04-22 06:11:38 -05:00
2020-07-17 11:03:07 +02:00
if ( entity = = null | | _storage ? . Contains ( entity ) = = false )
{
break ;
}
var item = entity . GetComponent < ItemComponent > ( ) ;
if ( item = = null | |
2020-08-20 16:48:00 +02:00
! player . TryGetComponent ( out HandsComponent ? hands ) )
2020-07-17 11:03:07 +02:00
{
break ;
}
2018-04-22 06:11:38 -05:00
2020-07-18 14:59:22 +02:00
if ( ! hands . CanPutInHand ( item ) )
2018-04-22 06:11:38 -05:00
{
2020-07-17 11:03:07 +02:00
break ;
2018-04-22 06:11:38 -05:00
}
2020-07-17 11:03:07 +02:00
hands . PutInHand ( item ) ;
2018-10-25 17:35:34 -07:00
break ;
2019-07-31 16:38:24 -07:00
}
2020-02-17 01:19:35 +02:00
case InsertEntityMessage _ :
{
2020-07-17 11:03:07 +02:00
EnsureInitialCalculated ( ) ;
var player = session . AttachedEntity ;
if ( player = = null )
2020-02-17 01:19:35 +02:00
{
2020-07-17 11:03:07 +02:00
break ;
2020-02-17 01:19:35 +02:00
}
2020-08-30 11:37:06 +02:00
if ( ! player . InRangeUnobstructed ( Owner , popup : true ) )
2020-07-17 11:03:07 +02:00
{
break ;
}
PlayerInsertEntity ( player ) ;
2020-02-17 01:19:35 +02:00
break ;
}
2018-10-25 17:35:34 -07:00
case CloseStorageUIMessage _ :
{
2020-07-17 11:03:07 +02:00
if ( ! ( session is IPlayerSession playerSession ) )
{
break ;
}
UnsubscribeSession ( playerSession ) ;
2018-04-22 06:11:38 -05:00
break ;
2019-07-31 16:38:24 -07:00
}
2018-04-22 06:11:38 -05:00
}
}
2018-08-22 01:19:47 -07:00
2020-07-17 11:03:07 +02:00
/// <summary>
/// Inserts storable entities into this storage container if possible, otherwise return to the hand of the user
/// </summary>
/// <param name="eventArgs"></param>
/// <returns>true if inserted, false otherwise</returns>
2020-08-18 14:39:08 +02:00
async Task < bool > IInteractUsing . InteractUsing ( InteractUsingEventArgs eventArgs )
2018-08-22 01:19:47 -07:00
{
2020-07-17 11:03:07 +02:00
Logger . DebugS ( LoggerName , $"Storage (UID {Owner.Uid}) attacked by user (UID {eventArgs.User.Uid}) with entity (UID {eventArgs.Using.Uid})." ) ;
2018-11-11 20:04:52 +01:00
2020-07-17 11:03:07 +02:00
if ( Owner . HasComponent < PlaceableSurfaceComponent > ( ) )
2018-11-11 20:04:52 +01:00
{
2020-07-17 11:03:07 +02:00
return false ;
2018-11-11 20:04:52 +01:00
}
2020-07-17 11:03:07 +02:00
return PlayerInsertEntity ( eventArgs . User ) ;
}
2019-05-05 13:09:21 +02:00
2020-07-17 11:03:07 +02:00
/// <summary>
/// Sends a message to open the storage UI
/// </summary>
/// <param name="eventArgs"></param>
/// <returns></returns>
bool IUse . UseEntity ( UseEntityEventArgs eventArgs )
{
EnsureInitialCalculated ( ) ;
OpenStorageUI ( eventArgs . User ) ;
return false ;
}
2018-11-11 20:04:52 +01:00
2020-07-17 11:03:07 +02:00
void IActivate . Activate ( ActivateEventArgs eventArgs )
{
( ( IUse ) this ) . UseEntity ( new UseEntityEventArgs { User = eventArgs . User } ) ;
2018-11-11 20:04:52 +01:00
}
2019-06-07 16:15:20 +05:00
void IDestroyAct . OnDestroy ( DestructionEventArgs eventArgs )
{
2020-07-17 11:03:07 +02:00
var storedEntities = StoredEntities ? . ToList ( ) ;
if ( storedEntities = = null )
{
return ;
}
2019-06-07 16:15:20 +05:00
foreach ( var entity in storedEntities )
{
Remove ( entity ) ;
}
}
2020-02-17 01:19:35 +02:00
2020-06-21 21:57:22 +02:00
void IExAct . OnExplosion ( ExplosionEventArgs eventArgs )
{
if ( eventArgs . Severity < ExplosionSeverity . Heavy )
{
return ;
}
2020-07-17 11:03:07 +02:00
var storedEntities = StoredEntities ? . ToList ( ) ;
if ( storedEntities = = null )
{
return ;
}
2020-06-21 21:57:22 +02:00
foreach ( var entity in storedEntities )
{
2020-08-20 23:44:35 +02:00
var exActs = entity . GetAllComponents < IExAct > ( ) . ToArray ( ) ;
2020-06-21 21:57:22 +02:00
foreach ( var exAct in exActs )
{
exAct . OnExplosion ( eventArgs ) ;
}
}
}
2018-04-22 06:11:38 -05:00
}
}