//----------------------------------------------------------------------------- // File: DPConnect.cpp // // Desc: Support file to prompt the user for the DirectPlay connection // and the DirectPlay session they want to join or create. // // Call DPConnect_StartDirectPlayConnect() to start the series of // dialogs boxes. At the end the player will be connected to an active // DirectPlay session that the user either created or joined. Or the // function may also return an EXITCODE as defined below in the event // of error or user cancel. // // Copyright (c) 1999-2001 Microsoft Corp. All rights reserved. //----------------------------------------------------------------------------- #define IDIRECTPLAY2_OR_GREATER #include #include #include #include #include #include "resource.h" //----------------------------------------------------------------------------- // Defines, and constants //----------------------------------------------------------------------------- #define SAFE_DELETE(p) { if(p) { delete (p); (p)=NULL; } } #define SAFE_DELETE_ARRAY(p) { if(p) { delete[] (p); (p)=NULL; } } #define SAFE_RELEASE(p) { if(p) { (p)->Release(); (p)=NULL; } } #define MAX_PLAYER_NAME 14 #define MAX_SESSION_NAME 256 struct DPSessionInfo { GUID guidSession; TCHAR szSession[MAX_SESSION_NAME]; DPSessionInfo* pDPSINext; }; //----------------------------------------------------------------------------- // Global variables //----------------------------------------------------------------------------- #define EXITCODE_FORWARD 1 // Dialog success, so go forward #define EXITCODE_BACKUP 2 // Dialog canceled, show previous dialog #define EXITCODE_QUIT 3 // Dialog quit, close app #define EXITCODE_ERROR 4 // Dialog error, terminate app #define EXITCODE_LOBBYCONNECT 5 // Dialog connected from lobby, connect success extern LPDIRECTPLAY4A g_pDP; extern LPDPLCONNECTION g_pDPLConnection; extern LPDIRECTPLAYLOBBY3 g_pDPLobby; extern GUID g_AppGUID; extern HANDLE g_hDPMessageEvent; extern TCHAR g_strPreferredProvider[MAX_SESSION_NAME]; extern TCHAR g_strSessionName[MAX_SESSION_NAME]; extern TCHAR g_strLocalPlayerName[MAX_PLAYER_NAME]; extern DPID g_LocalPlayerDPID; extern BOOL g_bHostPlayer; extern TCHAR g_strAppName[256]; extern BOOL g_bUseProtocol; extern BOOL g_bAsyncSupported; // Asynchronous sends supported DPSessionInfo g_DPSIHead; BOOL g_bSearchingForSessions; BOOL g_bWaitForConnectionSettings; //----------------------------------------------------------------------------- // Function-prototypes //----------------------------------------------------------------------------- int DPConnect_StartDirectPlayConnect( HINSTANCE hInst, BOOL bBackTrack ); INT_PTR CALLBACK DPConnect_ConnectionsDlgProc( HWND, UINT, WPARAM, LPARAM ); BOOL FAR PASCAL DPConnect_EnumConnectionsCallback( LPCGUID, VOID*, DWORD, LPCDPNAME, DWORD, VOID* ); HRESULT DPConnect_ConnectionsDlgFillListBox( HWND hDlg ); HRESULT DPConnect_ConnectionsDlgOnOK( HWND hDlg ); VOID DPConnect_ConnectionsDlgCleanup( HWND hDlg ); INT_PTR CALLBACK DPConnect_SessionsDlgProc( HWND, UINT, WPARAM, LPARAM ); INT_PTR CALLBACK DPConnect_CreateSessionDlgProc( HWND, UINT, WPARAM, LPARAM ); BOOL FAR PASCAL DPConnect_EnumSessionsCallback( LPCDPSESSIONDESC2, DWORD*, DWORD, VOID* ); VOID DPConnect_SessionsDlgInitListbox( HWND hDlg ); HRESULT DPConnect_SessionsDlgShowGames( HWND hDlg ); HRESULT DPConnect_SessionsDlgJoinGame( HWND hDlg ); HRESULT DPConnect_SessionsDlgCreateGame( HWND hDlg ); VOID DPConnect_SessionsDlgCleanup(); INT_PTR CALLBACK DPConnect_LobbyWaitDlgProc( HWND, UINT, WPARAM, LPARAM ); HRESULT DPConnect_CheckForLobbyLaunch( BOOL* pbLaunchedByLobby ); HRESULT DPConnect_DoLobbyLaunch(); HRESULT DPConnect_WaitForLobbyLaunch( HWND hParentDlg ); HRESULT DPConnect_GetLobbyConnectionSettingsMessage(); //----------------------------------------------------------------------------- // Name: DPConnect_StartDirectPlayConnect() // Desc: Prompts the user for the DirectPlay connection and the DirectPlay // session they want to join or create. This function returns one // of the EXITCODEs as #defined above //----------------------------------------------------------------------------- int DPConnect_StartDirectPlayConnect( HINSTANCE hInst, BOOL bBackTrack = FALSE ) { int nExitCode; int nStep; // Setup the g_DPSIHead circular linked list ZeroMemory( &g_DPSIHead, sizeof( DPSessionInfo ) ); g_DPSIHead.pDPSINext = &g_DPSIHead; // If the back track flag is true, then the user has already been through // the connect process once, and has back tracked out of the main 'game' // so we need start at the last dialog box if( bBackTrack ) nStep = 1; else nStep = 0; // Show the dialog boxes to connect while( TRUE ) { switch( nStep ) { case 0: // Display the multiplayer connect dialog box. nExitCode = (int)DialogBox( hInst, MAKEINTRESOURCE(IDD_MULTIPLAYER_CONNECT), NULL, (DLGPROC)DPConnect_ConnectionsDlgProc ); break; case 1: // Display the multiplayer games dialog box. nExitCode = (int)DialogBox( hInst, MAKEINTRESOURCE(IDD_MULTIPLAYER_GAMES), NULL, (DLGPROC)DPConnect_SessionsDlgProc ); break; } if( nExitCode == EXITCODE_ERROR || nExitCode == EXITCODE_QUIT ) return nExitCode; if( nExitCode == EXITCODE_BACKUP ) nStep--; else nStep++; if( nExitCode == EXITCODE_LOBBYCONNECT ) return EXITCODE_LOBBYCONNECT; if( nStep == 2 ) break; } return EXITCODE_FORWARD; } //----------------------------------------------------------------------------- // Name: DPConnect_ConnectionsDlgProc() // Desc: Handles messages for the multiplayer connect dialog //----------------------------------------------------------------------------- INT_PTR CALLBACK DPConnect_ConnectionsDlgProc( HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam ) { HRESULT hr; switch( msg ) { case WM_INITDIALOG: { SetDlgItemText( hDlg, IDC_PLAYER_NAME_EDIT, g_strLocalPlayerName ); // Load and set the icon #ifdef _WIN64 HINSTANCE hInst = (HINSTANCE) GetWindowLongPtr( hDlg, GWLP_HINSTANCE ); #else HINSTANCE hInst = (HINSTANCE) GetWindowLong( hDlg, GWL_HINSTANCE ); #endif HICON hIcon = LoadIcon( hInst, MAKEINTRESOURCE( IDI_MAIN ) ); SendMessage( hDlg, WM_SETICON, ICON_BIG, (LPARAM) hIcon ); // Set big icon SendMessage( hDlg, WM_SETICON, ICON_SMALL, (LPARAM) hIcon ); // Set small icon // Set the window title TCHAR strWindowTitle[256]; sprintf( strWindowTitle, "%s - Multiplayer Connect", g_strAppName ); SetWindowText( hDlg, strWindowTitle ); if( FAILED( hr = DPConnect_ConnectionsDlgFillListBox( hDlg ) ) ) EndDialog( hDlg, EXITCODE_ERROR ); } break; case WM_COMMAND: switch( LOWORD(wParam) ) { case IDC_CONNECTION_LIST: if( HIWORD(wParam) != LBN_DBLCLK ) break; // Fall through case IDOK: if( FAILED( hr = DPConnect_ConnectionsDlgOnOK( hDlg ) ) ) EndDialog( hDlg, EXITCODE_ERROR ); break; case IDCANCEL: EndDialog( hDlg, EXITCODE_QUIT ); break; default: return FALSE; // Message not handled } break; case WM_DESTROY: DPConnect_ConnectionsDlgCleanup( hDlg ); break; default: return FALSE; // Message not handled } // Message was handled return TRUE; } //----------------------------------------------------------------------------- // Name: DPConnect_ConnectionsDlgFillListBox() // Desc: Fills the DirectPlay connection listbox, and also adds // a "Wait for Lobby" connection option. //----------------------------------------------------------------------------- HRESULT DPConnect_ConnectionsDlgFillListBox( HWND hDlg ) { HRESULT hr; HWND hWndListBox = GetDlgItem( hDlg, IDC_CONNECTION_LIST ); LPDIRECTPLAY4 pDP = NULL; int nIndex; // Create a IDirectPlay object if( FAILED( hr = CoCreateInstance( CLSID_DirectPlay, NULL, CLSCTX_ALL, IID_IDirectPlay4A, (VOID**)&pDP ) ) ) { if( hr == E_NOINTERFACE ) { MessageBox( NULL, TEXT("This application requires DirectPlay 6 or later. " "The sample will now quit."), TEXT("DirectPlay Sample"), MB_OK | MB_ICONERROR ); } return hr; } // Enumerate all DirectPlay connections, and store them in the listbox if( FAILED( hr = pDP->EnumConnections( &g_AppGUID, DPConnect_EnumConnectionsCallback, hWndListBox, 0 ) ) ) { SAFE_RELEASE( pDP ); return hr; } SAFE_RELEASE( pDP ); // Add "Wait for Lobby Connection" selection in list box SendMessage( hWndListBox, LB_ADDSTRING, 0, (LPARAM)"Wait for Lobby Connection" ); SetFocus( hWndListBox ); nIndex = (int)SendMessage( hWndListBox, LB_FINDSTRINGEXACT, -1, (LPARAM)g_strPreferredProvider ); if( nIndex != LB_ERR ) SendMessage( hWndListBox, LB_SETCURSEL, nIndex, 0 ); else SendMessage( hWndListBox, LB_SETCURSEL, 0, 0 ); return S_OK; } //----------------------------------------------------------------------------- // Name: DPConnect_EnumConnectionsCallback() // Desc: Enumerates through all DirectPlay connections types, // and stores them in the listbox //----------------------------------------------------------------------------- BOOL FAR PASCAL DPConnect_EnumConnectionsCallback( LPCGUID pguidSP, VOID* pConnection, DWORD dwConnectionSize, LPCDPNAME pName, DWORD dwFlags, VOID* pvContext ) { HRESULT hr; LPDIRECTPLAY4 pDP = NULL; VOID* pConnectionBuffer = NULL; HWND hWndListBox = (HWND)pvContext; LRESULT iIndex; // Create a IDirectPlay object if( FAILED( hr = CoCreateInstance( CLSID_DirectPlay, NULL, CLSCTX_ALL, IID_IDirectPlay4A, (VOID**)&pDP ) ) ) return FALSE; // Error, stop enumerating // Test the if the connection is available by attempting to initialize // the connection if( FAILED( hr = pDP->InitializeConnection( pConnection, 0 ) ) ) { SAFE_RELEASE( pDP ); return TRUE; // Unavailable connection, keep enumerating } // Don't need the IDirectPlay interface anymore, so release it SAFE_RELEASE( pDP ); // Found a good connection, so put it in the listbox iIndex = SendMessage( hWndListBox, LB_ADDSTRING, 0, (LPARAM)pName->lpszShortNameA ); if( iIndex == CB_ERR ) return FALSE; // Error, stop enumerating pConnectionBuffer = new BYTE[ dwConnectionSize ]; if( pConnectionBuffer == NULL ) return FALSE; // Error, stop enumerating // Store pointer to GUID in listbox memcpy( pConnectionBuffer, pConnection, dwConnectionSize ); SendMessage( hWndListBox, LB_SETITEMDATA, iIndex, (LPARAM)pConnectionBuffer ); return TRUE; // Keep enumerating } //----------------------------------------------------------------------------- // Name: DPConnect_ConnectionsDlgOnOK() // Desc: Stores the player name g_strPlayerName, and in creates a IDirectPlay // object based on the connection type the user selected. //----------------------------------------------------------------------------- HRESULT DPConnect_ConnectionsDlgOnOK( HWND hDlg ) { VOID* pConnection = NULL; LRESULT iIndex; HRESULT hr; GetDlgItemText( hDlg, IDC_PLAYER_NAME_EDIT, g_strLocalPlayerName, MAX_PLAYER_NAME ); if( strlen( g_strLocalPlayerName ) == 0 ) { MessageBox( hDlg, TEXT("You must enter a valid player name."), TEXT("DirectPlay Sample"), MB_OK ); return S_OK; } HWND hWndListBox = GetDlgItem( hDlg, IDC_CONNECTION_LIST ); iIndex = SendMessage( hWndListBox, LB_GETCURSEL, 0, 0 ); SendMessage( hWndListBox, LB_GETTEXT, iIndex, (LPARAM)g_strPreferredProvider ); pConnection = (VOID*) SendMessage( hWndListBox, LB_GETITEMDATA, iIndex, 0 ); if( NULL == pConnection ) { if( FAILED( hr = DPConnect_WaitForLobbyLaunch( hDlg ) ) ) { if( hr == DPERR_USERCANCEL ) return S_OK; return hr; } EndDialog( hDlg, EXITCODE_LOBBYCONNECT ); return S_OK; } // Release previously selected DirectPlay object, if any. SAFE_RELEASE( g_pDP ); // Create the DirectPlay object if( FAILED( hr = CoCreateInstance( CLSID_DirectPlay, NULL, CLSCTX_ALL, IID_IDirectPlay4A, (VOID**)&g_pDP ) ) ) return hr; // Initialize the connection based on the selected connection type if( FAILED( hr = g_pDP->InitializeConnection( pConnection, 0 ) ) ) return hr; EndDialog( hDlg, EXITCODE_FORWARD ); return S_OK; } //----------------------------------------------------------------------------- // Name: DPConnect_ConnectionsDlgCleanup() // Desc: Deletes the connection buffers from the listbox //----------------------------------------------------------------------------- VOID DPConnect_ConnectionsDlgCleanup( HWND hDlg ) { VOID* pConnectionBuffer = NULL; DWORD iIndex; DWORD dwCount; HWND hWndListBox = GetDlgItem( hDlg, IDC_CONNECTION_LIST ); dwCount = (DWORD)SendMessage( hWndListBox, LB_GETCOUNT, 0, 0 ); for( iIndex = 0; iIndex < dwCount; iIndex++ ) { pConnectionBuffer = (VOID*) SendMessage( hWndListBox, LB_GETITEMDATA, iIndex, 0 ); SAFE_DELETE_ARRAY( pConnectionBuffer ); } } //----------------------------------------------------------------------------- // Name: DPConnect_SessionsDlgProc() // Desc: Handles messages fro the multiplayer games dialog //----------------------------------------------------------------------------- INT_PTR CALLBACK DPConnect_SessionsDlgProc( HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam ) { HRESULT hr; switch( msg ) { case WM_INITDIALOG: { // Load and set the icon #ifdef _WIN64 HINSTANCE hInst = (HINSTANCE) GetWindowLongPtr( hDlg, GWLP_HINSTANCE ); #else HINSTANCE hInst = (HINSTANCE) GetWindowLong( hDlg, GWL_HINSTANCE ); #endif HICON hIcon = LoadIcon( hInst, MAKEINTRESOURCE( IDI_MAIN ) ); SendMessage( hDlg, WM_SETICON, ICON_BIG, (LPARAM) hIcon ); // Set big icon SendMessage( hDlg, WM_SETICON, ICON_SMALL, (LPARAM) hIcon ); // Set small icon // Set the window title TCHAR strWindowTitle[256]; sprintf( strWindowTitle, "%s - Multiplayer Games", g_strAppName ); SetWindowText( hDlg, strWindowTitle ); g_bSearchingForSessions = FALSE; SetDlgItemText( hDlg, IDC_SEARCH_CHECK, "Start Search" ); DPConnect_SessionsDlgInitListbox( hDlg ); } break; case WM_TIMER: if( FAILED( hr = DPConnect_SessionsDlgShowGames( hDlg ) ) ) { KillTimer( hDlg, 1 ); MessageBox( hDlg, TEXT("Error enumerating DirectPlay games."), TEXT("DirectPlay Sample"), MB_OK | MB_ICONERROR ); } break; case WM_COMMAND: switch( LOWORD(wParam) ) { case IDC_SEARCH_CHECK: g_bSearchingForSessions = !g_bSearchingForSessions; if( g_bSearchingForSessions ) { // Start the timer, and start the async enumeratation SetDlgItemText( hDlg, IDC_SEARCH_CHECK, "Searching..." ); SetTimer( hDlg, 1, 250, NULL ); if( FAILED( hr = DPConnect_SessionsDlgShowGames( hDlg ) ) ) { KillTimer( hDlg, 1 ); MessageBox( hDlg, TEXT("Error enumerating DirectPlay games."), TEXT("DirectPlay Sample"), MB_OK | MB_ICONERROR ); } } else { // Stop the timer, and stop the async enumeration KillTimer( hDlg, 1 ); DPSESSIONDESC2 dpsd; ZeroMemory( &dpsd, sizeof(dpsd) ); dpsd.dwSize = sizeof(dpsd); dpsd.guidApplication = g_AppGUID; hr = g_pDP->EnumSessions( &dpsd, 0, DPConnect_EnumSessionsCallback, NULL, DPENUMSESSIONS_STOPASYNC ); if( hr != DPERR_GENERIC && FAILED(hr) ) EndDialog( hDlg, EXITCODE_ERROR ); SetDlgItemText( hDlg, IDC_SEARCH_CHECK, "Start Search" ); DPConnect_SessionsDlgInitListbox( hDlg ); } break; case IDC_GAMES_LIST: if( HIWORD(wParam) != LBN_DBLCLK ) break; // Fall through case IDC_JOIN: if( FAILED( hr = DPConnect_SessionsDlgJoinGame( hDlg ) ) ) { MessageBox( hDlg, TEXT("Unable to join game."), TEXT("DirectPlay Sample"), MB_OK | MB_ICONERROR ); } break; case IDC_CREATE: if( FAILED( hr = DPConnect_SessionsDlgCreateGame( hDlg ) ) ) { MessageBox( hDlg, TEXT("Unable to create game."), TEXT("DirectPlay Sample"), MB_OK | MB_ICONERROR ); } break; case IDCANCEL: // The close button was press EndDialog( hDlg, EXITCODE_QUIT ); break; case IDC_BACK: // "Cancel" button was pressed EndDialog( hDlg, EXITCODE_BACKUP ); break; default: return FALSE; // Message not handled } break; case WM_DESTROY: KillTimer( hDlg, 1 ); DPConnect_SessionsDlgCleanup(); break; default: return FALSE; // Message not handled } // Message was handled return TRUE; } //----------------------------------------------------------------------------- // Name: DPConnect_SessionsDlgInitListbox() // Desc: Initializes the listbox //----------------------------------------------------------------------------- VOID DPConnect_SessionsDlgInitListbox( HWND hDlg ) { HWND hWndListBox = GetDlgItem( hDlg, IDC_GAMES_LIST ); LPDIRECTPLAY4 pDP = NULL; // Clear the contents from the list box, and // display "Looking for games" text in listbox SendMessage( hWndListBox, LB_RESETCONTENT, 0, 0 ); if( g_bSearchingForSessions ) { SendMessage( hWndListBox, LB_ADDSTRING, 0, (LPARAM)"Looking for games..." ); } else { SendMessage( hWndListBox, LB_ADDSTRING, 0, (LPARAM)"Click Start Search to see a list of games. " "Click Create to start a new game." ); } SendMessage( hWndListBox, LB_SETITEMDATA, 0, NULL ); SendMessage( hWndListBox, LB_SETCURSEL, 0, 0 ); // Disable the join button until sessions are found EnableWindow( GetDlgItem( hDlg, IDC_JOIN ), FALSE ); } //----------------------------------------------------------------------------- // Name: DPConnect_SessionsDlgShowGames() // Desc: Enumerates the DirectPlay sessions, and displays them in the listbox //----------------------------------------------------------------------------- HRESULT DPConnect_SessionsDlgShowGames( HWND hDlg ) { HRESULT hr; HWND hWndListBox = GetDlgItem( hDlg, IDC_GAMES_LIST ); DPSESSIONDESC2 dpsd; DPSessionInfo* pDPSISelected = NULL; int nItemSelected; GUID guidSelectedSession; BOOL bFindSelectedGUID; BOOL bFoundSelectedGUID; // Try to keep the same session selected unless it goes away or // there is no real session currently selected bFindSelectedGUID = FALSE; bFoundSelectedGUID = TRUE; nItemSelected = (int)SendMessage( hWndListBox, LB_GETCURSEL, 0, 0 ); if( nItemSelected != LB_ERR ) { pDPSISelected = (DPSessionInfo*) SendMessage( hWndListBox, LB_GETITEMDATA, nItemSelected, 0 ); if( pDPSISelected != NULL ) { guidSelectedSession = pDPSISelected->guidSession; bFindSelectedGUID = TRUE; } } // Remove the previous games from the linked list DPConnect_SessionsDlgCleanup(); // Enum sessions and let DirectPlay decide the timeout ZeroMemory( &dpsd, sizeof(dpsd) ); dpsd.dwSize = sizeof(dpsd); dpsd.guidApplication = g_AppGUID; // Enumerate all the active DirectPlay games on the selected connection hr = g_pDP->EnumSessions( &dpsd, 0, DPConnect_EnumSessionsCallback, NULL, DPENUMSESSIONS_ALL | DPENUMSESSIONS_ASYNC ); if( FAILED(hr) ) { if( hr == DPERR_USERCANCEL ) { // The user canceled the DirectPlay connection dialog, // so stop the search if( g_bSearchingForSessions ) { CheckDlgButton( hDlg, IDC_SEARCH_CHECK, BST_UNCHECKED ); SendMessage( hDlg, WM_COMMAND, IDC_SEARCH_CHECK, 0 ); } return S_OK; } else { DPConnect_SessionsDlgInitListbox( hDlg ); if ( hr == DPERR_CONNECTING ) return S_OK; return hr; } } // Tell listbox not to redraw itself since the contents are going to change SendMessage( hWndListBox, WM_SETREDRAW, FALSE, 0 ); // Add the enumerated sessions to the listbox if( g_DPSIHead.pDPSINext != &g_DPSIHead ) { // Clear the contents from the list box and enable the join button SendMessage( hWndListBox, LB_RESETCONTENT, 0, 0 ); EnableWindow( GetDlgItem( hDlg, IDC_JOIN ), TRUE ); DPSessionInfo* pDPSI = g_DPSIHead.pDPSINext; while ( pDPSI != &g_DPSIHead ) { // Add session to the list box int nIndex = (int)SendMessage( hWndListBox, LB_ADDSTRING, 0, (LPARAM)pDPSI->szSession ); SendMessage( hWndListBox, LB_SETITEMDATA, nIndex, (LPARAM)pDPSI ); if( bFindSelectedGUID ) { // Look for the session the was selected before if( pDPSI->guidSession == guidSelectedSession ) { SendMessage( hWndListBox, LB_SETCURSEL, nIndex, 0 ); bFoundSelectedGUID = TRUE; } } pDPSI = pDPSI->pDPSINext; } if( !bFindSelectedGUID || !bFoundSelectedGUID ) SendMessage( hWndListBox, LB_SETCURSEL, 0, 0 ); } else { // There are no active session, so just reset the listbox DPConnect_SessionsDlgInitListbox( hDlg ); } // Tell listbox to redraw itself now since the contents have changed SendMessage( hWndListBox, WM_SETREDRAW, TRUE, 0 ); InvalidateRect( hWndListBox, NULL, FALSE ); return S_OK; } //----------------------------------------------------------------------------- // Name: DPConnect_EnumSessionsCallback() // Desc: Enumerates the sessions, and stores them in the linked list, g_DPSIHead //----------------------------------------------------------------------------- BOOL FAR PASCAL DPConnect_EnumSessionsCallback( LPCDPSESSIONDESC2 pdpsd, DWORD* pdwTimeout, DWORD dwFlags, VOID* pvContext ) { DPSessionInfo* pDPSINew = NULL; if( dwFlags & DPESC_TIMEDOUT ) return FALSE; // The enumeration has timed out, so stop the enumeration. // Found a good session, save it pDPSINew = new DPSessionInfo; if( NULL == pDPSINew ) return FALSE; ZeroMemory( pDPSINew, sizeof(DPSessionInfo) ); // Copy the information into pDPSINew pDPSINew->guidSession = pdpsd->guidInstance; sprintf( pDPSINew->szSession, "%s (%d/%d)", pdpsd->lpszSessionNameA, pdpsd->dwCurrentPlayers, pdpsd->dwMaxPlayers ); // Add pDPSINew to the circular linked list, g_pDPSIFirst pDPSINew->pDPSINext = g_DPSIHead.pDPSINext; g_DPSIHead.pDPSINext = pDPSINew; return TRUE; } //----------------------------------------------------------------------------- // Name: DPConnect_SessionsDlgJoinGame() // Desc: Joins the selected DirectPlay session //----------------------------------------------------------------------------- HRESULT DPConnect_SessionsDlgJoinGame( HWND hDlg ) { DPSESSIONDESC2 dpsd; DPNAME dpname; HRESULT hr; HWND hWndListBox = GetDlgItem( hDlg, IDC_GAMES_LIST ); DPSessionInfo* pDPSISelected = NULL; int nItemSelected; nItemSelected = (int)SendMessage( hWndListBox, LB_GETCURSEL, 0, 0 ); // Add status text in list box pDPSISelected = (DPSessionInfo*) SendMessage( hWndListBox, LB_GETITEMDATA, nItemSelected, 0 ); if( NULL == pDPSISelected ) { MessageBox( hDlg, TEXT("There are no games to join."), TEXT("DirectPlay Sample"), MB_OK ); return S_OK; } // Setup the DPSESSIONDESC2, and get the session guid from // the selected listbox item ZeroMemory( &dpsd, sizeof(dpsd) ); dpsd.dwSize = sizeof(dpsd); dpsd.guidInstance = pDPSISelected->guidSession; dpsd.guidApplication = g_AppGUID; // Join the session g_bHostPlayer = FALSE; if( FAILED( hr = g_pDP->Open( &dpsd, DPOPEN_JOIN ) ) ) return hr; // Check for Async message support // The caps we are checking do not differ for guaranteed msg DPCAPS dpcaps; ZeroMemory( &dpcaps, sizeof(DPCAPS) ); dpcaps.dwSize = sizeof(DPCAPS); g_pDP->GetCaps( &dpcaps, 0 ); // Determine if Aync messages are supported. g_bAsyncSupported = (dpcaps.dwFlags & DPCAPS_ASYNCSUPPORTED) != 0; // Create player based on g_strLocalPlayerName. // Store the player's DPID in g_LocalPlayerDPID. // Also all DirectPlay messages for this player will signal g_hDPMessageEvent ZeroMemory( &dpname, sizeof(DPNAME) ); dpname.dwSize = sizeof(DPNAME); dpname.lpszShortNameA = g_strLocalPlayerName; if( FAILED( hr = g_pDP->CreatePlayer( &g_LocalPlayerDPID, &dpname, g_hDPMessageEvent, NULL, 0, 0 ) ) ) return hr; // DirectPlay connect successful, so end dialog EndDialog( hDlg, EXITCODE_FORWARD ); return S_OK; } //----------------------------------------------------------------------------- // Name: DPConnect_SessionsDlgCreateGame() // Desc: Asks the user the session name, and creates a new DirectPlay session //----------------------------------------------------------------------------- HRESULT DPConnect_SessionsDlgCreateGame( HWND hDlg ) { DPSESSIONDESC2 dpsd; DPNAME dpname; HRESULT hr; int nResult; HINSTANCE hInst; #ifdef _WIN64 hInst = (HINSTANCE) GetWindowLongPtr( hDlg, GWLP_HINSTANCE ); #else hInst = (HINSTANCE) GetWindowLong( hDlg, GWL_HINSTANCE ); #endif // Display a modal multiplayer connect dialog box. EnableWindow( hDlg, FALSE ); nResult = (int)DialogBox( hInst, MAKEINTRESOURCE(IDD_MULTIPLAYER_CREATE), hDlg, (DLGPROC)DPConnect_CreateSessionDlgProc ); EnableWindow( hDlg, TRUE ); if( nResult == IDCANCEL ) return S_OK; // Setup the DPSESSIONDESC2 based on g_AppGUID, and g_strSessionName. // The DPSESSION_KEEPALIVE flag keeps the session alive if players abnormally exit ZeroMemory( &dpsd, sizeof(dpsd) ); dpsd.dwSize = sizeof(dpsd); dpsd.guidApplication = g_AppGUID; dpsd.lpszSessionNameA = g_strSessionName; dpsd.dwMaxPlayers = 10; dpsd.dwFlags = DPSESSION_KEEPALIVE | DPSESSION_MIGRATEHOST; if( g_bUseProtocol ) dpsd.dwFlags |= DPSESSION_DIRECTPLAYPROTOCOL; // Create a new session g_bHostPlayer = TRUE; if( FAILED( hr = g_pDP->Open( &dpsd, DPOPEN_CREATE ) ) ) return hr; // Check for Async message support // The caps we are checking do not differ for guaranteed msg DPCAPS dpcaps; ZeroMemory( &dpcaps, sizeof(DPCAPS) ); dpcaps.dwSize = sizeof(DPCAPS); g_pDP->GetCaps( &dpcaps, 0 ); // Determine if Aync messages are supported. g_bAsyncSupported = (dpcaps.dwFlags & DPCAPS_ASYNCSUPPORTED) != 0; // Create player based on g_strLocalPlayerName. // Store the player's DPID in g_LocalPlayerDPID. // Also all DirectPlay messages for this player will signal g_hDPMessageEvent ZeroMemory( &dpname, sizeof(DPNAME) ); dpname.dwSize = sizeof(DPNAME); dpname.lpszShortNameA = g_strLocalPlayerName; if( FAILED( hr = g_pDP->CreatePlayer( &g_LocalPlayerDPID, &dpname, g_hDPMessageEvent, NULL, 0, DPPLAYER_SERVERPLAYER ) ) ) return hr; // DirectPlay connect successful, so end dialog EndDialog( hDlg, EXITCODE_FORWARD ); return S_OK; } //----------------------------------------------------------------------------- // Name: DPConnect_CreateSessionDlgProc() // Desc: Handles messages fro the multiplayer create game dialog //----------------------------------------------------------------------------- INT_PTR CALLBACK DPConnect_CreateSessionDlgProc( HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam ) { DWORD dwNameLength; switch( msg ) { case WM_INITDIALOG: SetDlgItemText( hDlg, IDC_EDIT_SESSION_NAME, g_strSessionName ); return TRUE; case WM_COMMAND: switch( LOWORD(wParam) ) { case IDOK: dwNameLength = GetDlgItemText( hDlg, IDC_EDIT_SESSION_NAME, g_strSessionName, MAX_SESSION_NAME ); if( dwNameLength == 0 ) return TRUE; // Don't accept blank session names g_bUseProtocol = TRUE; EndDialog( hDlg, IDOK ); return TRUE; case IDCANCEL: EndDialog( hDlg, IDCANCEL ); return TRUE; } break; } return FALSE; // Didn't handle message } //----------------------------------------------------------------------------- // Name: DPConnect_SessionsDlgCleanup() // Desc: Deletes the linked list, g_DPSIHead //----------------------------------------------------------------------------- VOID DPConnect_SessionsDlgCleanup() { DPSessionInfo* pDPSI = g_DPSIHead.pDPSINext; DPSessionInfo* pDPSIDelete; while ( pDPSI != &g_DPSIHead ) { pDPSIDelete = pDPSI; pDPSI = pDPSI->pDPSINext; SAFE_DELETE( pDPSIDelete ); } // Re-link the g_DPSIHead circular linked list g_DPSIHead.pDPSINext = &g_DPSIHead; } //----------------------------------------------------------------------------- // Name: DPConnect_WaitForLobbyLaunch() // Desc: Wait for the a lobby connection and connect when found //----------------------------------------------------------------------------- HRESULT DPConnect_WaitForLobbyLaunch( HWND hParentDlg ) { HRESULT hr; BOOL bLaunchedByLobby; // Create a new lobby object SAFE_RELEASE( g_pDPLobby ); if( FAILED( hr = CoCreateInstance( CLSID_DirectPlayLobby, NULL, CLSCTX_INPROC_SERVER, IID_IDirectPlayLobby3A, (VOID**)&g_pDPLobby ) ) ) return hr; // Put the application into wait mode if( FAILED( hr = g_pDPLobby->WaitForConnectionSettings( 0 ) ) ) return hr; // Use the IDD_LOBBY_WAIT_STATUS dialog to check for a connection message g_bWaitForConnectionSettings = TRUE; EnableWindow( hParentDlg, FALSE ); // Display a dialog box and wait for the connection settings DialogBoxParam( NULL, MAKEINTRESOURCE( IDD_LOBBY_WAIT_STATUS ), hParentDlg, (DLGPROC)DPConnect_LobbyWaitDlgProc, (LPARAM)&hr ); EnableWindow( hParentDlg, TRUE ); if( FAILED(hr) ) { g_pDPLobby->WaitForConnectionSettings( DPLWAIT_CANCEL ); SAFE_RELEASE( g_pDPLobby ); return hr; } // The lobby server has launched the game, so check for a lobby // launch to continue on to connect to the session and create the player if( FAILED( hr = DPConnect_CheckForLobbyLaunch( &bLaunchedByLobby ) ) ) return hr; // This may be DPERR_USERCANCEL if the user canceled the dialog return S_OK; } //----------------------------------------------------------------------------- // Name: DPConnect_CheckForLobbyLaunch() // Desc: Determines if we were launched by a lobby. If so, gets the connection // settings and creates our player using the information from the lobby //----------------------------------------------------------------------------- HRESULT DPConnect_CheckForLobbyLaunch( BOOL* pbLaunchedByLobby ) { HRESULT hr; DWORD dwSize; if( NULL == pbLaunchedByLobby ) return E_FAIL; // Create a new lobby object SAFE_RELEASE( g_pDPLobby ); if( FAILED( hr = CoCreateInstance( CLSID_DirectPlayLobby, NULL, CLSCTX_INPROC_SERVER, IID_IDirectPlayLobby3A, (VOID**)&g_pDPLobby ) ) ) { if( hr == E_NOINTERFACE ) { MessageBox( NULL, TEXT("This application requires DirectPlay 6 or later. " "The sample will now quit."), TEXT("DirectPlay Sample"), MB_OK | MB_ICONERROR ); } return hr; } // Get connection settings from the lobby (into g_pdplConnection) hr = g_pDPLobby->GetConnectionSettings( 0, NULL, &dwSize ); if( FAILED(hr) && (DPERR_BUFFERTOOSMALL != hr) ) { if( DPERR_NOTLOBBIED == hr ) { *pbLaunchedByLobby = FALSE; // Cleanup since we were not launched from the lobby SAFE_DELETE_ARRAY( g_pDPLConnection ); SAFE_RELEASE( g_pDPLobby ); return S_OK; } return hr; } // Allocate memory for the connection SAFE_DELETE_ARRAY( g_pDPLConnection ); g_pDPLConnection = (DPLCONNECTION*)new BYTE[ dwSize ]; // Get the connection settings if( FAILED( hr = g_pDPLobby->GetConnectionSettings( 0, g_pDPLConnection, &dwSize ) ) ) return hr; *pbLaunchedByLobby = TRUE; if( FAILED( hr = DPConnect_DoLobbyLaunch() ) ) { // Cleanup SAFE_DELETE_ARRAY( g_pDPLConnection ); SAFE_RELEASE( g_pDPLobby ); return hr; // This may be DPERR_USERCANCEL if the user canceled the dialog } return S_OK; } //----------------------------------------------------------------------------- // Name: DPConnect_DoLobbyLaunch() // Desc: The app was launched by the lobby server, so connect to the session // and create the player //----------------------------------------------------------------------------- HRESULT DPConnect_DoLobbyLaunch() { HRESULT hr; // Detrimine if this is the host player g_bHostPlayer = ( (g_pDPLConnection->dwFlags & DPLCONNECTION_CREATESESSION) != 0 ); // Set our session flags g_pDPLConnection->lpSessionDesc->dwFlags = DPSESSION_KEEPALIVE | DPSESSION_MIGRATEHOST; // Let lobby know our connection flags if( FAILED( hr = g_pDPLobby->SetConnectionSettings( 0, 0, g_pDPLConnection ) ) ) return hr; // If we're hosting, just call ConnectEx syncronously. Otherwise, display // the connecting dialog and do the connect asynchronously. if( g_bHostPlayer ) { // Connect to the lobby syncronously if( FAILED( hr = g_pDPLobby->ConnectEx( 0, IID_IDirectPlay4A, (VOID**)&g_pDP, NULL ) ) ) return hr; } else { // Use the IDD_LOBBY_WAIT_STATUS dialog to attempt to connect to // the new session g_bWaitForConnectionSettings = FALSE; // Connect to the lobby asyncronously DialogBoxParam( NULL, MAKEINTRESOURCE( IDD_LOBBY_WAIT_STATUS ), NULL, (DLGPROC)DPConnect_LobbyWaitDlgProc, (LPARAM)&hr ); if( FAILED(hr) ) return hr; // This may be DPERR_USERCANCEL if the user canceled the dialog } strcpy( g_strLocalPlayerName, g_pDPLConnection->lpPlayerName->lpszShortNameA ); // Create our player DPNAME dpname; ZeroMemory( &dpname, sizeof(DPNAME) ); dpname.dwSize = sizeof(DPNAME); dpname.lpszShortNameA = g_strLocalPlayerName; if( FAILED( hr = g_pDP->CreatePlayer( &g_LocalPlayerDPID, &dpname, NULL, NULL, 0, 0 ) ) ) return hr; // Successfully lobby launch, note that we don't release the lobby object return S_OK; } //----------------------------------------------------------------------------- // Name: DPConnect_LobbyWaitDlgProc() // Desc: Handles dialog messages. This dialog starts a timer that attempts to // either receive a lobby connection settings message or // connect to new session via the lobby connection depending on the // g_bWaitForConnectionSettings flag. //----------------------------------------------------------------------------- INT_PTR CALLBACK DPConnect_LobbyWaitDlgProc( HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam ) { static HRESULT* phr = NULL; switch( msg ) { case WM_INITDIALOG: // Save the HRESULT pointer phr = (HRESULT*)lParam; if( g_bWaitForConnectionSettings ) SetDlgItemText( hDlg, IDC_WAIT_TEXT, TEXT("Waiting for connection...") ); else SetDlgItemText( hDlg, IDC_WAIT_TEXT, TEXT("Finding Game...") ); // Create a timer. The dialog will attempt to connect at every WM_TIMER SetTimer( hDlg, 0, 250, NULL ); break; case WM_DESTROY: KillTimer( hDlg, 0 ); break; case WM_TIMER: // Attempt to connect to the game if( g_bWaitForConnectionSettings ) { *phr = DPConnect_GetLobbyConnectionSettingsMessage(); if( *phr == S_OK ) EndDialog( hDlg, 0 ); } else { *phr = g_pDPLobby->ConnectEx( DPCONNECT_RETURNSTATUS, IID_IDirectPlay4A, (VOID**)&g_pDP, NULL ); if( *phr == DPERR_CONNECTING ) break; // Try again on the next WM_TIMER message EndDialog( hDlg, 0 ); } break; case WM_COMMAND: switch( LOWORD(wParam) ) { case IDC_LOBBYCONNECTCANCEL: case IDCANCEL: // the async Connect will be stopped when the // IDirectPlayLobby3 interface is released. // Set the HRESULT to user cancel and close the dialog *phr = DPERR_USERCANCEL; EndDialog( hDlg, 0 ); break; default: return FALSE; // Message not handled } break; default: return FALSE; // Message not handled } return TRUE; // Message handled } //----------------------------------------------------------------------------- // Name: DPConnect_GetLobbyConnectionSettingsMessage() // Desc: Attepts to get a lobby connection settings message. If it does // it returns S_OK otherwise it returns S_FALSE //----------------------------------------------------------------------------- HRESULT DPConnect_GetLobbyConnectionSettingsMessage() { HRESULT hr = NOERROR; LPVOID pvMsgBuffer = NULL; DWORD dwMsgBufferSize = 0; DWORD dwMsgFlags; while( TRUE ) { hr = g_pDPLobby->ReceiveLobbyMessage( 0, 0, &dwMsgFlags, pvMsgBuffer, &dwMsgBufferSize ); if( hr == DPERR_BUFFERTOOSMALL ) { // The current buffer was too small, // so reallocate it and try again SAFE_DELETE_ARRAY( pvMsgBuffer ); pvMsgBuffer = new BYTE[ dwMsgBufferSize ]; if( pvMsgBuffer == NULL ) return E_OUTOFMEMORY; continue; // Now that the buffer is bigger, try again } if( DPERR_NOMESSAGES == hr ) break; if( FAILED(hr) ) break; // Found a messages, check to see if its a CONNECTIONSETTINGS message if( dwMsgFlags & DPLMSG_SYSTEM ) { // If it is found then stop looking for it if( ( (DPLMSG_GENERIC*)pvMsgBuffer)->dwType == DPLSYS_NEWCONNECTIONSETTINGS ) break; } } // Cleanup buffer SAFE_DELETE_ARRAY( pvMsgBuffer ); // Return S_FALSE if there we no connection settings messages if( DPERR_NOMESSAGES == hr ) return S_FALSE; // If the message was recieved the hr should be S_OK return hr; }