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.

589 lines
17 KiB

  1. //===== Copyright � 1996-2005, Valve Corporation, All rights reserved. ======//
  2. //
  3. // Purpose:
  4. //
  5. // $Workfile: $
  6. // $Date: $
  7. // $NoKeywords: $
  8. //===========================================================================//
  9. #include "quakedef.h"
  10. #include "world.h"
  11. #include "eiface.h"
  12. #include "server.h"
  13. #include "cmodel_engine.h"
  14. #include "gl_model_private.h"
  15. #include "sv_main.h"
  16. #include "vengineserver_impl.h"
  17. #include "collisionutils.h"
  18. #include "vphysics_interface.h"
  19. #include "ispatialpartitioninternal.h"
  20. #include "staticpropmgr.h"
  21. #include "shadowmgr.h"
  22. #include "string_t.h"
  23. #include "enginetrace.h"
  24. #include "sys_dll.h"
  25. #include "client.h"
  26. #include "cdll_int.h"
  27. #include "cdll_engine_int.h"
  28. #include "icliententitylist.h"
  29. #include "client_class.h"
  30. #include "icliententity.h"
  31. // memdbgon must be the last include file in a .cpp file!!!
  32. #include "tier0/memdbgon.h"
  33. //-----------------------------------------------------------------------------
  34. // Method to convert edict to index
  35. //-----------------------------------------------------------------------------
  36. static inline int IndexOfEdict( edict_t* pEdict )
  37. {
  38. return (int)(pEdict - sv.edicts);
  39. }
  40. //============================================================================
  41. /*
  42. ===============
  43. SV_ClearWorld
  44. ===============
  45. */
  46. void SV_ClearWorld (void)
  47. {
  48. MDLCACHE_COARSE_LOCK_(g_pMDLCache);
  49. // Clean up static props from the previous level
  50. #if !defined( DEDICATED )
  51. if ( !sv.IsDedicated() )
  52. {
  53. g_pShadowMgr->LevelShutdown();
  54. }
  55. #endif // DEDICATED
  56. StaticPropMgr()->LevelShutdown();
  57. for ( int i = 0; i < 3; i++ )
  58. {
  59. if ( host_state.worldmodel->mins[i] < MIN_COORD_INTEGER || host_state.worldmodel->maxs[i] > MAX_COORD_INTEGER )
  60. {
  61. Host_EndGame(true, "Map coordinate extents are too large!!\nCheck for errors!\n" );
  62. }
  63. }
  64. SpatialPartition()->Init( host_state.worldmodel->mins, host_state.worldmodel->maxs );
  65. // Load all static props into the spatial partition
  66. StaticPropMgr()->LevelInit();
  67. #if !defined( DEDICATED )
  68. if ( !sv.IsDedicated() )
  69. {
  70. g_pShadowMgr->LevelInit( host_state.worldbrush->numsurfaces );
  71. }
  72. #endif // DEDICATED
  73. }
  74. //-----------------------------------------------------------------------------
  75. // Trigger world-space bounds
  76. //-----------------------------------------------------------------------------
  77. static void CM_TriggerWorldSpaceBounds( ICollideable *pCollideable, Vector *pMins, Vector *pMaxs )
  78. {
  79. if ( pCollideable->GetSolidFlags() & FSOLID_USE_TRIGGER_BOUNDS )
  80. {
  81. pCollideable->WorldSpaceTriggerBounds( pMins, pMaxs );
  82. }
  83. else
  84. {
  85. CM_WorldSpaceBounds( pCollideable, pMins, pMaxs );
  86. }
  87. }
  88. static void CM_GetCollideableTriggerTestBox( ICollideable *pCollide, Vector *pMins, Vector *pMaxs, bool bUseAccurateBbox )
  89. {
  90. if ( bUseAccurateBbox && pCollide->GetSolid() == SOLID_BBOX )
  91. {
  92. *pMins = pCollide->OBBMins();
  93. *pMaxs = pCollide->OBBMaxs();
  94. }
  95. else
  96. {
  97. const Vector &vecStart = pCollide->GetCollisionOrigin();
  98. pCollide->WorldSpaceSurroundingBounds( pMins, pMaxs );
  99. *pMins -= vecStart;
  100. *pMaxs -= vecStart;
  101. }
  102. }
  103. //-----------------------------------------------------------------------------
  104. // Little enumeration class used to try touching all triggers
  105. //-----------------------------------------------------------------------------
  106. class CTouchLinks : public IPartitionEnumerator
  107. {
  108. public:
  109. CTouchLinks( edict_t* pEnt, const Vector* pPrevAbsOrigin, bool accurateBboxTriggerChecks ) : m_TouchedEntities( 8, 8 )
  110. {
  111. m_pEnt = pEnt;
  112. m_pCollide = pEnt->GetCollideable();
  113. m_nRequiredTriggerFlags = m_pCollide->GetRequiredTriggerFlags();
  114. Assert( m_pCollide );
  115. Vector vecMins, vecMaxs;
  116. CM_GetCollideableTriggerTestBox( m_pCollide, &vecMins, &vecMaxs, accurateBboxTriggerChecks );
  117. const Vector &vecStart = m_pCollide->GetCollisionOrigin();
  118. if (pPrevAbsOrigin)
  119. {
  120. m_Ray.Init( *pPrevAbsOrigin, vecStart, vecMins, vecMaxs );
  121. }
  122. else
  123. {
  124. m_Ray.Init( vecStart, vecStart, vecMins, vecMaxs );
  125. }
  126. }
  127. IterationRetval_t EnumElement( IHandleEntity *pHandleEntity )
  128. {
  129. if ( !m_nRequiredTriggerFlags )
  130. return ITERATION_CONTINUE;
  131. // Static props should never be in the trigger list
  132. Assert( !StaticPropMgr()->IsStaticProp( pHandleEntity ) );
  133. IServerUnknown *pUnk = static_cast<IServerUnknown*>( pHandleEntity );
  134. Assert( pUnk );
  135. // Convert the IHandleEntity to an edict_t*...
  136. // Context is the thing we're testing everything against
  137. edict_t* pTouch = pUnk->GetNetworkable()->GetEdict();
  138. // Can't bump against itself
  139. if ( pTouch == m_pEnt )
  140. return ITERATION_CONTINUE;
  141. IServerEntity *pTriggerEntity = pTouch->GetIServerEntity();
  142. if ( !pTriggerEntity )
  143. return ITERATION_CONTINUE;
  144. // Hmmm.. everything in this list should be a trigger....
  145. ICollideable *pTriggerCollideable = pTriggerEntity->GetCollideable();
  146. int nTriggerSolidFlags = pTriggerCollideable->GetSolidFlags();
  147. if ( (nTriggerSolidFlags & m_nRequiredTriggerFlags) == m_nRequiredTriggerFlags )
  148. {
  149. if ( nTriggerSolidFlags & FSOLID_USE_TRIGGER_BOUNDS )
  150. {
  151. Vector vecTriggerMins, vecTriggerMaxs;
  152. pTriggerCollideable->WorldSpaceTriggerBounds( &vecTriggerMins, &vecTriggerMaxs );
  153. if ( !IsBoxIntersectingRay( vecTriggerMins, vecTriggerMaxs, m_Ray ) )
  154. return ITERATION_CONTINUE;
  155. }
  156. else
  157. {
  158. trace_t tr;
  159. g_pEngineTraceServer->ClipRayToCollideable( m_Ray, MASK_SOLID, pTriggerCollideable, &tr );
  160. if ( !(tr.contents & MASK_SOLID) )
  161. return ITERATION_CONTINUE;
  162. }
  163. m_TouchedEntities.AddToTail( pTouch );
  164. }
  165. return ITERATION_CONTINUE;
  166. }
  167. void HandleTouchedEntities()
  168. {
  169. for ( int i = 0; i < m_TouchedEntities.Count(); ++i )
  170. {
  171. serverGameEnts->MarkEntitiesAsTouching( m_TouchedEntities[i], m_pEnt );
  172. }
  173. }
  174. Ray_t m_Ray;
  175. private:
  176. edict_t *m_pEnt;
  177. ICollideable *m_pCollide;
  178. uint m_nRequiredTriggerFlags;
  179. CUtlVector< edict_t* > m_TouchedEntities;
  180. };
  181. //-----------------------------------------------------------------------------
  182. // Little enumeration class used to try touching all triggers
  183. //-----------------------------------------------------------------------------
  184. class CTouchLinks_ClientSide : public IPartitionEnumerator
  185. {
  186. public:
  187. CTouchLinks_ClientSide( IClientEntity *pEnt, const Vector* pPrevAbsOrigin, bool accurateBboxTriggerChecks ) : m_TouchedEntities( 8, 8 )
  188. {
  189. m_pEnt = pEnt;
  190. m_pCollide = pEnt->GetCollideable();
  191. m_nRequiredTriggerFlags = m_pCollide->GetRequiredTriggerFlags();
  192. Assert( m_pCollide );
  193. Vector vecMins, vecMaxs;
  194. CM_GetCollideableTriggerTestBox( m_pCollide, &vecMins, &vecMaxs, accurateBboxTriggerChecks );
  195. const Vector &vecStart = m_pCollide->GetCollisionOrigin();
  196. if (pPrevAbsOrigin)
  197. {
  198. m_Ray.Init( *pPrevAbsOrigin, vecStart, vecMins, vecMaxs );
  199. }
  200. else
  201. {
  202. m_Ray.Init( vecStart, vecStart, vecMins, vecMaxs );
  203. }
  204. }
  205. IterationRetval_t EnumElement( IHandleEntity *pHandleEntity )
  206. {
  207. #ifndef DEDICATED
  208. if ( sv.IsDedicated() )
  209. {
  210. return ITERATION_CONTINUE;
  211. }
  212. if ( !m_nRequiredTriggerFlags )
  213. return ITERATION_CONTINUE;
  214. // Static props should never be in the trigger list
  215. Assert( !StaticPropMgr()->IsStaticProp( pHandleEntity ) );
  216. IClientUnknown *pUnk = static_cast<IClientUnknown*>( pHandleEntity );
  217. Assert( pUnk );
  218. // Convert the IHandleEntity to an edict_t*...
  219. // Context is the thing we're testing everything against
  220. IClientEntity *pTriggerEntity = pUnk->GetIClientEntity();
  221. // Can't bump against itself
  222. if ( pTriggerEntity == m_pEnt )
  223. return ITERATION_CONTINUE;
  224. // Hmmm.. everything in this list should be a trigger....
  225. ICollideable *pTriggerCollideable = pTriggerEntity->GetCollideable();
  226. int nTriggerSolidFlags = pTriggerCollideable->GetSolidFlags();
  227. if ( (nTriggerSolidFlags & m_nRequiredTriggerFlags) == m_nRequiredTriggerFlags )
  228. {
  229. if ( pTriggerCollideable->GetSolidFlags() & FSOLID_USE_TRIGGER_BOUNDS )
  230. {
  231. Vector vecTriggerMins, vecTriggerMaxs;
  232. pTriggerCollideable->WorldSpaceTriggerBounds( &vecTriggerMins, &vecTriggerMaxs );
  233. if ( !IsBoxIntersectingRay( vecTriggerMins, vecTriggerMaxs, m_Ray ) )
  234. {
  235. return ITERATION_CONTINUE;
  236. }
  237. }
  238. else
  239. {
  240. trace_t tr;
  241. g_pEngineTraceClient->ClipRayToCollideable( m_Ray, MASK_SOLID, pTriggerCollideable, &tr );
  242. if ( !(tr.contents & MASK_SOLID) )
  243. return ITERATION_CONTINUE;
  244. }
  245. }
  246. m_TouchedEntities.AddToTail( pTriggerEntity );
  247. #endif
  248. return ITERATION_CONTINUE;
  249. }
  250. void HandleTouchedEntities()
  251. {
  252. #ifndef DEDICATED
  253. if ( sv.IsDedicated() )
  254. {
  255. return;
  256. }
  257. for ( int i = 0; i < m_TouchedEntities.Count(); ++i )
  258. {
  259. g_ClientDLL->MarkEntitiesAsTouching( m_TouchedEntities[i], m_pEnt );
  260. }
  261. #endif
  262. }
  263. Ray_t m_Ray;
  264. private:
  265. IClientEntity *m_pEnt;
  266. ICollideable *m_pCollide;
  267. uint m_nRequiredTriggerFlags;
  268. CUtlVector< IClientEntity* > m_TouchedEntities;
  269. };
  270. // enumerator class that's used to update touch links for a trigger when
  271. // it moves or changes solid type
  272. class CTriggerMoved : public IPartitionEnumerator
  273. {
  274. public:
  275. CTriggerMoved( bool accurateBboxTriggerChecks ) : m_TouchedEntities( 8, 8 )
  276. {
  277. m_bAccurateBBoxCheck = accurateBboxTriggerChecks;
  278. }
  279. void TriggerMoved( edict_t *pTriggerEntity )
  280. {
  281. m_pTriggerEntity = pTriggerEntity;
  282. m_pTrigger = pTriggerEntity->GetCollideable();
  283. m_triggerSolidFlags = m_pTrigger->GetSolidFlags();
  284. Vector vecAbsMins, vecAbsMaxs;
  285. CM_TriggerWorldSpaceBounds( m_pTrigger, &vecAbsMins, &vecAbsMaxs );
  286. SpatialPartition()->EnumerateElementsInBox( PARTITION_ENGINE_SOLID_EDICTS,
  287. vecAbsMins, vecAbsMaxs, false, this );
  288. }
  289. IterationRetval_t EnumElement( IHandleEntity *pHandleEntity )
  290. {
  291. #ifndef DEDICATED
  292. if ( sv.IsDedicated() )
  293. {
  294. return ITERATION_CONTINUE;
  295. }
  296. // skip static props, the game DLL doesn't care about them
  297. if ( StaticPropMgr()->IsStaticProp( pHandleEntity ) )
  298. return ITERATION_CONTINUE;
  299. IServerUnknown *pUnk = static_cast< IServerUnknown* >( pHandleEntity );
  300. Assert( pUnk );
  301. // Convert the user ID to and edict_t*...
  302. edict_t* pTouch = pUnk->GetNetworkable()->GetEdict();
  303. Assert( pTouch );
  304. ICollideable *pTouchCollide = pUnk->GetCollideable();
  305. // Can't ever touch itself because it's in the other list
  306. if ( pTouchCollide == m_pTrigger )
  307. return ITERATION_CONTINUE;
  308. int nReqFlags = pTouchCollide->GetRequiredTriggerFlags();
  309. if ( !nReqFlags || (nReqFlags & m_triggerSolidFlags) != nReqFlags )
  310. return ITERATION_CONTINUE;
  311. IServerEntity *serverEntity = pTouch->GetIServerEntity();
  312. if ( !serverEntity )
  313. return ITERATION_CONTINUE;
  314. // FIXME: Should we be using the surrounding bounds here?
  315. Vector vecMins, vecMaxs;
  316. CM_GetCollideableTriggerTestBox( pTouchCollide, &vecMins, &vecMaxs, m_bAccurateBBoxCheck );
  317. const Vector &vecStart = pTouchCollide->GetCollisionOrigin();
  318. Ray_t ray;
  319. ray.Init( vecStart, vecStart, vecMins, vecMaxs );
  320. if ( m_pTrigger->GetSolidFlags() & FSOLID_USE_TRIGGER_BOUNDS )
  321. {
  322. Vector vecTriggerMins, vecTriggerMaxs;
  323. m_pTrigger->WorldSpaceTriggerBounds( &vecTriggerMins, &vecTriggerMaxs );
  324. if ( !IsBoxIntersectingRay( vecTriggerMins, vecTriggerMaxs, ray ) )
  325. {
  326. return ITERATION_CONTINUE;
  327. }
  328. }
  329. else
  330. {
  331. trace_t tr;
  332. g_pEngineTraceServer->ClipRayToCollideable( ray, MASK_SOLID, m_pTrigger, &tr );
  333. if ( !(tr.contents & MASK_SOLID) )
  334. return ITERATION_CONTINUE;
  335. }
  336. m_TouchedEntities.AddToTail( pTouch );
  337. #endif
  338. return ITERATION_CONTINUE;
  339. }
  340. void HandleTouchedEntities( )
  341. {
  342. for ( int i = 0; i < m_TouchedEntities.Count(); ++i )
  343. {
  344. serverGameEnts->MarkEntitiesAsTouching( m_TouchedEntities[i], m_pTriggerEntity );
  345. }
  346. }
  347. private:
  348. edict_t* m_pTriggerEntity;
  349. ICollideable* m_pTrigger;
  350. int m_triggerSolidFlags;
  351. Vector m_vecDelta;
  352. CUtlVector< edict_t* > m_TouchedEntities;
  353. bool m_bAccurateBBoxCheck;
  354. };
  355. // enumerator class that's used to update touch links for a trigger when
  356. // it moves or changes solid type
  357. class CTriggerMoved_ClientSide : public IPartitionEnumerator
  358. {
  359. public:
  360. CTriggerMoved_ClientSide( bool accurateBboxTriggerChecks ) : m_TouchedEntities( 8, 8 )
  361. {
  362. m_bAccurateBBoxCheck = accurateBboxTriggerChecks;
  363. }
  364. void TriggerMoved( IClientEntity *pTriggerEntity )
  365. {
  366. m_pTriggerEntity = pTriggerEntity;
  367. m_pTrigger = pTriggerEntity->GetCollideable();
  368. m_triggerSolidFlags = m_pTrigger->GetSolidFlags();
  369. Vector vecAbsMins, vecAbsMaxs;
  370. CM_TriggerWorldSpaceBounds( m_pTrigger, &vecAbsMins, &vecAbsMaxs );
  371. SpatialPartition()->EnumerateElementsInBox( PARTITION_CLIENT_SOLID_EDICTS,
  372. vecAbsMins, vecAbsMaxs, false, this );
  373. }
  374. IterationRetval_t EnumElement( IHandleEntity *pHandleEntity )
  375. {
  376. #ifndef DEDICATED
  377. if ( sv.IsDedicated() )
  378. {
  379. return ITERATION_CONTINUE;
  380. }
  381. // skip static props, the game DLL doesn't care about them
  382. if ( StaticPropMgr()->IsStaticProp( pHandleEntity ) )
  383. return ITERATION_CONTINUE;
  384. IClientUnknown *pUnk = static_cast< IClientUnknown* >( pHandleEntity );
  385. Assert( pUnk );
  386. // Convert the user ID to and edict_t*...
  387. IClientEntity* pTouch = pUnk->GetIClientEntity();
  388. Assert( pTouch );
  389. ICollideable *pTouchCollide = pUnk->GetCollideable();
  390. // Can't ever touch itself because it's in the other list
  391. if ( pTouchCollide == m_pTrigger )
  392. return ITERATION_CONTINUE;
  393. int nReqFlags = pTouchCollide->GetRequiredTriggerFlags();
  394. if ( !nReqFlags || (nReqFlags & m_triggerSolidFlags) != nReqFlags )
  395. return ITERATION_CONTINUE;
  396. // FIXME: Should we be using the surrounding bounds here?
  397. Vector vecMins, vecMaxs;
  398. CM_GetCollideableTriggerTestBox( pTouchCollide, &vecMins, &vecMaxs, m_bAccurateBBoxCheck );
  399. const Vector &vecStart = pTouchCollide->GetCollisionOrigin();
  400. Ray_t ray;
  401. ray.Init( vecStart, vecStart, vecMins, vecMaxs );
  402. if ( m_pTrigger->GetSolidFlags() & FSOLID_USE_TRIGGER_BOUNDS )
  403. {
  404. Vector vecTriggerMins, vecTriggerMaxs;
  405. m_pTrigger->WorldSpaceTriggerBounds( &vecTriggerMins, &vecTriggerMaxs );
  406. if ( !IsBoxIntersectingRay( vecTriggerMins, vecTriggerMaxs, ray ) )
  407. {
  408. return ITERATION_CONTINUE;
  409. }
  410. }
  411. else
  412. {
  413. trace_t tr;
  414. g_pEngineTraceClient->ClipRayToCollideable( ray, MASK_SOLID, m_pTrigger, &tr );
  415. if ( !(tr.contents & MASK_SOLID) )
  416. return ITERATION_CONTINUE;
  417. }
  418. m_TouchedEntities.AddToTail( pTouch );
  419. #endif
  420. return ITERATION_CONTINUE;
  421. }
  422. void HandleTouchedEntities( )
  423. {
  424. #ifndef DEDICATED
  425. if ( sv.IsDedicated() )
  426. {
  427. return;
  428. }
  429. for ( int i = 0; i < m_TouchedEntities.Count(); ++i )
  430. {
  431. g_ClientDLL->MarkEntitiesAsTouching( m_TouchedEntities[i], m_pTriggerEntity );
  432. }
  433. #endif
  434. }
  435. private:
  436. IClientEntity* m_pTriggerEntity;
  437. ICollideable* m_pTrigger;
  438. int m_triggerSolidFlags;
  439. Vector m_vecDelta;
  440. CUtlVector< IClientEntity* > m_TouchedEntities;
  441. bool m_bAccurateBBoxCheck;
  442. };
  443. //-----------------------------------------------------------------------------
  444. // Touches triggers. Or, if it is a trigger, causes other things to touch it
  445. // returns true if untouch needs to be checked
  446. //-----------------------------------------------------------------------------
  447. void SV_TriggerMoved( edict_t *pTriggerEnt, bool accurateBboxTriggerChecks )
  448. {
  449. CTriggerMoved triggerEnum( accurateBboxTriggerChecks );
  450. triggerEnum.TriggerMoved( pTriggerEnt );
  451. triggerEnum.HandleTouchedEntities( );
  452. }
  453. void CL_TriggerMoved( IClientEntity *pTriggerEnt, bool accurateBboxTriggerChecks )
  454. {
  455. CTriggerMoved_ClientSide triggerEnum( accurateBboxTriggerChecks );
  456. triggerEnum.TriggerMoved( pTriggerEnt );
  457. triggerEnum.HandleTouchedEntities();
  458. }
  459. void SV_SolidMoved( edict_t *pSolidEnt, ICollideable *pSolidCollide, const Vector* pPrevAbsOrigin, bool accurateBboxTriggerChecks )
  460. {
  461. if (!pPrevAbsOrigin)
  462. {
  463. CTouchLinks touchEnumerator(pSolidEnt, NULL, accurateBboxTriggerChecks);
  464. Vector vecWorldMins, vecWorldMaxs;
  465. pSolidCollide->WorldSpaceSurroundingBounds( &vecWorldMins, &vecWorldMaxs );
  466. SpatialPartition()->EnumerateElementsInBox( PARTITION_ENGINE_TRIGGER_EDICTS,
  467. vecWorldMins, vecWorldMaxs, false, &touchEnumerator );
  468. touchEnumerator.HandleTouchedEntities( );
  469. }
  470. else
  471. {
  472. CTouchLinks touchEnumerator(pSolidEnt, pPrevAbsOrigin, accurateBboxTriggerChecks);
  473. // A version that checks against an extruded ray indicating the motion
  474. SpatialPartition()->EnumerateElementsAlongRay( PARTITION_ENGINE_TRIGGER_EDICTS,
  475. touchEnumerator.m_Ray, false, &touchEnumerator );
  476. touchEnumerator.HandleTouchedEntities( );
  477. }
  478. }
  479. void CL_SolidMoved( IClientEntity *pTriggerEnt, ICollideable *pSolidCollide, const Vector* pPrevAbsOrigin, bool accurateBboxTriggerChecks )
  480. {
  481. if (!pPrevAbsOrigin)
  482. {
  483. CTouchLinks_ClientSide touchEnumerator(pTriggerEnt, NULL, accurateBboxTriggerChecks);
  484. Vector vecWorldMins, vecWorldMaxs;
  485. pSolidCollide->WorldSpaceSurroundingBounds( &vecWorldMins, &vecWorldMaxs );
  486. SpatialPartition()->EnumerateElementsInBox( PARTITION_CLIENT_TRIGGER_ENTITIES,
  487. vecWorldMins, vecWorldMaxs, false, &touchEnumerator );
  488. touchEnumerator.HandleTouchedEntities();
  489. }
  490. else
  491. {
  492. CTouchLinks_ClientSide touchEnumerator(pTriggerEnt, pPrevAbsOrigin, accurateBboxTriggerChecks);
  493. // A version that checks against an extruded ray indicating the motion
  494. SpatialPartition()->EnumerateElementsAlongRay( PARTITION_CLIENT_TRIGGER_ENTITIES,
  495. touchEnumerator.m_Ray, false, &touchEnumerator );
  496. touchEnumerator.HandleTouchedEntities();
  497. }
  498. }