2019-10-01 21:45:39 +02:00
# include <sourcemod>
# include <sdkhooks>
# include <sdktools>
2019-12-18 12:02:38 +01:00
# include <PhysHooks>
2019-10-01 21:45:39 +02:00
# include <dhooks>
2020-02-27 11:20:40 +01:00
# include <clientprefs>
# define PLUGIN_VERSION "1.0.1"
2019-10-01 21:45:39 +02:00
2019-11-17 20:46:38 +01:00
# define SetBit(%1,%2) ((%1)[(%2) >> 5] |= (1 << ((%2) & 31)))
# define ClearBit(%1,%2) ((%1)[(%2) >> 5] &= ~(1 << ((%2) & 31)))
# define CheckBit(%1,%2) !!((%1)[(%2) >> 5] & (1 << ((%2) & 31)))
2019-10-01 21:45:39 +02:00
# pragma semicolon 1
# pragma newdecls required
public Plugin myinfo =
{
name = " LagCompensation " ,
author = " BotoX " ,
description = " " ,
2020-02-27 11:20:40 +01:00
version = PLUGIN_VERSION ,
2019-10-01 21:45:39 +02:00
url = " "
} ;
bool g_bLateLoad = false ;
2019-12-18 12:02:38 +01:00
bool g_bHasPhysHooks = true ;
2019-10-01 21:45:39 +02:00
2019-10-10 15:38:58 +02:00
// Don't change this.
# define MAX_EDICTS 2048
2019-11-12 12:47:48 +01:00
# define FSOLID_FORCE_WORLD_ALIGNED 0x0040
# define FSOLID_ROOT_PARENT_ALIGNED 0x0100
2019-11-12 23:55:38 +01:00
# define EFL_DIRTY_ABSTRANSFORM (1<<11)
2019-11-12 12:47:48 +01:00
# define EFL_DIRTY_SURROUNDING_COLLISION_BOUNDS (1<<14)
2019-11-12 23:55:38 +01:00
# define EFL_CHECK_UNTOUCH (1<<24)
2019-11-13 21:02:36 +01:00
# define COORDINATE_FRAME_SIZE 14
2019-11-12 12:47:48 +01:00
enum
{
USE_OBB_COLLISION_BOUNDS = 0 ,
USE_BEST_COLLISION_BOUNDS ,
USE_HITBOXES ,
USE_SPECIFIED_BOUNDS ,
USE_GAME_CODE ,
USE_ROTATION_EXPANDED_BOUNDS ,
USE_COLLISION_BOUNDS_NEVER_VPHYSICS ,
}
enum
{
SOLID_NONE = 0 , // no solid model
SOLID_BSP = 1 , // a BSP tree
SOLID_BBOX = 2 , // an AABB
SOLID_OBB = 3 , // an OBB (not implemented yet)
SOLID_OBB_YAW = 4 , // an OBB, constrained so that it can only yaw
SOLID_CUSTOM = 5 , // Always call into the entity for tests
SOLID_VPHYSICS = 6 , // solid vphysics object, get vcollide from the model and collide with that
SOLID_LAST ,
} ;
2019-10-10 15:38:58 +02:00
2019-11-17 20:46:38 +01:00
enum
{
SF_TRIGGER_ALLOW_CLIENTS = 0x01 , // Players can fire this trigger
SF_TRIGGER_ALLOW_NPCS = 0x02 , // NPCS can fire this trigger
SF_TRIGGER_ALLOW_PUSHABLES = 0x04 , // Pushables can fire this trigger
SF_TRIGGER_ALLOW_PHYSICS = 0x08 , // Physics objects can fire this trigger
SF_TRIGGER_ONLY_PLAYER_ALLY_NPCS = 0x10 , // *if* NPCs can fire this trigger, this flag means only player allies do so
SF_TRIGGER_ONLY_CLIENTS_IN_VEHICLES = 0x20 , // *if* Players can fire this trigger, this flag means only players inside vehicles can
SF_TRIGGER_ALLOW_ALL = 0x40 , // Everything can fire this trigger EXCEPT DEBRIS!
SF_TRIGGER_ONLY_CLIENTS_OUT_OF_VEHICLES = 0x200 , // *if* Players can fire this trigger, this flag means only players outside vehicles can
SF_TRIG_PUSH_ONCE = 0x80 , // trigger_push removes itself after firing once
SF_TRIG_PUSH_AFFECT_PLAYER_ON_LADDER = 0x100 , // if pushed object is player on a ladder, then this disengages them from the ladder (HL2only)
SF_TRIG_TOUCH_DEBRIS = 0x400 , // Will touch physics debris objects
SF_TRIGGER_ONLY_NPCS_IN_VEHICLES = 0x800 , // *if* NPCs can fire this trigger, only NPCs in vehicles do so (respects player ally flag too)
} ;
2019-11-26 21:59:47 +01:00
# define SF_LAGCOMP_DISABLE (1 << 30)
2019-10-02 18:54:19 +02:00
# define MAX_RECORDS 32
2019-11-13 22:04:36 +01:00
# define MAX_ENTITIES 256
2019-10-01 21:45:39 +02:00
enum struct LagRecord
{
float vecOrigin [ 3 ] ;
2019-11-12 12:47:48 +01:00
float vecAbsOrigin [ 3 ] ;
float angRotation [ 3 ] ;
float angAbsRotation [ 3 ] ;
2019-10-06 20:24:43 +02:00
float flSimulationTime ;
2019-11-13 21:02:36 +01:00
float rgflCoordinateFrame [ COORDINATE_FRAME_SIZE ] ;
2019-10-01 21:45:39 +02:00
}
enum struct EntityLagData
{
int iEntity ;
int iRecordIndex ;
int iNumRecords ;
2019-10-02 23:28:20 +02:00
int iRecordsValid ;
2019-10-10 15:38:58 +02:00
int iSpawned ;
2019-10-02 23:28:20 +02:00
int iDeleted ;
2019-10-03 12:02:23 +02:00
int iNotMoving ;
2019-10-01 21:45:39 +02:00
bool bRestore ;
2019-10-20 12:44:34 +02:00
bool bLateKill ;
2019-10-01 21:45:39 +02:00
LagRecord RestoreData ;
}
LagRecord g_aaLagRecords [ MAX_ENTITIES ] [ MAX_RECORDS ] ;
EntityLagData g_aEntityLagData [ MAX_ENTITIES ] ;
int g_iNumEntities = 0 ;
2019-11-12 12:47:48 +01:00
bool g_bCleaningUp = true ;
2019-10-01 21:45:39 +02:00
2020-02-27 11:20:40 +01:00
bool g_bHasOnEntitySpawned = false ;
2019-11-12 12:47:48 +01:00
Handle g_hCalcAbsolutePosition ;
Handle g_hMarkPartitionHandleDirty ;
2019-10-01 21:45:39 +02:00
2019-10-02 23:28:20 +02:00
Handle g_hUTIL_Remove ;
2019-10-03 22:09:53 +02:00
Handle g_hRestartRound ;
2019-11-01 15:58:14 +01:00
Handle g_hSetTarget ;
2019-11-02 01:35:20 +01:00
Handle g_hSetTargetPost ;
2019-11-13 17:21:47 +01:00
Handle g_hFrameUpdatePostEntityThink ;
2019-11-17 00:56:57 +01:00
Handle g_hActivate ;
2019-11-19 21:00:46 +01:00
Handle g_hAcceptInput ;
2019-10-03 22:09:53 +02:00
2020-02-27 11:20:40 +01:00
int g_iNetworkableOuter ;
2019-11-17 00:56:57 +01:00
int g_iParent ;
2019-11-17 20:46:38 +01:00
int g_iSpawnFlags ;
2019-11-12 12:47:48 +01:00
int g_iCollision ;
int g_iSolidFlags ;
int g_iSolidType ;
int g_iSurroundType ;
int g_iEFlags ;
2020-02-27 11:20:40 +01:00
int g_iLerpTime = - 1 ;
2019-11-12 12:47:48 +01:00
int g_iVecOrigin ;
int g_iVecAbsOrigin ;
int g_iAngRotation ;
int g_iAngAbsRotation ;
int g_iSimulationTime ;
int g_iCoordinateFrame ;
2019-11-17 00:56:57 +01:00
int g_aLagCompensated [ MAX_EDICTS ] = { - 1 , . . . } ;
2019-12-18 12:02:38 +01:00
int g_aBlockTriggerTouchPlayers [ MAX_EDICTS / 32 ] ;
int g_aaFilterClientSolidTouch [ ( ( MAXPLAYERS + 1 ) * MAX_EDICTS ) / 32 ] ;
2019-11-17 20:46:38 +01:00
int g_aBlockTriggerMoved [ MAX_EDICTS / 32 ] ;
2019-11-19 21:00:46 +01:00
int g_aBlacklisted [ MAX_EDICTS / 32 ] ;
2019-10-02 23:28:20 +02:00
2020-02-27 11:20:40 +01:00
Handle g_hCookie_DisableLagComp ;
bool g_bDisableLagComp [ MAXPLAYERS + 1 ] ;
int g_iDisableLagComp [ MAXPLAYERS + 1 ] ;
2019-10-01 21:45:39 +02:00
public void OnPluginStart ( )
{
2020-02-27 11:20:40 +01:00
CreateConVar ( " sm_lagcomp_version " , PLUGIN_VERSION , " LagCompensation Version " , FCVAR_SPONLY | FCVAR_NOTIFY | FCVAR_DONTRECORD ) . SetString ( PLUGIN_VERSION ) ;
2019-10-01 21:45:39 +02:00
Handle hGameData = LoadGameConfigFile ( " LagCompensation.games " ) ;
if ( ! hGameData )
SetFailState ( " Failed to load LagCompensation gamedata. " ) ;
2019-11-12 12:47:48 +01:00
// CBaseEntity::CalcAbsolutePosition
2019-10-02 23:28:20 +02:00
StartPrepSDKCall ( SDKCall_Entity ) ;
2019-11-12 12:47:48 +01:00
if ( ! PrepSDKCall_SetFromConf ( hGameData , SDKConf_Signature , " CalcAbsolutePosition " ) )
2019-10-02 23:28:20 +02:00
{
delete hGameData ;
2019-11-12 12:47:48 +01:00
SetFailState ( " PrepSDKCall_SetFromConf(hGameData, SDKConf_Signature, \" CalcAbsolutePosition \" ) failed! " ) ;
2019-10-02 23:28:20 +02:00
}
2019-11-12 12:47:48 +01:00
g_hCalcAbsolutePosition = EndPrepSDKCall ( ) ;
2019-10-02 23:28:20 +02:00
2019-11-12 12:47:48 +01:00
// CCollisionProperty::MarkPartitionHandleDirty
StartPrepSDKCall ( SDKCall_Raw ) ;
if ( ! PrepSDKCall_SetFromConf ( hGameData , SDKConf_Signature , " MarkPartitionHandleDirty " ) )
2019-10-01 21:45:39 +02:00
{
delete hGameData ;
2019-11-12 12:47:48 +01:00
SetFailState ( " PrepSDKCall_SetFromConf(hGameData, SDKConf_Signature, \" MarkPartitionHandleDirty \" ) failed! " ) ;
2019-10-01 21:45:39 +02:00
}
2019-11-12 12:47:48 +01:00
g_hMarkPartitionHandleDirty = EndPrepSDKCall ( ) ;
2019-10-01 21:45:39 +02:00
2019-10-03 22:09:53 +02:00
// ::UTIL_Remove
2019-10-02 23:28:20 +02:00
g_hUTIL_Remove = DHookCreateFromConf ( hGameData , " UTIL_Remove " ) ;
if ( ! g_hUTIL_Remove )
{
delete hGameData ;
SetFailState ( " Failed to setup detour for UTIL_Remove " ) ;
}
if ( ! DHookEnableDetour ( g_hUTIL_Remove , false , Detour_OnUTIL_Remove ) )
2019-10-03 22:09:53 +02:00
{
delete hGameData ;
2019-10-02 23:28:20 +02:00
SetFailState ( " Failed to detour UTIL_Remove. " ) ;
2019-10-03 22:09:53 +02:00
}
2019-10-02 23:28:20 +02:00
2019-10-03 22:09:53 +02:00
// CCSGameRules::RestartRound
g_hRestartRound = DHookCreateFromConf ( hGameData , " CCSGameRules__RestartRound " ) ;
if ( ! g_hRestartRound )
{
delete hGameData ;
SetFailState ( " Failed to setup detour for CCSGameRules__RestartRound " ) ;
}
2019-10-02 23:28:20 +02:00
2019-10-03 22:09:53 +02:00
if ( ! DHookEnableDetour ( g_hRestartRound , false , Detour_OnRestartRound ) )
2019-10-05 15:17:10 +02:00
{
delete hGameData ;
2019-10-03 22:09:53 +02:00
SetFailState ( " Failed to detour CCSGameRules__RestartRound. " ) ;
2019-10-05 15:17:10 +02:00
}
2019-10-10 15:38:58 +02:00
2019-11-01 15:58:14 +01:00
// CLogicMeasureMovement::SetTarget
g_hSetTarget = DHookCreateFromConf ( hGameData , " CLogicMeasureMovement__SetTarget " ) ;
if ( ! g_hSetTarget )
{
delete hGameData ;
SetFailState ( " Failed to setup detour for CLogicMeasureMovement__SetTarget " ) ;
}
2019-11-02 01:35:20 +01:00
if ( ! DHookEnableDetour ( g_hSetTarget , false , Detour_OnSetTargetPre ) )
2019-11-01 15:58:14 +01:00
{
delete hGameData ;
SetFailState ( " Failed to detour CLogicMeasureMovement__SetTarget. " ) ;
}
2019-11-02 01:35:20 +01:00
// CLogicMeasureMovement::SetTarget (fix post hook crashing due to this pointer being overwritten)
g_hSetTargetPost = DHookCreateFromConf ( hGameData , " CLogicMeasureMovement__SetTarget_post " ) ;
if ( ! g_hSetTargetPost )
{
delete hGameData ;
SetFailState ( " Failed to setup detour for CLogicMeasureMovement__SetTarget_post " ) ;
}
if ( ! DHookEnableDetour ( g_hSetTargetPost , true , Detour_OnSetTargetPost ) )
{
delete hGameData ;
SetFailState ( " Failed to detour CLogicMeasureMovement__SetTarget_post. " ) ;
}
2019-11-13 17:21:47 +01:00
// CEntityTouchManager::FrameUpdatePostEntityThink
g_hFrameUpdatePostEntityThink = DHookCreateFromConf ( hGameData , " CEntityTouchManager__FrameUpdatePostEntityThink " ) ;
if ( ! g_hFrameUpdatePostEntityThink )
{
delete hGameData ;
SetFailState ( " Failed to setup detour for CEntityTouchManager__FrameUpdatePostEntityThink " ) ;
}
if ( ! DHookEnableDetour ( g_hFrameUpdatePostEntityThink , false , Detour_OnFrameUpdatePostEntityThink ) )
{
delete hGameData ;
SetFailState ( " Failed to detour CEntityTouchManager__FrameUpdatePostEntityThink. " ) ;
}
2020-02-27 11:20:40 +01:00
g_iNetworkableOuter = GameConfGetOffset ( hGameData , " CServerNetworkableProperty::m_pOuter " ) ;
if ( g_iNetworkableOuter = = - 1 )
{
delete hGameData ;
SetFailState ( " GameConfGetOffset(hGameData, \" CServerNetworkableProperty::m_pOuter \" ) failed! " ) ;
}
2019-10-05 15:17:10 +02:00
delete hGameData ;
2019-11-17 00:56:57 +01:00
hGameData = LoadGameConfigFile ( " sdktools.games " ) ;
if ( ! hGameData )
SetFailState ( " Failed to load sdktools gamedata. " ) ;
int offset = GameConfGetOffset ( hGameData , " Activate " ) ;
2019-11-19 21:00:46 +01:00
if ( offset = = - 1 )
2019-11-17 00:56:57 +01:00
SetFailState ( " Failed to find Activate offset " ) ;
// CPhysForce::Activate
g_hActivate = DHookCreate ( offset , HookType_Entity , ReturnType_Void , ThisPointer_CBaseEntity , Hook_CPhysForce_Activate ) ;
if ( g_hActivate = = INVALID_HANDLE )
SetFailState ( " Failed to DHookCreate Activate " ) ;
2019-11-19 21:00:46 +01:00
offset = GameConfGetOffset ( hGameData , " AcceptInput " ) ;
if ( offset = = - 1 )
SetFailState ( " Failed to find AcceptInput offset. " ) ;
// CBaseEntity::AcceptInput( const char *szInputName, CBaseEntity *pActivator, CBaseEntity *pCaller, variant_t Value, int outputID )
g_hAcceptInput = DHookCreate ( offset , HookType_Entity , ReturnType_Bool , ThisPointer_CBaseEntity , OnAcceptInput ) ;
if ( g_hAcceptInput = = INVALID_HANDLE )
SetFailState ( " Failed to DHook AcceptInput. " ) ;
DHookAddParam ( g_hAcceptInput , HookParamType_CharPtr ) ;
DHookAddParam ( g_hAcceptInput , HookParamType_CBaseEntity ) ;
DHookAddParam ( g_hAcceptInput , HookParamType_CBaseEntity ) ;
DHookAddParam ( g_hAcceptInput , HookParamType_Object , 20 , DHookPass_ByVal | DHookPass_ODTOR | DHookPass_OCTOR | DHookPass_OASSIGNOP ) ; // variant_t is a union of 12 (float[3]) plus two int type params 12 + 8 = 20
DHookAddParam ( g_hAcceptInput , HookParamType_Int ) ;
2019-11-17 00:56:57 +01:00
delete hGameData ;
2020-02-27 11:20:40 +01:00
g_bHasOnEntitySpawned = GetFeatureStatus ( FeatureType_Capability , " SDKHook_OnEntitySpawned " ) = = FeatureStatus_Available ;
g_hCookie_DisableLagComp = RegClientCookie ( " disable_lagcomp " , " " , CookieAccess_Private ) ;
RegConsoleCmd ( " sm_lagcomp " , OnToggleLagCompSettings ) ;
RegConsoleCmd ( " sm_0ping " , OnToggleLagCompSettings ) ;
SetCookieMenuItem ( MenuHandler_CookieMenu , 0 , " LagCompensation " ) ;
CreateTimer ( 0.1 , DisableLagCompTimer , _ , TIMER_REPEAT ) ;
2019-11-17 00:56:57 +01:00
2019-10-10 15:38:58 +02:00
RegAdminCmd ( " sm_unlag " , Command_AddLagCompensation , ADMFLAG_RCON , " sm_unlag <entidx> " ) ;
2019-10-06 20:24:43 +02:00
RegAdminCmd ( " sm_lagged " , Command_CheckLagCompensated , ADMFLAG_GENERIC , " sm_lagged " ) ;
2019-10-05 15:17:10 +02:00
2019-12-18 12:02:38 +01:00
FilterClientSolidTouch ( g_aaFilterClientSolidTouch , true ) ;
2019-11-17 20:46:38 +01:00
BlockTriggerMoved ( g_aBlockTriggerMoved , true ) ;
2019-10-02 23:28:20 +02:00
}
2019-11-13 21:02:36 +01:00
public APLRes AskPluginLoad2 ( Handle myself , bool late , char [ ] error , int err_max )
2019-10-05 15:17:10 +02:00
{
2019-11-13 21:02:36 +01:00
g_bLateLoad = late ;
return APLRes_Success ;
}
2019-10-05 15:17:10 +02:00
2019-11-28 15:15:32 +01:00
public void OnLibraryRemoved ( const char [ ] name )
{
2019-12-18 12:02:38 +01:00
if ( StrEqual ( name , " PhysHooks " ) )
g_bHasPhysHooks = false ;
2019-11-28 15:15:32 +01:00
}
2019-11-13 21:02:36 +01:00
public void OnPluginEnd ( )
{
g_bCleaningUp = true ;
2019-12-18 12:02:38 +01:00
if ( g_bHasPhysHooks )
2019-11-28 15:15:32 +01:00
{
2019-12-18 12:02:38 +01:00
FilterClientSolidTouch ( g_aaFilterClientSolidTouch , false ) ;
2019-11-28 15:15:32 +01:00
BlockTriggerMoved ( g_aBlockTriggerMoved , false ) ;
2019-12-18 12:02:38 +01:00
BlockTriggerTouchPlayers ( g_aBlockTriggerTouchPlayers , false ) ;
2019-11-28 15:15:32 +01:00
}
2019-10-05 15:17:10 +02:00
2019-11-13 21:02:36 +01:00
DHookDisableDetour ( g_hUTIL_Remove , false , Detour_OnUTIL_Remove ) ;
2019-10-05 15:17:10 +02:00
2019-11-13 21:02:36 +01:00
for ( int i = 0 ; i < g_iNumEntities ; i + + )
2019-10-20 12:44:34 +02:00
{
2019-11-13 21:02:36 +01:00
if ( ! IsValidEntity ( g_aEntityLagData [ i ] . iEntity ) )
continue ;
2019-10-05 15:17:10 +02:00
2019-11-13 21:02:36 +01:00
if ( g_aEntityLagData [ i ] . iDeleted )
{
RemoveEdict ( g_aEntityLagData [ i ] . iEntity ) ;
}
}
2019-10-05 15:17:10 +02:00
}
2019-10-02 23:28:20 +02:00
2019-11-13 21:02:36 +01:00
public void OnMapStart ( )
2019-10-06 20:24:43 +02:00
{
2019-11-13 21:02:36 +01:00
bool bLate = g_bLateLoad ;
2019-10-06 20:24:43 +02:00
2019-11-13 21:02:36 +01:00
g_bCleaningUp = false ;
2019-10-06 20:24:43 +02:00
2019-11-17 00:56:57 +01:00
g_iParent = FindDataMapInfo ( 0 , " m_pParent " ) ;
2019-11-17 20:46:38 +01:00
g_iSpawnFlags = FindDataMapInfo ( 0 , " m_spawnflags " ) ;
2019-11-13 21:02:36 +01:00
g_iCollision = FindDataMapInfo ( 0 , " m_Collision " ) ;
g_iSolidFlags = FindDataMapInfo ( 0 , " m_usSolidFlags " ) ;
g_iSolidType = FindDataMapInfo ( 0 , " m_nSolidType " ) ;
g_iSurroundType = FindDataMapInfo ( 0 , " m_nSurroundType " ) ;
g_iEFlags = FindDataMapInfo ( 0 , " m_iEFlags " ) ;
2019-10-06 20:24:43 +02:00
2019-11-13 21:02:36 +01:00
g_iVecOrigin = FindDataMapInfo ( 0 , " m_vecOrigin " ) ;
g_iVecAbsOrigin = FindDataMapInfo ( 0 , " m_vecAbsOrigin " ) ;
g_iAngRotation = FindDataMapInfo ( 0 , " m_angRotation " ) ;
g_iAngAbsRotation = FindDataMapInfo ( 0 , " m_angAbsRotation " ) ;
g_iSimulationTime = FindDataMapInfo ( 0 , " m_flSimulationTime " ) ;
g_iCoordinateFrame = FindDataMapInfo ( 0 , " m_rgflCoordinateFrame " ) ;
2019-10-06 20:24:43 +02:00
2019-11-13 21:02:36 +01:00
/* Late Load */
if ( bLate )
2019-10-06 20:24:43 +02:00
{
2020-02-27 11:20:40 +01:00
for ( int client = 1 ; client < = MaxClients ; client + + )
{
if ( IsClientInGame ( client ) )
{
OnClientConnected ( client ) ;
if ( AreClientCookiesCached ( client ) )
OnClientCookiesCached ( client ) ;
OnClientPutInServer ( client ) ;
}
}
2019-11-13 21:02:36 +01:00
int entity = INVALID_ENT_REFERENCE ;
while ( ( entity = FindEntityByClassname ( entity , " * " ) ) ! = INVALID_ENT_REFERENCE )
2019-10-06 20:24:43 +02:00
{
2019-11-13 21:02:36 +01:00
char sClassname [ 64 ] ;
if ( GetEntityClassname ( entity , sClassname , sizeof ( sClassname ) ) )
2019-11-17 00:56:57 +01:00
{
OnEntityCreated ( entity , sClassname ) ;
2019-11-13 21:02:36 +01:00
OnEntitySpawned ( entity , sClassname ) ;
2019-11-17 00:56:57 +01:00
if ( StrEqual ( sClassname , " phys_thruster " , false ) )
{
Hook_CPhysForce_Activate ( entity ) ;
}
}
2019-10-06 20:24:43 +02:00
}
2019-11-13 21:02:36 +01:00
}
2019-11-19 21:00:46 +01:00
g_bLateLoad = false ;
2019-11-13 21:02:36 +01:00
}
2019-10-06 20:24:43 +02:00
2020-02-27 11:20:40 +01:00
public void OnMapEnd ( )
{
Detour_OnRestartRound ( ) ;
g_bCleaningUp = true ;
}
public void OnClientConnected ( int client )
{
g_bDisableLagComp [ client ] = false ;
g_iDisableLagComp [ client ] = 0 ;
}
public void OnClientCookiesCached ( int client )
{
char sBuffer [ 16 ] ;
GetClientCookie ( client , g_hCookie_DisableLagComp , sBuffer , sizeof ( sBuffer ) ) ;
if ( sBuffer [ 0 ] )
g_bDisableLagComp [ client ] = true ;
else
g_bDisableLagComp [ client ] = false ;
}
public void OnClientPutInServer ( int client )
{
if ( g_iLerpTime = = - 1 )
{
g_iLerpTime = FindDataMapInfo ( client , " m_fLerpTime " ) ;
}
}
public void OnClientDisconnect ( int client )
{
g_bDisableLagComp [ client ] = false ;
g_iDisableLagComp [ client ] = 0 ;
}
2019-11-17 00:56:57 +01:00
public void OnEntityCreated ( int entity , const char [ ] classname )
{
if ( g_bCleaningUp )
return ;
if ( StrEqual ( classname , " phys_thruster " , false ) )
{
DHookEntity ( g_hActivate , true , entity ) ;
}
2019-11-19 21:00:46 +01:00
else if ( StrEqual ( classname , " game_ui " , false ) )
{
DHookEntity ( g_hAcceptInput , true , entity ) ;
}
2020-02-27 11:20:40 +01:00
if ( ! g_bHasOnEntitySpawned )
{
SDKHook ( entity , SDKHook_SpawnPost , OnSDKHookEntitySpawnPost ) ;
}
}
public void OnSDKHookEntitySpawnPost ( int entity )
{
char classname [ 64 ] ;
GetEntityClassname ( entity , classname , sizeof ( classname ) ) ;
OnEntitySpawned ( entity , classname ) ;
2019-11-17 00:56:57 +01:00
}
2019-11-13 21:02:36 +01:00
public void OnEntitySpawned ( int entity , const char [ ] classname )
{
if ( g_bCleaningUp )
return ;
2019-10-06 20:24:43 +02:00
2019-11-17 00:56:57 +01:00
CheckEntityForLagComp ( entity , classname ) ;
}
2019-11-19 21:00:46 +01:00
public MRESReturn OnAcceptInput ( int entity , Handle hReturn , Handle hParams )
{
if ( ! IsValidEntity ( entity ) )
return MRES_Ignored ;
char sCommand [ 128 ] ;
DHookGetParamString ( hParams , 1 , sCommand , sizeof ( sCommand ) ) ;
if ( ! StrEqual ( sCommand , " Activate " , false ) )
return MRES_Ignored ;
if ( DHookIsNullParam ( hParams , 3 ) )
return MRES_Ignored ;
int iCaller = DHookGetParam ( hParams , 3 ) ;
if ( iCaller < = 0 | | iCaller > = MAX_EDICTS )
return MRES_Ignored ;
// Don't lagcompensate anything that has a game_ui button in their hierarchy.
BlacklistFamily ( iCaller ) ;
return MRES_Ignored ;
}
void BlacklistFamily ( int entity )
{
if ( entity > 0 & & entity < MAX_EDICTS )
{
SetBit ( g_aBlacklisted , entity ) ;
RemoveEntityFromLagCompensation ( entity ) ;
}
// Blacklist children of this entity
BlacklistChildren ( entity ) ;
// And blacklist all parents and their children
for ( ; ; )
{
entity = GetEntDataEnt2 ( entity , g_iParent ) ;
if ( entity = = INVALID_ENT_REFERENCE )
break ;
if ( entity > 0 & & entity < MAX_EDICTS )
{
SetBit ( g_aBlacklisted , entity ) ;
RemoveEntityFromLagCompensation ( entity ) ;
}
// And their children
BlacklistChildren ( entity ) ;
}
}
void BlacklistChildren ( int parent )
{
int entity = INVALID_ENT_REFERENCE ;
while ( ( entity = FindEntityByClassname ( entity , " * " ) ) ! = INVALID_ENT_REFERENCE )
{
if ( GetEntDataEnt2 ( entity , g_iParent ) ! = parent )
continue ;
if ( entity > 0 & & entity < MAX_EDICTS )
{
SetBit ( g_aBlacklisted , entity ) ;
RemoveEntityFromLagCompensation ( entity ) ;
}
}
}
2019-11-17 00:56:57 +01:00
bool CheckEntityForLagComp ( int entity , const char [ ] classname , bool bRecursive = false , bool bGoodParents = false )
{
2019-11-13 21:02:36 +01:00
if ( entity < 0 | | entity > MAX_EDICTS )
2019-11-17 00:56:57 +01:00
return false ;
2019-10-06 20:24:43 +02:00
2019-11-13 21:02:36 +01:00
if ( ! IsValidEntity ( entity ) )
2019-11-17 00:56:57 +01:00
return false ;
if ( g_aLagCompensated [ entity ] ! = - 1 )
return false ;
2019-10-06 20:24:43 +02:00
2019-11-26 21:59:47 +01:00
int SpawnFlags = GetEntData ( entity , g_iSpawnFlags ) ;
if ( SpawnFlags & SF_LAGCOMP_DISABLE )
return false ;
2019-11-13 21:02:36 +01:00
bool bTrigger = StrEqual ( classname , " trigger_hurt " , false ) | |
StrEqual ( classname , " trigger_push " , false ) | |
2019-11-16 01:35:27 +01:00
StrEqual ( classname , " trigger_teleport " , false ) ;
2019-11-13 21:02:36 +01:00
2019-11-17 00:56:57 +01:00
bool bPhysbox = ! strncmp ( classname , " func_physbox " , 12 , false ) ;
2019-11-13 21:02:36 +01:00
2019-11-19 21:00:46 +01:00
bool bBlacklisted = CheckBit ( g_aBlacklisted , entity ) ;
if ( ! bTrigger & & ! bPhysbox | | bBlacklisted )
2019-11-17 00:56:57 +01:00
return false ;
2019-11-13 21:02:36 +01:00
// Don't lag compensate anything that could be parented to a player
// The player simulation would usually move the entity,
// but we would overwrite that position change by restoring the entity to its previous state.
int iParent = INVALID_ENT_REFERENCE ;
char sParentClassname [ 64 ] ;
for ( int iTmp = entity ; ; )
{
2019-11-17 00:56:57 +01:00
iTmp = GetEntDataEnt2 ( iTmp , g_iParent ) ;
2019-11-13 21:02:36 +01:00
if ( iTmp = = INVALID_ENT_REFERENCE )
break ;
iParent = iTmp ;
GetEntityClassname ( iParent , sParentClassname , sizeof ( sParentClassname ) ) ;
2020-02-27 11:20:40 +01:00
if ( ( iParent > = 1 & & iParent < = MaxClients ) | |
2019-11-13 21:02:36 +01:00
! strncmp ( sParentClassname , " weapon_ " , 7 ) )
{
2019-11-17 00:56:57 +01:00
return false ;
}
2019-11-19 21:00:46 +01:00
if ( CheckBit ( g_aBlacklisted , entity ) )
{
return false ;
}
2019-11-17 00:56:57 +01:00
if ( g_aLagCompensated [ iParent ] ! = - 1 )
{
bGoodParents = true ;
break ;
}
if ( strncmp ( sParentClassname , " func_ " , 5 ) )
continue ;
if ( StrEqual ( sParentClassname [ 5 ] , " movelinear " ) | |
StrEqual ( sParentClassname [ 5 ] , " door " ) | |
StrEqual ( sParentClassname [ 5 ] , " rotating " ) | |
StrEqual ( sParentClassname [ 5 ] , " tracktrain " ) )
{
bGoodParents = true ;
break ;
2019-10-06 20:24:43 +02:00
}
}
2019-11-17 00:56:57 +01:00
if ( ! bGoodParents )
return false ;
if ( AddEntityForLagCompensation ( entity , bTrigger ) )
2019-11-13 21:02:36 +01:00
{
2019-11-17 20:46:38 +01:00
if ( bTrigger )
{
2019-11-26 21:59:47 +01:00
if ( ! ( SpawnFlags & ( SF_TRIGGER_ALLOW_PUSHABLES | SF_TRIGGER_ALLOW_PHYSICS | SF_TRIGGER_ALLOW_ALL | SF_TRIG_TOUCH_DEBRIS ) ) )
2019-11-17 20:46:38 +01:00
SetBit ( g_aBlockTriggerMoved , entity ) ;
}
2019-11-17 00:56:57 +01:00
if ( bRecursive )
{
CheckEntityChildrenForLagComp ( entity ) ;
}
2019-11-17 20:46:38 +01:00
2019-11-17 00:56:57 +01:00
return true ;
2019-11-13 21:02:36 +01:00
}
2019-11-17 00:56:57 +01:00
return false ;
}
void CheckEntityChildrenForLagComp ( int parent )
{
int entity = INVALID_ENT_REFERENCE ;
while ( ( entity = FindEntityByClassname ( entity , " * " ) ) ! = INVALID_ENT_REFERENCE )
2019-11-13 21:02:36 +01:00
{
2019-11-17 00:56:57 +01:00
if ( GetEntDataEnt2 ( entity , g_iParent ) ! = parent )
continue ;
char sClassname [ 64 ] ;
if ( GetEntityClassname ( entity , sClassname , sizeof ( sClassname ) ) )
2019-11-13 21:02:36 +01:00
{
2019-11-17 00:56:57 +01:00
CheckEntityForLagComp ( entity , sClassname , _ , true ) ;
CheckEntityChildrenForLagComp ( entity ) ;
2019-11-13 21:02:36 +01:00
}
}
2019-10-06 20:24:43 +02:00
}
2019-11-13 21:02:36 +01:00
public void OnEntityDestroyed ( int entity )
2019-10-03 22:09:53 +02:00
{
2019-11-13 21:02:36 +01:00
if ( g_bCleaningUp )
return ;
2019-10-04 11:23:05 +02:00
2019-11-13 21:02:36 +01:00
if ( entity < 0 | | entity > MAX_EDICTS )
return ;
2019-11-19 21:00:46 +01:00
ClearBit ( g_aBlacklisted , entity ) ;
2019-11-17 00:56:57 +01:00
int iIndex = g_aLagCompensated [ entity ] ;
if ( iIndex = = - 1 )
2019-11-13 21:02:36 +01:00
return ;
2019-10-04 11:23:05 +02:00
2019-11-17 00:56:57 +01:00
RemoveRecord ( iIndex ) ;
2019-10-02 23:28:20 +02:00
}
2019-10-01 21:45:39 +02:00
2019-10-02 23:28:20 +02:00
public MRESReturn Detour_OnUTIL_Remove ( Handle hParams )
2019-10-01 21:45:39 +02:00
{
2019-10-05 15:17:10 +02:00
if ( g_bCleaningUp )
return MRES_Ignored ;
2020-02-27 11:20:40 +01:00
if ( DHookIsNullParam ( hParams , 1 ) )
return MRES_Ignored ;
int entity = DHookGetParamObjectPtrVar ( hParams , 1 , g_iNetworkableOuter , ObjectValueType_CBaseEntityPtr ) ;
2019-10-10 15:38:58 +02:00
if ( entity < 0 | | entity > MAX_EDICTS )
2019-10-02 23:28:20 +02:00
return MRES_Ignored ;
2019-11-17 00:56:57 +01:00
int iIndex = g_aLagCompensated [ entity ] ;
if ( iIndex = = - 1 )
return MRES_Ignored ;
2019-10-20 12:44:34 +02:00
2019-11-17 00:56:57 +01:00
// let it die
if ( ! g_aEntityLagData [ iIndex ] . bLateKill )
return MRES_Ignored ;
2019-10-20 12:44:34 +02:00
2019-11-17 00:56:57 +01:00
// ignore sleeping entities
if ( g_aEntityLagData [ iIndex ] . iNotMoving > = MAX_RECORDS )
return MRES_Ignored ;
2019-10-02 23:28:20 +02:00
2019-11-17 00:56:57 +01:00
if ( ! g_aEntityLagData [ iIndex ] . iDeleted )
{
g_aEntityLagData [ iIndex ] . iDeleted = GetGameTickCount ( ) ;
2019-10-02 23:28:20 +02:00
}
2019-11-17 00:56:57 +01:00
return MRES_Supercede ;
2019-10-01 21:45:39 +02:00
}
2019-10-03 22:09:53 +02:00
public MRESReturn Detour_OnRestartRound ( )
{
2019-10-05 15:17:10 +02:00
g_bCleaningUp = true ;
2019-11-13 21:02:36 +01:00
2019-10-03 22:09:53 +02:00
for ( int i = 0 ; i < g_iNumEntities ; i + + )
{
2019-11-16 01:35:27 +01:00
int iEntity = g_aEntityLagData [ i ] . iEntity ;
2019-11-17 00:56:57 +01:00
g_aLagCompensated [ iEntity ] = - 1 ;
2019-12-18 12:02:38 +01:00
ClearBit ( g_aBlockTriggerTouchPlayers , iEntity ) ;
2019-11-17 20:46:38 +01:00
ClearBit ( g_aBlockTriggerMoved , iEntity ) ;
2019-10-05 15:17:10 +02:00
2019-10-20 12:44:34 +02:00
for ( int client = 1 ; client < = MaxClients ; client + + )
2019-10-05 15:17:10 +02:00
{
2019-12-18 12:02:38 +01:00
ClearBit ( g_aaFilterClientSolidTouch , client * MAX_EDICTS + iEntity ) ;
2019-10-20 12:44:34 +02:00
}
2019-10-05 15:17:10 +02:00
2019-10-20 12:44:34 +02:00
if ( g_aEntityLagData [ i ] . iDeleted )
{
2019-11-16 01:35:27 +01:00
if ( IsValidEntity ( iEntity ) )
RemoveEdict ( iEntity ) ;
2019-10-05 15:17:10 +02:00
}
2019-10-06 20:24:43 +02:00
g_aEntityLagData [ i ] . iEntity = INVALID_ENT_REFERENCE ;
2019-10-03 22:09:53 +02:00
}
2019-11-19 21:00:46 +01:00
for ( int i = 0 ; i < sizeof ( g_aBlacklisted ) ; i + + )
g_aBlacklisted [ i ] = 0 ;
2019-10-03 22:09:53 +02:00
g_iNumEntities = 0 ;
2019-10-05 15:17:10 +02:00
g_bCleaningUp = false ;
2019-10-03 22:09:53 +02:00
return MRES_Ignored ;
}
2019-11-01 15:58:14 +01:00
// https://developer.valvesoftware.com/wiki/Logic_measure_movement
2019-11-02 01:35:20 +01:00
int g_OnSetTarget_pThis ;
public MRESReturn Detour_OnSetTargetPre ( int pThis , Handle hParams )
{
g_OnSetTarget_pThis = pThis ;
return MRES_Ignored ;
}
public MRESReturn Detour_OnSetTargetPost ( Handle hParams )
2019-11-01 15:58:14 +01:00
{
2019-11-02 01:35:20 +01:00
int entity = GetEntPropEnt ( g_OnSetTarget_pThis , Prop_Data , " m_hTarget " ) ;
2019-11-01 15:58:14 +01:00
if ( ! IsValidEntity ( entity ) )
return MRES_Ignored ;
char sClassname [ 64 ] ;
if ( ! GetEntityClassname ( entity , sClassname , sizeof ( sClassname ) ) )
return MRES_Ignored ;
2019-11-17 00:56:57 +01:00
CheckEntityForLagComp ( entity , sClassname , true , true ) ;
return MRES_Ignored ;
}
public MRESReturn Hook_CPhysForce_Activate ( int entity )
{
int attachedObject = GetEntPropEnt ( entity , Prop_Data , " m_attachedObject " ) ;
if ( ! IsValidEntity ( attachedObject ) )
2019-11-01 15:58:14 +01:00
return MRES_Ignored ;
2019-11-17 00:56:57 +01:00
char sClassname [ 64 ] ;
if ( ! GetEntityClassname ( attachedObject , sClassname , sizeof ( sClassname ) ) )
return MRES_Ignored ;
CheckEntityForLagComp ( attachedObject , sClassname , true , true ) ;
2019-11-01 15:58:14 +01:00
return MRES_Ignored ;
}
2019-11-13 21:02:36 +01:00
public MRESReturn Detour_OnFrameUpdatePostEntityThink ( )
2019-10-01 21:45:39 +02:00
{
2019-11-13 21:02:36 +01:00
for ( int i = 0 ; i < g_iNumEntities ; i + + )
2019-10-01 21:45:39 +02:00
{
2019-11-13 21:02:36 +01:00
// Don't make the entity check untouch in FrameUpdatePostEntityThink.
// If the player didn't get simulated in the current frame then
// they didn't have a chance to touch this entity.
// Hence the touchlink could be broken and we only let the player check untouch.
int EFlags = GetEntData ( g_aEntityLagData [ i ] . iEntity , g_iEFlags ) ;
EFlags & = ~ EFL_CHECK_UNTOUCH ;
SetEntData ( g_aEntityLagData [ i ] . iEntity , g_iEFlags , EFlags ) ;
2019-10-01 21:45:39 +02:00
}
}
2019-11-13 21:02:36 +01:00
2019-10-01 21:45:39 +02:00
public void OnRunThinkFunctions ( bool simulating )
{
2019-12-18 12:02:38 +01:00
BlockTriggerTouchPlayers ( g_aBlockTriggerTouchPlayers , false ) ;
2019-11-13 17:21:47 +01:00
2019-10-03 12:02:23 +02:00
for ( int i = 0 ; i < g_iNumEntities ; i + + )
2019-10-01 21:45:39 +02:00
{
if ( ! IsValidEntity ( g_aEntityLagData [ i ] . iEntity ) )
{
2019-10-06 20:24:43 +02:00
PrintToBoth ( " !!!!!!!!!!! OnRunThinkFunctions SHIT deleted: %d / %d " , i , g_aEntityLagData [ i ] . iEntity ) ;
2019-10-03 12:02:23 +02:00
RemoveRecord ( i ) ;
i - - ; continue ;
2019-10-01 21:45:39 +02:00
}
2019-10-02 23:28:20 +02:00
if ( g_aEntityLagData [ i ] . iDeleted )
{
2019-10-24 00:10:29 +02:00
if ( g_aEntityLagData [ i ] . iDeleted + MAX_RECORDS < = GetGameTickCount ( ) )
2019-10-02 23:28:20 +02:00
{
2019-10-03 12:02:23 +02:00
// calls OnEntityDestroyed right away
// which calls RemoveRecord
// which moves the next element to our current position
2019-10-02 23:28:20 +02:00
RemoveEdict ( g_aEntityLagData [ i ] . iEntity ) ;
2019-10-03 12:02:23 +02:00
i - - ; continue ;
2019-10-02 23:28:20 +02:00
}
continue ;
}
2019-10-05 15:17:10 +02:00
if ( g_aEntityLagData [ i ] . iNotMoving > = MAX_RECORDS )
continue ;
2019-10-01 21:45:39 +02:00
RecordDataIntoRecord ( g_aEntityLagData [ i ] . iEntity , g_aEntityLagData [ i ] . RestoreData ) ;
}
}
public Action OnPlayerRunCmd ( int client , int & buttons , int & impulse , float vel [ 3 ] , float angles [ 3 ] , int & weapon , int & subtype , int & cmdnum , int & tickcount , int & seed , int mouse [ 2 ] )
{
2019-10-02 12:22:35 +02:00
if ( ! IsPlayerAlive ( client ) )
return Plugin_Continue ;
2019-11-16 01:35:27 +01:00
// -1 because the newest record in the list is one tick old
// this is because we simulate players first
// hence no new entity record was inserted on the current tick
int iGameTick = GetGameTickCount ( ) - 1 ;
2020-02-27 11:20:40 +01:00
float fTickInterval = GetTickInterval ( ) ;
float fLerpTime = GetEntDataFloat ( client , g_iLerpTime ) ;
// -1 lerp ticks was determined by analyzing hits visually at host_timescale 0.01 and debug_touchlinks 1
int iLerpTicks = RoundToFloor ( 0.5 + ( fLerpTime / fTickInterval ) ) - 1 ;
int iTargetTick = tickcount - iLerpTicks ;
int iDelta = iGameTick - iTargetTick ;
float fCorrect = 0.0 ;
fCorrect + = GetClientLatency ( client , NetFlow_Outgoing ) ;
fCorrect + = iLerpTicks * fTickInterval ;
float fDeltaTime = fCorrect - iDelta * fTickInterval ;
if ( FloatAbs ( fDeltaTime ) > 0.2 )
{
// difference between cmd time and latency is too big > 200ms, use time correction based on latency
iDelta = RoundToFloor ( 0.5 + ( fCorrect / fTickInterval ) ) ;
}
// The player is stupid and doesn't want lag compensation.
// To get the original behavior back lets assume they actually have 0 latency.
// To avoid abusing toggling lagcomp we increase/decrease this var by 1 every 100ms.
// This is so we only skip single ticks at a time. Fully ON = 0, Fully OFF = MAX_RECORDS
iDelta - = g_iDisableLagComp [ client ] ;
2019-10-20 12:44:34 +02:00
2019-10-10 15:38:58 +02:00
if ( iDelta < 0 )
iDelta = 0 ;
if ( iDelta > MAX_RECORDS )
iDelta = MAX_RECORDS ;
2019-11-03 01:11:40 +01:00
int iPlayerSimTick = iGameTick - iDelta ;
2019-10-01 21:45:39 +02:00
2019-10-03 12:02:23 +02:00
for ( int i = 0 ; i < g_iNumEntities ; i + + )
2019-10-01 21:45:39 +02:00
{
2019-10-20 12:44:34 +02:00
int iEntity = g_aEntityLagData [ i ] . iEntity ;
2019-10-01 21:45:39 +02:00
2019-10-10 15:38:58 +02:00
// Entity too new, the client couldn't even see it yet.
2019-10-24 00:10:29 +02:00
if ( g_aEntityLagData [ i ] . iSpawned > iPlayerSimTick )
2019-10-20 12:44:34 +02:00
{
2019-12-18 12:02:38 +01:00
SetBit ( g_aaFilterClientSolidTouch , client * MAX_EDICTS + iEntity ) ;
2019-10-20 12:44:34 +02:00
continue ;
}
2019-10-02 23:28:20 +02:00
if ( g_aEntityLagData [ i ] . iDeleted )
{
2019-10-24 00:10:29 +02:00
if ( g_aEntityLagData [ i ] . iDeleted < = iPlayerSimTick )
2019-10-05 15:17:10 +02:00
{
2019-12-18 12:02:38 +01:00
SetBit ( g_aaFilterClientSolidTouch , client * MAX_EDICTS + iEntity ) ;
2019-10-02 23:28:20 +02:00
continue ;
2019-10-05 15:17:10 +02:00
}
2019-10-02 23:28:20 +02:00
}
2020-02-27 11:20:40 +01:00
else
{
ClearBit ( g_aaFilterClientSolidTouch , client * MAX_EDICTS + iEntity ) ;
}
2019-10-02 23:28:20 +02:00
2019-10-24 00:10:29 +02:00
if ( g_aEntityLagData [ i ] . iNotMoving > = MAX_RECORDS )
continue ;
2019-11-16 01:35:27 +01:00
int iRecord = iDelta ;
if ( iRecord > = g_aEntityLagData [ i ] . iNumRecords )
iRecord = g_aEntityLagData [ i ] . iNumRecords - 1 ;
2019-11-03 17:18:43 +01:00
2019-11-16 01:35:27 +01:00
int iRecordIndex = g_aEntityLagData [ i ] . iRecordIndex - iRecord ;
2019-10-01 21:45:39 +02:00
if ( iRecordIndex < 0 )
iRecordIndex + = MAX_RECORDS ;
2019-10-20 12:44:34 +02:00
RestoreEntityFromRecord ( iEntity , g_aaLagRecords [ i ] [ iRecordIndex ] ) ;
2019-11-16 01:35:27 +01:00
g_aEntityLagData [ i ] . bRestore = ! g_aEntityLagData [ i ] . iDeleted ;
2019-10-01 21:45:39 +02:00
}
return Plugin_Continue ;
}
2019-10-02 18:54:19 +02:00
public void OnPostPlayerThinkFunctions ( )
2019-10-01 21:45:39 +02:00
{
2019-10-03 12:02:23 +02:00
for ( int i = 0 ; i < g_iNumEntities ; i + + )
2019-10-01 21:45:39 +02:00
{
2019-10-03 10:39:27 +02:00
if ( ! g_aEntityLagData [ i ] . bRestore )
2019-10-01 21:45:39 +02:00
continue ;
2019-10-16 15:18:09 +02:00
RestoreEntityFromRecord ( g_aEntityLagData [ i ] . iEntity , g_aEntityLagData [ i ] . RestoreData ) ;
2019-10-01 21:45:39 +02:00
g_aEntityLagData [ i ] . bRestore = false ;
}
2019-10-02 14:19:09 +02:00
2019-12-18 12:02:38 +01:00
BlockTriggerTouchPlayers ( g_aBlockTriggerTouchPlayers , true ) ;
2019-10-01 21:45:39 +02:00
}
public void OnRunThinkFunctionsPost ( bool simulating )
{
2019-10-03 12:02:23 +02:00
for ( int i = 0 ; i < g_iNumEntities ; i + + )
2019-10-01 21:45:39 +02:00
{
2019-10-02 23:28:20 +02:00
if ( g_aEntityLagData [ i ] . iDeleted )
2019-10-01 21:45:39 +02:00
{
2019-10-02 23:28:20 +02:00
if ( g_aEntityLagData [ i ] . iRecordsValid )
{
g_aEntityLagData [ i ] . iRecordIndex + + ;
if ( g_aEntityLagData [ i ] . iRecordIndex > = MAX_RECORDS )
g_aEntityLagData [ i ] . iRecordIndex = 0 ;
g_aEntityLagData [ i ] . iRecordsValid - - ;
}
2019-10-01 21:45:39 +02:00
continue ;
}
2019-10-03 12:02:23 +02:00
LagRecord TmpRecord ;
RecordDataIntoRecord ( g_aEntityLagData [ i ] . iEntity , TmpRecord ) ;
2019-10-03 22:09:53 +02:00
// sleep detection
2019-10-02 18:54:19 +02:00
{
2019-10-03 12:02:23 +02:00
int iOldRecord = g_aEntityLagData [ i ] . iRecordIndex ;
2019-10-02 18:54:19 +02:00
2019-11-12 12:47:48 +01:00
if ( CompareVectors ( g_aaLagRecords [ i ] [ iOldRecord ] . vecAbsOrigin , TmpRecord . vecAbsOrigin ) & &
2019-11-12 23:55:38 +01:00
CompareVectors ( g_aaLagRecords [ i ] [ iOldRecord ] . angAbsRotation , TmpRecord . angAbsRotation ) )
2019-10-03 12:02:23 +02:00
{
g_aEntityLagData [ i ] . iNotMoving + + ;
}
else
{
g_aEntityLagData [ i ] . iNotMoving = 0 ;
2019-10-02 18:54:19 +02:00
}
2019-10-03 12:02:23 +02:00
if ( g_aEntityLagData [ i ] . iNotMoving > = MAX_RECORDS )
continue ;
2019-10-02 18:54:19 +02:00
}
2019-10-01 21:45:39 +02:00
g_aEntityLagData [ i ] . iRecordIndex + + ;
if ( g_aEntityLagData [ i ] . iRecordIndex > = MAX_RECORDS )
g_aEntityLagData [ i ] . iRecordIndex = 0 ;
if ( g_aEntityLagData [ i ] . iNumRecords < MAX_RECORDS )
2019-10-02 23:28:20 +02:00
g_aEntityLagData [ i ] . iRecordsValid = + + g_aEntityLagData [ i ] . iNumRecords ;
2019-10-01 21:45:39 +02:00
2019-10-03 12:02:23 +02:00
LagRecord_Copy ( g_aaLagRecords [ i ] [ g_aEntityLagData [ i ] . iRecordIndex ] , TmpRecord ) ;
2019-10-01 21:45:39 +02:00
}
2019-11-13 17:21:47 +01:00
}
2019-10-02 14:19:09 +02:00
2019-10-01 21:45:39 +02:00
void RecordDataIntoRecord ( int iEntity , LagRecord Record )
{
2019-11-12 23:55:38 +01:00
// Force recalculation of all values
int EFlags = GetEntData ( iEntity , g_iEFlags ) ;
EFlags | = EFL_DIRTY_ABSTRANSFORM ;
SetEntData ( iEntity , g_iEFlags , EFlags ) ;
2019-11-12 12:47:48 +01:00
SDKCall ( g_hCalcAbsolutePosition , iEntity ) ;
2019-11-12 23:55:38 +01:00
2019-11-12 12:47:48 +01:00
GetEntDataVector ( iEntity , g_iVecOrigin , Record . vecOrigin ) ;
GetEntDataVector ( iEntity , g_iVecAbsOrigin , Record . vecAbsOrigin ) ;
GetEntDataVector ( iEntity , g_iAngRotation , Record . angRotation ) ;
GetEntDataVector ( iEntity , g_iAngAbsRotation , Record . angAbsRotation ) ;
2019-11-13 21:02:36 +01:00
GetEntDataArray ( iEntity , g_iCoordinateFrame , view_as < int > ( Record . rgflCoordinateFrame ) , COORDINATE_FRAME_SIZE ) ;
2019-11-12 12:47:48 +01:00
Record . flSimulationTime = GetEntDataFloat ( iEntity , g_iSimulationTime ) ;
}
bool DoesRotationInvalidateSurroundingBox ( int iEntity )
{
int SolidFlags = GetEntData ( iEntity , g_iSolidFlags ) ;
if ( SolidFlags & FSOLID_ROOT_PARENT_ALIGNED )
return true ;
int SurroundType = GetEntData ( iEntity , g_iSurroundType ) ;
switch ( SurroundType )
{
case USE_COLLISION_BOUNDS_NEVER_VPHYSICS ,
USE_OBB_COLLISION_BOUNDS ,
USE_BEST_COLLISION_BOUNDS :
{
// IsBoundsDefinedInEntitySpace()
int SolidType = GetEntData ( iEntity , g_iSolidType ) ;
return ( ( SolidFlags & FSOLID_FORCE_WORLD_ALIGNED ) = = 0 ) & &
( SolidType ! = SOLID_BBOX ) & & ( SolidType ! = SOLID_NONE ) ;
}
case USE_HITBOXES ,
USE_GAME_CODE :
{
return true ;
}
case USE_ROTATION_EXPANDED_BOUNDS ,
USE_SPECIFIED_BOUNDS :
{
return false ;
}
default :
{
return true ;
}
}
2019-10-01 21:45:39 +02:00
}
2019-11-12 23:55:38 +01:00
void InvalidatePhysicsRecursive ( int iEntity )
2019-10-01 21:45:39 +02:00
{
2019-11-12 12:47:48 +01:00
// NetworkProp()->MarkPVSInformationDirty()
int fStateFlags = GetEdictFlags ( iEntity ) ;
fStateFlags | = FL_EDICT_DIRTY_PVS_INFORMATION ;
SetEdictFlags ( iEntity , fStateFlags ) ;
// CollisionProp()->MarkPartitionHandleDirty();
2019-11-12 23:55:38 +01:00
Address CollisionProp = GetEntityAddress ( iEntity ) + view_as < Address > ( g_iCollision ) ;
2019-11-12 12:47:48 +01:00
SDKCall ( g_hMarkPartitionHandleDirty , CollisionProp ) ;
if ( DoesRotationInvalidateSurroundingBox ( iEntity ) )
{
2019-11-12 23:55:38 +01:00
// CollisionProp()->MarkSurroundingBoundsDirty();
2019-11-12 12:47:48 +01:00
int EFlags = GetEntData ( iEntity , g_iEFlags ) ;
EFlags | = EFL_DIRTY_SURROUNDING_COLLISION_BOUNDS ;
SetEntData ( iEntity , g_iEFlags , EFlags ) ;
}
2019-10-01 21:45:39 +02:00
}
2019-11-12 23:55:38 +01:00
void RestoreEntityFromRecord ( int iEntity , LagRecord Record )
{
SetEntDataVector ( iEntity , g_iVecOrigin , Record . vecOrigin ) ;
SetEntDataVector ( iEntity , g_iVecAbsOrigin , Record . vecAbsOrigin ) ;
SetEntDataVector ( iEntity , g_iAngRotation , Record . angRotation ) ;
SetEntDataVector ( iEntity , g_iAngAbsRotation , Record . angAbsRotation ) ;
2019-11-13 21:02:36 +01:00
SetEntDataArray ( iEntity , g_iCoordinateFrame , view_as < int > ( Record . rgflCoordinateFrame ) , COORDINATE_FRAME_SIZE ) ;
2019-11-12 23:55:38 +01:00
SetEntDataFloat ( iEntity , g_iSimulationTime , Record . flSimulationTime ) ;
InvalidatePhysicsRecursive ( iEntity ) ;
}
2019-11-13 21:02:36 +01:00
2019-10-20 12:44:34 +02:00
bool AddEntityForLagCompensation ( int iEntity , bool bLateKill )
2019-10-01 21:45:39 +02:00
{
2019-10-05 15:17:10 +02:00
if ( g_bCleaningUp )
return false ;
2019-10-03 10:39:27 +02:00
if ( g_iNumEntities = = MAX_ENTITIES )
2019-10-16 15:18:09 +02:00
{
char sClassname [ 64 ] ;
GetEntityClassname ( iEntity , sClassname , sizeof ( sClassname ) ) ;
char sTargetname [ 64 ] ;
GetEntPropString ( iEntity , Prop_Data , " m_iName " , sTargetname , sizeof ( sTargetname ) ) ;
int iHammerID = GetEntProp ( iEntity , Prop_Data , " m_iHammerID " ) ;
PrintToBoth ( " [%d] OUT OF LAGCOMP SLOTS entity %d (%s) \" %s \" (#%d) " , GetGameTickCount ( ) , iEntity , sClassname , sTargetname , iHammerID ) ;
2019-10-03 10:39:27 +02:00
return false ;
2019-10-16 15:18:09 +02:00
}
2019-10-03 10:39:27 +02:00
2019-11-17 00:56:57 +01:00
if ( g_aLagCompensated [ iEntity ] ! = - 1 )
return false ;
2019-10-01 21:45:39 +02:00
2019-10-03 12:02:23 +02:00
int i = g_iNumEntities ;
g_iNumEntities + + ;
2019-10-01 21:45:39 +02:00
2019-11-17 00:56:57 +01:00
g_aLagCompensated [ iEntity ] = i ;
2019-10-03 12:02:23 +02:00
g_aEntityLagData [ i ] . iEntity = iEntity ;
2019-10-03 22:09:53 +02:00
g_aEntityLagData [ i ] . iRecordIndex = 0 ;
g_aEntityLagData [ i ] . iNumRecords = 1 ;
g_aEntityLagData [ i ] . iRecordsValid = 1 ;
2019-10-10 15:38:58 +02:00
g_aEntityLagData [ i ] . iSpawned = GetGameTickCount ( ) ;
2019-10-03 12:02:23 +02:00
g_aEntityLagData [ i ] . iDeleted = 0 ;
g_aEntityLagData [ i ] . iNotMoving = MAX_RECORDS ;
g_aEntityLagData [ i ] . bRestore = false ;
2019-10-20 12:44:34 +02:00
g_aEntityLagData [ i ] . bLateKill = bLateKill ;
2019-10-02 23:28:20 +02:00
2019-11-17 00:56:57 +01:00
if ( bLateKill )
2019-11-17 20:46:38 +01:00
{
2019-12-18 12:02:38 +01:00
SetBit ( g_aBlockTriggerTouchPlayers , iEntity ) ;
2019-11-17 20:46:38 +01:00
}
2019-11-17 00:56:57 +01:00
2019-10-16 15:18:09 +02:00
RecordDataIntoRecord ( iEntity , g_aaLagRecords [ i ] [ 0 ] ) ;
2019-10-03 22:09:53 +02:00
2019-10-03 12:02:23 +02:00
{
char sClassname [ 64 ] ;
2019-10-16 15:18:09 +02:00
GetEntityClassname ( iEntity , sClassname , sizeof ( sClassname ) ) ;
2019-10-02 23:28:20 +02:00
2019-10-03 12:02:23 +02:00
char sTargetname [ 64 ] ;
2019-10-16 15:18:09 +02:00
GetEntPropString ( iEntity , Prop_Data , " m_iName " , sTargetname , sizeof ( sTargetname ) ) ;
2019-10-02 23:28:20 +02:00
2019-10-16 15:18:09 +02:00
int iHammerID = GetEntProp ( iEntity , Prop_Data , " m_iHammerID " ) ;
2019-10-03 10:39:27 +02:00
2019-10-03 22:09:53 +02:00
PrintToBoth ( " [%d] added entity %d (%s) \" %s \" (#%d) under index %d " , GetGameTickCount ( ) , iEntity , sClassname , sTargetname , iHammerID , i ) ;
2019-10-01 21:45:39 +02:00
}
2019-10-03 10:39:27 +02:00
2019-10-03 12:02:23 +02:00
return true ;
2019-10-01 21:45:39 +02:00
}
2019-11-19 21:00:46 +01:00
bool RemoveEntityFromLagCompensation ( int iEntity )
{
int index = g_aLagCompensated [ iEntity ] ;
if ( index = = - 1 )
return false ;
RemoveRecord ( index ) ;
return true ;
}
2019-10-03 12:02:23 +02:00
void RemoveRecord ( int index )
{
2019-10-05 15:17:10 +02:00
if ( g_bCleaningUp )
return ;
2019-11-16 01:35:27 +01:00
int iEntity = g_aEntityLagData [ index ] . iEntity ;
2019-11-17 00:56:57 +01:00
if ( IsValidEntity ( iEntity ) )
2019-10-03 22:09:53 +02:00
{
char sClassname [ 64 ] ;
GetEntityClassname ( g_aEntityLagData [ index ] . iEntity , sClassname , sizeof ( sClassname ) ) ;
char sTargetname [ 64 ] ;
GetEntPropString ( g_aEntityLagData [ index ] . iEntity , Prop_Data , " m_iName " , sTargetname , sizeof ( sTargetname ) ) ;
int iHammerID = GetEntProp ( g_aEntityLagData [ index ] . iEntity , Prop_Data , " m_iHammerID " ) ;
PrintToBoth ( " [%d] RemoveRecord %d / %d (%s) \" %s \" (#%d), num: %d " , GetGameTickCount ( ) , index , g_aEntityLagData [ index ] . iEntity , sClassname , sTargetname , iHammerID , g_iNumEntities ) ;
}
2019-11-17 00:56:57 +01:00
g_aLagCompensated [ iEntity ] = - 1 ;
2019-12-18 12:02:38 +01:00
ClearBit ( g_aBlockTriggerTouchPlayers , iEntity ) ;
2019-11-17 20:46:38 +01:00
ClearBit ( g_aBlockTriggerMoved , iEntity ) ;
2019-10-05 15:17:10 +02:00
2019-10-20 12:44:34 +02:00
for ( int client = 1 ; client < = MaxClients ; client + + )
2019-10-05 15:17:10 +02:00
{
2019-12-18 12:02:38 +01:00
ClearBit ( g_aaFilterClientSolidTouch , client * MAX_EDICTS + iEntity ) ;
2019-10-05 15:17:10 +02:00
}
2019-10-06 20:24:43 +02:00
g_aEntityLagData [ index ] . iEntity = INVALID_ENT_REFERENCE ;
2019-10-03 12:02:23 +02:00
for ( int src = index + 1 ; src < g_iNumEntities ; src + + )
{
int dest = src - 1 ;
EntityLagData_Copy ( g_aEntityLagData [ dest ] , g_aEntityLagData [ src ] ) ;
2019-10-06 20:24:43 +02:00
g_aEntityLagData [ src ] . iEntity = INVALID_ENT_REFERENCE ;
2019-11-17 00:56:57 +01:00
g_aLagCompensated [ g_aEntityLagData [ dest ] . iEntity ] = dest ;
2019-10-03 12:02:23 +02:00
int iNumRecords = g_aEntityLagData [ dest ] . iNumRecords ;
for ( int i = 0 ; i < iNumRecords ; i + + )
{
LagRecord_Copy ( g_aaLagRecords [ dest ] [ i ] , g_aaLagRecords [ src ] [ i ] ) ;
}
}
g_iNumEntities - - ;
}
void EntityLagData_Copy ( EntityLagData obj , const EntityLagData other )
{
obj . iEntity = other . iEntity ;
obj . iRecordIndex = other . iRecordIndex ;
obj . iNumRecords = other . iNumRecords ;
obj . iRecordsValid = other . iRecordsValid ;
2019-10-10 15:38:58 +02:00
obj . iSpawned = other . iSpawned ;
2019-10-03 12:02:23 +02:00
obj . iDeleted = other . iDeleted ;
obj . iNotMoving = other . iNotMoving ;
obj . bRestore = other . bRestore ;
2019-10-20 12:44:34 +02:00
obj . bLateKill = other . bLateKill ;
2019-11-13 21:02:36 +01:00
2019-10-03 12:02:23 +02:00
LagRecord_Copy ( obj . RestoreData , other . RestoreData ) ;
}
void LagRecord_Copy ( LagRecord obj , const LagRecord other )
{
2019-11-13 21:02:36 +01:00
for ( int i = 0 ; i < 3 ; i + + )
{
obj . vecOrigin [ i ] = other . vecOrigin [ i ] ;
obj . vecAbsOrigin [ i ] = other . vecAbsOrigin [ i ] ;
obj . angRotation [ i ] = other . angRotation [ i ] ;
obj . angAbsRotation [ i ] = other . angAbsRotation [ i ] ;
}
2019-10-06 20:24:43 +02:00
obj . flSimulationTime = other . flSimulationTime ;
2019-11-12 12:47:48 +01:00
2019-11-13 21:02:36 +01:00
for ( int i = 0 ; i < COORDINATE_FRAME_SIZE ; i + + )
{
obj . rgflCoordinateFrame [ i ] = other . rgflCoordinateFrame [ i ] ;
}
2019-10-03 12:02:23 +02:00
}
2019-10-03 22:09:53 +02:00
2019-11-05 22:26:38 +01:00
bool CompareVectors ( const float vec1 [ 3 ] , const float vec2 [ 3 ] )
{
return vec1 [ 0 ] = = vec2 [ 0 ] & & vec1 [ 1 ] = = vec2 [ 1 ] & & vec1 [ 2 ] = = vec2 [ 2 ] ;
}
2019-10-03 22:09:53 +02:00
2019-11-13 21:02:36 +01:00
public Action Command_AddLagCompensation ( int client , int argc )
{
if ( argc < 1 )
{
ReplyToCommand ( client , " [SM] Usage: sm_unlag <entidx> [late] " ) ;
return Plugin_Handled ;
}
char sArgs [ 32 ] ;
GetCmdArg ( 1 , sArgs , sizeof ( sArgs ) ) ;
int entity = StringToInt ( sArgs ) ;
bool late = false ;
if ( argc > = 2 )
{
GetCmdArg ( 2 , sArgs , sizeof ( sArgs ) ) ;
late = view_as < bool > ( StringToInt ( sArgs ) ) ;
}
AddEntityForLagCompensation ( entity , late ) ;
return Plugin_Handled ;
}
public Action Command_CheckLagCompensated ( int client , int argc )
{
2019-11-19 21:00:46 +01:00
if ( argc = = 0 )
2019-11-13 21:02:36 +01:00
{
2019-11-19 21:00:46 +01:00
for ( int i = 0 ; i < g_iNumEntities ; i + + )
{
char sClassname [ 64 ] ;
GetEntityClassname ( g_aEntityLagData [ i ] . iEntity , sClassname , sizeof ( sClassname ) ) ;
2019-11-13 21:02:36 +01:00
2019-11-19 21:00:46 +01:00
char sTargetname [ 64 ] ;
GetEntPropString ( g_aEntityLagData [ i ] . iEntity , Prop_Data , " m_iName " , sTargetname , sizeof ( sTargetname ) ) ;
2019-11-13 21:02:36 +01:00
2019-11-19 21:00:46 +01:00
int iHammerID = GetEntProp ( g_aEntityLagData [ i ] . iEntity , Prop_Data , " m_iHammerID " ) ;
2019-11-13 21:02:36 +01:00
2019-11-19 21:00:46 +01:00
PrintToConsole ( client , " %2d. #%d %s \" %s \" (#%d) " , i , g_aEntityLagData [ i ] . iEntity , sClassname , sTargetname , iHammerID ) ;
}
return Plugin_Handled ;
2019-11-13 21:02:36 +01:00
}
2019-11-19 21:00:46 +01:00
for ( int iEntity = 0 ; iEntity < MAX_EDICTS ; iEntity + + )
2019-11-13 21:02:36 +01:00
{
bool bDeleted = false ;
for ( int j = 1 ; j < = MaxClients ; j + + )
{
2019-12-18 12:02:38 +01:00
if ( CheckBit ( g_aaFilterClientSolidTouch , j * MAX_EDICTS + iEntity ) )
2019-11-13 21:02:36 +01:00
{
bDeleted = true ;
break ;
}
}
2019-12-18 12:02:38 +01:00
bool bBlockPhysics = CheckBit ( g_aBlockTriggerTouchPlayers , iEntity ) ;
2019-11-19 21:00:46 +01:00
bool bBlockTriggerMoved = CheckBit ( g_aBlockTriggerMoved , iEntity ) ;
bool bBlacklisted = CheckBit ( g_aBlacklisted , iEntity ) ;
if ( bDeleted | | bBlockPhysics | | bBlockTriggerMoved | | bBlacklisted )
2019-11-13 21:02:36 +01:00
{
int index = - 1 ;
for ( int j = 0 ; j < g_iNumEntities ; j + + )
{
2019-11-19 21:00:46 +01:00
if ( g_aEntityLagData [ j ] . iEntity = = iEntity )
2019-11-13 21:02:36 +01:00
{
index = j ;
break ;
}
}
char sClassname [ 64 ] = " INVALID " ;
char sTargetname [ 64 ] = " INVALID " ;
int iHammerID = - 1 ;
2019-11-19 21:00:46 +01:00
if ( IsValidEntity ( iEntity ) )
2019-11-13 21:02:36 +01:00
{
2019-11-19 21:00:46 +01:00
GetEntityClassname ( iEntity , sClassname , sizeof ( sClassname ) ) ;
GetEntPropString ( iEntity , Prop_Data , " m_iName " , sTargetname , sizeof ( sTargetname ) ) ;
iHammerID = GetEntProp ( iEntity , Prop_Data , " m_iHammerID " ) ;
2019-11-13 21:02:36 +01:00
}
2019-11-19 21:00:46 +01:00
PrintToConsole ( client , " %2d. #%d %s \" %s \" (#%d) -> Phys: %d / TrigMov: %d / Deleted: %d / Black: %d " ,
index , iEntity , sClassname , sTargetname , iHammerID , bBlockPhysics , bBlockTriggerMoved , bDeleted , bBlacklisted ) ;
2019-11-13 21:02:36 +01:00
}
}
return Plugin_Handled ;
}
2019-10-03 22:09:53 +02:00
stock void PrintToBoth ( const char [ ] format , any . . . )
{
char buffer [ 254 ] ;
VFormat ( buffer , sizeof ( buffer ) , format , 2 ) ;
LogMessage ( buffer ) ;
2020-02-27 11:20:40 +01:00
for ( int client = 1 ; client < = MaxClients ; client + + )
{
if ( IsClientInGame ( client ) )
{
PrintToConsole ( client , " %s " , buffer ) ;
}
}
}
public Action DisableLagCompTimer ( Handle timer )
{
for ( int client = 1 ; client < = MaxClients ; client + + )
{
if ( g_bDisableLagComp [ client ] & & g_iDisableLagComp [ client ] < MAX_RECORDS )
{
g_iDisableLagComp [ client ] + + ;
}
else if ( ! g_bDisableLagComp [ client ] & & g_iDisableLagComp [ client ] > 0 )
{
g_iDisableLagComp [ client ] - - ;
}
}
return Plugin_Continue ;
}
public Action OnLagCompSettings ( int client , int args )
{
ShowSettingsMenu ( client ) ;
return Plugin_Handled ;
}
public Action OnToggleLagCompSettings ( int client , int args )
{
ToggleLagCompSettings ( client ) ;
return Plugin_Handled ;
}
public void ToggleLagCompSettings ( int client )
{
g_bDisableLagComp [ client ] = ! g_bDisableLagComp [ client ] ;
SetClientCookie ( client , g_hCookie_DisableLagComp , g_bDisableLagComp [ client ] ? " 1 " : " " ) ;
PrintToChat ( client , " \x04 [LagCompensation] \x01 LagCompensation has been %s. " , g_bDisableLagComp [ client ] ? " disabled " : " enabled " ) ;
}
public void ShowSettingsMenu ( int client )
{
Menu menu = new Menu ( MenuHandler_MainMenu ) ;
menu . SetTitle ( " LagCompensation Settings " , client ) ;
menu . ExitBackButton = true ;
char sBuffer [ 128 ] ;
Format ( sBuffer , sizeof ( sBuffer ) , " LagCompensation: %s " , g_bDisableLagComp [ client ] ? " Disabled " : " Enabled " ) ;
menu . AddItem ( " 0 " , sBuffer ) ;
menu . Display ( client , MENU_TIME_FOREVER ) ;
}
public void MenuHandler_CookieMenu ( int client , CookieMenuAction action , any info , char [ ] buffer , int maxlen )
{
switch ( action )
{
case ( CookieMenuAction_DisplayOption ) :
{
Format ( buffer , maxlen , " LagCompensation " , client ) ;
}
case ( CookieMenuAction_SelectOption ) :
{
ShowSettingsMenu ( client ) ;
}
}
}
public int MenuHandler_MainMenu ( Menu menu , MenuAction action , int client , int selection )
{
switch ( action )
2019-10-03 22:09:53 +02:00
{
2020-02-27 11:20:40 +01:00
case ( MenuAction_Select ) :
{
switch ( selection )
{
case ( 0 ) : ToggleLagCompSettings ( client ) ;
}
ShowSettingsMenu ( client ) ;
}
case ( MenuAction_Cancel ) :
{
ShowCookieMenu ( client ) ;
}
case ( MenuAction_End ) :
2019-10-03 22:09:53 +02:00
{
2020-02-27 11:20:40 +01:00
delete menu ;
2019-10-03 22:09:53 +02:00
}
}
2019-11-05 22:26:38 +01:00
}