2025-04-27 07:49:33 -04:00

631 lines
16 KiB
C++

/*++
Copyright (c) 2000 Microsoft Corporation
Module Name:
DirectPlayConnection.cpp
Abstract:
DirectPlayConnection is a wrapper around the direct play interfaces
to just do what is needed for this application. It takes care of
connecting to the remote machine and sending/recieving the connction
paramters.
Author:
Marc Reyhner 7/5/2000
SteveShi (updated) 8/23/2000
--*/
#include "stdafx.h"
#include "DirectPlayConnection.h"
#include "resource.h"
#include "msmsgs.h"
#define DP_ERR( hr ) if(SUCCEEDED(hr)) hr = E_FAIL; // Weird DP error.
#define DP_MY_DATATYPE 0x0111
typedef struct _DP_MY_DATA
{
DWORD dwType; // My message type
DWORD dwDataSize;
BYTE pData[1];
} DP_MY_DATA, *LPDP_MY_DATA;
/***********************************************
Func:
EnumPlayersCallback
Abstract:
Callback function for enumerating the player list.
For our scenario, we've only one remote player.
************************************************/
static BOOL FAR PASCAL
EnumPlayersCallback(DPID dpId, DWORD dwPlayerType, LPCDPNAME lpName, DWORD dwFlags, LPVOID lpContext)
{
*(DPID*)lpContext = dpId;
return FALSE;
}
/*************************************************
Func:
EnumAddressCallback
Abstract:
Return remote player's IP address.
**************************************************/
BOOL FAR PASCAL
EnumAddressCallback(REFGUID guidDataType,
DWORD dwDataSize,
LPCVOID lpData,
LPVOID lpContext)
{
if (guidDataType == DPAID_INet)
{
strcpy((char*)lpContext, (char*)lpData);
return FALSE;
}
return TRUE;
}
/**********************************************
Func:
Constructor
Abstract:
Initialize member variables
***********************************************/
CDirectPlayConnection::CDirectPlayConnection()
{
m_rLobby = NULL;
m_rSettings = NULL;
m_rDirectPlay = NULL;
m_bConnected = FALSE;
m_PlayerID = 0;
m_hEventHandle = NULL;
m_bstrLocalName = NULL;
m_idOtherPlayer = 0;
}
/**********************************************
Func:
Destructor
Abstract:
Clean up left memory
***********************************************/
CDirectPlayConnection::~CDirectPlayConnection()
{
DisconnectRemoteApplication();
if (m_bstrLocalName)
SysFreeString(m_bstrLocalName);
}
/*********************************************************
Func:
ConnectToRemoteApplication
Abstract:
Start DP connection
**********************************************************/
HRESULT CDirectPlayConnection::ConnectToRemoteApplication()
{
DWORD dwSize = 0;
HRESULT hr;
if (!m_rLobby)
{
hr = CoCreateInstance(CLSID_DirectPlayLobby,NULL,CLSCTX_ALL,
IID_IDirectPlayLobby,(LPVOID*)&m_rLobby);
if (hr != S_OK)
goto done;
}
if (!m_hEventHandle)
{
m_hEventHandle = CreateEvent(NULL,FALSE,FALSE,NULL);
if (!m_hEventHandle)
{
hr = HRESULT_FROM_WIN32(GetLastError());
goto done;
}
}
hr = m_rLobby->GetConnectionSettings(0,NULL,&dwSize);
if (hr != DPERR_BUFFERTOOSMALL) // Unknown error.
{
DP_ERR(hr);
goto done;
}
m_rSettings = (DPLCONNECTION *)new BYTE [dwSize];
if (!m_rSettings)
{
hr = E_OUTOFMEMORY;
goto done;
}
hr = m_rLobby->GetConnectionSettings(0,m_rSettings,&dwSize);
if (hr != DP_OK)
{
DP_ERR(hr);
goto done;
}
hr = m_rLobby->Connect(0,&m_rDirectPlay,NULL);
if (hr != DP_OK)
{
DP_ERR(hr);
goto done;
}
if (SUCCEEDED(GetLocalPlayerName()))
{
DPNAME DPName;
DPName.dwSize=sizeof(DPNAME);
DPName.dwFlags = 0;
DPName.lpszShortName = m_bstrLocalName;
hr = m_rDirectPlay->CreatePlayer(&m_PlayerID,&DPName,m_hEventHandle,NULL,0,0);
}
else
hr = m_rDirectPlay->CreatePlayer(&m_PlayerID,NULL,m_hEventHandle,NULL,0,0);
if (hr != DP_OK)
goto done;
m_bConnected = TRUE;
done:
return hr;
}
/**********************************************************
Func:
DisconnectRemoteApplication
Abstract:
Disconnect DP and clean up
***********************************************************/
HRESULT CDirectPlayConnection::DisconnectRemoteApplication()
{
HRESULT hr = S_OK;
if (m_rDirectPlay)
{
m_rDirectPlay->Close();
m_rDirectPlay->Release();
m_rDirectPlay = NULL;
}
if (m_rLobby)
{
m_rLobby->Release();
m_rLobby = NULL;
}
if (m_hEventHandle)
{
CloseHandle(m_hEventHandle);
m_hEventHandle = NULL;
}
if (m_rSettings)
{
delete m_rSettings;
m_rSettings = NULL;
}
m_idOtherPlayer = 0;
m_bConnected = FALSE;
return hr;
}
/*********************************************
Func:
SendConnectionParameters
Abstract:
Send data to remote player
Params:
parms: A BSTR data string to send
**********************************************/
HRESULT CDirectPlayConnection::SendConnectionParameters(BSTR parms)
{
HRESULT hr;
DWORD dwCount;
if (!m_bConnected)
{
hr = S_FALSE;
goto done;
}
if (m_idOtherPlayer == 0 && (FAILED(GetOtherPlayerID(&m_idOtherPlayer))))
{
hr = S_FALSE; // no other player
goto done;
}
// We don't support Secured session now, leave the code here for future usage.
/************************
if (m_rSettings->lpSessionDesc->dwFlags & DPSESSION_SECURESERVER)
{
hr = m_rDirectPlay->Send(m_PlayerID,
m_idOtherPlayer,
DPSEND_ENCRYPTED|DPSEND_GUARANTEED|DPSEND_SIGNED,
parms,
dwCount);
if (hr != DP_OK)
goto done;
}
else
*************************/
{
dwCount = (wcslen(parms)+1) * sizeof(OLECHAR);
LPDP_MY_DATA pConnectParms = (LPDP_MY_DATA)new BYTE[sizeof(DP_MY_DATA) + dwCount];
if (!pConnectParms)
{
hr = E_OUTOFMEMORY;
goto done;
}
pConnectParms->dwType = DP_MY_DATATYPE;
pConnectParms->dwDataSize = dwCount;
CopyMemory(pConnectParms->pData, (LPVOID)parms, dwCount);
hr = m_rDirectPlay->Send(m_PlayerID,
m_idOtherPlayer,
DPSEND_GUARANTEED,
pConnectParms,
sizeof(DP_MY_DATA) + dwCount);
delete pConnectParms;
}
done:
return hr;
}
/************************************************
Func:
ReceiveConnectionParameters
Abstract:
Receive data from remote player
Return:
BSTR data string from remote player
**************************************************/
HRESULT CDirectPlayConnection::ReceiveConnectionParameters( BSTR* pBstr)
{
DWORD dwSize = 0;
DWORD endWaitTime;
DWORD dwResult;
HRESULT hr = S_OK;
LPDP_MY_DATA pData = NULL;
if (!m_bConnected)
return S_FALSE; // not connected
if (m_idOtherPlayer == 0 && FAILED(GetOtherPlayerID(&m_idOtherPlayer)))
{
hr = S_FALSE; // No other player
goto done;
}
endWaitTime = GetTickCount() + GETPARMSTIMEOUT; // 1 minute
while (1)
{
hr = m_rDirectPlay->Receive(&m_idOtherPlayer,
&m_PlayerID,
DPRECEIVE_FROMPLAYER | DPRECEIVE_TOPLAYER,
NULL,
&dwSize);
if (hr == DPERR_BUFFERTOOSMALL && dwSize >= sizeof(DP_MY_DATA))
{
// Have received some meaningful message...
pData = (LPDP_MY_DATA) new BYTE[dwSize];
if (!pData)
{
hr = E_OUTOFMEMORY;
goto done;
}
hr = m_rDirectPlay->Receive(&m_idOtherPlayer,
&m_PlayerID,
DPRECEIVE_FROMPLAYER | DPRECEIVE_TOPLAYER,
(LPVOID)pData,
&dwSize);
if (hr != DP_OK)
goto done;
if (pData->dwType == DP_MY_DATATYPE)
{
*pBstr = SysAllocString((WCHAR*)pData->pData);
break;
}
}
// If we can't get the message in pre-defined duration, then we failed.
if (endWaitTime < GetTickCount())
{
hr = E_FAIL;
goto done;
}
// Wait for another message to come.
dwResult = WaitForSingleObject(m_hEventHandle, GETPARMSTIMEOUT); // Wait 60 seconds.
if (dwResult != WAIT_OBJECT_0)
{
hr = E_FAIL; // Timed out.
goto done;
}
}
done:
if (pData)
delete pData;
return hr;
}
BOOL CDirectPlayConnection::IsServer()
{
if (!m_bConnected)
{
return FALSE;
}
return (m_rSettings->dwFlags & DPLCONNECTION_CREATESESSION);
}
/***************************************
Func:
GetOtherPlayerID
Abstract:
Find the other player's ID
Params:
*pID: the return ID.
****************************************/
HRESULT CDirectPlayConnection::GetOtherPlayerID(DPID* pID)
{
HRESULT hr = S_OK;
DPID ID = 0;
hr = m_rDirectPlay->EnumPlayers(NULL, EnumPlayersCallback, &ID, /*&playerData,*/ DPENUMPLAYERS_REMOTE);
if (hr == DP_OK && ID)
{
*pID = ID;
return hr;
}
// OK. The other player is not in yet. Waiting for the System msg that tells when he's ready.
LPDPMSG_GENERIC lpMsg = NULL;
LPDPMSG_CREATEPLAYERORGROUP lpCreateMsg;
DWORD dwResult;
DWORD timeOutTime;
timeOutTime = GetTickCount() + GETREMOTEPLAYERTIMEOUT; // Wait 60 seconds.
hr = E_FAIL;
while (1)
{
// Listen to all system messages to catch the Create Player msg.
while (lpMsg = ReceiveMessage(DPID_SYSMSG,m_PlayerID,
DPRECEIVE_TOPLAYER|DPRECEIVE_FROMPLAYER))
{
if (lpMsg->dwType == DPSYS_CREATEPLAYERORGROUP)
{
lpCreateMsg = (LPDPMSG_CREATEPLAYERORGROUP)lpMsg;
if (lpCreateMsg->dwPlayerType == DPPLAYERTYPE_PLAYER &&
lpCreateMsg->dpId != m_PlayerID)
{
*pID = lpCreateMsg->dpId;
delete lpMsg;
hr = S_OK;
goto done;
}
}
delete lpMsg;
}
if (timeOutTime < GetTickCount()) // Timeout.
break;
// Wait for next message.
dwResult = WaitForSingleObject(m_hEventHandle,GETREMOTEPLAYERTIMEOUT);
if (dwResult != WAIT_OBJECT_0) // Timeout.
goto done;
}
done:
return hr;
}
/***************************************************
Func:
GetLocalPlayerName
Abstract:
Return the user name of local machine.
****************************************************/
HRESULT CDirectPlayConnection::GetLocalPlayerName()
{
HRESULT hr = S_OK;
// Clean up local name buffer.
if (m_bstrLocalName)
{
SysFreeString(m_bstrLocalName);
m_bstrLocalName = NULL;
}
#if USE_IM_OBJECT // Don't use the IM objects.
IMsgrObject *pObj;
hr = CoCreateInstance(CLSID_MsgrObject, //Class identifier (CLSID) of the object
NULL, //Pointer to controlling IUnknown
CLSCTX_INPROC_SERVER, //Context for running executable code
IID_IMsgrObject, //Reference to the identifier of the interface
(LPVOID*)&pObj); //Address of output variable that receives
if (SUCCEEDED(hr))
{
pObj->get_LocalFriendlyName(&m_bstrLocalName);
pObj->Release();
goto done;
}
#else // Use the local username variable
WCHAR szName[256];
DWORD dw = GetEnvironmentVariable(L"USERNAME", szName, 255);
if (dw == 0)
{
hr = E_FAIL;
goto done;
}
if (dw > 255)
{
WCHAR *p = new WCHAR[dw+1];
if (!p)
{
hr = E_OUTOFMEMORY;
goto done;
}
GetEnvironmentVariable(L"USERNAME", p, dw);
m_bstrLocalName = SysAllocString(p);
delete p;
}
else
m_bstrLocalName = SysAllocString(szName);
#endif
done:
return hr;
}
/*****************************************
Func:
GetOtherPlayerName
Abstract:
Return the name on the other side of connection
Params:
pName: buffer. If other player doesn't have a name, a null string will be returned.
pdwCount: buffer size in char.
******************************************/
HRESULT CDirectPlayConnection::GetOtherPlayerName(TCHAR* pName, DWORD* pdwCount)
{
DPID ID;
HRESULT hr = E_FAIL;
LPBYTE pBuffer = NULL;
DWORD dwOriSize = *pdwCount;
*pdwCount = 0;
if (m_idOtherPlayer == 0)
hr = GetOtherPlayerID(&m_idOtherPlayer);
if (SUCCEEDED(hr))
{
DWORD dwSize = 0;
hr = m_rDirectPlay->GetPlayerName(m_idOtherPlayer, (LPVOID)pBuffer, &dwSize);
if (hr == DPERR_BUFFERTOOSMALL)
{
pBuffer = new BYTE[dwSize];
if (!pBuffer)
{
hr = E_OUTOFMEMORY;
goto done;
}
hr = m_rDirectPlay->GetPlayerName(m_idOtherPlayer, (LPVOID)pBuffer, &dwSize);
if (hr == DP_OK)
{
LPTSTR p = ((DPNAME*)pBuffer)->lpszShortName;
if (p && *p != _T('\0'))
{
dwSize = _tcslen(p);
if (dwSize > dwOriSize)
{
hr = DPERR_BUFFERTOOSMALL;
goto done;
}
_stprintf(pName, p);
*pdwCount = dwSize;
goto done;
}
}
}
}
done:
if (pBuffer)
delete pBuffer;
return hr;
}
/*++
Routine Description:
This reads the next message for the given from and to addresses.
*/
LPDPMSG_GENERIC CDirectPlayConnection::ReceiveMessage(DPID from,DPID to,DWORD dwFlags)
{
HRESULT hr;
LPVOID lpMsg = 0x0;
DWORD dwSize = 0;
hr = m_rDirectPlay->Receive(&from,&to,dwFlags,NULL,&dwSize);
if (hr != DPERR_BUFFERTOOSMALL)
goto done; //Either no message or failed
lpMsg = (LPVOID)new BYTE [dwSize];
if (!lpMsg) //"Out of memory.
goto done;
hr = m_rDirectPlay->Receive(&from,&to,dwFlags,lpMsg,&dwSize);
if (hr != DP_OK)
{
//"Error receiving message.";
goto done;
}
return (LPDPMSG_GENERIC)lpMsg;
done:
if (lpMsg) {
delete lpMsg;
}
return NULL;
}
/* Do we need this function ???*/
static BOOL
SendLaunchSuccessful(
IN OUT LPDIRECTPLAYLOBBY2 pLobby
)
{
HRESULT hr;
DPLMSG_GENERIC msg;
msg.dwType = DPLSYS_DPLAYCONNECTSUCCEEDED;
hr = pLobby->SendLobbyMessage( DPLMSG_STANDARD, 0, &msg, sizeof(msg) );
if ( FAILED(hr) )
{
MessageBox( NULL, TEXT("Send system message failed."), TEXT("Error"), MB_OK |
MB_APPLMODAL );
return false;
}
return true;
}