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.

440 lines
13 KiB

  1. //====== Copyright � 1996-2005, Valve Corporation, All rights reserved. =======
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================
  6. #include "cbase.h"
  7. #include "const.h"
  8. #include "toolframework/itoolentity.h"
  9. #include "entitylist.h"
  10. #include "toolframework/itoolsystem.h"
  11. #include "keyvalues.h"
  12. #include "icliententity.h"
  13. #include "iserverentity.h"
  14. #include "sceneentity.h"
  15. #include "particles/particles.h"
  16. #include "mapentities_shared.h"
  17. #include "TemplateEntities.h"
  18. #include "mapentities.h"
  19. #include "point_template.h"
  20. // NOTE: This has to be the last file included!
  21. #include "tier0/memdbgon.h"
  22. class CFoundryEntitySpawnRecord
  23. {
  24. public:
  25. CUtlVector<char> m_VMFText;
  26. int m_iEntityIndex;
  27. int m_iHammerID;
  28. int m_iSerialNumber;
  29. int m_debugOverlays;
  30. };
  31. static CUtlLinkedList<CFoundryEntitySpawnRecord*,int> g_FoundryEntitySpawnRecords;
  32. //-----------------------------------------------------------------------------
  33. // Interface from engine to tools for manipulating entities
  34. //-----------------------------------------------------------------------------
  35. class CServerTools : public IServerTools
  36. {
  37. public:
  38. // Inherited from IServerTools
  39. virtual IServerEntity *GetIServerEntity( IClientEntity *pClientEntity );
  40. virtual bool GetPlayerPosition( Vector &org, QAngle &ang, IClientEntity *pClientPlayer = NULL );
  41. virtual bool SnapPlayerToPosition( const Vector &org, const QAngle &ang, IClientEntity *pClientPlayer = NULL );
  42. virtual int GetPlayerFOV( IClientEntity *pClientPlayer = NULL );
  43. virtual bool SetPlayerFOV( int fov, IClientEntity *pClientPlayer = NULL );
  44. virtual bool IsInNoClipMode( IClientEntity *pClientPlayer = NULL );
  45. virtual void *FirstEntity( void );
  46. virtual void *NextEntity( void *pEntity );
  47. virtual void *FindEntityByHammerID( int iHammerID );
  48. virtual bool GetKeyValue( void *pEntity, const char *szField, char *szValue, int iMaxLen );
  49. virtual bool SetKeyValue( void *pEntity, const char *szField, const char *szValue );
  50. virtual bool SetKeyValue( void *pEntity, const char *szField, float flValue );
  51. virtual bool SetKeyValue( void *pEntity, const char *szField, const Vector &vecValue );
  52. virtual void *CreateEntityByName( const char *szClassName );
  53. virtual void DispatchSpawn( void *pEntity );
  54. virtual void ReloadParticleDefintions( const char *pFileName, const void *pBufData, int nLen );
  55. virtual void AddOriginToPVS( const Vector &org );
  56. virtual bool DestroyEntityByHammerId( int iHammerID );
  57. virtual bool RespawnEntitiesWithEdits( CEntityRespawnInfo *pInfos, int nInfos );
  58. virtual void MoveEngineViewTo( const Vector &vPos, const QAngle &vAngles );
  59. virtual void RemoveEntity( int nHammerID );
  60. };
  61. //-----------------------------------------------------------------------------
  62. // Singleton
  63. //-----------------------------------------------------------------------------
  64. static CServerTools g_ServerTools;
  65. IServerTools *g_pServerTools = &g_ServerTools;
  66. EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CServerTools, IServerTools, VSERVERTOOLS_INTERFACE_VERSION, g_ServerTools );
  67. IServerEntity *CServerTools::GetIServerEntity( IClientEntity *pClientEntity )
  68. {
  69. if ( pClientEntity == NULL )
  70. return NULL;
  71. CBaseHandle ehandle = pClientEntity->GetRefEHandle();
  72. if ( ehandle.GetEntryIndex() >= MAX_EDICTS )
  73. return NULL; // the first MAX_EDICTS entities are networked, the rest are client or server only
  74. #if 0
  75. // this fails, since the server entities have extra bits in their serial numbers,
  76. // since 20 bits are reserved for serial numbers, except for networked entities, which are restricted to 10
  77. // Brian believes that everything should just restrict itself to 10 to make things simpler,
  78. // so if/when he changes NUM_SERIAL_NUM_BITS to 10, we can switch back to this simpler code
  79. IServerNetworkable *pNet = gEntList.GetServerNetworkable( ehandle );
  80. if ( pNet == NULL )
  81. return NULL;
  82. CBaseEntity *pServerEnt = pNet->GetBaseEntity();
  83. return pServerEnt;
  84. #else
  85. IHandleEntity *pEnt = gEntList.LookupEntityByNetworkIndex( ehandle.GetEntryIndex() );
  86. if ( pEnt == NULL )
  87. return NULL;
  88. CBaseHandle h = gEntList.GetNetworkableHandle( ehandle.GetEntryIndex() );
  89. const int mask = ( 1 << NUM_NETWORKED_EHANDLE_SERIAL_NUMBER_BITS ) - 1;
  90. if ( !h.IsValid() || ( ( h.GetSerialNumber() & mask ) != ( ehandle.GetSerialNumber() & mask ) ) )
  91. return NULL;
  92. IServerUnknown *pUnk = static_cast< IServerUnknown* >( pEnt );
  93. return pUnk->GetBaseEntity();
  94. #endif
  95. }
  96. bool CServerTools::GetPlayerPosition( Vector &org, QAngle &ang, IClientEntity *pClientPlayer )
  97. {
  98. IServerEntity *pServerPlayer = GetIServerEntity( pClientPlayer );
  99. CBasePlayer *pPlayer = pServerPlayer ? ( CBasePlayer* )pServerPlayer : UTIL_GetLocalPlayer();
  100. if ( pPlayer == NULL )
  101. return false;
  102. org = pPlayer->EyePosition();
  103. ang = pPlayer->EyeAngles();
  104. return true;
  105. }
  106. bool CServerTools::SnapPlayerToPosition( const Vector &org, const QAngle &ang, IClientEntity *pClientPlayer )
  107. {
  108. IServerEntity *pServerPlayer = GetIServerEntity( pClientPlayer );
  109. CBasePlayer *pPlayer = pServerPlayer ? ( CBasePlayer* )pServerPlayer : UTIL_GetLocalPlayer();
  110. if ( pPlayer == NULL )
  111. return false;
  112. pPlayer->SetAbsOrigin( org - pPlayer->GetViewOffset() );
  113. pPlayer->SnapEyeAngles( ang );
  114. // Disengage from hierarchy
  115. pPlayer->SetParent( NULL );
  116. return true;
  117. }
  118. int CServerTools::GetPlayerFOV( IClientEntity *pClientPlayer )
  119. {
  120. IServerEntity *pServerPlayer = GetIServerEntity( pClientPlayer );
  121. CBasePlayer *pPlayer = pServerPlayer ? ( CBasePlayer* )pServerPlayer : UTIL_GetLocalPlayer();
  122. if ( pPlayer == NULL )
  123. return 0;
  124. return pPlayer->GetFOV();
  125. }
  126. bool CServerTools::SetPlayerFOV( int fov, IClientEntity *pClientPlayer )
  127. {
  128. IServerEntity *pServerPlayer = GetIServerEntity( pClientPlayer );
  129. CBasePlayer *pPlayer = pServerPlayer ? ( CBasePlayer* )pServerPlayer : UTIL_GetLocalPlayer();
  130. if ( pPlayer == NULL )
  131. return false;
  132. pPlayer->SetDefaultFOV( fov );
  133. CBaseEntity *pFOVOwner = pPlayer->GetFOVOwner();
  134. return pPlayer->SetFOV( pFOVOwner ? pFOVOwner : pPlayer, fov );
  135. }
  136. bool CServerTools::IsInNoClipMode( IClientEntity *pClientPlayer )
  137. {
  138. IServerEntity *pServerPlayer = GetIServerEntity( pClientPlayer );
  139. CBasePlayer *pPlayer = pServerPlayer ? ( CBasePlayer* )pServerPlayer : UTIL_GetLocalPlayer();
  140. if ( pPlayer == NULL )
  141. return true;
  142. return pPlayer->GetMoveType() == MOVETYPE_NOCLIP;
  143. }
  144. void *CServerTools::FirstEntity( void )
  145. {
  146. return (void *)gEntList.FirstEnt();
  147. }
  148. void *CServerTools::NextEntity( void *pEntity )
  149. {
  150. CBaseEntity *pEnt;
  151. if ( pEntity == NULL )
  152. {
  153. pEnt = gEntList.FirstEnt();
  154. }
  155. else
  156. {
  157. pEnt = gEntList.NextEnt( (CBaseEntity *)pEntity );
  158. }
  159. return (void *)pEnt;
  160. }
  161. void *CServerTools::FindEntityByHammerID( int iHammerID )
  162. {
  163. CBaseEntity *pEntity = gEntList.FirstEnt();
  164. while (pEntity)
  165. {
  166. if (pEntity->m_iHammerID == iHammerID)
  167. return (void *)pEntity;
  168. pEntity = gEntList.NextEnt( pEntity );
  169. }
  170. return NULL;
  171. }
  172. bool CServerTools::GetKeyValue( void *pEntity, const char *szField, char *szValue, int iMaxLen )
  173. {
  174. CBaseEntity *pEnt = (CBaseEntity *)pEntity;
  175. return pEnt->GetKeyValue( szField, szValue, iMaxLen );
  176. }
  177. bool CServerTools::SetKeyValue( void *pEntity, const char *szField, const char *szValue )
  178. {
  179. CBaseEntity *pEnt = (CBaseEntity *)pEntity;
  180. return pEnt->KeyValue( szField, szValue );
  181. }
  182. bool CServerTools::SetKeyValue( void *pEntity, const char *szField, float flValue )
  183. {
  184. CBaseEntity *pEnt = (CBaseEntity *)pEntity;
  185. return pEnt->KeyValue( szField, flValue );
  186. }
  187. bool CServerTools::SetKeyValue( void *pEntity, const char *szField, const Vector &vecValue )
  188. {
  189. CBaseEntity *pEnt = (CBaseEntity *)pEntity;
  190. return pEnt->KeyValue( szField, vecValue );
  191. }
  192. //-----------------------------------------------------------------------------
  193. // entity spawning
  194. //-----------------------------------------------------------------------------
  195. void *CServerTools::CreateEntityByName( const char *szClassName )
  196. {
  197. return ::CreateEntityByName( szClassName );
  198. }
  199. void CServerTools::DispatchSpawn( void *pEntity )
  200. {
  201. ::DispatchSpawn( (CBaseEntity *)pEntity );
  202. }
  203. bool CServerTools::DestroyEntityByHammerId( int iHammerID )
  204. {
  205. CBaseEntity *pEntity = (CBaseEntity*)FindEntityByHammerID( iHammerID );
  206. if ( !pEntity )
  207. return false;
  208. UTIL_Remove( pEntity );
  209. return true;
  210. }
  211. void HandleFoundryEntitySpawnRecords()
  212. {
  213. if ( g_FoundryEntitySpawnRecords.Count() == 0 )
  214. return;
  215. VPROF("HandleFoundryEntitySpawnRecords");
  216. // Create all the entities.
  217. CUtlVector<CBaseEntity*> newEnts;
  218. CMapEntitySpawner spawner;
  219. spawner.m_bFoundryMode = true;
  220. FOR_EACH_LL( g_FoundryEntitySpawnRecords, i )
  221. {
  222. CFoundryEntitySpawnRecord *pRecord = g_FoundryEntitySpawnRecords[i];
  223. if ( pRecord->m_iEntityIndex > 0 )
  224. {
  225. gEntList.ForceEntSerialNumber( pRecord->m_iEntityIndex, pRecord->m_iSerialNumber );
  226. engine->ForceFlushEntity( pRecord->m_iEntityIndex );
  227. }
  228. // Figure out the class name.
  229. CEntityMapData entData( pRecord->m_VMFText.Base() );
  230. char szClassName[MAPKEY_MAXLENGTH];
  231. if ( !entData.ExtractValue( "classname", szClassName ) )
  232. {
  233. Assert( false );
  234. continue;
  235. }
  236. // Respawn it in the same slot.
  237. int nIndexToSpawn = pRecord->m_iEntityIndex;
  238. if ( nIndexToSpawn == 0 )
  239. nIndexToSpawn = -1;
  240. CBaseEntity *pNewEntity = ::CreateEntityByName( szClassName, nIndexToSpawn );
  241. if ( !pNewEntity )
  242. {
  243. Warning( "HandleFoundryEntitySpawnRecords - CreateEntityByName( %s, %d ) failed\n", szClassName, pRecord->m_iEntityIndex );
  244. continue;
  245. }
  246. const char *pBaseMapDataForThisEntity = entData.CurrentBufferPosition();
  247. pNewEntity->ParseMapData( &entData );
  248. if ( pRecord->m_debugOverlays != -1 )
  249. pNewEntity->m_debugOverlays = pRecord->m_debugOverlays;
  250. pNewEntity->m_iHammerID = pRecord->m_iHammerID;
  251. spawner.AddEntity( pNewEntity, pBaseMapDataForThisEntity, (entData.CurrentBufferPosition() - pBaseMapDataForThisEntity) + 2 );
  252. }
  253. spawner.HandleTemplates();
  254. spawner.SpawnAndActivate( true );
  255. // Now that all of the active entities have been loaded in, precache any entities who need point_template parameters
  256. // to be parsed (the above code has loaded all point_template entities)
  257. PrecachePointTemplates();
  258. // Sometimes an ent will Remove() itself during its precache, so RemoveImmediate won't happen.
  259. // This makes sure those ents get cleaned up.
  260. gEntList.CleanupDeleteList();
  261. g_FoundryEntitySpawnRecords.PurgeAndDeleteElements();
  262. }
  263. bool CServerTools::RespawnEntitiesWithEdits( CEntityRespawnInfo *pInfos, int nInfos )
  264. {
  265. // Create a spawn record so it'll respawn the entity next frame.
  266. for ( int i=0; i < nInfos; i++ )
  267. {
  268. CFoundryEntitySpawnRecord *pRecord = new CFoundryEntitySpawnRecord;
  269. CEntityRespawnInfo *pInfo = &pInfos[i];
  270. pRecord->m_VMFText.SetSize( V_strlen( pInfo->m_pEntText ) + 1 );
  271. V_strncpy( pRecord->m_VMFText.Base(), pInfo->m_pEntText, pRecord->m_VMFText.Count() );
  272. pRecord->m_iHammerID = pInfo->m_nHammerID;
  273. CBaseEntity *pOldEntity = (CBaseEntity*)FindEntityByHammerID( pInfo->m_nHammerID );
  274. if ( pOldEntity )
  275. {
  276. // This is a respawn.
  277. pRecord->m_iEntityIndex = pOldEntity->entindex();
  278. pRecord->m_iSerialNumber = pOldEntity->GetRefEHandle().GetSerialNumber();
  279. pRecord->m_debugOverlays = pOldEntity->m_debugOverlays;
  280. UTIL_Remove( pOldEntity );
  281. }
  282. else
  283. {
  284. // This is a new spawn.
  285. pRecord->m_iEntityIndex = -1;
  286. pRecord->m_iSerialNumber = -1;
  287. pRecord->m_debugOverlays = -1;
  288. }
  289. g_FoundryEntitySpawnRecords.AddToTail( pRecord );
  290. }
  291. return true;
  292. }
  293. //-----------------------------------------------------------------------------
  294. // Reload particle definitions
  295. //-----------------------------------------------------------------------------
  296. void CServerTools::ReloadParticleDefintions( const char *pFileName, const void *pBufData, int nLen )
  297. {
  298. // FIXME: Use file name to determine if we care about this data
  299. CUtlBuffer buf( pBufData, nLen, CUtlBuffer::READ_ONLY );
  300. g_pParticleSystemMgr->ReadParticleConfigFile( buf, true );
  301. }
  302. void CServerTools::AddOriginToPVS( const Vector &org )
  303. {
  304. engine->AddOriginToPVS( org );
  305. }
  306. void CServerTools::MoveEngineViewTo( const Vector &vPos, const QAngle &vAngles )
  307. {
  308. CBasePlayer *pPlayer = UTIL_GetListenServerHost();
  309. if ( !pPlayer )
  310. return;
  311. extern void EnableNoClip( CBasePlayer *pPlayer );
  312. EnableNoClip( pPlayer );
  313. Vector zOffset = pPlayer->EyePosition() - pPlayer->GetAbsOrigin();
  314. pPlayer->SetAbsOrigin( vPos - zOffset );
  315. pPlayer->SnapEyeAngles( vAngles );
  316. }
  317. void CServerTools::RemoveEntity( int nHammerID )
  318. {
  319. CBaseEntity *pOldEntity = (CBaseEntity*)FindEntityByHammerID( nHammerID );
  320. if ( pOldEntity )
  321. UTIL_Remove( pOldEntity );
  322. }
  323. //------------------------------------------------------------------------------
  324. // Applies keyvalues to the entity by hammer ID.
  325. //------------------------------------------------------------------------------
  326. void CC_Ent_Keyvalue( const CCommand &args )
  327. {
  328. // Must have an odd number of arguments.
  329. if ( ( args.ArgC() < 4 ) || ( args.ArgC() & 1 ) )
  330. {
  331. Msg( "Format: ent_keyvalue <entity id> \"key1\"=\"value1\" \"key2\" \"value2\" ... \"keyN\" \"valueN\"\n" );
  332. return;
  333. }
  334. int nID = atoi( args[1] );
  335. void *pEnt = g_ServerTools.FindEntityByHammerID( nID );
  336. if ( !pEnt )
  337. {
  338. Msg( "Entity ID %d not found.\n", nID );
  339. return;
  340. }
  341. int nArg = 2;
  342. while ( nArg < args.ArgC() )
  343. {
  344. const char *pszKey = args[ nArg ];
  345. const char *pszValue = args[ nArg + 1 ];
  346. nArg += 2;
  347. g_ServerTools.SetKeyValue( pEnt, pszKey, pszValue );
  348. }
  349. }
  350. static ConCommand ent_keyvalue("ent_keyvalue", CC_Ent_Keyvalue, "Applies the comma delimited key=value pairs to the entity with the given Hammer ID.\n\tFormat: ent_keyvalue <entity id> <key1>=<value1>,<key2>=<value2>,...,<keyN>=<valueN>\n", FCVAR_CHEAT);