Team Fortress 2 Source Code as on 22/4/2020
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.

2103 lines
54 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $Workfile: $
  6. // $NoKeywords: $
  7. //===========================================================================//
  8. #include "server_pch.h"
  9. #include "tier0/valve_minmax_off.h"
  10. #include <algorithm>
  11. #include "tier0/valve_minmax_on.h"
  12. #include "vengineserver_impl.h"
  13. #include "vox.h"
  14. #include "sound.h"
  15. #include "gl_model_private.h"
  16. #include "host_saverestore.h"
  17. #include "world.h"
  18. #include "l_studio.h"
  19. #include "decal.h"
  20. #include "sys_dll.h"
  21. #include "sv_log.h"
  22. #include "sv_main.h"
  23. #include "tier1/strtools.h"
  24. #include "collisionutils.h"
  25. #include "staticpropmgr.h"
  26. #include "string_t.h"
  27. #include "vstdlib/random.h"
  28. #include "EngineSoundInternal.h"
  29. #include "dt_send_eng.h"
  30. #include "PlayerState.h"
  31. #include "irecipientfilter.h"
  32. #include "sv_user.h"
  33. #include "server_class.h"
  34. #include "cdll_engine_int.h"
  35. #include "enginesingleuserfilter.h"
  36. #include "ispatialpartitioninternal.h"
  37. #include "con_nprint.h"
  38. #include "tmessage.h"
  39. #include "iscratchpad3d.h"
  40. #include "pr_edict.h"
  41. #include "networkstringtableserver.h"
  42. #include "networkstringtable.h"
  43. #include "LocalNetworkBackdoor.h"
  44. #include "host_phonehome.h"
  45. #include "matchmaking.h"
  46. #include "sv_plugin.h"
  47. #include "sv_steamauth.h"
  48. #include "replay_internal.h"
  49. #include "replayserver.h"
  50. #include "replay/iserverengine.h"
  51. // memdbgon must be the last include file in a .cpp file!!!
  52. #include "tier0/memdbgon.h"
  53. #define MAX_MESSAGE_SIZE 2500
  54. #define MAX_TOTAL_ENT_LEAFS 128
  55. void SV_DetermineMulticastRecipients( bool usepas, const Vector& origin, CBitVec< ABSOLUTE_PLAYER_LIMIT >& playerbits );
  56. int MapList_ListMaps( const char *pszSubString, bool listobsolete, bool verbose, int maxcount, int maxitemlength, char maplist[][ 64 ] );
  57. extern CNetworkStringTableContainer *networkStringTableContainerServer;
  58. CSharedEdictChangeInfo g_SharedEdictChangeInfo;
  59. CSharedEdictChangeInfo *g_pSharedChangeInfo = &g_SharedEdictChangeInfo;
  60. IAchievementMgr *g_pAchievementMgr = NULL;
  61. CGamestatsData *g_pGamestatsData = NULL;
  62. void InvalidateSharedEdictChangeInfos()
  63. {
  64. if ( g_SharedEdictChangeInfo.m_iSerialNumber == 0xFFFF )
  65. {
  66. // Reset all edicts to 0.
  67. g_SharedEdictChangeInfo.m_iSerialNumber = 1;
  68. for ( int i=0; i < sv.num_edicts; i++ )
  69. sv.edicts[i].SetChangeInfoSerialNumber( 0 );
  70. }
  71. else
  72. {
  73. g_SharedEdictChangeInfo.m_iSerialNumber++;
  74. }
  75. g_SharedEdictChangeInfo.m_nChangeInfos = 0;
  76. }
  77. // ---------------------------------------------------------------------- //
  78. // Globals.
  79. // ---------------------------------------------------------------------- //
  80. struct MsgData
  81. {
  82. MsgData()
  83. {
  84. Reset();
  85. // link buffers to messages
  86. entityMsg.m_DataOut.StartWriting( entitydata, sizeof(entitydata) );
  87. entityMsg.m_DataOut.SetDebugName( "s_MsgData.entityMsg.m_DataOut" );
  88. userMsg.m_DataOut.StartWriting( userdata, sizeof(userdata) );
  89. userMsg.m_DataOut.SetDebugName( "s_MsgData.userMsg.m_DataOut" );
  90. }
  91. void Reset()
  92. {
  93. filter = NULL;
  94. reliable = false;
  95. subtype = 0;
  96. started = false;
  97. usermessagesize = -1;
  98. usermessagename = NULL;
  99. currentMsg = NULL;
  100. }
  101. byte userdata[ PAD_NUMBER( MAX_USER_MSG_DATA, 4 ) ]; // buffer for outgoing user messages
  102. byte entitydata[ PAD_NUMBER( MAX_ENTITY_MSG_DATA, 4 ) ]; // buffer for outgoing entity messages
  103. IRecipientFilter *filter; // clients who get this message
  104. bool reliable;
  105. INetMessage *currentMsg; // pointer to entityMsg or userMessage
  106. int subtype; // usermessage index
  107. bool started; // IS THERE A MESSAGE IN THE PROCESS OF BEING SENT?
  108. int usermessagesize;
  109. char const *usermessagename;
  110. SVC_EntityMessage entityMsg;
  111. SVC_UserMessage userMsg;
  112. };
  113. static MsgData s_MsgData;
  114. void SeedRandomNumberGenerator( bool random_invariant )
  115. {
  116. if (!random_invariant)
  117. {
  118. long iSeed;
  119. g_pVCR->Hook_Time( &iSeed );
  120. float flAppTime = Plat_FloatTime();
  121. ThreadId_t threadId = ThreadGetCurrentId();
  122. iSeed ^= (*((int *)&flAppTime));
  123. iSeed ^= threadId;
  124. RandomSeed( iSeed );
  125. }
  126. else
  127. {
  128. // Make those random numbers the same every time!
  129. RandomSeed( 0 );
  130. }
  131. }
  132. // ---------------------------------------------------------------------- //
  133. // Static helpers.
  134. // ---------------------------------------------------------------------- //
  135. static void PR_CheckEmptyString (const char *s)
  136. {
  137. if (s[0] <= ' ')
  138. Host_Error ("Bad string: %s", s);
  139. }
  140. // Average a list a vertices to find an approximate "center"
  141. static void CenterVerts( Vector verts[], int vertCount, Vector& center )
  142. {
  143. int i;
  144. float scale;
  145. if ( vertCount )
  146. {
  147. Vector edge0, edge1, normal;
  148. VectorCopy( vec3_origin, center );
  149. // sum up verts
  150. for ( i = 0; i < vertCount; i++ )
  151. {
  152. VectorAdd( center, verts[i], center );
  153. }
  154. scale = 1.0f / (float)vertCount;
  155. VectorScale( center, scale, center ); // divide by vertCount
  156. // Compute 2 poly edges
  157. VectorSubtract( verts[1], verts[0], edge0 );
  158. VectorSubtract( verts[vertCount-1], verts[0], edge1 );
  159. // cross for normal
  160. CrossProduct( edge0, edge1, normal );
  161. // Find the component of center that is outside of the plane
  162. scale = DotProduct( center, normal ) - DotProduct( verts[0], normal );
  163. // subtract it off
  164. VectorMA( center, scale, normal, center );
  165. // center is in the plane now
  166. }
  167. }
  168. // Copy the list of verts from an msurface_t int a linear array
  169. static void SurfaceToVerts( model_t *model, SurfaceHandle_t surfID, Vector verts[], int *vertCount )
  170. {
  171. if ( *vertCount > MSurf_VertCount( surfID ) )
  172. *vertCount = MSurf_VertCount( surfID );
  173. // Build the list of verts from 0 to n
  174. for ( int i = 0; i < *vertCount; i++ )
  175. {
  176. int vertIndex = model->brush.pShared->vertindices[ MSurf_FirstVertIndex( surfID ) + i ];
  177. Vector& vert = model->brush.pShared->vertexes[ vertIndex ].position;
  178. VectorCopy( vert, verts[i] );
  179. }
  180. // vert[0] is the first and last vert, there is no copy
  181. }
  182. // Calculate the surface area of an arbitrary msurface_t polygon (convex with collinear verts)
  183. static float SurfaceArea( model_t *model, SurfaceHandle_t surfID )
  184. {
  185. Vector center, verts[32];
  186. int vertCount = 32;
  187. float area;
  188. int i;
  189. // Compute a "center" point and fan
  190. SurfaceToVerts( model, surfID, verts, &vertCount );
  191. CenterVerts( verts, vertCount, center );
  192. area = 0;
  193. // For a triangle of the center and each edge
  194. for ( i = 0; i < vertCount; i++ )
  195. {
  196. Vector edge0, edge1, out;
  197. int next;
  198. next = (i+1)%vertCount;
  199. VectorSubtract( verts[i], center, edge0 ); // 0.5 * edge cross edge (0.5 is done once at the end)
  200. VectorSubtract( verts[next], center, edge1 );
  201. CrossProduct( edge0, edge1, out );
  202. area += VectorLength( out );
  203. }
  204. return area * 0.5; // 0.5 here
  205. }
  206. // Average the list of vertices to find an approximate "center"
  207. static void SurfaceCenter( model_t *model, SurfaceHandle_t surfID, Vector& center )
  208. {
  209. Vector verts[32]; // We limit faces to 32 verts elsewhere in the engine
  210. int vertCount = 32;
  211. SurfaceToVerts( model, surfID, verts, &vertCount );
  212. CenterVerts( verts, vertCount, center );
  213. }
  214. static bool ValidCmd( const char *pCmd )
  215. {
  216. int len;
  217. len = strlen(pCmd);
  218. // Valid commands all have a ';' or newline '\n' as their last character
  219. if ( len && (pCmd[len-1] == '\n' || pCmd[len-1] == ';') )
  220. return true;
  221. return false;
  222. }
  223. // ---------------------------------------------------------------------- //
  224. // CVEngineServer
  225. // ---------------------------------------------------------------------- //
  226. class CVEngineServer : public IVEngineServer
  227. {
  228. public:
  229. virtual void ChangeLevel( const char* s1, const char* s2)
  230. {
  231. if ( !s1 )
  232. {
  233. Sys_Error( "CVEngineServer::Changelevel with NULL s1\n" );
  234. }
  235. char cmd[ 256 ];
  236. char s1Escaped[ sizeof( cmd ) ];
  237. char s2Escaped[ sizeof( cmd ) ];
  238. if ( !Cbuf_EscapeCommandArg( s1, s1Escaped, sizeof( s1Escaped ) ) ||
  239. ( s2 && !Cbuf_EscapeCommandArg( s2, s2Escaped, sizeof( s2Escaped ) )))
  240. {
  241. Warning( "Illegal map name in ChangeLevel\n" );
  242. return;
  243. }
  244. int cmdLen = 0;
  245. if ( !s2 ) // no indication of where they are coming from; so just do a standard old changelevel
  246. {
  247. cmdLen = Q_snprintf( cmd, sizeof( cmd ), "changelevel %s\n", s1Escaped );
  248. }
  249. else
  250. {
  251. cmdLen = Q_snprintf( cmd, sizeof( cmd ), "changelevel2 %s %s\n", s1Escaped, s2Escaped );
  252. }
  253. if ( !cmdLen || cmdLen >= sizeof( cmd ) )
  254. {
  255. Warning( "Paramter overflow in ChangeLevel\n" );
  256. return;
  257. }
  258. Cbuf_AddText( cmd );
  259. }
  260. virtual int IsMapValid( const char *filename )
  261. {
  262. return modelloader->Map_IsValid( filename );
  263. }
  264. virtual bool IsDedicatedServer( void )
  265. {
  266. return sv.IsDedicated();
  267. }
  268. virtual int IsInEditMode( void )
  269. {
  270. #ifdef SWDS
  271. return false;
  272. #else
  273. return g_bInEditMode;
  274. #endif
  275. }
  276. virtual int IsInCommentaryMode( void )
  277. {
  278. #ifdef SWDS
  279. return false;
  280. #else
  281. return g_bInCommentaryMode;
  282. #endif
  283. }
  284. virtual void NotifyEdictFlagsChange( int iEdict )
  285. {
  286. if ( g_pLocalNetworkBackdoor )
  287. g_pLocalNetworkBackdoor->NotifyEdictFlagsChange( iEdict );
  288. }
  289. virtual const CCheckTransmitInfo* GetPrevCheckTransmitInfo( edict_t *pPlayerEdict )
  290. {
  291. int entnum = NUM_FOR_EDICT( pPlayerEdict );
  292. if ( entnum < 1 || entnum > sv.GetClientCount() )
  293. {
  294. Error( "Invalid client specified in GetPrevCheckTransmitInfo\n" );
  295. return NULL;
  296. }
  297. CGameClient *client = sv.Client( entnum-1 );
  298. return client->GetPrevPackInfo();
  299. }
  300. virtual int PrecacheDecal( const char *name, bool preload /*=false*/ )
  301. {
  302. PR_CheckEmptyString( name );
  303. int i = SV_FindOrAddDecal( name, preload );
  304. if ( i >= 0 )
  305. {
  306. return i;
  307. }
  308. Host_Error( "CVEngineServer::PrecacheDecal: '%s' overflow, too many decals", name );
  309. return 0;
  310. }
  311. virtual int PrecacheModel( const char *s, bool preload /*= false*/ )
  312. {
  313. PR_CheckEmptyString (s);
  314. int i = SV_FindOrAddModel( s, preload );
  315. if ( i >= 0 )
  316. {
  317. return i;
  318. }
  319. Host_Error( "CVEngineServer::PrecacheModel: '%s' overflow, too many models", s );
  320. return 0;
  321. }
  322. virtual int PrecacheGeneric(const char *s, bool preload /*= false*/ )
  323. {
  324. int i;
  325. PR_CheckEmptyString (s);
  326. i = SV_FindOrAddGeneric( s, preload );
  327. if (i >= 0)
  328. {
  329. return i;
  330. }
  331. Host_Error ("CVEngineServer::PrecacheGeneric: '%s' overflow", s);
  332. return 0;
  333. }
  334. virtual bool IsModelPrecached( char const *s ) const
  335. {
  336. int idx = SV_ModelIndex( s );
  337. return idx != -1 ? true : false;
  338. }
  339. virtual bool IsDecalPrecached( char const *s ) const
  340. {
  341. int idx = SV_DecalIndex( s );
  342. return idx != -1 ? true : false;
  343. }
  344. virtual bool IsGenericPrecached( char const *s ) const
  345. {
  346. int idx = SV_GenericIndex( s );
  347. return idx != -1 ? true : false;
  348. }
  349. virtual void ForceExactFile( const char *s )
  350. {
  351. Warning( "ForceExactFile no longer supported. Use sv_pure instead. (%s)\n", s );
  352. }
  353. virtual void ForceModelBounds( const char *s, const Vector &mins, const Vector &maxs )
  354. {
  355. PR_CheckEmptyString( s );
  356. SV_ForceModelBounds( s, mins, maxs );
  357. }
  358. virtual void ForceSimpleMaterial( const char *s )
  359. {
  360. PR_CheckEmptyString( s );
  361. SV_ForceSimpleMaterial( s );
  362. }
  363. virtual bool IsInternalBuild( void )
  364. {
  365. return !phonehome->IsExternalBuild();
  366. }
  367. //-----------------------------------------------------------------------------
  368. // Purpose: Precache a sentence file (parse on server, send to client)
  369. // Input : *s - file name
  370. //-----------------------------------------------------------------------------
  371. virtual int PrecacheSentenceFile( const char *s, bool preload /*= false*/ )
  372. {
  373. // UNDONE: Set up preload flag
  374. // UNDONE: Send this data to the client to support multiple sentence files
  375. VOX_ReadSentenceFile( s );
  376. return 0;
  377. }
  378. //-----------------------------------------------------------------------------
  379. // Purpose: Retrieves the pvs for an origin into the specified array
  380. // Input : *org - origin
  381. // outputpvslength - size of outputpvs array in bytes
  382. // *outputpvs - If null, then return value is the needed length
  383. // Output : int - length of pvs array used ( in bytes )
  384. //-----------------------------------------------------------------------------
  385. virtual int GetClusterForOrigin( const Vector& org )
  386. {
  387. return CM_LeafCluster( CM_PointLeafnum( org ) );
  388. }
  389. virtual int GetPVSForCluster( int clusterIndex, int outputpvslength, unsigned char *outputpvs )
  390. {
  391. int length = (CM_NumClusters()+7)>>3;
  392. if ( outputpvs )
  393. {
  394. if ( outputpvslength < length )
  395. {
  396. Sys_Error( "GetPVSForOrigin called with inusfficient sized pvs array, need %i bytes!", length );
  397. return length;
  398. }
  399. CM_Vis( outputpvs, outputpvslength, clusterIndex, DVIS_PVS );
  400. }
  401. return length;
  402. }
  403. //-----------------------------------------------------------------------------
  404. // Purpose: Test origin against pvs array retreived from GetPVSForOrigin
  405. // Input : *org - origin to chec
  406. // checkpvslength - length of pvs array
  407. // *checkpvs -
  408. // Output : bool - true if entity is visible
  409. //-----------------------------------------------------------------------------
  410. virtual bool CheckOriginInPVS( const Vector& org, const unsigned char *checkpvs, int checkpvssize )
  411. {
  412. int clusterIndex = CM_LeafCluster( CM_PointLeafnum( org ) );
  413. if ( clusterIndex < 0 )
  414. return false;
  415. int offset = clusterIndex>>3;
  416. if ( offset > checkpvssize )
  417. {
  418. Sys_Error( "CheckOriginInPVS: cluster would read past end of pvs data (%i:%i)\n",
  419. offset, checkpvssize );
  420. return false;
  421. }
  422. if ( !(checkpvs[offset] & (1<<(clusterIndex&7)) ) )
  423. {
  424. return false;
  425. }
  426. return true;
  427. }
  428. //-----------------------------------------------------------------------------
  429. // Purpose: Test origin against pvs array retreived from GetPVSForOrigin
  430. // Input : *org - origin to chec
  431. // checkpvslength - length of pvs array
  432. // *checkpvs -
  433. // Output : bool - true if entity is visible
  434. //-----------------------------------------------------------------------------
  435. virtual bool CheckBoxInPVS( const Vector& mins, const Vector& maxs, const unsigned char *checkpvs, int checkpvssize )
  436. {
  437. if ( !CM_BoxVisible( mins, maxs, checkpvs, checkpvssize ) )
  438. {
  439. return false;
  440. }
  441. return true;
  442. }
  443. virtual int GetPlayerUserId( const edict_t *e )
  444. {
  445. if ( !sv.IsActive() || !e)
  446. return -1;
  447. for ( int i = 0; i < sv.GetClientCount(); i++ )
  448. {
  449. CGameClient *pClient = sv.Client(i);
  450. if ( pClient->edict == e )
  451. {
  452. return pClient->m_UserID;
  453. }
  454. }
  455. // Couldn't find it
  456. return -1;
  457. }
  458. virtual const char *GetPlayerNetworkIDString( const edict_t *e )
  459. {
  460. if ( !sv.IsActive() || !e)
  461. return NULL;
  462. for ( int i = 0; i < sv.GetClientCount(); i++ )
  463. {
  464. CGameClient *pGameClient = sv.Client(i);
  465. if ( pGameClient->edict == e )
  466. {
  467. return pGameClient->GetNetworkIDString();
  468. }
  469. }
  470. // Couldn't find it
  471. return NULL;
  472. }
  473. virtual bool IsPlayerNameLocked( const edict_t *pEdict )
  474. {
  475. if ( !sv.IsActive() || !pEdict )
  476. return false;
  477. for ( int i = 0; i < sv.GetClientCount(); i++ )
  478. {
  479. CGameClient *pClient = sv.Client( i );
  480. if ( pClient->edict == pEdict )
  481. {
  482. return pClient->IsPlayerNameLocked();
  483. }
  484. }
  485. return false;
  486. }
  487. virtual bool CanPlayerChangeName( const edict_t *pEdict )
  488. {
  489. if ( !sv.IsActive() || !pEdict )
  490. return false;
  491. for ( int i = 0; i < sv.GetClientCount(); i++ )
  492. {
  493. CGameClient *pClient = sv.Client( i );
  494. if ( pClient->edict == pEdict )
  495. {
  496. return ( !pClient->IsPlayerNameLocked() && !pClient->IsNameChangeOnCooldown() );
  497. }
  498. }
  499. return false;
  500. }
  501. // See header comment. This is the canonical map lookup spot, and a superset of the server gameDLL's
  502. // CanProvideLevel/PrepareLevelResources
  503. virtual eFindMapResult FindMap( /* in/out */ char *pMapName, int nMapNameMax )
  504. {
  505. char szOriginalName[256] = { 0 };
  506. V_strncpy( szOriginalName, pMapName, sizeof( szOriginalName ) );
  507. IServerGameDLL::eCanProvideLevelResult eCanGameDLLProvide = IServerGameDLL::eCanProvideLevel_CannotProvide;
  508. if ( g_iServerGameDLLVersion >= 10 )
  509. {
  510. eCanGameDLLProvide = serverGameDLL->CanProvideLevel( pMapName, nMapNameMax );
  511. }
  512. if ( eCanGameDLLProvide == IServerGameDLL::eCanProvideLevel_Possibly )
  513. {
  514. return eFindMap_PossiblyAvailable;
  515. }
  516. else if ( eCanGameDLLProvide == IServerGameDLL::eCanProvideLevel_CanProvide )
  517. {
  518. // See if the game dll fixed up the map name
  519. return ( V_strcmp( szOriginalName, pMapName ) == 0 ) ? eFindMap_Found : eFindMap_NonCanonical;
  520. }
  521. AssertMsg( eCanGameDLLProvide == IServerGameDLL::eCanProvideLevel_CannotProvide,
  522. "Unhandled enum member" );
  523. char szDiskName[MAX_PATH] = { 0 };
  524. // Check if we can directly use this as a map
  525. Host_DefaultMapFileName( pMapName, szDiskName, sizeof( szDiskName ) );
  526. if ( *szDiskName && modelloader->Map_IsValid( szDiskName, true ) )
  527. {
  528. return eFindMap_Found;
  529. }
  530. // Fuzzy match in map list and check file
  531. char match[1][64] = { {0} };
  532. if ( MapList_ListMaps( pMapName, false, false, 1, sizeof( match[0] ), match ) && *(match[0]) )
  533. {
  534. Host_DefaultMapFileName( match[0], szDiskName, sizeof( szDiskName ) );
  535. if ( modelloader->Map_IsValid( szDiskName, true ) )
  536. {
  537. V_strncpy( pMapName, match[0], nMapNameMax );
  538. return eFindMap_FuzzyMatch;
  539. }
  540. }
  541. return eFindMap_NotFound;
  542. }
  543. virtual int IndexOfEdict(const edict_t *pEdict)
  544. {
  545. if ( !pEdict )
  546. {
  547. return 0;
  548. }
  549. int index = (int) ( pEdict - sv.edicts );
  550. if ( index < 0 || index > sv.max_edicts )
  551. {
  552. Sys_Error( "Bad entity in IndexOfEdict() index %i pEdict %p sv.edicts %p\n",
  553. index, pEdict, sv.edicts );
  554. }
  555. return index;
  556. }
  557. // Returns a pointer to an entity from an index, but only if the entity
  558. // is a valid DLL entity (ie. has an attached class)
  559. virtual edict_t* PEntityOfEntIndex(int iEntIndex)
  560. {
  561. if ( iEntIndex >= 0 && iEntIndex < sv.max_edicts )
  562. {
  563. edict_t *pEdict = EDICT_NUM( iEntIndex );
  564. if ( !pEdict->IsFree() )
  565. {
  566. return pEdict;
  567. }
  568. }
  569. return NULL;
  570. }
  571. virtual int GetEntityCount( void )
  572. {
  573. return sv.num_edicts - sv.free_edicts;
  574. }
  575. virtual INetChannelInfo* GetPlayerNetInfo( int playerIndex )
  576. {
  577. if ( playerIndex < 1 || playerIndex > sv.GetClientCount() )
  578. return NULL;
  579. CGameClient *client = sv.Client( playerIndex - 1 );
  580. return client->m_NetChannel;
  581. }
  582. virtual edict_t* CreateEdict( int iForceEdictIndex )
  583. {
  584. edict_t *pedict = ED_Alloc( iForceEdictIndex );
  585. if ( g_pServerPluginHandler )
  586. {
  587. g_pServerPluginHandler->OnEdictAllocated( pedict );
  588. }
  589. return pedict;
  590. }
  591. virtual void RemoveEdict(edict_t* ed)
  592. {
  593. if ( g_pServerPluginHandler )
  594. {
  595. g_pServerPluginHandler->OnEdictFreed( ed );
  596. }
  597. ED_Free(ed);
  598. }
  599. //
  600. // Request engine to allocate "cb" bytes on the entity's private data pointer.
  601. //
  602. virtual void *PvAllocEntPrivateData( long cb )
  603. {
  604. return calloc( 1, cb );
  605. }
  606. //
  607. // Release the private data memory, if any.
  608. //
  609. virtual void FreeEntPrivateData( void *pEntity )
  610. {
  611. #if defined( _DEBUG ) && defined( WIN32 )
  612. // set the memory to a known value
  613. int size = _msize( pEntity );
  614. memset( pEntity, 0xDD, size );
  615. #endif
  616. if ( pEntity )
  617. {
  618. free( pEntity );
  619. }
  620. }
  621. virtual void *SaveAllocMemory( size_t num, size_t size )
  622. {
  623. #ifndef SWDS
  624. return ::SaveAllocMemory(num, size);
  625. #else
  626. return NULL;
  627. #endif
  628. }
  629. virtual void SaveFreeMemory( void *pSaveMem )
  630. {
  631. #ifndef SWDS
  632. ::SaveFreeMemory(pSaveMem);
  633. #endif
  634. }
  635. /*
  636. =================
  637. EmitAmbientSound
  638. =================
  639. */
  640. virtual void EmitAmbientSound( int entindex, const Vector& pos, const char *samp, float vol,
  641. soundlevel_t soundlevel, int fFlags, int pitch, float soundtime /*=0.0f*/ )
  642. {
  643. SoundInfo_t sound;
  644. sound.SetDefault();
  645. sound.nEntityIndex = entindex;
  646. sound.fVolume = vol;
  647. sound.Soundlevel = soundlevel;
  648. sound.nFlags = fFlags;
  649. sound.nPitch = pitch;
  650. sound.nChannel = CHAN_STATIC;
  651. sound.vOrigin = pos;
  652. sound.bIsAmbient = true;
  653. ASSERT_COORD( sound.vOrigin );
  654. // set sound delay
  655. if ( soundtime != 0.0f )
  656. {
  657. sound.fDelay = soundtime - sv.GetTime();
  658. sound.nFlags |= SND_DELAY;
  659. }
  660. // if this is a sentence, get sentence number
  661. if ( TestSoundChar(samp, CHAR_SENTENCE) )
  662. {
  663. sound.bIsSentence = true;
  664. sound.nSoundNum = Q_atoi( PSkipSoundChars(samp) );
  665. if ( sound.nSoundNum >= VOX_SentenceCount() )
  666. {
  667. ConMsg("EmitAmbientSound: invalid sentence number: %s", PSkipSoundChars(samp));
  668. return;
  669. }
  670. }
  671. else
  672. {
  673. // check to see if samp was properly precached
  674. sound.bIsSentence = false;
  675. sound.nSoundNum = SV_SoundIndex( samp );
  676. if (sound.nSoundNum <= 0)
  677. {
  678. ConMsg ("EmitAmbientSound: sound not precached: %s\n", samp);
  679. return;
  680. }
  681. }
  682. if ( (fFlags & SND_SPAWNING) && sv.allowsignonwrites )
  683. {
  684. SVC_Sounds sndmsg;
  685. char buffer[32];
  686. sndmsg.m_DataOut.StartWriting(buffer, sizeof(buffer) );
  687. sndmsg.m_nNumSounds = 1;
  688. sndmsg.m_bReliableSound = true;
  689. SoundInfo_t defaultSound; defaultSound.SetDefault();
  690. sound.WriteDelta( &defaultSound, sndmsg.m_DataOut );
  691. // write into signon buffer
  692. if ( !sndmsg.WriteToBuffer( sv.m_Signon ) )
  693. {
  694. Sys_Error( "EmitAmbientSound: Init message would overflow signon buffer!\n" );
  695. return;
  696. }
  697. }
  698. else
  699. {
  700. if ( fFlags & SND_SPAWNING )
  701. {
  702. DevMsg("EmitAmbientSound: warning, broadcasting sound labled as SND_SPAWNING.\n" );
  703. }
  704. // send sound to all active players
  705. CEngineRecipientFilter filter;
  706. filter.AddAllPlayers();
  707. filter.MakeReliable();
  708. sv.BroadcastSound( sound, filter );
  709. }
  710. }
  711. virtual void FadeClientVolume(const edict_t *clientent,
  712. float fadePercent, float fadeOutSeconds, float holdTime, float fadeInSeconds)
  713. {
  714. int entnum = NUM_FOR_EDICT(clientent);
  715. if (entnum < 1 || entnum > sv.GetClientCount() )
  716. {
  717. ConMsg ("tried to DLL_FadeClientVolume a non-client\n");
  718. return;
  719. }
  720. IClient *client = sv.Client(entnum-1);
  721. NET_StringCmd sndMsg( va("soundfade %.1f %.1f %.1f %.1f", fadePercent, holdTime, fadeOutSeconds, fadeInSeconds ) );
  722. client->SendNetMsg( sndMsg );
  723. }
  724. //-----------------------------------------------------------------------------
  725. //
  726. // Sentence API
  727. //
  728. //-----------------------------------------------------------------------------
  729. virtual int SentenceGroupPick( int groupIndex, char *name, int nameLen )
  730. {
  731. if ( !name )
  732. {
  733. Sys_Error( "SentenceGroupPick with NULL name\n" );
  734. }
  735. Assert( nameLen > 0 );
  736. return VOX_GroupPick( groupIndex, name, nameLen );
  737. }
  738. virtual int SentenceGroupPickSequential( int groupIndex, char *name, int nameLen, int sentenceIndex, int reset )
  739. {
  740. if ( !name )
  741. {
  742. Sys_Error( "SentenceGroupPickSequential with NULL name\n" );
  743. }
  744. Assert( nameLen > 0 );
  745. return VOX_GroupPickSequential( groupIndex, name, nameLen, sentenceIndex, reset );
  746. }
  747. virtual int SentenceIndexFromName( const char *pSentenceName )
  748. {
  749. if ( !pSentenceName )
  750. {
  751. Sys_Error( "SentenceIndexFromName with NULL pSentenceName\n" );
  752. }
  753. int sentenceIndex = -1;
  754. VOX_LookupString( pSentenceName, &sentenceIndex );
  755. return sentenceIndex;
  756. }
  757. virtual const char *SentenceNameFromIndex( int sentenceIndex )
  758. {
  759. return VOX_SentenceNameFromIndex( sentenceIndex );
  760. }
  761. virtual int SentenceGroupIndexFromName( const char *pGroupName )
  762. {
  763. if ( !pGroupName )
  764. {
  765. Sys_Error( "SentenceGroupIndexFromName with NULL pGroupName\n" );
  766. }
  767. return VOX_GroupIndexFromName( pGroupName );
  768. }
  769. virtual const char *SentenceGroupNameFromIndex( int groupIndex )
  770. {
  771. return VOX_GroupNameFromIndex( groupIndex );
  772. }
  773. virtual float SentenceLength( int sentenceIndex )
  774. {
  775. return VOX_SentenceLength( sentenceIndex );
  776. }
  777. //-----------------------------------------------------------------------------
  778. virtual int CheckHeadnodeVisible( int nodenum, const byte *visbits, int vissize )
  779. {
  780. return CM_HeadnodeVisible(nodenum, visbits, vissize );
  781. }
  782. /*
  783. =================
  784. ServerCommand
  785. Sends text to servers execution buffer
  786. localcmd (string)
  787. =================
  788. */
  789. virtual void ServerCommand( const char *str )
  790. {
  791. if ( !str )
  792. {
  793. Sys_Error( "ServerCommand with NULL string\n" );
  794. }
  795. if ( ValidCmd( str ) )
  796. {
  797. Cbuf_AddText( str );
  798. }
  799. else
  800. {
  801. ConMsg( "Error, bad server command %s\n", str );
  802. }
  803. }
  804. /*
  805. =================
  806. ServerExecute
  807. Executes all commands in server buffer
  808. localcmd (string)
  809. =================
  810. */
  811. virtual void ServerExecute( void )
  812. {
  813. Cbuf_Execute();
  814. }
  815. /*
  816. =================
  817. ClientCommand
  818. Sends text over to the client's execution buffer
  819. stuffcmd (clientent, value)
  820. =================
  821. */
  822. virtual void ClientCommand(edict_t* pEdict, const char* szFmt, ...)
  823. {
  824. va_list argptr;
  825. static char szOut[1024];
  826. va_start(argptr, szFmt);
  827. Q_vsnprintf(szOut, sizeof( szOut ), szFmt, argptr);
  828. va_end(argptr);
  829. if ( szOut[0] == 0 )
  830. {
  831. Warning( "ClientCommand, 0 length string supplied.\n" );
  832. return;
  833. }
  834. int entnum = NUM_FOR_EDICT( pEdict );
  835. if ( ( entnum < 1 ) || ( entnum > sv.GetClientCount() ) )
  836. {
  837. ConMsg("\n!!!\n\nStuffCmd: Some entity tried to stuff '%s' to console buffer of entity %i when maxclients was set to %i, ignoring\n\n",
  838. szOut, entnum, sv.GetMaxClients() );
  839. return;
  840. }
  841. NET_StringCmd string( szOut );
  842. sv.GetClient(entnum-1)->SendNetMsg( string );
  843. }
  844. // Send a client command keyvalues
  845. // keyvalues are deleted inside the function
  846. virtual void ClientCommandKeyValues( edict_t *pEdict, KeyValues *pCommand )
  847. {
  848. if ( !pCommand )
  849. return;
  850. int entnum = NUM_FOR_EDICT( pEdict );
  851. if ( ( entnum < 1 ) || ( entnum > sv.GetClientCount() ) )
  852. {
  853. ConMsg("\n!!!\n\nClientCommandKeyValues: Some entity tried to stuff '%s' to console buffer of entity %i when maxclients was set to %i, ignoring\n\n",
  854. pCommand->GetName(), entnum, sv.GetMaxClients() );
  855. return;
  856. }
  857. SVC_CmdKeyValues cmd( pCommand );
  858. sv.GetClient(entnum-1)->SendNetMsg( cmd );
  859. }
  860. /*
  861. ===============
  862. LightStyle
  863. void(float style, string value) lightstyle
  864. ===============
  865. */
  866. virtual void LightStyle(int style, const char* val)
  867. {
  868. if ( !val )
  869. {
  870. Sys_Error( "LightStyle with NULL value!\n" );
  871. }
  872. // change the string in string table
  873. INetworkStringTable *stringTable = sv.GetLightStyleTable();
  874. stringTable->SetStringUserData( style, Q_strlen(val)+1, val );
  875. }
  876. virtual void StaticDecal( const Vector& origin, int decalIndex, int entityIndex, int modelIndex, bool lowpriority )
  877. {
  878. SVC_BSPDecal decal;
  879. decal.m_Pos = origin;
  880. decal.m_nDecalTextureIndex = decalIndex;
  881. decal.m_nEntityIndex = entityIndex;
  882. decal.m_nModelIndex = modelIndex;
  883. decal.m_bLowPriority = lowpriority;
  884. if ( sv.allowsignonwrites )
  885. {
  886. decal.WriteToBuffer( sv.m_Signon );
  887. }
  888. else
  889. {
  890. sv.BroadcastMessage( decal, false, true );
  891. }
  892. }
  893. void Message_DetermineMulticastRecipients( bool usepas, const Vector& origin, CBitVec< ABSOLUTE_PLAYER_LIMIT >& playerbits )
  894. {
  895. SV_DetermineMulticastRecipients( usepas, origin, playerbits );
  896. }
  897. /*
  898. ===============================================================================
  899. MESSAGE WRITING
  900. ===============================================================================
  901. */
  902. virtual bf_write *EntityMessageBegin( int ent_index, ServerClass * ent_class, bool reliable )
  903. {
  904. if ( s_MsgData.started )
  905. {
  906. Sys_Error( "EntityMessageBegin: New message started before matching call to EndMessage.\n " );
  907. return NULL;
  908. }
  909. s_MsgData.Reset();
  910. Assert( ent_class );
  911. s_MsgData.filter = NULL;
  912. s_MsgData.reliable = reliable;
  913. s_MsgData.started = true;
  914. s_MsgData.currentMsg = &s_MsgData.entityMsg;
  915. s_MsgData.entityMsg.m_nEntityIndex = ent_index;
  916. s_MsgData.entityMsg.m_nClassID = ent_class->m_ClassID;
  917. s_MsgData.entityMsg.m_DataOut.Reset();
  918. return &s_MsgData.entityMsg.m_DataOut;
  919. }
  920. virtual bf_write *UserMessageBegin( IRecipientFilter *filter, int msg_index )
  921. {
  922. if ( s_MsgData.started )
  923. {
  924. Sys_Error( "UserMessageBegin: New message started before matching call to EndMessage.\n " );
  925. return NULL;
  926. }
  927. s_MsgData.Reset();
  928. Assert( filter );
  929. s_MsgData.filter = filter;
  930. s_MsgData.reliable = filter->IsReliable();
  931. s_MsgData.started = true;
  932. s_MsgData.currentMsg = &s_MsgData.userMsg;
  933. s_MsgData.userMsg.m_nMsgType = msg_index;
  934. s_MsgData.userMsg.m_DataOut.Reset();
  935. return &s_MsgData.userMsg.m_DataOut;
  936. }
  937. // Validates user message type and checks to see if it's variable length
  938. // returns true if variable length
  939. int Message_CheckMessageLength()
  940. {
  941. if ( s_MsgData.currentMsg == &s_MsgData.userMsg )
  942. {
  943. char msgname[ 256 ];
  944. int msgsize = -1;
  945. int msgtype = s_MsgData.userMsg.m_nMsgType;
  946. if ( !serverGameDLL->GetUserMessageInfo( msgtype, msgname, sizeof(msgname), msgsize ) )
  947. {
  948. Warning( "Unable to find user message for index %i\n", msgtype );
  949. return -1;
  950. }
  951. int bytesWritten = s_MsgData.userMsg.m_DataOut.GetNumBytesWritten();
  952. if ( msgsize == -1 )
  953. {
  954. if ( bytesWritten > MAX_USER_MSG_DATA )
  955. {
  956. Warning( "DLL_MessageEnd: Refusing to send user message %s of %i bytes to client, user message size limit is %i bytes\n",
  957. msgname, bytesWritten, MAX_USER_MSG_DATA );
  958. return -1;
  959. }
  960. }
  961. else if ( msgsize != bytesWritten )
  962. {
  963. Warning( "User Msg '%s': %d bytes written, expected %d\n",
  964. msgname, bytesWritten, msgsize );
  965. return -1;
  966. }
  967. return bytesWritten; // all checks passed, estimated final length
  968. }
  969. if ( s_MsgData.currentMsg == &s_MsgData.entityMsg )
  970. {
  971. int bytesWritten = s_MsgData.entityMsg.m_DataOut.GetNumBytesWritten();
  972. if ( bytesWritten > MAX_ENTITY_MSG_DATA ) // TODO use a define or so
  973. {
  974. Warning( "Entity Message to %i, %i bytes written (max is %d)\n",
  975. s_MsgData.entityMsg.m_nEntityIndex, bytesWritten, MAX_ENTITY_MSG_DATA );
  976. return -1;
  977. }
  978. return bytesWritten; // all checks passed, estimated final length
  979. }
  980. Warning( "MessageEnd unknown message type.\n" );
  981. return -1;
  982. }
  983. virtual void MessageEnd( void )
  984. {
  985. if ( !s_MsgData.started )
  986. {
  987. Sys_Error( "MESSAGE_END called with no active message\n" );
  988. return;
  989. }
  990. int length = Message_CheckMessageLength();
  991. // check to see if it's a valid message
  992. if ( length < 0 )
  993. {
  994. s_MsgData.Reset(); // clear message data
  995. return;
  996. }
  997. if ( s_MsgData.filter )
  998. {
  999. // send entity/user messages only to full connected clients in filter
  1000. sv.BroadcastMessage( *s_MsgData.currentMsg, *s_MsgData.filter );
  1001. }
  1002. else
  1003. {
  1004. // send entity messages to all full connected clients
  1005. sv.BroadcastMessage( *s_MsgData.currentMsg, true, s_MsgData.reliable );
  1006. }
  1007. s_MsgData.Reset(); // clear message data
  1008. }
  1009. /* single print to a specific client */
  1010. virtual void ClientPrintf( edict_t *pEdict, const char *szMsg )
  1011. {
  1012. int entnum = NUM_FOR_EDICT( pEdict );
  1013. if (entnum < 1 || entnum > sv.GetClientCount() )
  1014. {
  1015. ConMsg ("tried to sprint to a non-client\n");
  1016. return;
  1017. }
  1018. sv.Client(entnum-1)->ClientPrintf( "%s", szMsg );
  1019. }
  1020. #ifdef SWDS
  1021. void Con_NPrintf( int pos, const char *fmt, ... )
  1022. {
  1023. }
  1024. void Con_NXPrintf( const struct con_nprint_s *info, const char *fmt, ... )
  1025. {
  1026. }
  1027. #else
  1028. void Con_NPrintf( int pos, const char *fmt, ... )
  1029. {
  1030. if ( IsDedicatedServer() )
  1031. return;
  1032. va_list argptr;
  1033. char text[4096];
  1034. va_start (argptr, fmt);
  1035. Q_vsnprintf(text, sizeof( text ), fmt, argptr);
  1036. va_end (argptr);
  1037. ::Con_NPrintf( pos, "%s", text );
  1038. }
  1039. void Con_NXPrintf( const struct con_nprint_s *info, const char *fmt, ... )
  1040. {
  1041. if ( IsDedicatedServer() )
  1042. return;
  1043. va_list argptr;
  1044. char text[4096];
  1045. va_start (argptr, fmt);
  1046. Q_vsnprintf(text, sizeof( text ), fmt, argptr);
  1047. va_end (argptr);
  1048. ::Con_NXPrintf( info, "%s", text );
  1049. }
  1050. #endif
  1051. virtual void SetView(const edict_t *clientent, const edict_t *viewent)
  1052. {
  1053. int clientnum = NUM_FOR_EDICT( clientent );
  1054. if (clientnum < 1 || clientnum > sv.GetClientCount() )
  1055. Host_Error ("DLL_SetView: not a client");
  1056. CGameClient *client = sv.Client(clientnum-1);
  1057. client->m_pViewEntity = viewent;
  1058. SVC_SetView view( NUM_FOR_EDICT(viewent) );
  1059. client->SendNetMsg( view );
  1060. }
  1061. virtual float Time(void)
  1062. {
  1063. return Sys_FloatTime();
  1064. }
  1065. virtual void CrosshairAngle(const edict_t *clientent, float pitch, float yaw)
  1066. {
  1067. int clientnum = NUM_FOR_EDICT( clientent );
  1068. if (clientnum < 1 || clientnum > sv.GetClientCount() )
  1069. Host_Error ("DLL_Crosshairangle: not a client");
  1070. IClient *client = sv.Client(clientnum-1);
  1071. if (pitch > 180)
  1072. pitch -= 360;
  1073. if (pitch < -180)
  1074. pitch += 360;
  1075. if (yaw > 180)
  1076. yaw -= 360;
  1077. if (yaw < -180)
  1078. yaw += 360;
  1079. SVC_CrosshairAngle crossHairMsg;
  1080. crossHairMsg.m_Angle.x = pitch;
  1081. crossHairMsg.m_Angle.y = yaw;
  1082. crossHairMsg.m_Angle.y = 0;
  1083. client->SendNetMsg( crossHairMsg );
  1084. }
  1085. virtual void GetGameDir( char *szGetGameDir, int maxlength )
  1086. {
  1087. COM_GetGameDir(szGetGameDir, maxlength );
  1088. }
  1089. virtual int CompareFileTime( const char *filename1, const char *filename2, int *iCompare)
  1090. {
  1091. return COM_CompareFileTime(filename1, filename2, iCompare);
  1092. }
  1093. virtual bool LockNetworkStringTables( bool lock )
  1094. {
  1095. return networkStringTableContainerServer->Lock( lock );
  1096. }
  1097. // For use with FAKE CLIENTS
  1098. virtual edict_t* CreateFakeClient( const char *netname )
  1099. {
  1100. CGameClient *fcl = static_cast<CGameClient*>( sv.CreateFakeClient( netname ) );
  1101. if ( !fcl )
  1102. {
  1103. // server is full
  1104. return NULL;
  1105. }
  1106. return fcl->edict;
  1107. }
  1108. // For use with FAKE CLIENTS
  1109. virtual edict_t* CreateFakeClientEx( const char *netname, bool bReportFakeClient /*= true*/ )
  1110. {
  1111. sv.SetReportNewFakeClients( bReportFakeClient );
  1112. edict_t *ret = CreateFakeClient( netname );
  1113. sv.SetReportNewFakeClients( true ); // Leave this set as true so other callers of sv.CreateFakeClient behave correctly.
  1114. return ret;
  1115. }
  1116. // Get a keyvalue for s specified client
  1117. virtual const char *GetClientConVarValue( int clientIndex, const char *name )
  1118. {
  1119. if ( clientIndex < 1 || clientIndex > sv.GetClientCount() )
  1120. {
  1121. DevMsg( 1, "GetClientConVarValue: player invalid index %i\n", clientIndex );
  1122. return "";
  1123. }
  1124. return sv.GetClient( clientIndex - 1 )->GetUserSetting( name );
  1125. }
  1126. virtual const char *ParseFile(const char *data, char *token, int maxlen)
  1127. {
  1128. return ::COM_ParseFile(data, token, maxlen );
  1129. }
  1130. virtual bool CopyFile( const char *source, const char *destination )
  1131. {
  1132. return ::COM_CopyFile( source, destination );
  1133. }
  1134. virtual void AddOriginToPVS( const Vector& origin )
  1135. {
  1136. ::SV_AddOriginToPVS(origin);
  1137. }
  1138. virtual void ResetPVS( byte* pvs, int pvssize )
  1139. {
  1140. ::SV_ResetPVS( pvs, pvssize );
  1141. }
  1142. virtual void SetAreaPortalState( int portalNumber, int isOpen )
  1143. {
  1144. CM_SetAreaPortalState(portalNumber, isOpen);
  1145. }
  1146. virtual void SetAreaPortalStates( const int *portalNumbers, const int *isOpen, int nPortals )
  1147. {
  1148. CM_SetAreaPortalStates( portalNumbers, isOpen, nPortals );
  1149. }
  1150. virtual void DrawMapToScratchPad( IScratchPad3D *pPad, unsigned long iFlags )
  1151. {
  1152. worldbrushdata_t *pData = host_state.worldmodel->brush.pShared;
  1153. if ( !pData )
  1154. return;
  1155. SurfaceHandle_t surfID = SurfaceHandleFromIndex( host_state.worldmodel->brush.firstmodelsurface, pData );
  1156. for (int i=0; i< host_state.worldmodel->brush.nummodelsurfaces; ++i, ++surfID)
  1157. {
  1158. // Don't bother with nodraw surfaces
  1159. if( MSurf_Flags( surfID ) & SURFDRAW_NODRAW )
  1160. continue;
  1161. CSPVertList vertList;
  1162. for ( int iVert=0; iVert < MSurf_VertCount( surfID ); iVert++ )
  1163. {
  1164. int iWorldVert = pData->vertindices[surfID->firstvertindex + iVert];
  1165. const Vector &vPos = pData->vertexes[iWorldVert].position;
  1166. vertList.m_Verts.AddToTail( CSPVert( vPos ) );
  1167. }
  1168. pPad->DrawPolygon( vertList );
  1169. }
  1170. }
  1171. const CBitVec<MAX_EDICTS>* GetEntityTransmitBitsForClient( int iClientIndex )
  1172. {
  1173. if ( iClientIndex < 0 || iClientIndex >= sv.GetClientCount() )
  1174. {
  1175. Assert( false );
  1176. return NULL;
  1177. }
  1178. CGameClient *pClient = sv.Client( iClientIndex );
  1179. CClientFrame *deltaFrame = pClient->GetClientFrame( pClient->m_nDeltaTick );
  1180. if ( !deltaFrame )
  1181. return NULL;
  1182. return &deltaFrame->transmit_entity;
  1183. }
  1184. virtual bool IsPaused()
  1185. {
  1186. return sv.IsPaused();
  1187. }
  1188. virtual void SetFakeClientConVarValue( edict_t *pEntity, const char *pCvarName, const char *value )
  1189. {
  1190. int clientnum = NUM_FOR_EDICT( pEntity );
  1191. if (clientnum < 1 || clientnum > sv.GetClientCount() )
  1192. Host_Error ("DLL_SetView: not a client");
  1193. CGameClient *client = sv.Client(clientnum-1);
  1194. if ( client->IsFakeClient() )
  1195. {
  1196. client->SetUserCVar( pCvarName, value );
  1197. client->m_bConVarsChanged = true;
  1198. }
  1199. }
  1200. virtual CSharedEdictChangeInfo* GetSharedEdictChangeInfo()
  1201. {
  1202. return &g_SharedEdictChangeInfo;
  1203. }
  1204. virtual IChangeInfoAccessor *GetChangeAccessor( const edict_t *pEdict )
  1205. {
  1206. return &sv.edictchangeinfo[ NUM_FOR_EDICT( pEdict ) ];
  1207. }
  1208. virtual QueryCvarCookie_t StartQueryCvarValue( edict_t *pPlayerEntity, const char *pCvarName )
  1209. {
  1210. int clientnum = NUM_FOR_EDICT( pPlayerEntity );
  1211. if (clientnum < 1 || clientnum > sv.GetClientCount() )
  1212. Host_Error( "StartQueryCvarValue: not a client" );
  1213. CGameClient *client = sv.Client( clientnum-1 );
  1214. return SendCvarValueQueryToClient( client, pCvarName, false );
  1215. }
  1216. // Name of most recently load .sav file
  1217. virtual char const *GetMostRecentlyLoadedFileName()
  1218. {
  1219. #if !defined( SWDS )
  1220. return saverestore->GetMostRecentlyLoadedFileName();
  1221. #else
  1222. return "";
  1223. #endif
  1224. }
  1225. virtual char const *GetSaveFileName()
  1226. {
  1227. #if !defined( SWDS )
  1228. return saverestore->GetSaveFileName();
  1229. #else
  1230. return "";
  1231. #endif
  1232. }
  1233. // Tells the engine we can immdiately re-use all edict indices
  1234. // even though we may not have waited enough time
  1235. virtual void AllowImmediateEdictReuse( )
  1236. {
  1237. ED_AllowImmediateReuse();
  1238. }
  1239. virtual void MultiplayerEndGame()
  1240. {
  1241. #if !defined( SWDS )
  1242. g_pMatchmaking->EndGame();
  1243. #endif
  1244. }
  1245. virtual void ChangeTeam( const char *pTeamName )
  1246. {
  1247. #if !defined( SWDS )
  1248. g_pMatchmaking->ChangeTeam( pTeamName );
  1249. #endif
  1250. }
  1251. virtual void SetAchievementMgr( IAchievementMgr *pAchievementMgr )
  1252. {
  1253. g_pAchievementMgr = pAchievementMgr;
  1254. }
  1255. virtual IAchievementMgr *GetAchievementMgr()
  1256. {
  1257. return g_pAchievementMgr;
  1258. }
  1259. virtual int GetAppID()
  1260. {
  1261. return GetSteamAppID();
  1262. }
  1263. virtual bool IsLowViolence();
  1264. /*
  1265. =================
  1266. InsertServerCommand
  1267. Sends text to servers execution buffer
  1268. localcmd (string)
  1269. =================
  1270. */
  1271. virtual void InsertServerCommand( const char *str )
  1272. {
  1273. if ( !str )
  1274. {
  1275. Sys_Error( "InsertServerCommand with NULL string\n" );
  1276. }
  1277. if ( ValidCmd( str ) )
  1278. {
  1279. Cbuf_InsertText( str );
  1280. }
  1281. else
  1282. {
  1283. ConMsg( "Error, bad server command %s (InsertServerCommand)\n", str );
  1284. }
  1285. }
  1286. bool GetPlayerInfo( int ent_num, player_info_t *pinfo )
  1287. {
  1288. // Entity numbers are offset by 1 from the player numbers
  1289. return sv.GetPlayerInfo( (ent_num-1), pinfo );
  1290. }
  1291. bool IsClientFullyAuthenticated( edict_t *pEdict )
  1292. {
  1293. int entnum = NUM_FOR_EDICT( pEdict );
  1294. if (entnum < 1 || entnum > sv.GetClientCount() )
  1295. return false;
  1296. // Entity numbers are offset by 1 from the player numbers
  1297. CGameClient *client = sv.Client(entnum-1);
  1298. if ( client )
  1299. return client->IsFullyAuthenticated();
  1300. return false;
  1301. }
  1302. void SetDedicatedServerBenchmarkMode( bool bBenchmarkMode )
  1303. {
  1304. g_bDedicatedServerBenchmarkMode = bBenchmarkMode;
  1305. if ( bBenchmarkMode )
  1306. {
  1307. extern ConVar sv_stressbots;
  1308. sv_stressbots.SetValue( (int)1 );
  1309. }
  1310. }
  1311. // Returns the SteamID of the game server
  1312. const CSteamID *GetGameServerSteamID()
  1313. {
  1314. if ( !Steam3Server().GetGSSteamID().IsValid() )
  1315. return NULL;
  1316. return &Steam3Server().GetGSSteamID();
  1317. }
  1318. // Returns the SteamID of the specified player. It'll be NULL if the player hasn't authenticated yet.
  1319. const CSteamID *GetClientSteamID( edict_t *pPlayerEdict )
  1320. {
  1321. int entnum = NUM_FOR_EDICT( pPlayerEdict );
  1322. return GetClientSteamIDByPlayerIndex( entnum );
  1323. }
  1324. const CSteamID *GetClientSteamIDByPlayerIndex( int entnum )
  1325. {
  1326. if (entnum < 1 || entnum > sv.GetClientCount() )
  1327. return NULL;
  1328. // Entity numbers are offset by 1 from the player numbers
  1329. CGameClient *client = sv.Client(entnum-1);
  1330. if ( !client )
  1331. return NULL;
  1332. // Make sure they are connected and Steam ID is valid
  1333. if ( !client->IsConnected() || !client->m_SteamID.IsValid() )
  1334. return NULL;
  1335. return &client->m_SteamID;
  1336. }
  1337. void SetGamestatsData( CGamestatsData *pGamestatsData )
  1338. {
  1339. g_pGamestatsData = pGamestatsData;
  1340. }
  1341. CGamestatsData *GetGamestatsData()
  1342. {
  1343. return g_pGamestatsData;
  1344. }
  1345. virtual IReplaySystem *GetReplay()
  1346. {
  1347. return g_pReplay;
  1348. }
  1349. virtual int GetClusterCount()
  1350. {
  1351. CCollisionBSPData *pBSPData = GetCollisionBSPData();
  1352. if ( pBSPData && pBSPData->map_vis )
  1353. return pBSPData->map_vis->numclusters;
  1354. return 0;
  1355. }
  1356. virtual int GetAllClusterBounds( bbox_t *pBBoxList, int maxBBox )
  1357. {
  1358. CCollisionBSPData *pBSPData = GetCollisionBSPData();
  1359. if ( pBSPData && pBSPData->map_vis && host_state.worldbrush )
  1360. {
  1361. // clamp to max clusters in the map
  1362. if ( maxBBox > pBSPData->map_vis->numclusters )
  1363. {
  1364. maxBBox = pBSPData->map_vis->numclusters;
  1365. }
  1366. // reset all of the bboxes
  1367. for ( int i = 0; i < maxBBox; i++ )
  1368. {
  1369. ClearBounds( pBBoxList[i].mins, pBBoxList[i].maxs );
  1370. }
  1371. // add each leaf's bounds to the bounds for that cluster
  1372. for ( int i = 0; i < host_state.worldbrush->numleafs; i++ )
  1373. {
  1374. mleaf_t *pLeaf = &host_state.worldbrush->leafs[i];
  1375. // skip solid leaves and leaves with cluster < 0
  1376. if ( !(pLeaf->contents & CONTENTS_SOLID) && pLeaf->cluster >= 0 && pLeaf->cluster < maxBBox )
  1377. {
  1378. Vector mins, maxs;
  1379. mins = pLeaf->m_vecCenter - pLeaf->m_vecHalfDiagonal;
  1380. maxs = pLeaf->m_vecCenter + pLeaf->m_vecHalfDiagonal;
  1381. AddPointToBounds( mins, pBBoxList[pLeaf->cluster].mins, pBBoxList[pLeaf->cluster].maxs );
  1382. AddPointToBounds( maxs, pBBoxList[pLeaf->cluster].mins, pBBoxList[pLeaf->cluster].maxs );
  1383. }
  1384. }
  1385. return pBSPData->map_vis->numclusters;
  1386. }
  1387. return 0;
  1388. }
  1389. virtual int GetServerVersion() const OVERRIDE
  1390. {
  1391. return GetSteamInfIDVersionInfo().ServerVersion;
  1392. }
  1393. virtual float GetServerTime() const OVERRIDE
  1394. {
  1395. return sv.GetTime();
  1396. }
  1397. virtual IServer *GetIServer() OVERRIDE
  1398. {
  1399. return (IServer *)&sv;
  1400. }
  1401. virtual void SetPausedForced( bool bPaused, float flDuration /*= -1.f*/ ) OVERRIDE
  1402. {
  1403. sv.SetPausedForced( bPaused, flDuration );
  1404. }
  1405. private:
  1406. // Purpose: Sends a temp entity to the client ( follows the format of the original MESSAGE_BEGIN stuff from HL1
  1407. virtual void PlaybackTempEntity( IRecipientFilter& filter, float delay, const void *pSender, const SendTable *pST, int classID );
  1408. virtual int CheckAreasConnected( int area1, int area2 );
  1409. virtual int GetArea( const Vector& origin );
  1410. virtual void GetAreaBits( int area, unsigned char *bits, int buflen );
  1411. virtual bool GetAreaPortalPlane( Vector const &vViewOrigin, int portalKey, VPlane *pPlane );
  1412. virtual client_textmessage_t *TextMessageGet( const char *pName );
  1413. virtual void LogPrint(const char * msg);
  1414. virtual bool LoadGameState( char const *pMapName, bool createPlayers );
  1415. virtual void LoadAdjacentEnts( const char *pOldLevel, const char *pLandmarkName );
  1416. virtual void ClearSaveDir();
  1417. virtual void ClearSaveDirAfterClientLoad();
  1418. virtual const char* GetMapEntitiesString();
  1419. virtual void BuildEntityClusterList( edict_t *pEdict, PVSInfo_t *pPVSInfo );
  1420. virtual void CleanUpEntityClusterList( PVSInfo_t *pPVSInfo );
  1421. virtual void SolidMoved( edict_t *pSolidEnt, ICollideable *pSolidCollide, const Vector* pPrevAbsOrigin, bool accurateBboxTriggerChecks );
  1422. virtual void TriggerMoved( edict_t *pTriggerEnt, bool accurateBboxTriggerChecks );
  1423. virtual ISpatialPartition *CreateSpatialPartition( const Vector& worldmin, const Vector& worldmax ) { return ::CreateSpatialPartition( worldmin, worldmax ); }
  1424. virtual void DestroySpatialPartition( ISpatialPartition *pPartition ) { ::DestroySpatialPartition( pPartition ); }
  1425. };
  1426. // Backwards-compat shim that inherits newest then provides overrides for the legacy behavior
  1427. class CVEngineServer22 : public CVEngineServer
  1428. {
  1429. virtual int IsMapValid( const char *filename ) OVERRIDE
  1430. {
  1431. // For users of the older interface, preserve here the old modelloader behavior of wrapping maps/%.bsp around
  1432. // the filename. This went away in newer interfaces since maps can now live in other places.
  1433. char szWrappedName[MAX_PATH] = { 0 };
  1434. V_snprintf( szWrappedName, sizeof( szWrappedName ), "maps/%s.bsp", filename );
  1435. return modelloader->Map_IsValid( szWrappedName );
  1436. }
  1437. };
  1438. //-----------------------------------------------------------------------------
  1439. // Expose CVEngineServer to the game DLL.
  1440. //-----------------------------------------------------------------------------
  1441. static CVEngineServer g_VEngineServer;
  1442. static CVEngineServer22 g_VEngineServer22;
  1443. // INTERFACEVERSION_VENGINESERVER_VERSION_21 is compatible with 22 latest since we only added virtuals to the end, so expose that as well.
  1444. EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CVEngineServer, IVEngineServer021, INTERFACEVERSION_VENGINESERVER_VERSION_21, g_VEngineServer22 );
  1445. EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CVEngineServer, IVEngineServer022, INTERFACEVERSION_VENGINESERVER_VERSION_22, g_VEngineServer22 );
  1446. EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CVEngineServer, IVEngineServer, INTERFACEVERSION_VENGINESERVER, g_VEngineServer );
  1447. // When bumping the version to this interface, check that our assumption is still valid and expose the older version in the same way
  1448. COMPILE_TIME_ASSERT( INTERFACEVERSION_VENGINESERVER_INT == 23 );
  1449. //-----------------------------------------------------------------------------
  1450. // Expose CVEngineServer to the engine.
  1451. //-----------------------------------------------------------------------------
  1452. IVEngineServer *g_pVEngineServer = &g_VEngineServer;
  1453. //-----------------------------------------------------------------------------
  1454. // Used to allocate pvs infos
  1455. //-----------------------------------------------------------------------------
  1456. static CUtlMemoryPool s_PVSInfoAllocator( 128, 128 * 64, CUtlMemoryPool::GROW_SLOW, "pvsinfopool", 128 );
  1457. //-----------------------------------------------------------------------------
  1458. // Purpose: Sends a temp entity to the client ( follows the format of the original MESSAGE_BEGIN stuff from HL1
  1459. // Input : msg_dest -
  1460. // delay -
  1461. // *origin -
  1462. // *recipient -
  1463. // *pSender -
  1464. // *pST -
  1465. // classID -
  1466. //-----------------------------------------------------------------------------
  1467. void CVEngineServer::PlaybackTempEntity( IRecipientFilter& filter, float delay, const void *pSender, const SendTable *pST, int classID )
  1468. {
  1469. VPROF( "PlaybackTempEntity" );
  1470. // don't add more events to a snapshot than a client can receive
  1471. if ( sv.m_TempEntities.Count() >= ((1<<CEventInfo::EVENT_INDEX_BITS)-1) )
  1472. {
  1473. // remove oldest effect
  1474. delete sv.m_TempEntities[0];
  1475. sv.m_TempEntities.Remove( 0 );
  1476. }
  1477. // Make this start at 1
  1478. classID = classID + 1;
  1479. // Encode now!
  1480. ALIGN4 unsigned char data[ CEventInfo::MAX_EVENT_DATA ] ALIGN4_POST;
  1481. bf_write buffer( "PlaybackTempEntity", data, sizeof(data) );
  1482. // write all properties, if init or reliable message delta against zero values
  1483. if( !SendTable_Encode( pST, pSender, &buffer, classID, NULL, false ) )
  1484. {
  1485. Host_Error( "PlaybackTempEntity: SendTable_Encode returned false (ent %d), overflow? %i\n", classID, buffer.IsOverflowed() ? 1 : 0 );
  1486. return;
  1487. }
  1488. // create CEventInfo:
  1489. CEventInfo *newEvent = new CEventInfo;
  1490. //copy client filter
  1491. newEvent->filter.AddPlayersFromFilter( &filter );
  1492. newEvent->classID = classID;
  1493. newEvent->pSendTable= pST;
  1494. newEvent->fire_delay= delay;
  1495. newEvent->bits = buffer.GetNumBitsWritten();
  1496. int size = Bits2Bytes( buffer.GetNumBitsWritten() );
  1497. newEvent->pData = new byte[size];
  1498. Q_memcpy( newEvent->pData, data, size );
  1499. // add to list
  1500. sv.m_TempEntities[sv.m_TempEntities.AddToTail()] = newEvent;
  1501. }
  1502. int CVEngineServer::CheckAreasConnected( int area1, int area2 )
  1503. {
  1504. return CM_AreasConnected(area1, area2);
  1505. }
  1506. //-----------------------------------------------------------------------------
  1507. // Purpose:
  1508. // Input : *origin -
  1509. // *bits -
  1510. // Output : void
  1511. //-----------------------------------------------------------------------------
  1512. int CVEngineServer::GetArea( const Vector& origin )
  1513. {
  1514. return CM_LeafArea( CM_PointLeafnum( origin ) );
  1515. }
  1516. void CVEngineServer::GetAreaBits( int area, unsigned char *bits, int buflen )
  1517. {
  1518. CM_WriteAreaBits( bits, buflen, area );
  1519. }
  1520. bool CVEngineServer::GetAreaPortalPlane( Vector const &vViewOrigin, int portalKey, VPlane *pPlane )
  1521. {
  1522. return CM_GetAreaPortalPlane( vViewOrigin, portalKey, pPlane );
  1523. }
  1524. client_textmessage_t *CVEngineServer::TextMessageGet( const char *pName )
  1525. {
  1526. return ::TextMessageGet( pName );
  1527. }
  1528. void CVEngineServer::LogPrint(const char * msg)
  1529. {
  1530. g_Log.Print( msg );
  1531. }
  1532. // HACKHACK: Save/restore wrapper - Move this to a different interface
  1533. bool CVEngineServer::LoadGameState( char const *pMapName, bool createPlayers )
  1534. {
  1535. #ifndef SWDS
  1536. return saverestore->LoadGameState( pMapName, createPlayers ) != 0;
  1537. #else
  1538. return 0;
  1539. #endif
  1540. }
  1541. void CVEngineServer::LoadAdjacentEnts( const char *pOldLevel, const char *pLandmarkName )
  1542. {
  1543. #ifndef SWDS
  1544. saverestore->LoadAdjacentEnts( pOldLevel, pLandmarkName );
  1545. #endif
  1546. }
  1547. void CVEngineServer::ClearSaveDir()
  1548. {
  1549. #ifndef SWDS
  1550. saverestore->ClearSaveDir();
  1551. #endif
  1552. }
  1553. void CVEngineServer::ClearSaveDirAfterClientLoad()
  1554. {
  1555. #ifndef SWDS
  1556. saverestore->RequestClearSaveDir();
  1557. #endif
  1558. }
  1559. const char* CVEngineServer::GetMapEntitiesString()
  1560. {
  1561. return CM_EntityString();
  1562. }
  1563. //-----------------------------------------------------------------------------
  1564. // Builds PVS information for an entity
  1565. //-----------------------------------------------------------------------------
  1566. inline bool SortClusterLessFunc( const int &left, const int &right )
  1567. {
  1568. return left < right;
  1569. }
  1570. void CVEngineServer::BuildEntityClusterList( edict_t *pEdict, PVSInfo_t *pPVSInfo )
  1571. {
  1572. int i, j;
  1573. int topnode;
  1574. int leafCount;
  1575. int leafs[MAX_TOTAL_ENT_LEAFS], clusters[MAX_TOTAL_ENT_LEAFS];
  1576. int area;
  1577. CleanUpEntityClusterList( pPVSInfo );
  1578. pPVSInfo->m_pClusters = 0;
  1579. pPVSInfo->m_nClusterCount = 0;
  1580. pPVSInfo->m_nAreaNum = 0;
  1581. pPVSInfo->m_nAreaNum2 = 0;
  1582. if ( !pEdict )
  1583. return;
  1584. ICollideable *pCollideable = pEdict->GetCollideable();
  1585. Assert( pCollideable );
  1586. if ( !pCollideable )
  1587. return;
  1588. topnode = -1;
  1589. //get all leafs, including solids
  1590. Vector vecWorldMins, vecWorldMaxs;
  1591. pCollideable->WorldSpaceSurroundingBounds( &vecWorldMins, &vecWorldMaxs );
  1592. leafCount = CM_BoxLeafnums( vecWorldMins, vecWorldMaxs, leafs, MAX_TOTAL_ENT_LEAFS, &topnode );
  1593. // set areas
  1594. for ( i = 0; i < leafCount; i++ )
  1595. {
  1596. clusters[i] = CM_LeafCluster( leafs[i] );
  1597. area = CM_LeafArea( leafs[i] );
  1598. if ( area == 0 )
  1599. continue;
  1600. // doors may legally straggle two areas,
  1601. // but nothing should ever need more than that
  1602. if ( pPVSInfo->m_nAreaNum && pPVSInfo->m_nAreaNum != area )
  1603. {
  1604. if ( pPVSInfo->m_nAreaNum2 && pPVSInfo->m_nAreaNum2 != area && sv.IsLoading() )
  1605. {
  1606. ConDMsg ("Object touching 3 areas at %f %f %f\n",
  1607. vecWorldMins[0], vecWorldMins[1], vecWorldMins[2]);
  1608. }
  1609. pPVSInfo->m_nAreaNum2 = area;
  1610. }
  1611. else
  1612. {
  1613. pPVSInfo->m_nAreaNum = area;
  1614. }
  1615. }
  1616. Vector center = (vecWorldMins+vecWorldMaxs) * 0.5f; // calc center
  1617. pPVSInfo->m_nHeadNode = topnode; // save headnode
  1618. // save origin
  1619. pPVSInfo->m_vCenter[0] = center[0];
  1620. pPVSInfo->m_vCenter[1] = center[1];
  1621. pPVSInfo->m_vCenter[2] = center[2];
  1622. if ( leafCount >= MAX_TOTAL_ENT_LEAFS )
  1623. {
  1624. // assume we missed some leafs, and mark by headnode
  1625. pPVSInfo->m_nClusterCount = -1;
  1626. return;
  1627. }
  1628. pPVSInfo->m_pClusters = pPVSInfo->m_pClustersInline;
  1629. if ( leafCount >= 16 )
  1630. {
  1631. std::make_heap( clusters, clusters + leafCount, SortClusterLessFunc );
  1632. std::sort_heap( clusters, clusters + leafCount, SortClusterLessFunc );
  1633. for ( i = 0; i < leafCount; i++ )
  1634. {
  1635. if ( clusters[i] == -1 )
  1636. continue; // not a visible leaf
  1637. if ( ( i > 0 ) && ( clusters[i] == clusters[i-1] ) )
  1638. continue;
  1639. if ( pPVSInfo->m_nClusterCount == MAX_FAST_ENT_CLUSTERS )
  1640. {
  1641. unsigned short *pClusters = (unsigned short *)s_PVSInfoAllocator.Alloc();
  1642. memcpy( pClusters, pPVSInfo->m_pClusters, MAX_FAST_ENT_CLUSTERS * sizeof(unsigned short) );
  1643. pPVSInfo->m_pClusters = pClusters;
  1644. }
  1645. else if ( pPVSInfo->m_nClusterCount == MAX_ENT_CLUSTERS )
  1646. {
  1647. // assume we missed some leafs, and mark by headnode
  1648. s_PVSInfoAllocator.Free( pPVSInfo->m_pClusters );
  1649. pPVSInfo->m_pClusters = 0;
  1650. pPVSInfo->m_nClusterCount = -1;
  1651. break;
  1652. }
  1653. pPVSInfo->m_pClusters[pPVSInfo->m_nClusterCount++] = (short)clusters[i];
  1654. }
  1655. return;
  1656. }
  1657. for ( i = 0; i < leafCount; i++ )
  1658. {
  1659. if ( clusters[i] == -1 )
  1660. continue; // not a visible leaf
  1661. for ( j = 0; j < i; j++ )
  1662. {
  1663. if ( clusters[j] == clusters[i] )
  1664. break;
  1665. }
  1666. if ( j != i )
  1667. continue;
  1668. if ( pPVSInfo->m_nClusterCount == MAX_FAST_ENT_CLUSTERS )
  1669. {
  1670. unsigned short *pClusters = (unsigned short*)s_PVSInfoAllocator.Alloc();
  1671. memcpy( pClusters, pPVSInfo->m_pClusters, MAX_FAST_ENT_CLUSTERS * sizeof(unsigned short) );
  1672. pPVSInfo->m_pClusters = pClusters;
  1673. }
  1674. else if ( pPVSInfo->m_nClusterCount == MAX_ENT_CLUSTERS )
  1675. {
  1676. // assume we missed some leafs, and mark by headnode
  1677. s_PVSInfoAllocator.Free( pPVSInfo->m_pClusters );
  1678. pPVSInfo->m_pClusters = 0;
  1679. pPVSInfo->m_nClusterCount = -1;
  1680. break;
  1681. }
  1682. pPVSInfo->m_pClusters[pPVSInfo->m_nClusterCount++] = (short)clusters[i];
  1683. }
  1684. }
  1685. //-----------------------------------------------------------------------------
  1686. // Cleans up the cluster list
  1687. //-----------------------------------------------------------------------------
  1688. void CVEngineServer::CleanUpEntityClusterList( PVSInfo_t *pPVSInfo )
  1689. {
  1690. if ( pPVSInfo->m_nClusterCount > MAX_FAST_ENT_CLUSTERS )
  1691. {
  1692. s_PVSInfoAllocator.Free( pPVSInfo->m_pClusters );
  1693. pPVSInfo->m_pClusters = 0;
  1694. pPVSInfo->m_nClusterCount = 0;
  1695. }
  1696. }
  1697. //-----------------------------------------------------------------------------
  1698. // Adds a handle to the list of entities to update when a partition query occurs
  1699. //-----------------------------------------------------------------------------
  1700. void CVEngineServer::SolidMoved( edict_t *pSolidEnt, ICollideable *pSolidCollide, const Vector* pPrevAbsOrigin, bool accurateBboxTriggerChecks )
  1701. {
  1702. SV_SolidMoved( pSolidEnt, pSolidCollide, pPrevAbsOrigin, accurateBboxTriggerChecks );
  1703. }
  1704. void CVEngineServer::TriggerMoved( edict_t *pTriggerEnt, bool accurateBboxTriggerChecks )
  1705. {
  1706. SV_TriggerMoved( pTriggerEnt, accurateBboxTriggerChecks );
  1707. }
  1708. //-----------------------------------------------------------------------------
  1709. // Called by the server to determine violence settings.
  1710. //-----------------------------------------------------------------------------
  1711. bool CVEngineServer::IsLowViolence()
  1712. {
  1713. return g_bLowViolence;
  1714. }