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.

300 lines
6.2 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include <windows.h>
  8. #include "messagemgr.h"
  9. #include "tcpsocket.h"
  10. #include "iphelpers.h"
  11. #include "tier0/platform.h"
  12. #include "threadhelpers.h"
  13. #define MSGMGR_LISTEN_PORT_FIRST 22512
  14. #define MSGMGR_LISTEN_PORT_LAST 22520
  15. #define BROADCAST_INTERVAL 2 // Broadcast our presence every N seconds.
  16. #define NUM_QUEUED_MESSAGES 200
  17. class CMessageMgr : public IMessageMgr
  18. {
  19. public:
  20. CMessageMgr();
  21. ~CMessageMgr();
  22. bool Init();
  23. void Term();
  24. // IMessageMgr overrides.
  25. public:
  26. virtual void Print( const char *pMsg );
  27. private:
  28. DWORD ThreadFn();
  29. static DWORD WINAPI StaticThreadFn( LPVOID pParameter );
  30. private:
  31. // Only our thread touches this, NOT the main thread.
  32. CUtlLinkedList<ITCPSocket*,int> m_Sockets;
  33. HANDLE m_hThread;
  34. DWORD m_dwThreadID;
  35. HANDLE m_hExitObj; // This is signalled when we want the thread to exit.
  36. HANDLE m_hExitResponseObj; // The thread sets this when it exits.
  37. HANDLE m_hMessageObj; // This signals to the thread that there's a message to send.
  38. HANDLE m_hMessageSentObj; // This signals back to the main thread that the message was sent.
  39. const char *m_pMessageText; // The text to send.
  40. // This is only touched by the thread.
  41. CUtlLinkedList<char*,int> m_MessageQ; // FIFO of NUM_QUEUED_MESSAGES.
  42. ITCPListenSocket *m_pListenSocket;
  43. int m_iListenPort;
  44. ISocket *m_pBroadcastSocket;
  45. double m_flLastBroadcast;
  46. };
  47. CMessageMgr::CMessageMgr()
  48. {
  49. m_pBroadcastSocket = NULL;
  50. m_pListenSocket = NULL;
  51. m_hThread = NULL;
  52. m_hExitObj = m_hExitResponseObj = m_hMessageObj = m_hMessageSentObj = NULL;
  53. }
  54. CMessageMgr::~CMessageMgr()
  55. {
  56. Term();
  57. }
  58. bool CMessageMgr::Init()
  59. {
  60. m_hExitObj = CreateEvent( NULL, false, false, NULL );
  61. m_hExitResponseObj = CreateEvent( NULL, false, false, NULL );
  62. m_hMessageObj = CreateEvent( NULL, false, false, NULL );
  63. m_hMessageSentObj = CreateEvent( NULL, false, false, NULL );
  64. if ( !m_hExitObj || !m_hExitResponseObj || !m_hMessageObj || !m_hMessageSentObj )
  65. return false;
  66. // Create the broadcast socket.
  67. m_pBroadcastSocket = CreateIPSocket();
  68. if ( !m_pBroadcastSocket )
  69. return false;
  70. if ( !m_pBroadcastSocket->BindToAny( 0 ) )
  71. return false;
  72. // Create the listen socket.
  73. m_pListenSocket = NULL;
  74. for ( m_iListenPort=MSGMGR_LISTEN_PORT_FIRST; m_iListenPort <= MSGMGR_LISTEN_PORT_LAST; m_iListenPort++ )
  75. {
  76. m_pListenSocket = CreateTCPListenSocket( m_iListenPort );
  77. if ( m_pListenSocket )
  78. break;
  79. }
  80. if ( !m_pListenSocket )
  81. return false;
  82. // Create our broadcast/connection thread.
  83. m_flLastBroadcast = 0;
  84. m_hThread = CreateThread(
  85. NULL,
  86. 0,
  87. &CMessageMgr::StaticThreadFn,
  88. this,
  89. 0,
  90. &m_dwThreadID );
  91. if ( !m_hThread )
  92. return false;
  93. Plat_SetThreadName( m_dwThreadID, "MessageMgr" );
  94. return true;
  95. }
  96. void CMessageMgr::Term()
  97. {
  98. // Wait for the thread to exit?
  99. if ( m_hThread )
  100. {
  101. DWORD dwExitCode = 0;
  102. if ( GetExitCodeThread( m_hThread, &dwExitCode ) && dwExitCode == STILL_ACTIVE )
  103. {
  104. SetEvent( m_hExitObj );
  105. WaitForSingleObject( m_hExitResponseObj, INFINITE );
  106. }
  107. CloseHandle( m_hThread );
  108. m_hThread = NULL;
  109. }
  110. CloseHandle( m_hExitObj );
  111. m_hExitObj = NULL;
  112. CloseHandle( m_hExitResponseObj );
  113. m_hExitResponseObj = NULL;
  114. CloseHandle( m_hMessageObj );
  115. m_hMessageObj = NULL;
  116. CloseHandle( m_hMessageSentObj );
  117. m_hMessageSentObj = NULL;
  118. if ( m_pListenSocket )
  119. {
  120. m_pListenSocket->Release();
  121. m_pListenSocket = NULL;
  122. }
  123. if ( m_pBroadcastSocket )
  124. {
  125. m_pBroadcastSocket->Release();
  126. m_pBroadcastSocket = NULL;
  127. }
  128. }
  129. void CMessageMgr::Print( const char *pMsg )
  130. {
  131. m_pMessageText = pMsg;
  132. SetEvent( m_hMessageObj );
  133. WaitForSingleObject( m_hMessageSentObj, INFINITE );
  134. }
  135. DWORD CMessageMgr::ThreadFn()
  136. {
  137. while ( 1 )
  138. {
  139. // Broadcast our presence?
  140. double flCurTime = Plat_FloatTime();
  141. if ( flCurTime - m_flLastBroadcast >= BROADCAST_INTERVAL )
  142. {
  143. // Broadcast our presence.
  144. char msg[9];
  145. msg[0] = MSGMGR_PACKETID_ANNOUNCE_PRESENCE;
  146. *((int*)&msg[1]) = MSGMGR_VERSION;
  147. *((int*)&msg[5]) = m_iListenPort;
  148. m_pBroadcastSocket->Broadcast( msg, sizeof( msg ), MSGMGR_BROADCAST_PORT );
  149. m_flLastBroadcast = flCurTime;
  150. }
  151. // Accept new connections.
  152. CIPAddr addr;
  153. ITCPSocket *pConn = m_pListenSocket->UpdateListen( &addr );
  154. if ( pConn )
  155. {
  156. // Send what's in our queue.
  157. FOR_EACH_LL( m_MessageQ, iQ )
  158. {
  159. char *pMsg = m_MessageQ[iQ];
  160. int bufLen = strlen( pMsg ) + 1;
  161. char packetID = MSGMGR_PACKETID_MSG;
  162. const void *data[2] = { &packetID, pMsg };
  163. int len[2] = { 1, bufLen };
  164. // Send it out to our sockets.
  165. pConn->SendChunks( data, len, 2 );
  166. }
  167. m_Sockets.AddToTail( pConn );
  168. }
  169. // Should we exit?
  170. HANDLE handles[2] = {m_hExitObj, m_hMessageObj};
  171. DWORD ret = WaitForMultipleObjects( 2, handles, FALSE, 200 );
  172. if ( ret == WAIT_OBJECT_0 )
  173. {
  174. break;
  175. }
  176. else if ( ret == (WAIT_OBJECT_0+1) )
  177. {
  178. // Add it to the queue.
  179. int index;
  180. if ( m_MessageQ.Count() >= NUM_QUEUED_MESSAGES )
  181. {
  182. index = m_MessageQ.Tail();
  183. delete m_MessageQ[index];
  184. }
  185. else
  186. {
  187. index = m_MessageQ.AddToTail();
  188. }
  189. int bufLen = strlen( m_pMessageText ) + 1;
  190. m_MessageQ[index] = new char[ bufLen ];
  191. strcpy( m_MessageQ[index], m_pMessageText );
  192. // Ok, send out the message.
  193. char packetID = MSGMGR_PACKETID_MSG;
  194. const void *data[2] = { &packetID, m_pMessageText };
  195. int len[2] = { 1, bufLen };
  196. // Send it out to our sockets.
  197. FOR_EACH_LL( m_Sockets, i )
  198. {
  199. m_Sockets[i]->SendChunks( data, len, 2 );
  200. }
  201. // Notify the main thread that we've sent it.
  202. SetEvent( m_hMessageSentObj );
  203. }
  204. }
  205. // Cleanup all our sockets (the main thread should never touch them).
  206. FOR_EACH_LL( m_Sockets, i )
  207. m_Sockets[i]->Release();
  208. m_Sockets.Purge();
  209. m_MessageQ.PurgeAndDeleteElements();
  210. SetEvent( m_hExitResponseObj );
  211. return 0;
  212. }
  213. DWORD CMessageMgr::StaticThreadFn( LPVOID pParameter )
  214. {
  215. return ((CMessageMgr*)pParameter)->ThreadFn();
  216. }
  217. static CMessageMgr g_MessageMgr;
  218. IMessageMgr* GetMessageMgr()
  219. {
  220. return &g_MessageMgr;
  221. }