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.

826 lines
22 KiB

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