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.

1191 lines
37 KiB

  1. //========= Copyright � 1996-2008, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: "Steam" based pseudo socket support (PC only)
  4. //
  5. // $NoKeywords: $
  6. //
  7. //=============================================================================//
  8. #include "net_ws_headers.h"
  9. #include "tier0/vprof.h"
  10. #include "sv_ipratelimit.h"
  11. #if IsPlatformWindows()
  12. #else
  13. #include <poll.h>
  14. #endif
  15. #if !defined(_X360) && !defined(NO_STEAM) && !defined(DEDICATED)
  16. #define USE_STEAM_SOCKETS
  17. #endif
  18. ConVar net_threaded_socket_recovery_time( "net_threaded_socket_recovery_time", "60", FCVAR_RELEASE, "Number of seconds over which the threaded socket pump algorithm will fully recover client ratelimit." );
  19. ConVar net_threaded_socket_recovery_rate( "net_threaded_socket_recovery_rate", "6400", FCVAR_RELEASE, "Number of packets per second that threaded socket pump algorithm allows from client." );
  20. ConVar net_threaded_socket_burst_cap( "net_threaded_socket_burst_cap",
  21. #ifdef DEDICATED
  22. "256",
  23. #else
  24. "1024",
  25. #endif
  26. FCVAR_RELEASE, "Max number of packets per burst beyond which threaded socket pump algorithm will start dropping packets." );
  27. struct net_threaded_buffer_t
  28. {
  29. int len;
  30. byte buf[ NET_MAX_MESSAGE ];
  31. inline byte *Base() { return buf + len; }
  32. inline int Capacity() { return sizeof( buf ) - len; }
  33. inline byte *MoveAppend( net_threaded_buffer_t *pOther )
  34. {
  35. if ( pOther->len > Capacity() )
  36. return NULL;
  37. byte *pBase = Base();
  38. Q_memcpy( pBase, pOther->buf, pOther->len );
  39. len += pOther->len;
  40. pOther->len = 0;
  41. return pBase;
  42. }
  43. };
  44. CTSPool<net_threaded_buffer_t> g_NetThreadedBuffers;
  45. netadr_t g_NetAdrRatelimited;
  46. int32 g_numRatelimitedPackets = -100;
  47. class CThreadedSocketQueue
  48. {
  49. public:
  50. CThreadedSocketQueue() : m_mapSocketThreads( DefLessFunc( int ) ) {}
  51. ~CThreadedSocketQueue()
  52. {
  53. m_mapSocketThreads.PurgeAndDeleteElements();
  54. }
  55. static bool ShouldUseSocketsThreaded()
  56. {
  57. static bool s_bThreaded = !CommandLine()->FindParm( "-nothreadedsockets" );
  58. return s_bThreaded;
  59. }
  60. int recvfrom( int s, char * buf, int len, struct sockaddr * from )
  61. {
  62. CSocketThread *pThread = GetSocketThread( s );
  63. return pThread ? pThread->recvfrom( buf, len, from ) : 0;
  64. }
  65. void EnableThreadedRecv( int s, int nsSock, bool bEnable )
  66. {
  67. CSocketThread *pThread = GetSocketThread( s, nsSock, bEnable );
  68. ( void ) ( pThread );
  69. }
  70. void CloseSocket( int s )
  71. {
  72. CUtlMap< int, CSocketThread * >::IndexType_t idx = m_mapSocketThreads.Find( s );
  73. if ( idx != m_mapSocketThreads.InvalidIndex() )
  74. {
  75. CSocketThread *pThread = m_mapSocketThreads.Element( idx );
  76. delete pThread;
  77. m_mapSocketThreads.RemoveAt( idx );
  78. }
  79. }
  80. private:
  81. class CSocketThread
  82. {
  83. public:
  84. explicit CSocketThread( int s, int nsSock ) : m_s( s ), m_nsSock( nsSock ), m_hThread( NULL ), m_pDataQueueBufferCollect( NULL )
  85. {
  86. #if IsPlatformWindows()
  87. m_wsaEvents[0] = ::WSACreateEvent();
  88. if ( !m_wsaEvents[0] )
  89. Error( "WSACreateEvent failed\n" );
  90. m_wsaEvents[1] = ::WSACreateEvent();
  91. if ( !m_wsaEvents[1] )
  92. Error( "WSACreateEvent failed\n" );
  93. int ret = ::WSAEventSelect( m_s, m_wsaEvents[ 1 ], FD_READ );
  94. if ( ret )
  95. Error( "WSAEventSelect failed\n" );
  96. #else
  97. Q_memset( m_sockSignalPipe, -1, sizeof( m_sockSignalPipe ) );
  98. int ret = socketpair( PF_LOCAL, SOCK_STREAM, 0, m_sockSignalPipe ); // 0=main; 1=pump
  99. if ( ret )
  100. Error( "socketpair failed!\n" );
  101. #endif
  102. // Create the thread and start socket processing
  103. m_hThread = CreateSimpleThread( CallbackThreadProc, this );
  104. if ( !m_hThread )
  105. Error( "socket thread failed!\n" );
  106. }
  107. ~CSocketThread()
  108. {
  109. // Kill the thread!
  110. #if IsPlatformWindows()
  111. ::WSASetEvent( m_wsaEvents[0] );
  112. #else
  113. write( m_sockSignalPipe[0], "", 1 ); // write one zero byte to wake the thread
  114. #endif
  115. // wait for it to die
  116. ThreadJoin( m_hThread );
  117. ReleaseThreadHandle( m_hThread );
  118. m_hThread = NULL;
  119. // Shutdown resources
  120. #if IsPlatformWindows()
  121. ::WSACloseEvent( m_wsaEvents[0] );
  122. ::WSACloseEvent( m_wsaEvents[1] );
  123. #else
  124. close( m_sockSignalPipe[0] );
  125. close( m_sockSignalPipe[1] );
  126. #endif
  127. // Purge all packet data blocks
  128. m_tslstDataQueue.Purge();
  129. // Return all the buffers back into the pool
  130. do
  131. {
  132. if ( m_pDataQueueBufferCollect )
  133. {
  134. g_NetThreadedBuffers.PutObject( m_pDataQueueBufferCollect );
  135. m_pDataQueueBufferCollect = NULL;
  136. }
  137. } while ( m_tslstBuffers.PopItem( &m_pDataQueueBufferCollect ) );
  138. }
  139. int recvfrom( char * buf, int len, struct sockaddr * from )
  140. {
  141. //
  142. // Called on application main thread
  143. //
  144. ReceivedData_t data;
  145. if ( !m_tslstDataQueue.PopItem( &data ) )
  146. return 0;
  147. Q_memcpy( buf, data.buf, MIN( len, data.len ) );
  148. Q_memcpy( from, &data.from, sizeof( data.from ) );
  149. //
  150. // We are about to return this data, free up allocated buffers up to this
  151. //
  152. if ( !m_pDataQueueBufferCollect )
  153. {
  154. m_tslstBuffers.PopItem( &m_pDataQueueBufferCollect );
  155. }
  156. if ( ( data.buf >= m_pDataQueueBufferCollect->buf ) && ( data.buf < m_pDataQueueBufferCollect->buf + sizeof( m_pDataQueueBufferCollect->buf ) ) )
  157. ;// The returned data was still referencing the head of scratch buffers
  158. else
  159. { // The returned data is ahead in the list, previous buffer can go back in the pool
  160. g_NetThreadedBuffers.PutObject( m_pDataQueueBufferCollect );
  161. m_pDataQueueBufferCollect = NULL;
  162. }
  163. return data.len;
  164. }
  165. private:
  166. struct CPerNetChanRatelimit_t
  167. {
  168. double m_dblNetTimeMark;
  169. int32 m_numPackets;
  170. int32 m_numRatelimited;
  171. };
  172. static uintp CallbackThreadProc( void *pvParam ) { reinterpret_cast<CSocketThread*>(pvParam)->ThreadProc(); return 0; }
  173. void ThreadProc()
  174. {
  175. // Where are we getting new data?
  176. net_threaded_buffer_t *pThreadBufferSyscall = NULL;
  177. net_threaded_buffer_t *pThreadBufferCollect = NULL;
  178. struct sockaddr from;
  179. int fromlen = sizeof( from );
  180. netadr_t adrt;
  181. adrt.SetType( NA_IP );
  182. adrt.Clear();
  183. extern volatile int g_NetChannelsRefreshCounter;
  184. int pumpNetChannelsRefreshCounter = -1;
  185. CUtlVector< struct sockaddr > arrNetChans;
  186. typedef CUtlMap< uint64, CPerNetChanRatelimit_t, int, CDefLess< uint64 > > MapPerClientRatelimit_t;
  187. MapPerClientRatelimit_t mapPerClientRatelimit;
  188. #if IsPlatformWindows()
  189. const DWORD cWsaEvents = Q_ARRAYSIZE( m_wsaEvents );
  190. #else
  191. const int numSyscallPollFDs = 2;
  192. struct pollfd syscallPollFDs[ numSyscallPollFDs ];
  193. Q_memset( syscallPollFDs, 0, sizeof( syscallPollFDs ) );
  194. syscallPollFDs[ 0 ].fd = m_s;
  195. syscallPollFDs[ 0 ].events = POLLIN;
  196. syscallPollFDs[ 1 ].fd = m_sockSignalPipe[1];
  197. syscallPollFDs[ 1 ].events = POLLIN;
  198. #endif
  199. volatile double &vdblNetTimeForThread = net_time;
  200. for ( ;; )
  201. {
  202. if ( !pThreadBufferSyscall )
  203. {
  204. // Need a new buffer
  205. net_threaded_buffer_t *pbuf = g_NetThreadedBuffers.GetObject();
  206. pbuf->len = 0;
  207. m_tslstBuffers.PushItem( pbuf );
  208. pThreadBufferSyscall = pbuf;
  209. }
  210. // Recv socket data
  211. int ret = ::recvfrom( m_s, ( char* ) pThreadBufferSyscall->buf, sizeof( pThreadBufferSyscall->buf ), 0, &from, (socklen_t*)&fromlen );
  212. if ( ret <= 0 )
  213. {
  214. // Efficiently sleep while we wait for next packet
  215. #if IsPlatformWindows()
  216. DWORD dwWsaWaitResult = ::WSAWaitForMultipleEvents( cWsaEvents, m_wsaEvents, FALSE, WSA_INFINITE, FALSE );
  217. if ( dwWsaWaitResult == WSA_WAIT_EVENT_0 )
  218. return; // Finish the socket pump thread due to event signal from destructor
  219. WSANETWORKEVENTS wsaNetworkEvents;
  220. ::WSAEnumNetworkEvents( m_s, m_wsaEvents[1], &wsaNetworkEvents ); // Reset socket read event
  221. #else
  222. ret = ::poll( syscallPollFDs, numSyscallPollFDs, -1 ); // Sleep indefinitely until data available
  223. if ( syscallPollFDs[ 1 ].revents )
  224. return; // Finish the socket reading thread if socketpair pump end of the pipe has incoming signal
  225. #endif
  226. continue;
  227. }
  228. if ( ret > 0 )
  229. {
  230. // Is this a connectionless packet?
  231. if ( 0xFFFFFFFF == * ( uint32 * ) pThreadBufferSyscall->buf )
  232. {
  233. adrt.SetFromSockadr( &from );
  234. if ( !CheckConnectionLessRateLimits( adrt ) )
  235. ret = 0;
  236. }
  237. else
  238. {
  239. // Check if we need to refresh our netchans for this socket type
  240. if ( pumpNetChannelsRefreshCounter != g_NetChannelsRefreshCounter )
  241. {
  242. pumpNetChannelsRefreshCounter = g_NetChannelsRefreshCounter;
  243. arrNetChans.RemoveAll();
  244. extern void NET_FindAllNetChannelAddresses( int socket, CUtlVector< struct sockaddr > &arrNetChans );
  245. NET_FindAllNetChannelAddresses( m_nsSock, arrNetChans );
  246. // Since we just recomputed net channels, use this opportunity to expire obsolete ratelimits for old clients
  247. FOR_EACH_MAP_FAST( mapPerClientRatelimit, idxPerClientRateLimit )
  248. {
  249. CPerNetChanRatelimit_t &pncrt = mapPerClientRatelimit.Element( idxPerClientRateLimit );
  250. double dblTimeSinceTimeMark = ( vdblNetTimeForThread - pncrt.m_dblNetTimeMark );
  251. if ( dblTimeSinceTimeMark > net_threaded_socket_recovery_time.GetFloat() )
  252. mapPerClientRatelimit.RemoveAt( idxPerClientRateLimit );
  253. }
  254. }
  255. // This is a connection-oriented packet, must have a netchan
  256. bool bNetChanAvailable = false;
  257. for ( int k = 0; k < arrNetChans.Count(); ++ k )
  258. {
  259. struct sockaddr &sockAddressNetChan = arrNetChans[k];
  260. if (
  261. !Q_memcmp( &((struct sockaddr_in*)&sockAddressNetChan)->sin_addr.s_addr, &((struct sockaddr_in*)&from)->sin_addr.s_addr, sizeof( ((struct sockaddr_in*)&from)->sin_addr.s_addr ) )
  262. && ( ((struct sockaddr_in*)&sockAddressNetChan)->sin_port == ((struct sockaddr_in*)&from)->sin_port )
  263. )
  264. {
  265. bNetChanAvailable = true;
  266. //
  267. // Track ratelimit on this netchan
  268. //
  269. uint64 uiRatelimitKey = ( uint64( ((struct sockaddr_in*)&from)->sin_addr.s_addr ) << 32 ) | uint32( ((struct sockaddr_in*)&from)->sin_port );
  270. MapPerClientRatelimit_t::IndexType_t idxPerClientRateLimit = mapPerClientRatelimit.Find( uiRatelimitKey );
  271. if ( idxPerClientRateLimit == mapPerClientRatelimit.InvalidIndex() )
  272. {
  273. CPerNetChanRatelimit_t pncrt;
  274. Q_memset( &pncrt, 0, sizeof( pncrt ) );
  275. pncrt.m_dblNetTimeMark = vdblNetTimeForThread;
  276. pncrt.m_numPackets = 1;
  277. mapPerClientRatelimit.Insert( uiRatelimitKey, pncrt );
  278. }
  279. else
  280. {
  281. CPerNetChanRatelimit_t &pncrt = mapPerClientRatelimit.Element( idxPerClientRateLimit );
  282. double dblTimeSinceTimeMark = ( vdblNetTimeForThread - pncrt.m_dblNetTimeMark );
  283. if ( ( dblTimeSinceTimeMark > net_threaded_socket_recovery_time.GetFloat() ) ||
  284. ( dblTimeSinceTimeMark < 0 ) )
  285. {
  286. pncrt.m_numPackets = 0;
  287. }
  288. else if ( dblTimeSinceTimeMark > 0 )
  289. {
  290. int32 numPacketsToRecover = dblTimeSinceTimeMark * net_threaded_socket_recovery_rate.GetFloat();
  291. pncrt.m_numPackets = MAX( pncrt.m_numPackets - numPacketsToRecover, 0 );
  292. }
  293. if ( pncrt.m_numPackets > net_threaded_socket_burst_cap.GetInt() )
  294. {
  295. bNetChanAvailable = false;
  296. ++ pncrt.m_numRatelimited;
  297. if ( pncrt.m_numRatelimited > g_numRatelimitedPackets + 5 )
  298. {
  299. g_NetAdrRatelimited.SetFromSockadr( &from ); // remember last ratelimited address for logging purposes
  300. g_numRatelimitedPackets = pncrt.m_numRatelimited;
  301. }
  302. }
  303. else
  304. {
  305. pncrt.m_dblNetTimeMark = vdblNetTimeForThread;
  306. ++ pncrt.m_numPackets;
  307. pncrt.m_numRatelimited = 0;
  308. }
  309. }
  310. break;
  311. }
  312. }
  313. if ( !bNetChanAvailable )
  314. ret = 0;
  315. }
  316. }
  317. if ( ret > 0 )
  318. {
  319. ReceivedData_t recvData;
  320. recvData.buf = NULL;
  321. recvData.len = ret;
  322. Q_memcpy( &recvData.from, &from, sizeof( recvData.from ) );
  323. // Check if we still have more room in pending recv buffer
  324. pThreadBufferSyscall->len = ret;
  325. if ( byte *pbMoveAppend = pThreadBufferCollect ? pThreadBufferCollect->MoveAppend( pThreadBufferSyscall ) : NULL )
  326. {
  327. recvData.buf = pbMoveAppend;
  328. }
  329. else
  330. {
  331. pThreadBufferCollect = pThreadBufferSyscall;
  332. pThreadBufferSyscall = NULL;
  333. recvData.buf = pThreadBufferCollect->buf;
  334. }
  335. m_tslstDataQueue.PushItem( recvData );
  336. }
  337. }
  338. }
  339. struct ReceivedData_t
  340. {
  341. byte *buf;
  342. int len;
  343. struct sockaddr from;
  344. };
  345. //
  346. // Thread object data members
  347. //
  348. private:
  349. int const m_s; // [const] accessed by pump thread in ::recvfrom
  350. int const m_nsSock; // [const] accessed by pump thread doing netchans lookup
  351. ThreadHandle_t m_hThread; // Thread handle, accessed by main thread
  352. CTSQueue< ReceivedData_t > m_tslstDataQueue; // FIFO - actual data packets pumped from socket, thread-safe access on both threads
  353. CTSQueue< net_threaded_buffer_t * > m_tslstBuffers; // FIFO - buffers storing data pumped from socket, multiple packets can be stored in one memory chunk, thread-safe access on both threads
  354. net_threaded_buffer_t *m_pDataQueueBufferCollect; // Main thread tracking when collect buffer can be returned to global pool
  355. #if IsPlatformWindows()
  356. WSAEVENT m_wsaEvents[2];
  357. #else
  358. int m_sockSignalPipe[2];
  359. #endif
  360. };
  361. CSocketThread * GetSocketThread( int s, int nsSock = -1, bool bRequired = false )
  362. {
  363. CUtlMap< int, CSocketThread * >::IndexType_t idx = m_mapSocketThreads.Find( s );
  364. if ( idx != m_mapSocketThreads.InvalidIndex() )
  365. return m_mapSocketThreads.Element( idx );
  366. if ( bRequired )
  367. {
  368. CSocketThread *pNew = new CSocketThread( s, nsSock );
  369. m_mapSocketThreads.Insert( s, pNew );
  370. return pNew;
  371. }
  372. return NULL;
  373. }
  374. CUtlMap< int, CSocketThread * > m_mapSocketThreads;
  375. };
  376. CThreadedSocketQueue g_ThreadedSocketQueue;
  377. void On_NET_ProcessSocket_Start( int hUDP, int sock )
  378. {
  379. if ( g_numRatelimitedPackets > 0 )
  380. { // Spew about ratelimit on the main thread
  381. ConMsg( "Net channel ratelimit exceeded for %s: %d packets rejected.\n", g_NetAdrRatelimited.ToString(), g_numRatelimitedPackets );
  382. g_NetAdrRatelimited.Clear();
  383. g_numRatelimitedPackets = -100;
  384. }
  385. }
  386. void On_NET_ProcessSocket_End( int hUDP, int sock )
  387. {
  388. if ( CThreadedSocketQueue::ShouldUseSocketsThreaded() )
  389. g_ThreadedSocketQueue.EnableThreadedRecv( hUDP, sock, true );
  390. }
  391. #if defined( USE_STEAM_SOCKETS )
  392. #include "cl_steamauth.h"
  393. #include "tier1/tokenset.h"
  394. #include "utlmap.h"
  395. // matchmaking
  396. #include "matchmaking/imatchframework.h"
  397. #include "matchmaking/iplayer.h"
  398. #include "matchmaking/imatchtitle.h"
  399. #include "matchmaking/mm_helpers.h"
  400. // for INetSupport defines
  401. #include "engine/inetsupport.h"
  402. #include "server.h"
  403. ConVar net_steamcnx_debug( "net_steamcnx_debug", "0", 0, "Show debug spew for steam based connections, 2 shows all network traffic for steam sockets." );
  404. static ConVar net_steamcnx_enabled( "net_steamcnx_enabled", "1", FCVAR_RELEASE, "Use steam connections on listen server as a fallback, 2 forces use of steam connections instead of raw UDP." );
  405. static ConVar net_steamcnx_allowrelay( "net_steamcnx_allowrelay", "1", FCVAR_RELEASE | FCVAR_ARCHIVE, "Allow steam connections to attempt to use relay servers as fallback (best if specified on command line: +net_steamcnx_allowrelay 1)" );
  406. #define STEAM_CNX_COLOR Color( 255, 255, 100, 255 )
  407. extern ConVar cl_timeout;
  408. static const tokenset_t< ESocketIndex_t > s_SocketIndexMap[] =
  409. {
  410. { "NS_CLIENT", NS_CLIENT },
  411. { "NS_SERVER", NS_SERVER },
  412. #ifdef _X360
  413. { "NS_X360_SYSTEMLINK", NS_X360_SYSTEMLINK },
  414. { "NS_X360_LOBBY", NS_X360_LOBBY },
  415. { "NS_X360_TEAMLINK", NS_X360_TEAMLINK },
  416. #endif
  417. { "NS_HLTV", NS_HLTV },
  418. { "NS_HLTV1", NS_HLTV1 },
  419. { NULL, ( ESocketIndex_t )-1 }
  420. };
  421. static const tokenset_t< EP2PSessionError > s_EP2PSessionErrorIndexMap[] =
  422. {
  423. { "None", k_EP2PSessionErrorNone },
  424. { "Not running app", k_EP2PSessionErrorNotRunningApp }, // target is not running the same game
  425. { "No rights to app", k_EP2PSessionErrorNoRightsToApp }, // local user doesn't own the app that is running
  426. { "User not logged in", k_EP2PSessionErrorDestinationNotLoggedIn }, // target user isn't connected to Steam
  427. { "Timeout", k_EP2PSessionErrorTimeout }
  428. };
  429. // Why are there two Steam P2P channels instead of one client/server channel?
  430. //
  431. // We use a client receive channel and a server receive channel to simulate sockets. When a user is running a listen server, ::recvfrom will be called
  432. // simultaneously by both the server & client objects. If we were only using one channel, we would need to parse each packet received on that channel,
  433. // determine if really intended for the callers socket, and potentially store if for another socket.
  434. // code in this file only handles two types of sockets
  435. static inline bool IsSteamSocketType( ESocketIndex_t eSocketType )
  436. {
  437. return (eSocketType == NS_CLIENT || eSocketType == NS_SERVER);
  438. }
  439. // assumes you have already called IsSteamSocketType
  440. static inline int GetChannelForSocketType( ESocketIndex_t eSocketType )
  441. {
  442. return (eSocketType == NS_CLIENT) ? INetSupport::SP2PC_RECV_CLIENT : INetSupport::SP2PC_RECV_SERVER;
  443. }
  444. // each virtual socket we have open to another user
  445. class CSteamSocket
  446. {
  447. public:
  448. explicit CSteamSocket( const CSteamID &steamIdRemote, ESocketIndex_t eSocketType, const netadr_t &addr );
  449. const CSteamID &GetSteamID() const { return m_steamID; }
  450. ESocketIndex_t GetSocketType() const { return m_eSocketType; }
  451. const netadr_t &GetNetAddress() const { return m_addr; }
  452. inline ESocketIndex_t GetRemoteSocketType() const
  453. {
  454. return ( m_eSocketType == NS_CLIENT ) ? NS_SERVER : NS_CLIENT;
  455. }
  456. inline int GetRemoteChannel() const
  457. {
  458. return GetChannelForSocketType( GetRemoteSocketType() );
  459. }
  460. inline int GetLocalChannel() const
  461. {
  462. return GetChannelForSocketType( m_eSocketType );
  463. }
  464. private:
  465. CSteamID m_steamID; // SteamID of other user
  466. ESocketIndex_t m_eSocketType; // The socket type this connection was created on
  467. netadr_t m_addr; // The fake net address we have returned for this user
  468. };
  469. CSteamSocket::CSteamSocket( const CSteamID &steamIdRemote, ESocketIndex_t eSocketType, const netadr_t &addr ) :
  470. m_steamID( steamIdRemote ),
  471. m_eSocketType( eSocketType ),
  472. m_addr( addr )
  473. {
  474. }
  475. class CSteamSocketMgr : public ISteamSocketMgr
  476. {
  477. public:
  478. CSteamSocketMgr();
  479. ~CSteamSocketMgr();
  480. virtual void Init() OVERRIDE;
  481. virtual void Shutdown() OVERRIDE;
  482. virtual ISteamSocketMgr::ESteamCnxType GetCnxType();
  483. virtual void OpenSocket( int s, int nModule, int nSetPort, int nDefaultPort, const char *pName, int nProtocol, bool bTryAny ) OVERRIDE;
  484. virtual void CloseSocket( int s, int nModule ) OVERRIDE;
  485. virtual int sendto( int s, const char * buf, int len, int flags, const ns_address &to ) OVERRIDE;
  486. virtual int recvfrom( int s, char * buf, int len, int flags, ns_address *from ) OVERRIDE;
  487. virtual uint64 GetSteamIDForRemote( const ns_address &remote ) OVERRIDE;
  488. // client connection state
  489. STEAM_CALLBACK( CSteamSocketMgr, OnP2PSessionRequest, P2PSessionRequest_t, m_callbackP2PSessionRequest );
  490. STEAM_CALLBACK( CSteamSocketMgr, OnP2PSessionConnectFail, P2PSessionConnectFail_t, m_callbackP2PSessionConnectFail );
  491. CSteamSocket *InitiateConnection( ESocketIndex_t eSocketType, const CSteamID &steamID, const byte *data, size_t len );
  492. void DestroyConnection( ESocketIndex_t eSocketType, const CSteamID &steamID );
  493. void PrintStatus();
  494. private:
  495. CSteamSocket *CreateConnection( ESocketIndex_t eSocketType, const CSteamID &steamID );
  496. void DestroyConnection( CSteamSocket *pSocket );
  497. bool GetTypeForSocket( int s, ESocketIndex_t *peType );
  498. netadr_t GenerateRemoteAddress();
  499. CSteamSocket *FindSocketForAddress( const ns_address &adr );
  500. CSteamSocket *FindSocketForUser( ESocketIndex_t eSocketType, const CSteamID &steamID );
  501. bool IsValid() const
  502. {
  503. return m_bInitialized && Steam3Client().SteamNetworking();
  504. }
  505. // For Remote clients
  506. CUtlVector< CSteamSocket * > m_vecRemoteSockets;
  507. CUtlMap< netadr_t, CSteamSocket * > m_mapAdrToSteamSocket;
  508. CUtlMap< int, ESocketIndex_t > m_mapSocketToESocketType;
  509. int m_nNextRemoteAddress;
  510. bool m_bInitialized;
  511. };
  512. CSteamSocketMgr::CSteamSocketMgr() :
  513. m_bInitialized( false ),
  514. m_nNextRemoteAddress( 1 ),
  515. m_mapAdrToSteamSocket( 0, 0, DefLessFunc( netadr_t ) ),
  516. m_mapSocketToESocketType( 0, 0, DefLessFunc( int ) ),
  517. m_callbackP2PSessionRequest( this, &CSteamSocketMgr::OnP2PSessionRequest ),
  518. m_callbackP2PSessionConnectFail( this, &CSteamSocketMgr::OnP2PSessionConnectFail )
  519. {
  520. }
  521. CSteamSocketMgr::~CSteamSocketMgr()
  522. {
  523. }
  524. void CSteamSocketMgr::Init()
  525. {
  526. m_bInitialized = true;
  527. if ( Steam3Client().SteamNetworking() )
  528. Steam3Client().SteamNetworking()->AllowP2PPacketRelay( net_steamcnx_allowrelay.GetBool() );
  529. }
  530. void CSteamSocketMgr::Shutdown()
  531. {
  532. if ( !IsValid() )
  533. return;
  534. // Destroy remote sockets
  535. FOR_EACH_VEC_BACK( m_vecRemoteSockets, i )
  536. {
  537. CSteamSocket *pSocket = m_vecRemoteSockets[i];
  538. // this will delete pSocket
  539. DestroyConnection( pSocket );
  540. }
  541. m_vecRemoteSockets.RemoveAll();
  542. Assert( m_mapAdrToSteamSocket.Count() == 0 );
  543. m_mapAdrToSteamSocket.RemoveAll();
  544. m_mapSocketToESocketType.RemoveAll();
  545. m_bInitialized = false;
  546. }
  547. netadr_t CSteamSocketMgr::GenerateRemoteAddress()
  548. {
  549. netadr_t ret( m_nNextRemoteAddress++, STEAM_CNX_PORT );
  550. return ret;
  551. }
  552. void CSteamSocketMgr::OpenSocket( int s, int nModule, int nSetPort, int nDefaultPort, const char *pName, int nProtocol, bool bTryAny )
  553. {
  554. if ( !IsValid() )
  555. return;
  556. ESocketIndex_t eSocketType = ESocketIndex_t( nModule );
  557. if ( !IsSteamSocketType( eSocketType ) )
  558. return;
  559. // make sure we dont have a socket for this type
  560. FOR_EACH_MAP_FAST( m_mapSocketToESocketType, i )
  561. {
  562. if ( m_mapSocketToESocketType[i] == eSocketType )
  563. {
  564. AssertMsg1( false, "Already have a socket for this type: %s", s_SocketIndexMap->GetNameByToken( eSocketType ) );
  565. return;
  566. }
  567. }
  568. // save socket
  569. m_mapSocketToESocketType.InsertOrReplace( s, eSocketType );
  570. if ( net_steamcnx_debug.GetBool() )
  571. {
  572. ConColorMsg( STEAM_CNX_COLOR, "Opened Steam Socket %s ( socket %d )\n", s_SocketIndexMap->GetNameByToken( eSocketType ), s );
  573. }
  574. }
  575. void CSteamSocketMgr::CloseSocket( int s, int nModule )
  576. {
  577. if ( g_ThreadedSocketQueue.ShouldUseSocketsThreaded() )
  578. g_ThreadedSocketQueue.CloseSocket( s );
  579. if ( !IsValid() )
  580. return;
  581. ESocketIndex_t eSocketType = ESocketIndex_t( nModule );
  582. if ( !IsSteamSocketType( eSocketType ) )
  583. return;
  584. if ( net_steamcnx_debug.GetBool() )
  585. {
  586. ConColorMsg( STEAM_CNX_COLOR, "Closed Steam Socket %s\n", s_SocketIndexMap->GetNameByToken( eSocketType ) );
  587. }
  588. FOR_EACH_VEC_BACK( m_vecRemoteSockets, i )
  589. {
  590. CSteamSocket *pSocket = m_vecRemoteSockets[i];
  591. if ( pSocket->GetSocketType() == eSocketType )
  592. DestroyConnection( pSocket );
  593. }
  594. FOR_EACH_MAP( m_mapSocketToESocketType, i )
  595. {
  596. if ( m_mapSocketToESocketType[ i ] == eSocketType )
  597. {
  598. m_mapSocketToESocketType.RemoveAt( i );
  599. break;
  600. }
  601. }
  602. }
  603. CSteamSocket *CSteamSocketMgr::InitiateConnection( ESocketIndex_t eSocketTypeFrom, const CSteamID &steamID, const byte *data, size_t len )
  604. {
  605. CSteamSocket *pSocket = CreateConnection( eSocketTypeFrom, steamID );
  606. if ( !pSocket )
  607. return NULL;
  608. // don't have to wait for a connection to be established.. just send the packet
  609. if ( !Steam3Client().SteamNetworking()->SendP2PPacket( pSocket->GetSteamID(), data, len, k_EP2PSendReliable, pSocket->GetRemoteChannel() ) )
  610. {
  611. DestroyConnection( eSocketTypeFrom, steamID );
  612. return NULL;
  613. }
  614. return pSocket;
  615. }
  616. CSteamSocket *CSteamSocketMgr::CreateConnection( ESocketIndex_t eSocketType, const CSteamID &steamID )
  617. {
  618. if ( !IsValid() )
  619. return NULL;
  620. // if we already have a socket for this user, return that
  621. CSteamSocket *pSocket = FindSocketForUser( eSocketType, steamID );
  622. if ( pSocket )
  623. return pSocket;
  624. netadr_t adrRemote = GenerateRemoteAddress();
  625. ConColorMsg( STEAM_CNX_COLOR, "Generated %s for %llx\n", adrRemote.ToString(), steamID.ConvertToUint64() );
  626. // create
  627. pSocket = new CSteamSocket( steamID, eSocketType, adrRemote );
  628. m_mapAdrToSteamSocket.Insert( adrRemote, pSocket );
  629. m_vecRemoteSockets.AddToTail( pSocket );
  630. if ( net_steamcnx_debug.GetBool() )
  631. {
  632. ConColorMsg( STEAM_CNX_COLOR, "Created %s connection to %llx\n", s_SocketIndexMap->GetNameByToken( eSocketType ), steamID.ConvertToUint64() );
  633. }
  634. return pSocket;
  635. }
  636. void CSteamSocketMgr::DestroyConnection( CSteamSocket *pSocket )
  637. {
  638. if ( !IsValid() || !pSocket )
  639. return;
  640. // remove from address map & vector
  641. m_mapAdrToSteamSocket.Remove( pSocket->GetNetAddress() );
  642. m_vecRemoteSockets.FindAndFastRemove( pSocket );
  643. // we can close both channels with this user, as we can only talk to his server or client. If their client is talking to our server,
  644. // our client shouldn't be talking to their server
  645. Steam3Client().SteamNetworking()->CloseP2PChannelWithUser( pSocket->GetSteamID(), pSocket->GetLocalChannel() );
  646. Steam3Client().SteamNetworking()->CloseP2PChannelWithUser( pSocket->GetSteamID(), pSocket->GetRemoteChannel() );
  647. // log
  648. if ( net_steamcnx_debug.GetBool() )
  649. {
  650. ConColorMsg( STEAM_CNX_COLOR, "Destroyed %s connection to %llx\n", s_SocketIndexMap->GetNameByToken( pSocket->GetSocketType() ), pSocket->GetSteamID().ConvertToUint64() );
  651. }
  652. // done with socket
  653. delete pSocket;
  654. }
  655. void CSteamSocketMgr::DestroyConnection( ESocketIndex_t eSocketType, const CSteamID &steamID )
  656. {
  657. if ( !IsValid() )
  658. return;
  659. CSteamSocket *pSocket = FindSocketForUser( eSocketType, steamID );
  660. DestroyConnection( pSocket );
  661. }
  662. CSteamSocket *CSteamSocketMgr::FindSocketForAddress( const ns_address &adr )
  663. {
  664. // !FIXME! Eventually we probably should actually use SteamID P2P address type for this,
  665. // and not assign virtual addresses.
  666. if ( !adr.IsType<netadr_t>() )
  667. {
  668. Assert( false );
  669. return nullptr;
  670. }
  671. int idx = m_mapAdrToSteamSocket.Find( adr.AsType<netadr_t>() );
  672. if ( idx == m_mapAdrToSteamSocket.InvalidIndex() )
  673. return NULL;
  674. return m_mapAdrToSteamSocket[ idx ];
  675. }
  676. CSteamSocket *CSteamSocketMgr::FindSocketForUser( ESocketIndex_t eSocketType, const CSteamID &steamID )
  677. {
  678. FOR_EACH_VEC( m_vecRemoteSockets, i )
  679. {
  680. CSteamSocket *pSocket = m_vecRemoteSockets[i];
  681. if ( pSocket->GetSteamID() == steamID && pSocket->GetSocketType() == eSocketType )
  682. return pSocket;
  683. }
  684. return NULL;
  685. }
  686. void CSteamSocketMgr::OnP2PSessionRequest( P2PSessionRequest_t *pParam )
  687. {
  688. #ifndef DEDICATED
  689. // on listen servers, don't accept connections from others if they aren't in our matchmaking session
  690. if ( !g_pMatchFramework || !g_pMatchFramework->GetMatchSession() )
  691. return;
  692. if ( GetBaseLocalClient().IsConnected() && !sv.IsActive() )
  693. return;
  694. if ( !SessionMembersFindPlayer( g_pMatchFramework->GetMatchSession()->GetSessionSettings(), pParam->m_steamIDRemote.ConvertToUint64() ) )
  695. return;
  696. #endif
  697. // accept all connections
  698. Steam3Client().SteamNetworking()->AcceptP2PSessionWithUser( pParam->m_steamIDRemote );
  699. if ( net_steamcnx_debug.GetBool() )
  700. {
  701. ConColorMsg( STEAM_CNX_COLOR, "Accepted P2P connection with %llx\n", pParam->m_steamIDRemote.ConvertToUint64() );
  702. }
  703. }
  704. void CSteamSocketMgr::OnP2PSessionConnectFail( P2PSessionConnectFail_t *pParam )
  705. {
  706. // log disconnect
  707. if ( net_steamcnx_debug.GetBool() )
  708. {
  709. const char *pchP2PError = s_EP2PSessionErrorIndexMap->GetNameByToken( (EP2PSessionError)pParam->m_eP2PSessionError );
  710. ConColorMsg( STEAM_CNX_COLOR, "Received connection fail for user %llx %s\n", pParam->m_steamIDRemote.ConvertToUint64(), pchP2PError );
  711. }
  712. // close all connections to this user
  713. FOR_EACH_VEC_BACK( m_vecRemoteSockets, i )
  714. {
  715. CSteamSocket *pSocket = m_vecRemoteSockets[i];
  716. if ( pSocket->GetSteamID() != pParam->m_steamIDRemote )
  717. continue;
  718. DestroyConnection( pSocket );
  719. }
  720. }
  721. int CSteamSocketMgr::sendto( int s, const char * buf, int len, int flags, const ns_address &to )
  722. {
  723. if ( !to.IsType<netadr_t>() )
  724. {
  725. Warning( "WARNING: sendto: don't know how to send to non-IP address '%s'\n", ns_address_render( to ).String() );
  726. Assert( false );
  727. return -1; // return socket_error
  728. }
  729. if ( IsValid() )
  730. {
  731. CSteamSocket *pSteamSocket = FindSocketForAddress( to );
  732. if ( pSteamSocket )
  733. {
  734. Steam3Client().SteamNetworking()->SendP2PPacket( pSteamSocket->GetSteamID(), buf, len, k_EP2PSendUnreliable, pSteamSocket->GetRemoteChannel() );
  735. if ( net_steamcnx_debug.GetInt() >= 3 )
  736. {
  737. P2PSessionState_t p2pSessionState;
  738. Q_memset( &p2pSessionState, 0, sizeof( p2pSessionState ) );
  739. bool bSuccess = Steam3Client().SteamNetworking()->GetP2PSessionState( pSteamSocket->GetSteamID(), &p2pSessionState );
  740. ESocketIndex_t eType = NS_INVALID;
  741. GetTypeForSocket( s, &eType );
  742. ConColorMsg( STEAM_CNX_COLOR, " Send to %llx %u bytes on %s (status %s - %s)\n",
  743. pSteamSocket->GetSteamID().ConvertToUint64(),
  744. len,
  745. s_SocketIndexMap->GetNameByToken( eType ),
  746. bSuccess ? "true" : "false",
  747. p2pSessionState.m_bConnectionActive ? "connected" :"not connected" );
  748. }
  749. return len;
  750. }
  751. else if ( to.AsType<netadr_t>().GetPort() == STEAM_CNX_PORT )
  752. {
  753. if ( net_steamcnx_debug.GetInt() >= 1 )
  754. {
  755. ConColorMsg( STEAM_CNX_COLOR, " Attempted to send %u bytes on unknown steam socket address %s\n", len, ns_address_render( to ).String() );
  756. }
  757. return len;
  758. }
  759. }
  760. if ( OnlyUseSteamSockets() )
  761. {
  762. Warning( "WARNING: sendto: CSteamSocketMgr isn't initialized and we aren't falling back to our own sockets\n");
  763. return -1; // return socket_error
  764. }
  765. // Plain old socket send
  766. sockaddr sadr;
  767. to.AsType<netadr_t>().ToSockadr( &sadr );
  768. return ::sendto( s, buf, len, flags, &sadr, sizeof(sadr) );
  769. }
  770. bool CSteamSocketMgr::GetTypeForSocket( int s, ESocketIndex_t *peType )
  771. {
  772. int i = m_mapSocketToESocketType.Find( s );
  773. if ( i == m_mapSocketToESocketType.InvalidIndex() )
  774. return false;
  775. *peType = m_mapSocketToESocketType[i];
  776. return true;
  777. }
  778. int CSteamSocketMgr::recvfrom( int s, char * buf, int len, int flags, ns_address *from )
  779. {
  780. // Check non-steam socket first
  781. if ( !OnlyUseSteamSockets() )
  782. {
  783. sockaddr sadrfrom;
  784. socklen_t fromlen = sizeof(sadrfrom);
  785. int iret = ( g_ThreadedSocketQueue.ShouldUseSocketsThreaded() )
  786. ? g_ThreadedSocketQueue.recvfrom( s, buf, len, &sadrfrom )
  787. : ::recvfrom( s, buf, len, flags, &sadrfrom, &fromlen );
  788. if ( iret > 0 )
  789. {
  790. from->SetFromSockadr( &sadrfrom );
  791. return iret;
  792. }
  793. }
  794. if ( !IsValid() )
  795. return 0;
  796. // need to get data by socket type
  797. ESocketIndex_t eSocketType = NS_INVALID;
  798. if ( !GetTypeForSocket( s, &eSocketType ) )
  799. return 0;
  800. //
  801. // IPC-Steam performance optimization: don't do any IPC to P2P sockets API calls
  802. // if the session settings indicate that there can be no P2P communication
  803. //
  804. switch ( eSocketType )
  805. {
  806. case NS_CLIENT:
  807. // We can only be receiving P2P communication if we are a client of another
  808. // listen server, make sure that the session has the right data
  809. if ( sv.IsDedicated() )
  810. return 0;
  811. if ( sv.IsActive() )
  812. return 0;
  813. // Otherwise check how many players are connected to our session, if nobody is connected
  814. // then do no P2P communication with nobody
  815. if ( !g_pMatchFramework || !g_pMatchFramework->GetMatchSession() ||
  816. ( g_pMatchFramework->GetMatchSession()->GetSessionSettings()->GetInt( "members/numMachines", 0 ) < 2 ) ||
  817. Q_stricmp( g_pMatchFramework->GetMatchSession()->GetSessionSettings()->GetString( "server/server" ), "listen" ) )
  818. return 0;
  819. break;
  820. case NS_SERVER:
  821. // Dedicated servers don't do P2P communication
  822. if ( sv.IsDedicated() )
  823. return 0;
  824. // If we are not running a listen server then there shouldn't be any P2P communication
  825. if ( !sv.IsActive() )
  826. return 0;
  827. // Otherwise check how many players are connected to our session, if nobody is connected
  828. // then do no P2P communication with nobody
  829. if ( !g_pMatchFramework || !g_pMatchFramework->GetMatchSession() ||
  830. ( g_pMatchFramework->GetMatchSession()->GetSessionSettings()->GetInt( "members/numMachines", 0 ) < 2 ) )
  831. return 0;
  832. break;
  833. default:
  834. // There can be no P2P communication on any other socket type
  835. return 0;
  836. }
  837. uint32 cubMsg = 0;
  838. CSteamID steamIDRemote;
  839. // if no data to read, will return false
  840. if ( !Steam3Client().SteamNetworking()->ReadP2PPacket( buf, len, &cubMsg, &steamIDRemote, GetChannelForSocketType( eSocketType ) ) || cubMsg == 0 )
  841. return 0;
  842. // We have the SteamID for a user who sent us a packet on the channel, but we dont know on what channel the sender is waiting for a response.
  843. // We could add the response channel to each packet, but because clients only communicate with servers, and vice versa, we can assume
  844. // that the sender is our opposite. This conversion is done in CSteamSocket::GetTargetSocketType()
  845. // could be a new connection from this user.. add if necessary
  846. CSteamSocket *pSocket = FindSocketForUser( eSocketType, steamIDRemote );
  847. if ( !pSocket )
  848. {
  849. pSocket = CreateConnection( eSocketType, steamIDRemote );
  850. Assert( pSocket );
  851. }
  852. // got data.. update params
  853. *from = ns_address( pSocket->GetNetAddress() );
  854. if ( net_steamcnx_debug.GetInt() >= 3 )
  855. {
  856. ConColorMsg( STEAM_CNX_COLOR, " Received from %llx %u bytes on %s\n", steamIDRemote.ConvertToUint64(), cubMsg, s_SocketIndexMap->GetNameByToken( eSocketType ) );
  857. }
  858. return cubMsg;
  859. }
  860. ISteamSocketMgr::ESteamCnxType CSteamSocketMgr::GetCnxType()
  861. {
  862. return (ISteamSocketMgr::ESteamCnxType)clamp( net_steamcnx_enabled.GetInt(), (int)ESCT_NEVER, (int)ESCT_MAXTYPE - 1 );
  863. }
  864. uint64 CSteamSocketMgr::GetSteamIDForRemote( const ns_address &remote )
  865. {
  866. const CSteamSocket *pSocket = FindSocketForAddress( remote );
  867. if ( pSocket )
  868. {
  869. return pSocket->GetSteamID().ConvertToUint64();
  870. }
  871. return 0ull;
  872. }
  873. void CSteamSocketMgr::PrintStatus()
  874. {
  875. ConColorMsg( STEAM_CNX_COLOR, "SteamSocketMgr Status\n" );
  876. if ( !IsValid() )
  877. {
  878. ConColorMsg( STEAM_CNX_COLOR, " Invalid (no Steam3Client API?)\n" );
  879. return;
  880. }
  881. // print socket info
  882. ConColorMsg( STEAM_CNX_COLOR, " %d connections\n", m_vecRemoteSockets.Count() );
  883. FOR_EACH_VEC( m_vecRemoteSockets, i )
  884. {
  885. CSteamSocket *pSocket = m_vecRemoteSockets[i];
  886. P2PSessionState_t p2pSessionState;
  887. if ( !Steam3Client().SteamNetworking()->GetP2PSessionState( pSocket->GetSteamID(), &p2pSessionState ) )
  888. {
  889. ConColorMsg( STEAM_CNX_COLOR, " %d %llx, failed to get session state\n", i, pSocket->GetSteamID().ConvertToUint64() );
  890. continue;
  891. }
  892. ConColorMsg( STEAM_CNX_COLOR, " %d %llx, type(%s), psuedoAddr(%s), connected(%s), connecting(%s), relay(%s), bytesQueued(%d), packetsQueued(%d), lasterror(%s)\n",
  893. i,
  894. pSocket->GetSteamID().ConvertToUint64(),
  895. s_SocketIndexMap->GetNameByToken( pSocket->GetSocketType() ),
  896. pSocket->GetNetAddress().ToString(),
  897. p2pSessionState.m_bConnectionActive ? "yes" : "no",
  898. p2pSessionState.m_bConnecting ? "yes" : "no",
  899. p2pSessionState.m_bUsingRelay ? "yes" : "no",
  900. p2pSessionState.m_nBytesQueuedForSend,
  901. p2pSessionState.m_nPacketsQueuedForSend,
  902. s_EP2PSessionErrorIndexMap->GetNameByToken( (EP2PSessionError)p2pSessionState.m_eP2PSessionError ) );
  903. }
  904. }
  905. #else
  906. // For LINUX it's basically all stubbed
  907. #ifdef _PS3
  908. ASSERT_INVARIANT( sizeof( int ) == sizeof( socklen_t ) );
  909. #endif
  910. class CSteamSocketMgr : public ISteamSocketMgr
  911. {
  912. public:
  913. virtual void Init() {}
  914. virtual void Shutdown() {}
  915. ISteamSocketMgr::ESteamCnxType GetCnxType() { return ESCT_NEVER; }
  916. virtual void OpenSocket( int s, int nModule, int nSetPort, int nDefaultPort, const char *pName, int nProtocol, bool bTryAny ) {}
  917. virtual void CloseSocket( int s, int nModule )
  918. {
  919. if ( g_ThreadedSocketQueue.ShouldUseSocketsThreaded() )
  920. g_ThreadedSocketQueue.CloseSocket( s );
  921. }
  922. virtual int sendto( int s, const char * buf, int len, int flags, const ns_address &to ) OVERRIDE
  923. {
  924. if ( to.IsType<netadr_t>() )
  925. {
  926. sockaddr sadr;
  927. to.AsType<netadr_t>().ToSockadr( &sadr );
  928. return ::sendto( s, buf, len, flags, &sadr, sizeof(sadr) );
  929. }
  930. AssertMsg1( false, "Tried to send to non-IP address '%s'", ns_address_render( to ).String() );
  931. return -1;
  932. }
  933. virtual int recvfrom( int s, char * buf, int len, int flags, ns_address *from ) OVERRIDE
  934. {
  935. sockaddr sadrfrom;
  936. socklen_t fromlen = sizeof(sadrfrom);
  937. int iret = ( g_ThreadedSocketQueue.ShouldUseSocketsThreaded() )
  938. ? g_ThreadedSocketQueue.recvfrom( s, buf, len, &sadrfrom )
  939. : ::recvfrom( s, buf, len, flags, &sadrfrom, &fromlen );
  940. if ( iret > 0 )
  941. from->SetFromSockadr( &sadrfrom );
  942. return iret;
  943. }
  944. virtual uint64 GetSteamIDForRemote( const ns_address &remote )
  945. {
  946. return 0ull;
  947. }
  948. void PrintStatus()
  949. {
  950. }
  951. };
  952. #endif
  953. CSteamSocketMgr g_SteamSocketMgr;
  954. ISteamSocketMgr *g_pSteamSocketMgr = &g_SteamSocketMgr;
  955. netadr_t NET_InitiateSteamConnection(int sock, uint64 uSteamID, const char *format, ...)
  956. {
  957. netadr_t adr;
  958. #if defined( USE_STEAM_SOCKETS )
  959. if ( uSteamID == 0ull )
  960. {
  961. Warning( "NET_InitiateSteamConnection called with uSteamID == 0\n" );
  962. return adr;
  963. }
  964. va_list argptr;
  965. char string[ MAX_ROUTABLE_PAYLOAD ];
  966. va_start( argptr, format );
  967. Q_vsnprintf( string, sizeof( string ), format, argptr );
  968. va_end( argptr );
  969. int length = Q_strlen( string );
  970. CUtlBuffer sendBuf;
  971. sendBuf.PutUnsignedInt( (unsigned int)-1 );
  972. sendBuf.Put( string, length );
  973. CSteamID steamID;
  974. steamID.SetFromUint64( uSteamID );
  975. if ( net_steamcnx_debug.GetBool() )
  976. {
  977. ConColorMsg( STEAM_CNX_COLOR, "Initiate %llx\n", uSteamID );
  978. }
  979. CSteamSocket *pSocket = g_SteamSocketMgr.InitiateConnection( (ESocketIndex_t)sock, steamID, (const byte *)sendBuf.Base(), sendBuf.TellPut() );
  980. if ( !pSocket )
  981. {
  982. Warning( "NET_InitiateSteamConnection failed to create a socket\n" );
  983. return adr;
  984. }
  985. adr = pSocket->GetNetAddress();
  986. #endif
  987. return adr;
  988. }
  989. void NET_TerminateSteamConnection( int sock, uint64 uSteamID )
  990. {
  991. #if defined( USE_STEAM_SOCKETS )
  992. if ( uSteamID == 0ull )
  993. return;
  994. if ( net_steamcnx_debug.GetBool() )
  995. {
  996. ConColorMsg( STEAM_CNX_COLOR, "Terminate %llx\n", uSteamID );
  997. }
  998. g_SteamSocketMgr.DestroyConnection( (ESocketIndex_t)sock, uSteamID );
  999. #endif
  1000. }
  1001. CON_COMMAND( net_steamcnx_status, "Print status of steam connection sockets." )
  1002. {
  1003. g_SteamSocketMgr.PrintStatus();
  1004. }