home *** CD-ROM | disk | FTP | other *** search
/ Beginning Direct3D Game Programming / Direct3D.iso / directx / dxf / samples / multimedia / directplay / simplepeer / simplepeer.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2000-11-04  |  28.1 KB  |  710 lines

  1. //----------------------------------------------------------------------------
  2. // File: SimplePeer.cpp
  3. //
  4. // Desc: The main game file for the SimplePeer 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. //       game called "The Greeting Game".  When two or more players are connected
  11. //       to the game, the players have the option of sending a single simple 
  12. //       DirectPlay message to all of the other players. When this message
  13. //       is receieved by the other players, they simply display a dialog box.
  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 <dplay8.h>
  21. #include <dplobby8.h>
  22. #include <dxerr8.h>
  23. #include "NetConnect.h"
  24. #include "DXUtil.h"
  25. #include "resource.h"
  26.  
  27.  
  28.  
  29.  
  30. //-----------------------------------------------------------------------------
  31. // Player context locking defines
  32. //-----------------------------------------------------------------------------
  33. CRITICAL_SECTION g_csPlayerContext;
  34. #define PLAYER_LOCK()                   EnterCriticalSection( &g_csPlayerContext ); 
  35. #define PLAYER_ADDREF( pPlayerInfo )    if( pPlayerInfo ) pPlayerInfo->lRefCount++;
  36. #define PLAYER_RELEASE( pPlayerInfo )   if( pPlayerInfo ) { pPlayerInfo->lRefCount--; if( pPlayerInfo->lRefCount <= 0 ) SAFE_DELETE( pPlayerInfo ); } pPlayerInfo = NULL;
  37. #define PLAYER_UNLOCK()                 LeaveCriticalSection( &g_csPlayerContext );
  38.  
  39.  
  40. //-----------------------------------------------------------------------------
  41. // Defines, and constants
  42. //-----------------------------------------------------------------------------
  43. #define DPLAY_SAMPLE_KEY        TEXT("Software\\Microsoft\\DirectX DirectPlay Samples")
  44. #define MAX_PLAYER_NAME         14
  45. #define WM_APP_UPDATE_STATS    (WM_APP + 0)
  46. #define WM_APP_DISPLAY_WAVE    (WM_APP + 1)
  47.  
  48. // This GUID allows DirectPlay to find other instances of the same game on
  49. // the network.  So it must be unique for every game, and the same for 
  50. // every instance of that game.  // {02AE835D-9179-485f-8343-901D327CE794}
  51. GUID g_guidApp = { 0x2ae835d, 0x9179, 0x485f, { 0x83, 0x43, 0x90, 0x1d, 0x32, 0x7c, 0xe7, 0x94 } };
  52.  
  53. struct APP_PLAYER_INFO
  54. {
  55.     LONG  lRefCount;                        // Ref count so we can cleanup when all threads 
  56.                                             // are done w/ this object
  57.     DPNID dpnidPlayer;                      // DPNID of player
  58.     TCHAR strPlayerName[MAX_PLAYER_NAME];   // Player name
  59. };
  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("SimplePeer");
  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_WAVE        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.     DWORD dwType;
  95. };
  96.  
  97. // Pop the old pack alignment
  98. #pragma pack( pop )
  99.  
  100.  
  101.  
  102. //-----------------------------------------------------------------------------
  103. // Function-prototypes
  104. //-----------------------------------------------------------------------------
  105. HRESULT WINAPI   DirectPlayMessageHandler( PVOID pvUserContext, DWORD dwMessageId, PVOID pMsgBuffer );
  106. HRESULT WINAPI   DirectPlayLobbyMessageHandler( PVOID pvUserContext, DWORD dwMessageId, PVOID pMsgBuffer );
  107. INT_PTR CALLBACK GreetingDlgProc( HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam );
  108. HRESULT  InitDirectPlay();
  109. HRESULT  WaveToAllPlayers();
  110. VOID     AppendTextToEditControl( HWND hDlg, TCHAR* strNewLogLine );
  111.  
  112.  
  113.  
  114.  
  115.  
  116. //-----------------------------------------------------------------------------
  117. // Name: WinMain()
  118. // Desc: Entry point for the application.  Since we use a simple dialog for 
  119. //       user interaction we don't need to pump messages.
  120. //-----------------------------------------------------------------------------
  121. INT APIENTRY WinMain( HINSTANCE hInst, HINSTANCE hPrevInst, 
  122.                       LPSTR pCmdLine, INT nCmdShow )
  123. {
  124.     HRESULT hr;
  125.     HKEY    hDPlaySampleRegKey;
  126.     BOOL    bConnectSuccess = FALSE;
  127.  
  128.     g_hInst = hInst; 
  129.     InitializeCriticalSection( &g_csPlayerContext );
  130.  
  131.     // Read persistent state information from registry
  132.     RegCreateKeyEx( HKEY_CURRENT_USER, DPLAY_SAMPLE_KEY, 0, NULL,
  133.                     REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, 
  134.                     &hDPlaySampleRegKey, NULL );
  135.     DXUtil_ReadStringRegKey( hDPlaySampleRegKey, TEXT("Player Name"), 
  136.                              g_strLocalPlayerName, MAX_PATH, TEXT("TestPlayer") );
  137.     DXUtil_ReadStringRegKey( hDPlaySampleRegKey, TEXT("Session Name"), 
  138.                              g_strSessionName, MAX_PATH, TEXT("TestGame") );
  139.     DXUtil_ReadStringRegKey( hDPlaySampleRegKey, TEXT("Preferred Provider"), 
  140.                              g_strPreferredProvider, MAX_PATH, 
  141.                              TEXT("DirectPlay8 TCP/IP Service Provider") );
  142.  
  143.     // Init COM so we can use CoCreateInstance
  144.     CoInitializeEx( NULL, COINIT_MULTITHREADED );
  145.  
  146.     // Create helper class
  147.     g_pNetConnectWizard = new CNetConnectWizard( hInst, g_strAppName, &g_guidApp );
  148.  
  149.     if( FAILED( hr = InitDirectPlay() ) )
  150.     {
  151.         DXTRACE_ERR( TEXT("InitDirectPlay"), hr );
  152.         MessageBox( NULL, TEXT("Failed initializing IDirectPlay8Peer. ")
  153.                     TEXT("The sample will now quit."),
  154.                     TEXT("DirectPlay Sample"), MB_OK | MB_ICONERROR );
  155.         return FALSE;
  156.     }
  157.  
  158.     // If we were launched from a lobby client, then we may have connection settings
  159.     // that we can use either host or join a game.  If not, then we'll need to prompt 
  160.     // the user to detrimine how to connect.
  161.     if( g_bWasLobbyLaunched && g_pNetConnectWizard->HaveConnectionSettingsFromLobby() )
  162.     {
  163.         // If were lobby launched then the DPL_MSGID_CONNECT has already been
  164.         // handled, and since the lobby client also sent us connection settings
  165.         // we can use them to either host or join a DirectPlay session. 
  166.         if( FAILED( hr = g_pNetConnectWizard->ConnectUsingLobbySettings() ) )
  167.         {
  168.             DXTRACE_ERR( TEXT("ConnectUsingLobbySettings"), hr );
  169.             MessageBox( NULL, TEXT("Failed to connect using lobby settings. ")
  170.                         TEXT("The sample will now quit."),
  171.                         TEXT("DirectPlay Sample"), MB_OK | MB_ICONERROR );
  172.  
  173.             bConnectSuccess = FALSE;
  174.         }
  175.         else
  176.         {
  177.             // Read information from g_pNetConnectWizard
  178.             _tcscpy( g_strLocalPlayerName, g_pNetConnectWizard->GetPlayerName() );
  179.  
  180.             bConnectSuccess = TRUE; 
  181.         }
  182.     }
  183.     else
  184.     {
  185.         // If not lobby launched, prompt the user about the network 
  186.         // connection and which session they would like to join or 
  187.         // if they want to create a new one.
  188.  
  189.         // Setup connection wizard
  190.         g_pNetConnectWizard->SetPlayerName( g_strLocalPlayerName );
  191.         g_pNetConnectWizard->SetSessionName( g_strSessionName );
  192.         g_pNetConnectWizard->SetPreferredProvider( g_strPreferredProvider );
  193.  
  194.         // Start a connection wizard.  The wizard uses GDI dialog boxes.
  195.         // More complex games can use this as a starting point and add a 
  196.         // fancier graphics layer such as Direct3D.
  197.         hr = g_pNetConnectWizard->DoConnectWizard( FALSE );        
  198.         if( FAILED( hr ) ) 
  199.         {
  200.             DXTRACE_ERR( TEXT("DoConnectWizard"), hr );
  201.             MessageBox( NULL, TEXT("Mutliplayer connect failed. ")
  202.                         TEXT("The sample will now quit."),
  203.                         TEXT("DirectPlay Sample"), MB_OK | MB_ICONERROR );
  204.             bConnectSuccess = FALSE;
  205.         } 
  206.         else if( hr == NCW_S_QUIT ) 
  207.         {
  208.             // The user canceled the mutliplayer connect, so quit 
  209.             bConnectSuccess = FALSE;
  210.         }
  211.         else
  212.         {
  213.             bConnectSuccess = TRUE; 
  214.  
  215.             // Read information from g_pNetConnectWizard
  216.             _tcscpy( g_strLocalPlayerName, g_pNetConnectWizard->GetPlayerName() );
  217.             _tcscpy( g_strSessionName, g_pNetConnectWizard->GetSessionName() );
  218.             _tcscpy( g_strPreferredProvider, g_pNetConnectWizard->GetPreferredProvider() );
  219.  
  220.             // Write information to the registry
  221.             DXUtil_WriteStringRegKey( hDPlaySampleRegKey, TEXT("Player Name"), g_strLocalPlayerName );
  222.             DXUtil_WriteStringRegKey( hDPlaySampleRegKey, TEXT("Session Name"), g_strSessionName );
  223.             DXUtil_WriteStringRegKey( hDPlaySampleRegKey, TEXT("Preferred Provider"), g_strPreferredProvider );
  224.         }
  225.     }
  226.  
  227.     if( bConnectSuccess )
  228.     {
  229.         // App is now connected via DirectPlay, so start the game.  
  230.  
  231.         // For this sample, we just start a simple dialog box game.
  232.         g_hrDialog = S_OK;
  233.         DialogBox( hInst, MAKEINTRESOURCE(IDD_MAIN_GAME), NULL, 
  234.                    (DLGPROC) GreetingDlgProc );
  235.  
  236.         if( FAILED( g_hrDialog ) )
  237.         {
  238.             if( g_hrDialog == DPNERR_CONNECTIONLOST )
  239.             {
  240.                 MessageBox( NULL, TEXT("The DirectPlay session was lost. ")
  241.                             TEXT("The sample will now quit."),
  242.                             TEXT("DirectPlay Sample"), MB_OK | MB_ICONERROR );
  243.             }
  244.             else
  245.             {
  246.                 DXTRACE_ERR( TEXT("DialogBox"), g_hrDialog );
  247.                 MessageBox( NULL, TEXT("An error occured during the game. ")
  248.                             TEXT("The sample will now quit."),
  249.                             TEXT("DirectPlay Sample"), MB_OK | MB_ICONERROR );
  250.             }
  251.         }
  252.     }
  253.  
  254.     // Cleanup DirectPlay and helper classes
  255.     g_pNetConnectWizard->Shutdown();
  256.  
  257.     if( g_pDP )
  258.     {
  259.         g_pDP->Close(0);
  260.         SAFE_RELEASE( g_pDP );
  261.     }
  262.  
  263.     if( g_pLobbiedApp )
  264.     {
  265.         g_pLobbiedApp->Close( 0 );
  266.         SAFE_RELEASE( g_pLobbiedApp );
  267.     }    
  268.  
  269.     // Don't delete the wizard until we know that 
  270.     // DirectPlay is out of its message handlers.
  271.     // This will be true after Close() has been called. 
  272.     SAFE_DELETE( g_pNetConnectWizard );
  273.  
  274.     RegCloseKey( hDPlaySampleRegKey );
  275.     DeleteCriticalSection( &g_csPlayerContext );
  276.     CoUninitialize();
  277.  
  278.     return TRUE;
  279. }
  280.  
  281.  
  282.  
  283.  
  284. //-----------------------------------------------------------------------------
  285. // Name: InitDirectPlay()
  286. // Desc: 
  287. //-----------------------------------------------------------------------------
  288. HRESULT InitDirectPlay()
  289. {
  290.     DPNHANDLE hLobbyLaunchedConnection = NULL;
  291.     HRESULT hr;
  292.  
  293.     // Create IDirectPlay8Peer
  294.     if( FAILED( hr = CoCreateInstance( CLSID_DirectPlay8Peer, NULL, 
  295.                                        CLSCTX_INPROC_SERVER,
  296.                                        IID_IDirectPlay8Peer, 
  297.                                        (LPVOID*) &g_pDP ) ) )
  298.         return DXTRACE_ERR( TEXT("CoCreateInstance"), hr );
  299.  
  300.     // Create IDirectPlay8LobbiedApplication
  301.     if( FAILED( hr = CoCreateInstance( CLSID_DirectPlay8LobbiedApplication, NULL, 
  302.                                        CLSCTX_INPROC_SERVER,
  303.                                        IID_IDirectPlay8LobbiedApplication, 
  304.                                        (LPVOID*) &g_pLobbiedApp ) ) )
  305.         return DXTRACE_ERR( TEXT("CoCreateInstance"), hr );
  306.  
  307.     // Init the helper class, now that g_pDP and g_pLobbiedApp are valid
  308.     g_pNetConnectWizard->Init( g_pDP, g_pLobbiedApp );
  309.  
  310.     // Init IDirectPlay8Peer
  311.     if( FAILED( hr = g_pDP->Initialize( NULL, DirectPlayMessageHandler, 0 ) ) )
  312.         return DXTRACE_ERR( TEXT("Initialize"), hr );
  313.  
  314.     // Init IDirectPlay8LobbiedApplication.  Before this Initialize() returns 
  315.     // a DPL_MSGID_CONNECT msg may come in to the DirectPlayLobbyMessageHandler 
  316.     // so be prepared ahead of time.
  317.     if( FAILED( hr = g_pLobbiedApp->Initialize( NULL, DirectPlayLobbyMessageHandler, 
  318.                                                 &hLobbyLaunchedConnection, 0 ) ) )
  319.         return DXTRACE_ERR( TEXT("Initialize"), hr );
  320.  
  321.     // IDirectPlay8LobbiedApplication::Initialize returns a handle to a connnection
  322.     // if we have been lobby launced.  Initialize is guanteeded to return after 
  323.     // the DPL_MSGID_CONNECT msg has been processed.  So unless a we are expected 
  324.     // multiple lobby connections, we do not need to remember the lobby connection
  325.     // handle since it will be recorded upon the DPL_MSGID_CONNECT msg.
  326.     g_bWasLobbyLaunched = ( hLobbyLaunchedConnection != NULL );
  327.  
  328.     return S_OK;
  329. }
  330.  
  331.  
  332.  
  333.  
  334. //-----------------------------------------------------------------------------
  335. // Name: GreetingDlgProc()
  336. // Desc: Handles dialog messages
  337. //-----------------------------------------------------------------------------
  338. INT_PTR CALLBACK GreetingDlgProc( HWND hDlg, UINT msg, 
  339.                                   WPARAM wParam, LPARAM lParam )
  340. {
  341.     switch( msg ) 
  342.     {
  343.         case WM_INITDIALOG:
  344.         {
  345.             g_hDlg = hDlg;
  346.  
  347.             // Load and set the icon
  348.             HICON hIcon = LoadIcon( g_hInst, MAKEINTRESOURCE( IDI_MAIN ) );
  349.             SendMessage( hDlg, WM_SETICON, ICON_BIG,   (LPARAM) hIcon );  // Set big icon
  350.             SendMessage( hDlg, WM_SETICON, ICON_SMALL, (LPARAM) hIcon );  // Set small icon
  351.  
  352.             if( g_pNetConnectWizard->IsHostPlayer() )
  353.                 SetWindowText( hDlg, TEXT("SimplePeer (Host)") );
  354.             else
  355.                 SetWindowText( hDlg, TEXT("SimplePeer") );
  356.  
  357.             // Display local player's name
  358.             SetDlgItemText( hDlg, IDC_PLAYER_NAME, g_strLocalPlayerName );
  359.  
  360.             PostMessage( hDlg, WM_APP_UPDATE_STATS, 0, 0 );
  361.             break;
  362.         }
  363.  
  364.         case WM_APP_UPDATE_STATS:
  365.         {
  366.             // Update the number of players in the game
  367.             TCHAR strNumberPlayers[32];
  368.  
  369.             wsprintf( strNumberPlayers, TEXT("%d"), g_lNumberOfActivePlayers );
  370.             SetDlgItemText( hDlg, IDC_NUM_PLAYERS, strNumberPlayers );
  371.             break;
  372.         }
  373.  
  374.         case WM_APP_DISPLAY_WAVE:
  375.         {
  376.             HRESULT          hr;
  377.             DPNID            dpnidPlayer = (DWORD)wParam;
  378.             APP_PLAYER_INFO* pPlayerInfo = NULL;
  379.             
  380.             PLAYER_LOCK(); // enter player context CS
  381.  
  382.             // Get the player context accosicated with this DPNID
  383.             hr = g_pDP->GetPlayerContext( dpnidPlayer, 
  384.                                           (LPVOID* const) &pPlayerInfo,
  385.                                           0);
  386.  
  387.             PLAYER_ADDREF( pPlayerInfo ); // addref player, since we are using it now
  388.             PLAYER_UNLOCK(); // leave player context CS
  389.  
  390.             if( FAILED(hr) || pPlayerInfo == NULL )
  391.             {
  392.                 // The player who sent this may have gone away before this 
  393.                 // message was handled, so just ignore it
  394.                 break;
  395.             }
  396.             
  397.             // Make wave message and display it.
  398.             TCHAR szWaveMessage[MAX_PATH];
  399.             wsprintf( szWaveMessage, TEXT("%s just waved at you, %s!\r\n"), 
  400.                       pPlayerInfo->strPlayerName, g_strLocalPlayerName );
  401.  
  402.             PLAYER_LOCK();
  403.             PLAYER_RELEASE( pPlayerInfo );  // Release player and cleanup if needed
  404.             PLAYER_UNLOCK();
  405.  
  406.             AppendTextToEditControl( hDlg, szWaveMessage );
  407.             break;
  408.         }
  409.  
  410.         case WM_COMMAND:
  411.         {
  412.             switch( LOWORD(wParam) )
  413.             {
  414.                 case IDC_WAVE:
  415.                     if( FAILED( g_hrDialog = WaveToAllPlayers() ) )
  416.                     {
  417.                         DXTRACE_ERR( TEXT("WaveToAllPlayers"), g_hrDialog );
  418.                         EndDialog( hDlg, 0 );
  419.                     }
  420.  
  421.                     return TRUE;
  422.  
  423.                 case IDCANCEL:
  424.                     g_hrDialog = S_OK;
  425.                     EndDialog( hDlg, 0 );
  426.                     return TRUE;
  427.             }
  428.             break;
  429.         }
  430.     }
  431.  
  432.     return FALSE; // Didn't handle message
  433. }
  434.  
  435.  
  436.  
  437.  
  438. //-----------------------------------------------------------------------------
  439. // Name: DirectPlayMessageHandler
  440. // Desc: Handler for DirectPlay messages.  This function is called by
  441. //       the DirectPlay message handler pool of threads, so be careful of thread
  442. //       synchronization problems with shared memory
  443. //-----------------------------------------------------------------------------
  444. HRESULT WINAPI DirectPlayMessageHandler( PVOID pvUserContext, 
  445.                                          DWORD dwMessageId, 
  446.                                          PVOID pMsgBuffer )
  447. {
  448.     // Try not to stay in this message handler for too long, otherwise
  449.     // there will be a backlog of data.  The best solution is to 
  450.     // queue data as it comes in, and then handle it on other threads.
  451.     
  452.     // This function is called by the DirectPlay message handler pool of 
  453.     // threads, so be careful of thread synchronization problems with shared memory
  454.  
  455.     switch( dwMessageId )
  456.     {
  457.         case DPN_MSGID_CREATE_PLAYER:
  458.         {
  459.             HRESULT hr;
  460.             PDPNMSG_CREATE_PLAYER pCreatePlayerMsg;
  461.             pCreatePlayerMsg = (PDPNMSG_CREATE_PLAYER)pMsgBuffer;
  462.  
  463.             // Get the peer info and extract its name
  464.             DWORD dwSize = 0;
  465.             DPN_PLAYER_INFO* pdpPlayerInfo = NULL;
  466.             hr = g_pDP->GetPeerInfo( pCreatePlayerMsg->dpnidPlayer, 
  467.                                      pdpPlayerInfo, &dwSize, 0 );
  468.             if( FAILED(hr) && hr != DPNERR_BUFFERTOOSMALL )
  469.                 return DXTRACE_ERR( TEXT("GetPeerInfo"), hr );
  470.             pdpPlayerInfo = (DPN_PLAYER_INFO*) new BYTE[ dwSize ];
  471.             ZeroMemory( pdpPlayerInfo, dwSize );
  472.             pdpPlayerInfo->dwSize = sizeof(DPN_PLAYER_INFO);
  473.             hr = g_pDP->GetPeerInfo( pCreatePlayerMsg->dpnidPlayer, 
  474.                                      pdpPlayerInfo, &dwSize, 0 );
  475.             if( FAILED(hr) )
  476.                 return DXTRACE_ERR( TEXT("GetPeerInfo"), hr );
  477.  
  478.             // Create a new and fill in a APP_PLAYER_INFO
  479.             APP_PLAYER_INFO* pPlayerInfo = new APP_PLAYER_INFO;
  480.             ZeroMemory( pPlayerInfo, sizeof(APP_PLAYER_INFO) );
  481.             pPlayerInfo->lRefCount   = 1;
  482.             pPlayerInfo->dpnidPlayer = pCreatePlayerMsg->dpnidPlayer;
  483.  
  484.             // This stores a extra TCHAR copy of the player name for 
  485.             // easier access.  This will be redundent copy since DPlay 
  486.             // also keeps a copy of the player name in GetPeerInfo()
  487.             DXUtil_ConvertWideStringToGeneric( pPlayerInfo->strPlayerName, 
  488.                                                pdpPlayerInfo->pwszName, MAX_PLAYER_NAME );
  489.  
  490.             if( pdpPlayerInfo->dwPlayerFlags & DPNPLAYER_LOCAL )
  491.                 g_dpnidLocalPlayer = pCreatePlayerMsg->dpnidPlayer;
  492.  
  493.             SAFE_DELETE_ARRAY( pdpPlayerInfo );
  494.  
  495.             // Tell DirectPlay to store this pPlayerInfo 
  496.             // pointer in the pvPlayerContext.
  497.             pCreatePlayerMsg->pvPlayerContext = pPlayerInfo;
  498.  
  499.             // Update the number of active players, and 
  500.             // post a message to the dialog thread to update the 
  501.             // UI.  This keeps the DirectPlay message handler 
  502.             // from blocking
  503.             InterlockedIncrement( &g_lNumberOfActivePlayers );
  504.             if( g_hDlg != NULL )
  505.                 PostMessage( g_hDlg, WM_APP_UPDATE_STATS, 0, 0 );
  506.  
  507.             break;
  508.         }
  509.  
  510.         case DPN_MSGID_DESTROY_PLAYER:
  511.         {
  512.             PDPNMSG_DESTROY_PLAYER pDestroyPlayerMsg;
  513.             pDestroyPlayerMsg = (PDPNMSG_DESTROY_PLAYER)pMsgBuffer;
  514.             APP_PLAYER_INFO* pPlayerInfo = (APP_PLAYER_INFO*) pDestroyPlayerMsg->pvPlayerContext;
  515.  
  516.             PLAYER_LOCK();                  // enter player context CS
  517.             PLAYER_RELEASE( pPlayerInfo );  // Release player and cleanup if needed
  518.             PLAYER_UNLOCK();                // leave player context CS
  519.  
  520.             // Update the number of active players, and 
  521.             // post a message to the dialog thread to update the 
  522.             // UI.  This keeps the DirectPlay message handler 
  523.             // from blocking
  524.             InterlockedDecrement( &g_lNumberOfActivePlayers );
  525.             if( g_hDlg != NULL )
  526.                 PostMessage( g_hDlg, WM_APP_UPDATE_STATS, 0, 0 );
  527.  
  528.             break;
  529.         }
  530.  
  531.         case DPN_MSGID_HOST_MIGRATE:
  532.         {
  533.             PDPNMSG_HOST_MIGRATE pHostMigrateMsg;
  534.             pHostMigrateMsg = (PDPNMSG_HOST_MIGRATE)pMsgBuffer;
  535.  
  536.             // Check to see if we are the new host
  537.             if( pHostMigrateMsg->dpnidNewHost == g_dpnidLocalPlayer )
  538.                 SetWindowText( g_hDlg, TEXT("SimplePeer (Host)") );
  539.             break;
  540.         }
  541.  
  542.         case DPN_MSGID_TERMINATE_SESSION:
  543.         {
  544.             PDPNMSG_TERMINATE_SESSION pTerminateSessionMsg;
  545.             pTerminateSessionMsg = (PDPNMSG_TERMINATE_SESSION)pMsgBuffer;
  546.  
  547.             g_hrDialog = DPNERR_CONNECTIONLOST;
  548.             EndDialog( g_hDlg, 0 );
  549.             break;
  550.         }
  551.  
  552.         case DPN_MSGID_RECEIVE:
  553.         {
  554.             PDPNMSG_RECEIVE pReceiveMsg;
  555.             pReceiveMsg = (PDPNMSG_RECEIVE)pMsgBuffer;
  556.             APP_PLAYER_INFO* pPlayerInfo = (APP_PLAYER_INFO*) pReceiveMsg->pvPlayerContext;
  557.  
  558.             GAMEMSG_GENERIC* pMsg = (GAMEMSG_GENERIC*) pReceiveMsg->pReceiveData;
  559.             if( pMsg->dwType == GAME_MSGID_WAVE )
  560.             {
  561.                 // This message is sent when a player has waved to us, so 
  562.                 // post a message to the dialog thread to update the UI.  
  563.                 // This keeps the DirectPlay threads from blocking, and also
  564.                 // serializes the recieves since DirectPlayMessageHandler can
  565.                 // be called simultaneously from a pool of DirectPlay threads.
  566.                 PostMessage( g_hDlg, WM_APP_DISPLAY_WAVE, pPlayerInfo->dpnidPlayer, 0 );
  567.             }
  568.             break;
  569.         }
  570.     }
  571.  
  572.     // Make sure the DirectPlay MessageHandler calls the CNetConnectWizard handler, 
  573.     // so it can be informed of messages such as DPN_MSGID_ENUM_HOSTS_RESPONSE.
  574.     if( g_pNetConnectWizard )
  575.         return g_pNetConnectWizard->MessageHandler( pvUserContext, dwMessageId, 
  576.                                                     pMsgBuffer );
  577.     
  578.     return S_OK;
  579. }
  580.  
  581.  
  582.  
  583.  
  584. //-----------------------------------------------------------------------------
  585. // Name: DirectPlayLobbyMessageHandler
  586. // Desc: Handler for DirectPlay lobby messages.  This function is called by
  587. //       the DirectPlay lobby message handler pool of threads, so be careful of 
  588. //       thread synchronization problems with shared memory
  589. //-----------------------------------------------------------------------------
  590. HRESULT WINAPI DirectPlayLobbyMessageHandler( PVOID pvUserContext, 
  591.                                               DWORD dwMessageId, 
  592.                                               PVOID pMsgBuffer )
  593. {
  594.     switch( dwMessageId )
  595.     {
  596.         case DPL_MSGID_CONNECT:
  597.         {
  598.             PDPL_MESSAGE_CONNECT pConnectMsg;
  599.             pConnectMsg = (PDPL_MESSAGE_CONNECT)pMsgBuffer;
  600.  
  601.             // The CNetConnectWizard will handle this message for us,
  602.             // so there is nothing we need to do here for this simple
  603.             // sample.
  604.             break;
  605.         }
  606.  
  607.         case DPL_MSGID_DISCONNECT:
  608.         {
  609.             PDPL_MESSAGE_DISCONNECT pDisconnectMsg;
  610.             pDisconnectMsg = (PDPL_MESSAGE_DISCONNECT)pMsgBuffer;
  611.  
  612.             // We should free any data associated with the lobby 
  613.             // client here, but there is none.
  614.             break;
  615.         }
  616.  
  617.         case DPL_MSGID_RECEIVE:
  618.         {
  619.             PDPL_MESSAGE_RECEIVE pReceiveMsg;
  620.             pReceiveMsg = (PDPL_MESSAGE_RECEIVE)pMsgBuffer;
  621.  
  622.             // The lobby client sent us data.  This sample doesn't
  623.             // expected data from the client, but it is useful 
  624.             // for more complex apps.
  625.             break;
  626.         }
  627.  
  628.         case DPL_MSGID_CONNECTION_SETTINGS:
  629.         {
  630.             PDPL_MESSAGE_CONNECTION_SETTINGS pConnectionStatusMsg;
  631.             pConnectionStatusMsg = (PDPL_MESSAGE_CONNECTION_SETTINGS)pMsgBuffer;
  632.  
  633.             // The lobby client has changed the connection settings.  
  634.             // This simple sample doesn't handle this, but more complex apps may
  635.             // want to.
  636.             break;
  637.         }
  638.     }
  639.  
  640.     // Make sure the DirectPlay MessageHandler calls the CNetConnectWizard handler, 
  641.     // so the wizard can be informed of lobby messages such as DPL_MSGID_CONNECT
  642.     if( g_pNetConnectWizard )
  643.         return g_pNetConnectWizard->LobbyMessageHandler( pvUserContext, dwMessageId, 
  644.                                                          pMsgBuffer );
  645.     
  646.     return S_OK;
  647. }
  648.  
  649.  
  650.  
  651.  
  652. //-----------------------------------------------------------------------------
  653. // Name: WaveToAllPlayers()
  654. // Desc: Send a app-defined "wave" DirectPlay message to all connected players
  655. //-----------------------------------------------------------------------------
  656. HRESULT WaveToAllPlayers()
  657. {
  658.     // This is called by the dialog UI thread.  This will send a message to all
  659.     // the players or inform the player that there is no one to wave at.
  660.     if( g_lNumberOfActivePlayers == 1 )
  661.     {
  662.         MessageBox( NULL, TEXT("No one is around to wave at! :("), 
  663.                     TEXT("SimplePeer"), MB_OK );
  664.     }
  665.     else
  666.     {
  667.         // Send a message to all of the players
  668.         GAMEMSG_GENERIC msgWave;
  669.         msgWave.dwType = GAME_MSGID_WAVE;
  670.  
  671.         DPN_BUFFER_DESC bufferDesc;
  672.         bufferDesc.dwBufferSize = sizeof(GAMEMSG_GENERIC);
  673.         bufferDesc.pBufferData  = (BYTE*) &msgWave;
  674.  
  675.         DPNHANDLE hAsync;
  676.         g_pDP->SendTo( DPNID_ALL_PLAYERS_GROUP, &bufferDesc, 1,
  677.                        0, NULL, &hAsync, DPNSEND_NOLOOPBACK );
  678.     }
  679.  
  680.     return S_OK;
  681. }
  682.  
  683.  
  684.  
  685.  
  686. //-----------------------------------------------------------------------------
  687. // Name: AppendTextToEditControl()
  688. // Desc: Appends a string of text to the edit control
  689. //-----------------------------------------------------------------------------
  690. VOID AppendTextToEditControl( HWND hDlg, TCHAR* strNewLogLine )
  691. {
  692.     static TCHAR strText[1024*10];
  693.  
  694.     HWND hEdit = GetDlgItem( hDlg, IDC_LOG_EDIT );
  695.     SendMessage( hEdit, WM_SETREDRAW, FALSE, 0 );
  696.     GetWindowText( hEdit, strText, 1024*9 );
  697.  
  698.     _tcscat( strText, strNewLogLine );
  699.  
  700.     int nSecondLine = 0;
  701.     if( SendMessage( hEdit, EM_GETLINECOUNT, 0, 0 ) > 9 )
  702.         nSecondLine = (int)SendMessage( hEdit, EM_LINEINDEX, 1, 0 );
  703.  
  704.     SetWindowText( hEdit, &strText[nSecondLine] );
  705.  
  706.     SendMessage( hEdit, WM_SETREDRAW, TRUE, 0 );
  707.     InvalidateRect( hEdit, NULL, TRUE );
  708.     UpdateWindow( hEdit );
  709. }
  710.