home *** CD-ROM | disk | FTP | other *** search
/ Visual Basic Game Programming for Teens / VBGPFT.cdr / DirectX8 / dx8a_sdk.exe / samples / multimedia / directplay / datarelay / datarelay.cpp next >
Encoding:
C/C++ Source or Header  |  2000-11-04  |  64.6 KB  |  1,679 lines

  1. //----------------------------------------------------------------------------
  2. // File: DataRelay.cpp
  3. //
  4. // Desc: The main game file for the DataRelay sample.  It connects 
  5. //       players together with two dialog boxes to prompt users on the 
  6. //       connection settings to join or create a session. After the user 
  7. //       connects to a sesssion, the sample displays a multiplayer stage. 
  8. // 
  9. //       This sample uses DirectPlay to process high volumes of incoming 
  10. //       data on a seperate worker while avoiding copying the incoming packets
  11. //       by using the ReturnBuffer() call.  It also shows some basics of 
  12. //       thread synconization between the worker thread and the DirectPlay
  13. //       message handler thread pool.
  14. //
  15. // Copyright (c) 1999-2000 Microsoft Corp. All rights reserved.
  16. //-----------------------------------------------------------------------------
  17. #define STRICT
  18. #include <windows.h>
  19. #include <basetsd.h>
  20. #include <process.h>
  21. #include <mmsystem.h>
  22. #include <dxerr8.h>
  23. #include <dplay8.h>
  24. #include <dplobby8.h>
  25. #include <stdio.h>
  26. #include <assert.h>
  27. #include "NetConnect.h"
  28. #include "DXUtil.h"
  29. #include "resource.h"
  30.  
  31.  
  32.  
  33.  
  34. //-----------------------------------------------------------------------------
  35. // Player context locking defines
  36. //-----------------------------------------------------------------------------
  37. CRITICAL_SECTION g_csPlayerContext;
  38. #define PLAYER_LOCK()                   EnterCriticalSection( &g_csPlayerContext ); 
  39. #define PLAYER_ADDREF( pPlayerInfo )    if( pPlayerInfo ) pPlayerInfo->lRefCount++;
  40. #define PLAYER_RELEASE( pPlayerInfo )   if( pPlayerInfo ) { pPlayerInfo->lRefCount--; if( pPlayerInfo->lRefCount <= 0 ) SAFE_DELETE( pPlayerInfo ); } pPlayerInfo = NULL;
  41. #define PLAYER_UNLOCK()                 LeaveCriticalSection( &g_csPlayerContext );
  42.  
  43.  
  44. //-----------------------------------------------------------------------------
  45. // Defines, and constants
  46. //-----------------------------------------------------------------------------
  47. #define DPLAY_SAMPLE_KEY        TEXT("Software\\Microsoft\\DirectX DirectPlay Samples")
  48. #define MAX_PLAYER_NAME         14
  49. #define TIMERID_NETWORK         0
  50. #define TIMERID_STATS           1
  51. #define WM_APP_UPDATE_TARGETS   (WM_APP + 0)
  52. #define WM_APP_APPEND_TEXT      (WM_APP + 1)
  53.  
  54. // This GUID allows DirectPlay to find other instances of the same game on
  55. // the network.  So it must be unique for every game, and the same for 
  56. // every instance of that game.  // {BA214178-AAE6-4ea6-84E0-65CE36F84479}
  57. GUID g_guidApp = { 0xba214178, 0xaae6, 0x4ea6, { 0x84, 0xe0, 0x65, 0xce, 0x36, 0xf8, 0x44, 0x79 } };
  58.  
  59. struct APP_PLAYER_INFO
  60. {
  61.     LONG  lRefCount;                        // Ref count so we can cleanup when all threads 
  62.                                             // are done w/ this object
  63.     DPNID dpnidPlayer;                      // DPNID of player
  64.     TCHAR strPlayerName[MAX_PLAYER_NAME];  // Player name
  65.     DWORD dwFlags;                          // Player flags
  66.     APP_PLAYER_INFO* pNext;
  67.     APP_PLAYER_INFO* pPrev;
  68. };
  69.  
  70.  
  71.  
  72.  
  73. //-----------------------------------------------------------------------------
  74. // App specific DirectPlay messages and structures 
  75. //-----------------------------------------------------------------------------
  76. #define GAME_MSGID_GAMEPACKET       1
  77.  
  78. // Change compiler pack alignment to be BYTE aligned, and pop the current value
  79. #pragma pack( push, 1 )
  80.  
  81. struct GAMEMSG_GENERIC
  82. {
  83.     DWORD dwType;
  84.     DWORD dwPacketId;
  85. };
  86.  
  87. #define GAMEMSG_GENERIC_SIZE 8
  88.  
  89. struct GAMEMSG_DATA_512 : GAMEMSG_GENERIC
  90. {
  91.     BYTE pBuffer[512-GAMEMSG_GENERIC_SIZE];
  92. };
  93.  
  94. struct GAMEMSG_DATA_256 : GAMEMSG_GENERIC
  95. {
  96.     BYTE pBuffer[256-GAMEMSG_GENERIC_SIZE];
  97. };
  98.  
  99. struct GAMEMSG_DATA_128 : GAMEMSG_GENERIC
  100. {
  101.     BYTE pBuffer[128-GAMEMSG_GENERIC_SIZE];
  102. };
  103.  
  104. struct GAMEMSG_DATA_64 : GAMEMSG_GENERIC
  105. {
  106.     BYTE pBuffer[64-GAMEMSG_GENERIC_SIZE];
  107. };
  108.  
  109. struct GAMEMSG_DATA_32 : GAMEMSG_GENERIC
  110. {
  111.     BYTE pBuffer[32-GAMEMSG_GENERIC_SIZE];
  112. };
  113.  
  114. struct GAMEMSG_DATA_16 : GAMEMSG_GENERIC
  115. {
  116.     BYTE pBuffer[16-GAMEMSG_GENERIC_SIZE];
  117. };
  118.  
  119. #define DATA_TYPE_NETPACKET_RECIEVE       1
  120. #define DATA_TYPE_NETPACKET_SENT          2
  121. #define DATA_TYPE_NETPACKET_TIMEOUT       3
  122.  
  123. struct GAMEMSG_DATA_NODE
  124. {
  125.     GAMEMSG_GENERIC*    pDataMsg;
  126.     DWORD               dwPacketId;
  127.     APP_PLAYER_INFO*    pPlayerFrom; 
  128.     DWORD               dwReceiveDataSize;
  129.     DPNHANDLE           hBufferHandle;
  130.     DWORD               dwType;
  131.     GAMEMSG_DATA_NODE*  pNext;
  132.     GAMEMSG_DATA_NODE*  pPrev;
  133. };
  134.  
  135. // Pop the old pack alignment
  136. #pragma pack( pop )
  137.  
  138.  
  139.  
  140.  
  141. //-----------------------------------------------------------------------------
  142. // Global variables
  143. //-----------------------------------------------------------------------------
  144. TCHAR              g_strAppName[256]          = TEXT("DataRelay");
  145. IDirectPlay8Peer*  g_pDP                      = NULL;   // DirectPlay peer object
  146. CNetConnectWizard* g_pNetConnectWizard        = NULL;   // Connection wizard
  147. IDirectPlay8LobbiedApplication* g_pLobbiedApp = NULL;   // DirectPlay lobbied app 
  148. BOOL               g_bWasLobbyLaunched        = FALSE;   // TRUE if lobby launched
  149. HINSTANCE          g_hInst                    = NULL;   // HINST of app
  150. HWND               g_hDlg                     = NULL;   // HWND of main dialog
  151. HRESULT            g_hrDialog;                          // Exit code for app 
  152. TCHAR              g_strLocalPlayerName[MAX_PATH];      // Local player name
  153. TCHAR              g_strSessionName[MAX_PATH];          // Session name
  154. TCHAR              g_strPreferredProvider[MAX_PATH];    // Provider string
  155. HANDLE             g_hDPDataAvailEvent        = NULL;   // Signaled if there is data to process
  156. HANDLE             g_hShutdownEvent           = NULL;   // Signaled if shutting down
  157. DWORD              g_dwPacketId               = 0;      // Packet id counter
  158. APP_PLAYER_INFO*   g_pTargetPlayer            = NULL;   // The current target player
  159. BOOL               g_bSendingData             = FALSE;  // TRUE if sending data
  160. DPNID              g_dpnidLocalPlayer         = 0;      // DPNID of local player
  161. DWORD              g_dwDataRecieved           = 0;      // Amount of data recieved 
  162. DWORD              g_dwDataSent               = 0;      // Amount of data sent
  163. DWORD              g_dwSendTimeout            = 0;      // Length of send timeout
  164. DWORD              g_dwTimeBetweenSends       = 0;      // Time between packet sends
  165. DWORD              g_dwSendSize               = 0;      // Size in bytes of packet
  166. UINT               g_dwProcessNetDataThreadID = 0;      // Worker thread ID
  167. HANDLE             g_hProcessNetDataThread    = NULL;   // Worker thread handle
  168. APP_PLAYER_INFO    g_PlayerHead;                        // Linked list of players connected
  169. GAMEMSG_DATA_NODE  g_DataHead;                          // Linked list of data to process
  170. CRITICAL_SECTION   g_csPlayerList;                      // CS for g_PlayerHead
  171. CRITICAL_SECTION   g_csDataList;                        // CS for g_DataHead
  172. APP_PLAYER_INFO*   g_pConnInfoTargetPlayer    = NULL;   // The current target for Connection Info
  173.  
  174.  
  175.  
  176.  
  177. //-----------------------------------------------------------------------------
  178. // Function-prototypes
  179. //-----------------------------------------------------------------------------
  180. HRESULT WINAPI   DirectPlayMessageHandler( PVOID pvUserContext, DWORD dwMessageId, PVOID pMsgBuffer );
  181. HRESULT WINAPI   DirectPlayLobbyMessageHandler( PVOID pvUserContext, DWORD dwMessageId, PVOID pMsgBuffer );
  182. INT_PTR CALLBACK SampleDlgProc( HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam );
  183. UINT WINAPI     ProcessNetDataProc( LPVOID lpParameter );
  184. HRESULT InitDirectPlay();
  185. HRESULT OnInitDialog( HWND hDlg );
  186. VOID    FillTargetCombo( HWND hDlg );
  187. VOID    FillOneTimeCombos( HWND hDlg );
  188. HRESULT LinkPlayer( DPNID dpnid, APP_PLAYER_INFO** ppPlayerInfo );
  189. HRESULT SendNetworkData();
  190. HRESULT ProcessData();
  191. VOID    AppendTextToEditControl( HWND hDlg, TCHAR* strNewLogLine );
  192. VOID    ReadCombos( HWND hDlg );
  193. VOID    UpdateSendQueueInfo( HWND hDlg );
  194. VOID    UpdateStats( HWND hDlg );
  195.  
  196.  
  197.  
  198.  
  199. //-----------------------------------------------------------------------------
  200. // Name: WinMain()
  201. // Desc: Entry point for the application.  Since we use a simple dialog for 
  202. //       user interaction we don't need to pump messages.
  203. //-----------------------------------------------------------------------------
  204. INT APIENTRY WinMain( HINSTANCE hInst, HINSTANCE hPrevInst, 
  205.                       LPSTR pCmdLine, INT nCmdShow )
  206. {
  207.     HRESULT hr;
  208.     BOOL    bConnectSuccess     = FALSE;
  209.     HKEY    hDPlaySampleRegKey;
  210.  
  211.     // Create events and critical sections
  212.     g_hDPDataAvailEvent = CreateEvent( NULL, FALSE, FALSE, NULL ); 
  213.     g_hShutdownEvent = CreateEvent( NULL, FALSE, FALSE, NULL ); 
  214.     InitializeCriticalSection( &g_csPlayerList );
  215.     InitializeCriticalSection( &g_csDataList );
  216.     InitializeCriticalSection( &g_csPlayerContext );
  217.  
  218.     g_hInst = hInst;
  219.  
  220.     // Init circular linked list
  221.     ZeroMemory( &g_PlayerHead, sizeof(APP_PLAYER_INFO) );
  222.     g_PlayerHead.pNext = &g_PlayerHead;
  223.     g_PlayerHead.pPrev = &g_PlayerHead;
  224.     g_PlayerHead.dpnidPlayer = DPNID_ALL_PLAYERS_GROUP;
  225.     _tcscpy( g_PlayerHead.strPlayerName, TEXT("Everyone") );
  226.  
  227.     // Init circular linked list
  228.     ZeroMemory( &g_DataHead, sizeof(GAMEMSG_DATA_NODE) );
  229.     g_DataHead.pNext = &g_DataHead;
  230.     g_DataHead.pPrev = &g_DataHead;  
  231.  
  232.     // Read persistent state information from registry
  233.     RegCreateKeyEx( HKEY_CURRENT_USER, DPLAY_SAMPLE_KEY, 0, NULL,
  234.                     REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, 
  235.                     &hDPlaySampleRegKey, NULL );
  236.     DXUtil_ReadStringRegKey( hDPlaySampleRegKey, TEXT("Player Name"), 
  237.                              g_strLocalPlayerName, MAX_PATH, TEXT("TestPlayer") );
  238.     DXUtil_ReadStringRegKey( hDPlaySampleRegKey, TEXT("Session Name"), 
  239.                              g_strSessionName, MAX_PATH, TEXT("TestGame") );
  240.     DXUtil_ReadStringRegKey( hDPlaySampleRegKey, TEXT("Preferred Provider"), 
  241.                              g_strPreferredProvider, MAX_PATH, TEXT("DirectPlay8 TCP/IP Service Provider") );
  242.  
  243.     // Init COM so we can use CoCreateInstance
  244.     CoInitializeEx( NULL, COINIT_MULTITHREADED );
  245.     
  246.     // Create helper class
  247.     g_pNetConnectWizard = new CNetConnectWizard( hInst, g_strAppName, &g_guidApp );
  248.  
  249.     if( FAILED( hr = InitDirectPlay() ) )
  250.     {
  251.         DXTRACE_ERR( TEXT("InitDirectPlay"), hr );
  252.         MessageBox( NULL, TEXT("Failed initializing IDirectPlay8Peer. ")
  253.                     TEXT("The sample will now quit."),
  254.                     TEXT("DirectPlay Sample"), MB_OK | MB_ICONERROR );
  255.         return FALSE;
  256.     }
  257.  
  258.     // Check if we were launched from a lobby client
  259.     if( g_bWasLobbyLaunched && g_pNetConnectWizard->HaveConnectionSettingsFromLobby() )
  260.     {
  261.         // If were lobby launched then DPL_MSGID_CONNECT has already been
  262.         // handled, so we can just tell the wizard to connect to the lobby
  263.         // that has sent us a DPL_MSGID_CONNECT msg.
  264.         if( FAILED( hr = g_pNetConnectWizard->ConnectUsingLobbySettings() ) )
  265.         {
  266.             DXTRACE_ERR( TEXT("ConnectUsingLobbySettings"), hr );
  267.             MessageBox( NULL, TEXT("Failed to connect using lobby settings. ")
  268.                         TEXT("The sample will now quit."),
  269.                         TEXT("DirectPlay Sample"), MB_OK | MB_ICONERROR );
  270.  
  271.             bConnectSuccess = FALSE;
  272.         }
  273.         else
  274.         {
  275.             // Read information from g_pNetConnectWizard
  276.             _tcsncpy( g_strLocalPlayerName, g_pNetConnectWizard->GetPlayerName(), MAX_PLAYER_NAME );
  277.             g_strLocalPlayerName[MAX_PLAYER_NAME-1]=0;
  278.  
  279.             bConnectSuccess = TRUE; 
  280.         }
  281.     }
  282.     else
  283.     {
  284.         // If not lobby launched, prompt the user about the network 
  285.         // connection and which session they would like to join or 
  286.         // if they want to create a new one.
  287.  
  288.         // Setup connection wizard
  289.         g_pNetConnectWizard->SetPlayerName( g_strLocalPlayerName );
  290.         g_pNetConnectWizard->SetSessionName( g_strSessionName );
  291.         g_pNetConnectWizard->SetPreferredProvider( g_strPreferredProvider );
  292.  
  293.         // Do the connection wizard
  294.         hr = g_pNetConnectWizard->DoConnectWizard( FALSE );        
  295.         if( FAILED( hr ) ) 
  296.         {
  297.             DXTRACE_ERR( TEXT("DoConnectWizard"), hr );
  298.             MessageBox( NULL, TEXT("Mutliplayer connect failed. ")
  299.                         TEXT("The sample will now quit."),
  300.                         TEXT("DirectPlay Sample"), MB_OK | MB_ICONERROR );
  301.             bConnectSuccess = FALSE;
  302.         } 
  303.         else if( hr == NCW_S_QUIT ) 
  304.         {
  305.             // The user canceled the mutliplayer connect, so quit 
  306.             bConnectSuccess = FALSE;
  307.         }
  308.         else
  309.         {
  310.             bConnectSuccess = TRUE; 
  311.  
  312.             // Read information from g_pNetConnectWizard
  313.             _tcsncpy( g_strLocalPlayerName, g_pNetConnectWizard->GetPlayerName(), MAX_PLAYER_NAME );
  314.             g_strLocalPlayerName[MAX_PLAYER_NAME-1]=0;
  315.             _tcscpy( g_strSessionName, g_pNetConnectWizard->GetSessionName() );
  316.             _tcscpy( g_strPreferredProvider, g_pNetConnectWizard->GetPreferredProvider() );
  317.  
  318.             // Write information to the registry
  319.             DXUtil_WriteStringRegKey( hDPlaySampleRegKey, TEXT("Player Name"), g_strLocalPlayerName );
  320.             DXUtil_WriteStringRegKey( hDPlaySampleRegKey, TEXT("Session Name"), g_strSessionName );
  321.             DXUtil_WriteStringRegKey( hDPlaySampleRegKey, TEXT("Preferred Provider"), g_strPreferredProvider );
  322.         }
  323.     }
  324.  
  325.     if( bConnectSuccess )
  326.     {
  327.         // For this sample, we just start a simple dialog box game.
  328.         g_hrDialog = S_OK;
  329.         DialogBox( hInst, MAKEINTRESOURCE(IDD_MAIN_GAME), NULL, 
  330.                    (DLGPROC) SampleDlgProc );
  331.     }
  332.  
  333.     // Close down process data thread
  334.     SetEvent( g_hShutdownEvent );
  335.     WaitForSingleObject( g_hProcessNetDataThread, INFINITE );
  336.     CloseHandle( g_hProcessNetDataThread );
  337.  
  338.     if( FAILED( g_hrDialog ) )
  339.     {
  340.         if( g_hrDialog == DPNERR_CONNECTIONLOST )
  341.         {
  342.             MessageBox( NULL, TEXT("The DirectPlay session was lost. ")
  343.                         TEXT("The sample will now quit."),
  344.                         TEXT("DirectPlay Sample"), MB_OK | MB_ICONERROR );
  345.         }
  346.         else
  347.         {
  348.             DXTRACE_ERR( TEXT("DialogBox"), g_hrDialog );
  349.             MessageBox( NULL, TEXT("An error occured during the game. ")
  350.                         TEXT("The sample will now quit."),
  351.                         TEXT("DirectPlay Sample"), MB_OK | MB_ICONERROR );
  352.         }
  353.     }
  354.  
  355.     // Cleanup DirectPlay and helper classes
  356.     g_pNetConnectWizard->Shutdown();
  357.  
  358.     if( g_pDP )
  359.     {
  360.         g_pDP->Close(0);
  361.         SAFE_RELEASE( g_pDP );
  362.     }
  363.  
  364.     if( g_pLobbiedApp )
  365.     {
  366.         g_pLobbiedApp->Close( 0 );
  367.         SAFE_RELEASE( g_pLobbiedApp );
  368.     }    
  369.  
  370.     // Don't delete the wizard until we know that 
  371.     // DirectPlay is out of its message handlers.
  372.     // This will be true after Close() has been called. 
  373.     SAFE_DELETE( g_pNetConnectWizard );
  374.  
  375.     // Cleanup circular linked list, g_PlayerHead
  376.     {
  377.         EnterCriticalSection( &g_csPlayerList );
  378.         APP_PLAYER_INFO* pNode = g_PlayerHead.pNext;
  379.         APP_PLAYER_INFO* pDeleteNode;
  380.         while( pNode != &g_PlayerHead ) 
  381.         {
  382.             pDeleteNode = pNode;
  383.             pNode = pNode->pNext;
  384.             SAFE_DELETE( pDeleteNode );
  385.         }
  386.         LeaveCriticalSection( &g_csPlayerList );
  387.     }
  388.  
  389.     // Cleanup circular linked list, g_DataHead
  390.     {
  391.         EnterCriticalSection( &g_csDataList );
  392.         GAMEMSG_DATA_NODE* pNode = g_DataHead.pNext;
  393.         GAMEMSG_DATA_NODE* pDeleteNode;
  394.         while( pNode != &g_DataHead ) 
  395.         {
  396.             pDeleteNode = pNode;
  397.             pNode = pNode->pNext;
  398.             SAFE_DELETE( pDeleteNode );
  399.         }
  400.         LeaveCriticalSection( &g_csDataList );
  401.     }
  402.  
  403.     // Cleanup Win32 resources
  404.     RegCloseKey( hDPlaySampleRegKey );
  405.     CloseHandle( g_hShutdownEvent );
  406.     CloseHandle( g_hDPDataAvailEvent );
  407.     DeleteCriticalSection( &g_csPlayerList );
  408.     DeleteCriticalSection( &g_csDataList );
  409.     DeleteCriticalSection( &g_csPlayerContext );
  410.     CoUninitialize();
  411.  
  412.     return TRUE;
  413. }
  414.  
  415.  
  416.  
  417.  
  418. //-----------------------------------------------------------------------------
  419. // Name: InitDirectPlay()
  420. // Desc: 
  421. //-----------------------------------------------------------------------------
  422. HRESULT InitDirectPlay()
  423. {
  424.     DPNHANDLE hLobbyLaunchedConnection = NULL;
  425.     HRESULT hr;
  426.  
  427.     // Create IDirectPlay8Peer
  428.     if( FAILED( hr = CoCreateInstance( CLSID_DirectPlay8Peer, NULL, 
  429.                                        CLSCTX_INPROC_SERVER,
  430.                                        IID_IDirectPlay8Peer, 
  431.                                        (LPVOID*) &g_pDP ) ) )
  432.         return DXTRACE_ERR( TEXT("CoCreateInstance"), hr );
  433.  
  434.     // Create IDirectPlay8LobbiedApplication
  435.     if( FAILED( hr = CoCreateInstance( CLSID_DirectPlay8LobbiedApplication, NULL, 
  436.                                        CLSCTX_INPROC_SERVER,
  437.                                        IID_IDirectPlay8LobbiedApplication, 
  438.                                        (LPVOID*) &g_pLobbiedApp ) ) )
  439.         return DXTRACE_ERR( TEXT("CoCreateInstance"), hr );
  440.  
  441.     // Init the helper class, now that g_pDP and g_pLobbiedApp are valid
  442.     g_pNetConnectWizard->Init( g_pDP, g_pLobbiedApp );
  443.  
  444.     // Init IDirectPlay8Peer
  445.     if( FAILED( hr = g_pDP->Initialize( NULL, DirectPlayMessageHandler, 0 ) ) )
  446.         return DXTRACE_ERR( TEXT("Initialize"), hr );
  447.  
  448.     // Init IDirectPlay8LobbiedApplication.  Before this Initialize() returns 
  449.     // a DPL_MSGID_CONNECT msg may come in to the DirectPlayLobbyMessageHandler 
  450.     // so be prepared ahead of time.
  451.     if( FAILED( hr = g_pLobbiedApp->Initialize( NULL, DirectPlayLobbyMessageHandler, 
  452.                                                 &hLobbyLaunchedConnection, 0 ) ) )
  453.         return DXTRACE_ERR( TEXT("Initialize"), hr );
  454.  
  455.     // IDirectPlay8LobbiedApplication::Initialize returns a handle to a connnection
  456.     // if we have been lobby launced.  Initialize is guanteeded to return after 
  457.     // the DPL_MSGID_CONNECT msg has been processed.  So unless a we are expected 
  458.     // multiple lobby connections, we do not need to remember the lobby connection
  459.     // handle since it will be recorded upon the DPL_MSGID_CONNECT msg.
  460.     g_bWasLobbyLaunched = ( hLobbyLaunchedConnection != NULL );
  461.  
  462.     return S_OK;
  463. }
  464.  
  465.  
  466.  
  467.  
  468. //-----------------------------------------------------------------------------
  469. // Name: SampleDlgProc()
  470. // Desc: Handles dialog messages
  471. //-----------------------------------------------------------------------------
  472. INT_PTR CALLBACK SampleDlgProc( HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam )
  473. {
  474.     switch( msg ) 
  475.     {
  476.         case WM_INITDIALOG:
  477.         {
  478.             g_hDlg = hDlg;
  479.             if( FAILED( g_hrDialog = OnInitDialog( hDlg ) ) )
  480.             {
  481.                 DXTRACE_ERR( TEXT("OnInitDialog"), g_hrDialog );
  482.                 EndDialog( hDlg, 0 );
  483.             }
  484.  
  485.             break;
  486.         }
  487.  
  488.         case WM_APP_UPDATE_TARGETS:
  489.         {
  490.             // Display the player names in the UI
  491.             CheckDlgButton( hDlg, IDC_SEND_READY, (g_bSendingData) ? BST_CHECKED : BST_UNCHECKED );
  492.             PostMessage( hDlg, WM_COMMAND, IDC_SEND_READY, 0 );
  493.             FillTargetCombo( g_hDlg );
  494.             ReadCombos( g_hDlg );
  495.             break;
  496.         }
  497.  
  498.         case WM_APP_APPEND_TEXT:
  499.         {
  500.             // Append a string to the edit control
  501.             TCHAR* strNewLogLine = (TCHAR*) wParam;
  502.             AppendTextToEditControl( g_hDlg, strNewLogLine );
  503.             SAFE_DELETE( strNewLogLine );
  504.             break;
  505.         }
  506.  
  507.         case WM_COMMAND:
  508.         {
  509.             switch( LOWORD(wParam) )
  510.             {
  511.                 case IDC_SEND_READY:
  512.                     g_bSendingData = (IsDlgButtonChecked( hDlg, IDC_SEND_READY ) == BST_CHECKED );
  513.                     if( g_bSendingData )
  514.                     {
  515.                         SetTimer( g_hDlg, TIMERID_NETWORK, g_dwTimeBetweenSends, NULL );
  516.                         SetDlgItemText( hDlg, IDC_SEND_READY, TEXT("Sending...") );
  517.                     }
  518.                     else
  519.                     {
  520.                         KillTimer( g_hDlg, TIMERID_NETWORK );
  521.                         SetDlgItemText( hDlg, IDC_SEND_READY, TEXT("Push to Send") );
  522.                     }
  523.                     break;
  524.  
  525.                 case IDCANCEL:
  526.                     g_hrDialog = S_OK;
  527.                     EndDialog( hDlg, 0 );
  528.                     return TRUE;
  529.  
  530.                 case IDC_SEND_SIZE_COMBO:
  531.                 case IDC_SEND_RATE_COMBO:
  532.                 case IDC_SEND_TARGET_COMBO:
  533.                 case IDC_CONNINFO_COMBO:
  534.                     if( HIWORD(wParam) == CBN_SELENDOK )
  535.                         ReadCombos( hDlg );
  536.                     break;
  537.             }
  538.             break;
  539.         }
  540.  
  541.         case WM_TIMER:
  542.         {
  543.             switch( wParam )
  544.             {
  545.                 case TIMERID_NETWORK:
  546.                 {
  547.                     // Send network data
  548.                     if( FAILED( g_hrDialog = SendNetworkData() ) )
  549.                     {
  550.                         DXTRACE_ERR( TEXT("SendNetworkData"), g_hrDialog );
  551.                         EndDialog( hDlg, 0 );
  552.                     }
  553.                     break;
  554.                 }
  555.                 
  556.                 case TIMERID_STATS:
  557.                 {
  558.                     UpdateStats( hDlg );
  559.                     break;
  560.                 }
  561.             }
  562.             break;
  563.         }
  564.     }
  565.  
  566.     return FALSE; // Didn't handle message
  567. }
  568.  
  569.  
  570.  
  571.  
  572. //-----------------------------------------------------------------------------
  573. // Name: OnInitDialog()
  574. // Desc: Inits the dialog.
  575. //-----------------------------------------------------------------------------
  576. HRESULT OnInitDialog( HWND hDlg )
  577. {
  578.     // Load and set the icon
  579.     HICON hIcon = LoadIcon( g_hInst, MAKEINTRESOURCE( IDI_MAIN ) );
  580.     SendMessage( hDlg, WM_SETICON, ICON_BIG,   (LPARAM) hIcon );  // Set big icon
  581.     SendMessage( hDlg, WM_SETICON, ICON_SMALL, (LPARAM) hIcon );  // Set small icon
  582.  
  583.     // Display local player's name
  584.     SetDlgItemText( hDlg, IDC_PLAYER_NAME, g_strLocalPlayerName );
  585.  
  586.     // Start a thread to process the network data
  587.     g_hProcessNetDataThread = (HANDLE) _beginthreadex( NULL, 0, ProcessNetDataProc, 
  588.                                                        hDlg, 0, &g_dwProcessNetDataThreadID );
  589.  
  590.     // Update the dialog box
  591.     SendMessage( hDlg, WM_APP_UPDATE_TARGETS, 0, 0 );
  592.     FillOneTimeCombos( hDlg );
  593.     ReadCombos( hDlg );
  594.  
  595.     // Update the stats every second
  596.     SetTimer( hDlg, TIMERID_STATS, 1000, NULL );
  597.  
  598.     if( g_pNetConnectWizard->IsHostPlayer() )
  599.         SetWindowText( hDlg, TEXT("DataRelay (Host)") );
  600.     else
  601.         SetWindowText( hDlg, TEXT("DataRelay") );
  602.  
  603.     return S_OK;
  604. }
  605.  
  606.  
  607.  
  608.  
  609. //-----------------------------------------------------------------------------
  610. // Name: FillTargetCombo()
  611. // Desc: Fills the target combo with all of the players
  612. //-----------------------------------------------------------------------------
  613. VOID FillTargetCombo( HWND hDlg )
  614. {
  615.     DWORD    dwNumberOfActivePlayers = 0;
  616.     int      nIndex;
  617.     int      nCurSelect;
  618.     int      nNewSelect = 0;
  619.     LONG_PTR lCurItemData; 
  620.     HWND     hTargetCombo;
  621.     HWND     hInfoTargetCombo;
  622.  
  623.     if( hDlg == NULL )
  624.         return;
  625.  
  626.     hTargetCombo = GetDlgItem( hDlg, IDC_SEND_TARGET_COMBO );
  627.     if( hTargetCombo == NULL )
  628.         return;
  629.  
  630.     nCurSelect   = (int)SendMessage( hTargetCombo, CB_GETCURSEL, 0, 0 );
  631.     lCurItemData = (LONG_PTR)SendMessage( hTargetCombo, CB_GETITEMDATA, nCurSelect, 0 );
  632.  
  633.     // Clear combo box
  634.     SendMessage( hTargetCombo, CB_RESETCONTENT, 0, 0 );
  635.  
  636.     // Add "everyone"
  637.     nIndex = (int)SendMessage( hTargetCombo, CB_ADDSTRING, 0, (LPARAM) TEXT("Everyone") );
  638.     SendMessage( hTargetCombo, CB_SETITEMDATA, nIndex, (LPARAM) &g_PlayerHead );
  639.  
  640.     // Enter the player list CS, because we are about to read shared memory, and 
  641.     // the data may be changed concurrently from the DirectPlay
  642.     // message handler threads
  643.     EnterCriticalSection( &g_csPlayerList );
  644.  
  645.     // Add each player
  646.     APP_PLAYER_INFO* pNode = g_PlayerHead.pNext;
  647.     while( pNode != &g_PlayerHead ) 
  648.     {
  649.         if( (pNode->dwFlags & DPNPLAYER_LOCAL) == 0 )
  650.         {
  651.             nIndex = (int)SendMessage( hTargetCombo, CB_ADDSTRING, 0, (LPARAM) pNode->strPlayerName );
  652.             SendMessage( hTargetCombo, CB_SETITEMDATA, nIndex, (LPARAM) pNode );
  653.             if( lCurItemData == (LONG_PTR) pNode )
  654.                 nNewSelect = nIndex;
  655.         }
  656.  
  657.         dwNumberOfActivePlayers++;
  658.         pNode = pNode->pNext;
  659.     }
  660.  
  661.     LeaveCriticalSection( &g_csPlayerList );
  662.      
  663.     SendMessage( hTargetCombo, CB_SETCURSEL, nNewSelect, 0 );  
  664.  
  665.     // Update the number of players in game counter
  666.     TCHAR strNumberPlayers[32];
  667.     wsprintf( strNumberPlayers, TEXT("%d"), dwNumberOfActivePlayers );
  668.     SetDlgItemText( hDlg, IDC_NUM_PLAYERS, strNumberPlayers );
  669.  
  670.     if( dwNumberOfActivePlayers > 1 )
  671.     {
  672.         EnableWindow( GetDlgItem( hDlg, IDC_SEND_READY ), TRUE );
  673.     }
  674.     else
  675.     {
  676.         EnableWindow( GetDlgItem( hDlg, IDC_SEND_READY ), FALSE );
  677.         CheckDlgButton( hDlg, IDC_SEND_READY, BST_UNCHECKED );
  678.         SetDlgItemText( hDlg, IDC_SEND_READY, TEXT("Push to Send") );
  679.     }
  680.  
  681.     // Populate the connection info box    
  682.     hInfoTargetCombo = GetDlgItem( hDlg, IDC_CONNINFO_COMBO );
  683.         
  684.     nCurSelect   = (int)SendMessage( hInfoTargetCombo, CB_GETCURSEL, 0, 0 );
  685.     lCurItemData = (LONG_PTR)SendMessage( hInfoTargetCombo, CB_GETITEMDATA, nCurSelect, 0 );
  686.     
  687.     // Clear combo box
  688.     SendMessage( hInfoTargetCombo, CB_RESETCONTENT, 0, 0 );
  689.     
  690.     // Add "none"
  691.     nIndex = (int)SendMessage( hInfoTargetCombo, CB_ADDSTRING, 0, (LPARAM) "None" );
  692.     SendMessage( hInfoTargetCombo, CB_SETITEMDATA, nIndex, (LPARAM) &g_PlayerHead );
  693.  
  694.     // Enter the player list CS, because we are about to read shared memory, and 
  695.     // the data may be changed concurrently from the DirectPlay
  696.     // message handler threads
  697.     EnterCriticalSection( &g_csPlayerList );
  698.  
  699.     // Add each player    
  700.     pNode = g_PlayerHead.pNext;
  701.     nNewSelect = 0;
  702.     while( pNode != &g_PlayerHead ) 
  703.     {
  704.         if( (pNode->dwFlags & DPNPLAYER_LOCAL) == 0 )
  705.         {
  706.             nIndex = (int)SendMessage( hInfoTargetCombo, CB_ADDSTRING, 0, (LPARAM) pNode->strPlayerName );
  707.             SendMessage( hInfoTargetCombo, CB_SETITEMDATA, nIndex, (LPARAM) pNode );
  708.             if( lCurItemData == (LONG_PTR) pNode )
  709.                 nNewSelect = nIndex;
  710.         }
  711.  
  712.         dwNumberOfActivePlayers++;
  713.         pNode = pNode->pNext;
  714.     }
  715.  
  716.     LeaveCriticalSection( &g_csPlayerList );
  717.      
  718.     SendMessage( hInfoTargetCombo, CB_SETCURSEL, nNewSelect, 0 );  
  719. }
  720.  
  721.  
  722.  
  723.  
  724. //-----------------------------------------------------------------------------
  725. // Name: FillOneTimeCombos()
  726. // Desc: Fill the unchanging UI combos box
  727. //-----------------------------------------------------------------------------
  728. VOID FillOneTimeCombos( HWND hDlg )
  729. {
  730.     if( hDlg == NULL )
  731.         return;
  732.  
  733.     HWND hRateCombo = GetDlgItem( hDlg, IDC_SEND_RATE_COMBO );
  734.     SendMessage( hRateCombo, WM_SETREDRAW, FALSE, 0 );
  735.     SendMessage( hRateCombo, CB_RESETCONTENT, 0, 0 );
  736.     SendMessage( hRateCombo, CB_ADDSTRING, 0, (LPARAM) TEXT("1000") );
  737.     SendMessage( hRateCombo, CB_ADDSTRING, 0, (LPARAM) TEXT("500") );
  738.     SendMessage( hRateCombo, CB_ADDSTRING, 0, (LPARAM) TEXT("250") );
  739.     SendMessage( hRateCombo, CB_ADDSTRING, 0, (LPARAM) TEXT("100") );
  740.     SendMessage( hRateCombo, CB_ADDSTRING, 0, (LPARAM) TEXT("50") );
  741.     SendMessage( hRateCombo, CB_ADDSTRING, 0, (LPARAM) TEXT("0") );
  742.     SendMessage( hRateCombo, WM_SETREDRAW, TRUE, 0 );
  743.     SendMessage( hRateCombo, CB_SETCURSEL, 0, 0 );  
  744.     
  745.     HWND hSizeCombo = GetDlgItem( hDlg, IDC_SEND_SIZE_COMBO );
  746.     SendMessage( hSizeCombo, WM_SETREDRAW, FALSE, 0 );
  747.     SendMessage( hSizeCombo, CB_RESETCONTENT, 0, 0 );
  748.     SendMessage( hSizeCombo, CB_ADDSTRING, 0, (LPARAM) TEXT("512") );
  749.     SendMessage( hSizeCombo, CB_ADDSTRING, 0, (LPARAM) TEXT("256") );
  750.     SendMessage( hSizeCombo, CB_ADDSTRING, 0, (LPARAM) TEXT("128") );
  751.     SendMessage( hSizeCombo, CB_ADDSTRING, 0, (LPARAM) TEXT("64") );
  752.     SendMessage( hSizeCombo, CB_ADDSTRING, 0, (LPARAM) TEXT("32") );
  753.     SendMessage( hSizeCombo, CB_ADDSTRING, 0, (LPARAM) TEXT("16") );
  754.     SendMessage( hSizeCombo, WM_SETREDRAW, TRUE, 0 );
  755.     SendMessage( hSizeCombo, CB_SETCURSEL, 0, 0 );  
  756.  
  757.     HWND hTimeoutCombo = GetDlgItem( hDlg, IDC_SEND_TIMEOUT_COMBO );
  758.     SendMessage( hTimeoutCombo, WM_SETREDRAW, FALSE, 0 );
  759.     SendMessage( hTimeoutCombo, CB_RESETCONTENT, 0, 0 );
  760.     SendMessage( hTimeoutCombo, CB_ADDSTRING, 0, (LPARAM) TEXT("5") );
  761.     SendMessage( hTimeoutCombo, CB_ADDSTRING, 0, (LPARAM) TEXT("10") );
  762.     SendMessage( hTimeoutCombo, CB_ADDSTRING, 0, (LPARAM) TEXT("20") );
  763.     SendMessage( hTimeoutCombo, CB_ADDSTRING, 0, (LPARAM) TEXT("50") );
  764.     SendMessage( hTimeoutCombo, CB_ADDSTRING, 0, (LPARAM) TEXT("100") );
  765.     SendMessage( hTimeoutCombo, CB_ADDSTRING, 0, (LPARAM) TEXT("250") );
  766.     SendMessage( hTimeoutCombo, CB_ADDSTRING, 0, (LPARAM) TEXT("500") );
  767.     SendMessage( hTimeoutCombo, WM_SETREDRAW, TRUE, 0 );
  768.     SendMessage( hTimeoutCombo, CB_SETCURSEL, 2, 0 );  
  769. }
  770.  
  771.  
  772.  
  773.  
  774. //-----------------------------------------------------------------------------
  775. // Name: ReadCombos()
  776. // Desc: Reads the state of the combos and updates the global vars
  777. //-----------------------------------------------------------------------------
  778. VOID ReadCombos( HWND hDlg )
  779. {
  780.     TCHAR strText[128];
  781.     int nCurSelect;
  782.  
  783.     // Get target player
  784.     HWND hTargetCombo  = GetDlgItem( hDlg, IDC_SEND_TARGET_COMBO );
  785.     nCurSelect = (int)SendMessage( hTargetCombo, CB_GETCURSEL, 0, 0 );
  786.     if( nCurSelect != CB_ERR ) 
  787.     {
  788.         g_pTargetPlayer = (APP_PLAYER_INFO*) SendMessage( hTargetCombo, CB_GETITEMDATA, 
  789.                                                           nCurSelect, 0 );
  790.     }
  791.  
  792.     // Get rate (in ms)
  793.     HWND hRateCombo  = GetDlgItem( hDlg, IDC_SEND_RATE_COMBO );
  794.     nCurSelect = (int)SendMessage( hRateCombo, CB_GETCURSEL, 0, 0 );
  795.     if( nCurSelect != CB_ERR ) 
  796.     {
  797.         SendMessage( hRateCombo, CB_GETLBTEXT, nCurSelect, (LPARAM) strText );
  798.         g_dwTimeBetweenSends = _ttoi( strText );
  799.         KillTimer( g_hDlg, TIMERID_NETWORK );
  800.         SetTimer( g_hDlg, TIMERID_NETWORK, g_dwTimeBetweenSends, NULL );
  801.     }
  802.  
  803.     // Get size (in bytes)
  804.     HWND hSizeCombo = GetDlgItem( hDlg, IDC_SEND_SIZE_COMBO );
  805.     nCurSelect = (int)SendMessage( hSizeCombo, CB_GETCURSEL, 0, 0 );
  806.     if( nCurSelect != CB_ERR ) 
  807.     {
  808.         SendMessage( hSizeCombo, CB_GETLBTEXT, nCurSelect, (LPARAM) strText );
  809.         g_dwSendSize = _ttoi( strText );
  810.     }
  811.  
  812.     // Get timeout (in ms)
  813.     HWND hTimeoutCombo = GetDlgItem( hDlg, IDC_SEND_TIMEOUT_COMBO );
  814.     nCurSelect = (int)SendMessage( hTimeoutCombo, CB_GETCURSEL, 0, 0 );
  815.     if( nCurSelect != CB_ERR ) 
  816.     {
  817.         SendMessage( hTimeoutCombo, CB_GETLBTEXT, nCurSelect, (LPARAM) strText );
  818.         g_dwSendTimeout = _ttoi( strText );
  819.     }
  820.  
  821.     // Get the ConnectionInfo Target player
  822.     HWND hConnTargetCombo  = GetDlgItem( hDlg, IDC_CONNINFO_COMBO );
  823.     nCurSelect = (int)SendMessage( hConnTargetCombo, CB_GETCURSEL, 0, 0 );
  824.     if( nCurSelect != CB_ERR ) 
  825.     {
  826.         g_pConnInfoTargetPlayer = (APP_PLAYER_INFO*) SendMessage( hConnTargetCombo, CB_GETITEMDATA, 
  827.                                                       nCurSelect, 0 );
  828.     }
  829. }
  830.  
  831.  
  832.  
  833.  
  834. //-----------------------------------------------------------------------------
  835. // Name: DirectPlayMessageHandler
  836. // Desc: Handler for DirectPlay messages.  This function is called by
  837. //       the DirectPlay message handler pool of threads, so be care of thread
  838. //       synchronization problems with shared memory
  839. //-----------------------------------------------------------------------------
  840. HRESULT WINAPI DirectPlayMessageHandler( PVOID pvUserContext, 
  841.                                          DWORD dwMessageId, 
  842.                                          PVOID pMsgBuffer )
  843. {
  844.     // Try not to stay in this message handler for too long, otherwise
  845.     // there will be a backlog of data.  The best solution is to 
  846.     // queue data as it comes in, and then handle it on other threads
  847.     // as this sample shows
  848.  
  849.     // This function is called by the DirectPlay message handler pool of 
  850.     // threads, so be care of thread synchronization problems with shared memory
  851.  
  852.     HRESULT hReturn = S_OK;
  853.  
  854.     switch( dwMessageId )
  855.     {
  856.         case DPN_MSGID_CREATE_PLAYER:
  857.         {
  858.             HRESULT hr;
  859.             PDPNMSG_CREATE_PLAYER pCreatePlayerMsg;
  860.             pCreatePlayerMsg = (PDPNMSG_CREATE_PLAYER) pMsgBuffer;
  861.  
  862.             APP_PLAYER_INFO* pPlayerInfo;
  863.             if( FAILED( hr = LinkPlayer( pCreatePlayerMsg->dpnidPlayer, 
  864.                                          &pPlayerInfo ) ) )
  865.                 return DXTRACE_ERR( TEXT("LinkPlayer"), hr );
  866.  
  867.             // Tell DirectPlay to store this pPlayerInfo 
  868.             // pointer in the pvPlayerContext.
  869.             pCreatePlayerMsg->pvPlayerContext = pPlayerInfo;
  870.  
  871.             // Post a message to the dialog thread to update the 
  872.             // UI.  This keeps the DirectPlay message handler 
  873.             // from blocking
  874.             if( g_hDlg != NULL )
  875.                 PostMessage( g_hDlg, WM_APP_UPDATE_TARGETS, 0, 0 );
  876.             break;
  877.         }
  878.  
  879.         case DPN_MSGID_DESTROY_PLAYER:
  880.         {
  881.             PDPNMSG_DESTROY_PLAYER pDestroyPlayerMsg;
  882.             pDestroyPlayerMsg = (PDPNMSG_DESTROY_PLAYER)pMsgBuffer;
  883.             APP_PLAYER_INFO* pPlayerInfo = (APP_PLAYER_INFO*) pDestroyPlayerMsg->pvPlayerContext;
  884.  
  885.             // Enter the player list CS, because we are about to 
  886.             // modify shared memory, and the data may be changed 
  887.             // concurrently from other DirectPlay message handler threads 
  888.             EnterCriticalSection( &g_csPlayerList );
  889.  
  890.             // Unlink pPlayerInfo from the chain, m_PlayerHead.
  891.             pPlayerInfo->pPrev->pNext = pPlayerInfo->pNext;
  892.             pPlayerInfo->pNext->pPrev = pPlayerInfo->pPrev;
  893.  
  894.             LeaveCriticalSection( &g_csPlayerList );
  895.  
  896.             // Stop sending if the target was destroyed
  897.             if( g_pTargetPlayer )
  898.             {
  899.                 if( g_pTargetPlayer->dpnidPlayer == pPlayerInfo->dpnidPlayer )
  900.                     g_bSendingData = FALSE;
  901.             }
  902.  
  903.             PLAYER_LOCK();                  // enter player context CS
  904.             PLAYER_RELEASE( pPlayerInfo );  // Release player and cleanup if needed
  905.             PLAYER_UNLOCK();                // leave player context CS
  906.  
  907.             // Post a message to the dialog thread to update the 
  908.             // UI.  This keeps the DirectPlay message handler 
  909.             // from blocking
  910.             if( g_hDlg != NULL )
  911.                 PostMessage( g_hDlg, WM_APP_UPDATE_TARGETS, 0, 0 );
  912.             break;
  913.         }
  914.  
  915.         case DPN_MSGID_TERMINATE_SESSION:
  916.         {
  917.             PDPNMSG_TERMINATE_SESSION pTerminateSessionMsg;
  918.             pTerminateSessionMsg = (PDPNMSG_TERMINATE_SESSION)pMsgBuffer;
  919.  
  920.             g_hrDialog = DPNERR_CONNECTIONLOST;
  921.             EndDialog( g_hDlg, 0 );
  922.             break;
  923.         }
  924.  
  925.         case DPN_MSGID_HOST_MIGRATE:
  926.         {
  927.             PDPNMSG_HOST_MIGRATE pHostMigrateMsg;
  928.             pHostMigrateMsg = (PDPNMSG_HOST_MIGRATE)pMsgBuffer;
  929.  
  930.             if( pHostMigrateMsg->dpnidNewHost == g_dpnidLocalPlayer )
  931.                 SetWindowText( g_hDlg, TEXT("DataRelay (Host)") );
  932.             break;
  933.         }
  934.  
  935.         case DPN_MSGID_RECEIVE:
  936.         {
  937.             PDPNMSG_RECEIVE pReceiveMsg;
  938.             pReceiveMsg = (PDPNMSG_RECEIVE)pMsgBuffer;
  939.             APP_PLAYER_INFO* pPlayerInfo = (APP_PLAYER_INFO*) pReceiveMsg->pvPlayerContext;
  940.  
  941.             GAMEMSG_GENERIC* pMsg = (GAMEMSG_GENERIC*) pReceiveMsg->pReceiveData;
  942.             if( pMsg->dwType == GAME_MSGID_GAMEPACKET )
  943.             {
  944.                 GAMEMSG_GENERIC* pDataMsg = (GAMEMSG_GENERIC*) pMsg;
  945.  
  946.                 // Make a new GAMEMSG_DATA_NODE and hand it off to the
  947.                 // app worker thread.  It will process the node, and
  948.                 // then update the UI to show that the packet was processed
  949.                 GAMEMSG_DATA_NODE* pDataMsgNode = new GAMEMSG_DATA_NODE;                
  950.                 ZeroMemory( pDataMsgNode, sizeof(GAMEMSG_DATA_NODE) ); 
  951.  
  952.                 pDataMsgNode->dwType            = DATA_TYPE_NETPACKET_RECIEVE;
  953.                 pDataMsgNode->dwPacketId        = pDataMsg->dwPacketId;
  954.                 pDataMsgNode->pDataMsg          = pDataMsg;
  955.                 pDataMsgNode->dwReceiveDataSize = pReceiveMsg->dwReceiveDataSize;
  956.                 pDataMsgNode->pPlayerFrom       = pPlayerInfo;
  957.                 pDataMsgNode->hBufferHandle     = pReceiveMsg->hBufferHandle;
  958.  
  959.                 // Enter the data list CS, because we are about to modify shared memory, and 
  960.                 // the data may be changed concurrently from other DirectPlay
  961.                 // message handler threads, as well as the the app worker thread
  962.                 EnterCriticalSection( &g_csDataList );
  963.  
  964.                 g_dwDataRecieved += pReceiveMsg->dwReceiveDataSize;
  965.  
  966.                 // Then add it to the circular linked list, g_DataHead
  967.                 // so it can be processed by a worker thread
  968.                 pDataMsgNode->pNext = &g_DataHead;
  969.                 pDataMsgNode->pPrev = g_DataHead.pPrev;
  970.                 g_DataHead.pPrev->pNext = pDataMsgNode;
  971.                 g_DataHead.pPrev        = pDataMsgNode;
  972.  
  973.                 LeaveCriticalSection( &g_csDataList );
  974.  
  975.                 // Tell the app worker thread that there 
  976.                 // is new network data availible
  977.                 SetEvent( g_hDPDataAvailEvent );
  978.  
  979.                 // Tell DirectPlay to assume that ownership of the buffer 
  980.                 // has been transferred to the application, and so it will 
  981.                 // neither free nor modify it until ownership is returned 
  982.                 // to DirectPlay through the ReturnBuffer() call.
  983.                 hReturn = DPNSUCCESS_PENDING;
  984.             }
  985.             break;
  986.         }
  987.  
  988.         case DPN_MSGID_SEND_COMPLETE:
  989.         {
  990.             PDPNMSG_SEND_COMPLETE pSendCompleteMsg;
  991.             pSendCompleteMsg = (PDPNMSG_SEND_COMPLETE)pMsgBuffer;
  992.  
  993.             GAMEMSG_GENERIC* pGameMsg;
  994.             pGameMsg = (GAMEMSG_GENERIC*) pSendCompleteMsg->pvUserContext;
  995.  
  996.             if( pSendCompleteMsg->hResultCode == DPNERR_TIMEDOUT )
  997.             {
  998.                 // Make a new GAMEMSG_DATA_NODE and hand it off to the
  999.                 // app worker thread.  It will process the node, and
  1000.                 // then update the UI to show that the packet timed out
  1001.                 GAMEMSG_DATA_NODE* pDataMsgNode = new GAMEMSG_DATA_NODE;
  1002.                 ZeroMemory( pDataMsgNode, sizeof(GAMEMSG_DATA_NODE) ); 
  1003.                 
  1004.                 pDataMsgNode->dwType     = DATA_TYPE_NETPACKET_TIMEOUT;
  1005.                 pDataMsgNode->dwPacketId = pGameMsg->dwPacketId;
  1006.  
  1007.                 // Enter the data list CS, because we are about to modify shared memory, and 
  1008.                 // the data may be changed concurrently from other DirectPlay
  1009.                 // message handler threads, as well as the the app worker thread
  1010.                 EnterCriticalSection( &g_csDataList );
  1011.  
  1012.                 // Then add it to the circular linked list, g_DataHead
  1013.                 // so it can be processed by a worker thread
  1014.                 pDataMsgNode->pNext = &g_DataHead;
  1015.                 pDataMsgNode->pPrev = g_DataHead.pPrev;
  1016.                 g_DataHead.pPrev->pNext = pDataMsgNode;
  1017.                 g_DataHead.pPrev        = pDataMsgNode;
  1018.  
  1019.                 LeaveCriticalSection( &g_csDataList );
  1020.  
  1021.                 // Tell the app worker thread that there 
  1022.                 // is new data availible. 
  1023.                 SetEvent( g_hDPDataAvailEvent );
  1024.             }
  1025.  
  1026.             SAFE_DELETE( pGameMsg );
  1027.  
  1028.             break;
  1029.         }
  1030.     }
  1031.  
  1032.     // Make sure the DirectPlay MessageHandler calls the CNetConnectWizard handler, 
  1033.     // so it can be informed of messages such as DPN_MSGID_ENUM_HOSTS_RESPONSE.
  1034.     if( DPNSUCCESS_PENDING != hReturn && SUCCEEDED(hReturn) && g_pNetConnectWizard )
  1035.         hReturn = g_pNetConnectWizard->MessageHandler( pvUserContext, dwMessageId, pMsgBuffer );
  1036.     
  1037.     return hReturn;
  1038. }
  1039.  
  1040.  
  1041.  
  1042.  
  1043. //-----------------------------------------------------------------------------
  1044. // Name: LinkPlayer()
  1045. // Desc: Link a new player to the circular linked list, g_PlayerHead.  This
  1046. //       is called by the DirectPlay message handler threads, so we enter a 
  1047. //       CS to avoid thread syncoronization problems.
  1048. //-----------------------------------------------------------------------------
  1049. HRESULT LinkPlayer( DPNID dpnid, APP_PLAYER_INFO** ppPlayerInfo )
  1050. {
  1051.     HRESULT hr;
  1052.  
  1053.     // Get the peer info and extract its name
  1054.     DWORD dwSize = 0;
  1055.     DPN_PLAYER_INFO* pdpPlayerInfo = NULL;
  1056.     hr = g_pDP->GetPeerInfo( dpnid, pdpPlayerInfo, &dwSize, 0 );
  1057.     if( FAILED(hr) && hr != DPNERR_BUFFERTOOSMALL )
  1058.         return DXTRACE_ERR( TEXT("GetPeerInfo"), hr );
  1059.     pdpPlayerInfo = (DPN_PLAYER_INFO*) new BYTE[ dwSize ];
  1060.     ZeroMemory( pdpPlayerInfo, dwSize );
  1061.     pdpPlayerInfo->dwSize = sizeof(DPN_PLAYER_INFO);
  1062.     hr = g_pDP->GetPeerInfo( dpnid, pdpPlayerInfo, &dwSize, 0 );
  1063.     if( FAILED(hr) )
  1064.         return DXTRACE_ERR( TEXT("GetPeerInfo"), hr );
  1065.  
  1066.     // Create a new and fill in a APP_PLAYER_INFO
  1067.     APP_PLAYER_INFO* pPlayerInfo = new APP_PLAYER_INFO;
  1068.     pPlayerInfo->dpnidPlayer = dpnid;
  1069.     pPlayerInfo->lRefCount   = 1;
  1070.     pPlayerInfo->dwFlags     = pdpPlayerInfo->dwPlayerFlags;
  1071.  
  1072.     // This stores a extra TCHAR copy of the player name for 
  1073.     // easier access.  This will be redundent copy since DPlay 
  1074.     // also keeps a copy of the player name in GetPeerInfo()
  1075.     DXUtil_ConvertWideStringToGeneric( pPlayerInfo->strPlayerName, 
  1076.                                        pdpPlayerInfo->pwszName, MAX_PLAYER_NAME );
  1077.  
  1078.     if( pdpPlayerInfo->dwPlayerFlags & DPNPLAYER_LOCAL )
  1079.         g_dpnidLocalPlayer = dpnid;
  1080.  
  1081.     SAFE_DELETE_ARRAY( pdpPlayerInfo );
  1082.  
  1083.     // Enter the player list CS, because we are about to modify shared memory, and 
  1084.     // the data may be changed concurrently from the DirectPlay
  1085.     // message handler threads 
  1086.     EnterCriticalSection( &g_csPlayerList );
  1087.  
  1088.     // Then add it to the circular linked list, g_PlayerHead,
  1089.     pPlayerInfo->pNext = g_PlayerHead.pNext;
  1090.     pPlayerInfo->pPrev = &g_PlayerHead;
  1091.     g_PlayerHead.pNext->pPrev = pPlayerInfo;
  1092.     g_PlayerHead.pNext        = pPlayerInfo;
  1093.  
  1094.     LeaveCriticalSection( &g_csPlayerList );
  1095.  
  1096.     if( ppPlayerInfo )
  1097.         *ppPlayerInfo = pPlayerInfo;
  1098.  
  1099.     return S_OK;
  1100. }
  1101.  
  1102.  
  1103.  
  1104.  
  1105.  
  1106.  
  1107.  
  1108.  
  1109. //-----------------------------------------------------------------------------
  1110. // Name: DirectPlayLobbyMessageHandler
  1111. // Desc: Handler for DirectPlay lobby messages.  This function is called by
  1112. //       the DirectPlay lobby message handler pool of threads, so be careful of 
  1113. //       thread synchronization problems with shared memory
  1114. //-----------------------------------------------------------------------------
  1115. HRESULT WINAPI DirectPlayLobbyMessageHandler( PVOID pvUserContext, 
  1116.                                               DWORD dwMessageId, 
  1117.                                               PVOID pMsgBuffer )
  1118. {
  1119.     switch( dwMessageId )
  1120.     {
  1121.         case DPL_MSGID_CONNECT:
  1122.         {
  1123.             PDPL_MESSAGE_CONNECT pConnectMsg;
  1124.             pConnectMsg = (PDPL_MESSAGE_CONNECT)pMsgBuffer;
  1125.  
  1126.             // The CNetConnectWizard will handle this message for us,
  1127.             // so there is nothing we need to do here for this simple
  1128.             // sample.
  1129.             break;
  1130.         }
  1131.  
  1132.         case DPL_MSGID_DISCONNECT:
  1133.         {
  1134.             PDPL_MESSAGE_DISCONNECT pDisconnectMsg;
  1135.             pDisconnectMsg = (PDPL_MESSAGE_DISCONNECT)pMsgBuffer;
  1136.  
  1137.             // We should free any data associated with the lobby 
  1138.             // client here, but there is none.
  1139.             break;
  1140.         }
  1141.  
  1142.         case DPL_MSGID_RECEIVE:
  1143.         {
  1144.             PDPL_MESSAGE_RECEIVE pReceiveMsg;
  1145.             pReceiveMsg = (PDPL_MESSAGE_RECEIVE)pMsgBuffer;
  1146.  
  1147.             // The lobby client sent us data.  This sample doesn't
  1148.             // expected data from the client, but it is useful 
  1149.             // for more complex apps.
  1150.             break;
  1151.         }
  1152.  
  1153.         case DPL_MSGID_CONNECTION_SETTINGS:
  1154.         {
  1155.             PDPL_MESSAGE_CONNECTION_SETTINGS pConnectionStatusMsg;
  1156.             pConnectionStatusMsg = (PDPL_MESSAGE_CONNECTION_SETTINGS)pMsgBuffer;
  1157.  
  1158.             // The lobby client has changed the connection settings.  
  1159.             // This simple sample doesn't handle this, but more complex apps may
  1160.             // want to.
  1161.             break;
  1162.         }
  1163.     }
  1164.  
  1165.     // Make sure the DirectPlay MessageHandler calls the CNetConnectWizard handler, 
  1166.     // so the wizard can be informed of lobby messages such as DPL_MSGID_CONNECT
  1167.     if( g_pNetConnectWizard )
  1168.         return g_pNetConnectWizard->LobbyMessageHandler( pvUserContext, dwMessageId, 
  1169.                                                          pMsgBuffer );
  1170.     
  1171.     return S_OK;
  1172. }
  1173.  
  1174.  
  1175.  
  1176.  
  1177. //-----------------------------------------------------------------------------
  1178. // Name: SendNetworkData()
  1179. // Desc: Sends a network packet of the specified size to the specified target.
  1180. //       This is called by the dialog UI thread.
  1181. //-----------------------------------------------------------------------------
  1182. HRESULT SendNetworkData()
  1183. {
  1184.     HRESULT hr;    
  1185.  
  1186.     if( !g_bSendingData || 
  1187.         IsDlgButtonChecked( g_hDlg, IDC_SEND_READY ) == BST_UNCHECKED )
  1188.     {
  1189.         KillTimer( g_hDlg, TIMERID_NETWORK );
  1190.         return S_OK;
  1191.     }
  1192.  
  1193.     if( g_pTargetPlayer == NULL )
  1194.         return E_FAIL;
  1195.  
  1196.     GAMEMSG_GENERIC* pGameMsg = NULL;
  1197.     DWORD            dwBufferSize;
  1198.  
  1199.     // Create a game packet based on the selected choices
  1200.     switch( g_dwSendSize )
  1201.     {
  1202.         case 512:
  1203.         {
  1204.             GAMEMSG_DATA_512* pMsg512 = new GAMEMSG_DATA_512;
  1205.             FillMemory( pMsg512->pBuffer, sizeof(pMsg512->pBuffer), 1 );
  1206.             pGameMsg = pMsg512;
  1207.             dwBufferSize = sizeof(GAMEMSG_DATA_512);
  1208.             break;
  1209.         }
  1210.         
  1211.         case 256:
  1212.         {
  1213.             GAMEMSG_DATA_256* pMsg256 = new GAMEMSG_DATA_256;
  1214.             FillMemory( pMsg256->pBuffer, sizeof(pMsg256->pBuffer), 2 );
  1215.             pGameMsg = pMsg256;
  1216.             dwBufferSize = sizeof(GAMEMSG_DATA_256);
  1217.             break;
  1218.         }
  1219.  
  1220.         case 128:
  1221.         {
  1222.             GAMEMSG_DATA_128* pMsg128 = new GAMEMSG_DATA_128;
  1223.             FillMemory( pMsg128->pBuffer, sizeof(pMsg128->pBuffer), 3 );
  1224.             pGameMsg = pMsg128;
  1225.             dwBufferSize = sizeof(GAMEMSG_DATA_128);
  1226.             break;
  1227.         }
  1228.  
  1229.         case 64:
  1230.         {
  1231.             GAMEMSG_DATA_64* pMsg64 = new GAMEMSG_DATA_64;
  1232.             FillMemory( pMsg64->pBuffer, sizeof(pMsg64->pBuffer), 4 );
  1233.             pGameMsg = pMsg64;
  1234.             dwBufferSize = sizeof(GAMEMSG_DATA_64);
  1235.             break;
  1236.         }
  1237.  
  1238.         case 32:
  1239.         {
  1240.             GAMEMSG_DATA_32* pMsg32 = new GAMEMSG_DATA_32;
  1241.             FillMemory( pMsg32->pBuffer, sizeof(pMsg32->pBuffer), 5 );
  1242.             pGameMsg = pMsg32;
  1243.             dwBufferSize = sizeof(GAMEMSG_DATA_32);
  1244.             break;
  1245.         }
  1246.  
  1247.         default:
  1248.         case 16:
  1249.         {
  1250.             GAMEMSG_DATA_16* pMsg16 = new GAMEMSG_DATA_16;
  1251.             FillMemory( pMsg16->pBuffer, sizeof(pMsg16->pBuffer), 6 );
  1252.             pGameMsg = pMsg16;
  1253.             dwBufferSize = sizeof(GAMEMSG_DATA_16);
  1254.             break;
  1255.         }
  1256.     }
  1257.  
  1258.     // Update the rest of the game msg 
  1259.     g_dwPacketId++;
  1260.     pGameMsg->dwPacketId   = g_dwPacketId;
  1261.     pGameMsg->dwType       = GAME_MSGID_GAMEPACKET;
  1262.             
  1263.     // Make a new GAMEMSG_DATA_NODE and hand it off to the
  1264.     // app worker thread.  It will process the node, and
  1265.     // then update the UI to show that a packet was sent
  1266.     GAMEMSG_DATA_NODE* pDataMsgNode = new GAMEMSG_DATA_NODE;
  1267.     ZeroMemory( pDataMsgNode, sizeof(GAMEMSG_DATA_NODE) ); 
  1268.  
  1269.     pDataMsgNode->dwType            = DATA_TYPE_NETPACKET_SENT;
  1270.     pDataMsgNode->dwPacketId        = pGameMsg->dwPacketId;
  1271.     pDataMsgNode->dwReceiveDataSize = dwBufferSize;
  1272.  
  1273.     // Enter the data list CS, because we are about to modify shared memory, and 
  1274.     // the data may be changed concurrently from other DirectPlay
  1275.     // message handler threads, as well as the the app worker thread
  1276.     EnterCriticalSection( &g_csDataList );
  1277.  
  1278.     // Then add it to the circular linked list, g_DataHead
  1279.     // so it can be processed by a worker thread
  1280.     pDataMsgNode->pNext = &g_DataHead;
  1281.     pDataMsgNode->pPrev = g_DataHead.pPrev;
  1282.     g_DataHead.pPrev->pNext = pDataMsgNode;
  1283.     g_DataHead.pPrev        = pDataMsgNode;
  1284.  
  1285.     LeaveCriticalSection( &g_csDataList );
  1286.  
  1287.     // This var is only accessed by the dialog 
  1288.     // thread, so it is safe to access it directly
  1289.     g_dwDataSent += dwBufferSize;
  1290.  
  1291.     DPN_BUFFER_DESC bufferDesc;
  1292.     bufferDesc.dwBufferSize = dwBufferSize;
  1293.     bufferDesc.pBufferData  = (BYTE*) pGameMsg;
  1294.  
  1295.     DPNHANDLE hAsync;
  1296.     hr = g_pDP->SendTo( g_pTargetPlayer->dpnidPlayer, &bufferDesc, 1,
  1297.                         g_dwSendTimeout, pGameMsg, &hAsync, 
  1298.                         DPNSEND_NOLOOPBACK | DPNSEND_NOCOPY );
  1299.     // Ignore all errors except DPNERR_INVALIDPLAYER
  1300.     if( hr == DPNERR_INVALIDPLAYER )
  1301.     {
  1302.         // Stop sending if the target left game
  1303.         g_bSendingData = FALSE;
  1304.         PostMessage( g_hDlg, WM_APP_UPDATE_TARGETS, 0, 0 );
  1305.         return S_OK;
  1306.     }
  1307.     
  1308.  
  1309.     // Tell the app worker thread that there 
  1310.     // is new data availible. 
  1311.     SetEvent( g_hDPDataAvailEvent );
  1312.  
  1313.     return S_OK;
  1314. }
  1315.  
  1316.  
  1317.  
  1318.  
  1319. //-----------------------------------------------------------------------------
  1320. // Name: ProcessNetDataProc()
  1321. // Desc: Worker thread that processes data found in g_DataHead, 
  1322. //       and updates the UI state based on the data 
  1323. //-----------------------------------------------------------------------------
  1324. UINT WINAPI ProcessNetDataProc( LPVOID lpParameter )
  1325. {
  1326.     HWND   hDlg = (HWND) lpParameter;
  1327.     DWORD  dwResult;
  1328.     BOOL   bDone = FALSE;
  1329.     HANDLE ahHandles[2];
  1330.  
  1331.     ahHandles[0] = g_hDPDataAvailEvent;
  1332.     ahHandles[1] = g_hShutdownEvent;
  1333.  
  1334.     while( !bDone ) 
  1335.     { 
  1336.         dwResult = WaitForMultipleObjects( 2, ahHandles, FALSE, INFINITE );
  1337.  
  1338.         switch( dwResult )
  1339.         {
  1340.             case WAIT_OBJECT_0 + 0:
  1341.             {
  1342.                 // g_hDPDataAvailEvent is signaled, so there is 
  1343.                 // network data available to process
  1344.                 if( FAILED( g_hrDialog = ProcessData() ) ) 
  1345.                 {
  1346.                     DXTRACE_ERR( TEXT("ProcessData"), g_hrDialog );
  1347.                     bDone = TRUE;
  1348.                 }
  1349.                 break;
  1350.             }
  1351.  
  1352.             case WAIT_OBJECT_0 + 1:
  1353.             {
  1354.                 // g_hShutdownEvent is signaled, so shut down
  1355.                 bDone = TRUE;
  1356.                 break;
  1357.             }
  1358.         }
  1359.     }
  1360.  
  1361.     return 0;
  1362. }   
  1363.  
  1364.  
  1365.  
  1366.  
  1367. //-----------------------------------------------------------------------------
  1368. // Name: ProcessData()
  1369. // Desc: This is called by the app worker thread to process the data found in
  1370. //       g_DataHead.  The DirectPlay message handler threads add new nodes 
  1371. //       upon events such as packet packet recieve/send or timeout.
  1372. //-----------------------------------------------------------------------------
  1373. HRESULT ProcessData()
  1374. {
  1375.     TCHAR* strNewLogLine = NULL;
  1376.  
  1377.     // Enter the data list CS, because we are about to read shared memory, and 
  1378.     // the data may be changed concurrently from the DirectPlay
  1379.     // message handler threads 
  1380.     EnterCriticalSection( &g_csDataList );
  1381.  
  1382.     GAMEMSG_DATA_NODE* pDeleteNode; 
  1383.     GAMEMSG_DATA_NODE* pNode = g_DataHead.pNext;
  1384.     while( pNode != &g_DataHead )
  1385.     {
  1386.         // Allocate a string on the heap, and fill it
  1387.         // with text about what happened.
  1388.         strNewLogLine = new TCHAR[MAX_PATH];
  1389.  
  1390.         switch( pNode->dwType )
  1391.         {
  1392.             case DATA_TYPE_NETPACKET_RECIEVE:
  1393.                 wsprintf( strNewLogLine, TEXT("Got %s's #%d (%d bytes)\r\n"), 
  1394.                         pNode->pPlayerFrom->strPlayerName,
  1395.                         pNode->dwPacketId,            
  1396.                         pNode->dwReceiveDataSize );           
  1397.  
  1398.                 // The app can do more process of the network 
  1399.                 // packet here here -- i.e. change the
  1400.                 // state of the game.  This simple sample just
  1401.                 // updates the UI.
  1402.                 break;
  1403.  
  1404.             case DATA_TYPE_NETPACKET_SENT:
  1405.                 wsprintf( strNewLogLine, TEXT("Sending #%d to %s (%d bytes)\r\n"), 
  1406.                                          pNode->dwPacketId,            
  1407.                                          g_pTargetPlayer->strPlayerName,
  1408.                                          pNode->dwReceiveDataSize );           
  1409.                 break;
  1410.  
  1411.             case DATA_TYPE_NETPACKET_TIMEOUT:
  1412.                 wsprintf( strNewLogLine, TEXT("Packet #%d timed out\r\n"), 
  1413.                          pNode->dwPacketId );           
  1414.                 break;
  1415.         }
  1416.  
  1417.         // Post a message to the dialog thread, so it can
  1418.         // take the string on the heap, and appead it to the log.
  1419.         // If the log is appended from this thread, then if the dialog
  1420.         // thread tries to enter the g_csDataList CS, then it will be
  1421.         // locked until this thread release it, and the 
  1422.         // AppendTextToEditControl function relies on reponses from the 
  1423.         // dialog thread, so a race condition is possible unless
  1424.         // the dialog thread itself appeads the text.
  1425.         PostMessage( g_hDlg, WM_APP_APPEND_TEXT, (WPARAM) strNewLogLine, 0 );
  1426.  
  1427.         // The dialog thread will free the string
  1428.         strNewLogLine = NULL;
  1429.  
  1430.         if( pNode->hBufferHandle != NULL )
  1431.         {
  1432.             // Done with the buffer, so return it DirectPlay, 
  1433.             // so that the memory can be reused
  1434.             g_pDP->ReturnBuffer( pNode->hBufferHandle,0 );
  1435.         }
  1436.  
  1437.         // Unlink pPlayerInfo from the chain, g_DataHead.
  1438.         pNode->pPrev->pNext = pNode->pNext;
  1439.         pNode->pNext->pPrev = pNode->pPrev;
  1440.  
  1441.         pDeleteNode = pNode;
  1442.         pNode = pNode->pNext;
  1443.  
  1444.         assert( pDeleteNode != &g_DataHead );
  1445.         SAFE_DELETE( pDeleteNode );
  1446.     }
  1447.  
  1448.     LeaveCriticalSection( &g_csDataList );
  1449.  
  1450.     return S_OK;
  1451. }
  1452.  
  1453.  
  1454.  
  1455.  
  1456. //-----------------------------------------------------------------------------
  1457. // Name: AppendTextToEditControl()
  1458. // Desc: Appends a string of text to the edit control
  1459. //-----------------------------------------------------------------------------
  1460. VOID AppendTextToEditControl( HWND hDlg, TCHAR* strNewLogLine )
  1461. {
  1462.     static TCHAR strText[1024*10];
  1463.  
  1464.     HWND hEdit = GetDlgItem( hDlg, IDC_LOG_EDIT );
  1465.     SendMessage( hEdit, WM_SETREDRAW, FALSE, 0 );
  1466.     GetWindowText( hEdit, strText, 1024*9 );
  1467.  
  1468.     _tcscat( strText, strNewLogLine );
  1469.  
  1470.     int nSecondLine = 0;
  1471.     if( SendMessage( hEdit, EM_GETLINECOUNT, 0, 0 ) > 18 )
  1472.         nSecondLine = (int)SendMessage( hEdit, EM_LINEINDEX, 1, 0 );
  1473.  
  1474.     SetWindowText( hEdit, &strText[nSecondLine] );
  1475.  
  1476.     SendMessage( hEdit, WM_SETREDRAW, TRUE, 0 );
  1477.     InvalidateRect( hEdit, NULL, TRUE );
  1478.     UpdateWindow( hEdit );
  1479. }
  1480.  
  1481.  
  1482.  
  1483.  
  1484. //-----------------------------------------------------------------------------
  1485. // Name: UpdateConnectionInfo()
  1486. // Desc: Using the info buffer passed in,  display some interesting tidbits about
  1487. //       the connection.
  1488. //-----------------------------------------------------------------------------
  1489. VOID UpdateConnectionInfo( HWND hDlg, PDPN_CONNECTION_INFO pConnectionInfo,
  1490.                            DWORD dwHighMessages, DWORD dwHighBytes, 
  1491.                            DWORD dwNormalMessages, DWORD dwNormalBytes,
  1492.                            DWORD dwLowMessages, DWORD dwLowBytes )
  1493. {
  1494.     TCHAR strText[1024];
  1495.     int   nDrops;
  1496.     int   nSends;
  1497.  
  1498.     HWND hEdit = GetDlgItem( hDlg, IDC_CONNINFO_EDIT );
  1499.     int nThumbIndex = GetScrollPos( hEdit, SB_VERT );
  1500.  
  1501.     if( pConnectionInfo )
  1502.     {
  1503.         nDrops = pConnectionInfo->dwPacketsDropped + pConnectionInfo->dwPacketsRetried;
  1504.         nDrops *= 10000;
  1505.  
  1506.         nSends = pConnectionInfo->dwPacketsSentGuaranteed + pConnectionInfo->dwPacketsSentNonGuaranteed;
  1507.  
  1508.         if(nSends)
  1509.             nDrops /= nSends; // percent nDrops * 100
  1510.  
  1511.         _stprintf(strText, 
  1512.                            "Send Queue Messages High Priority=%d\r\n"       \
  1513.                            "Send Queue Bytes High Priority=%d\r\n"     \
  1514.                            
  1515.                            "Send Queue Messages Normal Priority=%d\r\n"       \
  1516.                            "Send Queue Bytes Normal Priority=%d\r\n"     \
  1517.  
  1518.                            "Send Queue Messages Low Priority=%d\r\n"       \
  1519.                            "Send Queue Bytes Low Priority=%d\r\n"     \
  1520.  
  1521.                            "Round Trip Latency MS=%dms\r\n"                  \
  1522.                            "Throughput BPS=%d\r\n"                         \
  1523.                            "Peak Throughput BPS=%d\r\n"                     \
  1524.                                                                             \
  1525.                            "Bytes Sent Guaranteed=%d\r\n"                   \
  1526.                            "Packets Sent Guaranteed=%d\r\n"                 \
  1527.                            "Bytes Sent Non-Guaranteed=%d\r\n"                \
  1528.                            "Packets Sent Non-Guaranteed=%d\r\n"              \
  1529.                                                                             \
  1530.                            "Bytes Retried Guaranteed=%d\r\n"        \
  1531.                            "Packets Retried Guaranteed=%d\r\n"      \
  1532.                            "Bytes Dropped Non-Guaranteed=%d\r\n"    \
  1533.                            "Packets Dropped Non-Guaranteed=%d\r\n"  \
  1534.                                                                             \
  1535.                            "Messages Transmitted High Priority=%d\r\n"       \
  1536.                            "Messages Timed Out High Priority=%d\r\n"          \
  1537.                            "Messages Transmitted Normal Priority=%d\r\n"     \
  1538.                            "Messages Timed Out Normal Priority=%d\r\n"        \
  1539.                            "Messages Transmitted Low Priority=%d\r\n"        \
  1540.                            "Messages Timed Out Low Priority=%d\r\n"           \
  1541.                                                                             \
  1542.                            "Bytes Received Guaranteed=%d\r\n"               \
  1543.                            "Packets Received Guaranteed=%d\r\n"             \
  1544.                            "Bytes Received Non-Guaranteed=%d\r\n"            \
  1545.                            "Packets Received Non-Guaranteed=%d\r\n"          \
  1546.                            "Messages Received=%d\r\n"                      \
  1547.                                                                             \
  1548.                            "Loss Rate=%d.%02d%%\r\n",
  1549.                             
  1550.                            dwHighMessages, dwHighBytes,
  1551.                            dwNormalMessages, dwNormalBytes,
  1552.                            dwLowMessages, dwLowBytes,
  1553.  
  1554.                            pConnectionInfo->dwRoundTripLatencyMS, 
  1555.                            pConnectionInfo->dwThroughputBPS, 
  1556.                            pConnectionInfo->dwPeakThroughputBPS,
  1557.  
  1558.                            pConnectionInfo->dwBytesSentGuaranteed,
  1559.                            pConnectionInfo->dwPacketsSentGuaranteed,
  1560.                            pConnectionInfo->dwBytesSentNonGuaranteed,
  1561.                            pConnectionInfo->dwPacketsSentNonGuaranteed,
  1562.  
  1563.                            pConnectionInfo->dwBytesRetried,
  1564.                            pConnectionInfo->dwPacketsRetried,
  1565.                            pConnectionInfo->dwBytesDropped,
  1566.                            pConnectionInfo->dwPacketsDropped,
  1567.  
  1568.                            pConnectionInfo->dwMessagesTransmittedHighPriority,
  1569.                            pConnectionInfo->dwMessagesTimedOutHighPriority,
  1570.                            pConnectionInfo->dwMessagesTransmittedNormalPriority,
  1571.                            pConnectionInfo->dwMessagesTimedOutNormalPriority,
  1572.                            pConnectionInfo->dwMessagesTransmittedLowPriority,
  1573.                            pConnectionInfo->dwMessagesTimedOutLowPriority,
  1574.  
  1575.                            pConnectionInfo->dwBytesReceivedGuaranteed,
  1576.                            pConnectionInfo->dwPacketsReceivedGuaranteed,
  1577.                            pConnectionInfo->dwBytesReceivedNonGuaranteed,
  1578.                            pConnectionInfo->dwPacketsReceivedNonGuaranteed,
  1579.                            pConnectionInfo->dwMessagesReceived,
  1580.  
  1581.                            (nDrops/100), (nDrops % 100) );
  1582.     }
  1583.     else 
  1584.     {
  1585.         strText[0] = 0;
  1586.     }
  1587.  
  1588.     SetWindowText( hEdit, strText );  
  1589.     SendMessage( hEdit, EM_LINESCROLL, 0, nThumbIndex );
  1590. }
  1591.  
  1592.  
  1593.  
  1594.  
  1595. //-----------------------------------------------------------------------------
  1596. // Name: UpdateStats()
  1597. // Desc: 
  1598. //-----------------------------------------------------------------------------
  1599. VOID UpdateStats( HWND hDlg )
  1600. {
  1601.     HRESULT hr;
  1602.     static DWORD s_dwLastTime = timeGetTime();
  1603.     DWORD dwCurTime = timeGetTime();
  1604.  
  1605.     if( (dwCurTime - s_dwLastTime) < 200 )
  1606.         return;
  1607.  
  1608.     TCHAR strDataIn[32];
  1609.     TCHAR strDataOut[32];
  1610.  
  1611.     FLOAT fSecondsPassed = (dwCurTime - s_dwLastTime) / 1000.0f;
  1612.     FLOAT fDataIn  = g_dwDataRecieved / fSecondsPassed;
  1613.     FLOAT fDataOut = g_dwDataSent / fSecondsPassed;
  1614.  
  1615.     s_dwLastTime     = dwCurTime;
  1616.     g_dwDataRecieved = 0;
  1617.     g_dwDataSent     = 0;
  1618.  
  1619.     _stprintf( strDataIn,  TEXT("%0.1f BPS"), fDataIn );
  1620.     _stprintf( strDataOut, TEXT("%0.1f BPS"), fDataOut );
  1621.     SetDlgItemText( hDlg, IDC_ACTUAL_SEND_RATE, strDataOut );
  1622.     SetDlgItemText( hDlg, IDC_ACTUAL_RECIEVE_RATE, strDataIn );
  1623.  
  1624.     //  If a player has been selected for Connection Info Display we will handle it next
  1625.     if( g_pConnInfoTargetPlayer->dpnidPlayer != 0 )
  1626.     {
  1627.         // Call GetConnectionInfo and display results
  1628.         DPN_CONNECTION_INFO dpnConnectionInfo;
  1629.         ZeroMemory( &dpnConnectionInfo, sizeof(DPN_CONNECTION_INFO) );
  1630.         dpnConnectionInfo.dwSize = sizeof(DPN_CONNECTION_INFO);
  1631.         hr = g_pDP->GetConnectionInfo( g_pConnInfoTargetPlayer->dpnidPlayer, 
  1632.                                                    &dpnConnectionInfo,
  1633.                                                    0);
  1634.  
  1635.         if( SUCCEEDED(hr) )
  1636.         {
  1637.             DWORD dwHighMessages, dwHighBytes;
  1638.             DWORD dwNormalMessages, dwNormalBytes;
  1639.             DWORD dwLowMessages, dwLowBytes;
  1640.  
  1641.             hr = g_pDP->GetSendQueueInfo( g_pConnInfoTargetPlayer->dpnidPlayer, 
  1642.                                           &dwHighMessages, &dwHighBytes, 
  1643.                                           DPNGETSENDQUEUEINFO_PRIORITY_HIGH );
  1644.             if( FAILED(hr) )
  1645.                 DXTRACE_ERR( TEXT("GetSendQueueInfo"), hr );
  1646.  
  1647.             hr = g_pDP->GetSendQueueInfo( g_pConnInfoTargetPlayer->dpnidPlayer, 
  1648.                                      &dwNormalMessages, &dwNormalBytes, 
  1649.                                      DPNGETSENDQUEUEINFO_PRIORITY_NORMAL );
  1650.             if( FAILED(hr) )
  1651.                 DXTRACE_ERR( TEXT("GetSendQueueInfo"), hr );
  1652.  
  1653.             hr = g_pDP->GetSendQueueInfo( g_pConnInfoTargetPlayer->dpnidPlayer, 
  1654.                                      &dwLowMessages, &dwLowBytes, 
  1655.                                      DPNGETSENDQUEUEINFO_PRIORITY_LOW );
  1656.             if( FAILED(hr) )
  1657.                 DXTRACE_ERR( TEXT("GetSendQueueInfo"), hr );
  1658.  
  1659.             UpdateConnectionInfo( hDlg, &dpnConnectionInfo,
  1660.                                   dwHighMessages, dwHighBytes,
  1661.                                   dwNormalMessages, dwNormalBytes,
  1662.                                   dwLowMessages, dwLowBytes );
  1663.         }
  1664.         else
  1665.         {
  1666.             // If the player goes away, the set the target to none
  1667.             SendDlgItemMessage( hDlg, IDC_CONNINFO_COMBO, CB_SETCURSEL, 0, 0 );  
  1668.             ReadCombos( hDlg );
  1669.         }
  1670.     }
  1671.     else 
  1672.     {
  1673.         // Clear the conn info window if no connection is selected to display        
  1674.         UpdateConnectionInfo( hDlg, NULL, 0, 0, 0, 0, 0, 0 );
  1675.     }
  1676. }
  1677.  
  1678.  
  1679.