Refactor: Prevent deleting vital entites

► Based on feedback, it is a bad idea to check against classnames. Instead, we will check the class the entity is based on directly, i.e. we should not be able to delete entities falling under CBasePlayer, the world index --which should be 0, and spawn point classes.
This commit is contained in:
speedvoltage 2025-03-15 09:51:31 +01:00
parent 7702383398
commit 560c66709b
5 changed files with 134 additions and 29 deletions

View file

@ -63,6 +63,7 @@
#include "tier1/utlstring.h"
#include "utlhashtable.h"
#include "vscript_server.h"
#include "subs.h"
#if defined( TF_DLL )
#include "tf_gamerules.h"
@ -5708,14 +5709,59 @@ void CC_Ent_Remove( const CCommand& args )
CBasePlayer *pPlayer = UTIL_GetCommandClient();
if ( pPlayer )
{
if ( !Q_stricmp( args[ 1 ], "player" ) ||
!Q_stricmp( args[ 1 ], "worldspawn" ) ||
!Q_stricmp( args[ 1 ], "info_player_deathmatch" ) ||
!Q_stricmp( args[ 1 ], "info_player_rebel" )
|| !Q_stricmp( args[ 1 ], "info_player_combine" ) )
CBaseEntity *targetEntity = NULL;
if ( FStrEq( args[ 1 ], "" ) )
{
ClientPrint( pPlayer, HUD_PRINTCONSOLE, "This entity cannot be removed\n" );
return;
targetEntity = FindPickerEntity( pPlayer );
}
else
{
int index = atoi( args[ 1 ] );
if ( index )
{
targetEntity = CBaseEntity::Instance( index );
}
else
{
targetEntity = gEntList.FindEntityByName( NULL, args[ 1 ] );
if ( !targetEntity )
{
targetEntity = gEntList.FindEntityByClassname( NULL, args[ 1 ] );
}
}
}
// Don't check against a classname. They can be changed maliciously
// e.g. ent_fire player addoutput "classname abc";ent_remove abc
if ( targetEntity )
{
// Check if it's a player
if ( dynamic_cast< CBasePlayer * >( targetEntity ) )
{
ClientPrint( pPlayer, HUD_PRINTCONSOLE, "You cannot remove players\n" );
return;
}
// Check if it's the world entity
if ( targetEntity->edict() == INDEXENT( 0 ) )
{
ClientPrint( pPlayer, HUD_PRINTCONSOLE, "You cannot remove the world entity\n" );
return;
}
// Check if it's a DM spawn point
if ( dynamic_cast< CBaseDMStart * >( targetEntity ) )
{
ClientPrint( pPlayer, HUD_PRINTCONSOLE, "You cannot remove spawn points\n" );
return;
}
if ( dynamic_cast< CBaseTeamSpawn * >( targetEntity ) )
{
ClientPrint( pPlayer, HUD_PRINTCONSOLE, "You cannot remove spawn points\n" );
return;
}
}
}
@ -5770,14 +5816,59 @@ void CC_Ent_RemoveAll( const CCommand& args )
CBasePlayer *pPlayer = UTIL_GetCommandClient();
if ( pPlayer )
{
if ( !Q_stricmp( args[ 1 ], "player" ) ||
!Q_stricmp( args[ 1 ], "worldspawn" ) ||
!Q_stricmp( args[ 1 ], "info_player_deathmatch" ) ||
!Q_stricmp( args[ 1 ], "info_player_rebel" )
|| !Q_stricmp( args[ 1 ], "info_player_combine" ) )
CBaseEntity *targetEntity = NULL;
if ( FStrEq( args[ 1 ], "" ) )
{
ClientPrint( pPlayer, HUD_PRINTCONSOLE, "This entity cannot be removed\n" );
return;
targetEntity = FindPickerEntity( pPlayer );
}
else
{
int index = atoi( args[ 1 ] );
if ( index )
{
targetEntity = CBaseEntity::Instance( index );
}
else
{
targetEntity = gEntList.FindEntityByName( NULL, args[ 1 ] );
if ( !targetEntity )
{
targetEntity = gEntList.FindEntityByClassname( NULL, args[ 1 ] );
}
}
}
// If we found an entity, check if it's a protected one
if ( targetEntity )
{
// Check if it's a player
if ( dynamic_cast< CBasePlayer * >( targetEntity ) )
{
ClientPrint( pPlayer, HUD_PRINTCONSOLE, "You cannot remove players\n" );
return;
}
// Check if it's the world entity
if ( targetEntity->edict() == INDEXENT( 0 ) )
{
ClientPrint( pPlayer, HUD_PRINTCONSOLE, "You cannot remove the world entity\n" );
return;
}
// Check if it's a DM spawn point
if ( dynamic_cast< CBaseDMStart * >( targetEntity ) )
{
ClientPrint( pPlayer, HUD_PRINTCONSOLE, "You cannot remove spawn points\n" );
return;
}
// Check if it's a TDM spawn point
if ( dynamic_cast< CBaseTeamSpawn * >( targetEntity ) )
{
ClientPrint( pPlayer, HUD_PRINTCONSOLE, "You cannot remove spawn points\n" );
return;
}
}
}

View file

@ -22,6 +22,7 @@
#include "gamestats.h"
#include "ammodef.h"
#include "NextBot.h"
#include "subs.h"
#include "engine/IEngineSound.h"
#include "SoundEmitterSystem/isoundemittersystembase.h"
@ -43,8 +44,8 @@ void DropPrimedFragGrenade( CHL2MP_Player *pPlayer, CBaseCombatWeapon *pGrenade
LINK_ENTITY_TO_CLASS( player, CHL2MP_Player );
LINK_ENTITY_TO_CLASS( info_player_combine, CPointEntity );
LINK_ENTITY_TO_CLASS( info_player_rebel, CPointEntity );
LINK_ENTITY_TO_CLASS( info_player_combine, CBaseTeamSpawn );
LINK_ENTITY_TO_CLASS( info_player_rebel, CBaseTeamSpawn );
// specific to the local player
BEGIN_SEND_TABLE_NOBASE( CHL2MP_Player, DT_HL2MPLocalPlayerExclusive )

View file

@ -621,6 +621,7 @@ $Project
$File "$SRCDIR\public\stringregistry.h"
$File "$SRCDIR\game\shared\studio_shared.cpp"
$File "subs.cpp"
$File "subs.h"
$File "sun.cpp"
$File "tactical_mission.cpp"
$File "tactical_mission.h"

View file

@ -10,6 +10,7 @@
#include "doors.h"
#include "entitylist.h"
#include "globals.h"
#include "subs.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
@ -38,19 +39,6 @@ void CNullEntity::Spawn( void )
}
LINK_ENTITY_TO_CLASS(info_null,CNullEntity);
class CBaseDMStart : public CPointEntity
{
public:
DECLARE_CLASS( CBaseDMStart, CPointEntity );
bool IsTriggered( CBaseEntity *pEntity );
DECLARE_DATADESC();
string_t m_Master;
private:
};
BEGIN_DATADESC( CBaseDMStart )

24
src/game/server/subs.h Normal file
View file

@ -0,0 +1,24 @@
#include "cbase.h"
class CBaseDMStart : public CPointEntity
{
public:
DECLARE_CLASS( CBaseDMStart, CPointEntity );
bool IsTriggered( CBaseEntity *pEntity );
DECLARE_DATADESC();
string_t m_Master;
private:
};
// Peter: I don't see a reason why this could not use CBaseDMStart,
// but as a precaution because team spawn points were set to use CPointEntity,
// I will create a class for this.
class CBaseTeamSpawn : public CPointEntity
{
public:
DECLARE_CLASS( CBaseTeamSpawn, CPointEntity );
};