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

865 lines
23 KiB

  1. //===== Copyright 1996-2005, Valve Corporation, All rights reserved. ======//
  2. //
  3. // Purpose: implementation of the rcon client
  4. //
  5. //===========================================================================//
  6. #include "tier0/platform.h"
  7. #ifdef POSIX
  8. #include "net_ws_headers.h"
  9. #else
  10. #if !defined( _X360 )
  11. #include <winsock.h>
  12. #else
  13. #include "winsockx.h"
  14. #endif
  15. #undef SetPort // winsock screws with the SetPort string... *sigh*8
  16. #endif
  17. #include <tier0/dbg.h>
  18. #include "utlbuffer.h"
  19. #include "cl_rcon.h"
  20. #include "vprof_engine.h"
  21. #include "proto_oob.h" // PORT_RCON define
  22. #include "cmd.h"
  23. #include "tier2/fileutils.h"
  24. #include "zip/XUnzip.h"
  25. #if defined( _X360 )
  26. #include "xbox/xbox_win32stubs.h"
  27. #endif
  28. // memdbgon must be the last include file in a .cpp file!!!
  29. #include "tier0/memdbgon.h"
  30. static CRConClient g_RCONClient;
  31. CRConClient & RCONClient()
  32. {
  33. return g_RCONClient;
  34. }
  35. #ifdef ENABLE_RPT
  36. class CRPTClient : public CRConClient
  37. {
  38. typedef CRConClient BaseClass;
  39. public:
  40. virtual void OnSocketAccepted( SocketHandle_t hSocket, const netadr_t & netAdr, void** ppData )
  41. {
  42. BaseClass::OnSocketAccepted( hSocket, netAdr, ppData );
  43. // Immediately try to start vprofiling
  44. // Also, enable cheats on this client only
  45. Cmd_SetRptActive( true );
  46. StartVProfData();
  47. }
  48. virtual void OnSocketClosed( SocketHandle_t hSocket, const netadr_t & netAdr, void* pData )
  49. {
  50. StopVProfData();
  51. Cmd_SetRptActive( false );
  52. BaseClass::OnSocketClosed( hSocket, netAdr, pData );
  53. }
  54. };
  55. static CRPTClient g_RPTClient;
  56. CRConClient & RPTClient()
  57. {
  58. return g_RPTClient;
  59. }
  60. #endif // ENABLE_RPT
  61. static void RconAddressChanged_f( IConVar *pConVar, const char *pOldString, float flOldValue )
  62. {
  63. #ifndef DEDICATED
  64. ConVarRef var( pConVar );
  65. netadr_t to;
  66. const char *cmdargs = var.GetString();
  67. if ( !NET_StringToAdr( cmdargs, &to ) )
  68. {
  69. Msg( "Unable to resolve rcon address %s\n", var.GetString() );
  70. return;
  71. }
  72. RCONClient().SetAddress( to );
  73. #endif
  74. }
  75. static ConVar rcon_address( "rcon_address", "", FCVAR_SERVER_CANNOT_QUERY | FCVAR_DONTRECORD | FCVAR_RELEASE, "Address of remote server if sending unconnected rcon commands (format x.x.x.x:p) ", RconAddressChanged_f );
  76. //-----------------------------------------------------------------------------
  77. // Implementation of remote vprof
  78. //-----------------------------------------------------------------------------
  79. CRConVProfExport::CRConVProfExport()
  80. {
  81. }
  82. void CRConVProfExport::AddListener()
  83. {
  84. }
  85. void CRConVProfExport::RemoveListener()
  86. {
  87. }
  88. void CRConVProfExport::SetBudgetFlagsFilter( int filter )
  89. {
  90. }
  91. int CRConVProfExport::GetNumBudgetGroups()
  92. {
  93. return m_Info.Count();
  94. }
  95. void CRConVProfExport::GetBudgetGroupInfos( CExportedBudgetGroupInfo *pInfos )
  96. {
  97. memcpy( pInfos, m_Info.Base(), GetNumBudgetGroups() * sizeof(CExportedBudgetGroupInfo) );
  98. }
  99. void CRConVProfExport::GetBudgetGroupTimes( float times[IVProfExport::MAX_BUDGETGROUP_TIMES] )
  100. {
  101. int nGroups = MIN( m_Times.Count(), IVProfExport::MAX_BUDGETGROUP_TIMES );
  102. memset( times, 0, nGroups * sizeof(float) );
  103. nGroups = MIN( GetNumBudgetGroups(), nGroups );
  104. memcpy( times, m_Times.Base(), nGroups * sizeof(float) );
  105. }
  106. void CRConVProfExport::PauseProfile()
  107. {
  108. #ifdef VPROF_ENABLED
  109. // NOTE: This only has effect when testing on a listen server
  110. // it shouldn't do anything in the wild. When drawing the budget panel
  111. // this will cause the time spent doing so to not be counted
  112. VProfExport_Pause();
  113. #endif
  114. }
  115. void CRConVProfExport::ResumeProfile()
  116. {
  117. #ifdef VPROF_ENABLED
  118. // NOTE: This only has effect when testing on a listen server
  119. // it shouldn't do anything in the wild
  120. VProfExport_Resume();
  121. #endif
  122. }
  123. void CRConVProfExport::CleanupGroupData()
  124. {
  125. int nCount = m_Info.Count();
  126. for ( int i = 0; i < nCount; ++i )
  127. {
  128. delete m_Info[i].m_pName;
  129. }
  130. m_Info.RemoveAll();
  131. }
  132. void CRConVProfExport::OnRemoteGroupData( const void *data, int len )
  133. {
  134. CUtlBuffer buf( data, len, CUtlBuffer::READ_ONLY );
  135. int nFirstGroup = buf.GetInt();
  136. if ( nFirstGroup == 0 )
  137. {
  138. CleanupGroupData();
  139. }
  140. else
  141. {
  142. Assert( nFirstGroup == m_Info.Count() );
  143. }
  144. // NOTE: See WriteRemoteVProfGroupData in vprof_engine.cpp
  145. // to see the encoding of this data
  146. int nGroupCount = buf.GetInt();
  147. int nBase = m_Info.AddMultipleToTail( nGroupCount );
  148. char temp[1024];
  149. for ( int i = 0; i < nGroupCount; ++i )
  150. {
  151. CExportedBudgetGroupInfo *pInfo = &m_Info[nBase + i];
  152. unsigned char red, green, blue, alpha;
  153. red = buf.GetUnsignedChar( );
  154. green = buf.GetUnsignedChar( );
  155. blue = buf.GetUnsignedChar( );
  156. alpha = buf.GetUnsignedChar( );
  157. buf.GetString( temp, sizeof(temp) );
  158. int nLen = Q_strlen( temp );
  159. pInfo->m_Color.SetColor( red, green, blue, alpha );
  160. char *pBuf = new char[ nLen + 1 ];
  161. pInfo->m_pName = pBuf;
  162. memcpy( pBuf, temp, nLen+1 );
  163. pInfo->m_BudgetFlags = 0;
  164. }
  165. }
  166. void CRConVProfExport::OnRemoteData( const void *data, int len )
  167. {
  168. // NOTE: See WriteRemoteVProfData in vprof_engine.cpp
  169. // to see the encoding of this data
  170. int nCount = len / sizeof(float);
  171. Assert( nCount == m_Info.Count() );
  172. CUtlBuffer buf( data, len, CUtlBuffer::READ_ONLY );
  173. m_Times.SetCount( nCount );
  174. memcpy( m_Times.Base(), data, nCount * sizeof(float) );
  175. }
  176. CON_COMMAND( vprof_remote_start, "Request a VProf data stream from the remote server (requires authentication)" )
  177. {
  178. // TODO: Make this work (it might already!)
  179. // RCONClient().StartVProfData();
  180. }
  181. CON_COMMAND( vprof_remote_stop, "Stop an existing remote VProf data request" )
  182. {
  183. // TODO: Make this work (it might already!)
  184. // RCONClient().StopVProfData();
  185. }
  186. #ifdef ENABLE_RPT
  187. CON_COMMAND_F( rpt_screenshot, "", FCVAR_HIDDEN | FCVAR_DONTRECORD )
  188. {
  189. RPTClient().TakeScreenshot();
  190. }
  191. CON_COMMAND_F( rpt_download_log, "", FCVAR_HIDDEN | FCVAR_DONTRECORD )
  192. {
  193. RPTClient().GrabConsoleLog();
  194. }
  195. #endif // ENABLE_RPT
  196. //-----------------------------------------------------------------------------
  197. // Purpose: Constructor
  198. //-----------------------------------------------------------------------------
  199. #pragma warning ( disable : 4355 )
  200. CRConClient::CRConClient() : m_Socket( this )
  201. {
  202. m_bAuthenticated = false;
  203. m_iAuthRequestID = 1; // must start at 1
  204. m_iReqID = 0;
  205. m_nScreenShotIndex = 0;
  206. m_nConsoleLogIndex = 0;
  207. }
  208. #pragma warning ( default : 4355 )
  209. //-----------------------------------------------------------------------------
  210. // Purpose: Destructor
  211. //-----------------------------------------------------------------------------
  212. CRConClient::~CRConClient()
  213. {
  214. }
  215. //-----------------------------------------------------------------------------
  216. // Changes the password
  217. //-----------------------------------------------------------------------------
  218. void CRConClient::SetPassword( const char *pPassword )
  219. {
  220. m_Socket.CloseAllAcceptedSockets();
  221. m_Password = pPassword;
  222. }
  223. void CRConClient::SetRemoteFileDirectory( const char *pDir )
  224. {
  225. m_RemoteFileDir = pDir;
  226. m_nScreenShotIndex = 0;
  227. m_nConsoleLogIndex = 0;
  228. g_pFullFileSystem->CreateDirHierarchy( pDir, "MOD" );
  229. }
  230. //-----------------------------------------------------------------------------
  231. // Purpose: set the addresss of the remote server
  232. //-----------------------------------------------------------------------------
  233. void CRConClient::SetAddress( const netadr_t &netAdr )
  234. {
  235. m_Socket.CloseAllAcceptedSockets();
  236. m_Address = netAdr;
  237. if ( m_Address.GetPort() == 0 )
  238. {
  239. m_Address.SetPort( PORT_SERVER ); // override the port setting, by default rcon tries to bind to the same port as the server
  240. }
  241. }
  242. //-----------------------------------------------------------------------------
  243. // Inherited from ISocketCreatorListener
  244. //-----------------------------------------------------------------------------
  245. bool CRConClient::ShouldAcceptSocket( SocketHandle_t hSocket, const netadr_t & netAdr )
  246. {
  247. // Can't connect if we're already connected
  248. return !IsConnected();
  249. }
  250. void CRConClient::OnSocketAccepted( SocketHandle_t hSocket, const netadr_t & netAdr, void** ppData )
  251. {
  252. }
  253. void CRConClient::OnSocketClosed( SocketHandle_t hSocket, const netadr_t & netAdr, void* pData )
  254. {
  255. // reset state
  256. m_bAuthenticated = false;
  257. m_iReqID = 0;
  258. m_iAuthRequestID = 1; // must start at 1
  259. m_SendBuffer.Purge();
  260. m_RecvBuffer.Purge();
  261. }
  262. //-----------------------------------------------------------------------------
  263. // Connects to the address specified by SetAddress
  264. //-----------------------------------------------------------------------------
  265. bool CRConClient::ConnectSocket()
  266. {
  267. if ( m_Socket.ConnectSocket( m_Address, true ) < 0 )
  268. {
  269. Warning( "Unable to connect to remote server (%s)\n", m_Address.ToString() );
  270. return false;
  271. }
  272. return true;
  273. }
  274. void CRConClient::CloseSocket()
  275. {
  276. m_Socket.CloseAllAcceptedSockets();
  277. }
  278. //-----------------------------------------------------------------------------
  279. // Are we connected?
  280. //-----------------------------------------------------------------------------
  281. bool CRConClient::IsConnected() const
  282. {
  283. return m_Socket.GetAcceptedSocketCount() > 0;
  284. }
  285. //-----------------------------------------------------------------------------
  286. // Creates a listen server, connects to remote machines that connect to it
  287. //-----------------------------------------------------------------------------
  288. void CRConClient::CreateListenSocket( const netadr_t &netAdr )
  289. {
  290. m_Socket.CreateListenSocket( netAdr );
  291. }
  292. void CRConClient::CloseListenSocket()
  293. {
  294. m_Socket.CloseListenSocket( );
  295. }
  296. //-----------------------------------------------------------------------------
  297. // Purpose: send queued messages
  298. //-----------------------------------------------------------------------------
  299. void CRConClient::SendQueuedData()
  300. {
  301. SocketHandle_t hSocket = GetSocketHandle();
  302. while ( m_SendBuffer.TellMaxPut() - m_SendBuffer.TellGet() > sizeof(int) )
  303. {
  304. size_t nSize = *(int*)m_SendBuffer.PeekGet();
  305. Assert( nSize >= m_SendBuffer.TellMaxPut() - m_SendBuffer.TellGet() - sizeof( int ) );
  306. int ret = send( hSocket, (const char *)m_SendBuffer.PeekGet(), nSize + sizeof( int ), 0 );
  307. if ( ret != -1 )
  308. {
  309. m_SendBuffer.SeekGet( CUtlBuffer::SEEK_CURRENT, nSize + sizeof( int ) );
  310. continue;
  311. }
  312. if ( !SocketWouldBlock() )
  313. {
  314. Warning( "Lost RCON connection, please retry command.\n");
  315. CloseSocket();
  316. }
  317. break;
  318. }
  319. int nSizeRemaining = m_SendBuffer.TellMaxPut() - m_SendBuffer.TellGet();
  320. if ( nSizeRemaining <= sizeof(int) )
  321. {
  322. m_SendBuffer.Purge();
  323. return;
  324. }
  325. // In this case, we've still got queued messages to send
  326. // Keep the portion of the buffer we didn't process for next time
  327. CUtlBuffer tmpBuf;
  328. tmpBuf.Put( m_SendBuffer.PeekGet(), nSizeRemaining );
  329. m_SendBuffer.Purge();
  330. m_SendBuffer.Put( tmpBuf.Base(), tmpBuf.TellPut() );
  331. }
  332. //-----------------------------------------------------------------------------
  333. // Purpose: parse received data
  334. //-----------------------------------------------------------------------------
  335. void CRConClient::ParseReceivedData()
  336. {
  337. m_RecvBuffer.SeekGet( CUtlBuffer::SEEK_HEAD, 0 );
  338. int size = m_RecvBuffer.GetInt();
  339. while ( size && size <= m_RecvBuffer.TellPut() - m_RecvBuffer.TellGet() )
  340. {
  341. //DevMsg( "RCON: got packet %i long\n", size );
  342. int reqID = m_RecvBuffer.GetInt();
  343. int cmdID = m_RecvBuffer.GetInt(); // ignore the cmd id
  344. // DevMsg( "RCON Cmd: <-- %i %i %i\n", reqID, cmdID, readLen );
  345. switch( cmdID )
  346. {
  347. case SERVERDATA_AUTH_RESPONSE:
  348. {
  349. if ( reqID == -1 ) // bad password
  350. {
  351. Msg( "Bad RCON password\n" );
  352. m_bAuthenticated = false;
  353. }
  354. else
  355. {
  356. Assert( reqID == m_iAuthRequestID );
  357. m_bAuthenticated = true;
  358. }
  359. char dummy[2];
  360. m_RecvBuffer.GetString( dummy, sizeof(dummy) );
  361. m_RecvBuffer.GetString( dummy, sizeof(dummy) );
  362. }
  363. break;
  364. case SERVERDATA_SCREENSHOT_RESPONSE:
  365. {
  366. int nDataSize = m_RecvBuffer.GetInt();
  367. SaveRemoteScreenshot( m_RecvBuffer.PeekGet(), nDataSize );
  368. m_RecvBuffer.SeekGet( CUtlBuffer::SEEK_CURRENT, nDataSize );
  369. }
  370. break;
  371. case SERVERDATA_CONSOLE_LOG_RESPONSE:
  372. {
  373. int nDataSize = m_RecvBuffer.GetInt();
  374. SaveRemoteConsoleLog( m_RecvBuffer.PeekGet(), nDataSize );
  375. m_RecvBuffer.SeekGet( CUtlBuffer::SEEK_CURRENT, nDataSize );
  376. }
  377. break;
  378. case SERVERDATA_VPROF_DATA:
  379. {
  380. int nDataSize = m_RecvBuffer.GetInt();
  381. m_VProfExport.OnRemoteData( m_RecvBuffer.PeekGet(), nDataSize );
  382. m_RecvBuffer.SeekGet( CUtlBuffer::SEEK_CURRENT, nDataSize );
  383. }
  384. break;
  385. case SERVERDATA_VPROF_GROUPS:
  386. {
  387. int nDataSize = m_RecvBuffer.GetInt();
  388. m_VProfExport.OnRemoteGroupData( m_RecvBuffer.PeekGet(), nDataSize );
  389. m_RecvBuffer.SeekGet( CUtlBuffer::SEEK_CURRENT, nDataSize );
  390. }
  391. break;
  392. case SERVERDATA_RESPONSE_STRING:
  393. {
  394. char pBuf[2048];
  395. m_RecvBuffer.GetString( pBuf, sizeof(pBuf) );
  396. Msg( "%s", pBuf );
  397. }
  398. break;
  399. case SERVERDATA_RESPONSE_REMOTEBUG:
  400. {
  401. // Our connected server has submitted a partial bug report
  402. // to a directory. Start a bug report and populate the fields
  403. // with the data in the provided directory.
  404. char pBuf[MAX_PATH];
  405. m_RecvBuffer.GetString( pBuf, sizeof(pBuf) );
  406. CUtlString bugcmd;
  407. bugcmd.Format( "bug -remotebugpath %s",pBuf );
  408. Cbuf_AddText( CBUF_SERVER, bugcmd.Get() );
  409. Cbuf_Execute();
  410. }
  411. break;
  412. default:
  413. {
  414. // Displays a message from the server
  415. int strLen = m_RecvBuffer.TellPut() - m_RecvBuffer.TellGet();
  416. CUtlMemory<char> msg;
  417. msg.EnsureCapacity( strLen + 1 );
  418. m_RecvBuffer.GetString( msg.Base(), msg.Count() );
  419. msg[ msg.Count() - 1 ] = '\0';
  420. Msg( "%s", (const char *)msg.Base() );
  421. m_RecvBuffer.GetString( msg.Base(), msg.Count() ); // ignore the second string
  422. }
  423. break;
  424. }
  425. if ( m_RecvBuffer.TellPut() - m_RecvBuffer.TellGet() >= sizeof(int) )
  426. {
  427. size = m_RecvBuffer.GetInt(); // read how much is in this packet
  428. }
  429. else
  430. {
  431. size = 0; // finished the packet
  432. }
  433. }
  434. if ( size || (m_RecvBuffer.TellPut() - m_RecvBuffer.TellGet() > 0) )
  435. {
  436. // In this case, we've got a partial message; we didn't get it all.
  437. // Keep the portion of the buffer we didn't process for next time
  438. CUtlBuffer tmpBuf;
  439. if ( m_RecvBuffer.TellPut() - m_RecvBuffer.TellGet() > 0 )
  440. {
  441. tmpBuf.Put( m_RecvBuffer.PeekGet(), m_RecvBuffer.TellPut() - m_RecvBuffer.TellGet() );
  442. }
  443. m_RecvBuffer.Purge();
  444. if ( size > 0 )
  445. {
  446. m_RecvBuffer.PutInt( size );
  447. }
  448. if ( tmpBuf.TellPut() > 0 )
  449. {
  450. m_RecvBuffer.Put( tmpBuf.Base(), tmpBuf.TellPut() );
  451. }
  452. }
  453. else
  454. {
  455. m_RecvBuffer.Purge();
  456. }
  457. }
  458. //-----------------------------------------------------------------------------
  459. // Purpose: check for any server responses
  460. //-----------------------------------------------------------------------------
  461. void CRConClient::RunFrame()
  462. {
  463. m_Socket.RunFrame();
  464. if ( !IsConnected() )
  465. return;
  466. SendQueuedData();
  467. SocketHandle_t hSocket = GetSocketHandle();
  468. char ch;
  469. int pendingLen = recv( hSocket, &ch, sizeof(ch), MSG_PEEK );
  470. if ( pendingLen == -1 && SocketWouldBlock() )
  471. return;
  472. if ( pendingLen == 0 ) // socket got closed
  473. {
  474. CloseSocket();
  475. return;
  476. }
  477. if ( pendingLen < 0 )
  478. {
  479. CloseSocket();
  480. Warning( "Lost RCON connection, please retry command (%s)\n", NET_ErrorString( WSAGetLastError() ) );
  481. return;
  482. }
  483. // find out how much we have to read
  484. unsigned long readLen = 0;
  485. #ifdef _PS3
  486. ExecuteNTimes( 5, Warning( "CRConClient unsupported on PS3!\n" ) );
  487. readLen = 0;
  488. #else
  489. ioctlsocket( hSocket, FIONREAD, &readLen );
  490. #endif
  491. if ( readLen <= sizeof(int) )
  492. return;
  493. // we have a command to process
  494. // Read data into a utlbuffer
  495. m_RecvBuffer.EnsureCapacity( m_RecvBuffer.TellPut() + readLen + 1 );
  496. char *recvbuffer = (char *)stackalloc( MIN( 1024, readLen + 1 ) );
  497. unsigned int len = 0;
  498. while ( len < readLen )
  499. {
  500. int recvLen = recv( hSocket, recvbuffer , MIN( 1024, readLen - len ) , 0 );
  501. if ( recvLen == 0 ) // socket was closed
  502. {
  503. CloseSocket();
  504. break;
  505. }
  506. if ( recvLen < 0 && !SocketWouldBlock() )
  507. {
  508. Warning( "RCON Cmd: recv error (%s)\n", NET_ErrorString( WSAGetLastError() ) );
  509. break;
  510. }
  511. m_RecvBuffer.Put( recvbuffer, recvLen );
  512. len += recvLen;
  513. }
  514. ParseReceivedData();
  515. }
  516. //-----------------------------------------------------------------------------
  517. // Purpose: send a response to the server
  518. //-----------------------------------------------------------------------------
  519. void CRConClient::SendResponse( CUtlBuffer &response, bool bAutoAuthenticate )
  520. {
  521. if ( bAutoAuthenticate && !IsAuthenticated() )
  522. {
  523. Authenticate();
  524. if ( IsConnected() )
  525. {
  526. m_SendBuffer.Put( response.Base(), response.TellMaxPut() );
  527. }
  528. return;
  529. }
  530. int ret = send( GetSocketHandle(), (const char *)response.Base(), response.TellMaxPut(), 0 );
  531. if ( ret == -1 )
  532. {
  533. if ( SocketWouldBlock() )
  534. {
  535. m_SendBuffer.Put( response.Base(), response.TellMaxPut() );
  536. }
  537. else
  538. {
  539. Warning( "Lost RCON connection, please retry command\n" );
  540. CloseSocket();
  541. }
  542. }
  543. }
  544. //-----------------------------------------------------------------------------
  545. // Purpose: builds a simple command to send to the server
  546. //-----------------------------------------------------------------------------
  547. void CRConClient::BuildResponse( CUtlBuffer &response, ServerDataRequestType_t msg, const char *pString1, const char *pString2 )
  548. {
  549. // build the response
  550. response.PutInt(0); // the size, filled in below
  551. response.PutInt(m_iReqID++);
  552. response.PutInt(msg);
  553. response.PutString(pString1);
  554. response.PutString(pString2);
  555. int nSize = response.TellPut() - sizeof(int);
  556. response.SeekPut( CUtlBuffer::SEEK_HEAD, 0 );
  557. response.PutInt( nSize ); // the size
  558. response.SeekPut( CUtlBuffer::SEEK_CURRENT, nSize );
  559. }
  560. //-----------------------------------------------------------------------------
  561. // Purpose: authenticate ourselves
  562. //-----------------------------------------------------------------------------
  563. void CRConClient::Authenticate()
  564. {
  565. CUtlBuffer response;
  566. // build the response
  567. response.PutInt(0); // the size, filled in below
  568. response.PutInt(++m_iAuthRequestID);
  569. response.PutInt(SERVERDATA_AUTH);
  570. response.PutString( m_Password.Get() );
  571. // Use the otherwise-empty second string for the userid. The server will use this to
  572. // exec "mp_disable_autokick <userid>" upon successful authentication.
  573. bool addedUserID = false;
  574. if ( GetBaseLocalClient().IsConnected() )
  575. {
  576. if ( GetBaseLocalClient().m_nPlayerSlot < GetBaseLocalClient().m_nMaxClients && GetBaseLocalClient().m_nPlayerSlot >= 0 )
  577. {
  578. Assert( GetBaseLocalClient().m_pUserInfoTable );
  579. if ( GetBaseLocalClient().m_pUserInfoTable )
  580. {
  581. player_info_t *pi = (player_info_t*) GetBaseLocalClient().m_pUserInfoTable->GetStringUserData( GetBaseLocalClient().m_nPlayerSlot, NULL );
  582. if ( pi )
  583. {
  584. addedUserID = true;
  585. // Fixup from network order (little endian)
  586. response.PutString( va( "%d", LittleLong( pi->userID ) ) );
  587. }
  588. }
  589. }
  590. }
  591. if ( !addedUserID )
  592. {
  593. response.PutString( "" );
  594. }
  595. int size = response.TellPut() - sizeof(int);
  596. response.SeekPut( CUtlBuffer::SEEK_HEAD, 0 );
  597. response.PutInt(size); // the size
  598. response.SeekPut( CUtlBuffer::SEEK_CURRENT, size );
  599. SendResponse( response, false );
  600. }
  601. //-----------------------------------------------------------------------------
  602. // Purpose: send an rcon command to a connected server
  603. //-----------------------------------------------------------------------------
  604. void CRConClient::SendCmd( const char *msg )
  605. {
  606. if ( !IsConnected() )
  607. {
  608. if ( !ConnectSocket() )
  609. return;
  610. }
  611. CUtlBuffer response;
  612. BuildResponse( response, SERVERDATA_EXECCOMMAND, msg, "" );
  613. SendResponse( response );
  614. }
  615. //-----------------------------------------------------------------------------
  616. // Purpose: Start vprofiling
  617. //-----------------------------------------------------------------------------
  618. void CRConClient::StartVProfData()
  619. {
  620. if ( !IsConnected() )
  621. {
  622. if ( !ConnectSocket() )
  623. return;
  624. }
  625. #ifdef VPROF_ENABLED
  626. // Override the vprof export to point to our local profiling data
  627. OverrideVProfExport( &m_VProfExport );
  628. #endif
  629. CUtlBuffer response;
  630. BuildResponse( response, SERVERDATA_VPROF, "", "" );
  631. SendResponse( response );
  632. }
  633. //-----------------------------------------------------------------------------
  634. // Purpose: Stop vprofiling
  635. //-----------------------------------------------------------------------------
  636. void CRConClient::StopVProfData()
  637. {
  638. #ifdef VPROF_ENABLED
  639. // Reset the vprof export to point to the normal profiling data
  640. ResetVProfExport( &m_VProfExport );
  641. #endif
  642. // Don't bother restarting a connection to turn this off
  643. if ( !IsConnected() )
  644. return;
  645. CUtlBuffer response;
  646. BuildResponse( response, SERVERDATA_REMOVE_VPROF, "", "" );
  647. SendResponse( response );
  648. }
  649. //-----------------------------------------------------------------------------
  650. // Purpose: get data from the server
  651. //-----------------------------------------------------------------------------
  652. void CRConClient::TakeScreenshot()
  653. {
  654. if ( !IsConnected() )
  655. {
  656. if ( !ConnectSocket() )
  657. return;
  658. }
  659. CUtlBuffer response;
  660. BuildResponse( response, SERVERDATA_TAKE_SCREENSHOT, "", "" );
  661. SendResponse( response );
  662. }
  663. void CRConClient::GrabConsoleLog()
  664. {
  665. if ( !IsConnected() )
  666. {
  667. if ( !ConnectSocket() )
  668. return;
  669. }
  670. CUtlBuffer response;
  671. BuildResponse( response, SERVERDATA_SEND_CONSOLE_LOG, "", "" );
  672. SendResponse( response );
  673. }
  674. void CRConClient::SendBugRequest()
  675. {
  676. if ( !IsConnected() )
  677. {
  678. if ( !ConnectSocket() )
  679. {
  680. Warning( "Could not connect to remote machine, remote bug command failed\n" );
  681. return;
  682. }
  683. }
  684. CUtlBuffer response;
  685. BuildResponse( response, SERVERDATA_SEND_REMOTEBUG, "", "" );
  686. SendResponse( response );
  687. }
  688. #if 0
  689. CON_COMMAND( remote_bug, "Starts a bug report with data from the currently connected rcon machine" )
  690. {
  691. RCONClient().SendBugRequest();
  692. }
  693. #endif
  694. //-----------------------------------------------------------------------------
  695. // We've got data from the server, save it
  696. //-----------------------------------------------------------------------------
  697. void CRConClient::SaveRemoteScreenshot( const void* pBuffer, int nBufLen )
  698. {
  699. char pScreenshotPath[MAX_PATH];
  700. do
  701. {
  702. Q_snprintf( pScreenshotPath, sizeof( pScreenshotPath ), "%s/screenshot%04d.jpg", m_RemoteFileDir.Get(), m_nScreenShotIndex++ );
  703. } while ( g_pFullFileSystem->FileExists( pScreenshotPath, "MOD" ) );
  704. char pFullPath[MAX_PATH];
  705. GetModSubdirectory( pScreenshotPath, pFullPath, sizeof(pFullPath) );
  706. HZIP hZip = OpenZip( (void*)pBuffer, nBufLen, ZIP_MEMORY );
  707. int nIndex;
  708. ZIPENTRY zipInfo;
  709. FindZipItem( hZip, "screenshot.jpg", true, &nIndex, &zipInfo );
  710. if ( nIndex >= 0 )
  711. {
  712. UnzipItem( hZip, nIndex, pFullPath, 0, ZIP_FILENAME );
  713. }
  714. CloseZip( hZip );
  715. }
  716. void CRConClient::SaveRemoteConsoleLog( const void* pBuffer, int nBufLen )
  717. {
  718. if ( nBufLen == 0 )
  719. return;
  720. char pLogPath[MAX_PATH];
  721. do
  722. {
  723. Q_snprintf( pLogPath, sizeof( pLogPath ), "%s/console%04d.log", m_RemoteFileDir.Get(), m_nConsoleLogIndex++ );
  724. } while ( g_pFullFileSystem->FileExists( pLogPath, "MOD" ) );
  725. char pFullPath[MAX_PATH];
  726. GetModSubdirectory( pLogPath, pFullPath, sizeof(pFullPath) );
  727. HZIP hZip = OpenZip( (void*)pBuffer, nBufLen, ZIP_MEMORY );
  728. int nIndex;
  729. ZIPENTRY zipInfo;
  730. FindZipItem( hZip, "console.log", true, &nIndex, &zipInfo );
  731. if ( nIndex >= 0 )
  732. {
  733. UnzipItem( hZip, nIndex, pFullPath, 0, ZIP_FILENAME );
  734. }
  735. CloseZip( hZip );
  736. }