Counter Strike : Global Offensive Source Code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

861 lines
27 KiB

  1. //========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "cbase.h"
  8. #include "tier0/platform.h"
  9. #include "player_lagcompensation.h"
  10. #include "usercmd.h"
  11. #include "inetchannelinfo.h"
  12. #include "utllinkedlist.h"
  13. #include "BaseAnimatingOverlay.h"
  14. #include "tier0/vprof.h"
  15. #include "collisionutils.h"
  16. // memdbgon must be the last include file in a .cpp file!!!
  17. #include "tier0/memdbgon.h"
  18. #define LC_NONE 0
  19. #define LC_ALIVE (1<<0)
  20. #define LC_ORIGIN_CHANGED (1<<8)
  21. #define LC_ANGLES_CHANGED (1<<9)
  22. #define LC_SIZE_CHANGED (1<<10)
  23. #define LC_ANIMATION_CHANGED (1<<11)
  24. #define LAG_COMPENSATION_TELEPORTED_DISTANCE_SQR ( 64.0f * 64.0f )
  25. #define LAG_COMPENSATION_EPS_SQR ( 0.1f * 0.1f )
  26. // Allow 4 units of error ( about 1 / 8 bbox width )
  27. #define LAG_COMPENSATION_ERROR_EPS_SQR ( 4.0f * 4.0f )
  28. ConVar sv_unlag( "sv_unlag", "1", FCVAR_DEVELOPMENTONLY, "Enables player lag compensation" );
  29. ConVar sv_maxunlag( "sv_maxunlag", "1.0", FCVAR_DEVELOPMENTONLY, "Maximum lag compensation in seconds", true, 0.0f, true, 1.0f );
  30. ConVar sv_lagflushbonecache( "sv_lagflushbonecache", "1", FCVAR_DEVELOPMENTONLY, "Flushes entity bone cache on lag compensation" );
  31. ConVar sv_showlagcompensation( "sv_showlagcompensation", "0", FCVAR_CHEAT, "Show lag compensated hitboxes whenever a player is lag compensated." );
  32. ConVar sv_showlagcompensation_duration( "sv_showlagcompensation_duration", "4.0", FCVAR_CHEAT, "Duration to show lag-compensated hitboxes", true, 0.0f, true, 10.0f );
  33. ConVar sv_lagcompensationforcerestore( "sv_lagcompensationforcerestore", "1", FCVAR_CHEAT, "Don't test validity of a lag comp restore, just do it.");
  34. ConVar sv_unlag_fixstuck( "sv_unlag_fixstuck", "0", FCVAR_DEVELOPMENTONLY, "Disallow backtracking a player for lag compensation if it will cause them to become stuck" );
  35. ConVar sv_lagpushticks( "sv_lagpushticks", "0", FCVAR_DEVELOPMENTONLY, "Push computed lag compensation amount by this many ticks." );
  36. ConVar sv_lagcompensateself( "sv_lagcompensateself", "0", FCVAR_CHEAT, "Player can lag compensate themselves." );
  37. #define COSINE_20F 0.93969f
  38. #define SINE_20F 0.34202f
  39. static CLagCompensationManager g_LagCompensationManager( "CLagCompensationManager" );
  40. ILagCompensationManager *lagcompensation = &g_LagCompensationManager;
  41. //
  42. // Try to take the player from his current origin to vWantedPos.
  43. // If it can't get there, leave the player where he is.
  44. //
  45. ConVar sv_unlag_debug( "sv_unlag_debug", "0", FCVAR_GAMEDLL | FCVAR_DEVELOPMENTONLY );
  46. float g_flFractionScale = 0.95;
  47. static void LC_SetAbsOrigin( CBaseEntity *entity, const Vector &vecAbsOrigin, bool bFireTriggers )
  48. {
  49. entity->SetAbsOrigin( vecAbsOrigin );
  50. if ( bFireTriggers )
  51. {
  52. entity->PhysicsTouchTriggers();
  53. }
  54. }
  55. static void RestoreEntityTo( CBaseEntity *pEntity, const Vector &vWantedPos )
  56. {
  57. // Try to move to the wanted position from our current position.
  58. trace_t tr;
  59. VPROF_BUDGET( "RestoreEntityTo", "CLagCompensationManager" );
  60. if( sv_lagcompensationforcerestore.GetBool() )
  61. {
  62. // We don't have to test for validity. Just put them back where you found them.
  63. LC_SetAbsOrigin( pEntity, vWantedPos, true );
  64. }
  65. unsigned int mask = MASK_PLAYERSOLID;
  66. UTIL_TraceEntity( pEntity, vWantedPos, vWantedPos, mask, pEntity, COLLISION_GROUP_PLAYER_MOVEMENT, &tr );
  67. if ( tr.startsolid || tr.allsolid )
  68. {
  69. if ( sv_unlag_debug.GetBool() )
  70. {
  71. DevMsg( "RestoreEntityTo could not restore player position for client \"%d\" ( %.1f %.1f %.1f )\n",
  72. pEntity->entindex(), vWantedPos.x, vWantedPos.y, vWantedPos.z );
  73. }
  74. UTIL_TraceEntity( pEntity, pEntity->GetAbsOrigin(), vWantedPos, mask, pEntity, COLLISION_GROUP_PLAYER_MOVEMENT, &tr );
  75. if ( tr.startsolid || tr.allsolid )
  76. {
  77. // In this case, the guy got stuck back wherever we lag compensated him to. Nasty.
  78. if ( sv_unlag_debug.GetBool() )
  79. DevMsg( " restore failed entirely\n" );
  80. }
  81. else
  82. {
  83. // We can get to a valid place, but not all the way back to where we were.
  84. Vector vPos;
  85. VectorLerp( pEntity->GetAbsOrigin(), vWantedPos, tr.fraction * g_flFractionScale, vPos );
  86. LC_SetAbsOrigin( pEntity, vPos, true );
  87. if ( sv_unlag_debug.GetBool() )
  88. DevMsg( " restore got most of the way\n" );
  89. }
  90. }
  91. else
  92. {
  93. // Cool, the player can go back to whence he came.
  94. LC_SetAbsOrigin( pEntity, tr.endpos, true );
  95. }
  96. }
  97. // Mappers can flag certain additional entities to lag compensate, this handles them
  98. void CLagCompensationManager::AddAdditionalEntity( CBaseEntity *pEntity )
  99. {
  100. EHANDLE eh;
  101. eh = pEntity;
  102. if ( m_AdditionalEntities.Find( eh ) == m_AdditionalEntities.InvalidIndex() )
  103. {
  104. m_AdditionalEntities.Insert( eh );
  105. }
  106. }
  107. void CLagCompensationManager::RemoveAdditionalEntity( CBaseEntity *pEntity )
  108. {
  109. EHANDLE eh;
  110. eh = pEntity;
  111. m_AdditionalEntities.Remove( eh );
  112. }
  113. //-----------------------------------------------------------------------------
  114. // Purpose: Called once per frame after all entities have had a chance to think
  115. //-----------------------------------------------------------------------------
  116. void CLagCompensationManager::FrameUpdatePostEntityThink()
  117. {
  118. if ( (gpGlobals->maxClients <= 1) || !sv_unlag.GetBool() )
  119. {
  120. ClearHistory();
  121. return;
  122. }
  123. VPROF_BUDGET( "FrameUpdatePostEntityThink", "CLagCompensationManager" );
  124. CUtlRBTree< CBaseEntity * > rbEntityList( 0, 0, DefLessFunc( CBaseEntity * ) );
  125. // Add active players
  126. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  127. {
  128. CBasePlayer *pPlayer = UTIL_PlayerByIndex( i );
  129. if ( !pPlayer )
  130. {
  131. continue;
  132. }
  133. if ( rbEntityList.Find( pPlayer ) == rbEntityList.InvalidIndex() )
  134. {
  135. rbEntityList.Insert( pPlayer );
  136. }
  137. }
  138. // Add any additional entities
  139. for ( int i = m_AdditionalEntities.FirstInorder(); i != m_AdditionalEntities.InvalidIndex(); i = m_AdditionalEntities.NextInorder( i ) )
  140. {
  141. CBaseEntity *pAddEntity = m_AdditionalEntities[ i ].Get();
  142. if ( !pAddEntity )
  143. continue;
  144. if ( rbEntityList.Find( pAddEntity ) == rbEntityList.InvalidIndex() )
  145. {
  146. rbEntityList.Insert( pAddEntity );
  147. }
  148. }
  149. // Now record the actual history information
  150. for ( int i = rbEntityList.FirstInorder(); i != rbEntityList.InvalidIndex(); i = rbEntityList.NextInorder( i ) )
  151. {
  152. CBaseEntity *pEntity = rbEntityList[ i ];
  153. EHANDLE eh;
  154. eh = pEntity;
  155. int slot = m_CompensatedEntities.Find( eh );
  156. if ( slot == m_CompensatedEntities.InvalidIndex() )
  157. {
  158. EntityLagData *pNewEntry = new EntityLagData();
  159. slot = m_CompensatedEntities.Insert( eh, pNewEntry );
  160. }
  161. EntityLagData *ld = m_CompensatedEntities[ slot ];
  162. RecordDataIntoTrack( pEntity, &ld->m_LagRecords, true );
  163. }
  164. }
  165. //-----------------------------------------------------------------------------
  166. // Purpose: Called during gamemovment weapon firing to set up/restore after lag compensation around the bullet traces
  167. //-----------------------------------------------------------------------------
  168. void CLagCompensationManager::StartLagCompensation( CBasePlayer *player, LagCompensationType lagCompensationType, const Vector& weaponPos, const QAngle &weaponAngles, float weaponRange )
  169. {
  170. Assert(!m_isCurrentlyDoingCompensation);
  171. // Assume no entities need to be restored
  172. CUtlVector< EHANDLE > invalidList;
  173. FOR_EACH_MAP( m_CompensatedEntities, i )
  174. {
  175. EntityLagData *ld = m_CompensatedEntities[ i ];
  176. EHANDLE key;
  177. key = m_CompensatedEntities.Key( i );
  178. if ( !key.Get() )
  179. {
  180. // Note that the EHANDLE is NULL now
  181. invalidList.AddToTail( key );
  182. continue;
  183. }
  184. // Clear state
  185. ld->m_bRestoreEntity = false;
  186. ld->m_RestoreData.Clear();
  187. ld->m_ChangeData.Clear();
  188. }
  189. // Wipe any deleted entities from the list
  190. for ( int i = 0; i < invalidList.Count(); ++i )
  191. {
  192. int slot = m_CompensatedEntities.Find( invalidList[ i ] );
  193. Assert( slot != m_CompensatedEntities.InvalidIndex() );
  194. if ( slot == m_CompensatedEntities.InvalidIndex() )
  195. continue;
  196. EntityLagData *ld = m_CompensatedEntities[ slot ];
  197. delete ld;
  198. m_CompensatedEntities.RemoveAt( slot );
  199. }
  200. m_bNeedToRestore = false;
  201. m_pCurrentPlayer = player;
  202. if ( !player->m_bLagCompensation // Player not wanting lag compensation
  203. || (gpGlobals->maxClients <= 1) // no lag compensation in single player
  204. || !sv_unlag.GetBool() // disabled by server admin
  205. || player->IsBot() // not for bots
  206. || player->IsObserver() // not for spectators
  207. )
  208. return;
  209. const CUserCmd *cmd = player->GetCurrentUserCommand();
  210. if ( !cmd )
  211. {
  212. // This can hit if m_hActiveWeapon incorrectly gets set to a weapon not actually owned by the local player
  213. // (since lag comp asks for the GetPlayerOwner() which will have m_pCurrentCommand == NULL,
  214. // not the player currently executing a CUserCmd).
  215. Error( "CLagCompensationManager::StartLagCompensation with NULL CUserCmd!!!\n" );
  216. }
  217. // NOTE: Put this here so that it won't show up in single player mode.
  218. VPROF_BUDGET( "StartLagCompensation", VPROF_BUDGETGROUP_OTHER_NETWORKING );
  219. m_isCurrentlyDoingCompensation = true;
  220. // correct is the amount of time we have to correct game time
  221. float correct = 0.0f;
  222. // Get true latency
  223. INetChannelInfo *nci = engine->GetPlayerNetInfo( player->entindex() );
  224. if ( nci )
  225. {
  226. // add network latency
  227. correct+= nci->GetLatency( FLOW_OUTGOING );
  228. }
  229. // NOTE: do these computations in float time, not ticks, to avoid big roundoff error accumulations in the math
  230. // add view interpolation latency see C_BaseEntity::GetInterpolationAmount()
  231. correct += player->m_fLerpTime;
  232. // check bounds [0,sv_maxunlag]
  233. correct = clamp( correct, 0.0f, sv_maxunlag.GetFloat() );
  234. // correct tick send by player
  235. float flTargetTime = TICKS_TO_TIME( cmd->tick_count ) - player->m_fLerpTime;
  236. // calculate difference between tick sent by player and our latency based tick
  237. float deltaTime = correct - ( gpGlobals->curtime - flTargetTime );
  238. if ( fabs( deltaTime ) > 0.2f )
  239. {
  240. // difference between cmd time and latency is too big > 200ms, use time correction based on latency
  241. // DevMsg("StartLagCompensation: delta too big (%.3f)\n", deltaTime );
  242. flTargetTime = gpGlobals->curtime - correct;
  243. }
  244. flTargetTime += TICKS_TO_TIME( sv_lagpushticks.GetInt() );
  245. m_lagCompensationType = lagCompensationType;
  246. m_weaponPos = weaponPos;
  247. m_weaponAngles = weaponAngles;
  248. m_weaponRange = weaponRange;
  249. // populate position, angles and range if the provided values are zeroed
  250. if ( m_weaponPos == vec3_origin )
  251. {
  252. m_weaponPos = m_pCurrentPlayer->EyePosition();
  253. }
  254. if ( m_weaponAngles == vec3_angle )
  255. {
  256. m_weaponAngles = m_pCurrentPlayer->EyeAngles();
  257. }
  258. if ( m_weaponRange <= 0 )
  259. {
  260. m_weaponRange = FLOAT32_MAX;
  261. }
  262. // Iterate all lag compensatable entities
  263. const CBitVec<MAX_EDICTS> *pEntityTransmitBits = engine->GetEntityTransmitBitsForClient( player->entindex() - 1 );
  264. FOR_EACH_MAP( m_CompensatedEntities, i )
  265. {
  266. EntityLagData *ld = m_CompensatedEntities[ i ];
  267. EHANDLE key = m_CompensatedEntities.Key( i );
  268. CBaseEntity *pEntity = key.Get();
  269. if ( !pEntity )
  270. {
  271. // Stale entity in list, fixed up on next time through loop
  272. continue;
  273. }
  274. // Don't lag compensate self, unless we've set a convar explicitly allowing it
  275. if ( player == pEntity && !sv_lagcompensateself.GetBool() )
  276. {
  277. continue;
  278. }
  279. // Custom checks for if things should lag compensate (based on things like what team the entity is associated with).
  280. if ( !player->WantsLagCompensationOnEntity( pEntity, cmd, pEntityTransmitBits ) )
  281. continue;
  282. // Move entity back in time and remember that fact
  283. ld->m_bRestoreEntity = BacktrackEntity( pEntity, flTargetTime, &ld->m_LagRecords, &ld->m_RestoreData, &ld->m_ChangeData, true );
  284. }
  285. }
  286. bool CLagCompensationManager::BacktrackEntity( CBaseEntity *entity, float flTargetTime, LagRecordList *track, LagRecord *restore, LagRecord *change, bool wantsAnims )
  287. {
  288. Vector org, mins, maxs;
  289. QAngle ang;
  290. VPROF_BUDGET( "BacktrackEntity", "CLagCompensationManager" );
  291. // check if we have at least one entry
  292. if ( track->Count() <= 0 )
  293. return false;
  294. intp curr = track->Head();
  295. LagRecord *prevRecord = NULL;
  296. LagRecord *record = NULL;
  297. Vector prevOrg = entity->GetAbsOrigin();
  298. // Walk context looking for any invalidating event
  299. while( track->IsValidIndex(curr) )
  300. {
  301. // remember last record
  302. prevRecord = record;
  303. // get next record
  304. record = &track->Element( curr );
  305. if ( !(record->m_fFlags & LC_ALIVE) )
  306. {
  307. // entity must be alive, lost track
  308. return false;
  309. }
  310. Vector delta = record->m_vecOrigin - prevOrg;
  311. if ( delta.LengthSqr() > LAG_COMPENSATION_TELEPORTED_DISTANCE_SQR )
  312. {
  313. // lost track, too much difference
  314. return false;
  315. }
  316. // did we find a context smaller than target time ?
  317. if ( record->m_flSimulationTime <= flTargetTime )
  318. break; // hurra, stop
  319. prevOrg = record->m_vecOrigin;
  320. // go one step back
  321. curr = track->Next( curr );
  322. }
  323. Assert( record );
  324. if ( !record )
  325. {
  326. if ( sv_unlag_debug.GetBool() )
  327. {
  328. DevMsg( "No valid positions in history for BacktrackPlayer client ( %d )\n", entity->entindex() );
  329. }
  330. return false;
  331. }
  332. float frac = 0.0f;
  333. if ( prevRecord &&
  334. (record->m_flSimulationTime < flTargetTime) &&
  335. (record->m_flSimulationTime < prevRecord->m_flSimulationTime) )
  336. {
  337. // we didn't find the exact time but have a valid previous record
  338. // so interpolate between these two records;
  339. Assert( prevRecord->m_flSimulationTime > record->m_flSimulationTime );
  340. Assert( flTargetTime < prevRecord->m_flSimulationTime );
  341. // calc fraction between both records
  342. frac = ( flTargetTime - record->m_flSimulationTime ) /
  343. ( prevRecord->m_flSimulationTime - record->m_flSimulationTime );
  344. Assert( frac > 0 && frac < 1 ); // should never extrapolate
  345. ang = Lerp( frac, record->m_vecAngles, prevRecord->m_vecAngles );
  346. org = Lerp( frac, record->m_vecOrigin, prevRecord->m_vecOrigin );
  347. mins = Lerp( frac, record->m_vecMins, prevRecord->m_vecMins );
  348. maxs = Lerp( frac, record->m_vecMaxs, prevRecord->m_vecMaxs );
  349. }
  350. else
  351. {
  352. // we found the exact record or no other record to interpolate with
  353. // just copy these values since they are the best we have
  354. ang = record->m_vecAngles;
  355. org = record->m_vecOrigin;
  356. mins = record->m_vecMins;
  357. maxs = record->m_vecMaxs;
  358. }
  359. // See if this is still a valid position for us to teleport to
  360. if ( sv_unlag_fixstuck.GetBool() )
  361. {
  362. // Try to move to the wanted position from our current position.
  363. trace_t tr;
  364. UTIL_TraceEntity( entity, org, org, MASK_PLAYERSOLID, &tr );
  365. if ( tr.startsolid || tr.allsolid )
  366. {
  367. if ( sv_unlag_debug.GetBool() )
  368. DevMsg( "WARNING: BackupPlayer trying to back player into a bad position - client %d\n", entity->entindex() );
  369. CBaseEntity *pHitEntity = tr.m_pEnt;
  370. // don't lag compensate the current player
  371. if ( pHitEntity && ( pHitEntity != m_pCurrentPlayer ) )
  372. {
  373. // Find it
  374. EHANDLE eh;
  375. eh = pHitEntity;
  376. int slot = m_CompensatedEntities.Find( eh );
  377. if ( slot != m_CompensatedEntities.InvalidIndex() )
  378. {
  379. EntityLagData *ld = m_CompensatedEntities[ slot ];
  380. // If we haven't backtracked this player, do it now
  381. // this deliberately ignores WantsLagCompensationOnEntity.
  382. if ( !ld->m_bRestoreEntity )
  383. {
  384. // Temp turn this flag on
  385. ld->m_bRestoreEntity = true;
  386. BacktrackEntity( pHitEntity, flTargetTime, &ld->m_LagRecords, &ld->m_RestoreData, &ld->m_ChangeData, true );
  387. // Remove the temp flag
  388. ld->m_bRestoreEntity = false;
  389. }
  390. }
  391. }
  392. // now trace us back as far as we can go
  393. unsigned int mask = MASK_PLAYERSOLID;
  394. UTIL_TraceEntity( entity, entity->GetAbsOrigin(), org, mask, &tr );
  395. if ( tr.startsolid || tr.allsolid )
  396. {
  397. // Our starting position is bogus
  398. if ( sv_unlag_debug.GetBool() )
  399. DevMsg( "Backtrack failed completely, bad starting position\n" );
  400. }
  401. else
  402. {
  403. // We can get to a valid place, but not all the way to the target
  404. Vector vPos;
  405. VectorLerp( entity->GetAbsOrigin(), org, tr.fraction * g_flFractionScale, vPos );
  406. // This is as close as we're going to get
  407. org = vPos;
  408. if ( sv_unlag_debug.GetBool() )
  409. DevMsg( "Backtrack got most of the way\n" );
  410. }
  411. }
  412. }
  413. // See if this represents a change for the entity
  414. int flags = 0;
  415. QAngle angdiff = entity->GetAbsAngles() - ang;
  416. Vector orgdiff = entity->GetAbsOrigin() - org;
  417. // Always remember the pristine simulation time in case we need to restore it.
  418. restore->m_flSimulationTime = entity->GetSimulationTime();
  419. if ( angdiff.LengthSqr() > LAG_COMPENSATION_EPS_SQR )
  420. {
  421. flags |= LC_ANGLES_CHANGED;
  422. restore->m_vecAngles = entity->GetAbsAngles();
  423. entity->SetAbsAngles( ang );
  424. change->m_vecAngles = ang;
  425. }
  426. // Use absolute equality here
  427. if ( ( mins != entity->WorldAlignMins() ) ||
  428. ( maxs != entity->WorldAlignMaxs() ) )
  429. {
  430. flags |= LC_SIZE_CHANGED;
  431. restore->m_vecMins = entity->WorldAlignMins() ;
  432. restore->m_vecMaxs = entity->WorldAlignMaxs();
  433. entity->SetSize( mins, maxs );
  434. change->m_vecMins = mins;
  435. change->m_vecMaxs = maxs;
  436. }
  437. // Note, do origin at end since it causes a relink into the k/d tree
  438. if ( orgdiff.LengthSqr() > LAG_COMPENSATION_EPS_SQR )
  439. {
  440. flags |= LC_ORIGIN_CHANGED;
  441. restore->m_vecOrigin = entity->GetAbsOrigin();
  442. entity->SetAbsOrigin( org );
  443. change->m_vecOrigin = org;
  444. }
  445. bool skipAnims = false;
  446. switch (m_lagCompensationType)
  447. {
  448. case LAG_COMPENSATE_BOUNDS:
  449. skipAnims = true;
  450. break;
  451. case LAG_COMPENSATE_HITBOXES:
  452. skipAnims = false;
  453. break;
  454. case LAG_COMPENSATE_HITBOXES_ALONG_RAY:
  455. skipAnims = false;
  456. {
  457. // don't bother to set up lag-compensated animation layers on entities that fall outside a 20-degree cone from the weapon firing point
  458. Vector vecWeaponForward;
  459. AngleVectors( m_weaponAngles, &vecWeaponForward );
  460. if ( !IsSphereIntersectingCone( entity->WorldSpaceCenter(), entity->BoundingRadius() + 10.0f, m_weaponPos, vecWeaponForward, SINE_20F, COSINE_20F ) )
  461. {
  462. skipAnims = true;
  463. //indicate ignored entities with a red box
  464. if( sv_showlagcompensation.GetInt() == 1 && entity->GetBaseAnimating() )
  465. debugoverlay->AddBoxOverlay( entity->WorldSpaceCenter(), Vector(-3,-3,-3), Vector(3,3,3), QAngle(0,0,0), 255,0,0,255, sv_showlagcompensation_duration.GetFloat() * 0.125f );
  466. }
  467. }
  468. break;
  469. }
  470. if( !skipAnims && wantsAnims && entity->GetBaseAnimating() )
  471. {
  472. CBaseAnimating *pAnimating = entity->GetBaseAnimating();
  473. // Sorry for the loss of the optimization for the case of people
  474. // standing still, but you breathe even on the server.
  475. // This is quicker than actually comparing all bazillion floats.
  476. flags |= LC_ANIMATION_CHANGED;
  477. restore->m_masterSequence = pAnimating->GetSequence();
  478. restore->m_masterCycle = pAnimating->GetCycle();
  479. pAnimating->SetSequence(record->m_masterSequence);
  480. pAnimating->SetCycle(record->m_masterCycle);
  481. // populate restore record's pose parameters
  482. CStudioHdr *pHdr = pAnimating->GetModelPtr();
  483. if ( pHdr )
  484. {
  485. for( int i=0; i<pHdr->GetNumPoseParameters(); i++ )
  486. {
  487. restore->m_flPoseParameters[i] = pAnimating->GetPoseParameter( i );
  488. // apply the record's pose parameter
  489. pAnimating->SetPoseParameter( i, record->m_flPoseParameters[i] );
  490. }
  491. }
  492. ////////////////////////
  493. // Now do all the layers
  494. //
  495. CBaseAnimatingOverlay *pAnimatingOverlay = entity->GetBaseAnimatingOverlay();
  496. if ( pAnimatingOverlay )
  497. {
  498. int layerCount = pAnimatingOverlay->GetNumAnimOverlays();
  499. for( int layerIndex = 0; layerIndex < layerCount; ++layerIndex )
  500. {
  501. CAnimationLayer *currentLayer = pAnimatingOverlay->GetAnimOverlay(layerIndex);
  502. if( currentLayer )
  503. {
  504. restore->m_layerRecords[layerIndex].m_cycle = currentLayer->m_flCycle;
  505. restore->m_layerRecords[layerIndex].m_order = currentLayer->m_nOrder;
  506. restore->m_layerRecords[layerIndex].m_sequence = currentLayer->m_nSequence;
  507. restore->m_layerRecords[layerIndex].m_weight = currentLayer->m_flWeight;
  508. currentLayer->m_flCycle = record->m_layerRecords[layerIndex].m_cycle;
  509. currentLayer->m_nOrder = record->m_layerRecords[layerIndex].m_order;
  510. currentLayer->m_nSequence = record->m_layerRecords[layerIndex].m_sequence;
  511. currentLayer->m_flWeight = record->m_layerRecords[layerIndex].m_weight;
  512. }
  513. }
  514. }
  515. //indicate lag-compensated entities with a green box
  516. if( sv_showlagcompensation.GetInt() == 1 && entity->GetBaseAnimating() )
  517. debugoverlay->AddBoxOverlay( entity->WorldSpaceCenter(), Vector(-3,-3,-3), Vector(3,3,3), QAngle(0,0,0), 0,255,0,255, sv_showlagcompensation_duration.GetFloat() * 0.125f );
  518. }
  519. if ( !flags )
  520. return false; // we didn't change anything
  521. if ( sv_lagflushbonecache.GetBool() && (flags & LC_ANIMATION_CHANGED) && entity->GetBaseAnimating() )
  522. entity->GetBaseAnimating()->InvalidateBoneCache();
  523. /*
  524. char text[256]; Q_snprintf( text, sizeof(text), "time %.2f", flTargetTime );
  525. pPlayer->DrawServerHitboxes( 10 );
  526. NDebugOverlay::Text( org, text, false, 10 );
  527. if ( skipAnims )
  528. {
  529. NDebugOverlay::EntityBounds( pPlayer, 0, 255, 0, 32, 10 );
  530. }
  531. else
  532. {
  533. NDebugOverlay::EntityBounds( pPlayer, 255, 0, 0, 32, 10 );
  534. }
  535. */
  536. m_bNeedToRestore = true; // we changed at least one entity
  537. restore->m_fFlags = flags; // we need to restore these flags
  538. change->m_fFlags = flags; // we have changed these flags
  539. if( sv_showlagcompensation.GetInt() == 1 && entity->GetBaseAnimating() )
  540. {
  541. entity->GetBaseAnimating()->DrawServerHitboxes(sv_showlagcompensation_duration.GetFloat(), true);
  542. }
  543. return true;// Yes, this guy has been backtracked
  544. }
  545. void CLagCompensationManager::FinishLagCompensation( CBasePlayer *player )
  546. {
  547. VPROF_BUDGET_FLAGS( "FinishLagCompensation", VPROF_BUDGETGROUP_OTHER_NETWORKING, BUDGETFLAG_CLIENT|BUDGETFLAG_SERVER );
  548. m_isCurrentlyDoingCompensation = false;
  549. if ( !m_bNeedToRestore )
  550. return; // no entity was changed at all
  551. // Iterate all active entities
  552. FOR_EACH_MAP( m_CompensatedEntities, i )
  553. {
  554. EntityLagData *ld = m_CompensatedEntities[ i ];
  555. // entity wasn't changed by lag compensation
  556. if ( !ld->m_bRestoreEntity )
  557. continue;
  558. CBaseEntity *pEntity = m_CompensatedEntities.Key( i ).Get();
  559. if ( !pEntity )
  560. continue;
  561. LagRecord *restore = &ld->m_RestoreData;
  562. LagRecord *change = &ld->m_ChangeData;
  563. RestoreEntityFromRecords( pEntity, restore, change, true );
  564. }
  565. }
  566. void CLagCompensationManager::RecordDataIntoTrack( CBaseEntity *entity, LagRecordList *track, bool wantsAnims )
  567. {
  568. Assert( track->Count() < 1000 ); // insanity check
  569. // remove all records before that time:
  570. int flDeadtime = gpGlobals->curtime - sv_maxunlag.GetFloat();
  571. // remove tail records that are too old
  572. intp tailIndex = track->Tail();
  573. while ( track->IsValidIndex( tailIndex ) )
  574. {
  575. LagRecord &tail = track->Element( tailIndex );
  576. // if tail is within limits, stop
  577. if ( tail.m_flSimulationTime >= flDeadtime )
  578. break;
  579. // remove tail, get new tail
  580. track->Remove( tailIndex );
  581. tailIndex = track->Tail();
  582. }
  583. // check if head has same simulation time
  584. if ( track->Count() > 0 )
  585. {
  586. LagRecord &head = track->Element( track->Head() );
  587. // check if player changed simulation time since last time updated
  588. if ( head.m_flSimulationTime >= entity->GetSimulationTime() )
  589. return; // don't add new entry for same or older time
  590. }
  591. // add new record to entity track
  592. LagRecord &record = track->Element( track->AddToHead() );
  593. record.m_fFlags = 0;
  594. if ( entity->IsAlive() )
  595. {
  596. record.m_fFlags |= LC_ALIVE;
  597. }
  598. record.m_flSimulationTime = entity->GetSimulationTime();
  599. record.m_vecAngles = entity->GetAbsAngles();
  600. record.m_vecOrigin = entity->GetAbsOrigin();
  601. record.m_vecMaxs = entity->WorldAlignMaxs();
  602. record.m_vecMins = entity->WorldAlignMins();
  603. CBaseAnimating *pAnimating = entity->GetBaseAnimating();
  604. if( wantsAnims && pAnimating )
  605. {
  606. CBaseAnimatingOverlay *pAnimatingOverlay = entity->GetBaseAnimatingOverlay();
  607. if ( pAnimatingOverlay )
  608. {
  609. int layerCount = pAnimatingOverlay->GetNumAnimOverlays();
  610. for( int layerIndex = 0; layerIndex < layerCount; ++layerIndex )
  611. {
  612. CAnimationLayer *currentLayer = pAnimatingOverlay->GetAnimOverlay(layerIndex);
  613. if( currentLayer )
  614. {
  615. record.m_layerRecords[layerIndex].m_cycle = currentLayer->m_flCycle;
  616. record.m_layerRecords[layerIndex].m_order = currentLayer->m_nOrder;
  617. record.m_layerRecords[layerIndex].m_sequence = currentLayer->m_nSequence;
  618. record.m_layerRecords[layerIndex].m_weight = currentLayer->m_flWeight;
  619. }
  620. }
  621. }
  622. record.m_masterSequence = pAnimating->GetSequence();
  623. record.m_masterCycle = pAnimating->GetCycle();
  624. CStudioHdr *pHdr = pAnimating->GetModelPtr();
  625. if ( pHdr )
  626. {
  627. for( int i=0; i<pHdr->GetNumPoseParameters(); i++ )
  628. {
  629. record.m_flPoseParameters[i] = pAnimating->GetPoseParameter(i);
  630. }
  631. }
  632. }
  633. }
  634. void CLagCompensationManager::RestoreEntityFromRecords( CBaseEntity *entity, LagRecord *restore, LagRecord *change, bool wantsAnims )
  635. {
  636. bool restoreSimulationTime = false;
  637. if ( restore->m_fFlags & LC_SIZE_CHANGED )
  638. {
  639. restoreSimulationTime = true;
  640. // see if simulation made any changes, if no, then do the restore, otherwise,
  641. // leave new values in
  642. if ( entity->WorldAlignMins() == change->m_vecMins &&
  643. entity->WorldAlignMaxs() == change->m_vecMaxs )
  644. {
  645. // Restore it
  646. entity->SetSize( restore->m_vecMins, restore->m_vecMaxs );
  647. }
  648. }
  649. if ( restore->m_fFlags & LC_ANGLES_CHANGED )
  650. {
  651. restoreSimulationTime = true;
  652. if ( entity->GetAbsAngles() == change->m_vecAngles )
  653. {
  654. entity->SetAbsAngles( restore->m_vecAngles );
  655. }
  656. }
  657. if ( restore->m_fFlags & LC_ORIGIN_CHANGED )
  658. {
  659. restoreSimulationTime = true;
  660. // Okay, let's see if we can do something reasonable with the change
  661. Vector delta = entity->GetAbsOrigin() - change->m_vecOrigin;
  662. // If it moved really far, just leave the entity in the new spot!!!
  663. if ( delta.LengthSqr() < LAG_COMPENSATION_TELEPORTED_DISTANCE_SQR )
  664. {
  665. RestoreEntityTo( entity, restore->m_vecOrigin + delta );
  666. }
  667. }
  668. CBaseAnimating *pAnimating = entity->GetBaseAnimating();
  669. if( wantsAnims && pAnimating )
  670. {
  671. if( restore->m_fFlags & LC_ANIMATION_CHANGED )
  672. {
  673. restoreSimulationTime = true;
  674. pAnimating->SetSequence(restore->m_masterSequence);
  675. pAnimating->SetCycle(restore->m_masterCycle);
  676. CBaseAnimatingOverlay *pAnimatingOverlay = entity->GetBaseAnimatingOverlay();
  677. if ( pAnimatingOverlay )
  678. {
  679. int layerCount = pAnimatingOverlay->GetNumAnimOverlays();
  680. for( int layerIndex = 0; layerIndex < layerCount; ++layerIndex )
  681. {
  682. CAnimationLayer *currentLayer = pAnimatingOverlay->GetAnimOverlay(layerIndex);
  683. if( currentLayer )
  684. {
  685. currentLayer->m_flCycle = restore->m_layerRecords[layerIndex].m_cycle;
  686. currentLayer->m_nOrder = restore->m_layerRecords[layerIndex].m_order;
  687. currentLayer->m_nSequence = restore->m_layerRecords[layerIndex].m_sequence;
  688. currentLayer->m_flWeight = restore->m_layerRecords[layerIndex].m_weight;
  689. }
  690. }
  691. }
  692. CStudioHdr *pHdr = pAnimating->GetModelPtr();
  693. if ( pHdr )
  694. {
  695. for( int i=0; i<pHdr->GetNumPoseParameters(); i++ )
  696. {
  697. pAnimating->SetPoseParameter( i, restore->m_flPoseParameters[i] );
  698. }
  699. }
  700. }
  701. }
  702. if ( restoreSimulationTime )
  703. {
  704. entity->SetSimulationTime( restore->m_flSimulationTime );
  705. }
  706. }