CommManager.cpp
上传用户:royluo
上传日期:2007-01-05
资源大小:1584k
文件大小:34k
源码类别:

游戏

开发平台:

Visual C++

  1. /*****************************************************************************
  2. *                                                                             
  3. *   CommManager.cpp                                                            
  4. *                                                                             
  5. *   Electrical Engineering Faculty - Software Lab                             
  6. *   Spring semester 1998                                                      
  7. *                                                                             
  8. *   Tanks game                                                                
  9. *                                                                             
  10. *   Module description: The Communication Manager wraps the DPlay API, and 
  11. *                       manages the communication setting, initializing,
  12. *                       termination, and messages traffic to and from the app.
  13. *                                                                             
  14. *   Authors: Eran Yariv - 28484475                                           
  15. *            Moshe Zur  - 24070856                                           
  16. *                                                                            
  17. *                                                                            
  18. *   Date: 23/09/98                                                           
  19. *                                                                            
  20. ******************************************************************************/
  21. #include "stdafx.h"
  22. #include <objbase.h>    // we need this or else initguid.h won't let us compile
  23. #include <initguid.h>   // including the header makes DEFINE_GUID define the GUID.
  24. #include "Tanks.h"
  25. #include "Client.h"
  26. #include "Host.h"
  27. #include "EnumSession.h"
  28. #ifdef GATHER_NETWORK_STATS
  29.     #define SET_SESSION_START_TIME(t)           {m_dwSessionStartTime=(t);      
  30.                                                  m_dwLastSecTimeRecv = m_dwLastSecTimeSend = m_dwSessionStartTime;}
  31.     #define INC_TOTAL_BYTES_SENT(cb)            m_dwTotalBytesSent+=(cb);
  32.     #define INC_TOTAL_BYTES_RECEIVED(cb)        m_dwTotalBytesReceived+=(cb);
  33.     #define INC_TOTAL_CLIENT_MESSAGES_SENT      m_dwTotalClientMsgsSent++;
  34.     #define INC_TOTAL_CLIENT_MESSAGES_RECEIVED  m_dwTotalClientMsgsReceived++;
  35.     #define INC_TOTAL_HOST_MESSAGES_SENT        m_dwTotalHostMsgsSent++;
  36.     #define INC_TOTAL_HOST_MESSAGES_RECEIVED    m_dwTotalHostMsgsReceived++;
  37.     
  38.     #define UPDATE_BIGGEST_PACKET_SENT(cb)      m_dwBiggestSentPacketSize = max (m_dwBiggestSentPacketSize, (cb));
  39.     #define UPDATE_SMALLEST_PACKET_SENT(cb)     m_dwSmallestSentPacketSize = min (m_dwSmallestSentPacketSize, (cb));
  40.     #define UPDATE_BIGGEST_PACKET_RECEIVED(cb)  m_dwBiggestReceivedPacketSize = max (m_dwBiggestReceivedPacketSize, (cb));
  41.     #define UPDATE_SMALLEST_PACKET_RECEIVED(cb) m_dwSmallestReceivedPacketSize = min (m_dwSmallestReceivedPacketSize,(cb));
  42.     #define INC_CLIENT_SEND_ERRORS              m_dwClientSendErrors++;
  43.     #define INC_HOST_SEND_ERRORS                m_dwHostSendErrors++;
  44.     #define INC_RECEIVE_ERRORS                  m_dwReceiveErrors++;
  45.     #define UPDATE_RECEIVE                      UpdateReceive();
  46.     #define UPDATE_SEND                         UpdateSend();
  47.     #define TRACE_NETWORK_STATS                 TraceNetworkStats();
  48. #else       // !defined GATHER_NETWORK_STATS
  49.     #define SET_SESSION_START_TIME(t)           
  50.     #define INC_TOTAL_BYTES_SENT(cb)            
  51.     #define INC_TOTAL_BYTES_RECEIVED(cb)        
  52.     #define INC_TOTAL_CLIENT_MESSAGES_SENT      
  53.     #define INC_TOTAL_CLIENT_MESSAGES_RECEIVED  
  54.     #define INC_TOTAL_HOST_MESSAGES_SENT        
  55.     #define INC_TOTAL_HOST_MESSAGES_RECEIVED    
  56.     
  57.     #define UPDATE_BIGGEST_PACKET_SENT(cb)      
  58.     #define UPDATE_SMALLEST_PACKET_SENT(cb)     
  59.     #define UPDATE_BIGGEST_PACKET_RECEIVED(cb)  
  60.     #define UPDATE_SMALLEST_PACKET_RECEIVED(cb) 
  61.     #define INC_CLIENT_SEND_ERRORS              
  62.     #define INC_HOST_SEND_ERRORS                
  63.     #define INC_RECEIVE_ERRORS                  
  64.     #define UPDATE_RECEIVE                      
  65.     #define UPDATE_SEND
  66.     #define TRACE_NETWORK_STATS
  67. #endif // defined GATHER_NETWORK_STATS
  68. char TANKS_SESSION_NAME[] = "Tanks Game";
  69. const BYTE CCommManager::ACK = 0xFF;
  70. const DPID CCommManager::INVALID_PLAYER_ID = MAX_DWORD;
  71. CCommManager::CCommManager() :
  72.     m_pIDP(NULL),
  73.     m_fIsConnected(FALSE),
  74.     m_fIsPlaying(FALSE),
  75.     m_fIsHost(TRUE),
  76.     m_IncomingMsgQ(TANKS_APP->m_gIncomingMsgQueue),
  77.     m_OutgoingMsgQ(TANKS_APP->m_gOutgoingMsgQueue),
  78.     m_idHost(INVALID_PLAYER_ID),
  79.     m_idPlayer(INVALID_PLAYER_ID),
  80.     m_pHost(NULL)
  81. #ifdef GATHER_NETWORK_STATS
  82.     ,
  83.     m_dwTotalBytesSent               (0),
  84.     m_dwTotalBytesReceived           (0),
  85.     m_dwBiggestSentPacketSize        (0),
  86.     m_dwLastRecvTotal                (0),
  87.     m_dwLastSentTotal                (0),
  88.     m_dwSmallestSentPacketSize       (MAX_DWORD),
  89.     m_dwBiggestReceivedPacketSize    (0),
  90.     m_dwSmallestReceivedPacketSize   (MAX_DWORD),
  91.     m_dwMaxBytesSentInLastSecond     (0),
  92.     m_dwMaxBytesReceivedInLastSecond (0),
  93.     m_dwTotalClientMsgsSent          (0),
  94.     m_dwTotalClientMsgsReceived      (0),
  95.     m_dwClientSendErrors             (0),   
  96.     m_dwTotalHostMsgsSent            (0),
  97.     m_dwTotalHostMsgsReceived        (0),
  98.     m_dwHostSendErrors               (0),
  99.     m_dwReceiveErrors                (0)
  100. #endif //   GATHER_NETWORK_STATS
  101. {
  102.     m_hRecvEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
  103.     m_hQuitEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
  104.     // Create client
  105.     m_pClient = new CClient(*this);
  106. }
  107. CCommManager::~CCommManager ()
  108. {
  109.     if (m_pIDP)
  110.         m_pIDP->Release();
  111.     if (m_pClient)
  112.         delete m_pClient;
  113.     if (m_pHost)
  114.         delete m_pHost;
  115.     CloseHandle (m_hRecvEvent);
  116.     CloseHandle (m_hQuitEvent);
  117. }
  118. void
  119. CCommManager::SetCommParams()
  120. {
  121.     CCommSettingDlg dlg;
  122.     dlg.DoModal();
  123.     return;
  124. }
  125. /*------------------------------------------------------------------------------
  126.   Function: OnNewGame
  127.   Purpose:  Initailizes the DPlay object, and starts the connection protocol w/
  128.             the server. In case this is a server machine, it also starts the
  129.             host object.
  130.   Input:    None.
  131.   Output:   Return TRUE if succeeded to connect to a game session.
  132. ------------------------------------------------------------------------------*/
  133. BOOL 
  134. CCommManager::OnNewGame()
  135. {
  136.     try {
  137.         // Create DirectPlay2 interface
  138.         CreateDPInterface();
  139.         // Start session
  140.         OpenSession();
  141.         // Create player
  142.         CreatePlayer();
  143.     } catch (CString &strErr) {
  144.         AfxMessageBox(strErr, MB_OK | MB_ICONEXCLAMATION);
  145.         return FALSE;
  146.     }
  147.     //
  148.     //  At last - all is well, start the message pumping:
  149.     //
  150.         // Empty both queues:
  151.     TANKS_APP->m_gOutgoingMsgQueue.Empty();
  152.     TANKS_APP->m_gOutgoingMsgQueue.Empty();
  153.     m_bLostServerConnection = FALSE;
  154.     TANKS_APP->SetMaxRTT(MAX_VALID_RTT);  // Init the global max RTT param from the reg.
  155.     StartWorkerThread();
  156.     if (! m_pClient->WaitForGameProperties()) {
  157.         AfxMessageBox("Server timed out");
  158.         return FALSE;
  159.     }
  160.     return TRUE;
  161. }
  162. BOOL 
  163. CCommManager::OnStopGame()
  164. {
  165.     if (m_fIsPlaying) {
  166.         // Stop the working thread:
  167.         EndThread(TRUE);
  168.         // Destroy the player object:
  169.         DestroyPlayer();
  170.         // Stop the session:
  171.         CloseSession();
  172.     }
  173.     return TRUE;
  174. }
  175. void
  176. CCommManager::CreateDPInterface ()
  177. {
  178.     LPDIRECTPLAY   lpDirectPlay1 = NULL;
  179.     HRESULT        hr;
  180.     //
  181.     //  Get the GUID of the connection provider.
  182.     //
  183.     GUID guidCurr;
  184.     if (! TANKS_APP->GetStoredGUID(&guidCurr)) 
  185.     {   // Its the first run on this machine and no connection was selected.
  186.         throw CString ("No connection was selected.n"
  187.             "Use Setting->Communication prefernces menun"
  188.             "to select a connection and try again.");
  189.         return;
  190.     }
  191.     //
  192.     //  Release previous interface.
  193.     //
  194.     if (m_pIDP) 
  195.     {    
  196.         CloseSession();     // Just to make sure
  197.         m_pIDP->Release();  // Release the old interface
  198.         m_pIDP = NULL;      // Prepare for a failure
  199.     }
  200.     //
  201.     //  Get a new interface.
  202.     //
  203.     // Retrieve a DirectPlay 1.0 interface.
  204.     hr = DirectPlayCreate(&guidCurr, &lpDirectPlay1, NULL);
  205.     if FAILED(hr) {
  206.         TRACE("DirectPlayCreate failed HR=%uln", hr);
  207.         throw CString("Communication failure.n(DirectPlayCreate failed)");
  208.         return;
  209.     }
  210.     
  211.     // Query for an ANSI DirectPlay2 interface.
  212.     hr = lpDirectPlay1->QueryInterface(IID_IDirectPlay2A, (LPVOID *) &m_pIDP);
  213.     // Release DirectPlay 1.0 interface
  214.     if (lpDirectPlay1)
  215.         lpDirectPlay1->Release();
  216.     if FAILED(hr) {
  217.         TRACE("QueryInterface for IID_IDirectPlay2A failed HR=%uln", hr);
  218.         throw CString("Communication failure.n"
  219.             "(QueryInterface failed)");
  220.         return;
  221.     }
  222. }
  223. void
  224. CCommManager::OpenSession()
  225. {
  226.     HRESULT hr;
  227.     DPSESSIONDESC2 SessionDesc;
  228.     // Close any exiting session.
  229.     if (m_fIsConnected)
  230.         CloseSession();
  231.     ZeroMemory(&SessionDesc, sizeof(DPSESSIONDESC2));
  232.     SessionDesc.dwSize = sizeof(DPSESSIONDESC2);
  233.     SessionDesc.guidApplication = TANKS_GUID;
  234.     
  235.     // Get user choice from registry:
  236.     m_fIsHost = TANKS_APP->GetStoredIsHostFlag();
  237.     //
  238.     //  Open session:
  239.     //
  240.     if (m_fIsHost) 
  241.     {    // Create a new session
  242.         CString strName = TANKS_APP->GetStoredPlayerName();
  243.         SessionDesc.dwFlags =   DPSESSION_KEEPALIVE         | 
  244.                                 DPSESSION_NODATAMESSAGES;
  245.         // We add one for our Host that occupies an additional player.
  246.         SessionDesc.dwMaxPlayers = MAX_TANKS + 1;   
  247.         SessionDesc.lpszSessionNameA = const_cast<LPSTR>((LPCSTR)strName);
  248.         hr = m_pIDP->Open(&SessionDesc, DPOPEN_CREATE);
  249.         if FAILED(hr) {
  250.             throw CString("Cannot create new session");
  251.             return;
  252.         }
  253.         // Create the host object:
  254.         m_CSHost.Lock();
  255.         m_pHost = new CHost(*this);
  256.         if (! m_pHost) 
  257.         {
  258.             m_CSHost.Unlock();
  259.             throw CString("Cannot create new session");
  260.             return;
  261.         }
  262.         m_CSHost.Unlock();
  263.     } 
  264.     else 
  265.     {
  266.         //
  267.         // Let user select a session.
  268.         //
  269.         DPSESSIONDESC2 dpDesc;
  270.         ZeroMemory(&dpDesc, sizeof(DPSESSIONDESC2));
  271.         dpDesc.dwSize = sizeof(DPSESSIONDESC2);
  272.         dpDesc.guidApplication = TANKS_GUID;
  273.         // Bring the select session dialog.
  274.         CEnumSession dlg(m_pIDP, &dpDesc.guidInstance);
  275.         if (IDOK != dlg.DoModal())
  276.         {   // User aborted or EnumSession failed.
  277.             throw CString("No session selected");
  278.             return;
  279.         }
  280.         // Join the session:
  281.         hr = m_pIDP->Open(&dpDesc, DPOPEN_JOIN);
  282.         if FAILED(hr) {
  283.             throw CString("Cannot join session");
  284.             return;
  285.         }
  286.     }
  287. /*  The remarked code was used to check if its a TCP/IP connection.
  288.     // Check the session is supporting guarentied delivery:
  289.     ZeroMemory(&m_SessionCaps, sizeof(DPCAPS));
  290.     m_SessionCaps.dwSize = sizeof(DPCAPS);
  291.     hr = m_pIDP->GetCaps(&m_SessionCaps, DPGETCAPS_GUARANTEED);
  292.     if (FAILED(hr) || 
  293.         ! (m_SessionCaps.dwFlags & DPCAPS_GUARANTEEDSUPPORTED)) {
  294.         // We don't support the connection chosen by user:
  295.         m_pIDP->Close();
  296.         throw CString("The connection selected isn't supported.n"
  297.             "Select another connectionnand try again");
  298.         return;
  299.     }
  300. */
  301.     m_fIsConnected = TRUE;
  302.     SET_SESSION_START_TIME (GetTickCount());
  303. }
  304. void
  305. CCommManager::CloseSession()
  306. {
  307.     HRESULT hr;
  308.     if (m_fIsConnected) {
  309.         m_fIsConnected = FALSE;
  310.         if (m_fIsHost) {
  311.             m_pIDP->DestroyPlayer(m_idHost);
  312.             ASSERT (m_pHost);
  313.             m_CSHost.Lock();
  314.             delete m_pHost;
  315.             m_pHost = NULL;
  316.             m_CSHost.Unlock();
  317.         }
  318.         m_idHost = INVALID_PLAYER_ID;
  319.         hr = m_pIDP->Close();
  320.         if FAILED(hr)
  321.         {
  322.             TRACE("DirectPlay Close() session failed HR=%uln", hr);
  323.             throw CString("Communication failure.nFailed to close session");
  324.             return;
  325.         }
  326.         TRACE_NETWORK_STATS;
  327.     }
  328. }
  329. void
  330. CCommManager::CreatePlayer()
  331. {
  332.     HRESULT hr;
  333.     DPNAME dpName;
  334.     ASSERT (! m_fIsPlaying);    // We shouldn't be here if we are playing!!
  335.     // Check the RecvEvent handle:
  336.     if (! m_hRecvEvent) {
  337.         throw CString("Cannot create player:nReceive event is null");
  338.         return;
  339.     }
  340.     // Create host's player object:
  341.     if (m_fIsHost) {
  342.         ZeroMemory (&dpName, sizeof(DPNAME));
  343.         dpName.dwSize = sizeof(DPNAME);
  344.         dpName.lpszShortNameA = "Host";
  345.     
  346.         hr = m_pIDP->CreatePlayer(&m_idHost, &dpName, m_hRecvEvent, NULL, 0, 0);
  347.         if FAILED(hr) {
  348.             TRACE ("CreatePlayer failed HR=%uln", hr);
  349.             throw CString("Communication failure.nCannot create host player");
  350.             return;
  351.         }
  352.     }
  353.     // Get user name:
  354.     CString strPlayerName = TANKS_APP->GetStoredPlayerName();
  355.     ZeroMemory (&dpName, sizeof(DPNAME));
  356.     dpName.dwSize = sizeof(DPNAME);
  357.     dpName.lpszLongNameA = 
  358.         dpName.lpszShortNameA = const_cast<char*>((LPCSTR)strPlayerName);
  359.     // Try to create player object:    
  360.     hr = m_pIDP->CreatePlayer(&m_idPlayer, &dpName, m_hRecvEvent, NULL, 0, 0);
  361.     if FAILED(hr) {
  362.             TRACE ("CreatePlayer failed HR=%uln", hr);
  363.             throw CString("Communication failure.n"
  364.                 "Cannot create client player");
  365.         return;
  366.     }
  367.     m_pIDP->SetPlayerData (
  368.         m_idPlayer, 
  369.         dpName.lpszLongNameA, 
  370.         strPlayerName.GetLength() + 1, 
  371.         DPSET_REMOTE | DPSET_GUARANTEED);
  372.     m_fIsPlaying = TRUE;
  373. }
  374. void
  375. CCommManager::DestroyPlayer()
  376. {
  377.     if (m_fIsPlaying) {
  378.         m_fIsPlaying = FALSE;
  379.         m_pIDP->DestroyPlayer(m_idPlayer);
  380.         m_idPlayer = INVALID_PLAYER_ID;
  381.     }
  382. }
  383. /*------------------------------------------------------------------------------
  384.   Function: ThreadEntry
  385.   Purpose:  Main message loop. Thread is waiting for signals from outgoing and
  386.             incoming queues and when signaled delivers messages to their destination.
  387.   Input:    None.
  388.   Output:   None.
  389.   Remarks:  Incoming messages are dispatched to host or client objects to be
  390.             processed.
  391.             All outgoing messages are sent to the host machine.
  392. ------------------------------------------------------------------------------*/
  393. UINT 
  394. CCommManager::ThreadEntry (LPVOID /*pParam*/)
  395. {
  396. #define WAIT_OBJECT_1   (WAIT_OBJECT_0 + 1)
  397. #define WAIT_OBJECT_2   (WAIT_OBJECT_1 + 1)
  398.     HRESULT hr;
  399.     HANDLE  aHandles[3];
  400.     // Check the QuitEvent handle:
  401.     if (! m_hQuitEvent) {
  402.         ASSERT(m_hQuitEvent);
  403.         return 1;
  404.     }
  405.     // Attempt to create the enqueue event:
  406.     HANDLE hEnqueueEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
  407.     if (! hEnqueueEvent) {
  408.         ASSERT(hEnqueueEvent);
  409.         return 1;
  410.     }
  411.     
  412.     // Ask the incoming message queue to set the enqueue event on every enqueue.
  413.     m_OutgoingMsgQ.SetNotificationEvent (hEnqueueEvent);
  414.     aHandles[0] = m_hRecvEvent;
  415.     aHandles[1] = hEnqueueEvent;
  416.     aHandles[2] = m_hQuitEvent;
  417.     SetPriority (COMM_MANAGER_THREAD_PRIORITY);
  418.     while (! m_bEndThread) {
  419.         DWORD nResult = WaitForMultipleObjects(3, aHandles, FALSE, INFINITE);
  420.         switch (nResult) {
  421.         case WAIT_OBJECT_0: // new message arrived (from the network):
  422.             {
  423.                 DWORD dwMsgCount = 0;
  424.                 DWORD dwHostMsgCount = 0;
  425.                 // Get messages for both players:
  426.                 hr = m_pIDP->GetMessageCount(m_idPlayer, &dwMsgCount);
  427.                 if FAILED(hr)
  428.                     break;
  429.                 if (m_fIsHost)
  430.                 {   // Also host message may exist
  431.                     hr = m_pIDP->GetMessageCount(m_idHost, &dwHostMsgCount);
  432.                     if FAILED(hr)
  433.                         break;
  434.                 }
  435.                 for (UINT i = 0; i < dwHostMsgCount + dwMsgCount; i++) {
  436.                     m_Message.m_dwLength = CCommMessage::MAX_BUFFER_LENGTH;
  437.                     hr = m_pIDP->Receive(&m_idFrom, &m_idTo, DPRECEIVE_ALL,
  438.                         m_Message.m_aBuffer, &m_Message.m_dwLength);
  439.                     if SUCCEEDED(hr) 
  440.                     {
  441.                         if (m_idTo == m_idPlayer)
  442.                         {   // New message for the player (client)
  443.                             HandleClientMessage ();
  444.                             INC_TOTAL_CLIENT_MESSAGES_RECEIVED;
  445.                         }
  446.                         else if (m_fIsHost && m_idTo == m_idHost)
  447.                         {   // New message for the host
  448.                             HandleHostMessage();
  449.                             INC_TOTAL_HOST_MESSAGES_RECEIVED;
  450.                         }
  451.                         else
  452.                             ASSERT (FALSE);
  453.                         INC_TOTAL_BYTES_RECEIVED (m_Message.m_dwLength);
  454.                         UPDATE_BIGGEST_PACKET_RECEIVED (m_Message.m_dwLength);
  455.                         UPDATE_SMALLEST_PACKET_RECEIVED(m_Message.m_dwLength);
  456.                         UPDATE_RECEIVE;
  457.                     }   // End of successful message received
  458.                     else
  459.                     {
  460.                         INC_RECEIVE_ERRORS;
  461.                     }
  462.                 } 
  463.             }
  464.             break;
  465.         case WAIT_OBJECT_1: // new message to send (from the outgoing queue):
  466.             {   
  467.                 CMessage msg;
  468.                 // Empty outgoing queue:
  469.                 for (int count = m_OutgoingMsgQ.GetQueueSize(); count > 0; count--)
  470.                 {
  471.                     // Get uncompressed message:
  472.                     if (! m_OutgoingMsgQ.Dequeue(msg))  
  473.                         // TODO: although we were signaled after enqueue, we still encounter
  474.                         // empty Q occasionly.
  475.                         break;
  476.                     // Send message over net to game host:
  477.                     m_Message.m_dwLength = msg.GetBuffer(m_Message.m_aBuffer);
  478. #if defined (NET_MON_SYNC) || defined (NET_MON_GAME)
  479.                     if (msg.GetType() != CMessage::CHECK_SUM)   // Not checksum message
  480.                     {
  481.                         NET_GAME_TRACE (("Client sending to host %s, size=%d (msg time = %d), game time = %d",
  482.                                         msg.GetName(), m_Message.m_dwLength, msg.GetTime(), 
  483.                                         TANKS_APP->m_gTimer.GetLocalTime()))
  484.                     }
  485.                     else
  486.                     {
  487.                         NET_SYNC_TRACE (("Client sending checksum to host, size=%d (game time = %d)",
  488.                                          m_Message.m_dwLength, msg.GetTime()));
  489.                     }
  490. #endif // defined (NET_MON_SYNC) || defined (NET_MON_GAME)
  491.                     HRESULT hr = m_pIDP->Send( m_idPlayer,
  492.                                                m_idHost, 
  493.                                                DPSEND_GUARANTEED,
  494.                                                m_Message.m_aBuffer,
  495.                                                m_Message.m_dwLength);
  496.                     if (!SUCCEEDED(hr))
  497.                     {   // Can't send message
  498.                         TRACE ("tERROR: Client cannot send message to host !!!n");
  499.                         INC_CLIENT_SEND_ERRORS;
  500.                     }
  501.                     else
  502.                     {   // Message sent successfully
  503.                         INC_TOTAL_BYTES_SENT (m_Message.m_dwLength);
  504.                         INC_TOTAL_CLIENT_MESSAGES_SENT;
  505.                         UPDATE_BIGGEST_PACKET_SENT (m_Message.m_dwLength);
  506.                         UPDATE_SMALLEST_PACKET_SENT (m_Message.m_dwLength);
  507.                         UPDATE_SEND;
  508.                     }
  509.                 }   // End while
  510.             }   // End case block
  511.             break;
  512.         case WAIT_OBJECT_2: // we should quit thread:
  513.             m_bEndThread = TRUE;
  514.             break;
  515.         case WAIT_FAILED: {
  516.             CString strErr;
  517.             strErr.Format("CommManager thread failed with error code: %08X",
  518.                 GetLastError());
  519.             AfxMessageBox(strErr);
  520.                           }
  521.         default:
  522.             m_bEndThread = TRUE;
  523.         }
  524.     }   // end of major comm. manager loop
  525.     // Ask the incoming message queue to stop setting the enqueue event on every enqueue.
  526.     m_OutgoingMsgQ.SetNotificationEvent (NULL);
  527.     // Terminate the enqueue event
  528.     CloseHandle (hEnqueueEvent);
  529.     return 0;
  530. }
  531. void
  532. CCommManager::HandleClientMessage()
  533. {
  534.     // First lets take care of system msgs, such as NEWPLAYER..
  535.     if (DPID_SYSMSG==m_idFrom) 
  536.     {
  537.         switch (((DPMSG_GENERIC*)&m_Message.m_aBuffer[0])->dwType) 
  538.         {
  539.             case DPSYS_CREATEPLAYERORGROUP: // a new player is joining
  540.                 // Store player name in the array (where ?)
  541.                 NET_SYS_TRACE (("Client got Create player or group (player ID = %d)",
  542.                     ((DPMSG_CREATEPLAYERORGROUP*)m_Message.m_aBuffer)->dpId));
  543.                 break;
  544.             case DPSYS_DESTROYPLAYERORGROUP:
  545.                 if (((DPMSG_DESTROYPLAYERORGROUP*)&m_Message.m_aBuffer[0])->dpId == m_idHost)   
  546.                 {   // this means the host closed the session
  547.                     DisplayMessageAndEndGame (IDS_LOST_SEVER_CONNECTION);
  548.                 }
  549.                 NET_SYS_TRACE (("Client got Destroy player or group (player id = %d",
  550.                                 ((DPMSG_DESTROYPLAYERORGROUP*)&m_Message.m_aBuffer[0])->dpId));
  551.                 break;
  552.             case DPSYS_SESSIONLOST: // We can't proceed playing - stop game:
  553.                 DisplayMessageAndEndGame (IDS_LOST_SEVER_CONNECTION);
  554.                 NET_SYS_TRACE (("Client got session lost"));
  555.                 break;
  556.             default:
  557.                 // Disregard other DirectPlay system messages
  558.                 NET_SYS_TRACE (("Client got unhandled message (Msg ID = %d)", 
  559.                                 ((DPMSG_GENERIC*)&m_Message.m_aBuffer[0])->dwType));
  560.                 break;
  561.         }
  562.         return;
  563.     }
  564.     m_pClient->HandleMessage();
  565. }
  566.     
  567. void
  568. CCommManager::HandleHostMessage()
  569. {
  570.     // First lets take care of system msgs, such as NEWPLAYER..
  571.     if (DPID_SYSMSG==m_idFrom) {
  572.         switch (((DPMSG_GENERIC*)&m_Message.m_aBuffer[0])->dwType) 
  573.         {
  574.             case DPSYS_CREATEPLAYERORGROUP: 
  575.                 { // a new player is joining
  576.                     DPID idFrom = ((DPMSG_CREATEPLAYERORGROUP*)m_Message.m_aBuffer)->dpId;
  577.                     NET_SYS_TRACE (("Server got Create player or group (player ID = %d)",idFrom));
  578.                     if (idFrom==m_idHost)   // We received new player about our own object
  579.                         return;
  580.                     // Respond with ACK:
  581.                     m_Message.m_aBuffer[0] = ACK;
  582.                     m_Message.m_dwLength = 1;
  583.                     SendAsHost(idFrom); // As host
  584.                 }
  585.                 break;
  586.             case DPSYS_DESTROYPLAYERORGROUP: 
  587.                 { // a player just left the session:
  588.                     DPID idFrom = ((DPMSG_CREATEPLAYERORGROUP*)m_Message.m_aBuffer)->dpId;
  589.                     NET_SYS_TRACE (("Server got Destroy player or group (player ID = %d)",idFrom));
  590.                     // Notify other players ?
  591.                     // Remove the ID from the array:
  592.                     m_CSHost.Lock();
  593.                     m_pHost->RemovePlayerFromArray(idFrom);
  594.                     m_CSHost.Unlock();
  595.                 }
  596.                 break;
  597.             case DPSYS_SESSIONLOST: // We can't proceed playing - stop game:
  598.                 NET_SYS_TRACE (("Server got Session lost"));
  599.                 DisplayMessageAndEndGame (IDS_LOST_SEVER_CONNECTION);
  600.                 break;
  601.             default:
  602.                 // Disregard other DirectPlay system messages
  603.                 NET_SYS_TRACE (("Server got unhandled message (Msg ID = %d)", 
  604.                                 ((DPMSG_GENERIC*)&m_Message.m_aBuffer[0])->dwType));
  605.                 break;
  606.         }
  607.         return;
  608.     }
  609.     m_CSHost.Lock();
  610.     m_pHost->HandleMessage( m_idFrom,               // Send the player ID of the sender
  611.                             m_idFrom == m_idPlayer);// If this is the local player, it's the judge
  612.     m_CSHost.Unlock();
  613. }
  614. void
  615. CCommManager::SendAsHost(DPID idTo)
  616. {
  617.     ASSERT (m_pIDP);
  618.     
  619.     // Always called by host:
  620.     NET_GAME_TRACE (("Host sending %s to player ID %d (size=%d) at time %lu, msg.time=%lu",
  621.                     CMessage::GetName (m_Message.m_aBuffer[0]),
  622.                     idTo,
  623.                     m_Message.m_dwLength,
  624.                     TANKS_APP->m_gTimer.GetLocalTime(),
  625.                     *((PDWORD)&m_Message.m_aBuffer[1])));
  626.     HRESULT hr = m_pIDP->Send( m_idHost,
  627.                                idTo, 
  628.                                DPSEND_GUARANTEED,
  629.                                m_Message.m_aBuffer,
  630.                                m_Message.m_dwLength);
  631.     if (!SUCCEEDED(hr))
  632.     {   // Can't send message
  633.         TRACE ("tERROR: Host cannot send message to client !!!n");
  634.         INC_HOST_SEND_ERRORS;
  635.     }
  636.     else
  637.     {   // Message sent successfully
  638.         INC_TOTAL_BYTES_SENT (m_Message.m_dwLength);
  639.         INC_TOTAL_HOST_MESSAGES_SENT;
  640.         UPDATE_BIGGEST_PACKET_SENT (m_Message.m_dwLength);
  641.         UPDATE_SMALLEST_PACKET_SENT (m_Message.m_dwLength);
  642.         UPDATE_SEND;
  643.     }
  644. }
  645. void 
  646. CCommManager::NotifyBonusEaten (UINT uTankID)
  647. {   
  648.     // This function is called by a tank object in this machine.
  649.     // It is called only if we're the host machine => the prime judge of the game.
  650.     // A tank eats a bonus and wishes to notify all the players (including himself)
  651.     // of this happy event.
  652.     ASSERT (IsHost());  // Only a tank on a host machine can issue this call
  653.     
  654.     CMessage::MessageData Params;
  655.         // Create bonus eating notification message:
  656.     Params.BonusParam.Type = BONUS_NONE; // Bonus is removed, not added
  657.     Params.BonusParam.LifeSpan = uTankID;
  658.         // Send it to the comm. managers outgoing queue - it'll spread to all from there.
  659.         // The host player will receive it and will spread it to all players.
  660.     TANKS_APP->m_gOutgoingMsgQueue.Enqueue (CMessage::ADD_BONUS, Params);
  661. }
  662. void 
  663. CCommManager::NotifyNewBonus (BonusType typ, DWORD ttl, CPoint &loc)
  664. {   
  665.     // This function is called by the game manager in the host machine.
  666.     // It is called only if we're the host machine => the prime judge of the game.
  667.     // It's time to tell all of the game managers (including the local one)
  668.     // that a new bonus is added.
  669.     ASSERT (IsHost());  // Only the game manager on a host machine can issue this call
  670.     
  671.     CMessage::MessageData Params;
  672.         // Create bonus eating notification message:
  673.     Params.BonusParam.Type = typ;
  674.     Params.BonusParam.LifeSpan = ttl / 1000;    // Convert ttl to seconds
  675.     Params.BonusParam.XPos = WORD(loc.x);
  676.     Params.BonusParam.YPos = WORD(loc.y);
  677.         // Send it to the comm. managers outgoing queue - it'll spread to all from there.
  678.         // The host player will receive it and will spread it to all players.
  679.     TANKS_APP->m_gOutgoingMsgQueue.Enqueue (CMessage::ADD_BONUS, Params);
  680. }
  681. void 
  682. CCommManager::NotifyExplodingTank (UINT uTankID)
  683. {   // This function is called by a tank object in this machine.
  684.     // It is called only if we're the host machine => the prime judge of the game.
  685.     // A tank reaches the conclusion he's dying now wishes to notify all the 
  686.     // players (including himself) of this sad event.
  687. //    ASSERT (IsHost());  // Only a tank on a host machine can issue this call
  688.     
  689.     CMessage::MessageData Params;
  690.         // Create tank removal notification message:
  691.     Params.TankRemoveParam.ID = BYTE(uTankID);
  692.     Params.TankRemoveParam.Explode = TRUE;
  693.         // Send it to the comm. managers outgoing queue - it'll spread to all from there
  694.         // The host player will receive it and will spread it to all players.
  695.     TANKS_APP->m_gOutgoingMsgQueue.Enqueue (CMessage::REMOVE_TANK, Params);
  696. }
  697. void 
  698. CCommManager::NotifyCheckSum (CMessage::MessageData &msgdata)
  699. {   // This function is called by a the game manager in this machine.
  700.     // It is called when the game manager wishes to send a game check sum
  701.     m_OutgoingMsgQ.Enqueue (CMessage::CHECK_SUM, msgdata);
  702.     // We take advantage of the fact that this method is called every sec.
  703.     // to trigger the zombie check:
  704.     if (m_pHost)
  705.         m_pHost->ChkForZombies();
  706. }
  707. void
  708. CCommManager::NotifyChatMsg (LPCSTR szMsg)
  709. {
  710.     CMessage::MessageData Params;
  711.     strcpy (Params.ChatParam.Msg, szMsg);
  712.     Params.ChatParam.TankID = BYTE(TANKS_APP->m_gGameManager.GetLocalTankID());
  713.     Params.ChatParam.idFrom = m_idPlayer;
  714.     m_OutgoingMsgQ.Enqueue (CMessage::CHAT, Params);
  715. }
  716. void 
  717. CCommManager::DisplayMessageAndEndGame (UINT uMsgID)
  718. {
  719.     if (m_bLostServerConnection)
  720.         return;
  721.     m_bLostServerConnection = TRUE;
  722.     if (0 != uMsgID)
  723.         AfxMessageBox (uMsgID, MB_OK | MB_ICONHAND);
  724.             // Simulate a user menu selection of "Stop Game":
  725.     ::PostMessage(TANKS_APP->m_pMainWnd->m_hWnd, WM_COMMAND, ID_STOP_GAME, 0);
  726. }
  727. BOOL 
  728. CCommManager::GetPlayerInfo (
  729.                              UINT ind, 
  730.                              BOOL &bIsJudge, 
  731.                              CString &cstrName, 
  732.                              DWORD &dwDuration, 
  733.                              DWORD &dwRTT)
  734. {
  735.     m_CSHost.Lock();
  736.     BOOL bRes = FALSE;
  737.     if (m_pHost)
  738.     {   // Server active
  739.         bRes = m_pHost->GetPlayerInfo (ind, dwDuration, dwRTT);
  740.         if (bRes)
  741.         {   // Successfully queried server
  742.             DWORD dwDPID = m_pHost->GetDirectPlayID (ind);
  743.             bIsJudge = (m_idPlayer == dwDPID);
  744.             char szBuf[1024];
  745.             DWORD dwSize = 1024;
  746.             HRESULT hres = m_pIDP->GetPlayerData (dwDPID, szBuf, &dwSize, 0);
  747.             cstrName = (DP_OK == hres) ? szBuf : "";
  748.         }
  749.     }
  750.     m_CSHost.Unlock();
  751.     return bRes;
  752. }
  753. void
  754. CCommManager::GetPlayerName (DPID id, CString& cstrName)
  755. {
  756.     m_CSHost.Lock();
  757.     char szBuf[256];
  758.     DWORD dwSize = 256;
  759.     HRESULT hres = m_pIDP->GetPlayerData (id, szBuf, &dwSize, 0);
  760.     cstrName = (DP_OK == hres && dwSize) ? szBuf : "Unknown";
  761.     m_CSHost.Unlock();
  762. }
  763. BOOL CCommManager::KillPlayer (UINT uTankID)
  764. {
  765.     BOOL bRes = FALSE;
  766.     m_CSHost.Lock();
  767.     if (m_pHost)
  768.     {
  769.         ASSERT (uTankID < MAX_TANKS);
  770.         NotifyExplodingTank (uTankID);
  771.         bRes = TRUE;
  772.     }
  773.     m_CSHost.Unlock();
  774.     return bRes;
  775. }
  776. #ifdef GATHER_NETWORK_STATS
  777. void 
  778. CCommManager::UpdateReceive ()
  779. {
  780.     DWORD dwCurTime = GetTickCount();
  781.     DWORD dwWindowSize = dwCurTime - m_dwLastSecTimeRecv;
  782.     if (dwWindowSize < 1000)
  783.         return;
  784.     m_dwLastSecTimeRecv = dwCurTime;
  785.     DWORD dwAmountInWindow = m_dwTotalBytesReceived - m_dwLastRecvTotal;
  786.     m_dwLastRecvTotal = m_dwTotalBytesReceived;
  787.     DWORD dwBPS = DWORD(double(dwAmountInWindow) * double(1000.0) / double(dwWindowSize));
  788.     m_dwMaxBytesReceivedInLastSecond = max (m_dwMaxBytesReceivedInLastSecond, dwBPS);
  789. }
  790. void 
  791. CCommManager::UpdateSend ()
  792. {
  793.     DWORD dwCurTime = GetTickCount();
  794.     DWORD dwWindowSize = dwCurTime - m_dwLastSecTimeSend;
  795.     if (dwWindowSize < 1000)
  796.         return;
  797.     m_dwLastSecTimeSend = dwCurTime;
  798.     DWORD dwAmountInWindow = m_dwTotalBytesSent - m_dwLastSentTotal;
  799.     m_dwLastSentTotal = m_dwTotalBytesSent;
  800.     DWORD dwBPS = DWORD(double(dwAmountInWindow) * double(1000.0) / double(dwWindowSize));
  801.     m_dwMaxBytesSentInLastSecond = max (m_dwMaxBytesSentInLastSecond, dwBPS);
  802. }
  803. void
  804. CCommManager::TraceNetworkStats ()
  805. {
  806.     TRACE ( "nnttt**** Network statistics ****n");
  807.     DWORD dwNow = GetTickCount();
  808.     DWORD dwTotTime = dwNow - m_dwSessionStartTime;
  809.     TRACE ( "Session time = %.3f secs.n", 
  810.                 double(dwTotTime) / 1000.0);
  811.     TRACE ( "Send:n");
  812.     DWORD dwTotMsgs = m_dwTotalClientMsgsSent + m_dwTotalHostMsgsSent;
  813.     TRACE ( "tTotal bytes sent = %d (biggest packet size = %d, smallest packet size = %d, avg size = %d)n",
  814.                 m_dwTotalBytesSent, 
  815.                 m_dwBiggestSentPacketSize, 
  816.                 m_dwSmallestSentPacketSize,
  817.                 dwTotMsgs ? DWORD(double(m_dwTotalBytesSent) / double(dwTotMsgs)) : 0);
  818.     TRACE ( "tTotal messages sent = %d (client messages = %d, host messages = %d)n",
  819.                 dwTotMsgs, 
  820.                 m_dwTotalClientMsgsSent, 
  821.                 m_dwTotalHostMsgsSent);
  822.     TRACE ( "tTotal send errors = %d (client errors = %d, host errors = %d)n",
  823.                 m_dwClientSendErrors + m_dwHostSendErrors, 
  824.                 m_dwClientSendErrors, 
  825.                 m_dwHostSendErrors);
  826.     TRACE ( "tMaximum bytes sent per second = %d (avg = %d)n",
  827.                 m_dwMaxBytesSentInLastSecond,
  828.                 dwTotTime ? DWORD(double(m_dwTotalBytesSent) / (double(dwTotTime) / double(1000.0))) : 0);
  829.     TRACE ( "Receive:n");
  830.     dwTotMsgs = m_dwTotalClientMsgsReceived + m_dwTotalHostMsgsReceived;
  831.     TRACE ( "tTotal bytes received = %d (biggest packet size = %d, smallest packet size = %d, avg size = %d)n",
  832.                 m_dwTotalBytesReceived, 
  833.                 m_dwBiggestReceivedPacketSize, 
  834.                 m_dwSmallestReceivedPacketSize,
  835.                 dwTotMsgs ? DWORD(double(m_dwTotalBytesReceived) / double(dwTotMsgs)) : 0);               
  836.     TRACE ( "tTotal messages received = %d (client messages = %d, host messages = %d)n",
  837.                 dwTotMsgs, 
  838.                 m_dwTotalClientMsgsReceived, 
  839.                 m_dwTotalHostMsgsReceived);
  840.     TRACE ( "tTotal receive errors = %dn",
  841.                 m_dwReceiveErrors);
  842.     TRACE ( "tMaximum bytes received per second = %d (avg = %d)nn",
  843.                 m_dwMaxBytesReceivedInLastSecond,
  844.                 dwTotTime ? DWORD(double(m_dwTotalBytesReceived) / (double(dwTotTime) / double(1000.0))) : 0);
  845. }
  846. #endif  // GATHER_NETWORK_STATS