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.

933 lines
27 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Handles all the functions for implementing remote access to the engine
  4. //
  5. //===========================================================================//
  6. #include "server_pch.h"
  7. #include "iclient.h"
  8. #include "net.h"
  9. #include "utlbuffer.h"
  10. #include "utllinkedlist.h"
  11. #include "igameserverdata.h"
  12. #include "sv_remoteaccess.h"
  13. #include "sv_rcon.h"
  14. #include "sv_filter.h"
  15. #include "sys.h"
  16. #include "vprof_engine.h"
  17. #include "PlayerState.h"
  18. #include "sv_log.h"
  19. #ifndef SWDS
  20. #include "zip/XZip.h"
  21. #endif
  22. #include "cl_main.h"
  23. extern IServerGameDLL *serverGameDLL;
  24. CServerRemoteAccess g_ServerRemoteAccess;
  25. EXPOSE_SINGLE_INTERFACE_GLOBALVAR(CServerRemoteAccess, IGameServerData, GAMESERVERDATA_INTERFACE_VERSION, g_ServerRemoteAccess);
  26. ConVar sv_rcon_log( "sv_rcon_log", "1", 0, "Enable/disable rcon logging." );
  27. //-----------------------------------------------------------------------------
  28. // Host_Stats_f - prints out interesting stats about the server...
  29. //-----------------------------------------------------------------------------
  30. void Host_Stats_f (void)
  31. {
  32. char stats[512];
  33. g_ServerRemoteAccess.GetStatsString(stats, sizeof(stats));
  34. ConMsg("CPU In_(KB/s) Out_(KB/s) Uptime Map_changes FPS Players Connects\n%s\n", stats);
  35. }
  36. static ConCommand stats("stats", Host_Stats_f, "Prints server performance variables" );
  37. //-----------------------------------------------------------------------------
  38. // Purpose: Constructor
  39. //-----------------------------------------------------------------------------
  40. CServerRemoteAccess::CServerRemoteAccess()
  41. {
  42. m_iBytesSent = 0;
  43. m_iBytesReceived = 0;
  44. m_NextListenerID = 0;
  45. m_AdminUIID = INVALID_LISTENER_ID;
  46. m_nScreenshotListener = -1;
  47. }
  48. //-----------------------------------------------------------------------------
  49. // Purpose: unique id to associate data transfers with sessions
  50. //-----------------------------------------------------------------------------
  51. ra_listener_id CServerRemoteAccess::GetNextListenerID( bool authConnection, const netadr_t *adr )
  52. {
  53. int i = m_ListenerIDs.AddToTail();
  54. m_ListenerIDs[i].listenerID = i;
  55. m_ListenerIDs[i].authenticated = !authConnection;
  56. m_ListenerIDs[i].m_bHasAddress = ( adr != NULL );
  57. if ( adr )
  58. {
  59. m_ListenerIDs[i].adr = *adr;
  60. }
  61. return i;
  62. }
  63. bool GetStringHelper( CUtlBuffer & cmd, char *outBuf, int bufSize )
  64. {
  65. outBuf[0] = 0;
  66. cmd.GetStringManualCharCount( outBuf, bufSize );
  67. if ( !cmd.IsValid() )
  68. {
  69. cmd.Purge();
  70. return false;
  71. }
  72. return true;
  73. }
  74. //-----------------------------------------------------------------------------
  75. // Purpose: handles a request
  76. //-----------------------------------------------------------------------------
  77. void CServerRemoteAccess::WriteDataRequest( CRConServer *pNetworkListener, ra_listener_id listener, const void *buffer, int bufferSize)
  78. {
  79. m_iBytesReceived += bufferSize;
  80. // ConMsg("RemoteAccess: bytes received: %d\n", m_iBytesReceived);
  81. if ( bufferSize < 2*sizeof(int) ) // check that the buffer contains at least the id and type
  82. {
  83. return;
  84. }
  85. CUtlBuffer cmd(buffer, bufferSize, CUtlBuffer::READ_ONLY);
  86. bool invalidRequest = false;
  87. while ( invalidRequest == false && (int)cmd.TellGet() < (int)(cmd.Size() - 2 * sizeof(int) ) ) // while there is commands to read
  88. {
  89. // parse out the buffer
  90. int requestID = cmd.GetInt();
  91. pNetworkListener->SetRequestID( listener, requestID ); // tell the rcon server the ID so it can reflect it when the console redirect flushes
  92. int requestType = cmd.GetInt();
  93. switch (requestType)
  94. {
  95. case SERVERDATA_REQUESTVALUE:
  96. {
  97. if ( IsAuthenticated(listener) )
  98. {
  99. char variable[256];
  100. if ( !GetStringHelper( cmd, variable, sizeof(variable) ) )
  101. {
  102. invalidRequest = true;
  103. break;
  104. }
  105. RequestValue( listener, requestID, variable);
  106. if ( !GetStringHelper( cmd, variable, sizeof(variable) ) )
  107. {
  108. invalidRequest = true;
  109. break;
  110. }
  111. }
  112. else
  113. {
  114. char variable[256];
  115. if ( !GetStringHelper( cmd, variable, sizeof(variable) ) )
  116. {
  117. invalidRequest = true;
  118. break;
  119. }
  120. if ( !GetStringHelper( cmd, variable, sizeof(variable) ) )
  121. {
  122. invalidRequest = true;
  123. break;
  124. }
  125. }
  126. }
  127. break;
  128. case SERVERDATA_SETVALUE:
  129. {
  130. if ( IsAuthenticated(listener) )
  131. {
  132. char variable[256];
  133. char value[256];
  134. if ( !GetStringHelper( cmd, variable, sizeof(variable) ) )
  135. {
  136. invalidRequest = true;
  137. break;
  138. }
  139. if ( !GetStringHelper( cmd, value, sizeof(value) ) )
  140. {
  141. invalidRequest = true;
  142. break;
  143. }
  144. SetValue(variable, value);
  145. }
  146. else
  147. {
  148. char command[512];
  149. if ( !GetStringHelper( cmd, command, sizeof(command) ) )
  150. {
  151. invalidRequest = true;
  152. break;
  153. }
  154. if ( !GetStringHelper( cmd, command, sizeof(command) ) )
  155. {
  156. invalidRequest = true;
  157. break;
  158. }
  159. }
  160. }
  161. break;
  162. case SERVERDATA_EXECCOMMAND:
  163. {
  164. if ( IsAuthenticated(listener) )
  165. {
  166. char command[512];
  167. if ( !GetStringHelper( cmd, command, sizeof(command) ) )
  168. {
  169. invalidRequest = true;
  170. break;
  171. }
  172. ExecCommand(command);
  173. if ( listener != m_AdminUIID )
  174. {
  175. LogCommand( listener, va( "command \"%s\"", command) );
  176. }
  177. if ( !GetStringHelper( cmd, command, sizeof(command) ) )
  178. {
  179. invalidRequest = true;
  180. break;
  181. }
  182. }
  183. else
  184. {
  185. char command[512];
  186. if ( !GetStringHelper( cmd, command, sizeof(command) ) )
  187. {
  188. invalidRequest = true;
  189. break;
  190. }
  191. if ( !GetStringHelper( cmd, command, sizeof(command) ) )
  192. {
  193. invalidRequest = true;
  194. break;
  195. }
  196. LogCommand( listener, "Bad Password" );
  197. }
  198. }
  199. break;
  200. case SERVERDATA_AUTH:
  201. {
  202. char password[512];
  203. if ( !GetStringHelper( cmd, password, sizeof(password) ) )
  204. {
  205. invalidRequest = true;
  206. break;
  207. }
  208. CheckPassword( pNetworkListener, listener, requestID, password );
  209. if ( !GetStringHelper( cmd, password, sizeof(password) ) )
  210. {
  211. invalidRequest = true;
  212. break;
  213. }
  214. if ( m_ListenerIDs[ listener ].authenticated )
  215. {
  216. // if the second string has a non-zero value, it is a userid.
  217. int userID = atoi( password );
  218. const ConCommandBase *var = g_pCVar->GetCommands();
  219. while ( var )
  220. {
  221. if ( var->IsCommand() )
  222. {
  223. if ( Q_stricmp( var->GetName(), "mp_disable_autokick" ) == 0 )
  224. {
  225. Cbuf_AddText( va( "mp_disable_autokick %d\n", userID ) );
  226. Cbuf_Execute();
  227. break;
  228. }
  229. }
  230. var = var->GetNext();
  231. }
  232. }
  233. }
  234. break;
  235. case SERVERDATA_TAKE_SCREENSHOT:
  236. #ifndef SWDS
  237. m_nScreenshotListener = listener;
  238. CL_TakeJpeg( );
  239. #endif
  240. break;
  241. case SERVERDATA_SEND_CONSOLE_LOG:
  242. {
  243. #ifndef SWDS
  244. CUtlBuffer buf( 0, 0, CUtlBuffer::TEXT_BUFFER );
  245. if ( GetConsoleLogFileData( buf ) )
  246. {
  247. HZIP hZip = CreateZipZ( 0, 1024 * 1024, ZIP_MEMORY );
  248. void *pMem;
  249. unsigned long nLen;
  250. ZipAdd( hZip, "console.log", buf.Base(), buf.TellMaxPut(), ZIP_MEMORY );
  251. ZipGetMemory( hZip, &pMem, &nLen );
  252. SendResponseToClient( listener, SERVERDATA_CONSOLE_LOG_RESPONSE, pMem, nLen );
  253. CloseZip( hZip );
  254. }
  255. else
  256. {
  257. LogCommand( listener, "Failed to read console log!\n" );
  258. RespondString( listener, requestID, "Failed to read console log!\n" );
  259. }
  260. #endif
  261. }
  262. break;
  263. #ifdef VPROF_ENABLED
  264. case SERVERDATA_VPROF:
  265. {
  266. char password[25];
  267. if ( !GetStringHelper( cmd, password, sizeof(password) ) )
  268. {
  269. invalidRequest = true;
  270. break;
  271. }
  272. if ( !GetStringHelper( cmd, password, sizeof(password) ) )
  273. {
  274. invalidRequest = true;
  275. break;
  276. }
  277. if ( IsAuthenticated(listener) )
  278. {
  279. RegisterVProfDataListener( listener );
  280. LogCommand( listener, "Remote VProf started!\n" );
  281. RespondString( listener, requestID, "Remote VProf started!\n" );
  282. }
  283. }
  284. break;
  285. case SERVERDATA_REMOVE_VPROF:
  286. {
  287. char password[25];
  288. if ( !GetStringHelper( cmd, password, sizeof(password) ) )
  289. {
  290. invalidRequest = true;
  291. break;
  292. }
  293. if ( !GetStringHelper( cmd, password, sizeof(password) ) )
  294. {
  295. invalidRequest = true;
  296. break;
  297. }
  298. if ( IsAuthenticated(listener) )
  299. {
  300. RemoveVProfDataListener( listener );
  301. LogCommand( listener, "Remote VProf finished!\n" );
  302. RespondString( listener, requestID, "Remote VProf finished!\n" );
  303. }
  304. }
  305. break;
  306. #endif
  307. default:
  308. Assert(!("Unknown requestType in CServerRemoteAccess::WriteDataRequest()"));
  309. cmd.Purge();
  310. invalidRequest = true;
  311. break;
  312. };
  313. }
  314. }
  315. // NOTE: This version is used by the server DLL or server plugins
  316. void CServerRemoteAccess::WriteDataRequest( ra_listener_id listener, const void *buffer, int bufferSize )
  317. {
  318. WriteDataRequest( &RCONServer(), listener, buffer, bufferSize );
  319. }
  320. //-----------------------------------------------------------------------------
  321. // Uploads a screenshot to a particular listener
  322. //-----------------------------------------------------------------------------
  323. void CServerRemoteAccess::UploadScreenshot( const char *pFileName )
  324. {
  325. #ifndef SWDS
  326. if ( m_nScreenshotListener < 0 )
  327. return;
  328. CUtlBuffer buf( 128 * 1024, 0 );
  329. if ( g_pFullFileSystem->ReadFile( pFileName, "MOD", buf ) )
  330. {
  331. HZIP hZip = CreateZipZ( 0, 1024 * 1024, ZIP_MEMORY );
  332. void *pMem;
  333. unsigned long nLen;
  334. ZipAdd( hZip, "screenshot.jpg", buf.Base(), buf.TellMaxPut(), ZIP_MEMORY );
  335. ZipGetMemory( hZip, &pMem, &nLen );
  336. SendResponseToClient( m_nScreenshotListener, SERVERDATA_SCREENSHOT_RESPONSE, pMem, nLen );
  337. CloseZip( hZip );
  338. }
  339. else
  340. {
  341. LogCommand( m_nScreenshotListener, "Failed to read screenshot!\n" );
  342. RespondString( m_nScreenshotListener, 0, "Failed to read screenshot!\n" );
  343. }
  344. m_nScreenshotListener = -1;
  345. #endif
  346. }
  347. //-----------------------------------------------------------------------------
  348. // Purpose: log information about a command that ran
  349. //-----------------------------------------------------------------------------
  350. void CServerRemoteAccess::LogCommand( ra_listener_id listener, const char *msg )
  351. {
  352. if ( !sv_rcon_log.GetBool() )
  353. return;
  354. if ( listener < (ra_listener_id)m_ListenerIDs.Count() && m_ListenerIDs[listener].m_bHasAddress )
  355. {
  356. Log( "rcon from \"%s\": %s\n", m_ListenerIDs[listener].adr.ToString(), msg );
  357. }
  358. else
  359. {
  360. Log( "rcon from \"unknown\": %s\n", msg );
  361. }
  362. }
  363. //-----------------------------------------------------------------------------
  364. // Purpose: checks if this user has provided the correct password
  365. //-----------------------------------------------------------------------------
  366. void CServerRemoteAccess::CheckPassword( CRConServer *pNetworkListener, ra_listener_id listener, int requestID, const char *password )
  367. {
  368. // If the pw does not match, then not authed
  369. if ( !pNetworkListener->IsPassword( password ) )
  370. {
  371. BadPassword( pNetworkListener, listener );
  372. return;
  373. }
  374. // allocate a spot in the list for the response
  375. int i = m_ResponsePackets.AddToTail();
  376. m_ResponsePackets[i].responderID = listener; // record who we need to respond to
  377. CUtlBuffer &response = m_ResponsePackets[i].packet;
  378. // build the response
  379. response.PutInt(requestID);
  380. response.PutInt(SERVERDATA_AUTH_RESPONSE);
  381. response.PutString("");
  382. response.PutString("");
  383. m_ListenerIDs[ listener ].authenticated = true;
  384. }
  385. //-----------------------------------------------------------------------------
  386. // Purpose: returns true if this connection has provided the correct password
  387. //-----------------------------------------------------------------------------
  388. bool CServerRemoteAccess::IsAuthenticated( ra_listener_id listener )
  389. {
  390. // Checking for >= 0 is tautological because ra_listener_id is unsigned
  391. Assert( /*listener >= 0 &&*/ listener < (ra_listener_id)m_ListenerIDs.Count() );
  392. return m_ListenerIDs[listener].authenticated;
  393. }
  394. //-----------------------------------------------------------------------------
  395. // Purpose: send a bad password packet
  396. // Returns TRUE if socket was closed
  397. //-----------------------------------------------------------------------------
  398. void CServerRemoteAccess::BadPassword( CRConServer *pNetworkListener, ra_listener_id listener )
  399. {
  400. ListenerStore_t& listenerStore = m_ListenerIDs[listener];
  401. listenerStore.authenticated = false;
  402. if ( pNetworkListener->HandleFailedRconAuth( listenerStore.adr ) )
  403. {
  404. // Close the socket if too many failed attempts
  405. pNetworkListener->BCloseAcceptedSocket( listener );
  406. }
  407. else
  408. {
  409. //
  410. // Respond to the rcon user
  411. //
  412. // allocate a spot in the list for the response
  413. int i = m_ResponsePackets.AddToTail();
  414. m_ResponsePackets[i].responderID = listener; // record who we need to respond to
  415. CUtlBuffer &response = m_ResponsePackets[i].packet;
  416. // build the response
  417. response.PutInt(-1); // special flag for bad password
  418. response.PutInt(SERVERDATA_AUTH_RESPONSE);
  419. response.PutString("");
  420. response.PutString("");
  421. }
  422. }
  423. //-----------------------------------------------------------------------------
  424. // Purpose: returns the number of bytes read
  425. //-----------------------------------------------------------------------------
  426. int CServerRemoteAccess::GetDataResponseSize( ra_listener_id listener )
  427. {
  428. for( int i = m_ResponsePackets.Head(); m_ResponsePackets.IsValidIndex(i); i = m_ResponsePackets.Next(i) )
  429. {
  430. // copy response into buffer
  431. if ( m_ResponsePackets[i].responderID != listener ) // not for us, skip to the next entry
  432. continue;
  433. CUtlBuffer &response = m_ResponsePackets[i].packet;
  434. return response.TellPut();
  435. }
  436. return 0;
  437. }
  438. int CServerRemoteAccess::ReadDataResponse( ra_listener_id listener, void *buffer, int bufferSize )
  439. {
  440. for( int i = m_ResponsePackets.Head(); m_ResponsePackets.IsValidIndex(i); i = m_ResponsePackets.Next(i) )
  441. {
  442. // copy response into buffer
  443. if ( m_ResponsePackets[i].responderID != listener ) // not for us, skip to the next entry
  444. continue;
  445. CUtlBuffer &response = m_ResponsePackets[i].packet;
  446. int bytesToCopy = response.TellPut();
  447. Assert(bufferSize >= bytesToCopy);
  448. if (bytesToCopy <= bufferSize)
  449. {
  450. memcpy(buffer, response.Base(), bytesToCopy);
  451. }
  452. else
  453. {
  454. // not enough room in buffer, don't return message
  455. bytesToCopy = 0;
  456. }
  457. m_iBytesSent += bytesToCopy;
  458. // ConMsg("RemoteAccess: bytes sent: %d\n", m_iBytesSent);
  459. // remove from list
  460. m_ResponsePackets.Remove(i);
  461. // return bytes copied
  462. return bytesToCopy;
  463. }
  464. return 0;
  465. }
  466. //-----------------------------------------------------------------------------
  467. // Purpose: looks up a cvar and posts a return value
  468. //-----------------------------------------------------------------------------
  469. void CServerRemoteAccess::RequestValue( ra_listener_id listener, int requestID, const char *variable)
  470. {
  471. // look up the cvar
  472. CUtlBuffer value(0, 256, CUtlBuffer::TEXT_BUFFER); // text-mode buffer
  473. LookupValue(variable, value);
  474. // allocate a spot in the list for the response
  475. int i = m_ResponsePackets.AddToTail();
  476. m_ResponsePackets[i].responderID = listener; // record who we need to respond to
  477. CUtlBuffer &response = m_ResponsePackets[i].packet;
  478. // build the response
  479. response.PutInt(requestID);
  480. response.PutInt(SERVERDATA_RESPONSE_VALUE);
  481. response.PutString(variable);
  482. //Assert(value.TellPut() > 0);
  483. response.PutInt(value.TellPut());
  484. if (value.TellPut())
  485. {
  486. response.Put(value.Base(), value.TellPut());
  487. }
  488. }
  489. //-----------------------------------------------------------------------------
  490. // Purpose: looks up a cvar and posts a return value
  491. //-----------------------------------------------------------------------------
  492. void CServerRemoteAccess::RespondString( ra_listener_id listener, int requestID, const char *pString )
  493. {
  494. // allocate a spot in the list for the response
  495. int i = m_ResponsePackets.AddToTail();
  496. m_ResponsePackets[i].responderID = listener; // record who we need to respond to
  497. CUtlBuffer &response = m_ResponsePackets[i].packet;
  498. // build the response
  499. response.PutInt(requestID);
  500. response.PutInt(SERVERDATA_RESPONSE_STRING);
  501. response.PutString(pString);
  502. }
  503. //-----------------------------------------------------------------------------
  504. // Purpose: Sets a cvar or value
  505. //-----------------------------------------------------------------------------
  506. void CServerRemoteAccess::SetValue(const char *variable, const char *value)
  507. {
  508. // check for special types
  509. if (!stricmp(variable, "map"))
  510. {
  511. // push a map change command
  512. Cbuf_AddText( va( "changelevel %s\n", value ) );
  513. Cbuf_Execute();
  514. }
  515. else if (!stricmp(variable, "mapcycle"))
  516. {
  517. // write out a new mapcycle file
  518. ConVarRef mapcycle( "mapcyclefile" );
  519. if ( mapcycle.IsValid() )
  520. {
  521. FileHandle_t f = g_pFileSystem->Open(mapcycle.GetString(), "wt");
  522. if (!f)
  523. {
  524. // mapcycle file probably read only, fall pack to temporary file
  525. Msg("Couldn't write to read-only file %s, using file _temp_mapcycle.txt instead.\n", mapcycle.GetString());
  526. mapcycle.SetValue("_temp_mapcycle.txt" );
  527. f = g_pFileSystem->Open(mapcycle.GetString(), "wt");
  528. if (!f)
  529. {
  530. return;
  531. }
  532. }
  533. g_pFileSystem->Write(value, Q_strlen(value) + 1, f);
  534. g_pFileSystem->Close(f);
  535. }
  536. }
  537. else
  538. {
  539. // Stick the cvar set in the command string, so client notification, replication, etc happens
  540. Cbuf_AddText( va("%s %s", variable, value) );
  541. Cbuf_AddText("\n");
  542. Cbuf_Execute();
  543. }
  544. }
  545. //-----------------------------------------------------------------------------
  546. // Purpose: execs a command
  547. //-----------------------------------------------------------------------------
  548. void CServerRemoteAccess::ExecCommand(const char *cmdString)
  549. {
  550. Cbuf_AddText((char *)cmdString);
  551. Cbuf_AddText("\n");
  552. Cbuf_Execute();
  553. }
  554. //-----------------------------------------------------------------------------
  555. // Purpose: Finds the value of a particular server variable
  556. //-----------------------------------------------------------------------------
  557. bool CServerRemoteAccess::LookupValue(const char *variable, CUtlBuffer &value)
  558. {
  559. Assert(value.IsText());
  560. // first see if it's a cvar
  561. const char *strval = LookupStringValue(variable);
  562. if (strval)
  563. {
  564. value.PutString(strval);
  565. value.PutChar(0);
  566. }
  567. else if (!stricmp(variable, "stats"))
  568. {
  569. char szStats[512];
  570. GetStatsString( szStats, sizeof( szStats ) );
  571. value.PutString( szStats );
  572. value.PutChar(0);
  573. }
  574. else if (!stricmp(variable, "banlist"))
  575. {
  576. // returns a list of banned users and ip's
  577. GetUserBanList(value);
  578. }
  579. else if (!stricmp(variable, "playerlist"))
  580. {
  581. GetPlayerList(value);
  582. }
  583. else if (!stricmp(variable, "maplist"))
  584. {
  585. GetMapList(value);
  586. }
  587. else if (!stricmp(variable, "uptime"))
  588. {
  589. int timeSeconds = (int)(Plat_FloatTime());
  590. value.PutInt(timeSeconds);
  591. value.PutChar(0);
  592. }
  593. else if (!stricmp(variable, "ipaddress"))
  594. {
  595. char addr[25];
  596. Q_snprintf( addr, sizeof(addr), "%s:%i", net_local_adr.ToString(true), sv.GetUDPPort());
  597. value.PutString( addr );
  598. value.PutChar(0);
  599. }
  600. else if (!stricmp(variable, "mapcycle"))
  601. {
  602. ConVarRef mapcycle( "mapcyclefile" );
  603. if ( mapcycle.IsValid() )
  604. {
  605. // send the mapcycle list file
  606. FileHandle_t f = g_pFileSystem->Open(mapcycle.GetString(), "rb" );
  607. if ( f == FILESYSTEM_INVALID_HANDLE )
  608. return true;
  609. int len = g_pFileSystem->Size(f);
  610. char *mapcycleData = (char *)_alloca( len+1 );
  611. if ( len && g_pFileSystem->Read( mapcycleData, len, f ) )
  612. {
  613. mapcycleData[len] = 0; // Make sure it's null terminated.
  614. value.PutString((const char *)mapcycleData);
  615. value.PutChar(0);
  616. }
  617. else
  618. {
  619. value.PutString( "" );
  620. value.PutChar(0);
  621. }
  622. g_pFileSystem->Close( f );
  623. }
  624. }
  625. else
  626. {
  627. // value not found, null terminate
  628. value.PutChar(0);
  629. return false;
  630. }
  631. return true;
  632. }
  633. //-----------------------------------------------------------------------------
  634. // Purpose: Finds the value of a particular server variable for simple string values
  635. //-----------------------------------------------------------------------------
  636. const char *CServerRemoteAccess::LookupStringValue(const char *variable)
  637. {
  638. static char s_ReturnBuf[32];
  639. IConVar *pVar = g_pCVar->FindVar( variable );
  640. if ( pVar )
  641. {
  642. ConVarRef var( pVar );
  643. if ( var.IsValid() )
  644. return var.GetString();
  645. }
  646. // special types
  647. if ( !Q_stricmp( variable, "map" ) )
  648. return sv.GetMapName();
  649. if ( !Q_stricmp( variable, "playercount" ) )
  650. {
  651. Q_snprintf( s_ReturnBuf, sizeof(s_ReturnBuf) - 1, "%d", sv.GetNumClients() - sv.GetNumProxies());
  652. return s_ReturnBuf;
  653. }
  654. if ( !Q_stricmp( variable, "maxplayers" ) )
  655. {
  656. Q_snprintf( s_ReturnBuf, sizeof(s_ReturnBuf) - 1, "%d", sv.GetMaxClients() );
  657. return s_ReturnBuf;
  658. }
  659. if ( !Q_stricmp( variable, "gamedescription" ) && serverGameDLL )
  660. return serverGameDLL->GetGameDescription();
  661. return NULL;
  662. }
  663. //-----------------------------------------------------------------------------
  664. // Purpose: fills a buffer with a list of all banned IP addresses
  665. //-----------------------------------------------------------------------------
  666. void CServerRemoteAccess::GetUserBanList(CUtlBuffer &value)
  667. {
  668. // add user bans
  669. int i;
  670. for (i = 0; i < g_UserFilters.Count(); i++)
  671. {
  672. value.Printf("%i %s : %.3f min\n", i + 1, GetUserIDString(g_UserFilters[i].userid), g_UserFilters[i].banTime);
  673. }
  674. // add ip filters
  675. for (i = 0; i < g_IPFilters.Count() ; i++)
  676. {
  677. unsigned char b[4];
  678. *(unsigned *)b = g_IPFilters[i].compare;
  679. value.Printf("%i %i.%i.%i.%i : %.3f min\n", i + 1 + g_UserFilters.Count(), b[0], b[1], b[2], b[3], g_IPFilters[i].banTime);
  680. }
  681. value.PutChar(0);
  682. }
  683. void CServerRemoteAccess::GetStatsString(char *buf, int bufSize)
  684. {
  685. float avgIn=0,avgOut=0;
  686. sv.GetNetStats( avgIn, avgOut );
  687. // format: CPU percent, Bandwidth in, Bandwidth out, uptime, changelevels, framerate, total players
  688. _snprintf(buf, bufSize - 1, "%-6.2f %-10.2f %-11.2f %-7i %-12i %-8.2f %-8i %-8i",
  689. sv.GetCPUUsage() * 100,
  690. avgIn / 1024.0f,
  691. avgOut / 1024.0f,
  692. (int)(Sys_FloatTime()) / 60,
  693. sv.GetSpawnCount() - 1,
  694. 1.0/host_frametime, // frame rate
  695. sv.GetNumClients() - sv.GetNumProxies(),
  696. sv.GetNumConnections());
  697. buf[bufSize - 1] = 0;
  698. };
  699. //-----------------------------------------------------------------------------
  700. // Purpose: Fills buffer with details on everyone in the server
  701. //-----------------------------------------------------------------------------
  702. void CServerRemoteAccess::GetPlayerList(CUtlBuffer &value)
  703. {
  704. if ( !serverGameClients )
  705. {
  706. return;
  707. }
  708. for ( int i=0 ; i< sv.GetClientCount() ; i++ )
  709. {
  710. CGameClient *client = sv.Client(i);
  711. if ( !client || !client->IsActive() )
  712. continue;
  713. CPlayerState *pl = serverGameClients->GetPlayerState( client->edict );
  714. if ( !pl )
  715. continue;
  716. // valid user, add to buffer
  717. // format per user, each user seperated by a newline '\n'
  718. // "name authID ipAddress ping loss frags time"
  719. if ( client->IsFakeClient() )
  720. {
  721. value.Printf("\"%s\" %s 0 0 0 %d 0\n",
  722. client->GetClientName(),
  723. client->GetNetworkIDString(),
  724. pl->frags);
  725. }
  726. else
  727. {
  728. value.Printf("\"%s\" %s %s %d %d %d %d\n",
  729. client->GetClientName(),
  730. client->GetNetworkIDString(),
  731. client->GetNetChannel()->GetAddress(),
  732. (int)(client->GetNetChannel()->GetAvgLatency(FLOW_OUTGOING) * 1000.0f),
  733. (int)(client->GetNetChannel()->GetAvgLoss(FLOW_INCOMING)),
  734. pl->frags,
  735. (int)(client->GetNetChannel()->GetTimeConnected()));
  736. }
  737. }
  738. value.PutChar(0);
  739. }
  740. //-----------------------------------------------------------------------------
  741. // Purpose: Fills buffer with list of maps from this mod
  742. //-----------------------------------------------------------------------------
  743. void CServerRemoteAccess::GetMapList(CUtlBuffer &value)
  744. {
  745. // search the directory structure.
  746. char mapwild[MAX_QPATH];
  747. char friendly_com_gamedir[ MAX_OSPATH ];
  748. strcpy(mapwild, "maps/*.bsp");
  749. Q_strncpy( friendly_com_gamedir, com_gamedir, sizeof(friendly_com_gamedir) );
  750. Q_strlower( friendly_com_gamedir );
  751. char const *findfn = Sys_FindFirst( mapwild, NULL, 0 );
  752. while ( findfn )
  753. {
  754. char curDir[MAX_PATH];
  755. _snprintf(curDir, MAX_PATH, "maps/%s", findfn);
  756. g_pFileSystem->GetLocalPath(curDir, curDir, MAX_PATH);
  757. // limit maps displayed to ones for the mod only
  758. if (strstr(curDir, friendly_com_gamedir))
  759. {
  760. // clean up the map name
  761. char mapName[MAX_PATH];
  762. strcpy(mapName, findfn);
  763. char *extension = strstr(mapName, ".bsp");
  764. if (extension)
  765. {
  766. *extension = 0;
  767. }
  768. // write into buffer
  769. value.PutString(mapName);
  770. value.PutString("\n");
  771. }
  772. findfn = Sys_FindNext( NULL, 0 );
  773. }
  774. Sys_FindClose();
  775. value.PutChar(0);
  776. }
  777. //-----------------------------------------------------------------------------
  778. // Purpose: sends a message to all the watching admin UI's
  779. //-----------------------------------------------------------------------------
  780. void CServerRemoteAccess::SendMessageToAdminUI( ra_listener_id listenerID, const char *message)
  781. {
  782. if ( listenerID != m_AdminUIID )
  783. {
  784. Warning( "ServerRemoteAccess: Sending AdminUI message to non-AdminUI listener\n" );
  785. }
  786. // allocate a spot in the list for the response
  787. int i = m_ResponsePackets.AddToTail();
  788. m_ResponsePackets[i].responderID = listenerID; // record who we need to respond to
  789. CUtlBuffer &response = m_ResponsePackets[i].packet;
  790. // post the message
  791. response.PutInt(0);
  792. response.PutInt(SERVERDATA_UPDATE);
  793. response.PutString(message);
  794. }
  795. //-----------------------------------------------------------------------------
  796. // Purpose: Sends a response to the client
  797. //-----------------------------------------------------------------------------
  798. void CServerRemoteAccess::SendResponseToClient( ra_listener_id listenerID, ServerDataResponseType_t type, void *pData, int nDataLen )
  799. {
  800. // allocate a spot in the list for the response
  801. int i = m_ResponsePackets.AddToTail();
  802. m_ResponsePackets[i].responderID = listenerID; // record who we need to respond to
  803. CUtlBuffer &response = m_ResponsePackets[i].packet;
  804. // post the message
  805. response.PutInt( 0 );
  806. response.PutInt( type );
  807. response.PutInt( nDataLen );
  808. response.Put( pData, nDataLen );
  809. }
  810. //-----------------------------------------------------------------------------
  811. // Purpose: sends an opaque blob of data from VProf to a remote rcon listener
  812. //-----------------------------------------------------------------------------
  813. void CServerRemoteAccess::SendVProfData( ra_listener_id listenerID, bool bGroupData, void *data, int len )
  814. {
  815. Assert( listenerID != m_AdminUIID ); // only RCON clients support this right now
  816. SendResponseToClient( listenerID, bGroupData ? SERVERDATA_VPROF_GROUPS : SERVERDATA_VPROF_DATA, data, len );
  817. }
  818. //-----------------------------------------------------------------------------
  819. // Purpose: C function for rest of engine to access CServerRemoteAccess class
  820. //-----------------------------------------------------------------------------
  821. extern "C" void NotifyDedicatedServerUI(const char *message)
  822. {
  823. if ( g_ServerRemoteAccess.GetAdminUIID() != INVALID_LISTENER_ID ) // if we have an admin UI actually registered
  824. {
  825. g_ServerRemoteAccess.SendMessageToAdminUI( g_ServerRemoteAccess.GetAdminUIID(), message);
  826. }
  827. }