2021-10-05 14:29:03 +11:00
using System.Collections.Generic ;
2020-10-26 12:11:32 +01:00
using Content.Shared.GameTicking ;
2021-06-09 22:19:39 +02:00
using Content.Shared.Verbs ;
2020-10-26 12:11:32 +01:00
using Robust.Server.Player ;
using Robust.Shared.Enums ;
2020-03-25 11:16:57 +01:00
using Robust.Shared.GameObjects ;
2019-04-15 21:11:38 -06:00
using Robust.Shared.IoC ;
2020-08-13 14:40:27 +02:00
using Robust.Shared.Log ;
2018-11-21 20:58:11 +01:00
2021-06-09 22:19:39 +02:00
namespace Content.Server.Verbs
2018-11-21 20:58:11 +01:00
{
2021-10-05 14:29:03 +11:00
public sealed class VerbSystem : SharedVerbSystem
2018-11-21 20:58:11 +01:00
{
2020-10-26 12:11:32 +01:00
[Dependency] private readonly IPlayerManager _playerManager = default ! ;
2021-10-05 14:29:03 +11:00
/// <summary>
/// List of players that can see all entities on the context menu, ignoring normal visibility rules.
/// </summary>
public readonly HashSet < IPlayerSession > SeeAllContextPlayers = new ( ) ;
2018-11-21 20:58:11 +01:00
public override void Initialize ( )
{
base . Initialize ( ) ;
2020-10-26 12:11:32 +01:00
IoCManager . InjectDependencies ( this ) ;
2021-06-29 15:56:07 +02:00
SubscribeLocalEvent < RoundRestartCleanupEvent > ( Reset ) ;
2021-10-05 14:29:03 +11:00
SubscribeNetworkEvent < RequestServerVerbsEvent > ( HandleVerbRequest ) ;
SubscribeNetworkEvent < TryExecuteVerbEvent > ( HandleTryExecuteVerb ) ;
2020-02-18 19:43:54 -08:00
2020-10-26 12:11:32 +01:00
_playerManager . PlayerStatusChanged + = PlayerStatusChanged ;
}
2021-10-05 14:29:03 +11:00
public override void Shutdown ( )
{
base . Shutdown ( ) ;
_playerManager . PlayerStatusChanged - = PlayerStatusChanged ;
}
2020-10-26 12:11:32 +01:00
private void PlayerStatusChanged ( object? sender , SessionStatusEventArgs args )
{
if ( args . NewStatus = = SessionStatus . Disconnected )
{
2021-10-05 14:29:03 +11:00
SeeAllContextPlayers . Remove ( args . Session ) ;
2020-10-26 12:11:32 +01:00
}
}
2021-06-29 15:56:07 +02:00
public void Reset ( RoundRestartCleanupEvent ev )
2020-10-26 12:11:32 +01:00
{
2021-10-05 14:29:03 +11:00
SeeAllContextPlayers . Clear ( ) ;
2020-10-26 12:11:32 +01:00
}
2021-10-05 14:29:03 +11:00
public void ToggleSeeAllContext ( IPlayerSession player )
2020-10-26 12:11:32 +01:00
{
2021-10-05 14:29:03 +11:00
if ( ! SeeAllContextPlayers . Add ( player ) )
2020-10-26 12:11:32 +01:00
{
2021-10-05 14:29:03 +11:00
SeeAllContextPlayers . Remove ( player ) ;
2020-10-26 12:11:32 +01:00
}
2021-10-05 14:29:03 +11:00
SetSeeAllContextEvent args = new ( ) { CanSeeAllContext = SeeAllContextPlayers . Contains ( player ) } ;
RaiseNetworkEvent ( args , player . ConnectedClient ) ;
2020-10-26 12:11:32 +01:00
}
2021-10-05 14:29:03 +11:00
/// <summary>
/// Called when asked over the network to run a given verb.
/// </summary>
public void HandleTryExecuteVerb ( TryExecuteVerbEvent args , EntitySessionEventArgs eventArgs )
2020-10-26 12:11:32 +01:00
{
2020-03-25 11:16:57 +01:00
var session = eventArgs . SenderSession ;
2020-02-18 19:43:54 -08:00
var userEntity = session . AttachedEntity ;
2018-11-21 20:58:11 +01:00
2020-08-15 20:38:37 +02:00
if ( userEntity = = null )
{
2021-10-05 14:29:03 +11:00
Logger . Warning ( $"{nameof(HandleTryExecuteVerb)} called by player {session} with no attached entity." ) ;
2020-08-15 20:38:37 +02:00
return ;
}
2021-10-05 14:29:03 +11:00
if ( ! EntityManager . TryGetEntity ( args . Target , out var targetEntity ) )
2018-11-21 20:58:11 +01:00
{
2021-10-05 14:29:03 +11:00
return ;
2020-02-18 19:43:54 -08:00
}
2018-11-21 20:58:11 +01:00
2021-10-05 14:29:03 +11:00
// Get the list of verbs. This effectively also checks that the requested verb is in fact a valid verb that
// the user can perform. In principle, this might waste time checking & preparing unrelated verbs even
// though we know precisely which one we want. However, MOST entities will only have 1 or 2 verbs of a given
// type. The one exception here is the "other" verb type, which has 3-4 verbs + all the debug verbs. So maybe
// the debug verbs should be made a separate type?
var verbs = GetVerbs ( targetEntity , userEntity , args . Type ) [ args . Type ] ;
// Find the requested verb.
if ( verbs . TryGetValue ( args . RequestedVerb , out var verb ) )
TryExecuteVerb ( verb ) ;
else
// 404 Verb not found
Logger . Warning ( $"{nameof(HandleTryExecuteVerb)} called by player {session} with an invalid verb: {args.RequestedVerb.Category?.Text} {args.RequestedVerb.Text}" ) ;
2020-02-18 19:43:54 -08:00
}
2018-11-21 20:58:11 +01:00
2021-10-05 14:29:03 +11:00
private void HandleVerbRequest ( RequestServerVerbsEvent args , EntitySessionEventArgs eventArgs )
2020-02-18 19:43:54 -08:00
{
2020-03-25 11:16:57 +01:00
var player = ( IPlayerSession ) eventArgs . SenderSession ;
2018-11-21 20:58:11 +01:00
2021-10-05 14:29:03 +11:00
if ( ! EntityManager . TryGetEntity ( args . EntityUid , out var target ) )
2020-02-18 19:43:54 -08:00
{
2021-10-05 14:29:03 +11:00
Logger . Warning ( $"{nameof(HandleVerbRequest)} called on a non-existent entity with id {args.EntityUid} by player {player}." ) ;
2020-02-18 19:43:54 -08:00
return ;
}
2019-11-23 19:11:50 +00:00
2021-10-05 14:29:03 +11:00
var user = player . AttachedEntity ;
2020-02-18 19:43:54 -08:00
2021-10-05 14:29:03 +11:00
if ( user = = null )
2020-08-15 20:38:37 +02:00
{
2021-10-05 14:29:03 +11:00
Logger . Warning ( $"{nameof(HandleVerbRequest)} called by player {player} with no attached entity." ) ;
2020-08-15 20:38:37 +02:00
return ;
}
2021-10-05 14:29:03 +11:00
// Validate input (check that the user can see the entity)
TryGetContextEntities ( user ,
target . Transform . MapPosition ,
out var entities ,
buffer : true ,
ignoreVisibility : SeeAllContextPlayers . Contains ( player ) ) ;
2020-08-29 20:46:42 +10:00
2021-10-05 14:29:03 +11:00
VerbsResponseEvent response ;
if ( entities ! = null & & entities . Contains ( target ) )
2020-02-18 19:43:54 -08:00
{
2021-10-05 14:29:03 +11:00
response = new ( args . EntityUid , GetVerbs ( target , user , args . Type ) ) ;
2018-11-21 20:58:11 +01:00
}
2021-10-05 14:29:03 +11:00
else
2020-02-18 19:43:54 -08:00
{
2021-10-05 14:29:03 +11:00
// Don't leave the client hanging on "Waiting for server....", send empty response.
response = new ( args . EntityUid , null ) ;
2020-02-18 19:43:54 -08:00
}
2020-03-25 11:16:57 +01:00
RaiseNetworkEvent ( response , player . ConnectedClient ) ;
2018-11-21 20:58:11 +01:00
}
}
}