2023-05-10 00:05:38 +02:00
# include "core/stdafx.h"
# include "tier1/cmd.h"
# include "tier1/cvar.h"
2023-11-25 17:41:08 +01:00
# include "tier1/commandbuffer.h"
2023-05-10 00:05:38 +02:00
# include "engine/cmd.h"
2023-11-25 17:41:08 +01:00
CCommandBuffer * * s_pCommandBuffer = nullptr ; // array size = ECommandTarget_t::CBUF_COUNT.
LPCRITICAL_SECTION s_pCommandBufferMutex = nullptr ;
//=============================================================================
// List of execution markers
//=============================================================================
CUtlVector < int > * g_pExecutionMarkers = nullptr ;
//-----------------------------------------------------------------------------
// Purpose: checks if there's room left for execution markers
// Input : cExecutionMarkers -
// Output : true if there's room for execution markers, false otherwise
//-----------------------------------------------------------------------------
bool Cbuf_HasRoomForExecutionMarkers ( const int cExecutionMarkers )
{
return ( g_pExecutionMarkers - > Count ( ) + cExecutionMarkers ) < MAX_EXECUTION_MARKERS ;
}
//-----------------------------------------------------------------------------
// Purpose: adds command text at the end of the command buffer with execution markers
// Input : *pText -
// markerLeft -
// markerRight -
// Output : true if there's room for execution markers, false otherwise
//-----------------------------------------------------------------------------
bool Cbuf_AddTextWithMarkers ( const char * const pText , const ECmdExecutionMarker markerLeft , const ECmdExecutionMarker markerRight )
{
if ( Cbuf_HasRoomForExecutionMarkers ( 2 ) )
{
Cbuf_AddExecutionMarker ( Cbuf_GetCurrentPlayer ( ) , markerLeft ) ;
Cbuf_AddText ( Cbuf_GetCurrentPlayer ( ) , pText , cmd_source_t : : kCommandSrcCode ) ;
Cbuf_AddExecutionMarker ( Cbuf_GetCurrentPlayer ( ) , markerRight ) ;
return true ;
}
return false ;
}
//-----------------------------------------------------------------------------
// Purpose: adds command text at the end of the buffer
//-----------------------------------------------------------------------------
//void Cbuf_AddText(ECommandTarget_t eTarget, const char* pText, int nTickDelay)
//{
// LOCK_COMMAND_BUFFER();
// if (!s_pCommandBuffer[(int)eTarget]->AddText(pText, nTickDelay, cmd_source_t::kCommandSrcInvalid))
// {
// Error(eDLL_T::ENGINE, NO_ERROR, "%s: buffer overflow\n", __FUNCTION__);
// }
//}
2023-05-10 00:05:38 +02:00
//-----------------------------------------------------------------------------
// Purpose: Sends the entire command line over to the server
// Input : *args -
// Output : true on success, false otherwise
//-----------------------------------------------------------------------------
2024-02-24 02:15:09 +01:00
# ifndef DEDICATED
ConVar cl_quota_stringCmdsPerSecond ( " cl_quota_stringCmdsPerSecond " , " 16 " , FCVAR_RELEASE , " How many string commands per second user is allowed to submit, 0 to allow all submissions. " , true , 0.f , false , 0.f ) ;
# endif // DEDICATED
2023-05-10 00:05:38 +02:00
bool Cmd_ForwardToServer ( const CCommand * args )
{
# ifndef DEDICATED
// Client -> Server command throttling.
static double flForwardedCommandQuotaStartTime = - 1 ;
static int nForwardedCommandQuotaCount = 0 ;
// No command to forward.
if ( args - > ArgC ( ) = = 0 )
return false ;
2023-11-25 17:41:08 +01:00
const double flStartTime = Plat_FloatTime ( ) ;
2024-02-24 02:15:09 +01:00
const int nCmdQuotaLimit = cl_quota_stringCmdsPerSecond . GetInt ( ) ;
2023-05-10 00:05:38 +02:00
const char * pszCmdString = nullptr ;
// Special case: "cmd whatever args..." is forwarded as "whatever args...";
// in this case we strip "cmd" from the input.
if ( Q_strcasecmp ( args - > Arg ( 0 ) , " cmd " ) = = 0 )
pszCmdString = args - > ArgS ( ) ;
else
pszCmdString = args - > GetCommandString ( ) ;
if ( nCmdQuotaLimit )
{
if ( flStartTime - flForwardedCommandQuotaStartTime > = 1.0 )
{
flForwardedCommandQuotaStartTime = flStartTime ;
nForwardedCommandQuotaCount = 0 ;
}
+ + nForwardedCommandQuotaCount ;
if ( nForwardedCommandQuotaCount > nCmdQuotaLimit )
{
// If we are over quota commands per second, dump this on the floor.
// If we spam the server with too many commands, it will kick us.
Warning ( eDLL_T : : CLIENT , " Command '%s' ignored (submission quota of '%d' per second exceeded!) \n " , pszCmdString , nCmdQuotaLimit ) ;
return false ;
}
}
return v_Cmd_ForwardToServer ( args ) ;
# else // !DEDICATED
2024-02-24 02:15:09 +01:00
Assert ( 0 ) ;
2023-05-10 00:05:38 +02:00
return false ; // Client only.
# endif // DEDICATED
}
2024-04-14 23:25:36 +02:00
# ifndef CLIENT_DLL
//-----------------------------------------------------------------------------
// Purpose: execute commands directly (ignores all protection flags)
// Input : *pCommandString -
// *pValueString -
// Output : true on success, false otherwise
//
// NOTE : this function is dangerous, as it allows execution of any command
// without restrictions. Currently, this is only enabled on the
// dedicated server for the local console input and RCON, as they both
// are considered secure (local console needs physical access to the
// terminal application, RCON requires authentication and its protocol
// is secure. Do not use this anywhere else without a valid reason !!!
//
// NOTE : if client support is ever considered (unlikely), then the convar
// flag 'FCVAR_MATERIAL_THREAD_MASK' probably needs to be taken into
// account as well, also, change the DLL context of the warning to
// ENGINE if the client ever utilizes this.
//-----------------------------------------------------------------------------
bool Cmd_ExecuteUnrestricted ( const char * const pCommandString , const char * const pValueString )
{
ConCommandBase * const pCommandBase = g_pCVar - > FindCommandBase ( pCommandString ) ;
if ( ! pCommandBase )
{
// Found nothing.
Warning ( eDLL_T : : SERVER , " Command '%s' doesn't exist; request '%s' ignored \n " , pCommandString , pValueString ) ;
return false ;
}
if ( pCommandBase - > IsFlagSet ( FCVAR_SERVER_FRAME_THREAD ) )
ThreadJoinServerJob ( ) ;
if ( ! pCommandBase - > IsCommand ( ) )
{
// Here we want to skip over the command string in the value buffer.
// So if we got 'sv_cheats 1' in our value buffer, we want to skip
// over 'sv_cheats ', so that we are pointing directly to the value.
const char * pFound = V_strstr ( pValueString , pCommandString ) ;
const char * pValue = nullptr ;
if ( pFound )
{
pValue = pFound + V_strlen ( pCommandString ) ;
// Skip any leading space characters.
while ( * pValue = = ' ' )
{
+ + pValue ;
}
}
ConVar * const pConVar = reinterpret_cast < ConVar * > ( pCommandBase ) ;
pConVar - > SetValue ( pValue ? pValue : pValueString ) ;
}
else // Invoke command callback directly.
{
CCommand cmd ;
// Only tokenize if we actually have strings in the value buffer, some
// commands (like 'status') don't need any additional parameters.
if ( VALID_CHARSTAR ( pValueString ) )
{
cmd . Tokenize ( pValueString , cmd_source_t : : kCommandSrcCode ) ;
}
v_Cmd_Dispatch ( ECommandTarget_t : : CBUF_SERVER , pCommandBase , & cmd , false ) ;
}
return true ;
}
# endif // !CLIENT_DLL
2023-05-10 00:05:38 +02:00
///////////////////////////////////////////////////////////////////////////////
2023-11-26 13:21:20 +01:00
void VCmd : : Detour ( const bool bAttach ) const
2023-05-10 00:05:38 +02:00
{
2023-11-26 13:21:20 +01:00
DetourSetup ( & v_Cmd_ForwardToServer , & Cmd_ForwardToServer , bAttach ) ;
2023-05-10 00:05:38 +02:00
}