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

  1. //----------------------------------------------------------------------------
  2. // File: ChatPeer.cpp
  3. //
  4. // Desc: The main game file for the ChatPeer 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. //       After a new game has started the sample begins a very simplistic 
  10. //       chat session where users can send text to each other.
  11. //
  12. // Copyright (c) 1999-2000 Microsoft Corp. All rights reserved.
  13. //-----------------------------------------------------------------------------
  14. #define STRICT
  15. #include <windows.h>
  16. #include <basetsd.h>
  17. #include <richedit.h>
  18. #include <dplay8.h>
  19. #include <dplobby8.h>
  20. #include <dxerr8.h>
  21. #include "NetConnect.h"
  22. #include "DXUtil.h"
  23. #include "resource.h"
  24.  
  25.  
  26.  
  27.  
  28. //-----------------------------------------------------------------------------
  29. // Player context locking defines
  30. //-----------------------------------------------------------------------------
  31. CRITICAL_SECTION g_csPlayerContext;
  32. #define PLAYER_LOCK()                   EnterCriticalSection( &g_csPlayerContext ); 
  33. #define PLAYER_ADDREF( pPlayerInfo )    if( pPlayerInfo ) pPlayerInfo->lRefCount++;
  34. #define PLAYER_RELEASE( pPlayerInfo )   if( pPlayerInfo ) { pPlayerInfo->lRefCount--; if( pPlayerInfo->lRefCount <= 0 ) SAFE_DELETE( pPlayerInfo ); } pPlayerInfo = NULL;
  35. #define PLAYER_UNLOCK()                 LeaveCriticalSection( &g_csPlayerContext );
  36.  
  37.  
  38. //-----------------------------------------------------------------------------
  39. // Defines, and constants
  40. //-----------------------------------------------------------------------------
  41. #define DPLAY_SAMPLE_KEY        TEXT("Software\\Microsoft\\DirectX DirectPlay Samples")
  42. #define MAX_PLAYER_NAME         14
  43. #define MAX_CHAT_STRINGS        50
  44. #define MAX_CHAT_STRING_LENGTH  508
  45. #define WM_APP_UPDATE_STATS     (WM_APP + 0)
  46. #define WM_APP_CHAT             (WM_APP + 1)
  47.  
  48.  
  49. // This GUID allows DirectPlay to find other instances of the same game on
  50. // the network.  So it must be unique for every game, and the same for 
  51. // every instance of that game.  // {876A3036-FFD7-46bc-9209-B42F617B9BE7}
  52. GUID g_guidApp = { 0x876a3036, 0xffd7, 0x46bc, { 0x92, 0x9, 0xb4, 0x2f, 0x61, 0x7b, 0x9b, 0xe7 } };
  53.  
  54. struct APP_PLAYER_INFO
  55. {
  56.     LONG  lRefCount;                        // Ref count so we can cleanup when all threads 
  57.                                             // are done w/ this object
  58.     DPNID dpnidPlayer;                      // DPNID of player
  59.     TCHAR strPlayerName[MAX_PLAYER_NAME];   // Player name
  60. };
  61.  
  62.  
  63.  
  64. //-----------------------------------------------------------------------------
  65. // Global variables
  66. //-----------------------------------------------------------------------------
  67. IDirectPlay8Peer*  g_pDP                         = NULL;    // DirectPlay peer object
  68. CNetConnectWizard* g_pNetConnectWizard           = NULL;    // Connection wizard
  69. IDirectPlay8LobbiedApplication* g_pLobbiedApp    = NULL;    // DirectPlay lobbied app 
  70. BOOL               g_bWasLobbyLaunched           = FALSE;   // TRUE if lobby launched
  71. HINSTANCE          g_hInst                       = NULL;    // HINST of app
  72. HWND               g_hDlg                        = NULL;    // HWND of main dialog
  73. DPNID              g_dpnidLocalPlayer            = 0;       // DPNID of local player
  74. LONG               g_lNumberOfActivePlayers      = 0;       // Number of players currently in game
  75. TCHAR              g_strAppName[256]             = TEXT("ChatPeer");
  76. HRESULT            g_hrDialog;                              // Exit code for app 
  77. TCHAR              g_strLocalPlayerName[MAX_PATH];          // Local player name
  78. TCHAR              g_strSessionName[MAX_PATH];              // Session name
  79. TCHAR              g_strPreferredProvider[MAX_PATH];        // Provider string
  80.  
  81.  
  82.  
  83.  
  84. //-----------------------------------------------------------------------------
  85. // App specific DirectPlay messages and structures 
  86. //-----------------------------------------------------------------------------
  87. #define GAME_MSGID_CHAT    1
  88.  
  89. // Change compiler pack alignment to be BYTE aligned, and pop the current value
  90. #pragma pack( push, 1 )
  91.  
  92. struct GAMEMSG_GENERIC
  93. {
  94.     BYTE nType;
  95. };
  96.  
  97. struct GAMEMSG_CHAT : public GAMEMSG_GENERIC
  98. {
  99.     TCHAR strChatString[MAX_CHAT_STRING_LENGTH];
  100. };
  101.  
  102. // Pop the old pack alignment
  103. #pragma pack( pop )
  104.  
  105.  
  106. struct APP_QUEUED_DATA 
  107. {
  108.     GAMEMSG_CHAT* pChatMsg;
  109.     DPNHANDLE hBufferHandle;
  110. };
  111.  
  112.  
  113.  
  114. //-----------------------------------------------------------------------------
  115. // Function-prototypes
  116. //-----------------------------------------------------------------------------
  117. HRESULT WINAPI DirectPlayMessageHandler( PVOID pvUserContext, DWORD dwMessageId, PVOID pMsgBuffer );
  118. HRESULT WINAPI DirectPlayLobbyMessageHandler( PVOID pvUserContext, DWORD dwMessageId, PVOID pMsgBuffer );
  119. INT_PTR CALLBACK ChatDlgProc( HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam );
  120. HRESULT        InitDirectPlay();
  121. HRESULT        OnInitDialog( HWND hDlg );
  122. HRESULT        SendChatMessage( HWND hDlg );
  123.  
  124.  
  125.  
  126.  
  127. //-----------------------------------------------------------------------------
  128. // Name: WinMain()
  129. // Desc: Entry point for the application.  Since we use a simple dialog for 
  130. //       user interaction we don't need to pump messages.
  131. //-----------------------------------------------------------------------------
  132. INT APIENTRY WinMain( HINSTANCE hInst, HINSTANCE hPrevInst, 
  133.                       LPSTR pCmdLine, INT nCmdShow )
  134. {
  135.     HRESULT hr;
  136.     HKEY    hDPlaySampleRegKey;
  137.     BOOL    bConnectSuccess = FALSE;
  138.  
  139.     g_hInst = hInst;
  140.     InitializeCriticalSection( &g_csPlayerContext );
  141.  
  142.     // Read persistent state information from registry
  143.     RegCreateKeyEx( HKEY_CURRENT_USER, DPLAY_SAMPLE_KEY, 0, NULL,
  144.                     REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, 
  145.                     &hDPlaySampleRegKey, NULL );
  146.     DXUtil_ReadStringRegKey( hDPlaySampleRegKey, TEXT("Player Name"), 
  147.                              g_strLocalPlayerName, MAX_PATH, TEXT("TestPlayer") );
  148.     DXUtil_ReadStringRegKey( hDPlaySampleRegKey, TEXT("Session Name"), 
  149.                              g_strSessionName, MAX_PATH, TEXT("TestGame") );
  150.     DXUtil_ReadStringRegKey( hDPlaySampleRegKey, TEXT("Preferred Provider"), 
  151.                              g_strPreferredProvider, MAX_PATH, TEXT("DirectPlay8 TCP/IP Service Provider") );
  152.  
  153.     // Init COM so we can use CoCreateInstance
  154.     CoInitializeEx( NULL, COINIT_MULTITHREADED );
  155.  
  156.     // Create helper class
  157.     g_pNetConnectWizard = new CNetConnectWizard( hInst, g_strAppName, &g_guidApp );
  158.  
  159.     if( FAILED( hr = InitDirectPlay() ) )
  160.     {
  161.         DXTRACE_ERR( TEXT("InitDirectPlay"), hr );
  162.         MessageBox( NULL, TEXT("Failed initializing IDirectPlay8Peer. ")
  163.                     TEXT("The sample will now quit."),
  164.                     TEXT("DirectPlay Sample"), MB_OK | MB_ICONERROR );
  165.         return FALSE;
  166.     }
  167.  
  168.     // Check if we were launched from a lobby client
  169.     if( g_bWasLobbyLaunched && g_pNetConnectWizard->HaveConnectionSettingsFromLobby() )
  170.     {
  171.         // If were lobby launched then DPL_MSGID_CONNECT has already been
  172.         // handled, so we can just tell the wizard to connect to the lobby
  173.         // that has sent us a DPL_MSGID_CONNECT msg.
  174.         if( FAILED( hr = g_pNetConnectWizard->ConnectUsingLobbySettings() ) )
  175.         {
  176.             DXTRACE_ERR( TEXT("ConnectUsingLobbySettings"), hr );
  177.             MessageBox( NULL, TEXT("Failed to connect using lobby settings. ")
  178.                         TEXT("The sample will now quit."),
  179.                         TEXT("DirectPlay Sample"), MB_OK | MB_ICONERROR );
  180.  
  181.             bConnectSuccess = FALSE;
  182.         }
  183.         else
  184.         {
  185.             // Read information from g_pNetConnectWizard
  186.             _tcscpy( g_strLocalPlayerName, g_pNetConnectWizard->GetPlayerName() );
  187.  
  188.             bConnectSuccess = TRUE; 
  189.         }
  190.     }
  191.     else
  192.     {
  193.         // If not lobby launched, prompt the user about the network 
  194.         // connection and which session they would like to join or 
  195.         // if they want to create a new one.
  196.  
  197.         // Setup connection wizard
  198.         g_pNetConnectWizard->SetPlayerName( g_strLocalPlayerName );
  199.         g_pNetConnectWizard->SetSessionName( g_strSessionName );
  200.         g_pNetConnectWizard->SetPreferredProvider( g_strPreferredProvider );
  201.  
  202.         // Do the connection wizard
  203.         hr = g_pNetConnectWizard->DoConnectWizard( FALSE );        
  204.         if( FAILED( hr ) ) 
  205.         {
  206.             DXTRACE_ERR( TEXT("DoConnectWizard"), hr );
  207.             MessageBox( NULL, TEXT("Mutliplayer connect failed. ")
  208.                         TEXT("The sample will now quit."),
  209.                         TEXT("DirectPlay Sample"), MB_OK | MB_ICONERROR );
  210.             bConnectSuccess = FALSE;
  211.         } 
  212.         else if( hr == NCW_S_QUIT ) 
  213.         {
  214.             // The user canceled the mutliplayer connect, so quit 
  215.             bConnectSuccess = FALSE;
  216.         }
  217.         else
  218.         {
  219.             bConnectSuccess = TRUE; 
  220.  
  221.             // Read information from g_pNetConnectWizard
  222.             _tcscpy( g_strLocalPlayerName, g_pNetConnectWizard->GetPlayerName() );
  223.             _tcscpy( g_strSessionName, g_pNetConnectWizard->GetSessionName() );
  224.             _tcscpy( g_strPreferredProvider, g_pNetConnectWizard->GetPreferredProvider() );
  225.  
  226.             // Write information to the registry
  227.             DXUtil_WriteStringRegKey( hDPlaySampleRegKey, TEXT("Player Name"), g_strLocalPlayerName );
  228.             DXUtil_WriteStringRegKey( hDPlaySampleRegKey, TEXT("Session Name"), g_strSessionName );
  229.             DXUtil_WriteStringRegKey( hDPlaySampleRegKey, TEXT("Preferred Provider"), g_strPreferredProvider );
  230.         }
  231.     }
  232.  
  233.     if( bConnectSuccess )
  234.     {
  235.         // App is now connected via DirectPlay, so start the game.  
  236.  
  237.         // For this sample, we just start a simple dialog box game.
  238.         g_hrDialog = S_OK;
  239.         DialogBox( hInst, MAKEINTRESOURCE(IDD_MAIN_GAME), NULL, (DLGPROC) ChatDlgProc );
  240.  
  241.         if( FAILED( g_hrDialog ) )
  242.         {
  243.             if( g_hrDialog == DPNERR_CONNECTIONLOST )
  244.             {
  245.                 MessageBox( NULL, TEXT("The DirectPlay session was lost. ")
  246.                             TEXT("The sample will now quit."),
  247.                             TEXT("DirectPlay Sample"), MB_OK | MB_ICONERROR );
  248.             }
  249.             else
  250.             {
  251.                 DXTRACE_ERR( TEXT("DialogBox"), g_hrDialog );
  252.                 MessageBox( NULL, TEXT("An error occured during the game. ")
  253.                             TEXT("The sample will now quit."),
  254.                             TEXT("DirectPlay Sample"), MB_OK | MB_ICONERROR );
  255.             }
  256.         }
  257.     }
  258.  
  259.     // Cleanup DirectPlay and helper classes
  260.     g_pNetConnectWizard->Shutdown();
  261.  
  262.     if( g_pDP )
  263.     {
  264.         g_pDP->Close(0);
  265.         SAFE_RELEASE( g_pDP );
  266.     }
  267.  
  268.     if( g_pLobbiedApp )
  269.     {
  270.         g_pLobbiedApp->Close( 0 );
  271.         SAFE_RELEASE( g_pLobbiedApp );
  272.     }    
  273.  
  274.     // Don't delete the wizard until we know that 
  275.     // DirectPlay is out of its message handlers.
  276.     // This will be true after Close() has been called. 
  277.     SAFE_DELETE( g_pNetConnectWizard );
  278.  
  279.     RegCloseKey( hDPlaySampleRegKey );
  280.     DeleteCriticalSection( &g_csPlayerContext );
  281.     CoUninitialize();
  282.  
  283.     return TRUE;
  284. }
  285.  
  286.  
  287.  
  288.  
  289. //-----------------------------------------------------------------------------
  290. // Name: InitDirectPlay()
  291. // Desc: 
  292. //-----------------------------------------------------------------------------
  293. HRESULT InitDirectPlay()
  294. {
  295.     DPNHANDLE hLobbyLaunchedConnection = NULL;
  296.     HRESULT hr;
  297.  
  298.     // Create IDirectPlay8Peer
  299.     if( FAILED( hr = CoCreateInstance( CLSID_DirectPlay8Peer, NULL, 
  300.                                        CLSCTX_INPROC_SERVER,
  301.                                        IID_IDirectPlay8Peer, 
  302.                                        (LPVOID*) &g_pDP ) ) )
  303.         return DXTRACE_ERR( TEXT("CoCreateInstance"), hr );
  304.  
  305.     // Create IDirectPlay8LobbiedApplication
  306.     if( FAILED( hr = CoCreateInstance( CLSID_DirectPlay8LobbiedApplication, NULL, 
  307.                                        CLSCTX_INPROC_SERVER,
  308.                                        IID_IDirectPlay8LobbiedApplication, 
  309.                                        (LPVOID*) &g_pLobbiedApp ) ) )
  310.         return DXTRACE_ERR( TEXT("CoCreateInstance"), hr );
  311.  
  312.     // Init the helper class, now that g_pDP and g_pLobbiedApp are valid
  313.     g_pNetConnectWizard->Init( g_pDP, g_pLobbiedApp );
  314.  
  315.     // Init IDirectPlay8Peer
  316.     if( FAILED( hr = g_pDP->Initialize( NULL, DirectPlayMessageHandler, 0 ) ) )
  317.         return DXTRACE_ERR( TEXT("Initialize"), hr );
  318.  
  319.     // Init IDirectPlay8LobbiedApplication.  Before this Initialize() returns 
  320.     // a DPL_MSGID_CONNECT msg may come in to the DirectPlayLobbyMessageHandler 
  321.     // so be prepared ahead of time.
  322.     if( FAILED( hr = g_pLobbiedApp->Initialize( NULL, DirectPlayLobbyMessageHandler, 
  323.                                                 &hLobbyLaunchedConnection, 0 ) ) )
  324.         return DXTRACE_ERR( TEXT("Initialize"), hr );
  325.  
  326.     // IDirectPlay8LobbiedApplication::Initialize returns a handle to a connnection
  327.     // if we have been lobby launced.  Initialize is guanteeded to return after 
  328.     // the DPL_MSGID_CONNECT msg has been processed.  So unless a we are expected 
  329.     // multiple lobby connections, we do not need to remember the lobby connection
  330.     // handle since it will be recorded upon the DPL_MSGID_CONNECT msg.
  331.     g_bWasLobbyLaunched = ( hLobbyLaunchedConnection != NULL );
  332.  
  333.     return S_OK;
  334. }
  335.  
  336.  
  337.  
  338.  
  339. //-----------------------------------------------------------------------------
  340. // Name: ChatDlgProc()
  341. // Desc: Handles dialog messages
  342. //-----------------------------------------------------------------------------
  343. INT_PTR CALLBACK ChatDlgProc( HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam )
  344. {
  345.     switch( msg ) 
  346.     {
  347.         case WM_INITDIALOG:
  348.         {
  349.             g_hDlg = hDlg;
  350.             if( FAILED( g_hrDialog = OnInitDialog( hDlg ) ) )
  351.             {
  352.                 DXTRACE_ERR( TEXT("OnInitDialog"), g_hrDialog );
  353.                 EndDialog( hDlg, 0 );
  354.             }
  355.             break;
  356.         }
  357.  
  358.         case WM_APP_UPDATE_STATS:
  359.         {
  360.             // Update the number of players in the game
  361.             TCHAR strNumberPlayers[32];
  362.  
  363.             wsprintf( strNumberPlayers, TEXT("%d"), g_lNumberOfActivePlayers );
  364.             SetDlgItemText( hDlg, IDC_NUM_PLAYERS, strNumberPlayers );
  365.             break;
  366.         }
  367.  
  368.         case WM_APP_CHAT:
  369.         {
  370.             HRESULT          hr;
  371.             DPNID            dpnidPlayer = (DPNID) wParam;
  372.             APP_PLAYER_INFO* pPlayerInfo = NULL;
  373.  
  374.             PLAYER_LOCK(); // enter player context CS
  375.  
  376.             // Get the player context accosicated with this DPNID
  377.             hr = g_pDP->GetPlayerContext( dpnidPlayer, 
  378.                                           (LPVOID* const) &pPlayerInfo,
  379.                                           0);
  380.  
  381.             PLAYER_ADDREF( pPlayerInfo ); // addref player, since we are using it now
  382.             PLAYER_UNLOCK(); // leave player context CS
  383.  
  384.             if( FAILED(hr) || pPlayerInfo == NULL )
  385.             {
  386.                 // The player who sent this may have gone away before this 
  387.                 // message was handled, so just ignore it
  388.                 break;
  389.             }
  390.             
  391.             APP_QUEUED_DATA* pQueuedData = (APP_QUEUED_DATA*) lParam;
  392.  
  393.             // Add the message to the local listbox
  394.             HWND hWndChatBox = GetDlgItem( hDlg, IDC_CHAT_LISTBOX );
  395.             int nCount = (int)SendMessage( hWndChatBox, LB_GETCOUNT, 0, 0 );
  396.             if( nCount > MAX_CHAT_STRINGS )
  397.                 SendMessage( hWndChatBox, LB_DELETESTRING, 0, 0 );
  398.  
  399.             // Make the chat string from the player's name and the edit box string
  400.             TCHAR strChatBuffer[MAX_PLAYER_NAME + MAX_CHAT_STRING_LENGTH + 32];
  401.             wsprintf( strChatBuffer, TEXT("<%s> %s"), pPlayerInfo->strPlayerName, pQueuedData->pChatMsg->strChatString );
  402.  
  403.             PLAYER_LOCK(); // enter player context CS
  404.             PLAYER_RELEASE( pPlayerInfo );  // Release player and cleanup if needed
  405.             PLAYER_UNLOCK(); // leave player context CS            
  406.  
  407.             // Add it, and make sure it is visible
  408.             int nIndex = (int)SendMessage( hWndChatBox, LB_ADDSTRING, 0, (LPARAM)strChatBuffer );
  409.             SendMessage( hWndChatBox, LB_SETTOPINDEX, nIndex, 0 );
  410.  
  411.             // Done with the buffer, so return it DirectPlay, 
  412.             // so that the memory can be reused
  413.             g_pDP->ReturnBuffer( pQueuedData->hBufferHandle,0 );
  414.             SAFE_DELETE( pQueuedData );
  415.             break;
  416.         }
  417.  
  418.         case WM_COMMAND:
  419.             switch( LOWORD(wParam) )
  420.             {
  421.                 case IDC_CHAT_EDIT:
  422.                     if( HIWORD(wParam) == EN_UPDATE )
  423.                     {
  424.                         BOOL bEnableSend;
  425.                         if( 0 == GetWindowTextLength( GetDlgItem( hDlg, IDC_CHAT_EDIT ) ) )
  426.                             bEnableSend = FALSE;
  427.                         else
  428.                             bEnableSend = TRUE;
  429.  
  430.                         EnableWindow( GetDlgItem( hDlg, IDC_SEND ), bEnableSend );
  431.                     }
  432.                     break;
  433.  
  434.                 case IDC_SEND:
  435.                     // The enter key was pressed, so send out the chat message
  436.                     if( FAILED( g_hrDialog = SendChatMessage( hDlg ) ) )
  437.                     {
  438.                         DXTRACE_ERR( TEXT("SendChatMessage"), g_hrDialog );
  439.                         EndDialog( hDlg, 0 );
  440.                     }
  441.                     break;
  442.  
  443.                 case IDCANCEL:
  444.                     g_hrDialog = S_OK;
  445.                     EndDialog( hDlg, 0 );
  446.                     return TRUE;
  447.             }
  448.             break;
  449.     }
  450.  
  451.     return FALSE; // Didn't handle message
  452. }
  453.  
  454.  
  455.  
  456.  
  457. //-----------------------------------------------------------------------------
  458. // Name: OnInitDialog()
  459. // Desc: Inits the dialog for the chat client.
  460. //-----------------------------------------------------------------------------
  461. HRESULT OnInitDialog( HWND hDlg )
  462. {
  463.     // Load and set the icon
  464.     HICON hIcon = LoadIcon( g_hInst, MAKEINTRESOURCE( IDI_MAIN ) );
  465.     SendMessage( hDlg, WM_SETICON, ICON_BIG,   (LPARAM) hIcon );  // Set big icon
  466.     SendMessage( hDlg, WM_SETICON, ICON_SMALL, (LPARAM) hIcon );  // Set small icon
  467.  
  468.     // Display local player's name
  469.     SetDlgItemText( hDlg, IDC_PLAYER_NAME, g_strLocalPlayerName );
  470.  
  471.     PostMessage( hDlg, WM_APP_UPDATE_STATS, 0, 0 );
  472.     SetFocus( GetDlgItem( hDlg, IDC_CHAT_EDIT ) );
  473.  
  474.     SendMessage( GetDlgItem( hDlg, IDC_CHAT_EDIT ), EM_SETEVENTMASK, 0, ENM_UPDATE );
  475.     EnableWindow( GetDlgItem( hDlg, IDC_SEND ), FALSE );
  476.  
  477.     if( g_pNetConnectWizard->IsHostPlayer() )
  478.         SetWindowText( hDlg, TEXT("ChatPeer (Host)") );
  479.     else
  480.         SetWindowText( hDlg, TEXT("ChatPeer") );
  481.  
  482.     return S_OK;
  483. }
  484.  
  485.  
  486.  
  487.  
  488. //-----------------------------------------------------------------------------
  489. // Name: DirectPlayMessageHandler
  490. // Desc: Handler for DirectPlay messages.  This function is called by
  491. //       the DirectPlay message handler pool of threads, so be care of thread
  492. //       synchronization problems with shared memory
  493. //-----------------------------------------------------------------------------
  494. HRESULT WINAPI DirectPlayMessageHandler( PVOID pvUserContext, 
  495.                                          DWORD dwMessageId, 
  496.                                          PVOID pMsgBuffer )
  497. {
  498.     // Try not to stay in this message handler for too long, otherwise
  499.     // there will be a backlog of data.  The best solution is to 
  500.     // queue data as it comes in, and then handle it on other threads
  501.     // as this sample shows
  502.  
  503.     // This function is called by the DirectPlay message handler pool of 
  504.     // threads, so be care of thread synchronization problems with shared memory
  505.  
  506.     HRESULT hReturn = S_OK;
  507.  
  508.     switch( dwMessageId )
  509.     {
  510.         case DPN_MSGID_CREATE_PLAYER:
  511.         {
  512.             HRESULT hr;
  513.             PDPNMSG_CREATE_PLAYER pCreatePlayerMsg;
  514.             pCreatePlayerMsg = (PDPNMSG_CREATE_PLAYER)pMsgBuffer;
  515.  
  516.             // Get the peer info and extract its name
  517.             DWORD dwSize = 0;
  518.             DPN_PLAYER_INFO* pdpPlayerInfo = NULL;
  519.             hr = g_pDP->GetPeerInfo( pCreatePlayerMsg->dpnidPlayer, pdpPlayerInfo, &dwSize, 0 );
  520.             if( FAILED(hr) && hr != DPNERR_BUFFERTOOSMALL )
  521.                 return DXTRACE_ERR( TEXT("GetPeerInfo"), hr );
  522.             pdpPlayerInfo = (DPN_PLAYER_INFO*) new BYTE[ dwSize ];
  523.             ZeroMemory( pdpPlayerInfo, dwSize );
  524.             pdpPlayerInfo->dwSize = sizeof(DPN_PLAYER_INFO);
  525.             hr = g_pDP->GetPeerInfo( pCreatePlayerMsg->dpnidPlayer, pdpPlayerInfo, &dwSize, 0 );
  526.             if( FAILED(hr) )
  527.                 return DXTRACE_ERR( TEXT("GetPeerInfo"), hr );
  528.  
  529.             // Create a new and fill in a APP_PLAYER_INFO
  530.             APP_PLAYER_INFO* pPlayerInfo = new APP_PLAYER_INFO;
  531.             ZeroMemory( pPlayerInfo, sizeof(APP_PLAYER_INFO) );
  532.             pPlayerInfo->dpnidPlayer = pCreatePlayerMsg->dpnidPlayer;
  533.             pPlayerInfo->lRefCount   = 1;
  534.  
  535.             // This stores a extra TCHAR copy of the player name for 
  536.             // easier access.  This will be redundent copy since DPlay 
  537.             // also keeps a copy of the player name in GetPeerInfo()
  538.             DXUtil_ConvertWideStringToGeneric( pPlayerInfo->strPlayerName, 
  539.                                                pdpPlayerInfo->pwszName, MAX_PLAYER_NAME );
  540.  
  541.             if( pdpPlayerInfo->dwPlayerFlags & DPNPLAYER_LOCAL )
  542.                 g_dpnidLocalPlayer = pCreatePlayerMsg->dpnidPlayer;
  543.  
  544.             SAFE_DELETE_ARRAY( pdpPlayerInfo );
  545.  
  546.             // Tell DirectPlay to store this pPlayerInfo 
  547.             // pointer in the pvPlayerContext.
  548.             pCreatePlayerMsg->pvPlayerContext = pPlayerInfo;
  549.  
  550.             // Update the number of active players, and 
  551.             // post a message to the dialog thread to update the 
  552.             // UI.  This keeps the DirectPlay message handler 
  553.             // from blocking
  554.             InterlockedIncrement( &g_lNumberOfActivePlayers );
  555.             if( g_hDlg != NULL )
  556.                 PostMessage( g_hDlg, WM_APP_UPDATE_STATS, 0, 0 );
  557.  
  558.             break;
  559.         }
  560.  
  561.         case DPN_MSGID_DESTROY_PLAYER:
  562.         {
  563.             PDPNMSG_DESTROY_PLAYER pDestroyPlayerMsg;
  564.             pDestroyPlayerMsg = (PDPNMSG_DESTROY_PLAYER)pMsgBuffer;
  565.             APP_PLAYER_INFO* pPlayerInfo = (APP_PLAYER_INFO*) pDestroyPlayerMsg->pvPlayerContext;
  566.  
  567.             PLAYER_LOCK();                  // enter player context CS
  568.             PLAYER_RELEASE( pPlayerInfo );  // Release player and cleanup if needed
  569.             PLAYER_UNLOCK();                // leave player context CS
  570.  
  571.             // Update the number of active players, and 
  572.             // post a message to the dialog thread to update the 
  573.             // UI.  This keeps the DirectPlay message handler 
  574.             // from blocking
  575.             InterlockedDecrement( &g_lNumberOfActivePlayers );
  576.             if( g_hDlg != NULL )
  577.                 PostMessage( g_hDlg, WM_APP_UPDATE_STATS, 0, 0 );
  578.  
  579.             break;
  580.         }
  581.  
  582.         case DPN_MSGID_HOST_MIGRATE:
  583.         {
  584.             PDPNMSG_HOST_MIGRATE pHostMigrateMsg;
  585.             pHostMigrateMsg = (PDPNMSG_HOST_MIGRATE)pMsgBuffer;
  586.  
  587.             if( pHostMigrateMsg->dpnidNewHost == g_dpnidLocalPlayer )
  588.                 SetWindowText( g_hDlg, TEXT("ChatPeer (Host)") );
  589.             break;
  590.         }
  591.  
  592.         case DPN_MSGID_TERMINATE_SESSION:
  593.         {
  594.             PDPNMSG_TERMINATE_SESSION pTerminateSessionMsg;
  595.             pTerminateSessionMsg = (PDPNMSG_TERMINATE_SESSION)pMsgBuffer;
  596.  
  597.             g_hrDialog = DPNERR_CONNECTIONLOST;
  598.             EndDialog( g_hDlg, 0 );
  599.             break;
  600.         }
  601.  
  602.         case DPN_MSGID_RECEIVE:
  603.         {
  604.             PDPNMSG_RECEIVE pReceiveMsg;
  605.             pReceiveMsg = (PDPNMSG_RECEIVE)pMsgBuffer;
  606.             APP_PLAYER_INFO* pPlayerInfo = (APP_PLAYER_INFO*) pReceiveMsg->pvPlayerContext;
  607.  
  608.             GAMEMSG_GENERIC* pMsg = (GAMEMSG_GENERIC*) pReceiveMsg->pReceiveData;
  609.             if( pMsg->nType == GAME_MSGID_CHAT )
  610.             {
  611.                 // This message is sent when a player has send a chat message to us, so 
  612.                 // post a message to the dialog thread to update the UI.  
  613.                 // This keeps the DirectPlay threads from blocking, and also
  614.                 // serializes the recieves since DirectPlayMessageHandler can
  615.                 // be called simultaneously from a pool of DirectPlay threads.
  616.                 GAMEMSG_CHAT* pChatMessage = (GAMEMSG_CHAT*) pMsg;
  617.  
  618.                 // Record the buffer handle so the buffer can be returned later 
  619.                 APP_QUEUED_DATA* pQueuedData = new APP_QUEUED_DATA;
  620.                 pQueuedData->hBufferHandle = pReceiveMsg->hBufferHandle;
  621.                 pQueuedData->pChatMsg      = pChatMessage;
  622.  
  623.                 // Pass the APP_QUEUED_DATA to the main dialog thread, so it can
  624.                 // process it.  It will also cleanup the struct
  625.                 PostMessage( g_hDlg, WM_APP_CHAT, 
  626.                              pPlayerInfo->dpnidPlayer, (LPARAM) pQueuedData );
  627.  
  628.                 // Tell DirectPlay to assume that ownership of the buffer 
  629.                 // has been transferred to the application, and so it will 
  630.                 // neither free nor modify it until ownership is returned 
  631.                 // to DirectPlay through the ReturnBuffer() call.
  632.                 hReturn = DPNSUCCESS_PENDING;
  633.             }
  634.             break;
  635.         }
  636.     }
  637.  
  638.     // Make sure the DirectPlay MessageHandler calls the CNetConnectWizard handler, 
  639.     // so it can be informed of messages such as DPN_MSGID_ENUM_HOSTS_RESPONSE.
  640.     if( hReturn != DPNSUCCESS_PENDING && SUCCEEDED(hReturn) && g_pNetConnectWizard )
  641.         hReturn = g_pNetConnectWizard->MessageHandler( pvUserContext, dwMessageId, pMsgBuffer );
  642.     
  643.     return hReturn;
  644. }
  645.  
  646.  
  647.  
  648.  
  649. //-----------------------------------------------------------------------------
  650. // Name: DirectPlayLobbyMessageHandler
  651. // Desc: Handler for DirectPlay lobby messages.  This function is called by
  652. //       the DirectPlay lobby message handler pool of threads, so be careful of 
  653. //       thread synchronization problems with shared memory
  654. //-----------------------------------------------------------------------------
  655. HRESULT WINAPI DirectPlayLobbyMessageHandler( PVOID pvUserContext, 
  656.                                               DWORD dwMessageId, 
  657.                                               PVOID pMsgBuffer )
  658. {
  659.     switch( dwMessageId )
  660.     {
  661.         case DPL_MSGID_CONNECT:
  662.         {
  663.             PDPL_MESSAGE_CONNECT pConnectMsg;
  664.             pConnectMsg = (PDPL_MESSAGE_CONNECT)pMsgBuffer;
  665.  
  666.             // The CNetConnectWizard will handle this message for us,
  667.             // so there is nothing we need to do here for this simple
  668.             // sample.
  669.             break;
  670.         }
  671.  
  672.         case DPL_MSGID_DISCONNECT:
  673.         {
  674.             PDPL_MESSAGE_DISCONNECT pDisconnectMsg;
  675.             pDisconnectMsg = (PDPL_MESSAGE_DISCONNECT)pMsgBuffer;
  676.  
  677.             // We should free any data associated with the lobby 
  678.             // client here, but there is none.
  679.             break;
  680.         }
  681.  
  682.         case DPL_MSGID_RECEIVE:
  683.         {
  684.             PDPL_MESSAGE_RECEIVE pReceiveMsg;
  685.             pReceiveMsg = (PDPL_MESSAGE_RECEIVE)pMsgBuffer;
  686.  
  687.             // The lobby client sent us data.  This sample doesn't
  688.             // expected data from the client, but it is useful 
  689.             // for more complex apps.
  690.             break;
  691.         }
  692.  
  693.         case DPL_MSGID_CONNECTION_SETTINGS:
  694.         {
  695.             PDPL_MESSAGE_CONNECTION_SETTINGS pConnectionStatusMsg;
  696.             pConnectionStatusMsg = (PDPL_MESSAGE_CONNECTION_SETTINGS)pMsgBuffer;
  697.  
  698.             // The lobby client has changed the connection settings.  
  699.             // This simple sample doesn't handle this, but more complex apps may
  700.             // want to.
  701.             break;
  702.         }
  703.     }
  704.  
  705.     // Make sure the DirectPlay MessageHandler calls the CNetConnectWizard handler, 
  706.     // so the wizard can be informed of lobby messages such as DPL_MSGID_CONNECT
  707.     if( g_pNetConnectWizard )
  708.         return g_pNetConnectWizard->LobbyMessageHandler( pvUserContext, dwMessageId, 
  709.                                                          pMsgBuffer );
  710.     
  711.     return S_OK;
  712. }
  713.  
  714.  
  715.  
  716.  
  717. //-----------------------------------------------------------------------------
  718. // Name: SendChatMessage()
  719. // Desc: Create chat string based on the editbox and send it to everyone 
  720. //-----------------------------------------------------------------------------
  721. HRESULT SendChatMessage( HWND hDlg )
  722. {
  723.     // Get length of item text
  724.     DWORD dwEditboxBufferSize = (DWORD)SendDlgItemMessage( hDlg, IDC_CHAT_EDIT, 
  725.                                                            WM_GETTEXTLENGTH, 0, 0 );
  726.     if( dwEditboxBufferSize == 0 )
  727.         return S_OK;  // Don't do anything for blank messages 
  728.  
  729.     GAMEMSG_CHAT msgChat;
  730.     msgChat.nType = GAME_MSGID_CHAT;
  731.     GetDlgItemText( hDlg, IDC_CHAT_EDIT, msgChat.strChatString, MAX_CHAT_STRING_LENGTH );
  732.  
  733.     // Set the dwBufferSize to only send the string that has valid text in it.  
  734.     // Otherwise bandwidth is wasted.
  735.     DPN_BUFFER_DESC bufferDesc;
  736.     bufferDesc.dwBufferSize = sizeof(msgChat.nType) + 
  737.                               (_tcslen(msgChat.strChatString)+1)*sizeof(TCHAR);
  738.     bufferDesc.pBufferData  = (BYTE*) &msgChat;
  739.  
  740.     // Send it to all of the players include the local client
  741.     // DirectPlay will tell via the message handler 
  742.     // if there are any severe errors, so ignore any errors 
  743.     DPNHANDLE hAsync;
  744.     g_pDP->SendTo( DPNID_ALL_PLAYERS_GROUP, &bufferDesc, 1,
  745.                    0, NULL, &hAsync, 0 );
  746.  
  747.     // Blank out edit box
  748.     SetDlgItemText( hDlg, IDC_CHAT_EDIT, TEXT("") );
  749.  
  750.     return S_OK;
  751. }
  752.  
  753.  
  754.  
  755.  
  756.  
  757.