|
|
/*==========================================================================
* * Copyright (C) 1996-1997 Microsoft Corporation. All Rights Reserved. * * File: dplshare.c * Content: Methods for shared buffer management * * History: * Date By Reason * ======= ======= ====== * 5/18/96 myronth Created it * 12/12/96 myronth Fixed DPLCONNECTION validation & bug #4692 * 12/13/96 myronth Fixed bugs #4697 and #4607 * 2/12/97 myronth Mass DX5 changes * 2/20/97 myronth Changed buffer R/W to be circular * 3/12/97 myronth Kill thread timeout, DPF error levels * 4/1/97 myronth Fixed handle leak -- bug #7054 * 5/8/97 myronth Added bHeader parameter to packing function * 5/21/97 ajayj DPL_SendLobbyMessage - allow DPLMSG_STANDARD flag #8929 * 5/30/97 myronth Fixed SetConnectionSettings for invalid AppID (#9110) * Fixed SetLobbyMessageEvent for invalid handle (#9111) * 6/19/97 myronth Fixed handle leak (#10063) * 7/30/97 myronth Added support for standard lobby messaging and * fixed receive loop race condition (#10843) * 8/11/97 myronth Added guidInstance handling in standard lobby requests * 8/19/97 myronth Support for DPLMSG_NEWSESSIONHOST * 8/19/97 myronth Removed dead PRV_SendStandardSystemMessageByObject * 8/20/97 myronth Added DPLMSG_STANDARD to all standard messages * 11/13/97 myronth Added guidInstance to lobby system message (#10944) * 12/2/97 myronth Fixed swallowed error code, moved structure * validation for DPLCONNECTION (#15527, 15529) * 1/20/98 myronth Added WaitForConnectionSettings * 7/9/99 aarono Cleaning up GetLastError misuse, must call right away, * before calling anything else, including DPF. * 10/31/99 aarono add node lock when to SetLobbyMessageEvent * NTB#411892 * 02/08/00 aarono added monitoring for lobby client crash/exit, notify * lobbied application, Mill B#131938 * 7/12/00 aarono fix GUIDs for IPC to be fully significant, otherwise won't IPC. ***************************************************************************/ #include "dplobpr.h"
//--------------------------------------------------------------------------
//
// Debug Functions
//
//--------------------------------------------------------------------------
#ifdef DEBUG
DPF_DUMPWSTR(int level, LPWSTR lpwStr) { char lpszStr[200]; WideToAnsi(lpszStr,lpwStr,200); DPF(level, lpszStr); } #else
#define DPF_DUMPWSTR(a,b)
#endif
//--------------------------------------------------------------------------
//
// Functions
//
//--------------------------------------------------------------------------
HRESULT PRV_ReadCommandLineIPCGuid(GUID *lpguidIPC) { LPWSTR pwszCommandLine; LPWSTR pwszAlloc=NULL; LPWSTR pwszSwitch=NULL; HRESULT hr=DP_OK; if(!OS_IsPlatformUnicode()) { // if we get a command line in ANSI, convert to UNICODE, this allows
// us to avoid the DBCS issues in ANSI while scanning for the IPC GUID
LPSTR pszCommandLine;
pszCommandLine=(LPSTR)GetCommandLineA(); pwszAlloc=DPMEM_ALLOC(MAX_PATH*sizeof(WCHAR)); if (pwszAlloc == NULL) { goto exit; } hr=AnsiToWide(pwszAlloc,pszCommandLine,MAX_PATH); if(FAILED(hr)) { goto exit; } pwszCommandLine=pwszAlloc; } else { pwszCommandLine=(LPWSTR)GetCommandLine(); }
// pwszCommandLine now points to the UNICODE command line.
if(pwszSwitch=OS_StrStr(pwszCommandLine,SZ_DP_IPC_GUID)){ // found the GUID on the command line
if (OS_StrLen(pwszSwitch) >= (sizeof(SZ_DP_IPC_GUID)+sizeof(SZ_GUID_PROTOTYPE)-sizeof(WCHAR))/sizeof(WCHAR)){ // skip past the switch description to the actual GUID and extract
hr=GUIDFromString(pwszSwitch+(sizeof(SZ_DP_IPC_GUID)/sizeof(WCHAR))-1, lpguidIPC); } else { hr=DPERR_GENERIC; } } else { hr=DPERR_GENERIC; }
exit:
if(pwszAlloc){ DPMEM_FREE(pwszAlloc); } return hr; }
#undef DPF_MODNAME
#define DPF_MODNAME "PRV_GetInternalName"
HRESULT PRV_GetInternalName(LPDPLOBBYI_GAMENODE lpgn, DWORD dwType, LPWSTR lpName) { DWORD pid; LPWSTR lpFileName; LPSTR lpstr1, lpstr2, lpstr3; char szName[MAX_MMFILENAME_LENGTH * sizeof(WCHAR)]; BOOL bUseGuid=FALSE;
DPF(7, "Entering PRV_GetInternalName"); DPF(9, "Parameters: 0x%08x, 0x%08x, 0x%08x", lpgn, dwType, lpName);
if(lpgn->dwFlags & GN_IPCGUID_SET){ bUseGuid=TRUE; } // Get the current process ID if we are a game, otherwise, we need to
// get the process ID of the game that we spawned
else if(lpgn->dwFlags & GN_LOBBY_CLIENT) { if(lpgn->dwGameProcessID) pid = lpgn->dwGameProcessID; else return DPERR_APPNOTSTARTED; } else { pid = GetCurrentProcessId(); }
switch(dwType) { case TYPE_CONNECT_DATA_FILE: lpFileName = SZ_CONNECT_DATA_FILE; break;
case TYPE_CONNECT_DATA_MUTEX: lpFileName = SZ_CONNECT_DATA_MUTEX; break;
case TYPE_GAME_WRITE_FILE: lpFileName = SZ_GAME_WRITE_FILE; break;
case TYPE_LOBBY_WRITE_FILE: lpFileName = SZ_LOBBY_WRITE_FILE; break;
case TYPE_LOBBY_WRITE_EVENT: lpFileName = SZ_LOBBY_WRITE_EVENT; break;
case TYPE_GAME_WRITE_EVENT: lpFileName = SZ_GAME_WRITE_EVENT; break;
case TYPE_LOBBY_WRITE_MUTEX: lpFileName = SZ_LOBBY_WRITE_MUTEX; break;
case TYPE_GAME_WRITE_MUTEX: lpFileName = SZ_GAME_WRITE_MUTEX; break;
default: DPF(2, "We got an Internal Name Type that we didn't expect!"); return DPERR_GENERIC; }
GetAnsiString(&lpstr2, SZ_FILENAME_BASE); GetAnsiString(&lpstr3, lpFileName);
if(!bUseGuid){ // REVIEW!!!! -- I can't get the Unicode version of wsprintf to work, so
// for now, use the ANSI version and convert
// wsprintf(lpName, SZ_NAME_TEMPLATE, SZ_FILENAME_BASE, lpFileName, pid);
GetAnsiString(&lpstr1, SZ_NAME_TEMPLATE); wsprintfA((LPSTR)szName, lpstr1, lpstr2, lpstr3, pid); } else { GetAnsiString(&lpstr1, SZ_GUID_NAME_TEMPLATE); wsprintfA((LPSTR)szName, lpstr1, lpstr2, lpstr3); }
AnsiToWide(lpName, szName, (strlen(szName) + 1));
if(bUseGuid){ // concatenate the guid to the name if we are using the guid.
WCHAR *pGuid; pGuid = lpName + WSTRLEN(lpName) - 1; StringFromGUID(&lpgn->guidIPC, pGuid, GUID_STRING_SIZE); }
DPF(9, "Made internal Name..."); DPF_DUMPWSTR(9,lpName);
if(lpstr1) DPMEM_FREE(lpstr1); if(lpstr2) DPMEM_FREE(lpstr2); if(lpstr3) DPMEM_FREE(lpstr3);
return DP_OK;
} // PRV_GetInternalName
#undef DPF_MODNAME
#define DPF_MODNAME "PRV_AddNewGameNode"
HRESULT PRV_AddNewGameNode(LPDPLOBBYI_DPLOBJECT this, LPDPLOBBYI_GAMENODE * lplpgn, DWORD dwGameID, HANDLE hGameProcess, BOOL bLobbyClient, GUID *lpguidIPC) { LPDPLOBBYI_GAMENODE lpgn;
DPF(7, "Entering PRV_AddNewGameNode"); DPF(9, "Parameters: 0x%08x, 0x%08x, 0x%08x, 0x%08x, %lu", this, lplpgn, dwGameID, hGameProcess, bLobbyClient);
lpgn = DPMEM_ALLOC(sizeof(DPLOBBYI_GAMENODE)); if(!lpgn) { DPF(2, "Unable to allocate memory for GameNode structure!"); return DPERR_OUTOFMEMORY; }
// Initialize the GameNode
lpgn->dwSize = sizeof(DPLOBBYI_GAMENODE); lpgn->dwGameProcessID = dwGameID; lpgn->hGameProcess = hGameProcess; lpgn->this = this; lpgn->MessageHead.lpPrev = &lpgn->MessageHead; lpgn->MessageHead.lpNext = &lpgn->MessageHead;
if(lpguidIPC){ // provided during launch by lobby client
lpgn->guidIPC=*lpguidIPC; lpgn->dwFlags |= GN_IPCGUID_SET; } else { // need to extract the GUID from the command line if present.
if(DP_OK==PRV_ReadCommandLineIPCGuid(&lpgn->guidIPC)){ lpgn->dwFlags |= GN_IPCGUID_SET; } }
// If we are a lobby client, set the flag
if(bLobbyClient) lpgn->dwFlags |= GN_LOBBY_CLIENT; // Add the GameNode to the list
lpgn->lpgnNext = this->lpgnHead; this->lpgnHead = lpgn;
// Set the output pointer
*lplpgn = lpgn;
return DP_OK;
} // PRV_AddNewGameNode
#undef DPF_MODNAME
#define DPF_MODNAME "PRV_GetGameNode"
LPDPLOBBYI_GAMENODE PRV_GetGameNode(LPDPLOBBYI_GAMENODE lpgnHead, DWORD dwGameID) { LPDPLOBBYI_GAMENODE lpgnTemp = lpgnHead; GUID guidIPC=GUID_NULL; BOOL bFoundGUID;
DPF(7, "Entering PRV_GetGameNode"); DPF(9, "Parameters: 0x%08x, 0x%08x", lpgnHead, dwGameID);
if(DP_OK==PRV_ReadCommandLineIPCGuid(&guidIPC)){ bFoundGUID=TRUE; } else { bFoundGUID=FALSE; }
while(lpgnTemp) { if((lpgnTemp->dwGameProcessID == dwGameID) || ((bFoundGUID) && (lpgnTemp->dwFlags & GN_IPCGUID_SET) && (IsEqualGUID(&lpgnTemp->guidIPC,&guidIPC)))) return lpgnTemp; else lpgnTemp = lpgnTemp->lpgnNext; }
return NULL;
} // PRV_GetGameNode
#undef DPF_MODNAME
#define DPF_MODNAME "PRV_SetupClientDataAccess"
BOOL PRV_SetupClientDataAccess(LPDPLOBBYI_GAMENODE lpgn) { SECURITY_ATTRIBUTES sa; HANDLE hConnDataMutex = NULL; HANDLE hLobbyWrite = NULL; HANDLE hLobbyWriteMutex = NULL; HANDLE hGameWrite = NULL; HANDLE hGameWriteMutex = NULL; WCHAR szName[MAX_MMFILENAME_LENGTH * sizeof(WCHAR)];
DPF(7, "Entering PRV_SetupClientDataAccess"); DPF(9, "Parameters: 0x%08x", lpgn);
// Set up the security attributes (so that our objects can
// be inheritable)
sa.nLength = sizeof(SECURITY_ATTRIBUTES); sa.lpSecurityDescriptor = NULL; sa.bInheritHandle = TRUE;
// Create the ConnectionData Mutex
if(SUCCEEDED(PRV_GetInternalName(lpgn, TYPE_CONNECT_DATA_MUTEX, (LPWSTR)szName))) { hConnDataMutex = OS_CreateMutex(&sa, FALSE, (LPWSTR)szName); }
// Create the GameWrite Event
if(SUCCEEDED(PRV_GetInternalName(lpgn, TYPE_GAME_WRITE_EVENT, (LPWSTR)szName))) { hGameWrite = OS_CreateEvent(&sa, FALSE, FALSE, (LPWSTR)szName); }
// Create the GameWrite Mutex
if(SUCCEEDED(PRV_GetInternalName(lpgn, TYPE_GAME_WRITE_MUTEX, (LPWSTR)szName))) { hGameWriteMutex = OS_CreateMutex(&sa, FALSE, (LPWSTR)szName); }
// Create the LobbyWrite Event
if(SUCCEEDED(PRV_GetInternalName(lpgn, TYPE_LOBBY_WRITE_EVENT, (LPWSTR)szName))) { hLobbyWrite = OS_CreateEvent(&sa, FALSE, FALSE, (LPWSTR)szName); }
// Create the LobbyWrite Mutex
if(SUCCEEDED(PRV_GetInternalName(lpgn, TYPE_LOBBY_WRITE_MUTEX, (LPWSTR)szName))) { hLobbyWriteMutex = OS_CreateMutex(&sa, FALSE, (LPWSTR)szName); }
// Check for errors
if(!hConnDataMutex || !hGameWrite || !hGameWriteMutex || !hLobbyWrite || !hLobbyWriteMutex) { if(hConnDataMutex) CloseHandle(hConnDataMutex); if(hGameWrite) CloseHandle(hGameWrite); if(hGameWriteMutex) CloseHandle(hGameWriteMutex); if(hLobbyWrite) CloseHandle(hLobbyWrite); if(hLobbyWriteMutex) CloseHandle(hLobbyWriteMutex);
return FALSE; }
// Save the handles
lpgn->hConnectDataMutex = hConnDataMutex; lpgn->hGameWriteEvent = hGameWrite; lpgn->hGameWriteMutex = hGameWriteMutex; lpgn->hLobbyWriteEvent = hLobbyWrite; lpgn->hLobbyWriteMutex = hLobbyWriteMutex;
return TRUE;
} // PRV_SetupClientDataAccess
#undef DPF_MODNAME
#define DPF_MODNAME "PRV_GetDataBuffer"
HRESULT PRV_GetDataBuffer(LPDPLOBBYI_GAMENODE lpgn, DWORD dwType, DWORD dwSize, LPHANDLE lphFile, LPVOID * lplpMemory) { HRESULT hr; SECURITY_ATTRIBUTES sa; WCHAR szName[MAX_MMFILENAME_LENGTH * sizeof(WCHAR)]; LPVOID lpMemory = NULL; HANDLE hFile = NULL; DWORD dwError = 0;
DPF(7, "Entering PRV_GetDataBuffer"); DPF(9, "Parameters: 0x%08x, 0x%08x, %lu, 0x%08x, 0x%08x", lpgn, dwType, dwSize, lphFile, lplpMemory);
// Get the data buffer filename
hr = PRV_GetInternalName(lpgn, dwType, (LPWSTR)szName); if(FAILED(hr)) return hr;
// If we are a Lobby Client, we need to create the file. If we
// are a game, we need to open the already created file for
// connection data, or we can create the file for game data (if
// it doesn't already exist).
if(lpgn->dwFlags & GN_LOBBY_CLIENT) { // Set up the security attributes (so that our mapping can
// be inheritable
sa.nLength = sizeof(SECURITY_ATTRIBUTES); sa.lpSecurityDescriptor = NULL; sa.bInheritHandle = TRUE; // Create the file mapping
hFile = OS_CreateFileMapping(INVALID_HANDLE_VALUE, &sa, PAGE_READWRITE, 0, dwSize, (LPWSTR)szName); } else { hFile = OS_OpenFileMapping(FILE_MAP_ALL_ACCESS, TRUE, (LPWSTR)szName); }
if(!hFile) { dwError = GetLastError(); // WARNING: error may not be correct since calls we are trying to get last error from may have called out
// to another function before returning.
DPF(5, "Couldn't get a handle to the shared local memory, dwError = %lu (error may not be correct)", dwError); return DPERR_OUTOFMEMORY; }
// Map a View of the file
lpMemory = MapViewOfFile(hFile, FILE_MAP_ALL_ACCESS, 0, 0, 0);
if(!lpMemory) { dwError = GetLastError(); DPF(5, "Unable to get pointer to shared local memory, dwError = %lu", dwError); CloseHandle(hFile); return DPERR_OUTOFMEMORY; }
// Setup the control structure based on the buffer type
switch(dwType) { case TYPE_CONNECT_DATA_FILE: { LPDPLOBBYI_CONNCONTROL lpControl = NULL; lpControl = (LPDPLOBBYI_CONNCONTROL)lpMemory;
// If the buffer has been initialized, then don't worry
// about it. If the token is wrong (uninitialized), then do it
if(lpControl->dwToken != BC_TOKEN) { lpControl->dwToken = BC_TOKEN; lpControl->dwFlags = 0; if(lpgn->dwFlags & GN_LOBBY_CLIENT){ lpControl->CliProcId = GetCurrentProcessId(); } } break; } case TYPE_GAME_WRITE_FILE: case TYPE_LOBBY_WRITE_FILE: { LPDPLOBBYI_BUFFERCONTROL lpControl = NULL;
lpControl = (LPDPLOBBYI_BUFFERCONTROL)lpMemory; if(lpgn->dwFlags & GN_LOBBY_CLIENT) { // Since we're the lobby client, we know we create the buffer, so
// initialize the entire structure
lpControl->dwToken = BC_TOKEN; lpControl->dwReadOffset = sizeof(DPLOBBYI_BUFFERCONTROL); lpControl->dwWriteOffset = sizeof(DPLOBBYI_BUFFERCONTROL); lpControl->dwFlags = BC_LOBBY_ACTIVE; lpControl->dwMessages = 0; lpControl->dwBufferSize = dwSize; lpControl->dwBufferLeft = dwSize - sizeof(DPLOBBYI_BUFFERCONTROL); } else { // We're the game, but we don't know for sure if we just created
// the buffer or if a lobby client did. So check the token. If
// it is incorrect, we will assume we just created it and we need
// to initialize the buffer control struct. Otherwise, we will
// assume a lobby client created it and we just need to add
// our flag.
if(lpControl->dwToken != BC_TOKEN) { // We don't see the token, so initialize the structure
lpControl->dwReadOffset = sizeof(DPLOBBYI_BUFFERCONTROL); lpControl->dwWriteOffset = sizeof(DPLOBBYI_BUFFERCONTROL); lpControl->dwFlags = BC_GAME_ACTIVE; lpControl->dwMessages = 0; lpControl->dwBufferSize = dwSize; lpControl->dwBufferLeft = dwSize - sizeof(DPLOBBYI_BUFFERCONTROL); } else { // We assume the lobby created this buffer, so just set our flag
lpControl->dwFlags |= BC_GAME_ACTIVE; } } break; } }
// Fill in the output parameters
*lphFile = hFile; *lplpMemory = lpMemory;
return DP_OK;
} // PRV_GetDataBuffer
#undef DPF_MODNAME
#define DPF_MODNAME "PRV_StartReceiveThread"
HRESULT PRV_StartReceiveThread(LPDPLOBBYI_GAMENODE lpgn) { HANDLE hReceiveThread = NULL; HANDLE hKillEvent = NULL; DWORD dwThreadID;
DPF(7, "Entering PRV_StartReceiveThread"); DPF(9, "Parameters: 0x%08x", lpgn);
ASSERT(lpgn);
// Create the kill event if one doesn't exists
if(!(lpgn->hKillReceiveThreadEvent)) { hKillEvent = OS_CreateEvent(NULL, FALSE, FALSE, NULL); if(!hKillEvent) { DPF(2, "Unable to create Kill Receive Thread Event"); return DPERR_OUTOFMEMORY; } }
// If the Receive Thread isn't going, start it
if(!(lpgn->hReceiveThread)) { // Spawn off a receive notification thread for the cross-proc communication
hReceiveThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) PRV_ReceiveClientNotification, lpgn, 0, &dwThreadID);
if(!hReceiveThread) { DPF(2, "Unable to create Receive Thread!"); if(hKillEvent) CloseHandle(hKillEvent); return DPERR_OUTOFMEMORY; }
lpgn->hReceiveThread = hReceiveThread; if(hKillEvent) lpgn->hKillReceiveThreadEvent = hKillEvent;
}
return DP_OK;
} // PRV_StartReceiveThread
#undef DPF_MODNAME
#define DPF_MODNAME "PRV_SetupAllSharedMemory"
HRESULT PRV_SetupAllSharedMemory(LPDPLOBBYI_GAMENODE lpgn) { HRESULT hr; LPVOID lpConnDataMemory = NULL; LPVOID lpGameMemory = NULL; LPVOID lpLobbyMemory = NULL; HANDLE hFileConnData = NULL; HANDLE hFileGameWrite = NULL; HANDLE hFileLobbyWrite = NULL; DWORD dwError = 0;
DPF(7, "Entering PRV_SetupAllSharedMemory"); DPF(9, "Parameters: 0x%08x", lpgn);
// Get access to the Connection Data File
hr = PRV_GetDataBuffer(lpgn, TYPE_CONNECT_DATA_FILE, MAX_APPDATABUFFERSIZE, &hFileConnData, &lpConnDataMemory); if(FAILED(hr)) { DPF(5, "Couldn't get access to Connection Data buffer"); goto ERROR_SETUP_SHARED_MEMORY; }
// Do the same for the Game Write File...
hr = PRV_GetDataBuffer(lpgn, TYPE_GAME_WRITE_FILE, MAX_APPDATABUFFERSIZE, &hFileGameWrite, &lpGameMemory); if(FAILED(hr)) { DPF(5, "Couldn't get access to Game Write buffer"); goto ERROR_SETUP_SHARED_MEMORY; }
// Do the same for the Lobby Write File...
hr = PRV_GetDataBuffer(lpgn, TYPE_LOBBY_WRITE_FILE, MAX_APPDATABUFFERSIZE, &hFileLobbyWrite, &lpLobbyMemory); if(FAILED(hr)) { DPF(5, "Couldn't get access to Lobby Write buffer"); goto ERROR_SETUP_SHARED_MEMORY; }
// Setup the signalling objects
if(!PRV_SetupClientDataAccess(lpgn)) { DPF(5, "Unable to create synchronization objects for shared memory!"); return DPERR_OUTOFMEMORY; }
// Save the file handles
lpgn->hConnectDataFile = hFileConnData; lpgn->lpConnectDataBuffer = lpConnDataMemory; lpgn->hGameWriteFile = hFileGameWrite; lpgn->lpGameWriteBuffer = lpGameMemory; lpgn->hLobbyWriteFile = hFileLobbyWrite; lpgn->lpLobbyWriteBuffer = lpLobbyMemory;
// Set the flag that tells us the shared memory files are valid
lpgn->dwFlags |= GN_SHARED_MEMORY_AVAILABLE;
// Start the Receive Thread
hr = PRV_StartReceiveThread(lpgn); if(FAILED(hr)) { // In this case, we will keep our shared buffers around. Don't
// worry about cleaning them up here -- we'll probably still need
// them later, and they will get cleaned up later.
DPF(5, "Unable to start receive thread"); return hr; }
return DP_OK;
ERROR_SETUP_SHARED_MEMORY:
if(hFileConnData) CloseHandle(hFileConnData); if(lpConnDataMemory) UnmapViewOfFile(lpConnDataMemory); if(hFileGameWrite) CloseHandle(hFileGameWrite); if(lpGameMemory) UnmapViewOfFile(lpGameMemory); if(hFileLobbyWrite) CloseHandle(hFileLobbyWrite); if(lpLobbyMemory) UnmapViewOfFile(lpLobbyMemory);
return hr;
} // PRV_SetupAllSharedMemory
#undef DPF_MODNAME
#define DPF_MODNAME "PRV_EnterConnSettingsWaitMode"
void PRV_EnterConnSettingsWaitMode(LPDPLOBBYI_GAMENODE lpgn) { LPDPLOBBYI_CONNCONTROL lpConnControl = NULL; LPDPLOBBYI_BUFFERCONTROL lpBufferControl = NULL;
DPF(7, "Entering PRV_EnterConnSettingsWaitMode"); DPF(9, "Parameters: 0x%08x", lpgn);
ASSERT(lpgn);
// Set the flag in the ConnSettings buffer
WaitForSingleObject(lpgn->hConnectDataMutex, INFINITE); lpConnControl = (LPDPLOBBYI_CONNCONTROL)lpgn->lpConnectDataBuffer; lpConnControl->dwFlags |= BC_WAIT_MODE; ReleaseMutex(lpgn->hConnectDataMutex);
// Set the flag in the GameWrite buffer
WaitForSingleObject(lpgn->hGameWriteMutex, INFINITE); lpBufferControl = (LPDPLOBBYI_BUFFERCONTROL)lpgn->lpGameWriteBuffer; lpBufferControl->dwFlags |= BC_WAIT_MODE; ReleaseMutex(lpgn->hGameWriteMutex);
// Set the flag in the LobbyWrite buffer
WaitForSingleObject(lpgn->hLobbyWriteMutex, INFINITE); lpBufferControl = (LPDPLOBBYI_BUFFERCONTROL)lpgn->lpLobbyWriteBuffer; lpBufferControl->dwFlags |= BC_WAIT_MODE; ReleaseMutex(lpgn->hLobbyWriteMutex);
} // PRV_EnterConnSettingsWaitMode
#undef DPF_MODNAME
#define DPF_MODNAME "PRV_LeaveConnSettingsWaitMode"
void PRV_LeaveConnSettingsWaitMode(LPDPLOBBYI_GAMENODE lpgn) { LPDPLOBBYI_CONNCONTROL lpConnControl = NULL; LPDPLOBBYI_BUFFERCONTROL lpBufferControl = NULL;
DPF(7, "Entering PRV_LeaveConnSettingsWaitMode"); DPF(9, "Parameters: 0x%08x", lpgn);
ASSERT(lpgn);
// Clear the flag in the ConnSettings buffer
WaitForSingleObject(lpgn->hConnectDataMutex, INFINITE); lpConnControl = (LPDPLOBBYI_CONNCONTROL)lpgn->lpConnectDataBuffer; lpConnControl->dwFlags &= ~(BC_WAIT_MODE | BC_PENDING_CONNECT); ReleaseMutex(lpgn->hConnectDataMutex);
// Clear the flag in the GameWrite buffer
WaitForSingleObject(lpgn->hGameWriteMutex, INFINITE); lpBufferControl = (LPDPLOBBYI_BUFFERCONTROL)lpgn->lpGameWriteBuffer; lpBufferControl->dwFlags &= ~BC_WAIT_MODE; ReleaseMutex(lpgn->hGameWriteMutex);
// Clear the flag in the LobbyWrite buffer
WaitForSingleObject(lpgn->hLobbyWriteMutex, INFINITE); lpBufferControl = (LPDPLOBBYI_BUFFERCONTROL)lpgn->lpLobbyWriteBuffer; lpBufferControl->dwFlags &= ~BC_WAIT_MODE; ReleaseMutex(lpgn->hLobbyWriteMutex);
} // PRV_LeaveConnSettingsWaitMode
#undef DPF_MODNAME
#define DPF_MODNAME "PRV_WriteClientData"
HRESULT PRV_WriteClientData(LPDPLOBBYI_GAMENODE lpgn, DWORD dwFlags, LPVOID lpData, DWORD dwSize) { LPDPLOBBYI_BUFFERCONTROL lpControl = NULL; LPDPLOBBYI_MESSAGEHEADER lpHeader = NULL; HANDLE hMutex = NULL; DWORD dwSizeToEnd = 0; LPBYTE lpTemp = NULL; HRESULT hr = DP_OK; DWORD dwReadOffset; DWORD dwWriteOffset; DWORD dwBufferSize;
DPF(7, "Entering PRV_WriteClientData"); DPF(9, "Parameters: 0x%08x, 0x%08x, 0x%08x, %lu", lpgn, dwFlags, lpData, dwSize);
// Make sure we have a valid shared memory buffer
// Note: Take the GameNode lock so that nobody changes the flags
// for the buffers, or the buffers themselves out from under us.
ENTER_DPLGAMENODE(); if(!(lpgn->dwFlags & GN_SHARED_MEMORY_AVAILABLE)) { hr = PRV_SetupAllSharedMemory(lpgn); if(FAILED(hr)) { LEAVE_DPLGAMENODE(); DPF(2, "Unable to access App Data memory"); return hr; } } LEAVE_DPLGAMENODE();
// Grab the mutex
hMutex = (lpgn->dwFlags & GN_LOBBY_CLIENT) ? (lpgn->hLobbyWriteMutex) : (lpgn->hGameWriteMutex); WaitForSingleObject(hMutex, INFINITE);
// Get a pointer to our control structure
lpControl = (LPDPLOBBYI_BUFFERCONTROL)((lpgn->dwFlags & GN_LOBBY_CLIENT) ? (lpgn->lpLobbyWriteBuffer) : (lpgn->lpGameWriteBuffer));
// If we're in wait mode, bail
if(lpControl->dwFlags & BC_WAIT_MODE) { DPF_ERR("Cannot send lobby message while in Wait Mode for new ConnectionSettings"); hr = DPERR_UNAVAILABLE; goto EXIT_WRITE_CLIENT_DATA; }
// If we are the game, check to see if the lobby client is even there. In
// the self-lobbied case, it won't be. If it is not there, don't even
// bother sending anything.
if((!(lpgn->dwFlags & GN_LOBBY_CLIENT)) && (!(lpControl->dwFlags & BC_LOBBY_ACTIVE))) { DPF(5, "There is not active lobby client; Not sending message"); hr = DPERR_UNAVAILABLE; goto EXIT_WRITE_CLIENT_DATA; }
// Make sure there is enough space left for the message and two dwords
if(lpControl->dwBufferLeft < (dwSize + sizeof(DPLOBBYI_MESSAGEHEADER))) { DPF(5, "Not enough space left in the message buffer"); hr = DPERR_BUFFERTOOSMALL; goto EXIT_WRITE_CLIENT_DATA; }
if(lpControl->dwBufferLeft > MAX_APPDATABUFFERSIZE-(sizeof(DPLOBBYI_BUFFERCONTROL))) { DPF(4,"SECURITY WARN: invalid amount of buffer left in write buffer"); hr=DPERR_UNAVAILABLE; goto EXIT_WRITE_CLIENT_DATA; }
// SECURITY: need to snapshot these so they aren't
// altered by attacking code during processing
dwReadOffset = lpControl->dwReadOffset; dwWriteOffset = lpControl->dwWriteOffset; dwBufferSize = lpControl->dwBufferSize;
if(dwReadOffset >= MAX_APPDATABUFFERSIZE || dwWriteOffset >= MAX_APPDATABUFFERSIZE) { DPF(4,"SECURITY WARN: invalid offsets found in shared memory control block, bailing"); hr=DPERR_UNAVAILABLE; goto EXIT_WRITE_CLIENT_DATA; }
if(dwBufferSize != MAX_APPDATABUFFERSIZE) { DPF(4,"SECURITY WARN: shared memory control block buffer size tampered with, bailing"); hr=DPERR_UNAVAILABLE; goto EXIT_WRITE_CLIENT_DATA; }
// Copy in the data. First make sure we can write from the cursor
// forward without having to wrap around to the beginning of the buffer,
// but make sure we don't write past the read cursor
if(dwWriteOffset >= dwReadOffset) { // Our write pointer is ahead of our read pointer (cool). Figure
// out if we have enough room between our write pointer and the
// end of the buffer. If we do, then just write it. If we don't
// we need to wrap it.
dwSizeToEnd = dwBufferSize - dwWriteOffset; if(dwSizeToEnd >= (dwSize + sizeof(DPLOBBYI_MESSAGEHEADER))) { // We have enough room
lpHeader = (LPDPLOBBYI_MESSAGEHEADER)((LPBYTE)lpControl + dwWriteOffset); lpHeader->dwSize = dwSize; lpHeader->dwFlags = dwFlags; lpTemp = (LPBYTE)(++lpHeader);
memcpySecureD(lpTemp, lpData, dwSize, lpControl, MAX_APPDATABUFFERSIZE, "SECURITY WARN: shared memory was about to write outside shared buffer, aborting", hr=DPERR_UNAVAILABLE, EXIT_WRITE_CLIENT_DATA);
// Move the write cursor, and check to see if we have enough
// room for the header on the next message. If the move causes
// us to wrap, or if we are within one header's size,
// we need to move the write cursor back to the beginning
// of the buffer
dwWriteOffset += dwSize + sizeof(DPLOBBYI_MESSAGEHEADER); if(dwWriteOffset > (dwBufferSize - sizeof(DPLOBBYI_MESSAGEHEADER))) { // Increment the amount of free buffer by the amount we
// are about to skip over to wrap
lpControl->dwBufferLeft -= (lpControl->dwBufferSize - dwWriteOffset); // We're closer than one header's size
dwWriteOffset = sizeof(DPLOBBYI_BUFFERCONTROL); }
} else { // We don't have enough room before the end, so we need to
// wrap the message (ugh). Here's the rules:
// 1. If we don't have enough bytes for the header, start
// the whole thing at the beginning of the buffer
// 2. If we have enough bytes, write as much
// as we can and wrap the rest.
if(dwSizeToEnd < sizeof(DPLOBBYI_MESSAGEHEADER)) { // We don't even have room for our two dwords, so wrap
// the whole thing. So first decrement the amount of
// free memory left and make sure we will still fit
lpControl->dwBufferLeft -= dwSizeToEnd; if(lpControl->dwBufferLeft < (dwSize + sizeof(DPLOBBYI_MESSAGEHEADER))) { DPF(5, "Not enough space left in the message buffer"); hr = DPERR_BUFFERTOOSMALL; goto EXIT_WRITE_CLIENT_DATA; } // Reset the write pointer and copy
lpHeader = (LPDPLOBBYI_MESSAGEHEADER)((LPBYTE)lpControl + sizeof(DPLOBBYI_BUFFERCONTROL)); lpHeader->dwSize = dwSize; lpHeader->dwFlags = dwFlags; lpTemp = (LPBYTE)(++lpHeader); memcpySecureD(lpTemp, lpData, dwSize, lpControl, MAX_APPDATABUFFERSIZE, "SECURITY WARN: shared memory was about to write outside shared buffer, aborting", hr=DPERR_UNAVAILABLE, EXIT_WRITE_CLIENT_DATA);
// Move the write cursor
dwWriteOffset += sizeof(DPLOBBYI_BUFFERCONTROL) + (dwSize + sizeof(DPLOBBYI_MESSAGEHEADER)); } else { // We at least have enough room for the two dwords
lpHeader = (LPDPLOBBYI_MESSAGEHEADER)((LPBYTE)lpControl + dwWriteOffset); lpHeader->dwSize = dwSize; lpHeader->dwFlags = dwFlags;
// Now figure out how much we can write
lpTemp = (LPBYTE)(++lpHeader); dwSizeToEnd -= sizeof(DPLOBBYI_MESSAGEHEADER); if(!dwSizeToEnd) { // We need to wrap to write the whole message
lpTemp = (LPBYTE)lpControl + sizeof(DPLOBBYI_BUFFERCONTROL); memcpySecureD(lpTemp, lpData, dwSize, lpControl, MAX_APPDATABUFFERSIZE, "SECURITY WARN: shared memory was about to write outside shared buffer, aborting", hr=DPERR_UNAVAILABLE, EXIT_WRITE_CLIENT_DATA);
// Move the write cursor
dwWriteOffset = sizeof(DPLOBBYI_BUFFERCONTROL) + dwSize; } else { memcpySecureD(lpTemp, lpData, dwSizeToEnd, lpControl, MAX_APPDATABUFFERSIZE, "SECURITY WARN: shared memory was about to write outside shared buffer, aborting", hr=DPERR_UNAVAILABLE, EXIT_WRITE_CLIENT_DATA);
// Move both pointers and finish the job
lpTemp = (LPBYTE)lpControl + sizeof(DPLOBBYI_BUFFERCONTROL);
memcpySecureD(lpTemp, ((LPBYTE)lpData+dwSizeToEnd), (dwSize-dwSizeToEnd), lpControl, MAX_APPDATABUFFERSIZE, "SECURITY WARN: shared memory was about to write outside shared buffer, aborting", hr=DPERR_UNAVAILABLE, EXIT_WRITE_CLIENT_DATA);
// Move the write cursor
dwWriteOffset = sizeof(DPLOBBYI_BUFFERCONTROL) + (dwSize - dwSizeToEnd); } } } } else { // Our read pointer is ahead of our write pointer. Since we checked
// and found there is enough room to write, we should just be able
// to just slam this guy in.
lpHeader = (LPDPLOBBYI_MESSAGEHEADER)((LPBYTE)lpControl + dwWriteOffset); lpHeader->dwSize = dwSize; lpHeader->dwFlags = dwFlags; lpTemp = (LPBYTE)(++lpHeader); memcpySecureD(lpTemp, lpData, dwSize, lpControl, MAX_APPDATABUFFERSIZE, "SECURITY WARN: shared memory was about to write outside shared buffer, aborting", hr=DPERR_UNAVAILABLE, EXIT_WRITE_CLIENT_DATA);
// Move the write cursor
dwWriteOffset += dwSize + sizeof(DPLOBBYI_MESSAGEHEADER); }
lpControl->dwWriteOffset = dwWriteOffset;
// Decrement the amount of free space left and increment the message count
lpControl->dwBufferLeft -= (dwSize + sizeof(DPLOBBYI_MESSAGEHEADER)); lpControl->dwMessages++;
// Signal the other user that we have written something
SetEvent((lpgn->dwFlags & GN_LOBBY_CLIENT) ? (lpgn->hLobbyWriteEvent) : (lpgn->hGameWriteEvent));
// Fall through
EXIT_WRITE_CLIENT_DATA:
// Release the mutex
ReleaseMutex(hMutex); return hr;
} // PRV_WriteClientData
#undef DPF_MODNAME
#define DPF_MODNAME "PRV_SendStandardSystemMessage"
HRESULT PRV_SendStandardSystemMessage(LPDIRECTPLAYLOBBY lpDPL, DWORD dwMessage, DWORD dwGameID) { LPDPLOBBYI_DPLOBJECT this = NULL; LPDPLOBBYI_GAMENODE lpgn = NULL; HRESULT hr; DWORD dwMessageSize; LPVOID lpmsg = NULL; DWORD dwFlags;
DPF(7, "Entering PRV_SendStandardSystemMessage"); DPF(9, "Parameters: 0x%08x, %lu, %lu", lpDPL, dwMessage, dwGameID);
ENTER_DPLOBBY();
TRY { if( !VALID_DPLOBBY_INTERFACE( lpDPL )) { LEAVE_DPLOBBY(); return DPERR_INVALIDINTERFACE; }
this = DPLOBJECT_FROM_INTERFACE(lpDPL); if( !VALID_DPLOBBY_PTR( this ) ) { LEAVE_DPLOBBY(); return DPERR_INVALIDOBJECT; } }
EXCEPT( EXCEPTION_EXECUTE_HANDLER ) { LEAVE_DPLOBBY(); DPF_ERR( "Exception encountered validating parameters" ); return DPERR_INVALIDPARAMS; }
// If dwGameID is zero it means that we are the game, so
// we need to get the current process ID. Otherwise, it
// means we are the lobby client
if(!dwGameID) dwGameID = GetCurrentProcessId();
// Now find the correct game node
lpgn = PRV_GetGameNode(this->lpgnHead, dwGameID); if(!lpgn) { if(FAILED(PRV_AddNewGameNode(this, &lpgn, dwGameID, NULL, FALSE,NULL))) { LEAVE_DPLOBBY(); return DPERR_OUTOFMEMORY; } }
// Get the size of the message
switch(dwMessage) { case DPLSYS_NEWSESSIONHOST: dwMessageSize = sizeof(DPLMSG_NEWSESSIONHOST); break;
default: dwMessageSize = sizeof(DPLMSG_SYSTEMMESSAGE); break; }
// Allocate a buffer for the message
lpmsg = DPMEM_ALLOC(dwMessageSize); if(!lpmsg) { LEAVE_DPLOBBY(); DPF_ERRVAL("Unable to allocate memory for lobby system message, dwMessage = %lu", dwMessage); return DPERR_OUTOFMEMORY; }
// Setup the message
((LPDPLMSG_SYSTEMMESSAGE)lpmsg)->dwType = dwMessage; ((LPDPLMSG_SYSTEMMESSAGE)lpmsg)->guidInstance = lpgn->guidInstance;
// Write into the shared buffer
dwFlags = DPLMSG_SYSTEM | DPLMSG_STANDARD; hr = PRV_WriteClientData(lpgn, dwFlags, lpmsg, dwMessageSize); if(FAILED(hr)) { DPF(8, "Couldn't send system message"); }
// Free our buffer
DPMEM_FREE(lpmsg);
LEAVE_DPLOBBY(); return hr;
} // PRV_SendStandardSystemMessage
#undef DPF_MODNAME
#define DPF_MODNAME "PRV_AddNewRequestNode"
HRESULT PRV_AddNewRequestNode(LPDPLOBBYI_DPLOBJECT this, LPDPLOBBYI_GAMENODE lpgn, LPDPLMSG_GENERIC lpmsg, BOOL bSlamGuid) { LPDPLOBBYI_REQUESTNODE lprn = NULL;
// Allocate memory for a Request Node
lprn = DPMEM_ALLOC(sizeof(DPLOBBYI_REQUESTNODE)); if(!lprn) { DPF_ERR("Unable to allocate memory for request node, system message not sent"); return DPERR_OUTOFMEMORY; } // Setup the request node
lprn->dwFlags = lpgn->dwFlags; lprn->dwRequestID = this->dwCurrentRequest; lprn->dwAppRequestID = ((LPDPLMSG_GETPROPERTY)lpmsg)->dwRequestID; lprn->lpgn = lpgn;
// Add the slammed guid flag if needed
if(bSlamGuid) lprn->dwFlags |= GN_SLAMMED_GUID;
// Change the request ID in the message to our internal one (we'll
// change it back on Receive
((LPDPLMSG_GETPROPERTY)lpmsg)->dwRequestID = this->dwCurrentRequest++;
// Add the node to the list
if(this->lprnHead) this->lprnHead->lpPrev = lprn; lprn->lpNext = this->lprnHead; this->lprnHead = lprn;
return DP_OK;
} // PRV_AddNewRequestNode
#undef DPF_MODNAME
#define DPF_MODNAME "PRV_RemoveRequestNode"
void PRV_RemoveRequestNode(LPDPLOBBYI_DPLOBJECT this, LPDPLOBBYI_REQUESTNODE lprn) { // If we're the head, move it
if(lprn == this->lprnHead) this->lprnHead = lprn->lpNext;
// Fixup the previous & next pointers
if(lprn->lpPrev) lprn->lpPrev->lpNext = lprn->lpNext; if(lprn->lpNext) lprn->lpNext->lpPrev = lprn->lpPrev;
// Free the node
DPMEM_FREE(lprn);
} // PRV_RemoveRequestNode
#undef DPF_MODNAME
#define DPF_MODNAME "PRV_ForwardMessageToLobbyServer"
HRESULT PRV_ForwardMessageToLobbyServer(LPDPLOBBYI_GAMENODE lpgn, LPVOID lpBuffer, DWORD dwSize, BOOL bStandard) { LPDPLOBBYI_DPLOBJECT this; LPDPLMSG_GENERIC lpmsg = NULL; HRESULT hr; BOOL bSlamGuid = FALSE;
DPF(7, "Entering PRV_ForwardMessageToLobbyServer"); DPF(9, "Parameters: 0x%08x, 0x%08x, %lu, %lu", lpgn, lpBuffer, dwSize, bStandard);
TRY { // Validate the dplay object
hr = VALID_DPLAY_PTR( lpgn->lpDPlayObject ); if (FAILED(hr)) { DPF_ERRVAL("bad dplay ptr - hr = 0x%08lx\n",hr); return hr; }
// Validate the lobby object
this = lpgn->lpDPlayObject->lpLobbyObject; if( !VALID_DPLOBBY_PTR( this ) ) { DPF_ERR("Invalid lobby object"); return DPERR_INVALIDOBJECT; } } EXCEPT( EXCEPTION_EXECUTE_HANDLER ) { DPF_ERR( "Exception encountered validating parameters" ); return DPERR_INVALIDPARAMS; }
// If this is a property request, we need to create a request node
lpmsg = (LPDPLMSG_GENERIC)lpBuffer; if(bStandard) { // If it's a property message, we need a request node
switch(lpmsg->dwType) { case DPLSYS_GETPROPERTY: { LPDPLMSG_GETPROPERTY lpgp = lpBuffer;
// If it's a GETPROPERTY message, we need to check to see if
// the player guid is NULL. If it is, we need to
// stuff the game's Instance guid in that field
if(IsEqualGUID(&lpgp->guidPlayer, &GUID_NULL)) { // Stuff the instance guid of the game
lpgp->guidPlayer = lpgn->guidInstance; bSlamGuid = TRUE; }
// Add a request node to the pending requests list
hr = PRV_AddNewRequestNode(this, lpgn, lpmsg, bSlamGuid); if(FAILED(hr)) { DPF_ERRVAL("Unable to add request node to list, hr = 0x%08x", hr); return hr; } break; } case DPLSYS_SETPROPERTY: { LPDPLMSG_SETPROPERTY lpsp = lpBuffer; // If it's a SETPROPERTY message, we need to check to see if
// the player guid is NULL. If it is, we need to
// stuff the game's Instance guid in that field
if(IsEqualGUID(&lpsp->guidPlayer, &GUID_NULL)) { // Stuff the instance guid of the game
lpsp->guidPlayer = lpgn->guidInstance; bSlamGuid = TRUE; }
// If the request ID is zero, we don't need to swap
// the ID's or add a pending request
if(lpsp->dwRequestID != 0) { // Add a request node to the pending requests list
hr = PRV_AddNewRequestNode(this, lpgn, lpmsg, bSlamGuid); if(FAILED(hr)) { DPF_ERRVAL("Unable to add request node to list, hr = 0x%08x", hr); return hr; } } break; }
case DPLSYS_NEWSESSIONHOST: ((LPDPLMSG_NEWSESSIONHOST)lpBuffer)->guidInstance = lpgn->guidInstance; break; default: break; } }
// Call Send on the lobby object
hr = PRV_Send(this, lpgn->dpidPlayer, DPID_SERVERPLAYER, DPSEND_LOBBYSYSTEMMESSAGE, lpBuffer, dwSize); if(FAILED(hr)) { DPF_ERRVAL("Failed sending lobby message, hr = 0x%08x", hr); }
return hr;
} // PRV_ForwardMessageToLobbyServer
#undef DPF_MODNAME
#define DPF_MODNAME "PRV_InjectMessageInQueue"
HRESULT PRV_InjectMessageInQueue(LPDPLOBBYI_GAMENODE lpgn, DWORD dwFlags, LPVOID lpData, DWORD dwSize, BOOL bForward) { LPDPLOBBYI_MESSAGE lpm = NULL; LPVOID lpBuffer = NULL; HRESULT hr;
DPF(7, "Entering PRV_InjectMessageInQueue"); DPF(9, "Parameters: 0x%08x, 0x%08x, 0x%08x, %lu, %lu", lpgn, dwFlags, lpData, dwSize, bForward);
ASSERT(lpData);
// Allocate memory for the node and the data buffer
lpm = DPMEM_ALLOC(sizeof(DPLOBBYI_MESSAGE)); lpBuffer = DPMEM_ALLOC(dwSize); if((!lpm) || (!lpBuffer)) { DPF_ERR("Unable to allocate memory for system message"); if(lpm) DPMEM_FREE(lpm); if(lpBuffer) DPMEM_FREE(lpBuffer); return DPERR_OUTOFMEMORY; }
// Copy the data
memcpy(lpBuffer, lpData, dwSize);
// Before we put it in our own queue, forward it onto the lobby server
// if there is one.
if(bForward && (lpgn->dwFlags & GN_CLIENT_LAUNCHED)) { hr = PRV_ForwardMessageToLobbyServer(lpgn, lpData, dwSize, FALSE); if(FAILED(hr)) { DPF_ERRVAL("Failed forwarding system message to lobby server, hr = 0x%08x", hr); } }
// Save the data pointer & the external flags
// Note: If we're injecting this, it has to be a system message,
// so set the flag just in case we forgot elsewhere.
lpm->dwFlags = (dwFlags | DPLAD_SYSTEM); lpm->dwSize = dwSize; lpm->lpData = lpBuffer;
// Add the message to the end of the queue & increment the count
ENTER_DPLQUEUE(); lpm->lpPrev = lpgn->MessageHead.lpPrev; lpgn->MessageHead.lpPrev->lpNext = lpm; lpgn->MessageHead.lpPrev = lpm; lpm->lpNext = &lpgn->MessageHead;
lpgn->dwMessageCount++; LEAVE_DPLQUEUE();
// Kick the event handle
if(lpgn->hDupReceiveEvent) { SetEvent(lpgn->hDupReceiveEvent); }
return DP_OK;
} // PRV_InjectMessageInQueue
#undef DPF_MODNAME
#define DPF_MODNAME "PRV_ReadClientData"
HRESULT PRV_ReadClientData(LPDPLOBBYI_GAMENODE lpgn, LPDWORD lpdwFlags, LPVOID lpData, LPDWORD lpdwDataSize) { LPDPLOBBYI_BUFFERCONTROL lpControl = NULL; LPDPLOBBYI_MESSAGEHEADER lpHeader = NULL; DWORD dwSize = 0; DWORD_PTR dwSizeToEnd = 0; HANDLE hMutex = NULL; LPBYTE lpTemp = NULL; LPBYTE lpEnd = NULL; HRESULT hr = DP_OK;
DWORD dwReadOffset; DWORD dwBufferSize;
DPF(7, "Entering PRV_ReadClientData"); DPF(9, "Parameters: 0x%08x, 0x%08x, 0x%08x, 0x%08x", lpgn, lpdwFlags, lpData, lpdwDataSize);
// Make sure we have a valid shared memory buffer
// Note: Take the GameNode lock so that nobody changes the flags
// for the buffers, or the buffers themselves out from under us.
ENTER_DPLGAMENODE(); if(!(lpgn->dwFlags & GN_SHARED_MEMORY_AVAILABLE)) { hr = PRV_SetupAllSharedMemory(lpgn); if(FAILED(hr)) { LEAVE_DPLGAMENODE(); DPF(2, "Unable to access App Data memory"); return hr; } } LEAVE_DPLGAMENODE();
// Grab the mutex
// REVIEW!!!! -- Is there anything that might cause this wait to hang????
hMutex = (lpgn->dwFlags & GN_LOBBY_CLIENT) ? (lpgn->hGameWriteMutex) : (lpgn->hLobbyWriteMutex); WaitForSingleObject(hMutex, INFINITE);
// Get a pointer to our control structure
lpControl = (LPDPLOBBYI_BUFFERCONTROL)((lpgn->dwFlags & GN_LOBBY_CLIENT) ? (lpgn->lpGameWriteBuffer) : (lpgn->lpLobbyWriteBuffer));
// Make sure there are any messages in the buffer
if(!lpControl->dwMessages) { DPF(8, "No messages in shared buffer"); hr = DPERR_NOMESSAGES; goto EXIT_READ_CLIENT_DATA; }
// SECURITY need to snapshot these values so hackers
// can't change in the middle of accessing shared object.
dwReadOffset=lpControl->dwReadOffset; dwBufferSize=lpControl->dwBufferSize;
// Make sure there is enough space for the message
lpHeader = (LPDPLOBBYI_MESSAGEHEADER)((LPBYTE)lpControl + dwReadOffset);
if(lpControl->dwReadOffset >= MAX_APPDATABUFFERSIZE) { DPF(4,"SECURITY WARN: local application hacking shared memory, lobby connection broken"); hr=DPERR_NOMESSAGES; goto EXIT_READ_CLIENT_DATA; } dwSize = lpHeader->dwSize;
if(dwSize > MAX_APPDATABUFFERSIZE-(sizeof(DPLOBBYI_BUFFERCONTROL)+sizeof(DPLOBBYI_MESSAGEHEADER))){ DPF(4,"SECURITY WARN: lobby message size %d is too large",lpHeader->dwSize); hr=DPERR_NOMESSAGES; goto EXIT_READ_CLIENT_DATA; }
// Set the output data size (even if we fail, we want to return it)
if(lpdwDataSize) *lpdwDataSize = dwSize;
if((!lpData) || (dwSize > *lpdwDataSize)) { DPF(8, "Message buffer is too small, must be at least %d bytes", dwSize); hr = DPERR_BUFFERTOOSMALL; goto EXIT_READ_CLIENT_DATA; }
// Set the output flags
if(lpdwFlags) *lpdwFlags = lpHeader->dwFlags;
if(dwBufferSize != MAX_APPDATABUFFERSIZE) // note, makes storing this value in header redundant.
{ DPF(4, "SECURITY WARN: shared app memory has been tampered with"); hr = DPERR_NOMESSAGES; goto EXIT_READ_CLIENT_DATA; }
// Now check and see if we are going to wrap. If we are, some of the message
// will be at the end of the buffer, some will be at the beginning.
lpTemp = (LPBYTE)(++lpHeader) + dwSize; if(lpTemp > ((LPBYTE)lpControl + dwBufferSize)) { // Figure out where we need to wrap
dwSizeToEnd = ((LPBYTE)lpControl + dwBufferSize) - (LPBYTE)(lpHeader);
if(!dwSizeToEnd) { // We are at the end, so the whole message must be at the
// beginning of the buffer
lpTemp = (LPBYTE)lpControl + sizeof(DPLOBBYI_BUFFERCONTROL); memcpy(lpData, lpTemp, dwSize);
// Move the read cursor
dwReadOffset = sizeof(DPLOBBYI_BUFFERCONTROL) + dwSize; } else { // Copy the first part of the data
lpTemp = (LPBYTE)lpHeader; memcpy(lpData, lpTemp, (DWORD)dwSizeToEnd);
// Move the read cursor and copy the rest
lpTemp = (LPBYTE)lpControl + sizeof(DPLOBBYI_BUFFERCONTROL); memcpy(((LPBYTE)lpData + dwSizeToEnd), lpTemp, (DWORD)(dwSize - dwSizeToEnd));
// Move the read pointer
dwReadOffset = (DWORD)(sizeof(DPLOBBYI_BUFFERCONTROL) + (dwSize - dwSizeToEnd)); } } else { // We don't have to wrap (cool).
lpTemp = (LPBYTE)lpHeader; memcpy(lpData, lpTemp, dwSize);
// Move the read pointer. If there are less than 8 bytes left in the
// buffer, we should move the read pointer to the beginning. We need
// to add however many bytes we skip (at the end) back into our free
// buffer memory counter.
lpTemp += dwSize; lpEnd = (LPBYTE)lpControl + dwBufferSize; if(lpTemp > (lpEnd - sizeof(DPLOBBYI_MESSAGEHEADER))) { // Move the read cursor to the beginning
dwReadOffset = sizeof(DPLOBBYI_BUFFERCONTROL);
// Add the number of bytes to the free buffer total
lpControl->dwBufferLeft += (DWORD)(lpEnd - lpTemp); } else dwReadOffset += (DWORD)(dwSize + sizeof(DPLOBBYI_MESSAGEHEADER)); }
lpControl->dwReadOffset=dwReadOffset;
// Increment the amount of free space left and decrement the message count
lpControl->dwBufferLeft += (dwSize + sizeof(DPLOBBYI_MESSAGEHEADER)); lpControl->dwMessages--;
// Fall through
EXIT_READ_CLIENT_DATA:
// Release the mutex
ReleaseMutex(hMutex);
return hr;
} // PRV_ReadClientData
#undef DPF_MODNAME
#define DPF_MODNAME "PRV_ReceiveClientNotification"
DWORD WINAPI PRV_ReceiveClientNotification(LPVOID lpParam) { LPDPLOBBYI_GAMENODE lpgn = (LPDPLOBBYI_GAMENODE)lpParam; LPDPLOBBYI_MESSAGE lpm = NULL; LPDPLOBBYI_BUFFERCONTROL lpControl = NULL; LPDPLMSG_GENERIC lpmsg = NULL; HRESULT hr; HANDLE hEvents[4]; LPVOID lpBuffer = NULL; DWORD dwFlags; DWORD dwSize; DWORD dwReturn; BOOL bForward;
DWORD dwWait=INFINITE; DWORD nWait=2;
DPF(7, "Entering PRV_ReceiveClientNotification"); DPF(9, "Parameters: 0x%08x", lpParam);
// Make sure we have a valid shared memory buffer
// Note: Take the GameNode lock so that nobody changes the flags
// for the buffers, or the buffers themselves out from under us.
ENTER_DPLGAMENODE(); if(!(lpgn->dwFlags & GN_SHARED_MEMORY_AVAILABLE)) { BOOL bGameCreate=FALSE;
DPF(2, "NOTE: ReceiveClientNotification thread starting without shared memory set up. Setting up now."); // HACK!!!! -- SetLobbyMessageReceiveEvent may get called from
// the game without having been lobbied yet. If that is the case,
// we need to create the shared memory buffer. If we don't do
// that, we may miss messages.
if(!(lpgn->dwFlags & GN_LOBBY_CLIENT)) { // Fake the setup routine by setting the lobby client flag
lpgn->dwFlags |= GN_LOBBY_CLIENT;
// Set our flag
bGameCreate = TRUE; }
hr = PRV_SetupAllSharedMemory(lpgn);
// HACK!!!! -- Reset the settings we changed to fake the setup routines
if(bGameCreate) { lpgn->dwFlags &= (~GN_LOBBY_CLIENT); }
//hr = PRV_SetupAllSharedMemory(lpgn);
if(FAILED(hr)) { LEAVE_DPLGAMENODE(); DPF(2, "Unable to access App Data memory"); return 0L; } } LEAVE_DPLGAMENODE();
// Setup the two events -- one receive event, one kill event
hEvents[0] = ((lpgn->dwFlags & GN_LOBBY_CLIENT) ? (lpgn->hGameWriteEvent) : (lpgn->hLobbyWriteEvent)); hEvents[1] = lpgn->hKillReceiveThreadEvent;
if(lpgn->hLobbyClientProcess){ nWait=3; hEvents[2] = lpgn->hLobbyClientProcess; } else { hEvents[2] = INVALID_HANDLE_VALUE; if(!(lpgn->dwFlags & GN_LOBBY_CLIENT)){ dwWait = 5000; } } // This extra handle is here because of a Windows 95 bug. Windows
// will occasionally miss when it walks the handle table, causing
// my thread to wait on the wrong handles. By putting a guaranteed
// invalid handle at the end of our array, the kernel will do a
// forced re-walk of the handle table and find the correct handles.
hEvents[3] = INVALID_HANDLE_VALUE;
// Make sure we have a valid event
if(!hEvents[0] || !hEvents[1]) { DPF(2, "Either the Write Event or the Kill Event is NULL and it shouldn't be!"); ExitThread(0L); return 0; }
// If we are the game, we should check the buffer to see if any messages
// already exist in the shared buffer.
if(!(lpgn->dwFlags & GN_LOBBY_CLIENT)) { lpControl = (LPDPLOBBYI_BUFFERCONTROL)lpgn->lpLobbyWriteBuffer; // If there are any messages, kick our event so that our receive
// loop will immediately put the messages in the queue
if(lpControl->dwMessages) SetEvent(hEvents[0]); }
// Wait for the event notification
while(1) { // Sleep until something shows up
dwReturn = WaitForMultipleObjects(nWait, (HANDLE *)hEvents, FALSE, dwWait);
if(dwReturn == WAIT_TIMEOUT){ ASSERT(!(lpgn->dwFlags & GN_LOBBY_CLIENT)); if(lpgn->hLobbyClientProcess){ DPF(9,"Got the lobby client process handle, adding to wait list\n"); hEvents[2] = lpgn->hLobbyClientProcess; nWait = 3; dwWait=INFINITE; } continue; }
if(nWait==3 && dwReturn == WAIT_OBJECT_0 + 2){ // send the dead lobby client message
DPLMSG_SYSTEMMESSAGE msg;
if(lpgn->dwLobbyClientProcessID){ memset(&msg, 0, sizeof(DPLMSG_SYSTEMMESSAGE)); msg.dwType = DPLSYS_LOBBYCLIENTRELEASE; msg.guidInstance = lpgn->guidInstance; lpgn->dwLobbyClientProcessID = 0; hr = PRV_InjectMessageInQueue(lpgn, DPLMSG_SYSTEM | DPLMSG_STANDARD, &msg, sizeof(DPLMSG_SYSTEMMESSAGE), FALSE);
} nWait=2; hEvents[2]=INVALID_HANDLE_VALUE; continue; }
// If the return value was anything bug the receive event,
// kill the thread
if(dwReturn != WAIT_OBJECT_0) { if(dwReturn == WAIT_FAILED) { // This is a Windows 95 bug -- We may have gotten
// kicked for no reason. If that was the case, we
// still have valid handles (we think), the OS
// just goofed up. So, validate the handle and if
// they are valid, just return to waiting. See
// bug #3340 for a better explanation.
if(ERROR_INVALID_HANDLE == GetLastError()) { if(!OS_IsValidHandle(hEvents[0])) break; if(!OS_IsValidHandle(hEvents[1])) break; continue; } break; } else { // It is either our kill event, or something we don't
// understand or expect. In this case, let's exit.
break; } }
while(1) { // First, call PRV_ReadClientData to get the size of the data
hr = PRV_ReadClientData(lpgn, NULL, NULL, &dwSize); // If there are no messages, end the while loop
if(hr == DPERR_NOMESSAGES) break;
// Otherwise, we should get the BUFFERTOOSMALL case
if(hr != DPERR_BUFFERTOOSMALL) { // We should never have a problem here
DPF_ERRVAL("Recieved an unexpected error reading from shared buffer, hr = 0x%08x", hr); ASSERT(FALSE); // Might as well keep trying
break; } // Allocate memory for the node and the data buffer
lpm = DPMEM_ALLOC(sizeof(DPLOBBYI_MESSAGE)); lpBuffer = DPMEM_ALLOC(dwSize); if((!lpm) || (!lpBuffer)) { DPF_ERR("Unable to allocate memory for message"); ASSERT(FALSE); // Might as well keep trying
break; }
// Copy the data into our buffer
hr = PRV_ReadClientData(lpgn, &dwFlags, lpBuffer, &dwSize); if(FAILED(hr)) { DPF_ERRVAL("Error reading shared buffer, message not read, hr = 0x%08x", hr); ASSERT(FALSE); DPMEM_FREE(lpm); DPMEM_FREE(lpBuffer); // Might as well keep trying
break; }
// Clear our foward flag
bForward = FALSE; // If we are a dplay lobby client, we need to forward the message
// onto the lobby server using the IDP3 interface. If we're not,
// then just put the message in the receive queue.
if(lpgn->dwFlags & GN_CLIENT_LAUNCHED) { // Foward the message
hr = PRV_ForwardMessageToLobbyServer(lpgn, lpBuffer, dwSize, ((dwFlags & DPLMSG_STANDARD) ? TRUE : FALSE)); if(FAILED(hr)) { DPF_ERRVAL("Unable to send lobby system message, hr = 0x%08x", hr); }
// Set the forwarded flag
bForward = TRUE; }
// Check for an App Terminated message. If we get one off the wire,
// we need to shut down our ClientTerminateMonitor thread, signal
// this thread (the receive thread to shut down, and mark the game
// node as dead. This will keep us from sending or receiving any
// more messages from the now dead game. (This message will only
// ever be received by a lobby client).
lpmsg = (LPDPLMSG_GENERIC)lpBuffer; if(lpmsg->dwType == DPLSYS_APPTERMINATED) { // Kick the TerminateMonitor thread with it's kill event
SetEvent(lpgn->hKillTermThreadEvent);
// Set this thread's kill event (so that when we get done
// reading messages out of the shared buffer, we go away)
SetEvent(lpgn->hKillReceiveThreadEvent);
// Mark the GAMENODE as dead, but don't remove it since we know
// there will still messages in the queue.
lpgn->dwFlags |= GN_DEAD_GAME_NODE; }
// If it's one of our DX3 messages, we need to put it in the queue
// otherwise if we already forwarded it, we can free it. NOTE: All
// DX3 lobby system messages had a value between 0 and
// DPLSYS_APPTERMINATED (0x04).
if( (!bForward) || (lpmsg->dwType <= DPLSYS_APPTERMINATED)) {
if (lpmsg->dwType == DPLSYS_LOBBYCLIENTRELEASE) { if(lpgn->dwLobbyClientProcessID){ lpgn->dwLobbyClientProcessID = 0; } else { goto no_queue; } }
// Save the data pointer & the external flags
lpm->dwFlags = dwFlags & (~DPLOBBYPR_INTERNALMESSAGEFLAGS); lpm->dwSize = dwSize; lpm->lpData = lpBuffer;
// Add the message to the end of the queue & increment the count
ENTER_DPLQUEUE(); lpm->lpPrev = lpgn->MessageHead.lpPrev; lpgn->MessageHead.lpPrev->lpNext = lpm; lpgn->MessageHead.lpPrev = lpm; lpm->lpNext = &lpgn->MessageHead;
lpgn->dwMessageCount++; LEAVE_DPLQUEUE();
// NOTE: There is a potential thread problem here, but we are going
// to ignore it for now. It is possible for another thread to be
// going through the SetAppData code which changes this event handle.
// The problem is if they change it after this IF statement, but
// before we call SetEvent. However, the SetEvent call will either
// succeed on the new handle, or return an error if the handle is
// changed to NULL. In either case, no harm, no foul -- we don't care.
if(!lpgn->hDupReceiveEvent) { DPF(8, "The Receive Event handle is NULL!"); continue; }
SetEvent(lpgn->hDupReceiveEvent); } else { no_queue: // Free the buffers
DPMEM_FREE(lpm); DPMEM_FREE(lpBuffer); } } }
DPF(8, "Lobby Receive Thread is going away!!!!!"); ExitThread(0L);
return 0L; // avoid warning.
} // PRV_ReceiveClientNotification
#undef DPF_MODNAME
#define DPF_MODNAME "PRV_RemoveNodeFromQueue"
void PRV_RemoveNodeFromQueue(LPDPLOBBYI_GAMENODE lpgn, LPDPLOBBYI_MESSAGE lpm) { DPF(7, "Entering PRV_RemoveNodeFromQueue"); DPF(9, "Parameters: 0x%08x, 0x%08x", lpgn, lpm);
ASSERT(lpgn); ASSERT(lpm);
// Delete the message from the queue & decrement the count
lpm->lpPrev->lpNext = lpm->lpNext; lpm->lpNext->lpPrev = lpm->lpPrev;
lpgn->dwMessageCount--;
// Free the memory for the message node
DPMEM_FREE(lpm->lpData); DPMEM_FREE(lpm);
} // PRV_RemoveNodeFromQueue
#undef DPF_MODNAME
#define DPF_MODNAME "PRV_CleanUpQueue"
void PRV_CleanUpQueue(LPDPLOBBYI_GAMENODE lpgn) { LPDPLOBBYI_MESSAGE lpm, lpmNext;
DPF(7, "Entering PRV_CleanUpQueue"); DPF(9, "Parameters: 0x%08x", lpgn);
ASSERT(lpgn);
lpm = lpgn->MessageHead.lpNext; while(lpm != &lpgn->MessageHead) { // Save the next pointer
lpmNext = lpm->lpNext;
// Remove the node
PRV_RemoveNodeFromQueue(lpgn, lpm);
// Move to the next node
lpm = lpmNext; }
} // PRV_CleanUpQueue
#undef DPF_MODNAME
#define DPF_MODNAME "PRV_KillThread"
void PRV_KillThread(HANDLE hThread, HANDLE hEvent) {
DPF(7, "Entering PRV_KillThread"); DPF(9, "Parameters: 0x%08x, 0x%08x", hThread, hEvent); ASSERT(hThread); ASSERT(hEvent); // Signal the thread to die.
SetEvent(hEvent);
// Wait until the thread terminates, if it doesn't something is
// wrong, so we better fix it.
DPF(8, "Starting to wait for a thread to exit -- hThread = 0x%08x, hEvent = 0x%08x", hThread, hEvent); WaitForSingleObject(hThread, INFINITE);
// Now close both handles
CloseHandle(hThread); CloseHandle(hEvent);
} // PRV_KillThread
#undef DPF_MODNAME
#define DPF_MODNAME "PRV_FreeGameNode"
HRESULT PRV_FreeGameNode(LPDPLOBBYI_GAMENODE lpgn) { LPDPLOBBYI_BUFFERCONTROL lpControl = NULL;
DPF(7, "Entering PRV_FreeGameNode"); DPF(9, "Parameters: 0x%08x", lpgn);
// FIRST: Take care of the connection settings data buffer
// Unmap & release the shared memory
if(lpgn->lpConnectDataBuffer) UnmapViewOfFile(lpgn->lpConnectDataBuffer);
if(lpgn->hConnectDataFile) CloseHandle(lpgn->hConnectDataFile);
if(lpgn->hConnectDataMutex) CloseHandle(lpgn->hConnectDataMutex);
// NEXT: Take care of the App Data Events & Buffers
// Kill the Receive Thread
if(lpgn->hReceiveThread) { PRV_KillThread(lpgn->hReceiveThread, lpgn->hKillReceiveThreadEvent); CloseHandle(lpgn->hDupReceiveEvent); }
// Close the event handles
if(lpgn->hLobbyWriteEvent) CloseHandle(lpgn->hLobbyWriteEvent);
if(lpgn->hGameWriteEvent) CloseHandle(lpgn->hGameWriteEvent);
// Kill the Terminate Monitor Thread
if(lpgn->hTerminateThread) { PRV_KillThread(lpgn->hTerminateThread, lpgn->hKillTermThreadEvent); }
// Clear the flags since we are no longer going to be active
if(lpgn->lpGameWriteBuffer) { lpControl = (LPDPLOBBYI_BUFFERCONTROL)lpgn->lpGameWriteBuffer; lpControl->dwFlags &= ~((lpgn->dwFlags & GN_LOBBY_CLIENT) ? BC_LOBBY_ACTIVE : BC_GAME_ACTIVE); }
if(lpgn->lpLobbyWriteBuffer) { lpControl = (LPDPLOBBYI_BUFFERCONTROL)lpgn->lpLobbyWriteBuffer; lpControl->dwFlags &= ~((lpgn->dwFlags & GN_LOBBY_CLIENT) ? BC_LOBBY_ACTIVE : BC_GAME_ACTIVE); }
// Unmap & release the Game Write memory
if(lpgn->lpGameWriteBuffer) UnmapViewOfFile(lpgn->lpGameWriteBuffer);
if(lpgn->hGameWriteFile) CloseHandle(lpgn->hGameWriteFile);
if(lpgn->hGameWriteMutex) CloseHandle(lpgn->hGameWriteMutex);
// Unmap & release the Lobby Write memory
if(lpgn->lpLobbyWriteBuffer) UnmapViewOfFile(lpgn->lpLobbyWriteBuffer);
if(lpgn->hLobbyWriteFile) CloseHandle(lpgn->hLobbyWriteFile);
if(lpgn->hLobbyWriteMutex) CloseHandle(lpgn->hLobbyWriteMutex);
// Clean up the message queue
PRV_CleanUpQueue(lpgn);
// Close the process handle we have for the game
if(lpgn->hGameProcess) CloseHandle(lpgn->hGameProcess);
if(lpgn->hLobbyClientProcess) CloseHandle(lpgn->hLobbyClientProcess); // Free the game node structure
DPMEM_FREE(lpgn);
return DP_OK;
} // PRV_FreeGameNode
#undef DPF_MODNAME
#define DPF_MODNAME "PRV_DuplicateHandle"
HANDLE PRV_DuplicateHandle(HANDLE hSource) { HANDLE hProcess = NULL; HANDLE hTarget = NULL; DWORD dwProcessID; DWORD dwError;
DPF(7, "Entering PRV_DuplicateHandle"); DPF(9, "Parameters: 0x%08x", hSource);
dwProcessID = GetCurrentProcessId(); hProcess = OpenProcess(PROCESS_DUP_HANDLE, FALSE, dwProcessID); if(!DuplicateHandle(hProcess, hSource, hProcess, &hTarget, 0L, FALSE, DUPLICATE_SAME_ACCESS)) { dwError = GetLastError(); CloseHandle(hProcess); return NULL; }
CloseHandle(hProcess); return hTarget;
} // PRV_DuplicateHandle
#undef DPF_MODNAME
#define DPF_MODNAME "DPL_SetLobbyMessageEvent"
HRESULT DPLAPI DPL_SetLobbyMessageEvent(LPDIRECTPLAYLOBBY lpDPL, DWORD dwFlags, DWORD dwGameID, HANDLE hReceiveEvent) { LPDPLOBBYI_DPLOBJECT this; LPDPLOBBYI_GAMENODE lpgn = NULL; LPVOID lpBuffer = NULL; HANDLE hReceiveThread = NULL; HANDLE hDupReceiveEvent = NULL; HRESULT hr; BOOL bCreated = FALSE; BOOL bLobbyClient = TRUE; BOOL bNewEvent = FALSE;
DPF(7, "Entering DPL_SetLobbyMessageEvent"); DPF(9, "Parameters: 0x%08x, 0x%08x, 0x%08x, 0x%08x", lpDPL, dwFlags, dwGameID, hReceiveEvent);
ENTER_DPLOBBY();
TRY { if( !VALID_DPLOBBY_INTERFACE( lpDPL )) { LEAVE_DPLOBBY(); return DPERR_INVALIDINTERFACE; }
this = DPLOBJECT_FROM_INTERFACE(lpDPL); if( !VALID_DPLOBBY_PTR( this ) ) { LEAVE_DPLOBBY(); return DPERR_INVALIDOBJECT; }
// Validate the handle
if(hReceiveEvent) { if(!OS_IsValidHandle(hReceiveEvent)) { LEAVE_DPLOBBY(); DPF_ERR("Invalid hReceiveEvent handle"); return DPERR_INVALIDPARAMS; } }
// We haven't defined any flags for this release
if( (dwFlags) ) { LEAVE_DPLOBBY(); return DPERR_INVALIDPARAMS; } }
EXCEPT( EXCEPTION_EXECUTE_HANDLER ) { LEAVE_DPLOBBY(); DPF_ERR( "Exception encountered validating parameters" ); return DPERR_INVALIDPARAMS; }
// If the dwGameID is zero, we assume we are a game. In that case,
// the GameNode we are looking for should have our own ProcessID.
if(!dwGameID) { dwGameID = GetCurrentProcessId(); bLobbyClient = FALSE; }
ENTER_DPLGAMENODE(); lpgn = PRV_GetGameNode(this->lpgnHead, dwGameID);
// If the event handle is null, kill our duplicate handle
if(!hReceiveEvent) { if(!lpgn) { DPF(5, "Unable to find GameNode -- Invalid dwGameID!"); LEAVE_DPLGAMENODE(); LEAVE_DPLOBBY(); return DPERR_GENERIC; }
CloseHandle(lpgn->hDupReceiveEvent); lpgn->hDupReceiveEvent = NULL; LEAVE_DPLGAMENODE(); LEAVE_DPLOBBY(); return DP_OK; }
// If a GameNode structure exists for this process, we must be trying
// to replace the event handle, so kill the old event handle, OTHERWISE
// we need to allocate a new GameNode for this process
if(lpgn) { if(lpgn->hDupReceiveEvent) { CloseHandle(lpgn->hDupReceiveEvent); lpgn->hDupReceiveEvent = NULL; } } else { // If we are a game, go ahead and create the node
if(!bLobbyClient) { hr = PRV_AddNewGameNode(this, &lpgn, dwGameID, NULL, bLobbyClient,NULL); if(FAILED(hr)) { LEAVE_DPLGAMENODE(); LEAVE_DPLOBBY(); return hr; } } else { LEAVE_DPLGAMENODE(); LEAVE_DPLOBBY(); return DPERR_INVALIDPARAMS; }
}
// Duplicate the caller's handle in case they free it without calling
// us first to remove the Receive thread.
hDupReceiveEvent = PRV_DuplicateHandle(hReceiveEvent); if(!hDupReceiveEvent) { DPF(2, "Unable to duplicate ReceiveEvent handle"); LEAVE_DPLGAMENODE(); LEAVE_DPLOBBY(); return DPERR_OUTOFMEMORY; }
if(!lpgn->hDupReceiveEvent) bNewEvent = TRUE; lpgn->hDupReceiveEvent = hDupReceiveEvent;
// Check to see if the Receive thread already exists. If it
// doesn't, create it. Otherwise, leave it alone.
if(!(lpgn->hReceiveThread)) { hr = PRV_StartReceiveThread(lpgn); if(FAILED(hr)) { if(lpgn->hDupReceiveEvent) { CloseHandle(lpgn->hDupReceiveEvent); lpgn->hDupReceiveEvent = NULL; }
LEAVE_DPLGAMENODE(); LEAVE_DPLOBBY(); return hr; } }
// If this is a new event, check to see if there are any messages in the
// queue. If there are, kick the event so the user knows they are there.
if(bNewEvent && lpgn->dwMessageCount) SetEvent(hDupReceiveEvent);
LEAVE_DPLGAMENODE(); LEAVE_DPLOBBY(); return DP_OK;
} // DPL_SetLobbyMessageEvent
#undef DPF_MODNAME
#define DPF_MODNAME "DPL_SendLobbyMessage"
HRESULT DPLAPI DPL_SendLobbyMessage(LPDIRECTPLAYLOBBY lpDPL, DWORD dwFlags, DWORD dwGameID, LPVOID lpData, DWORD dwSize) { LPDPLOBBYI_DPLOBJECT this; LPDPLOBBYI_GAMENODE lpgn = NULL; LPDPLMSG_GENERIC lpmsg = NULL; HRESULT hr = DP_OK; BOOL bLobbyClient = TRUE; BOOL bStandard = FALSE;
DPF(7, "Entering DPL_SendLobbyMessage"); DPF(9, "Parameters: 0x%08x, 0x%08x, 0x%08x, 0x%08x, %lu", lpDPL, dwFlags, dwGameID, lpData, dwSize);
ENTER_DPLOBBY();
TRY { if( !VALID_DPLOBBY_INTERFACE( lpDPL )) { LEAVE_DPLOBBY(); return DPERR_INVALIDINTERFACE; }
this = DPLOBJECT_FROM_INTERFACE(lpDPL); if( !VALID_DPLOBBY_PTR( this ) ) { LEAVE_DPLOBBY(); return DPERR_INVALIDOBJECT; }
if( !VALID_READ_PTR( lpData, dwSize ) ) { LEAVE_DPLOBBY(); return DPERR_INVALIDPARAMS; }
// Check for valid flags
if( !VALID_SENDLOBBYMESSAGE_FLAGS(dwFlags)) { LEAVE_DPLOBBY(); return DPERR_INVALIDFLAGS; }
// If it's of the system message format, validate the dwType
if( dwFlags & DPLMSG_STANDARD ) { // Mark this as a standard message
bStandard = TRUE; // Make sure the message is big enough to read
if(! VALID_READ_PTR( lpData, sizeof(DPLMSG_GENERIC)) ) { LEAVE_DPLOBBY(); DPF_ERR("Invalid message buffer"); return DPERR_INVALIDPARAMS; } // Make sure it's one we support
lpmsg = (LPDPLMSG_GENERIC)lpData; switch(lpmsg->dwType) { case DPLSYS_GETPROPERTY: case DPLSYS_SETPROPERTY: break; default: DPF_ERR("The dwType of the message is invalid for a legal standard lobby message"); LEAVE_DPLOBBY(); return DPERR_INVALIDPARAMS; } } }
EXCEPT( EXCEPTION_EXECUTE_HANDLER ) { LEAVE_DPLOBBY(); DPF_ERR( "Exception encountered validating parameters" ); return DPERR_INVALIDPARAMS; }
// If a GameID was passed in, use it to find the correct GameNode. If
// one wasn't passed in, assume we are the game and use our ProcessID.
if(!dwGameID) { dwGameID = GetCurrentProcessId(); bLobbyClient = FALSE; }
// Now find the correct game node. If we don't find it, assume we
// have an invalid ID and error out.
lpgn = PRV_GetGameNode(this->lpgnHead, dwGameID); if(!lpgn) { LEAVE_DPLOBBY(); DPF_ERR("Invalid dwGameID"); return DPERR_INVALIDPARAMS; }
// If we are self-lobbied, we need to send the message onto the lobby
// using the IDP3 interface that we are communicating with the lobby on
// If not, we need to put it in the shared buffer and let the lobby
// client deal with it.
if(lpgn->dwFlags & GN_SELF_LOBBIED) { // Drop the lobby lock so we can call PRV_Send
LEAVE_DPLOBBY(); // Foward the message
hr = PRV_ForwardMessageToLobbyServer(lpgn, lpData, dwSize, bStandard); // Take the lock back
ENTER_DPLOBBY(); if(FAILED(hr)) { DPF_ERRVAL("Unable to send lobby system message, hr = 0x%08x", hr); } } else { // Write the data to our shared memory
hr = PRV_WriteClientData(lpgn, dwFlags, lpData, dwSize); }
LEAVE_DPLOBBY(); return hr;
} // DPL_SendLobbyMessage
#undef DPF_MODNAME
#define DPF_MODNAME "PRV_GetMessageFromQueue"
HRESULT PRV_GetMessageFromQueue(LPDPLOBBYI_GAMENODE lpgn, LPDWORD lpdwFlags, LPVOID lpData, LPDWORD lpdwSize) { LPDPLOBBYI_MESSAGE lpm;
DPF(7, "Entering PRV_GetMessageFromQueue"); DPF(9, "Parameters: 0x%08x, 0x%08x, 0x%08x, 0x%08x", lpgn, lpdwFlags, lpData, lpdwSize);
ENTER_DPLQUEUE();
// Get the top message in the queue
lpm = lpgn->MessageHead.lpNext;
// Make sure we have a message
if((!lpgn->dwMessageCount) || (lpm == &lpgn->MessageHead)) { LEAVE_DPLQUEUE(); return DPERR_NOMESSAGES; }
// If the lpData pointer is NULL, just return the size
if(!lpData) { *lpdwSize = lpm->dwSize; LEAVE_DPLQUEUE(); return DPERR_BUFFERTOOSMALL; }
// Otherwise, check the remaining output parameters
if( !VALIDEX_CODE_PTR( lpData ) ) { LEAVE_DPLQUEUE(); return DPERR_INVALIDPARAMS; }
if( !VALID_DWORD_PTR( lpdwFlags ) ) { LEAVE_DPLQUEUE(); return DPERR_INVALIDPARAMS; }
// Copy the message
if(*lpdwSize < lpm->dwSize) { *lpdwSize = lpm->dwSize; LEAVE_DPLQUEUE(); return DPERR_BUFFERTOOSMALL; } else memcpy(lpData, lpm->lpData, lpm->dwSize);
// Set the other output parameters
*lpdwSize = lpm->dwSize; *lpdwFlags = lpm->dwFlags;
// Delete the message from the queue & decrement the count
PRV_RemoveNodeFromQueue(lpgn, lpm);
// Check and see if our GAMENODE is dead. If it is, and if the message
// count has gone to zero, then free the GAMENODE structure.
if((!lpgn->dwMessageCount) && IS_GAME_DEAD(lpgn)) PRV_RemoveGameNodeFromList(lpgn);
LEAVE_DPLQUEUE(); return DP_OK;
} // PRV_GetMessageFromQueue
#undef DPF_MODNAME
#define DPF_MODNAME "DPL_ReceiveLobbyMessage"
HRESULT DPLAPI DPL_ReceiveLobbyMessage(LPDIRECTPLAYLOBBY lpDPL, DWORD dwFlags, DWORD dwGameID, LPDWORD lpdwMessageFlags, LPVOID lpData, LPDWORD lpdwDataLength) { LPDPLOBBYI_DPLOBJECT this; LPDPLOBBYI_GAMENODE lpgn = NULL; HRESULT hr = DP_OK; BOOL bLobbyClient = TRUE;
DPF(7, "Entering DPL_ReceiveLobbyMessage"); DPF(9, "Parameters: 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x", lpDPL, dwFlags, dwGameID, lpdwMessageFlags, lpData, lpdwDataLength);
ENTER_DPLOBBY();
TRY { if( !VALID_DPLOBBY_INTERFACE( lpDPL )) { LEAVE_DPLOBBY(); return DPERR_INVALIDINTERFACE; }
this = DPLOBJECT_FROM_INTERFACE(lpDPL); if( !VALID_DPLOBBY_PTR( this ) ) { LEAVE_DPLOBBY(); return DPERR_INVALIDOBJECT; }
if( !VALID_DWORD_PTR( lpdwDataLength ) ) { LEAVE_DPLOBBY(); return DPERR_INVALIDPARAMS; }
// We haven't defined any flags for this release
if( (dwFlags) ) { LEAVE_DPLOBBY(); return DPERR_INVALIDFLAGS; } }
EXCEPT( EXCEPTION_EXECUTE_HANDLER ) { LEAVE_DPLOBBY(); DPF_ERR( "Exception encountered validating parameters" ); return DPERR_INVALIDPARAMS; }
// If a GameID was passed in, use it to find the correct GameNode. If
// one wasn't passed in, assume we are the game and use our ProcessID.
if(!dwGameID) { dwGameID = GetCurrentProcessId(); bLobbyClient = FALSE; }
// Now find the correct game node. If we don't find it, assume we
// have an invalid ID and error out.
lpgn = PRV_GetGameNode(this->lpgnHead, dwGameID); if(!lpgn) { DPF_ERR("Invalid dwGameID"); hr = DPERR_INVALIDPARAMS; goto EXIT_RECEIVE_LOBBY_MESSAGE; }
// Read the data from shared memory
hr = PRV_GetMessageFromQueue(lpgn, lpdwMessageFlags, lpData, lpdwDataLength);
// REVIEW!!!! -- Do we need to send this to the lobby server as part of this API????
EXIT_RECEIVE_LOBBY_MESSAGE:
LEAVE_DPLOBBY(); return hr;
} // DPL_ReceiveLobbyMessage
#undef DPF_MODNAME
#define DPF_MODNAME "PRV_WriteConnectionSettings"
HRESULT PRV_WriteConnectionSettings(LPDPLOBBYI_GAMENODE lpgn, LPDPLCONNECTION lpConn, BOOL bOverrideWaitMode) { HRESULT hr; DWORD dwSize; BOOL bGameCreate = FALSE; LPBYTE lpConnBuffer = NULL; LPDPLOBBYI_CONNCONTROL lpConnControl = NULL;
DPF(7, "Entering PRV_WriteConnectionSettings"); DPF(9, "Parameters: 0x%08x, 0x%08x, %lu", lpgn, lpConn, bOverrideWaitMode);
ENTER_DPLGAMENODE();
// Make sure we have a valid shared memory buffer
// Note: Take the GameNode lock so that nobody changes the flags
// for the buffers, or the buffers themselves out from under us.
if(!(lpgn->dwFlags & GN_SHARED_MEMORY_AVAILABLE)) { // HACK!!!! -- SetConnectionSettings may get called from the game
// without having been lobbied. If that is the case, we need to
// create the shared memory with the game's process ID (this process)
if(!(lpgn->dwFlags & GN_LOBBY_CLIENT)) { // Fake the setup routine by setting the lobby client flag
lpgn->dwFlags |= GN_LOBBY_CLIENT;
// Set our flag
bGameCreate = TRUE; }
hr = PRV_SetupAllSharedMemory(lpgn);
// HACK!!!! -- Reset the settings we changed to fake the setup routines
if(bGameCreate) { lpgn->dwFlags &= (~GN_LOBBY_CLIENT); }
// Now handle the failure
if(FAILED(hr)) { LEAVE_DPLGAMENODE(); DPF(2, "Unable to access Connection Settings memory"); return hr; } }
// If the ConnectionSettings come from a StartSession message, we need to
// pick the dplay object pointer out of the DPLCONNECTION structure's
// reserved field. This pointer to a dplay object represents the object
// that has a connection to the lobby server.
if(lpConn->lpSessionDesc->dwReserved1) { // Save the pointer and player ID in our gamenode structure
lpgn->lpDPlayObject = (LPDPLAYI_DPLAY)lpConn->lpSessionDesc->dwReserved1; lpgn->dpidPlayer = (DWORD)lpConn->lpSessionDesc->dwReserved2;
// Clear the field
lpConn->lpSessionDesc->dwReserved1 = 0L; lpConn->lpSessionDesc->dwReserved2 = 0L; }
// Save the instance pointer for the system messages
lpgn->guidInstance = lpConn->lpSessionDesc->guidInstance;
// Get the packaged size of the DPLCONNECTION structure
PRV_GetDPLCONNECTIONPackageSize(lpConn, &dwSize, NULL);
// Check data sizes
if(dwSize > (MAX_APPDATABUFFERSIZE - APPDATA_RESERVEDSIZE)) { DPF(2, "Packaged Connection Settings exceeded max buffer size of %d", (MAX_APPDATABUFFERSIZE - APPDATA_RESERVEDSIZE)); LEAVE_DPLGAMENODE(); return DPERR_BUFFERTOOLARGE; }
// Make sure we have the mutex for the shared conn settings buffer
WaitForSingleObject(lpgn->hConnectDataMutex, INFINITE);
// Look at the control block to see if we are in wait mode
// If we are, and this is not a call from RunApplication, then
// we don't want to write the connection settings
hr = DPERR_UNAVAILABLE; // Default set to error
lpConnControl = (LPDPLOBBYI_CONNCONTROL)lpgn->lpConnectDataBuffer; if((!(lpConnControl->dwFlags & BC_WAIT_MODE)) || bOverrideWaitMode) { // Get a pointer to the actual buffer
lpConnBuffer = (LPBYTE)lpConnControl + sizeof(DPLOBBYI_CONNCONTROL);
// Package the connection settings into the buffer
hr = PRV_PackageDPLCONNECTION(lpConn, lpConnBuffer, TRUE); // If it succeeded, and we were overriding wait mode, we need
// to take the buffers out of wait mode and send the new connection
// settings available message
if(SUCCEEDED(hr) && bOverrideWaitMode) { // Take the buffers out of wait mode
PRV_LeaveConnSettingsWaitMode(lpgn); } }
ReleaseMutex(lpgn->hConnectDataMutex);
LEAVE_DPLGAMENODE(); return hr;
} // PRV_WriteConnectionSettings
#undef DPF_MODNAME
#define DPF_MODNAME "PRV_SetConnectionSettings"
HRESULT PRV_SetConnectionSettings(LPDIRECTPLAYLOBBY lpDPL, DWORD dwFlags, DWORD dwGameID, LPDPLCONNECTION lpConn) { LPDPLOBBYI_DPLOBJECT this; LPDPLOBBYI_GAMENODE lpgn = NULL; HRESULT hr; BOOL bLobbyClient = TRUE;
DPF(7, "Entering PRV_SetConnectionSettings"); DPF(9, "Parameters: 0x%08x, 0x%08x, 0x%08x, 0x%08x", lpDPL, dwFlags, dwGameID, lpConn);
TRY { if( !VALID_DPLOBBY_INTERFACE( lpDPL )) { return DPERR_INVALIDINTERFACE; }
this = DPLOBJECT_FROM_INTERFACE(lpDPL); if( !VALID_DPLOBBY_PTR( this ) ) { return DPERR_INVALIDOBJECT; }
// Validate the DPLCONNECTION structure
hr = PRV_ValidateDPLCONNECTION(lpConn, FALSE); if(FAILED(hr)) { return hr; }
// We haven't defined any flags for this release
if( (dwFlags) ) { return DPERR_INVALIDFLAGS; } }
EXCEPT( EXCEPTION_EXECUTE_HANDLER ) { DPF_ERR( "Exception encountered validating parameters" ); return DPERR_INVALIDPARAMS; }
// If dwGameID is zero, we assume we are a game. In that case, the
// GameNode we are looking for should have our ProcessID.
if(!dwGameID) { dwGameID = GetCurrentProcessId(); bLobbyClient = FALSE; }
lpgn = PRV_GetGameNode(this->lpgnHead, dwGameID); if(!lpgn) { // If we are a game, go ahead and create the node
if(!bLobbyClient) { hr = PRV_AddNewGameNode(this, &lpgn, dwGameID, NULL, bLobbyClient,NULL); if(FAILED(hr)) return hr; } else return DPERR_INVALIDPARAMS;
} // If the ConnectionSettings are from a StartSession message (lobby launched),
// we need to set the flag saying we are self-lobbied
if(lpConn->lpSessionDesc->dwReserved1) { // Set the flag that says we were lobby client launched
lpgn->dwFlags |= GN_SELF_LOBBIED; }
// Write the connection settings to our shared buffer
hr = PRV_WriteConnectionSettings(lpgn, lpConn, FALSE);
return hr;
} // PRV_SetConnectionSettings
#undef DPF_MODNAME
#define DPF_MODNAME "DPL_SetConnectionSettings"
HRESULT DPLAPI DPL_SetConnectionSettings(LPDIRECTPLAYLOBBY lpDPL, DWORD dwFlags, DWORD dwGameID, LPDPLCONNECTION lpConn) { HRESULT hr;
DPF(7, "Entering DPL_SetConnectionSettings"); DPF(9, "Parameters: 0x%08x, 0x%08x, 0x%08x, 0x%08x", lpDPL, dwFlags, dwGameID, lpConn);
ENTER_DPLOBBY();
// Set the ANSI flag to TRUE and call the internal function
hr = PRV_SetConnectionSettings(lpDPL, dwFlags, dwGameID, lpConn);
LEAVE_DPLOBBY(); return hr;
} // DPL_SetConnectionSettings
#undef DPF_MODNAME
#define DPF_MODNAME "PRV_ReadConnectionSettings"
HRESULT PRV_ReadConnectionSettings(LPDPLOBBYI_GAMENODE lpgn, LPVOID lpData, LPDWORD lpdwSize, BOOL bAnsi) { HRESULT hr = DP_OK; LPDWORD lpdwBuffer; LPDPLOBBYI_CONNCONTROL lpConnControl = NULL; LPBYTE lpConnBuffer = NULL; DWORD dwSize = 0, dwSizeAnsi, dwSizeUnicode;
DPF(7, "Entering PRV_ReadConnectionSettings"); DPF(9, "Parameters: 0x%08x, 0x%08x, 0x%08x, %lu", lpgn, lpData, lpdwSize, bAnsi);
// Make sure we have a valid memory pointer
// Note: Take the GameNode lock so that nobody changes the flags
// for the buffers, or the buffers themselves out from under us.
ENTER_DPLGAMENODE(); if(!(lpgn->dwFlags & GN_SHARED_MEMORY_AVAILABLE)) { hr = PRV_SetupAllSharedMemory(lpgn); if(FAILED(hr)) { LEAVE_DPLGAMENODE(); DPF(5, "Unable to access Connect Data memory"); return DPERR_NOTLOBBIED; } }
// Grab the shared buffer mutex
WaitForSingleObject(lpgn->hConnectDataMutex, INFINITE);
// Make sure we are not in wait mode without being in pending mode
lpConnControl = (LPDPLOBBYI_CONNCONTROL)lpgn->lpConnectDataBuffer; if((lpConnControl->dwFlags & BC_WAIT_MODE) && !(lpConnControl->dwFlags & BC_PENDING_CONNECT)) { hr = DPERR_UNAVAILABLE; goto EXIT_READ_CONN_SETTINGS; }
if(!(lpgn->dwFlags & GN_LOBBY_CLIENT)){ lpgn->dwLobbyClientProcessID = lpConnControl->CliProcId; lpgn->hLobbyClientProcess = OpenProcess(STANDARD_RIGHTS_REQUIRED|SYNCHRONIZE ,FALSE, lpgn->dwLobbyClientProcessID);
if(!lpgn->hLobbyClientProcess){ #ifdef DEBUG
DWORD err; err = GetLastError(); DPF(0,"Couldn't get lobby client processId %d, extended error %d\n",lpConnControl->CliProcId,err); #endif
// lobby client is already dead, don't allow settings across.
hr = DPERR_UNAVAILABLE; goto EXIT_READ_CONN_SETTINGS; } }
// Take us out of wait mode and pending mode
PRV_LeaveConnSettingsWaitMode(lpgn);
// Verify that the buffer is big enough. If it's not, OR if the lpData
// buffer pointer is NULL, just set the lpdwSize parameter to the
// correct size and return an error. Note: In our packed structure, the
// first DWORD is the size of the packed structure with Unicode strings
// and the second DWORD is the size of the packed structure with ANSI.
lpConnBuffer = (LPBYTE)lpConnControl + sizeof(DPLOBBYI_CONNCONTROL); lpdwBuffer = (LPDWORD)lpConnBuffer; dwSizeUnicode = *lpdwBuffer++; dwSizeAnsi = *lpdwBuffer; dwSize = (bAnsi) ? dwSizeAnsi : dwSizeUnicode;
if(dwSize > MAX_APPDATABUFFERSIZE-sizeof(DPLOBBYI_CONNCONTROL)){ DPF(4,"SECURITY WARN: illegal size in settings shared memory buffer"); hr=DPERR_NOTLOBBIED; goto EXIT_READ_CONN_SETTINGS; }
if(((*lpdwSize) < dwSize) || (!lpData)) { if(bAnsi) *lpdwSize = dwSizeAnsi; else *lpdwSize = dwSizeUnicode;
hr = DPERR_BUFFERTOOSMALL; goto EXIT_READ_CONN_SETTINGS; }
// Copy the DPLCONNECTION structure, taking the ANSI conversion
// into account if necessary.
if(bAnsi) hr = PRV_UnpackageDPLCONNECTIONAnsi(lpData, lpConnBuffer); else hr = PRV_UnpackageDPLCONNECTIONUnicode(lpData, lpConnBuffer);
// If we haven't yet saved off the Instance guid for the game, save
// it now so that we have it for the system messages
if(IsEqualGUID(&lpgn->guidInstance, &GUID_NULL)) lpgn->guidInstance = ((LPDPLCONNECTION)lpData)->lpSessionDesc->guidInstance;
// Fall through
EXIT_READ_CONN_SETTINGS:
ReleaseMutex(lpgn->hConnectDataMutex); LEAVE_DPLGAMENODE(); return hr;
} // PRV_ReadConnectionSettings
#undef DPF_MODNAME
#define DPF_MODNAME "PRV_GetConnectionSettings"
HRESULT PRV_GetConnectionSettings(LPDIRECTPLAYLOBBY lpDPL, DWORD dwGameID, LPVOID lpData, LPDWORD lpdwSize, BOOL bAnsi) { LPDPLOBBYI_DPLOBJECT this; LPDPLOBBYI_GAMENODE lpgn = NULL; HRESULT hr; BOOL bLobbyClient = TRUE;
DPF(7, "Entering PRV_GetConnectionSettings"); DPF(9, "Parameters: 0x%08x, 0x%08x, 0x%08x, 0x%08x, %lu", lpDPL, dwGameID, lpData, lpdwSize, bAnsi);
TRY { if( !VALID_DPLOBBY_INTERFACE( lpDPL )) { return DPERR_INVALIDINTERFACE; }
this = DPLOBJECT_FROM_INTERFACE(lpDPL); if( !VALID_DPLOBBY_PTR( this ) ) { return DPERR_INVALIDOBJECT; }
if( !VALID_DWORD_PTR( lpdwSize ) ) { DPF_ERR("lpdwSize was not a valid dword pointer!"); return DPERR_INVALIDPARAMS; }
if(lpData) { if( !VALID_WRITE_PTR(lpData, *lpdwSize) ) { DPF_ERR("lpData is not a valid output buffer of the size specified in *lpdwSize"); return DPERR_INVALIDPARAMS; } } }
EXCEPT( EXCEPTION_EXECUTE_HANDLER ) { DPF_ERR( "Exception encountered validating parameters" ); return DPERR_INVALIDPARAMS; }
// If dwGameID is zero, we assume we are a game. In that case, the
// GameNode we are looking for should have our ProcessID.
if(!dwGameID) { dwGameID = GetCurrentProcessId(); bLobbyClient = FALSE; }
lpgn = PRV_GetGameNode(this->lpgnHead, dwGameID); if(!lpgn) { // If we are a game, go ahead and create the node
if(!bLobbyClient) { hr = PRV_AddNewGameNode(this, &lpgn, dwGameID, NULL, bLobbyClient,NULL); if(FAILED(hr)) return hr; } else return DPERR_INVALIDPARAMS; } // Read the data from our shared memory
hr = PRV_ReadConnectionSettings(lpgn, lpData, lpdwSize, bAnsi);
return hr;
} // PRV_GetConnectionSettings
#undef DPF_MODNAME
#define DPF_MODNAME "DPL_GetConnectionSettings"
HRESULT DPLAPI DPL_GetConnectionSettings(LPDIRECTPLAYLOBBY lpDPL, DWORD dwGameID, LPVOID lpData, LPDWORD lpdwSize) { HRESULT hr;
DPF(7, "Entering DPL_GetConnectionSettings"); DPF(9, "Parameters: 0x%08x, 0x%08x, 0x%08x, 0x%08x", lpDPL, dwGameID, lpData, lpdwSize);
ENTER_DPLOBBY();
// Set the ANSI flag to TRUE and call the internal function
hr = PRV_GetConnectionSettings(lpDPL, dwGameID, lpData, lpdwSize, FALSE);
LEAVE_DPLOBBY(); return hr;
} // DPL_GetConnectionSettings
#undef DPF_MODNAME
#define DPF_MODNAME "PRV_RemoveGameNodeFromList"
void PRV_RemoveGameNodeFromList(LPDPLOBBYI_GAMENODE lpgn) { LPDPLOBBYI_GAMENODE lpgnTemp; BOOL bFound = FALSE;
DPF(7, "Entering PRV_RemoveGameNodeFromList"); DPF(9, "Parameters: 0x%08x", lpgn);
// Get the head pointer
lpgnTemp = lpgn->this->lpgnHead;
// Make sure it's not the first node. If it is, move the head pointer
if(lpgnTemp == lpgn) { lpgn->this->lpgnHead = lpgn->lpgnNext; PRV_FreeGameNode(lpgn); return; }
// Walk the list looking for the previous node
while(lpgnTemp) { if(lpgnTemp->lpgnNext == lpgn) { bFound = TRUE; break; }
lpgnTemp = lpgnTemp->lpgnNext; }
if(!bFound) { DPF_ERR("Unable to remove GameNode from list!"); return; }
// We've now got it's previous one, so remove it from the linked list
// and delete it.
lpgnTemp->lpgnNext = lpgn->lpgnNext; PRV_FreeGameNode(lpgn);
return;
} // PRV_RemoveGameNodeFromList
#undef DPF_MODNAME
#define DPF_MODNAME "PRV_ClientTerminateNotification"
DWORD WINAPI PRV_ClientTerminateNotification(LPVOID lpParam) { LPDPLOBBYI_GAMENODE lpgn = (LPDPLOBBYI_GAMENODE)lpParam; DPLMSG_SYSTEMMESSAGE msg; HANDLE hObjects[3]; HRESULT hr; DWORD dwResult; DWORD dwError;
DPF(7, "Entering PRV_ClientTerminateNotification"); DPF(9, "Parameters: 0x%08x", lpParam);
// Setup the objects to wait on -- one process handle, one kill event
hObjects[0] = lpgn->hGameProcess; hObjects[1] = lpgn->hKillTermThreadEvent; // This extra handle is here because of a Windows 95 bug. Windows
// will occasionally miss when it walks the handle table, causing
// my thread to wait on the wrong handles. By putting a guaranteed
// invalid handle at the end of our array, the kernel will do a
// forced re-walk of the handle table and find the correct handles.
hObjects[2] = INVALID_HANDLE_VALUE;
// Wait for the event notification
while(1) { // Wait for the process to go away
dwResult = WaitForMultipleObjects(2, (HANDLE *)hObjects, FALSE, INFINITE);
// If we are signalled by anything but the process going away,
// just kill the thread.
if(dwResult != WAIT_OBJECT_0) { if(dwResult == WAIT_FAILED) { // This is a Windows 95 bug -- We may have gotten
// kicked for no reason. If that was the case, we
// still have valid handles (we think), the OS
// just goofed up. So, validate the handle and if
// they are valid, just return to waiting. See
// bug #3340 for a better explanation.
dwError = GetLastError(); if(ERROR_INVALID_HANDLE == dwError) { DPF(1, "Wait for client termination failed due to invalid handle."); if(!OS_IsValidHandle(hObjects[0])) break; if(!OS_IsValidHandle(hObjects[1])) break; continue; } DPF(0, "Wait for client termination failed (err = %u)!", dwError); break; } else { // This is something we don't understand, so just go away.
DPF(1, "Exiting thread (result = %u).", dwResult); ExitThread(0L); return 0L; } } else { // This is our process handle going away, so bail out of
// the wait loop and send the system message.
DPF(2, "Client terminated."); break; } }
// Send the system message which says the app terminated
memset(&msg, 0, sizeof(DPLMSG_SYSTEMMESSAGE)); msg.dwType = DPLSYS_APPTERMINATED; msg.guidInstance = lpgn->guidInstance; hr = PRV_InjectMessageInQueue(lpgn, DPLAD_SYSTEM, &msg, sizeof(DPLMSG_SYSTEMMESSAGE), TRUE); if(FAILED(hr)) { DPF(0, "Failed to send App Termination message, hr = 0x%08x", hr); }
// Mark the GAMENODE as dead, but don't remove it since we know
// there are still messages in the queue.
lpgn->dwFlags |= GN_DEAD_GAME_NODE;
ExitThread(0L);
return 0L; // avoid warning.
} // PRV_ClientTerminateNotification
#undef DPF_MODNAME
#define DPF_MODNAME "DPL_WaitForConnectionSettings"
HRESULT DPLAPI DPL_WaitForConnectionSettings(LPDIRECTPLAYLOBBY lpDPL, DWORD dwFlags) { LPDPLOBBYI_DPLOBJECT this; LPDPLOBBYI_GAMENODE lpgn = NULL; LPDPLOBBYI_CONNCONTROL lpConnControl = NULL; LPDPLOBBYI_BUFFERCONTROL lpBuffControl = NULL; HRESULT hr = DP_OK; BOOL bCreated = FALSE; DWORD dwProcessID; BOOL bGameCreate = FALSE; BOOL bMessages = TRUE;
DPF(7, "Entering DPL_WaitForConnectionSettings"); DPF(9, "Parameters: 0x%08x, 0x%08x", lpDPL, dwFlags);
ENTER_DPLOBBY();
TRY { if( !VALID_DPLOBBY_INTERFACE( lpDPL )) { LEAVE_DPLOBBY(); return DPERR_INVALIDINTERFACE; }
this = DPLOBJECT_FROM_INTERFACE(lpDPL); if( !VALID_DPLOBBY_PTR( this ) ) { LEAVE_DPLOBBY(); return DPERR_INVALIDOBJECT; }
if(!VALID_WAIT_FLAGS(dwFlags)) { LEAVE_DPLOBBY(); return DPERR_INVALIDFLAGS; } }
EXCEPT( EXCEPTION_EXECUTE_HANDLER ) { LEAVE_DPLOBBY(); DPF_ERR( "Exception encountered validating parameters" ); return DPERR_INVALIDPARAMS; }
// Get the game node
dwProcessID = GetCurrentProcessId(); lpgn = PRV_GetGameNode(this->lpgnHead, dwProcessID); if(!lpgn) { // Create the game node
hr = PRV_AddNewGameNode(this, &lpgn, dwProcessID, NULL, FALSE, NULL); if(FAILED(hr)) { DPF_ERRVAL("Failed creating game node, hr = 0x%08x", hr); goto EXIT_WAIT_FOR_CONN_SETTINGS; }
// Set our flag saying we just created the game node
bCreated = TRUE; }
// when doing a wait for connection settings, we do NOT use the
// IPC_GUID, this is because the lobby launching us may not have
// provided the GUID.
lpgn->dwFlags &= ~(GN_IPCGUID_SET);
// Make sure we have a valid memory pointer
// Note: Take the GameNode lock so that nobody changes the flags
// for the buffers, or the buffers themselves out from under us.
ENTER_DPLGAMENODE(); if(!(lpgn->dwFlags & GN_SHARED_MEMORY_AVAILABLE)) { // First we need to try to setup access to the buffers assuming
// they already exist (we were lobby launched). If this doesn't
// work, then we need to create them.
hr = PRV_SetupAllSharedMemory(lpgn); if(FAILED(hr)) { // We don't have any memory, so set it up
// HACK!!!! -- WaitForConnectionSettings may get called from the game
// without having been lobbied. If that is the case, we need to
// create the shared memory with the game's process ID (this process)
// so we'll set the lobby client flag to fake out the creation
if(!(lpgn->dwFlags & GN_LOBBY_CLIENT)) { // Fake the setup routine by setting the lobby client flag
lpgn->dwFlags |= GN_LOBBY_CLIENT;
// Set our flag
bGameCreate = TRUE; }
// Setup the shared buffers
hr = PRV_SetupAllSharedMemory(lpgn);
// HACK!!!! -- Reset the settings we changed to fake the setup routines
if(bGameCreate) { lpgn->dwFlags &= (~GN_LOBBY_CLIENT); } }
if(FAILED(hr)) { LEAVE_DPLGAMENODE(); DPF_ERRVAL("Unable to access Connect Data memory, hr = 0x%08x", hr); goto EXIT_WAIT_FOR_CONN_SETTINGS; } }
// Drop the lock
LEAVE_DPLGAMENODE();
// If we are in wait mode, and the caller wants to end it, do so,
// otherwise, just return success
WaitForSingleObject(lpgn->hConnectDataMutex, INFINITE); lpConnControl = (LPDPLOBBYI_CONNCONTROL)lpgn->lpConnectDataBuffer; if(lpConnControl->dwFlags & BC_WAIT_MODE) { if(dwFlags & DPLWAIT_CANCEL) { // Release Mutex
ReleaseMutex(lpgn->hConnectDataMutex);
// Take us out of wait mode
PRV_LeaveConnSettingsWaitMode(lpgn); goto EXIT_WAIT_FOR_CONN_SETTINGS; } else { // Release Mutex
ReleaseMutex(lpgn->hConnectDataMutex);
// Might as well just return OK since we're already doing it
DPF_ERR("We're already in wait mode"); goto EXIT_WAIT_FOR_CONN_SETTINGS; } } else { // We're not it wait mode, and the caller asked us to turn it off
if(dwFlags & DPLWAIT_CANCEL) { // Release Mutex
ReleaseMutex(lpgn->hConnectDataMutex);
DPF_ERR("Cannot turn off wait mode - we're not in wait mode"); hr = DPERR_UNAVAILABLE; goto EXIT_WAIT_FOR_CONN_SETTINGS; } }
// Release Mutex
ReleaseMutex(lpgn->hConnectDataMutex);
// See if a lobby client exists on the other side, if it does, we
// need to tell him we are going into wait mode by sending him an
// AppTerminated message.
PRV_SendStandardSystemMessage(lpDPL, DPLSYS_APPTERMINATED, 0);
// Go into wait mode
PRV_EnterConnSettingsWaitMode(lpgn);
// Kick the receive thread to empty the buffer (just in case there
// are any messages in it)
SetEvent(lpgn->hLobbyWriteEvent);
// Spin waiting for the buffer to get emptied
while(bMessages) { // Grab the mutex for the lobby write buffer
WaitForSingleObject(lpgn->hLobbyWriteMutex, INFINITE); lpBuffControl = (LPDPLOBBYI_BUFFERCONTROL)lpgn->lpLobbyWriteBuffer;
if(!lpBuffControl->dwMessages) bMessages = FALSE;
// Drop the mutex
ReleaseMutex(lpgn->hLobbyWriteMutex);
if(bMessages) { // Now sleep to give the receive thread a chance to work
Sleep(50); } }
// Now clean out the message queue
PRV_CleanUpQueue(lpgn);
// Fall through
EXIT_WAIT_FOR_CONN_SETTINGS:
LEAVE_DPLOBBY(); return hr;
} // DPL_WaitForConnectionSettings
|