Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

3385 lines
92 KiB

  1. /*==========================================================================
  2. *
  3. * Copyright (C) 1996-1997 Microsoft Corporation. All Rights Reserved.
  4. *
  5. * File: dplshare.c
  6. * Content: Methods for shared buffer management
  7. *
  8. * History:
  9. * Date By Reason
  10. * ======= ======= ======
  11. * 5/18/96 myronth Created it
  12. * 12/12/96 myronth Fixed DPLCONNECTION validation & bug #4692
  13. * 12/13/96 myronth Fixed bugs #4697 and #4607
  14. * 2/12/97 myronth Mass DX5 changes
  15. * 2/20/97 myronth Changed buffer R/W to be circular
  16. * 3/12/97 myronth Kill thread timeout, DPF error levels
  17. * 4/1/97 myronth Fixed handle leak -- bug #7054
  18. * 5/8/97 myronth Added bHeader parameter to packing function
  19. * 5/21/97 ajayj DPL_SendLobbyMessage - allow DPLMSG_STANDARD flag #8929
  20. * 5/30/97 myronth Fixed SetConnectionSettings for invalid AppID (#9110)
  21. * Fixed SetLobbyMessageEvent for invalid handle (#9111)
  22. * 6/19/97 myronth Fixed handle leak (#10063)
  23. * 7/30/97 myronth Added support for standard lobby messaging and
  24. * fixed receive loop race condition (#10843)
  25. * 8/11/97 myronth Added guidInstance handling in standard lobby requests
  26. * 8/19/97 myronth Support for DPLMSG_NEWSESSIONHOST
  27. * 8/19/97 myronth Removed dead PRV_SendStandardSystemMessageByObject
  28. * 8/20/97 myronth Added DPLMSG_STANDARD to all standard messages
  29. * 11/13/97 myronth Added guidInstance to lobby system message (#10944)
  30. * 12/2/97 myronth Fixed swallowed error code, moved structure
  31. * validation for DPLCONNECTION (#15527, 15529)
  32. * 1/20/98 myronth Added WaitForConnectionSettings
  33. * 7/9/99 aarono Cleaning up GetLastError misuse, must call right away,
  34. * before calling anything else, including DPF.
  35. * 10/31/99 aarono add node lock when to SetLobbyMessageEvent
  36. * NTB#411892
  37. * 02/08/00 aarono added monitoring for lobby client crash/exit, notify
  38. * lobbied application, Mill B#131938
  39. * 7/12/00 aarono fix GUIDs for IPC to be fully significant, otherwise won't IPC.
  40. ***************************************************************************/
  41. #include "dplobpr.h"
  42. //--------------------------------------------------------------------------
  43. //
  44. // Debug Functions
  45. //
  46. //--------------------------------------------------------------------------
  47. #ifdef DEBUG
  48. DPF_DUMPWSTR(int level, LPWSTR lpwStr)
  49. {
  50. char lpszStr[200];
  51. WideToAnsi(lpszStr,lpwStr,200);
  52. DPF(level, lpszStr);
  53. }
  54. #else
  55. #define DPF_DUMPWSTR(a,b)
  56. #endif
  57. //--------------------------------------------------------------------------
  58. //
  59. // Functions
  60. //
  61. //--------------------------------------------------------------------------
  62. HRESULT PRV_ReadCommandLineIPCGuid(GUID *lpguidIPC)
  63. {
  64. LPWSTR pwszCommandLine;
  65. LPWSTR pwszAlloc=NULL;
  66. LPWSTR pwszSwitch=NULL;
  67. HRESULT hr=DP_OK;
  68. if(!OS_IsPlatformUnicode())
  69. {
  70. // if we get a command line in ANSI, convert to UNICODE, this allows
  71. // us to avoid the DBCS issues in ANSI while scanning for the IPC GUID
  72. LPSTR pszCommandLine;
  73. pszCommandLine=(LPSTR)GetCommandLineA();
  74. pwszAlloc=DPMEM_ALLOC(MAX_PATH*sizeof(WCHAR));
  75. if (pwszAlloc == NULL)
  76. {
  77. goto exit;
  78. }
  79. hr=AnsiToWide(pwszAlloc,pszCommandLine,MAX_PATH);
  80. if(FAILED(hr))
  81. {
  82. goto exit;
  83. }
  84. pwszCommandLine=pwszAlloc;
  85. }
  86. else
  87. {
  88. pwszCommandLine=(LPWSTR)GetCommandLine();
  89. }
  90. // pwszCommandLine now points to the UNICODE command line.
  91. if(pwszSwitch=OS_StrStr(pwszCommandLine,SZ_DP_IPC_GUID)){
  92. // found the GUID on the command line
  93. if (OS_StrLen(pwszSwitch) >= (sizeof(SZ_DP_IPC_GUID)+sizeof(SZ_GUID_PROTOTYPE)-sizeof(WCHAR))/sizeof(WCHAR)){
  94. // skip past the switch description to the actual GUID and extract
  95. hr=GUIDFromString(pwszSwitch+(sizeof(SZ_DP_IPC_GUID)/sizeof(WCHAR))-1, lpguidIPC);
  96. } else {
  97. hr=DPERR_GENERIC;
  98. }
  99. } else {
  100. hr=DPERR_GENERIC;
  101. }
  102. exit:
  103. if(pwszAlloc){
  104. DPMEM_FREE(pwszAlloc);
  105. }
  106. return hr;
  107. }
  108. #undef DPF_MODNAME
  109. #define DPF_MODNAME "PRV_GetInternalName"
  110. HRESULT PRV_GetInternalName(LPDPLOBBYI_GAMENODE lpgn, DWORD dwType, LPWSTR lpName)
  111. {
  112. DWORD pid;
  113. LPWSTR lpFileName;
  114. LPSTR lpstr1, lpstr2, lpstr3;
  115. char szName[MAX_MMFILENAME_LENGTH * sizeof(WCHAR)];
  116. BOOL bUseGuid=FALSE;
  117. DPF(7, "Entering PRV_GetInternalName");
  118. DPF(9, "Parameters: 0x%08x, 0x%08x, 0x%08x", lpgn, dwType, lpName);
  119. if(lpgn->dwFlags & GN_IPCGUID_SET){
  120. bUseGuid=TRUE;
  121. }
  122. // Get the current process ID if we are a game, otherwise, we need to
  123. // get the process ID of the game that we spawned
  124. else if(lpgn->dwFlags & GN_LOBBY_CLIENT)
  125. {
  126. if(lpgn->dwGameProcessID)
  127. pid = lpgn->dwGameProcessID;
  128. else
  129. return DPERR_APPNOTSTARTED;
  130. }
  131. else
  132. {
  133. pid = GetCurrentProcessId();
  134. }
  135. switch(dwType)
  136. {
  137. case TYPE_CONNECT_DATA_FILE:
  138. lpFileName = SZ_CONNECT_DATA_FILE;
  139. break;
  140. case TYPE_CONNECT_DATA_MUTEX:
  141. lpFileName = SZ_CONNECT_DATA_MUTEX;
  142. break;
  143. case TYPE_GAME_WRITE_FILE:
  144. lpFileName = SZ_GAME_WRITE_FILE;
  145. break;
  146. case TYPE_LOBBY_WRITE_FILE:
  147. lpFileName = SZ_LOBBY_WRITE_FILE;
  148. break;
  149. case TYPE_LOBBY_WRITE_EVENT:
  150. lpFileName = SZ_LOBBY_WRITE_EVENT;
  151. break;
  152. case TYPE_GAME_WRITE_EVENT:
  153. lpFileName = SZ_GAME_WRITE_EVENT;
  154. break;
  155. case TYPE_LOBBY_WRITE_MUTEX:
  156. lpFileName = SZ_LOBBY_WRITE_MUTEX;
  157. break;
  158. case TYPE_GAME_WRITE_MUTEX:
  159. lpFileName = SZ_GAME_WRITE_MUTEX;
  160. break;
  161. default:
  162. DPF(2, "We got an Internal Name Type that we didn't expect!");
  163. return DPERR_GENERIC;
  164. }
  165. GetAnsiString(&lpstr2, SZ_FILENAME_BASE);
  166. GetAnsiString(&lpstr3, lpFileName);
  167. if(!bUseGuid){
  168. // REVIEW!!!! -- I can't get the Unicode version of wsprintf to work, so
  169. // for now, use the ANSI version and convert
  170. // wsprintf(lpName, SZ_NAME_TEMPLATE, SZ_FILENAME_BASE, lpFileName, pid);
  171. GetAnsiString(&lpstr1, SZ_NAME_TEMPLATE);
  172. wsprintfA((LPSTR)szName, lpstr1, lpstr2, lpstr3, pid);
  173. } else {
  174. GetAnsiString(&lpstr1, SZ_GUID_NAME_TEMPLATE);
  175. wsprintfA((LPSTR)szName, lpstr1, lpstr2, lpstr3);
  176. }
  177. AnsiToWide(lpName, szName, (strlen(szName) + 1));
  178. if(bUseGuid){
  179. // concatenate the guid to the name if we are using the guid.
  180. WCHAR *pGuid;
  181. pGuid = lpName + WSTRLEN(lpName) - 1;
  182. StringFromGUID(&lpgn->guidIPC, pGuid, GUID_STRING_SIZE);
  183. }
  184. DPF(9, "Made internal Name...");
  185. DPF_DUMPWSTR(9,lpName);
  186. if(lpstr1)
  187. DPMEM_FREE(lpstr1);
  188. if(lpstr2)
  189. DPMEM_FREE(lpstr2);
  190. if(lpstr3)
  191. DPMEM_FREE(lpstr3);
  192. return DP_OK;
  193. } // PRV_GetInternalName
  194. #undef DPF_MODNAME
  195. #define DPF_MODNAME "PRV_AddNewGameNode"
  196. HRESULT PRV_AddNewGameNode(LPDPLOBBYI_DPLOBJECT this,
  197. LPDPLOBBYI_GAMENODE * lplpgn, DWORD dwGameID,
  198. HANDLE hGameProcess, BOOL bLobbyClient, GUID *lpguidIPC)
  199. {
  200. LPDPLOBBYI_GAMENODE lpgn;
  201. DPF(7, "Entering PRV_AddNewGameNode");
  202. DPF(9, "Parameters: 0x%08x, 0x%08x, 0x%08x, 0x%08x, %lu",
  203. this, lplpgn, dwGameID, hGameProcess, bLobbyClient);
  204. lpgn = DPMEM_ALLOC(sizeof(DPLOBBYI_GAMENODE));
  205. if(!lpgn)
  206. {
  207. DPF(2, "Unable to allocate memory for GameNode structure!");
  208. return DPERR_OUTOFMEMORY;
  209. }
  210. // Initialize the GameNode
  211. lpgn->dwSize = sizeof(DPLOBBYI_GAMENODE);
  212. lpgn->dwGameProcessID = dwGameID;
  213. lpgn->hGameProcess = hGameProcess;
  214. lpgn->this = this;
  215. lpgn->MessageHead.lpPrev = &lpgn->MessageHead;
  216. lpgn->MessageHead.lpNext = &lpgn->MessageHead;
  217. if(lpguidIPC){
  218. // provided during launch by lobby client
  219. lpgn->guidIPC=*lpguidIPC;
  220. lpgn->dwFlags |= GN_IPCGUID_SET;
  221. } else {
  222. // need to extract the GUID from the command line if present.
  223. if(DP_OK==PRV_ReadCommandLineIPCGuid(&lpgn->guidIPC)){
  224. lpgn->dwFlags |= GN_IPCGUID_SET;
  225. }
  226. }
  227. // If we are a lobby client, set the flag
  228. if(bLobbyClient)
  229. lpgn->dwFlags |= GN_LOBBY_CLIENT;
  230. // Add the GameNode to the list
  231. lpgn->lpgnNext = this->lpgnHead;
  232. this->lpgnHead = lpgn;
  233. // Set the output pointer
  234. *lplpgn = lpgn;
  235. return DP_OK;
  236. } // PRV_AddNewGameNode
  237. #undef DPF_MODNAME
  238. #define DPF_MODNAME "PRV_GetGameNode"
  239. LPDPLOBBYI_GAMENODE PRV_GetGameNode(LPDPLOBBYI_GAMENODE lpgnHead, DWORD dwGameID)
  240. {
  241. LPDPLOBBYI_GAMENODE lpgnTemp = lpgnHead;
  242. GUID guidIPC=GUID_NULL;
  243. BOOL bFoundGUID;
  244. DPF(7, "Entering PRV_GetGameNode");
  245. DPF(9, "Parameters: 0x%08x, 0x%08x", lpgnHead, dwGameID);
  246. if(DP_OK==PRV_ReadCommandLineIPCGuid(&guidIPC)){
  247. bFoundGUID=TRUE;
  248. } else {
  249. bFoundGUID=FALSE;
  250. }
  251. while(lpgnTemp)
  252. {
  253. if((lpgnTemp->dwGameProcessID == dwGameID) ||
  254. ((bFoundGUID) && (lpgnTemp->dwFlags & GN_IPCGUID_SET) && (IsEqualGUID(&lpgnTemp->guidIPC,&guidIPC))))
  255. return lpgnTemp;
  256. else
  257. lpgnTemp = lpgnTemp->lpgnNext;
  258. }
  259. return NULL;
  260. } // PRV_GetGameNode
  261. #undef DPF_MODNAME
  262. #define DPF_MODNAME "PRV_SetupClientDataAccess"
  263. BOOL PRV_SetupClientDataAccess(LPDPLOBBYI_GAMENODE lpgn)
  264. {
  265. SECURITY_ATTRIBUTES sa;
  266. HANDLE hConnDataMutex = NULL;
  267. HANDLE hLobbyWrite = NULL;
  268. HANDLE hLobbyWriteMutex = NULL;
  269. HANDLE hGameWrite = NULL;
  270. HANDLE hGameWriteMutex = NULL;
  271. WCHAR szName[MAX_MMFILENAME_LENGTH * sizeof(WCHAR)];
  272. DPF(7, "Entering PRV_SetupClientDataAccess");
  273. DPF(9, "Parameters: 0x%08x", lpgn);
  274. // Set up the security attributes (so that our objects can
  275. // be inheritable)
  276. sa.nLength = sizeof(SECURITY_ATTRIBUTES);
  277. sa.lpSecurityDescriptor = NULL;
  278. sa.bInheritHandle = TRUE;
  279. // Create the ConnectionData Mutex
  280. if(SUCCEEDED(PRV_GetInternalName(lpgn, TYPE_CONNECT_DATA_MUTEX,
  281. (LPWSTR)szName)))
  282. {
  283. hConnDataMutex = OS_CreateMutex(&sa, FALSE, (LPWSTR)szName);
  284. }
  285. // Create the GameWrite Event
  286. if(SUCCEEDED(PRV_GetInternalName(lpgn, TYPE_GAME_WRITE_EVENT, (LPWSTR)szName)))
  287. {
  288. hGameWrite = OS_CreateEvent(&sa, FALSE, FALSE, (LPWSTR)szName);
  289. }
  290. // Create the GameWrite Mutex
  291. if(SUCCEEDED(PRV_GetInternalName(lpgn, TYPE_GAME_WRITE_MUTEX,
  292. (LPWSTR)szName)))
  293. {
  294. hGameWriteMutex = OS_CreateMutex(&sa, FALSE, (LPWSTR)szName);
  295. }
  296. // Create the LobbyWrite Event
  297. if(SUCCEEDED(PRV_GetInternalName(lpgn, TYPE_LOBBY_WRITE_EVENT, (LPWSTR)szName)))
  298. {
  299. hLobbyWrite = OS_CreateEvent(&sa, FALSE, FALSE, (LPWSTR)szName);
  300. }
  301. // Create the LobbyWrite Mutex
  302. if(SUCCEEDED(PRV_GetInternalName(lpgn, TYPE_LOBBY_WRITE_MUTEX,
  303. (LPWSTR)szName)))
  304. {
  305. hLobbyWriteMutex = OS_CreateMutex(&sa, FALSE, (LPWSTR)szName);
  306. }
  307. // Check for errors
  308. if(!hConnDataMutex || !hGameWrite || !hGameWriteMutex
  309. || !hLobbyWrite || !hLobbyWriteMutex)
  310. {
  311. if(hConnDataMutex)
  312. CloseHandle(hConnDataMutex);
  313. if(hGameWrite)
  314. CloseHandle(hGameWrite);
  315. if(hGameWriteMutex)
  316. CloseHandle(hGameWriteMutex);
  317. if(hLobbyWrite)
  318. CloseHandle(hLobbyWrite);
  319. if(hLobbyWriteMutex)
  320. CloseHandle(hLobbyWriteMutex);
  321. return FALSE;
  322. }
  323. // Save the handles
  324. lpgn->hConnectDataMutex = hConnDataMutex;
  325. lpgn->hGameWriteEvent = hGameWrite;
  326. lpgn->hGameWriteMutex = hGameWriteMutex;
  327. lpgn->hLobbyWriteEvent = hLobbyWrite;
  328. lpgn->hLobbyWriteMutex = hLobbyWriteMutex;
  329. return TRUE;
  330. } // PRV_SetupClientDataAccess
  331. #undef DPF_MODNAME
  332. #define DPF_MODNAME "PRV_GetDataBuffer"
  333. HRESULT PRV_GetDataBuffer(LPDPLOBBYI_GAMENODE lpgn, DWORD dwType,
  334. DWORD dwSize, LPHANDLE lphFile, LPVOID * lplpMemory)
  335. {
  336. HRESULT hr;
  337. SECURITY_ATTRIBUTES sa;
  338. WCHAR szName[MAX_MMFILENAME_LENGTH * sizeof(WCHAR)];
  339. LPVOID lpMemory = NULL;
  340. HANDLE hFile = NULL;
  341. DWORD dwError = 0;
  342. DPF(7, "Entering PRV_GetDataBuffer");
  343. DPF(9, "Parameters: 0x%08x, 0x%08x, %lu, 0x%08x, 0x%08x",
  344. lpgn, dwType, dwSize, lphFile, lplpMemory);
  345. // Get the data buffer filename
  346. hr = PRV_GetInternalName(lpgn, dwType, (LPWSTR)szName);
  347. if(FAILED(hr))
  348. return hr;
  349. // If we are a Lobby Client, we need to create the file. If we
  350. // are a game, we need to open the already created file for
  351. // connection data, or we can create the file for game data (if
  352. // it doesn't already exist).
  353. if(lpgn->dwFlags & GN_LOBBY_CLIENT)
  354. {
  355. // Set up the security attributes (so that our mapping can
  356. // be inheritable
  357. sa.nLength = sizeof(SECURITY_ATTRIBUTES);
  358. sa.lpSecurityDescriptor = NULL;
  359. sa.bInheritHandle = TRUE;
  360. // Create the file mapping
  361. hFile = OS_CreateFileMapping(INVALID_HANDLE_VALUE, &sa,
  362. PAGE_READWRITE, 0, dwSize,
  363. (LPWSTR)szName);
  364. }
  365. else
  366. {
  367. hFile = OS_OpenFileMapping(FILE_MAP_ALL_ACCESS, TRUE, (LPWSTR)szName);
  368. }
  369. if(!hFile)
  370. {
  371. dwError = GetLastError();
  372. // WARNING: error may not be correct since calls we are trying to get last error from may have called out
  373. // to another function before returning.
  374. DPF(5, "Couldn't get a handle to the shared local memory, dwError = %lu (error may not be correct)", dwError);
  375. return DPERR_OUTOFMEMORY;
  376. }
  377. // Map a View of the file
  378. lpMemory = MapViewOfFile(hFile, FILE_MAP_ALL_ACCESS, 0, 0, 0);
  379. if(!lpMemory)
  380. {
  381. dwError = GetLastError();
  382. DPF(5, "Unable to get pointer to shared local memory, dwError = %lu", dwError);
  383. CloseHandle(hFile);
  384. return DPERR_OUTOFMEMORY;
  385. }
  386. // Setup the control structure based on the buffer type
  387. switch(dwType)
  388. {
  389. case TYPE_CONNECT_DATA_FILE:
  390. {
  391. LPDPLOBBYI_CONNCONTROL lpControl = NULL;
  392. lpControl = (LPDPLOBBYI_CONNCONTROL)lpMemory;
  393. // If the buffer has been initialized, then don't worry
  394. // about it. If the token is wrong (uninitialized), then do it
  395. if(lpControl->dwToken != BC_TOKEN)
  396. {
  397. lpControl->dwToken = BC_TOKEN;
  398. lpControl->dwFlags = 0;
  399. if(lpgn->dwFlags & GN_LOBBY_CLIENT){
  400. lpControl->CliProcId = GetCurrentProcessId();
  401. }
  402. }
  403. break;
  404. }
  405. case TYPE_GAME_WRITE_FILE:
  406. case TYPE_LOBBY_WRITE_FILE:
  407. {
  408. LPDPLOBBYI_BUFFERCONTROL lpControl = NULL;
  409. lpControl = (LPDPLOBBYI_BUFFERCONTROL)lpMemory;
  410. if(lpgn->dwFlags & GN_LOBBY_CLIENT)
  411. {
  412. // Since we're the lobby client, we know we create the buffer, so
  413. // initialize the entire structure
  414. lpControl->dwToken = BC_TOKEN;
  415. lpControl->dwReadOffset = sizeof(DPLOBBYI_BUFFERCONTROL);
  416. lpControl->dwWriteOffset = sizeof(DPLOBBYI_BUFFERCONTROL);
  417. lpControl->dwFlags = BC_LOBBY_ACTIVE;
  418. lpControl->dwMessages = 0;
  419. lpControl->dwBufferSize = dwSize;
  420. lpControl->dwBufferLeft = dwSize - sizeof(DPLOBBYI_BUFFERCONTROL);
  421. }
  422. else
  423. {
  424. // We're the game, but we don't know for sure if we just created
  425. // the buffer or if a lobby client did. So check the token. If
  426. // it is incorrect, we will assume we just created it and we need
  427. // to initialize the buffer control struct. Otherwise, we will
  428. // assume a lobby client created it and we just need to add
  429. // our flag.
  430. if(lpControl->dwToken != BC_TOKEN)
  431. {
  432. // We don't see the token, so initialize the structure
  433. lpControl->dwReadOffset = sizeof(DPLOBBYI_BUFFERCONTROL);
  434. lpControl->dwWriteOffset = sizeof(DPLOBBYI_BUFFERCONTROL);
  435. lpControl->dwFlags = BC_GAME_ACTIVE;
  436. lpControl->dwMessages = 0;
  437. lpControl->dwBufferSize = dwSize;
  438. lpControl->dwBufferLeft = dwSize - sizeof(DPLOBBYI_BUFFERCONTROL);
  439. }
  440. else
  441. {
  442. // We assume the lobby created this buffer, so just set our flag
  443. lpControl->dwFlags |= BC_GAME_ACTIVE;
  444. }
  445. }
  446. break;
  447. }
  448. }
  449. // Fill in the output parameters
  450. *lphFile = hFile;
  451. *lplpMemory = lpMemory;
  452. return DP_OK;
  453. } // PRV_GetDataBuffer
  454. #undef DPF_MODNAME
  455. #define DPF_MODNAME "PRV_StartReceiveThread"
  456. HRESULT PRV_StartReceiveThread(LPDPLOBBYI_GAMENODE lpgn)
  457. {
  458. HANDLE hReceiveThread = NULL;
  459. HANDLE hKillEvent = NULL;
  460. DWORD dwThreadID;
  461. DPF(7, "Entering PRV_StartReceiveThread");
  462. DPF(9, "Parameters: 0x%08x", lpgn);
  463. ASSERT(lpgn);
  464. // Create the kill event if one doesn't exists
  465. if(!(lpgn->hKillReceiveThreadEvent))
  466. {
  467. hKillEvent = OS_CreateEvent(NULL, FALSE, FALSE, NULL);
  468. if(!hKillEvent)
  469. {
  470. DPF(2, "Unable to create Kill Receive Thread Event");
  471. return DPERR_OUTOFMEMORY;
  472. }
  473. }
  474. // If the Receive Thread isn't going, start it
  475. if(!(lpgn->hReceiveThread))
  476. {
  477. // Spawn off a receive notification thread for the cross-proc communication
  478. hReceiveThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)
  479. PRV_ReceiveClientNotification, lpgn, 0, &dwThreadID);
  480. if(!hReceiveThread)
  481. {
  482. DPF(2, "Unable to create Receive Thread!");
  483. if(hKillEvent)
  484. CloseHandle(hKillEvent);
  485. return DPERR_OUTOFMEMORY;
  486. }
  487. lpgn->hReceiveThread = hReceiveThread;
  488. if(hKillEvent)
  489. lpgn->hKillReceiveThreadEvent = hKillEvent;
  490. }
  491. return DP_OK;
  492. } // PRV_StartReceiveThread
  493. #undef DPF_MODNAME
  494. #define DPF_MODNAME "PRV_SetupAllSharedMemory"
  495. HRESULT PRV_SetupAllSharedMemory(LPDPLOBBYI_GAMENODE lpgn)
  496. {
  497. HRESULT hr;
  498. LPVOID lpConnDataMemory = NULL;
  499. LPVOID lpGameMemory = NULL;
  500. LPVOID lpLobbyMemory = NULL;
  501. HANDLE hFileConnData = NULL;
  502. HANDLE hFileGameWrite = NULL;
  503. HANDLE hFileLobbyWrite = NULL;
  504. DWORD dwError = 0;
  505. DPF(7, "Entering PRV_SetupAllSharedMemory");
  506. DPF(9, "Parameters: 0x%08x", lpgn);
  507. // Get access to the Connection Data File
  508. hr = PRV_GetDataBuffer(lpgn, TYPE_CONNECT_DATA_FILE,
  509. MAX_APPDATABUFFERSIZE,
  510. &hFileConnData, &lpConnDataMemory);
  511. if(FAILED(hr))
  512. {
  513. DPF(5, "Couldn't get access to Connection Data buffer");
  514. goto ERROR_SETUP_SHARED_MEMORY;
  515. }
  516. // Do the same for the Game Write File...
  517. hr = PRV_GetDataBuffer(lpgn, TYPE_GAME_WRITE_FILE,
  518. MAX_APPDATABUFFERSIZE,
  519. &hFileGameWrite, &lpGameMemory);
  520. if(FAILED(hr))
  521. {
  522. DPF(5, "Couldn't get access to Game Write buffer");
  523. goto ERROR_SETUP_SHARED_MEMORY;
  524. }
  525. // Do the same for the Lobby Write File...
  526. hr = PRV_GetDataBuffer(lpgn, TYPE_LOBBY_WRITE_FILE,
  527. MAX_APPDATABUFFERSIZE,
  528. &hFileLobbyWrite, &lpLobbyMemory);
  529. if(FAILED(hr))
  530. {
  531. DPF(5, "Couldn't get access to Lobby Write buffer");
  532. goto ERROR_SETUP_SHARED_MEMORY;
  533. }
  534. // Setup the signalling objects
  535. if(!PRV_SetupClientDataAccess(lpgn))
  536. {
  537. DPF(5, "Unable to create synchronization objects for shared memory!");
  538. return DPERR_OUTOFMEMORY;
  539. }
  540. // Save the file handles
  541. lpgn->hConnectDataFile = hFileConnData;
  542. lpgn->lpConnectDataBuffer = lpConnDataMemory;
  543. lpgn->hGameWriteFile = hFileGameWrite;
  544. lpgn->lpGameWriteBuffer = lpGameMemory;
  545. lpgn->hLobbyWriteFile = hFileLobbyWrite;
  546. lpgn->lpLobbyWriteBuffer = lpLobbyMemory;
  547. // Set the flag that tells us the shared memory files are valid
  548. lpgn->dwFlags |= GN_SHARED_MEMORY_AVAILABLE;
  549. // Start the Receive Thread
  550. hr = PRV_StartReceiveThread(lpgn);
  551. if(FAILED(hr))
  552. {
  553. // In this case, we will keep our shared buffers around. Don't
  554. // worry about cleaning them up here -- we'll probably still need
  555. // them later, and they will get cleaned up later.
  556. DPF(5, "Unable to start receive thread");
  557. return hr;
  558. }
  559. return DP_OK;
  560. ERROR_SETUP_SHARED_MEMORY:
  561. if(hFileConnData)
  562. CloseHandle(hFileConnData);
  563. if(lpConnDataMemory)
  564. UnmapViewOfFile(lpConnDataMemory);
  565. if(hFileGameWrite)
  566. CloseHandle(hFileGameWrite);
  567. if(lpGameMemory)
  568. UnmapViewOfFile(lpGameMemory);
  569. if(hFileLobbyWrite)
  570. CloseHandle(hFileLobbyWrite);
  571. if(lpLobbyMemory)
  572. UnmapViewOfFile(lpLobbyMemory);
  573. return hr;
  574. } // PRV_SetupAllSharedMemory
  575. #undef DPF_MODNAME
  576. #define DPF_MODNAME "PRV_EnterConnSettingsWaitMode"
  577. void PRV_EnterConnSettingsWaitMode(LPDPLOBBYI_GAMENODE lpgn)
  578. {
  579. LPDPLOBBYI_CONNCONTROL lpConnControl = NULL;
  580. LPDPLOBBYI_BUFFERCONTROL lpBufferControl = NULL;
  581. DPF(7, "Entering PRV_EnterConnSettingsWaitMode");
  582. DPF(9, "Parameters: 0x%08x", lpgn);
  583. ASSERT(lpgn);
  584. // Set the flag in the ConnSettings buffer
  585. WaitForSingleObject(lpgn->hConnectDataMutex, INFINITE);
  586. lpConnControl = (LPDPLOBBYI_CONNCONTROL)lpgn->lpConnectDataBuffer;
  587. lpConnControl->dwFlags |= BC_WAIT_MODE;
  588. ReleaseMutex(lpgn->hConnectDataMutex);
  589. // Set the flag in the GameWrite buffer
  590. WaitForSingleObject(lpgn->hGameWriteMutex, INFINITE);
  591. lpBufferControl = (LPDPLOBBYI_BUFFERCONTROL)lpgn->lpGameWriteBuffer;
  592. lpBufferControl->dwFlags |= BC_WAIT_MODE;
  593. ReleaseMutex(lpgn->hGameWriteMutex);
  594. // Set the flag in the LobbyWrite buffer
  595. WaitForSingleObject(lpgn->hLobbyWriteMutex, INFINITE);
  596. lpBufferControl = (LPDPLOBBYI_BUFFERCONTROL)lpgn->lpLobbyWriteBuffer;
  597. lpBufferControl->dwFlags |= BC_WAIT_MODE;
  598. ReleaseMutex(lpgn->hLobbyWriteMutex);
  599. } // PRV_EnterConnSettingsWaitMode
  600. #undef DPF_MODNAME
  601. #define DPF_MODNAME "PRV_LeaveConnSettingsWaitMode"
  602. void PRV_LeaveConnSettingsWaitMode(LPDPLOBBYI_GAMENODE lpgn)
  603. {
  604. LPDPLOBBYI_CONNCONTROL lpConnControl = NULL;
  605. LPDPLOBBYI_BUFFERCONTROL lpBufferControl = NULL;
  606. DPF(7, "Entering PRV_LeaveConnSettingsWaitMode");
  607. DPF(9, "Parameters: 0x%08x", lpgn);
  608. ASSERT(lpgn);
  609. // Clear the flag in the ConnSettings buffer
  610. WaitForSingleObject(lpgn->hConnectDataMutex, INFINITE);
  611. lpConnControl = (LPDPLOBBYI_CONNCONTROL)lpgn->lpConnectDataBuffer;
  612. lpConnControl->dwFlags &= ~(BC_WAIT_MODE | BC_PENDING_CONNECT);
  613. ReleaseMutex(lpgn->hConnectDataMutex);
  614. // Clear the flag in the GameWrite buffer
  615. WaitForSingleObject(lpgn->hGameWriteMutex, INFINITE);
  616. lpBufferControl = (LPDPLOBBYI_BUFFERCONTROL)lpgn->lpGameWriteBuffer;
  617. lpBufferControl->dwFlags &= ~BC_WAIT_MODE;
  618. ReleaseMutex(lpgn->hGameWriteMutex);
  619. // Clear the flag in the LobbyWrite buffer
  620. WaitForSingleObject(lpgn->hLobbyWriteMutex, INFINITE);
  621. lpBufferControl = (LPDPLOBBYI_BUFFERCONTROL)lpgn->lpLobbyWriteBuffer;
  622. lpBufferControl->dwFlags &= ~BC_WAIT_MODE;
  623. ReleaseMutex(lpgn->hLobbyWriteMutex);
  624. } // PRV_LeaveConnSettingsWaitMode
  625. #undef DPF_MODNAME
  626. #define DPF_MODNAME "PRV_WriteClientData"
  627. HRESULT PRV_WriteClientData(LPDPLOBBYI_GAMENODE lpgn, DWORD dwFlags,
  628. LPVOID lpData, DWORD dwSize)
  629. {
  630. LPDPLOBBYI_BUFFERCONTROL lpControl = NULL;
  631. LPDPLOBBYI_MESSAGEHEADER lpHeader = NULL;
  632. HANDLE hMutex = NULL;
  633. DWORD dwSizeToEnd = 0;
  634. LPBYTE lpTemp = NULL;
  635. HRESULT hr = DP_OK;
  636. DWORD dwReadOffset;
  637. DWORD dwWriteOffset;
  638. DWORD dwBufferSize;
  639. DPF(7, "Entering PRV_WriteClientData");
  640. DPF(9, "Parameters: 0x%08x, 0x%08x, 0x%08x, %lu",
  641. lpgn, dwFlags, lpData, dwSize);
  642. // Make sure we have a valid shared memory buffer
  643. // Note: Take the GameNode lock so that nobody changes the flags
  644. // for the buffers, or the buffers themselves out from under us.
  645. ENTER_DPLGAMENODE();
  646. if(!(lpgn->dwFlags & GN_SHARED_MEMORY_AVAILABLE))
  647. {
  648. hr = PRV_SetupAllSharedMemory(lpgn);
  649. if(FAILED(hr))
  650. {
  651. LEAVE_DPLGAMENODE();
  652. DPF(2, "Unable to access App Data memory");
  653. return hr;
  654. }
  655. }
  656. LEAVE_DPLGAMENODE();
  657. // Grab the mutex
  658. hMutex = (lpgn->dwFlags & GN_LOBBY_CLIENT) ?
  659. (lpgn->hLobbyWriteMutex) : (lpgn->hGameWriteMutex);
  660. WaitForSingleObject(hMutex, INFINITE);
  661. // Get a pointer to our control structure
  662. lpControl = (LPDPLOBBYI_BUFFERCONTROL)((lpgn->dwFlags &
  663. GN_LOBBY_CLIENT) ? (lpgn->lpLobbyWriteBuffer)
  664. : (lpgn->lpGameWriteBuffer));
  665. // If we're in wait mode, bail
  666. if(lpControl->dwFlags & BC_WAIT_MODE)
  667. {
  668. DPF_ERR("Cannot send lobby message while in Wait Mode for new ConnectionSettings");
  669. hr = DPERR_UNAVAILABLE;
  670. goto EXIT_WRITE_CLIENT_DATA;
  671. }
  672. // If we are the game, check to see if the lobby client is even there. In
  673. // the self-lobbied case, it won't be. If it is not there, don't even
  674. // bother sending anything.
  675. if((!(lpgn->dwFlags & GN_LOBBY_CLIENT)) && (!(lpControl->dwFlags
  676. & BC_LOBBY_ACTIVE)))
  677. {
  678. DPF(5, "There is not active lobby client; Not sending message");
  679. hr = DPERR_UNAVAILABLE;
  680. goto EXIT_WRITE_CLIENT_DATA;
  681. }
  682. // Make sure there is enough space left for the message and two dwords
  683. if(lpControl->dwBufferLeft < (dwSize + sizeof(DPLOBBYI_MESSAGEHEADER)))
  684. {
  685. DPF(5, "Not enough space left in the message buffer");
  686. hr = DPERR_BUFFERTOOSMALL;
  687. goto EXIT_WRITE_CLIENT_DATA;
  688. }
  689. if(lpControl->dwBufferLeft > MAX_APPDATABUFFERSIZE-(sizeof(DPLOBBYI_BUFFERCONTROL)))
  690. {
  691. DPF(4,"SECURITY WARN: invalid amount of buffer left in write buffer");
  692. hr=DPERR_UNAVAILABLE;
  693. goto EXIT_WRITE_CLIENT_DATA;
  694. }
  695. // SECURITY: need to snapshot these so they aren't
  696. // altered by attacking code during processing
  697. dwReadOffset = lpControl->dwReadOffset;
  698. dwWriteOffset = lpControl->dwWriteOffset;
  699. dwBufferSize = lpControl->dwBufferSize;
  700. if(dwReadOffset >= MAX_APPDATABUFFERSIZE || dwWriteOffset >= MAX_APPDATABUFFERSIZE)
  701. {
  702. DPF(4,"SECURITY WARN: invalid offsets found in shared memory control block, bailing");
  703. hr=DPERR_UNAVAILABLE;
  704. goto EXIT_WRITE_CLIENT_DATA;
  705. }
  706. if(dwBufferSize != MAX_APPDATABUFFERSIZE)
  707. {
  708. DPF(4,"SECURITY WARN: shared memory control block buffer size tampered with, bailing");
  709. hr=DPERR_UNAVAILABLE;
  710. goto EXIT_WRITE_CLIENT_DATA;
  711. }
  712. // Copy in the data. First make sure we can write from the cursor
  713. // forward without having to wrap around to the beginning of the buffer,
  714. // but make sure we don't write past the read cursor
  715. if(dwWriteOffset >= dwReadOffset)
  716. {
  717. // Our write pointer is ahead of our read pointer (cool). Figure
  718. // out if we have enough room between our write pointer and the
  719. // end of the buffer. If we do, then just write it. If we don't
  720. // we need to wrap it.
  721. dwSizeToEnd = dwBufferSize - dwWriteOffset;
  722. if(dwSizeToEnd >= (dwSize + sizeof(DPLOBBYI_MESSAGEHEADER)))
  723. {
  724. // We have enough room
  725. lpHeader = (LPDPLOBBYI_MESSAGEHEADER)((LPBYTE)lpControl
  726. + dwWriteOffset);
  727. lpHeader->dwSize = dwSize;
  728. lpHeader->dwFlags = dwFlags;
  729. lpTemp = (LPBYTE)(++lpHeader);
  730. memcpySecureD(lpTemp, lpData, dwSize,
  731. lpControl, MAX_APPDATABUFFERSIZE,
  732. "SECURITY WARN: shared memory was about to write outside shared buffer, aborting",
  733. hr=DPERR_UNAVAILABLE,
  734. EXIT_WRITE_CLIENT_DATA);
  735. // Move the write cursor, and check to see if we have enough
  736. // room for the header on the next message. If the move causes
  737. // us to wrap, or if we are within one header's size,
  738. // we need to move the write cursor back to the beginning
  739. // of the buffer
  740. dwWriteOffset += dwSize + sizeof(DPLOBBYI_MESSAGEHEADER);
  741. if(dwWriteOffset > (dwBufferSize -
  742. sizeof(DPLOBBYI_MESSAGEHEADER)))
  743. {
  744. // Increment the amount of free buffer by the amount we
  745. // are about to skip over to wrap
  746. lpControl->dwBufferLeft -= (lpControl->dwBufferSize -
  747. dwWriteOffset);
  748. // We're closer than one header's size
  749. dwWriteOffset = sizeof(DPLOBBYI_BUFFERCONTROL);
  750. }
  751. }
  752. else
  753. {
  754. // We don't have enough room before the end, so we need to
  755. // wrap the message (ugh). Here's the rules:
  756. // 1. If we don't have enough bytes for the header, start
  757. // the whole thing at the beginning of the buffer
  758. // 2. If we have enough bytes, write as much
  759. // as we can and wrap the rest.
  760. if(dwSizeToEnd < sizeof(DPLOBBYI_MESSAGEHEADER))
  761. {
  762. // We don't even have room for our two dwords, so wrap
  763. // the whole thing. So first decrement the amount of
  764. // free memory left and make sure we will still fit
  765. lpControl->dwBufferLeft -= dwSizeToEnd;
  766. if(lpControl->dwBufferLeft < (dwSize +
  767. sizeof(DPLOBBYI_MESSAGEHEADER)))
  768. {
  769. DPF(5, "Not enough space left in the message buffer");
  770. hr = DPERR_BUFFERTOOSMALL;
  771. goto EXIT_WRITE_CLIENT_DATA;
  772. }
  773. // Reset the write pointer and copy
  774. lpHeader = (LPDPLOBBYI_MESSAGEHEADER)((LPBYTE)lpControl +
  775. sizeof(DPLOBBYI_BUFFERCONTROL));
  776. lpHeader->dwSize = dwSize;
  777. lpHeader->dwFlags = dwFlags;
  778. lpTemp = (LPBYTE)(++lpHeader);
  779. memcpySecureD(lpTemp, lpData, dwSize,
  780. lpControl, MAX_APPDATABUFFERSIZE,
  781. "SECURITY WARN: shared memory was about to write outside shared buffer, aborting",
  782. hr=DPERR_UNAVAILABLE,
  783. EXIT_WRITE_CLIENT_DATA);
  784. // Move the write cursor
  785. dwWriteOffset += sizeof(DPLOBBYI_BUFFERCONTROL) +
  786. (dwSize + sizeof(DPLOBBYI_MESSAGEHEADER));
  787. }
  788. else
  789. {
  790. // We at least have enough room for the two dwords
  791. lpHeader = (LPDPLOBBYI_MESSAGEHEADER)((LPBYTE)lpControl
  792. + dwWriteOffset);
  793. lpHeader->dwSize = dwSize;
  794. lpHeader->dwFlags = dwFlags;
  795. // Now figure out how much we can write
  796. lpTemp = (LPBYTE)(++lpHeader);
  797. dwSizeToEnd -= sizeof(DPLOBBYI_MESSAGEHEADER);
  798. if(!dwSizeToEnd)
  799. {
  800. // We need to wrap to write the whole message
  801. lpTemp = (LPBYTE)lpControl + sizeof(DPLOBBYI_BUFFERCONTROL);
  802. memcpySecureD(lpTemp, lpData, dwSize,
  803. lpControl, MAX_APPDATABUFFERSIZE,
  804. "SECURITY WARN: shared memory was about to write outside shared buffer, aborting",
  805. hr=DPERR_UNAVAILABLE,
  806. EXIT_WRITE_CLIENT_DATA);
  807. // Move the write cursor
  808. dwWriteOffset = sizeof(DPLOBBYI_BUFFERCONTROL)
  809. + dwSize;
  810. }
  811. else
  812. {
  813. memcpySecureD(lpTemp, lpData, dwSizeToEnd,
  814. lpControl, MAX_APPDATABUFFERSIZE,
  815. "SECURITY WARN: shared memory was about to write outside shared buffer, aborting",
  816. hr=DPERR_UNAVAILABLE,
  817. EXIT_WRITE_CLIENT_DATA);
  818. // Move both pointers and finish the job
  819. lpTemp = (LPBYTE)lpControl + sizeof(DPLOBBYI_BUFFERCONTROL);
  820. memcpySecureD(lpTemp, ((LPBYTE)lpData+dwSizeToEnd), (dwSize-dwSizeToEnd),
  821. lpControl, MAX_APPDATABUFFERSIZE,
  822. "SECURITY WARN: shared memory was about to write outside shared buffer, aborting",
  823. hr=DPERR_UNAVAILABLE,
  824. EXIT_WRITE_CLIENT_DATA);
  825. // Move the write cursor
  826. dwWriteOffset = sizeof(DPLOBBYI_BUFFERCONTROL)
  827. + (dwSize - dwSizeToEnd);
  828. }
  829. }
  830. }
  831. }
  832. else
  833. {
  834. // Our read pointer is ahead of our write pointer. Since we checked
  835. // and found there is enough room to write, we should just be able
  836. // to just slam this guy in.
  837. lpHeader = (LPDPLOBBYI_MESSAGEHEADER)((LPBYTE)lpControl +
  838. dwWriteOffset);
  839. lpHeader->dwSize = dwSize;
  840. lpHeader->dwFlags = dwFlags;
  841. lpTemp = (LPBYTE)(++lpHeader);
  842. memcpySecureD(lpTemp, lpData, dwSize,
  843. lpControl, MAX_APPDATABUFFERSIZE,
  844. "SECURITY WARN: shared memory was about to write outside shared buffer, aborting",
  845. hr=DPERR_UNAVAILABLE,
  846. EXIT_WRITE_CLIENT_DATA);
  847. // Move the write cursor
  848. dwWriteOffset += dwSize + sizeof(DPLOBBYI_MESSAGEHEADER);
  849. }
  850. lpControl->dwWriteOffset = dwWriteOffset;
  851. // Decrement the amount of free space left and increment the message count
  852. lpControl->dwBufferLeft -= (dwSize + sizeof(DPLOBBYI_MESSAGEHEADER));
  853. lpControl->dwMessages++;
  854. // Signal the other user that we have written something
  855. SetEvent((lpgn->dwFlags & GN_LOBBY_CLIENT) ?
  856. (lpgn->hLobbyWriteEvent) : (lpgn->hGameWriteEvent));
  857. // Fall through
  858. EXIT_WRITE_CLIENT_DATA:
  859. // Release the mutex
  860. ReleaseMutex(hMutex);
  861. return hr;
  862. } // PRV_WriteClientData
  863. #undef DPF_MODNAME
  864. #define DPF_MODNAME "PRV_SendStandardSystemMessage"
  865. HRESULT PRV_SendStandardSystemMessage(LPDIRECTPLAYLOBBY lpDPL,
  866. DWORD dwMessage, DWORD dwGameID)
  867. {
  868. LPDPLOBBYI_DPLOBJECT this = NULL;
  869. LPDPLOBBYI_GAMENODE lpgn = NULL;
  870. HRESULT hr;
  871. DWORD dwMessageSize;
  872. LPVOID lpmsg = NULL;
  873. DWORD dwFlags;
  874. DPF(7, "Entering PRV_SendStandardSystemMessage");
  875. DPF(9, "Parameters: 0x%08x, %lu, %lu",
  876. lpDPL, dwMessage, dwGameID);
  877. ENTER_DPLOBBY();
  878. TRY
  879. {
  880. if( !VALID_DPLOBBY_INTERFACE( lpDPL ))
  881. {
  882. LEAVE_DPLOBBY();
  883. return DPERR_INVALIDINTERFACE;
  884. }
  885. this = DPLOBJECT_FROM_INTERFACE(lpDPL);
  886. if( !VALID_DPLOBBY_PTR( this ) )
  887. {
  888. LEAVE_DPLOBBY();
  889. return DPERR_INVALIDOBJECT;
  890. }
  891. }
  892. EXCEPT( EXCEPTION_EXECUTE_HANDLER )
  893. {
  894. LEAVE_DPLOBBY();
  895. DPF_ERR( "Exception encountered validating parameters" );
  896. return DPERR_INVALIDPARAMS;
  897. }
  898. // If dwGameID is zero it means that we are the game, so
  899. // we need to get the current process ID. Otherwise, it
  900. // means we are the lobby client
  901. if(!dwGameID)
  902. dwGameID = GetCurrentProcessId();
  903. // Now find the correct game node
  904. lpgn = PRV_GetGameNode(this->lpgnHead, dwGameID);
  905. if(!lpgn)
  906. {
  907. if(FAILED(PRV_AddNewGameNode(this, &lpgn, dwGameID, NULL, FALSE,NULL)))
  908. {
  909. LEAVE_DPLOBBY();
  910. return DPERR_OUTOFMEMORY;
  911. }
  912. }
  913. // Get the size of the message
  914. switch(dwMessage)
  915. {
  916. case DPLSYS_NEWSESSIONHOST:
  917. dwMessageSize = sizeof(DPLMSG_NEWSESSIONHOST);
  918. break;
  919. default:
  920. dwMessageSize = sizeof(DPLMSG_SYSTEMMESSAGE);
  921. break;
  922. }
  923. // Allocate a buffer for the message
  924. lpmsg = DPMEM_ALLOC(dwMessageSize);
  925. if(!lpmsg)
  926. {
  927. LEAVE_DPLOBBY();
  928. DPF_ERRVAL("Unable to allocate memory for lobby system message, dwMessage = %lu", dwMessage);
  929. return DPERR_OUTOFMEMORY;
  930. }
  931. // Setup the message
  932. ((LPDPLMSG_SYSTEMMESSAGE)lpmsg)->dwType = dwMessage;
  933. ((LPDPLMSG_SYSTEMMESSAGE)lpmsg)->guidInstance = lpgn->guidInstance;
  934. // Write into the shared buffer
  935. dwFlags = DPLMSG_SYSTEM | DPLMSG_STANDARD;
  936. hr = PRV_WriteClientData(lpgn, dwFlags, lpmsg, dwMessageSize);
  937. if(FAILED(hr))
  938. {
  939. DPF(8, "Couldn't send system message");
  940. }
  941. // Free our buffer
  942. DPMEM_FREE(lpmsg);
  943. LEAVE_DPLOBBY();
  944. return hr;
  945. } // PRV_SendStandardSystemMessage
  946. #undef DPF_MODNAME
  947. #define DPF_MODNAME "PRV_AddNewRequestNode"
  948. HRESULT PRV_AddNewRequestNode(LPDPLOBBYI_DPLOBJECT this,
  949. LPDPLOBBYI_GAMENODE lpgn, LPDPLMSG_GENERIC lpmsg, BOOL bSlamGuid)
  950. {
  951. LPDPLOBBYI_REQUESTNODE lprn = NULL;
  952. // Allocate memory for a Request Node
  953. lprn = DPMEM_ALLOC(sizeof(DPLOBBYI_REQUESTNODE));
  954. if(!lprn)
  955. {
  956. DPF_ERR("Unable to allocate memory for request node, system message not sent");
  957. return DPERR_OUTOFMEMORY;
  958. }
  959. // Setup the request node
  960. lprn->dwFlags = lpgn->dwFlags;
  961. lprn->dwRequestID = this->dwCurrentRequest;
  962. lprn->dwAppRequestID = ((LPDPLMSG_GETPROPERTY)lpmsg)->dwRequestID;
  963. lprn->lpgn = lpgn;
  964. // Add the slammed guid flag if needed
  965. if(bSlamGuid)
  966. lprn->dwFlags |= GN_SLAMMED_GUID;
  967. // Change the request ID in the message to our internal one (we'll
  968. // change it back on Receive
  969. ((LPDPLMSG_GETPROPERTY)lpmsg)->dwRequestID = this->dwCurrentRequest++;
  970. // Add the node to the list
  971. if(this->lprnHead)
  972. this->lprnHead->lpPrev = lprn;
  973. lprn->lpNext = this->lprnHead;
  974. this->lprnHead = lprn;
  975. return DP_OK;
  976. } // PRV_AddNewRequestNode
  977. #undef DPF_MODNAME
  978. #define DPF_MODNAME "PRV_RemoveRequestNode"
  979. void PRV_RemoveRequestNode(LPDPLOBBYI_DPLOBJECT this,
  980. LPDPLOBBYI_REQUESTNODE lprn)
  981. {
  982. // If we're the head, move it
  983. if(lprn == this->lprnHead)
  984. this->lprnHead = lprn->lpNext;
  985. // Fixup the previous & next pointers
  986. if(lprn->lpPrev)
  987. lprn->lpPrev->lpNext = lprn->lpNext;
  988. if(lprn->lpNext)
  989. lprn->lpNext->lpPrev = lprn->lpPrev;
  990. // Free the node
  991. DPMEM_FREE(lprn);
  992. } // PRV_RemoveRequestNode
  993. #undef DPF_MODNAME
  994. #define DPF_MODNAME "PRV_ForwardMessageToLobbyServer"
  995. HRESULT PRV_ForwardMessageToLobbyServer(LPDPLOBBYI_GAMENODE lpgn,
  996. LPVOID lpBuffer, DWORD dwSize, BOOL bStandard)
  997. {
  998. LPDPLOBBYI_DPLOBJECT this;
  999. LPDPLMSG_GENERIC lpmsg = NULL;
  1000. HRESULT hr;
  1001. BOOL bSlamGuid = FALSE;
  1002. DPF(7, "Entering PRV_ForwardMessageToLobbyServer");
  1003. DPF(9, "Parameters: 0x%08x, 0x%08x, %lu, %lu",
  1004. lpgn, lpBuffer, dwSize, bStandard);
  1005. TRY
  1006. {
  1007. // Validate the dplay object
  1008. hr = VALID_DPLAY_PTR( lpgn->lpDPlayObject );
  1009. if (FAILED(hr))
  1010. {
  1011. DPF_ERRVAL("bad dplay ptr - hr = 0x%08lx\n",hr);
  1012. return hr;
  1013. }
  1014. // Validate the lobby object
  1015. this = lpgn->lpDPlayObject->lpLobbyObject;
  1016. if( !VALID_DPLOBBY_PTR( this ) )
  1017. {
  1018. DPF_ERR("Invalid lobby object");
  1019. return DPERR_INVALIDOBJECT;
  1020. }
  1021. }
  1022. EXCEPT( EXCEPTION_EXECUTE_HANDLER )
  1023. {
  1024. DPF_ERR( "Exception encountered validating parameters" );
  1025. return DPERR_INVALIDPARAMS;
  1026. }
  1027. // If this is a property request, we need to create a request node
  1028. lpmsg = (LPDPLMSG_GENERIC)lpBuffer;
  1029. if(bStandard)
  1030. {
  1031. // If it's a property message, we need a request node
  1032. switch(lpmsg->dwType)
  1033. {
  1034. case DPLSYS_GETPROPERTY:
  1035. {
  1036. LPDPLMSG_GETPROPERTY lpgp = lpBuffer;
  1037. // If it's a GETPROPERTY message, we need to check to see if
  1038. // the player guid is NULL. If it is, we need to
  1039. // stuff the game's Instance guid in that field
  1040. if(IsEqualGUID(&lpgp->guidPlayer, &GUID_NULL))
  1041. {
  1042. // Stuff the instance guid of the game
  1043. lpgp->guidPlayer = lpgn->guidInstance;
  1044. bSlamGuid = TRUE;
  1045. }
  1046. // Add a request node to the pending requests list
  1047. hr = PRV_AddNewRequestNode(this, lpgn, lpmsg, bSlamGuid);
  1048. if(FAILED(hr))
  1049. {
  1050. DPF_ERRVAL("Unable to add request node to list, hr = 0x%08x", hr);
  1051. return hr;
  1052. }
  1053. break;
  1054. }
  1055. case DPLSYS_SETPROPERTY:
  1056. {
  1057. LPDPLMSG_SETPROPERTY lpsp = lpBuffer;
  1058. // If it's a SETPROPERTY message, we need to check to see if
  1059. // the player guid is NULL. If it is, we need to
  1060. // stuff the game's Instance guid in that field
  1061. if(IsEqualGUID(&lpsp->guidPlayer, &GUID_NULL))
  1062. {
  1063. // Stuff the instance guid of the game
  1064. lpsp->guidPlayer = lpgn->guidInstance;
  1065. bSlamGuid = TRUE;
  1066. }
  1067. // If the request ID is zero, we don't need to swap
  1068. // the ID's or add a pending request
  1069. if(lpsp->dwRequestID != 0)
  1070. {
  1071. // Add a request node to the pending requests list
  1072. hr = PRV_AddNewRequestNode(this, lpgn, lpmsg, bSlamGuid);
  1073. if(FAILED(hr))
  1074. {
  1075. DPF_ERRVAL("Unable to add request node to list, hr = 0x%08x", hr);
  1076. return hr;
  1077. }
  1078. }
  1079. break;
  1080. }
  1081. case DPLSYS_NEWSESSIONHOST:
  1082. ((LPDPLMSG_NEWSESSIONHOST)lpBuffer)->guidInstance = lpgn->guidInstance;
  1083. break;
  1084. default:
  1085. break;
  1086. }
  1087. }
  1088. // Call Send on the lobby object
  1089. hr = PRV_Send(this, lpgn->dpidPlayer, DPID_SERVERPLAYER,
  1090. DPSEND_LOBBYSYSTEMMESSAGE, lpBuffer, dwSize);
  1091. if(FAILED(hr))
  1092. {
  1093. DPF_ERRVAL("Failed sending lobby message, hr = 0x%08x", hr);
  1094. }
  1095. return hr;
  1096. } // PRV_ForwardMessageToLobbyServer
  1097. #undef DPF_MODNAME
  1098. #define DPF_MODNAME "PRV_InjectMessageInQueue"
  1099. HRESULT PRV_InjectMessageInQueue(LPDPLOBBYI_GAMENODE lpgn, DWORD dwFlags,
  1100. LPVOID lpData, DWORD dwSize, BOOL bForward)
  1101. {
  1102. LPDPLOBBYI_MESSAGE lpm = NULL;
  1103. LPVOID lpBuffer = NULL;
  1104. HRESULT hr;
  1105. DPF(7, "Entering PRV_InjectMessageInQueue");
  1106. DPF(9, "Parameters: 0x%08x, 0x%08x, 0x%08x, %lu, %lu",
  1107. lpgn, dwFlags, lpData, dwSize, bForward);
  1108. ASSERT(lpData);
  1109. // Allocate memory for the node and the data buffer
  1110. lpm = DPMEM_ALLOC(sizeof(DPLOBBYI_MESSAGE));
  1111. lpBuffer = DPMEM_ALLOC(dwSize);
  1112. if((!lpm) || (!lpBuffer))
  1113. {
  1114. DPF_ERR("Unable to allocate memory for system message");
  1115. if(lpm)
  1116. DPMEM_FREE(lpm);
  1117. if(lpBuffer)
  1118. DPMEM_FREE(lpBuffer);
  1119. return DPERR_OUTOFMEMORY;
  1120. }
  1121. // Copy the data
  1122. memcpy(lpBuffer, lpData, dwSize);
  1123. // Before we put it in our own queue, forward it onto the lobby server
  1124. // if there is one.
  1125. if(bForward && (lpgn->dwFlags & GN_CLIENT_LAUNCHED))
  1126. {
  1127. hr = PRV_ForwardMessageToLobbyServer(lpgn, lpData, dwSize, FALSE);
  1128. if(FAILED(hr))
  1129. {
  1130. DPF_ERRVAL("Failed forwarding system message to lobby server, hr = 0x%08x", hr);
  1131. }
  1132. }
  1133. // Save the data pointer & the external flags
  1134. // Note: If we're injecting this, it has to be a system message,
  1135. // so set the flag just in case we forgot elsewhere.
  1136. lpm->dwFlags = (dwFlags | DPLAD_SYSTEM);
  1137. lpm->dwSize = dwSize;
  1138. lpm->lpData = lpBuffer;
  1139. // Add the message to the end of the queue & increment the count
  1140. ENTER_DPLQUEUE();
  1141. lpm->lpPrev = lpgn->MessageHead.lpPrev;
  1142. lpgn->MessageHead.lpPrev->lpNext = lpm;
  1143. lpgn->MessageHead.lpPrev = lpm;
  1144. lpm->lpNext = &lpgn->MessageHead;
  1145. lpgn->dwMessageCount++;
  1146. LEAVE_DPLQUEUE();
  1147. // Kick the event handle
  1148. if(lpgn->hDupReceiveEvent)
  1149. {
  1150. SetEvent(lpgn->hDupReceiveEvent);
  1151. }
  1152. return DP_OK;
  1153. } // PRV_InjectMessageInQueue
  1154. #undef DPF_MODNAME
  1155. #define DPF_MODNAME "PRV_ReadClientData"
  1156. HRESULT PRV_ReadClientData(LPDPLOBBYI_GAMENODE lpgn, LPDWORD lpdwFlags,
  1157. LPVOID lpData, LPDWORD lpdwDataSize)
  1158. {
  1159. LPDPLOBBYI_BUFFERCONTROL lpControl = NULL;
  1160. LPDPLOBBYI_MESSAGEHEADER lpHeader = NULL;
  1161. DWORD dwSize = 0;
  1162. DWORD_PTR dwSizeToEnd = 0;
  1163. HANDLE hMutex = NULL;
  1164. LPBYTE lpTemp = NULL;
  1165. LPBYTE lpEnd = NULL;
  1166. HRESULT hr = DP_OK;
  1167. DWORD dwReadOffset;
  1168. DWORD dwBufferSize;
  1169. DPF(7, "Entering PRV_ReadClientData");
  1170. DPF(9, "Parameters: 0x%08x, 0x%08x, 0x%08x, 0x%08x",
  1171. lpgn, lpdwFlags, lpData, lpdwDataSize);
  1172. // Make sure we have a valid shared memory buffer
  1173. // Note: Take the GameNode lock so that nobody changes the flags
  1174. // for the buffers, or the buffers themselves out from under us.
  1175. ENTER_DPLGAMENODE();
  1176. if(!(lpgn->dwFlags & GN_SHARED_MEMORY_AVAILABLE))
  1177. {
  1178. hr = PRV_SetupAllSharedMemory(lpgn);
  1179. if(FAILED(hr))
  1180. {
  1181. LEAVE_DPLGAMENODE();
  1182. DPF(2, "Unable to access App Data memory");
  1183. return hr;
  1184. }
  1185. }
  1186. LEAVE_DPLGAMENODE();
  1187. // Grab the mutex
  1188. // REVIEW!!!! -- Is there anything that might cause this wait to hang????
  1189. hMutex = (lpgn->dwFlags & GN_LOBBY_CLIENT) ?
  1190. (lpgn->hGameWriteMutex) : (lpgn->hLobbyWriteMutex);
  1191. WaitForSingleObject(hMutex, INFINITE);
  1192. // Get a pointer to our control structure
  1193. lpControl = (LPDPLOBBYI_BUFFERCONTROL)((lpgn->dwFlags &
  1194. GN_LOBBY_CLIENT) ? (lpgn->lpGameWriteBuffer)
  1195. : (lpgn->lpLobbyWriteBuffer));
  1196. // Make sure there are any messages in the buffer
  1197. if(!lpControl->dwMessages)
  1198. {
  1199. DPF(8, "No messages in shared buffer");
  1200. hr = DPERR_NOMESSAGES;
  1201. goto EXIT_READ_CLIENT_DATA;
  1202. }
  1203. // SECURITY need to snapshot these values so hackers
  1204. // can't change in the middle of accessing shared object.
  1205. dwReadOffset=lpControl->dwReadOffset;
  1206. dwBufferSize=lpControl->dwBufferSize;
  1207. // Make sure there is enough space for the message
  1208. lpHeader = (LPDPLOBBYI_MESSAGEHEADER)((LPBYTE)lpControl
  1209. + dwReadOffset);
  1210. if(lpControl->dwReadOffset >= MAX_APPDATABUFFERSIZE)
  1211. {
  1212. DPF(4,"SECURITY WARN: local application hacking shared memory, lobby connection broken");
  1213. hr=DPERR_NOMESSAGES;
  1214. goto EXIT_READ_CLIENT_DATA;
  1215. }
  1216. dwSize = lpHeader->dwSize;
  1217. if(dwSize > MAX_APPDATABUFFERSIZE-(sizeof(DPLOBBYI_BUFFERCONTROL)+sizeof(DPLOBBYI_MESSAGEHEADER))){
  1218. DPF(4,"SECURITY WARN: lobby message size %d is too large",lpHeader->dwSize);
  1219. hr=DPERR_NOMESSAGES;
  1220. goto EXIT_READ_CLIENT_DATA;
  1221. }
  1222. // Set the output data size (even if we fail, we want to return it)
  1223. if(lpdwDataSize)
  1224. *lpdwDataSize = dwSize;
  1225. if((!lpData) || (dwSize > *lpdwDataSize))
  1226. {
  1227. DPF(8, "Message buffer is too small, must be at least %d bytes", dwSize);
  1228. hr = DPERR_BUFFERTOOSMALL;
  1229. goto EXIT_READ_CLIENT_DATA;
  1230. }
  1231. // Set the output flags
  1232. if(lpdwFlags)
  1233. *lpdwFlags = lpHeader->dwFlags;
  1234. if(dwBufferSize != MAX_APPDATABUFFERSIZE) // note, makes storing this value in header redundant.
  1235. {
  1236. DPF(4, "SECURITY WARN: shared app memory has been tampered with");
  1237. hr = DPERR_NOMESSAGES;
  1238. goto EXIT_READ_CLIENT_DATA;
  1239. }
  1240. // Now check and see if we are going to wrap. If we are, some of the message
  1241. // will be at the end of the buffer, some will be at the beginning.
  1242. lpTemp = (LPBYTE)(++lpHeader) + dwSize;
  1243. if(lpTemp > ((LPBYTE)lpControl + dwBufferSize))
  1244. {
  1245. // Figure out where we need to wrap
  1246. dwSizeToEnd = ((LPBYTE)lpControl + dwBufferSize)
  1247. - (LPBYTE)(lpHeader);
  1248. if(!dwSizeToEnd)
  1249. {
  1250. // We are at the end, so the whole message must be at the
  1251. // beginning of the buffer
  1252. lpTemp = (LPBYTE)lpControl + sizeof(DPLOBBYI_BUFFERCONTROL);
  1253. memcpy(lpData, lpTemp, dwSize);
  1254. // Move the read cursor
  1255. dwReadOffset = sizeof(DPLOBBYI_BUFFERCONTROL) + dwSize;
  1256. }
  1257. else
  1258. {
  1259. // Copy the first part of the data
  1260. lpTemp = (LPBYTE)lpHeader;
  1261. memcpy(lpData, lpTemp, (DWORD)dwSizeToEnd);
  1262. // Move the read cursor and copy the rest
  1263. lpTemp = (LPBYTE)lpControl + sizeof(DPLOBBYI_BUFFERCONTROL);
  1264. memcpy(((LPBYTE)lpData + dwSizeToEnd), lpTemp,
  1265. (DWORD)(dwSize - dwSizeToEnd));
  1266. // Move the read pointer
  1267. dwReadOffset = (DWORD)(sizeof(DPLOBBYI_BUFFERCONTROL)
  1268. + (dwSize - dwSizeToEnd));
  1269. }
  1270. }
  1271. else
  1272. {
  1273. // We don't have to wrap (cool).
  1274. lpTemp = (LPBYTE)lpHeader;
  1275. memcpy(lpData, lpTemp, dwSize);
  1276. // Move the read pointer. If there are less than 8 bytes left in the
  1277. // buffer, we should move the read pointer to the beginning. We need
  1278. // to add however many bytes we skip (at the end) back into our free
  1279. // buffer memory counter.
  1280. lpTemp += dwSize;
  1281. lpEnd = (LPBYTE)lpControl + dwBufferSize;
  1282. if(lpTemp > (lpEnd - sizeof(DPLOBBYI_MESSAGEHEADER)))
  1283. {
  1284. // Move the read cursor to the beginning
  1285. dwReadOffset = sizeof(DPLOBBYI_BUFFERCONTROL);
  1286. // Add the number of bytes to the free buffer total
  1287. lpControl->dwBufferLeft += (DWORD)(lpEnd - lpTemp);
  1288. }
  1289. else
  1290. dwReadOffset += (DWORD)(dwSize + sizeof(DPLOBBYI_MESSAGEHEADER));
  1291. }
  1292. lpControl->dwReadOffset=dwReadOffset;
  1293. // Increment the amount of free space left and decrement the message count
  1294. lpControl->dwBufferLeft += (dwSize + sizeof(DPLOBBYI_MESSAGEHEADER));
  1295. lpControl->dwMessages--;
  1296. // Fall through
  1297. EXIT_READ_CLIENT_DATA:
  1298. // Release the mutex
  1299. ReleaseMutex(hMutex);
  1300. return hr;
  1301. } // PRV_ReadClientData
  1302. #undef DPF_MODNAME
  1303. #define DPF_MODNAME "PRV_ReceiveClientNotification"
  1304. DWORD WINAPI PRV_ReceiveClientNotification(LPVOID lpParam)
  1305. {
  1306. LPDPLOBBYI_GAMENODE lpgn = (LPDPLOBBYI_GAMENODE)lpParam;
  1307. LPDPLOBBYI_MESSAGE lpm = NULL;
  1308. LPDPLOBBYI_BUFFERCONTROL lpControl = NULL;
  1309. LPDPLMSG_GENERIC lpmsg = NULL;
  1310. HRESULT hr;
  1311. HANDLE hEvents[4];
  1312. LPVOID lpBuffer = NULL;
  1313. DWORD dwFlags;
  1314. DWORD dwSize;
  1315. DWORD dwReturn;
  1316. BOOL bForward;
  1317. DWORD dwWait=INFINITE;
  1318. DWORD nWait=2;
  1319. DPF(7, "Entering PRV_ReceiveClientNotification");
  1320. DPF(9, "Parameters: 0x%08x", lpParam);
  1321. // Make sure we have a valid shared memory buffer
  1322. // Note: Take the GameNode lock so that nobody changes the flags
  1323. // for the buffers, or the buffers themselves out from under us.
  1324. ENTER_DPLGAMENODE();
  1325. if(!(lpgn->dwFlags & GN_SHARED_MEMORY_AVAILABLE))
  1326. {
  1327. BOOL bGameCreate=FALSE;
  1328. DPF(2, "NOTE: ReceiveClientNotification thread starting without shared memory set up. Setting up now.");
  1329. // HACK!!!! -- SetLobbyMessageReceiveEvent may get called from
  1330. // the game without having been lobbied yet. If that is the case,
  1331. // we need to create the shared memory buffer. If we don't do
  1332. // that, we may miss messages.
  1333. if(!(lpgn->dwFlags & GN_LOBBY_CLIENT))
  1334. {
  1335. // Fake the setup routine by setting the lobby client flag
  1336. lpgn->dwFlags |= GN_LOBBY_CLIENT;
  1337. // Set our flag
  1338. bGameCreate = TRUE;
  1339. }
  1340. hr = PRV_SetupAllSharedMemory(lpgn);
  1341. // HACK!!!! -- Reset the settings we changed to fake the setup routines
  1342. if(bGameCreate)
  1343. {
  1344. lpgn->dwFlags &= (~GN_LOBBY_CLIENT);
  1345. }
  1346. //hr = PRV_SetupAllSharedMemory(lpgn);
  1347. if(FAILED(hr))
  1348. {
  1349. LEAVE_DPLGAMENODE();
  1350. DPF(2, "Unable to access App Data memory");
  1351. return 0L;
  1352. }
  1353. }
  1354. LEAVE_DPLGAMENODE();
  1355. // Setup the two events -- one receive event, one kill event
  1356. hEvents[0] = ((lpgn->dwFlags & GN_LOBBY_CLIENT) ?
  1357. (lpgn->hGameWriteEvent) : (lpgn->hLobbyWriteEvent));
  1358. hEvents[1] = lpgn->hKillReceiveThreadEvent;
  1359. if(lpgn->hLobbyClientProcess){
  1360. nWait=3;
  1361. hEvents[2] = lpgn->hLobbyClientProcess;
  1362. } else {
  1363. hEvents[2] = INVALID_HANDLE_VALUE;
  1364. if(!(lpgn->dwFlags & GN_LOBBY_CLIENT)){
  1365. dwWait = 5000;
  1366. }
  1367. }
  1368. // This extra handle is here because of a Windows 95 bug. Windows
  1369. // will occasionally miss when it walks the handle table, causing
  1370. // my thread to wait on the wrong handles. By putting a guaranteed
  1371. // invalid handle at the end of our array, the kernel will do a
  1372. // forced re-walk of the handle table and find the correct handles.
  1373. hEvents[3] = INVALID_HANDLE_VALUE;
  1374. // Make sure we have a valid event
  1375. if(!hEvents[0] || !hEvents[1])
  1376. {
  1377. DPF(2, "Either the Write Event or the Kill Event is NULL and it shouldn't be!");
  1378. ExitThread(0L);
  1379. return 0;
  1380. }
  1381. // If we are the game, we should check the buffer to see if any messages
  1382. // already exist in the shared buffer.
  1383. if(!(lpgn->dwFlags & GN_LOBBY_CLIENT))
  1384. {
  1385. lpControl = (LPDPLOBBYI_BUFFERCONTROL)lpgn->lpLobbyWriteBuffer;
  1386. // If there are any messages, kick our event so that our receive
  1387. // loop will immediately put the messages in the queue
  1388. if(lpControl->dwMessages)
  1389. SetEvent(hEvents[0]);
  1390. }
  1391. // Wait for the event notification
  1392. while(1)
  1393. {
  1394. // Sleep until something shows up
  1395. dwReturn = WaitForMultipleObjects(nWait, (HANDLE *)hEvents,
  1396. FALSE, dwWait);
  1397. if(dwReturn == WAIT_TIMEOUT){
  1398. ASSERT(!(lpgn->dwFlags & GN_LOBBY_CLIENT));
  1399. if(lpgn->hLobbyClientProcess){
  1400. DPF(9,"Got the lobby client process handle, adding to wait list\n");
  1401. hEvents[2] = lpgn->hLobbyClientProcess;
  1402. nWait = 3;
  1403. dwWait=INFINITE;
  1404. }
  1405. continue;
  1406. }
  1407. if(nWait==3 && dwReturn == WAIT_OBJECT_0 + 2){
  1408. // send the dead lobby client message
  1409. DPLMSG_SYSTEMMESSAGE msg;
  1410. if(lpgn->dwLobbyClientProcessID){
  1411. memset(&msg, 0, sizeof(DPLMSG_SYSTEMMESSAGE));
  1412. msg.dwType = DPLSYS_LOBBYCLIENTRELEASE;
  1413. msg.guidInstance = lpgn->guidInstance;
  1414. lpgn->dwLobbyClientProcessID = 0;
  1415. hr = PRV_InjectMessageInQueue(lpgn, DPLMSG_SYSTEM | DPLMSG_STANDARD, &msg,
  1416. sizeof(DPLMSG_SYSTEMMESSAGE), FALSE);
  1417. }
  1418. nWait=2;
  1419. hEvents[2]=INVALID_HANDLE_VALUE;
  1420. continue;
  1421. }
  1422. // If the return value was anything bug the receive event,
  1423. // kill the thread
  1424. if(dwReturn != WAIT_OBJECT_0)
  1425. {
  1426. if(dwReturn == WAIT_FAILED)
  1427. {
  1428. // This is a Windows 95 bug -- We may have gotten
  1429. // kicked for no reason. If that was the case, we
  1430. // still have valid handles (we think), the OS
  1431. // just goofed up. So, validate the handle and if
  1432. // they are valid, just return to waiting. See
  1433. // bug #3340 for a better explanation.
  1434. if(ERROR_INVALID_HANDLE == GetLastError())
  1435. {
  1436. if(!OS_IsValidHandle(hEvents[0]))
  1437. break;
  1438. if(!OS_IsValidHandle(hEvents[1]))
  1439. break;
  1440. continue;
  1441. }
  1442. break;
  1443. }
  1444. else
  1445. {
  1446. // It is either our kill event, or something we don't
  1447. // understand or expect. In this case, let's exit.
  1448. break;
  1449. }
  1450. }
  1451. while(1)
  1452. {
  1453. // First, call PRV_ReadClientData to get the size of the data
  1454. hr = PRV_ReadClientData(lpgn, NULL, NULL, &dwSize);
  1455. // If there are no messages, end the while loop
  1456. if(hr == DPERR_NOMESSAGES)
  1457. break;
  1458. // Otherwise, we should get the BUFFERTOOSMALL case
  1459. if(hr != DPERR_BUFFERTOOSMALL)
  1460. {
  1461. // We should never have a problem here
  1462. DPF_ERRVAL("Recieved an unexpected error reading from shared buffer, hr = 0x%08x", hr);
  1463. ASSERT(FALSE);
  1464. // Might as well keep trying
  1465. break;
  1466. }
  1467. // Allocate memory for the node and the data buffer
  1468. lpm = DPMEM_ALLOC(sizeof(DPLOBBYI_MESSAGE));
  1469. lpBuffer = DPMEM_ALLOC(dwSize);
  1470. if((!lpm) || (!lpBuffer))
  1471. {
  1472. DPF_ERR("Unable to allocate memory for message");
  1473. ASSERT(FALSE);
  1474. // Might as well keep trying
  1475. break;
  1476. }
  1477. // Copy the data into our buffer
  1478. hr = PRV_ReadClientData(lpgn, &dwFlags, lpBuffer, &dwSize);
  1479. if(FAILED(hr))
  1480. {
  1481. DPF_ERRVAL("Error reading shared buffer, message not read, hr = 0x%08x", hr);
  1482. ASSERT(FALSE);
  1483. DPMEM_FREE(lpm);
  1484. DPMEM_FREE(lpBuffer);
  1485. // Might as well keep trying
  1486. break;
  1487. }
  1488. // Clear our foward flag
  1489. bForward = FALSE;
  1490. // If we are a dplay lobby client, we need to forward the message
  1491. // onto the lobby server using the IDP3 interface. If we're not,
  1492. // then just put the message in the receive queue.
  1493. if(lpgn->dwFlags & GN_CLIENT_LAUNCHED)
  1494. {
  1495. // Foward the message
  1496. hr = PRV_ForwardMessageToLobbyServer(lpgn, lpBuffer, dwSize,
  1497. ((dwFlags & DPLMSG_STANDARD) ? TRUE : FALSE));
  1498. if(FAILED(hr))
  1499. {
  1500. DPF_ERRVAL("Unable to send lobby system message, hr = 0x%08x", hr);
  1501. }
  1502. // Set the forwarded flag
  1503. bForward = TRUE;
  1504. }
  1505. // Check for an App Terminated message. If we get one off the wire,
  1506. // we need to shut down our ClientTerminateMonitor thread, signal
  1507. // this thread (the receive thread to shut down, and mark the game
  1508. // node as dead. This will keep us from sending or receiving any
  1509. // more messages from the now dead game. (This message will only
  1510. // ever be received by a lobby client).
  1511. lpmsg = (LPDPLMSG_GENERIC)lpBuffer;
  1512. if(lpmsg->dwType == DPLSYS_APPTERMINATED)
  1513. {
  1514. // Kick the TerminateMonitor thread with it's kill event
  1515. SetEvent(lpgn->hKillTermThreadEvent);
  1516. // Set this thread's kill event (so that when we get done
  1517. // reading messages out of the shared buffer, we go away)
  1518. SetEvent(lpgn->hKillReceiveThreadEvent);
  1519. // Mark the GAMENODE as dead, but don't remove it since we know
  1520. // there will still messages in the queue.
  1521. lpgn->dwFlags |= GN_DEAD_GAME_NODE;
  1522. }
  1523. // If it's one of our DX3 messages, we need to put it in the queue
  1524. // otherwise if we already forwarded it, we can free it. NOTE: All
  1525. // DX3 lobby system messages had a value between 0 and
  1526. // DPLSYS_APPTERMINATED (0x04).
  1527. if( (!bForward) || (lpmsg->dwType <= DPLSYS_APPTERMINATED))
  1528. {
  1529. if (lpmsg->dwType == DPLSYS_LOBBYCLIENTRELEASE) {
  1530. if(lpgn->dwLobbyClientProcessID){
  1531. lpgn->dwLobbyClientProcessID = 0;
  1532. } else {
  1533. goto no_queue;
  1534. }
  1535. }
  1536. // Save the data pointer & the external flags
  1537. lpm->dwFlags = dwFlags & (~DPLOBBYPR_INTERNALMESSAGEFLAGS);
  1538. lpm->dwSize = dwSize;
  1539. lpm->lpData = lpBuffer;
  1540. // Add the message to the end of the queue & increment the count
  1541. ENTER_DPLQUEUE();
  1542. lpm->lpPrev = lpgn->MessageHead.lpPrev;
  1543. lpgn->MessageHead.lpPrev->lpNext = lpm;
  1544. lpgn->MessageHead.lpPrev = lpm;
  1545. lpm->lpNext = &lpgn->MessageHead;
  1546. lpgn->dwMessageCount++;
  1547. LEAVE_DPLQUEUE();
  1548. // NOTE: There is a potential thread problem here, but we are going
  1549. // to ignore it for now. It is possible for another thread to be
  1550. // going through the SetAppData code which changes this event handle.
  1551. // The problem is if they change it after this IF statement, but
  1552. // before we call SetEvent. However, the SetEvent call will either
  1553. // succeed on the new handle, or return an error if the handle is
  1554. // changed to NULL. In either case, no harm, no foul -- we don't care.
  1555. if(!lpgn->hDupReceiveEvent)
  1556. {
  1557. DPF(8, "The Receive Event handle is NULL!");
  1558. continue;
  1559. }
  1560. SetEvent(lpgn->hDupReceiveEvent);
  1561. }
  1562. else
  1563. {
  1564. no_queue:
  1565. // Free the buffers
  1566. DPMEM_FREE(lpm);
  1567. DPMEM_FREE(lpBuffer);
  1568. }
  1569. }
  1570. }
  1571. DPF(8, "Lobby Receive Thread is going away!!!!!");
  1572. ExitThread(0L);
  1573. return 0L; // avoid warning.
  1574. } // PRV_ReceiveClientNotification
  1575. #undef DPF_MODNAME
  1576. #define DPF_MODNAME "PRV_RemoveNodeFromQueue"
  1577. void PRV_RemoveNodeFromQueue(LPDPLOBBYI_GAMENODE lpgn, LPDPLOBBYI_MESSAGE lpm)
  1578. {
  1579. DPF(7, "Entering PRV_RemoveNodeFromQueue");
  1580. DPF(9, "Parameters: 0x%08x, 0x%08x", lpgn, lpm);
  1581. ASSERT(lpgn);
  1582. ASSERT(lpm);
  1583. // Delete the message from the queue & decrement the count
  1584. lpm->lpPrev->lpNext = lpm->lpNext;
  1585. lpm->lpNext->lpPrev = lpm->lpPrev;
  1586. lpgn->dwMessageCount--;
  1587. // Free the memory for the message node
  1588. DPMEM_FREE(lpm->lpData);
  1589. DPMEM_FREE(lpm);
  1590. } // PRV_RemoveNodeFromQueue
  1591. #undef DPF_MODNAME
  1592. #define DPF_MODNAME "PRV_CleanUpQueue"
  1593. void PRV_CleanUpQueue(LPDPLOBBYI_GAMENODE lpgn)
  1594. {
  1595. LPDPLOBBYI_MESSAGE lpm, lpmNext;
  1596. DPF(7, "Entering PRV_CleanUpQueue");
  1597. DPF(9, "Parameters: 0x%08x", lpgn);
  1598. ASSERT(lpgn);
  1599. lpm = lpgn->MessageHead.lpNext;
  1600. while(lpm != &lpgn->MessageHead)
  1601. {
  1602. // Save the next pointer
  1603. lpmNext = lpm->lpNext;
  1604. // Remove the node
  1605. PRV_RemoveNodeFromQueue(lpgn, lpm);
  1606. // Move to the next node
  1607. lpm = lpmNext;
  1608. }
  1609. } // PRV_CleanUpQueue
  1610. #undef DPF_MODNAME
  1611. #define DPF_MODNAME "PRV_KillThread"
  1612. void PRV_KillThread(HANDLE hThread, HANDLE hEvent)
  1613. {
  1614. DPF(7, "Entering PRV_KillThread");
  1615. DPF(9, "Parameters: 0x%08x, 0x%08x", hThread, hEvent);
  1616. ASSERT(hThread);
  1617. ASSERT(hEvent);
  1618. // Signal the thread to die.
  1619. SetEvent(hEvent);
  1620. // Wait until the thread terminates, if it doesn't something is
  1621. // wrong, so we better fix it.
  1622. DPF(8, "Starting to wait for a thread to exit -- hThread = 0x%08x, hEvent = 0x%08x", hThread, hEvent);
  1623. WaitForSingleObject(hThread, INFINITE);
  1624. // Now close both handles
  1625. CloseHandle(hThread);
  1626. CloseHandle(hEvent);
  1627. } // PRV_KillThread
  1628. #undef DPF_MODNAME
  1629. #define DPF_MODNAME "PRV_FreeGameNode"
  1630. HRESULT PRV_FreeGameNode(LPDPLOBBYI_GAMENODE lpgn)
  1631. {
  1632. LPDPLOBBYI_BUFFERCONTROL lpControl = NULL;
  1633. DPF(7, "Entering PRV_FreeGameNode");
  1634. DPF(9, "Parameters: 0x%08x", lpgn);
  1635. // FIRST: Take care of the connection settings data buffer
  1636. // Unmap & release the shared memory
  1637. if(lpgn->lpConnectDataBuffer)
  1638. UnmapViewOfFile(lpgn->lpConnectDataBuffer);
  1639. if(lpgn->hConnectDataFile)
  1640. CloseHandle(lpgn->hConnectDataFile);
  1641. if(lpgn->hConnectDataMutex)
  1642. CloseHandle(lpgn->hConnectDataMutex);
  1643. // NEXT: Take care of the App Data Events & Buffers
  1644. // Kill the Receive Thread
  1645. if(lpgn->hReceiveThread)
  1646. {
  1647. PRV_KillThread(lpgn->hReceiveThread, lpgn->hKillReceiveThreadEvent);
  1648. CloseHandle(lpgn->hDupReceiveEvent);
  1649. }
  1650. // Close the event handles
  1651. if(lpgn->hLobbyWriteEvent)
  1652. CloseHandle(lpgn->hLobbyWriteEvent);
  1653. if(lpgn->hGameWriteEvent)
  1654. CloseHandle(lpgn->hGameWriteEvent);
  1655. // Kill the Terminate Monitor Thread
  1656. if(lpgn->hTerminateThread)
  1657. {
  1658. PRV_KillThread(lpgn->hTerminateThread, lpgn->hKillTermThreadEvent);
  1659. }
  1660. // Clear the flags since we are no longer going to be active
  1661. if(lpgn->lpGameWriteBuffer)
  1662. {
  1663. lpControl = (LPDPLOBBYI_BUFFERCONTROL)lpgn->lpGameWriteBuffer;
  1664. lpControl->dwFlags &= ~((lpgn->dwFlags & GN_LOBBY_CLIENT) ?
  1665. BC_LOBBY_ACTIVE : BC_GAME_ACTIVE);
  1666. }
  1667. if(lpgn->lpLobbyWriteBuffer)
  1668. {
  1669. lpControl = (LPDPLOBBYI_BUFFERCONTROL)lpgn->lpLobbyWriteBuffer;
  1670. lpControl->dwFlags &= ~((lpgn->dwFlags & GN_LOBBY_CLIENT) ?
  1671. BC_LOBBY_ACTIVE : BC_GAME_ACTIVE);
  1672. }
  1673. // Unmap & release the Game Write memory
  1674. if(lpgn->lpGameWriteBuffer)
  1675. UnmapViewOfFile(lpgn->lpGameWriteBuffer);
  1676. if(lpgn->hGameWriteFile)
  1677. CloseHandle(lpgn->hGameWriteFile);
  1678. if(lpgn->hGameWriteMutex)
  1679. CloseHandle(lpgn->hGameWriteMutex);
  1680. // Unmap & release the Lobby Write memory
  1681. if(lpgn->lpLobbyWriteBuffer)
  1682. UnmapViewOfFile(lpgn->lpLobbyWriteBuffer);
  1683. if(lpgn->hLobbyWriteFile)
  1684. CloseHandle(lpgn->hLobbyWriteFile);
  1685. if(lpgn->hLobbyWriteMutex)
  1686. CloseHandle(lpgn->hLobbyWriteMutex);
  1687. // Clean up the message queue
  1688. PRV_CleanUpQueue(lpgn);
  1689. // Close the process handle we have for the game
  1690. if(lpgn->hGameProcess)
  1691. CloseHandle(lpgn->hGameProcess);
  1692. if(lpgn->hLobbyClientProcess)
  1693. CloseHandle(lpgn->hLobbyClientProcess);
  1694. // Free the game node structure
  1695. DPMEM_FREE(lpgn);
  1696. return DP_OK;
  1697. } // PRV_FreeGameNode
  1698. #undef DPF_MODNAME
  1699. #define DPF_MODNAME "PRV_DuplicateHandle"
  1700. HANDLE PRV_DuplicateHandle(HANDLE hSource)
  1701. {
  1702. HANDLE hProcess = NULL;
  1703. HANDLE hTarget = NULL;
  1704. DWORD dwProcessID;
  1705. DWORD dwError;
  1706. DPF(7, "Entering PRV_DuplicateHandle");
  1707. DPF(9, "Parameters: 0x%08x", hSource);
  1708. dwProcessID = GetCurrentProcessId();
  1709. hProcess = OpenProcess(PROCESS_DUP_HANDLE, FALSE, dwProcessID);
  1710. if(!DuplicateHandle(hProcess, hSource, hProcess, &hTarget,
  1711. 0L, FALSE, DUPLICATE_SAME_ACCESS))
  1712. {
  1713. dwError = GetLastError();
  1714. CloseHandle(hProcess);
  1715. return NULL;
  1716. }
  1717. CloseHandle(hProcess);
  1718. return hTarget;
  1719. } // PRV_DuplicateHandle
  1720. #undef DPF_MODNAME
  1721. #define DPF_MODNAME "DPL_SetLobbyMessageEvent"
  1722. HRESULT DPLAPI DPL_SetLobbyMessageEvent(LPDIRECTPLAYLOBBY lpDPL,
  1723. DWORD dwFlags, DWORD dwGameID,
  1724. HANDLE hReceiveEvent)
  1725. {
  1726. LPDPLOBBYI_DPLOBJECT this;
  1727. LPDPLOBBYI_GAMENODE lpgn = NULL;
  1728. LPVOID lpBuffer = NULL;
  1729. HANDLE hReceiveThread = NULL;
  1730. HANDLE hDupReceiveEvent = NULL;
  1731. HRESULT hr;
  1732. BOOL bCreated = FALSE;
  1733. BOOL bLobbyClient = TRUE;
  1734. BOOL bNewEvent = FALSE;
  1735. DPF(7, "Entering DPL_SetLobbyMessageEvent");
  1736. DPF(9, "Parameters: 0x%08x, 0x%08x, 0x%08x, 0x%08x",
  1737. lpDPL, dwFlags, dwGameID, hReceiveEvent);
  1738. ENTER_DPLOBBY();
  1739. TRY
  1740. {
  1741. if( !VALID_DPLOBBY_INTERFACE( lpDPL ))
  1742. {
  1743. LEAVE_DPLOBBY();
  1744. return DPERR_INVALIDINTERFACE;
  1745. }
  1746. this = DPLOBJECT_FROM_INTERFACE(lpDPL);
  1747. if( !VALID_DPLOBBY_PTR( this ) )
  1748. {
  1749. LEAVE_DPLOBBY();
  1750. return DPERR_INVALIDOBJECT;
  1751. }
  1752. // Validate the handle
  1753. if(hReceiveEvent)
  1754. {
  1755. if(!OS_IsValidHandle(hReceiveEvent))
  1756. {
  1757. LEAVE_DPLOBBY();
  1758. DPF_ERR("Invalid hReceiveEvent handle");
  1759. return DPERR_INVALIDPARAMS;
  1760. }
  1761. }
  1762. // We haven't defined any flags for this release
  1763. if( (dwFlags) )
  1764. {
  1765. LEAVE_DPLOBBY();
  1766. return DPERR_INVALIDPARAMS;
  1767. }
  1768. }
  1769. EXCEPT( EXCEPTION_EXECUTE_HANDLER )
  1770. {
  1771. LEAVE_DPLOBBY();
  1772. DPF_ERR( "Exception encountered validating parameters" );
  1773. return DPERR_INVALIDPARAMS;
  1774. }
  1775. // If the dwGameID is zero, we assume we are a game. In that case,
  1776. // the GameNode we are looking for should have our own ProcessID.
  1777. if(!dwGameID)
  1778. {
  1779. dwGameID = GetCurrentProcessId();
  1780. bLobbyClient = FALSE;
  1781. }
  1782. ENTER_DPLGAMENODE();
  1783. lpgn = PRV_GetGameNode(this->lpgnHead, dwGameID);
  1784. // If the event handle is null, kill our duplicate handle
  1785. if(!hReceiveEvent)
  1786. {
  1787. if(!lpgn)
  1788. {
  1789. DPF(5, "Unable to find GameNode -- Invalid dwGameID!");
  1790. LEAVE_DPLGAMENODE();
  1791. LEAVE_DPLOBBY();
  1792. return DPERR_GENERIC;
  1793. }
  1794. CloseHandle(lpgn->hDupReceiveEvent);
  1795. lpgn->hDupReceiveEvent = NULL;
  1796. LEAVE_DPLGAMENODE();
  1797. LEAVE_DPLOBBY();
  1798. return DP_OK;
  1799. }
  1800. // If a GameNode structure exists for this process, we must be trying
  1801. // to replace the event handle, so kill the old event handle, OTHERWISE
  1802. // we need to allocate a new GameNode for this process
  1803. if(lpgn)
  1804. {
  1805. if(lpgn->hDupReceiveEvent)
  1806. {
  1807. CloseHandle(lpgn->hDupReceiveEvent);
  1808. lpgn->hDupReceiveEvent = NULL;
  1809. }
  1810. }
  1811. else
  1812. {
  1813. // If we are a game, go ahead and create the node
  1814. if(!bLobbyClient)
  1815. {
  1816. hr = PRV_AddNewGameNode(this, &lpgn, dwGameID, NULL, bLobbyClient,NULL);
  1817. if(FAILED(hr))
  1818. {
  1819. LEAVE_DPLGAMENODE();
  1820. LEAVE_DPLOBBY();
  1821. return hr;
  1822. }
  1823. }
  1824. else
  1825. {
  1826. LEAVE_DPLGAMENODE();
  1827. LEAVE_DPLOBBY();
  1828. return DPERR_INVALIDPARAMS;
  1829. }
  1830. }
  1831. // Duplicate the caller's handle in case they free it without calling
  1832. // us first to remove the Receive thread.
  1833. hDupReceiveEvent = PRV_DuplicateHandle(hReceiveEvent);
  1834. if(!hDupReceiveEvent)
  1835. {
  1836. DPF(2, "Unable to duplicate ReceiveEvent handle");
  1837. LEAVE_DPLGAMENODE();
  1838. LEAVE_DPLOBBY();
  1839. return DPERR_OUTOFMEMORY;
  1840. }
  1841. if(!lpgn->hDupReceiveEvent)
  1842. bNewEvent = TRUE;
  1843. lpgn->hDupReceiveEvent = hDupReceiveEvent;
  1844. // Check to see if the Receive thread already exists. If it
  1845. // doesn't, create it. Otherwise, leave it alone.
  1846. if(!(lpgn->hReceiveThread))
  1847. {
  1848. hr = PRV_StartReceiveThread(lpgn);
  1849. if(FAILED(hr))
  1850. {
  1851. if(lpgn->hDupReceiveEvent)
  1852. {
  1853. CloseHandle(lpgn->hDupReceiveEvent);
  1854. lpgn->hDupReceiveEvent = NULL;
  1855. }
  1856. LEAVE_DPLGAMENODE();
  1857. LEAVE_DPLOBBY();
  1858. return hr;
  1859. }
  1860. }
  1861. // If this is a new event, check to see if there are any messages in the
  1862. // queue. If there are, kick the event so the user knows they are there.
  1863. if(bNewEvent && lpgn->dwMessageCount)
  1864. SetEvent(hDupReceiveEvent);
  1865. LEAVE_DPLGAMENODE();
  1866. LEAVE_DPLOBBY();
  1867. return DP_OK;
  1868. } // DPL_SetLobbyMessageEvent
  1869. #undef DPF_MODNAME
  1870. #define DPF_MODNAME "DPL_SendLobbyMessage"
  1871. HRESULT DPLAPI DPL_SendLobbyMessage(LPDIRECTPLAYLOBBY lpDPL, DWORD dwFlags,
  1872. DWORD dwGameID, LPVOID lpData, DWORD dwSize)
  1873. {
  1874. LPDPLOBBYI_DPLOBJECT this;
  1875. LPDPLOBBYI_GAMENODE lpgn = NULL;
  1876. LPDPLMSG_GENERIC lpmsg = NULL;
  1877. HRESULT hr = DP_OK;
  1878. BOOL bLobbyClient = TRUE;
  1879. BOOL bStandard = FALSE;
  1880. DPF(7, "Entering DPL_SendLobbyMessage");
  1881. DPF(9, "Parameters: 0x%08x, 0x%08x, 0x%08x, 0x%08x, %lu",
  1882. lpDPL, dwFlags, dwGameID, lpData, dwSize);
  1883. ENTER_DPLOBBY();
  1884. TRY
  1885. {
  1886. if( !VALID_DPLOBBY_INTERFACE( lpDPL ))
  1887. {
  1888. LEAVE_DPLOBBY();
  1889. return DPERR_INVALIDINTERFACE;
  1890. }
  1891. this = DPLOBJECT_FROM_INTERFACE(lpDPL);
  1892. if( !VALID_DPLOBBY_PTR( this ) )
  1893. {
  1894. LEAVE_DPLOBBY();
  1895. return DPERR_INVALIDOBJECT;
  1896. }
  1897. if( !VALID_READ_PTR( lpData, dwSize ) )
  1898. {
  1899. LEAVE_DPLOBBY();
  1900. return DPERR_INVALIDPARAMS;
  1901. }
  1902. // Check for valid flags
  1903. if( !VALID_SENDLOBBYMESSAGE_FLAGS(dwFlags))
  1904. {
  1905. LEAVE_DPLOBBY();
  1906. return DPERR_INVALIDFLAGS;
  1907. }
  1908. // If it's of the system message format, validate the dwType
  1909. if( dwFlags & DPLMSG_STANDARD )
  1910. {
  1911. // Mark this as a standard message
  1912. bStandard = TRUE;
  1913. // Make sure the message is big enough to read
  1914. if(! VALID_READ_PTR( lpData, sizeof(DPLMSG_GENERIC)) )
  1915. {
  1916. LEAVE_DPLOBBY();
  1917. DPF_ERR("Invalid message buffer");
  1918. return DPERR_INVALIDPARAMS;
  1919. }
  1920. // Make sure it's one we support
  1921. lpmsg = (LPDPLMSG_GENERIC)lpData;
  1922. switch(lpmsg->dwType)
  1923. {
  1924. case DPLSYS_GETPROPERTY:
  1925. case DPLSYS_SETPROPERTY:
  1926. break;
  1927. default:
  1928. DPF_ERR("The dwType of the message is invalid for a legal standard lobby message");
  1929. LEAVE_DPLOBBY();
  1930. return DPERR_INVALIDPARAMS;
  1931. }
  1932. }
  1933. }
  1934. EXCEPT( EXCEPTION_EXECUTE_HANDLER )
  1935. {
  1936. LEAVE_DPLOBBY();
  1937. DPF_ERR( "Exception encountered validating parameters" );
  1938. return DPERR_INVALIDPARAMS;
  1939. }
  1940. // If a GameID was passed in, use it to find the correct GameNode. If
  1941. // one wasn't passed in, assume we are the game and use our ProcessID.
  1942. if(!dwGameID)
  1943. {
  1944. dwGameID = GetCurrentProcessId();
  1945. bLobbyClient = FALSE;
  1946. }
  1947. // Now find the correct game node. If we don't find it, assume we
  1948. // have an invalid ID and error out.
  1949. lpgn = PRV_GetGameNode(this->lpgnHead, dwGameID);
  1950. if(!lpgn)
  1951. {
  1952. LEAVE_DPLOBBY();
  1953. DPF_ERR("Invalid dwGameID");
  1954. return DPERR_INVALIDPARAMS;
  1955. }
  1956. // If we are self-lobbied, we need to send the message onto the lobby
  1957. // using the IDP3 interface that we are communicating with the lobby on
  1958. // If not, we need to put it in the shared buffer and let the lobby
  1959. // client deal with it.
  1960. if(lpgn->dwFlags & GN_SELF_LOBBIED)
  1961. {
  1962. // Drop the lobby lock so we can call PRV_Send
  1963. LEAVE_DPLOBBY();
  1964. // Foward the message
  1965. hr = PRV_ForwardMessageToLobbyServer(lpgn, lpData, dwSize, bStandard);
  1966. // Take the lock back
  1967. ENTER_DPLOBBY();
  1968. if(FAILED(hr))
  1969. {
  1970. DPF_ERRVAL("Unable to send lobby system message, hr = 0x%08x", hr);
  1971. }
  1972. }
  1973. else
  1974. {
  1975. // Write the data to our shared memory
  1976. hr = PRV_WriteClientData(lpgn, dwFlags, lpData, dwSize);
  1977. }
  1978. LEAVE_DPLOBBY();
  1979. return hr;
  1980. } // DPL_SendLobbyMessage
  1981. #undef DPF_MODNAME
  1982. #define DPF_MODNAME "PRV_GetMessageFromQueue"
  1983. HRESULT PRV_GetMessageFromQueue(LPDPLOBBYI_GAMENODE lpgn, LPDWORD lpdwFlags,
  1984. LPVOID lpData, LPDWORD lpdwSize)
  1985. {
  1986. LPDPLOBBYI_MESSAGE lpm;
  1987. DPF(7, "Entering PRV_GetMessageFromQueue");
  1988. DPF(9, "Parameters: 0x%08x, 0x%08x, 0x%08x, 0x%08x",
  1989. lpgn, lpdwFlags, lpData, lpdwSize);
  1990. ENTER_DPLQUEUE();
  1991. // Get the top message in the queue
  1992. lpm = lpgn->MessageHead.lpNext;
  1993. // Make sure we have a message
  1994. if((!lpgn->dwMessageCount) || (lpm == &lpgn->MessageHead))
  1995. {
  1996. LEAVE_DPLQUEUE();
  1997. return DPERR_NOMESSAGES;
  1998. }
  1999. // If the lpData pointer is NULL, just return the size
  2000. if(!lpData)
  2001. {
  2002. *lpdwSize = lpm->dwSize;
  2003. LEAVE_DPLQUEUE();
  2004. return DPERR_BUFFERTOOSMALL;
  2005. }
  2006. // Otherwise, check the remaining output parameters
  2007. if( !VALIDEX_CODE_PTR( lpData ) )
  2008. {
  2009. LEAVE_DPLQUEUE();
  2010. return DPERR_INVALIDPARAMS;
  2011. }
  2012. if( !VALID_DWORD_PTR( lpdwFlags ) )
  2013. {
  2014. LEAVE_DPLQUEUE();
  2015. return DPERR_INVALIDPARAMS;
  2016. }
  2017. // Copy the message
  2018. if(*lpdwSize < lpm->dwSize)
  2019. {
  2020. *lpdwSize = lpm->dwSize;
  2021. LEAVE_DPLQUEUE();
  2022. return DPERR_BUFFERTOOSMALL;
  2023. }
  2024. else
  2025. memcpy(lpData, lpm->lpData, lpm->dwSize);
  2026. // Set the other output parameters
  2027. *lpdwSize = lpm->dwSize;
  2028. *lpdwFlags = lpm->dwFlags;
  2029. // Delete the message from the queue & decrement the count
  2030. PRV_RemoveNodeFromQueue(lpgn, lpm);
  2031. // Check and see if our GAMENODE is dead. If it is, and if the message
  2032. // count has gone to zero, then free the GAMENODE structure.
  2033. if((!lpgn->dwMessageCount) && IS_GAME_DEAD(lpgn))
  2034. PRV_RemoveGameNodeFromList(lpgn);
  2035. LEAVE_DPLQUEUE();
  2036. return DP_OK;
  2037. } // PRV_GetMessageFromQueue
  2038. #undef DPF_MODNAME
  2039. #define DPF_MODNAME "DPL_ReceiveLobbyMessage"
  2040. HRESULT DPLAPI DPL_ReceiveLobbyMessage(LPDIRECTPLAYLOBBY lpDPL, DWORD dwFlags,
  2041. DWORD dwGameID, LPDWORD lpdwMessageFlags, LPVOID lpData,
  2042. LPDWORD lpdwDataLength)
  2043. {
  2044. LPDPLOBBYI_DPLOBJECT this;
  2045. LPDPLOBBYI_GAMENODE lpgn = NULL;
  2046. HRESULT hr = DP_OK;
  2047. BOOL bLobbyClient = TRUE;
  2048. DPF(7, "Entering DPL_ReceiveLobbyMessage");
  2049. DPF(9, "Parameters: 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x",
  2050. lpDPL, dwFlags, dwGameID, lpdwMessageFlags, lpData, lpdwDataLength);
  2051. ENTER_DPLOBBY();
  2052. TRY
  2053. {
  2054. if( !VALID_DPLOBBY_INTERFACE( lpDPL ))
  2055. {
  2056. LEAVE_DPLOBBY();
  2057. return DPERR_INVALIDINTERFACE;
  2058. }
  2059. this = DPLOBJECT_FROM_INTERFACE(lpDPL);
  2060. if( !VALID_DPLOBBY_PTR( this ) )
  2061. {
  2062. LEAVE_DPLOBBY();
  2063. return DPERR_INVALIDOBJECT;
  2064. }
  2065. if( !VALID_DWORD_PTR( lpdwDataLength ) )
  2066. {
  2067. LEAVE_DPLOBBY();
  2068. return DPERR_INVALIDPARAMS;
  2069. }
  2070. // We haven't defined any flags for this release
  2071. if( (dwFlags) )
  2072. {
  2073. LEAVE_DPLOBBY();
  2074. return DPERR_INVALIDFLAGS;
  2075. }
  2076. }
  2077. EXCEPT( EXCEPTION_EXECUTE_HANDLER )
  2078. {
  2079. LEAVE_DPLOBBY();
  2080. DPF_ERR( "Exception encountered validating parameters" );
  2081. return DPERR_INVALIDPARAMS;
  2082. }
  2083. // If a GameID was passed in, use it to find the correct GameNode. If
  2084. // one wasn't passed in, assume we are the game and use our ProcessID.
  2085. if(!dwGameID)
  2086. {
  2087. dwGameID = GetCurrentProcessId();
  2088. bLobbyClient = FALSE;
  2089. }
  2090. // Now find the correct game node. If we don't find it, assume we
  2091. // have an invalid ID and error out.
  2092. lpgn = PRV_GetGameNode(this->lpgnHead, dwGameID);
  2093. if(!lpgn)
  2094. {
  2095. DPF_ERR("Invalid dwGameID");
  2096. hr = DPERR_INVALIDPARAMS;
  2097. goto EXIT_RECEIVE_LOBBY_MESSAGE;
  2098. }
  2099. // Read the data from shared memory
  2100. hr = PRV_GetMessageFromQueue(lpgn, lpdwMessageFlags, lpData, lpdwDataLength);
  2101. // REVIEW!!!! -- Do we need to send this to the lobby server as part of this API????
  2102. EXIT_RECEIVE_LOBBY_MESSAGE:
  2103. LEAVE_DPLOBBY();
  2104. return hr;
  2105. } // DPL_ReceiveLobbyMessage
  2106. #undef DPF_MODNAME
  2107. #define DPF_MODNAME "PRV_WriteConnectionSettings"
  2108. HRESULT PRV_WriteConnectionSettings(LPDPLOBBYI_GAMENODE lpgn,
  2109. LPDPLCONNECTION lpConn, BOOL bOverrideWaitMode)
  2110. {
  2111. HRESULT hr;
  2112. DWORD dwSize;
  2113. BOOL bGameCreate = FALSE;
  2114. LPBYTE lpConnBuffer = NULL;
  2115. LPDPLOBBYI_CONNCONTROL lpConnControl = NULL;
  2116. DPF(7, "Entering PRV_WriteConnectionSettings");
  2117. DPF(9, "Parameters: 0x%08x, 0x%08x, %lu",
  2118. lpgn, lpConn, bOverrideWaitMode);
  2119. ENTER_DPLGAMENODE();
  2120. // Make sure we have a valid shared memory buffer
  2121. // Note: Take the GameNode lock so that nobody changes the flags
  2122. // for the buffers, or the buffers themselves out from under us.
  2123. if(!(lpgn->dwFlags & GN_SHARED_MEMORY_AVAILABLE))
  2124. {
  2125. // HACK!!!! -- SetConnectionSettings may get called from the game
  2126. // without having been lobbied. If that is the case, we need to
  2127. // create the shared memory with the game's process ID (this process)
  2128. if(!(lpgn->dwFlags & GN_LOBBY_CLIENT))
  2129. {
  2130. // Fake the setup routine by setting the lobby client flag
  2131. lpgn->dwFlags |= GN_LOBBY_CLIENT;
  2132. // Set our flag
  2133. bGameCreate = TRUE;
  2134. }
  2135. hr = PRV_SetupAllSharedMemory(lpgn);
  2136. // HACK!!!! -- Reset the settings we changed to fake the setup routines
  2137. if(bGameCreate)
  2138. {
  2139. lpgn->dwFlags &= (~GN_LOBBY_CLIENT);
  2140. }
  2141. // Now handle the failure
  2142. if(FAILED(hr))
  2143. {
  2144. LEAVE_DPLGAMENODE();
  2145. DPF(2, "Unable to access Connection Settings memory");
  2146. return hr;
  2147. }
  2148. }
  2149. // If the ConnectionSettings come from a StartSession message, we need to
  2150. // pick the dplay object pointer out of the DPLCONNECTION structure's
  2151. // reserved field. This pointer to a dplay object represents the object
  2152. // that has a connection to the lobby server.
  2153. if(lpConn->lpSessionDesc->dwReserved1)
  2154. {
  2155. // Save the pointer and player ID in our gamenode structure
  2156. lpgn->lpDPlayObject = (LPDPLAYI_DPLAY)lpConn->lpSessionDesc->dwReserved1;
  2157. lpgn->dpidPlayer = (DWORD)lpConn->lpSessionDesc->dwReserved2;
  2158. // Clear the field
  2159. lpConn->lpSessionDesc->dwReserved1 = 0L;
  2160. lpConn->lpSessionDesc->dwReserved2 = 0L;
  2161. }
  2162. // Save the instance pointer for the system messages
  2163. lpgn->guidInstance = lpConn->lpSessionDesc->guidInstance;
  2164. // Get the packaged size of the DPLCONNECTION structure
  2165. PRV_GetDPLCONNECTIONPackageSize(lpConn, &dwSize, NULL);
  2166. // Check data sizes
  2167. if(dwSize > (MAX_APPDATABUFFERSIZE - APPDATA_RESERVEDSIZE))
  2168. {
  2169. DPF(2, "Packaged Connection Settings exceeded max buffer size of %d",
  2170. (MAX_APPDATABUFFERSIZE - APPDATA_RESERVEDSIZE));
  2171. LEAVE_DPLGAMENODE();
  2172. return DPERR_BUFFERTOOLARGE;
  2173. }
  2174. // Make sure we have the mutex for the shared conn settings buffer
  2175. WaitForSingleObject(lpgn->hConnectDataMutex, INFINITE);
  2176. // Look at the control block to see if we are in wait mode
  2177. // If we are, and this is not a call from RunApplication, then
  2178. // we don't want to write the connection settings
  2179. hr = DPERR_UNAVAILABLE; // Default set to error
  2180. lpConnControl = (LPDPLOBBYI_CONNCONTROL)lpgn->lpConnectDataBuffer;
  2181. if((!(lpConnControl->dwFlags & BC_WAIT_MODE)) || bOverrideWaitMode)
  2182. {
  2183. // Get a pointer to the actual buffer
  2184. lpConnBuffer = (LPBYTE)lpConnControl + sizeof(DPLOBBYI_CONNCONTROL);
  2185. // Package the connection settings into the buffer
  2186. hr = PRV_PackageDPLCONNECTION(lpConn, lpConnBuffer, TRUE);
  2187. // If it succeeded, and we were overriding wait mode, we need
  2188. // to take the buffers out of wait mode and send the new connection
  2189. // settings available message
  2190. if(SUCCEEDED(hr) && bOverrideWaitMode)
  2191. {
  2192. // Take the buffers out of wait mode
  2193. PRV_LeaveConnSettingsWaitMode(lpgn);
  2194. }
  2195. }
  2196. ReleaseMutex(lpgn->hConnectDataMutex);
  2197. LEAVE_DPLGAMENODE();
  2198. return hr;
  2199. } // PRV_WriteConnectionSettings
  2200. #undef DPF_MODNAME
  2201. #define DPF_MODNAME "PRV_SetConnectionSettings"
  2202. HRESULT PRV_SetConnectionSettings(LPDIRECTPLAYLOBBY lpDPL, DWORD dwFlags,
  2203. DWORD dwGameID, LPDPLCONNECTION lpConn)
  2204. {
  2205. LPDPLOBBYI_DPLOBJECT this;
  2206. LPDPLOBBYI_GAMENODE lpgn = NULL;
  2207. HRESULT hr;
  2208. BOOL bLobbyClient = TRUE;
  2209. DPF(7, "Entering PRV_SetConnectionSettings");
  2210. DPF(9, "Parameters: 0x%08x, 0x%08x, 0x%08x, 0x%08x",
  2211. lpDPL, dwFlags, dwGameID, lpConn);
  2212. TRY
  2213. {
  2214. if( !VALID_DPLOBBY_INTERFACE( lpDPL ))
  2215. {
  2216. return DPERR_INVALIDINTERFACE;
  2217. }
  2218. this = DPLOBJECT_FROM_INTERFACE(lpDPL);
  2219. if( !VALID_DPLOBBY_PTR( this ) )
  2220. {
  2221. return DPERR_INVALIDOBJECT;
  2222. }
  2223. // Validate the DPLCONNECTION structure
  2224. hr = PRV_ValidateDPLCONNECTION(lpConn, FALSE);
  2225. if(FAILED(hr))
  2226. {
  2227. return hr;
  2228. }
  2229. // We haven't defined any flags for this release
  2230. if( (dwFlags) )
  2231. {
  2232. return DPERR_INVALIDFLAGS;
  2233. }
  2234. }
  2235. EXCEPT( EXCEPTION_EXECUTE_HANDLER )
  2236. {
  2237. DPF_ERR( "Exception encountered validating parameters" );
  2238. return DPERR_INVALIDPARAMS;
  2239. }
  2240. // If dwGameID is zero, we assume we are a game. In that case, the
  2241. // GameNode we are looking for should have our ProcessID.
  2242. if(!dwGameID)
  2243. {
  2244. dwGameID = GetCurrentProcessId();
  2245. bLobbyClient = FALSE;
  2246. }
  2247. lpgn = PRV_GetGameNode(this->lpgnHead, dwGameID);
  2248. if(!lpgn)
  2249. {
  2250. // If we are a game, go ahead and create the node
  2251. if(!bLobbyClient)
  2252. {
  2253. hr = PRV_AddNewGameNode(this, &lpgn, dwGameID, NULL, bLobbyClient,NULL);
  2254. if(FAILED(hr))
  2255. return hr;
  2256. }
  2257. else
  2258. return DPERR_INVALIDPARAMS;
  2259. }
  2260. // If the ConnectionSettings are from a StartSession message (lobby launched),
  2261. // we need to set the flag saying we are self-lobbied
  2262. if(lpConn->lpSessionDesc->dwReserved1)
  2263. {
  2264. // Set the flag that says we were lobby client launched
  2265. lpgn->dwFlags |= GN_SELF_LOBBIED;
  2266. }
  2267. // Write the connection settings to our shared buffer
  2268. hr = PRV_WriteConnectionSettings(lpgn, lpConn, FALSE);
  2269. return hr;
  2270. } // PRV_SetConnectionSettings
  2271. #undef DPF_MODNAME
  2272. #define DPF_MODNAME "DPL_SetConnectionSettings"
  2273. HRESULT DPLAPI DPL_SetConnectionSettings(LPDIRECTPLAYLOBBY lpDPL,
  2274. DWORD dwFlags, DWORD dwGameID, LPDPLCONNECTION lpConn)
  2275. {
  2276. HRESULT hr;
  2277. DPF(7, "Entering DPL_SetConnectionSettings");
  2278. DPF(9, "Parameters: 0x%08x, 0x%08x, 0x%08x, 0x%08x",
  2279. lpDPL, dwFlags, dwGameID, lpConn);
  2280. ENTER_DPLOBBY();
  2281. // Set the ANSI flag to TRUE and call the internal function
  2282. hr = PRV_SetConnectionSettings(lpDPL, dwFlags, dwGameID, lpConn);
  2283. LEAVE_DPLOBBY();
  2284. return hr;
  2285. } // DPL_SetConnectionSettings
  2286. #undef DPF_MODNAME
  2287. #define DPF_MODNAME "PRV_ReadConnectionSettings"
  2288. HRESULT PRV_ReadConnectionSettings(LPDPLOBBYI_GAMENODE lpgn, LPVOID lpData,
  2289. LPDWORD lpdwSize, BOOL bAnsi)
  2290. {
  2291. HRESULT hr = DP_OK;
  2292. LPDWORD lpdwBuffer;
  2293. LPDPLOBBYI_CONNCONTROL lpConnControl = NULL;
  2294. LPBYTE lpConnBuffer = NULL;
  2295. DWORD dwSize = 0,
  2296. dwSizeAnsi,
  2297. dwSizeUnicode;
  2298. DPF(7, "Entering PRV_ReadConnectionSettings");
  2299. DPF(9, "Parameters: 0x%08x, 0x%08x, 0x%08x, %lu",
  2300. lpgn, lpData, lpdwSize, bAnsi);
  2301. // Make sure we have a valid memory pointer
  2302. // Note: Take the GameNode lock so that nobody changes the flags
  2303. // for the buffers, or the buffers themselves out from under us.
  2304. ENTER_DPLGAMENODE();
  2305. if(!(lpgn->dwFlags & GN_SHARED_MEMORY_AVAILABLE))
  2306. {
  2307. hr = PRV_SetupAllSharedMemory(lpgn);
  2308. if(FAILED(hr))
  2309. {
  2310. LEAVE_DPLGAMENODE();
  2311. DPF(5, "Unable to access Connect Data memory");
  2312. return DPERR_NOTLOBBIED;
  2313. }
  2314. }
  2315. // Grab the shared buffer mutex
  2316. WaitForSingleObject(lpgn->hConnectDataMutex, INFINITE);
  2317. // Make sure we are not in wait mode without being in pending mode
  2318. lpConnControl = (LPDPLOBBYI_CONNCONTROL)lpgn->lpConnectDataBuffer;
  2319. if((lpConnControl->dwFlags & BC_WAIT_MODE) &&
  2320. !(lpConnControl->dwFlags & BC_PENDING_CONNECT))
  2321. {
  2322. hr = DPERR_UNAVAILABLE;
  2323. goto EXIT_READ_CONN_SETTINGS;
  2324. }
  2325. if(!(lpgn->dwFlags & GN_LOBBY_CLIENT)){
  2326. lpgn->dwLobbyClientProcessID = lpConnControl->CliProcId;
  2327. lpgn->hLobbyClientProcess = OpenProcess(STANDARD_RIGHTS_REQUIRED|SYNCHRONIZE ,FALSE, lpgn->dwLobbyClientProcessID);
  2328. if(!lpgn->hLobbyClientProcess){
  2329. #ifdef DEBUG
  2330. DWORD err;
  2331. err = GetLastError();
  2332. DPF(0,"Couldn't get lobby client processId %d, extended error %d\n",lpConnControl->CliProcId,err);
  2333. #endif
  2334. // lobby client is already dead, don't allow settings across.
  2335. hr = DPERR_UNAVAILABLE;
  2336. goto EXIT_READ_CONN_SETTINGS;
  2337. }
  2338. }
  2339. // Take us out of wait mode and pending mode
  2340. PRV_LeaveConnSettingsWaitMode(lpgn);
  2341. // Verify that the buffer is big enough. If it's not, OR if the lpData
  2342. // buffer pointer is NULL, just set the lpdwSize parameter to the
  2343. // correct size and return an error. Note: In our packed structure, the
  2344. // first DWORD is the size of the packed structure with Unicode strings
  2345. // and the second DWORD is the size of the packed structure with ANSI.
  2346. lpConnBuffer = (LPBYTE)lpConnControl + sizeof(DPLOBBYI_CONNCONTROL);
  2347. lpdwBuffer = (LPDWORD)lpConnBuffer;
  2348. dwSizeUnicode = *lpdwBuffer++;
  2349. dwSizeAnsi = *lpdwBuffer;
  2350. dwSize = (bAnsi) ? dwSizeAnsi : dwSizeUnicode;
  2351. if(dwSize > MAX_APPDATABUFFERSIZE-sizeof(DPLOBBYI_CONNCONTROL)){
  2352. DPF(4,"SECURITY WARN: illegal size in settings shared memory buffer");
  2353. hr=DPERR_NOTLOBBIED;
  2354. goto EXIT_READ_CONN_SETTINGS;
  2355. }
  2356. if(((*lpdwSize) < dwSize) || (!lpData))
  2357. {
  2358. if(bAnsi)
  2359. *lpdwSize = dwSizeAnsi;
  2360. else
  2361. *lpdwSize = dwSizeUnicode;
  2362. hr = DPERR_BUFFERTOOSMALL;
  2363. goto EXIT_READ_CONN_SETTINGS;
  2364. }
  2365. // Copy the DPLCONNECTION structure, taking the ANSI conversion
  2366. // into account if necessary.
  2367. if(bAnsi)
  2368. hr = PRV_UnpackageDPLCONNECTIONAnsi(lpData, lpConnBuffer);
  2369. else
  2370. hr = PRV_UnpackageDPLCONNECTIONUnicode(lpData, lpConnBuffer);
  2371. // If we haven't yet saved off the Instance guid for the game, save
  2372. // it now so that we have it for the system messages
  2373. if(IsEqualGUID(&lpgn->guidInstance, &GUID_NULL))
  2374. lpgn->guidInstance = ((LPDPLCONNECTION)lpData)->lpSessionDesc->guidInstance;
  2375. // Fall through
  2376. EXIT_READ_CONN_SETTINGS:
  2377. ReleaseMutex(lpgn->hConnectDataMutex);
  2378. LEAVE_DPLGAMENODE();
  2379. return hr;
  2380. } // PRV_ReadConnectionSettings
  2381. #undef DPF_MODNAME
  2382. #define DPF_MODNAME "PRV_GetConnectionSettings"
  2383. HRESULT PRV_GetConnectionSettings(LPDIRECTPLAYLOBBY lpDPL, DWORD dwGameID,
  2384. LPVOID lpData, LPDWORD lpdwSize, BOOL bAnsi)
  2385. {
  2386. LPDPLOBBYI_DPLOBJECT this;
  2387. LPDPLOBBYI_GAMENODE lpgn = NULL;
  2388. HRESULT hr;
  2389. BOOL bLobbyClient = TRUE;
  2390. DPF(7, "Entering PRV_GetConnectionSettings");
  2391. DPF(9, "Parameters: 0x%08x, 0x%08x, 0x%08x, 0x%08x, %lu",
  2392. lpDPL, dwGameID, lpData, lpdwSize, bAnsi);
  2393. TRY
  2394. {
  2395. if( !VALID_DPLOBBY_INTERFACE( lpDPL ))
  2396. {
  2397. return DPERR_INVALIDINTERFACE;
  2398. }
  2399. this = DPLOBJECT_FROM_INTERFACE(lpDPL);
  2400. if( !VALID_DPLOBBY_PTR( this ) )
  2401. {
  2402. return DPERR_INVALIDOBJECT;
  2403. }
  2404. if( !VALID_DWORD_PTR( lpdwSize ) )
  2405. {
  2406. DPF_ERR("lpdwSize was not a valid dword pointer!");
  2407. return DPERR_INVALIDPARAMS;
  2408. }
  2409. if(lpData)
  2410. {
  2411. if( !VALID_WRITE_PTR(lpData, *lpdwSize) )
  2412. {
  2413. DPF_ERR("lpData is not a valid output buffer of the size specified in *lpdwSize");
  2414. return DPERR_INVALIDPARAMS;
  2415. }
  2416. }
  2417. }
  2418. EXCEPT( EXCEPTION_EXECUTE_HANDLER )
  2419. {
  2420. DPF_ERR( "Exception encountered validating parameters" );
  2421. return DPERR_INVALIDPARAMS;
  2422. }
  2423. // If dwGameID is zero, we assume we are a game. In that case, the
  2424. // GameNode we are looking for should have our ProcessID.
  2425. if(!dwGameID)
  2426. {
  2427. dwGameID = GetCurrentProcessId();
  2428. bLobbyClient = FALSE;
  2429. }
  2430. lpgn = PRV_GetGameNode(this->lpgnHead, dwGameID);
  2431. if(!lpgn)
  2432. {
  2433. // If we are a game, go ahead and create the node
  2434. if(!bLobbyClient)
  2435. {
  2436. hr = PRV_AddNewGameNode(this, &lpgn, dwGameID, NULL, bLobbyClient,NULL);
  2437. if(FAILED(hr))
  2438. return hr;
  2439. }
  2440. else
  2441. return DPERR_INVALIDPARAMS;
  2442. }
  2443. // Read the data from our shared memory
  2444. hr = PRV_ReadConnectionSettings(lpgn, lpData, lpdwSize, bAnsi);
  2445. return hr;
  2446. } // PRV_GetConnectionSettings
  2447. #undef DPF_MODNAME
  2448. #define DPF_MODNAME "DPL_GetConnectionSettings"
  2449. HRESULT DPLAPI DPL_GetConnectionSettings(LPDIRECTPLAYLOBBY lpDPL,
  2450. DWORD dwGameID, LPVOID lpData, LPDWORD lpdwSize)
  2451. {
  2452. HRESULT hr;
  2453. DPF(7, "Entering DPL_GetConnectionSettings");
  2454. DPF(9, "Parameters: 0x%08x, 0x%08x, 0x%08x, 0x%08x",
  2455. lpDPL, dwGameID, lpData, lpdwSize);
  2456. ENTER_DPLOBBY();
  2457. // Set the ANSI flag to TRUE and call the internal function
  2458. hr = PRV_GetConnectionSettings(lpDPL, dwGameID, lpData,
  2459. lpdwSize, FALSE);
  2460. LEAVE_DPLOBBY();
  2461. return hr;
  2462. } // DPL_GetConnectionSettings
  2463. #undef DPF_MODNAME
  2464. #define DPF_MODNAME "PRV_RemoveGameNodeFromList"
  2465. void PRV_RemoveGameNodeFromList(LPDPLOBBYI_GAMENODE lpgn)
  2466. {
  2467. LPDPLOBBYI_GAMENODE lpgnTemp;
  2468. BOOL bFound = FALSE;
  2469. DPF(7, "Entering PRV_RemoveGameNodeFromList");
  2470. DPF(9, "Parameters: 0x%08x", lpgn);
  2471. // Get the head pointer
  2472. lpgnTemp = lpgn->this->lpgnHead;
  2473. // Make sure it's not the first node. If it is, move the head pointer
  2474. if(lpgnTemp == lpgn)
  2475. {
  2476. lpgn->this->lpgnHead = lpgn->lpgnNext;
  2477. PRV_FreeGameNode(lpgn);
  2478. return;
  2479. }
  2480. // Walk the list looking for the previous node
  2481. while(lpgnTemp)
  2482. {
  2483. if(lpgnTemp->lpgnNext == lpgn)
  2484. {
  2485. bFound = TRUE;
  2486. break;
  2487. }
  2488. lpgnTemp = lpgnTemp->lpgnNext;
  2489. }
  2490. if(!bFound)
  2491. {
  2492. DPF_ERR("Unable to remove GameNode from list!");
  2493. return;
  2494. }
  2495. // We've now got it's previous one, so remove it from the linked list
  2496. // and delete it.
  2497. lpgnTemp->lpgnNext = lpgn->lpgnNext;
  2498. PRV_FreeGameNode(lpgn);
  2499. return;
  2500. } // PRV_RemoveGameNodeFromList
  2501. #undef DPF_MODNAME
  2502. #define DPF_MODNAME "PRV_ClientTerminateNotification"
  2503. DWORD WINAPI PRV_ClientTerminateNotification(LPVOID lpParam)
  2504. {
  2505. LPDPLOBBYI_GAMENODE lpgn = (LPDPLOBBYI_GAMENODE)lpParam;
  2506. DPLMSG_SYSTEMMESSAGE msg;
  2507. HANDLE hObjects[3];
  2508. HRESULT hr;
  2509. DWORD dwResult;
  2510. DWORD dwError;
  2511. DPF(7, "Entering PRV_ClientTerminateNotification");
  2512. DPF(9, "Parameters: 0x%08x", lpParam);
  2513. // Setup the objects to wait on -- one process handle, one kill event
  2514. hObjects[0] = lpgn->hGameProcess;
  2515. hObjects[1] = lpgn->hKillTermThreadEvent;
  2516. // This extra handle is here because of a Windows 95 bug. Windows
  2517. // will occasionally miss when it walks the handle table, causing
  2518. // my thread to wait on the wrong handles. By putting a guaranteed
  2519. // invalid handle at the end of our array, the kernel will do a
  2520. // forced re-walk of the handle table and find the correct handles.
  2521. hObjects[2] = INVALID_HANDLE_VALUE;
  2522. // Wait for the event notification
  2523. while(1)
  2524. {
  2525. // Wait for the process to go away
  2526. dwResult = WaitForMultipleObjects(2, (HANDLE *)hObjects,
  2527. FALSE, INFINITE);
  2528. // If we are signalled by anything but the process going away,
  2529. // just kill the thread.
  2530. if(dwResult != WAIT_OBJECT_0)
  2531. {
  2532. if(dwResult == WAIT_FAILED)
  2533. {
  2534. // This is a Windows 95 bug -- We may have gotten
  2535. // kicked for no reason. If that was the case, we
  2536. // still have valid handles (we think), the OS
  2537. // just goofed up. So, validate the handle and if
  2538. // they are valid, just return to waiting. See
  2539. // bug #3340 for a better explanation.
  2540. dwError = GetLastError();
  2541. if(ERROR_INVALID_HANDLE == dwError)
  2542. {
  2543. DPF(1, "Wait for client termination failed due to invalid handle.");
  2544. if(!OS_IsValidHandle(hObjects[0]))
  2545. break;
  2546. if(!OS_IsValidHandle(hObjects[1]))
  2547. break;
  2548. continue;
  2549. }
  2550. DPF(0, "Wait for client termination failed (err = %u)!", dwError);
  2551. break;
  2552. }
  2553. else
  2554. {
  2555. // This is something we don't understand, so just go away.
  2556. DPF(1, "Exiting thread (result = %u).", dwResult);
  2557. ExitThread(0L);
  2558. return 0L;
  2559. }
  2560. }
  2561. else
  2562. {
  2563. // This is our process handle going away, so bail out of
  2564. // the wait loop and send the system message.
  2565. DPF(2, "Client terminated.");
  2566. break;
  2567. }
  2568. }
  2569. // Send the system message which says the app terminated
  2570. memset(&msg, 0, sizeof(DPLMSG_SYSTEMMESSAGE));
  2571. msg.dwType = DPLSYS_APPTERMINATED;
  2572. msg.guidInstance = lpgn->guidInstance;
  2573. hr = PRV_InjectMessageInQueue(lpgn, DPLAD_SYSTEM, &msg,
  2574. sizeof(DPLMSG_SYSTEMMESSAGE), TRUE);
  2575. if(FAILED(hr))
  2576. {
  2577. DPF(0, "Failed to send App Termination message, hr = 0x%08x", hr);
  2578. }
  2579. // Mark the GAMENODE as dead, but don't remove it since we know
  2580. // there are still messages in the queue.
  2581. lpgn->dwFlags |= GN_DEAD_GAME_NODE;
  2582. ExitThread(0L);
  2583. return 0L; // avoid warning.
  2584. } // PRV_ClientTerminateNotification
  2585. #undef DPF_MODNAME
  2586. #define DPF_MODNAME "DPL_WaitForConnectionSettings"
  2587. HRESULT DPLAPI DPL_WaitForConnectionSettings(LPDIRECTPLAYLOBBY lpDPL, DWORD dwFlags)
  2588. {
  2589. LPDPLOBBYI_DPLOBJECT this;
  2590. LPDPLOBBYI_GAMENODE lpgn = NULL;
  2591. LPDPLOBBYI_CONNCONTROL lpConnControl = NULL;
  2592. LPDPLOBBYI_BUFFERCONTROL lpBuffControl = NULL;
  2593. HRESULT hr = DP_OK;
  2594. BOOL bCreated = FALSE;
  2595. DWORD dwProcessID;
  2596. BOOL bGameCreate = FALSE;
  2597. BOOL bMessages = TRUE;
  2598. DPF(7, "Entering DPL_WaitForConnectionSettings");
  2599. DPF(9, "Parameters: 0x%08x, 0x%08x", lpDPL, dwFlags);
  2600. ENTER_DPLOBBY();
  2601. TRY
  2602. {
  2603. if( !VALID_DPLOBBY_INTERFACE( lpDPL ))
  2604. {
  2605. LEAVE_DPLOBBY();
  2606. return DPERR_INVALIDINTERFACE;
  2607. }
  2608. this = DPLOBJECT_FROM_INTERFACE(lpDPL);
  2609. if( !VALID_DPLOBBY_PTR( this ) )
  2610. {
  2611. LEAVE_DPLOBBY();
  2612. return DPERR_INVALIDOBJECT;
  2613. }
  2614. if(!VALID_WAIT_FLAGS(dwFlags))
  2615. {
  2616. LEAVE_DPLOBBY();
  2617. return DPERR_INVALIDFLAGS;
  2618. }
  2619. }
  2620. EXCEPT( EXCEPTION_EXECUTE_HANDLER )
  2621. {
  2622. LEAVE_DPLOBBY();
  2623. DPF_ERR( "Exception encountered validating parameters" );
  2624. return DPERR_INVALIDPARAMS;
  2625. }
  2626. // Get the game node
  2627. dwProcessID = GetCurrentProcessId();
  2628. lpgn = PRV_GetGameNode(this->lpgnHead, dwProcessID);
  2629. if(!lpgn)
  2630. {
  2631. // Create the game node
  2632. hr = PRV_AddNewGameNode(this, &lpgn, dwProcessID, NULL, FALSE, NULL);
  2633. if(FAILED(hr))
  2634. {
  2635. DPF_ERRVAL("Failed creating game node, hr = 0x%08x", hr);
  2636. goto EXIT_WAIT_FOR_CONN_SETTINGS;
  2637. }
  2638. // Set our flag saying we just created the game node
  2639. bCreated = TRUE;
  2640. }
  2641. // when doing a wait for connection settings, we do NOT use the
  2642. // IPC_GUID, this is because the lobby launching us may not have
  2643. // provided the GUID.
  2644. lpgn->dwFlags &= ~(GN_IPCGUID_SET);
  2645. // Make sure we have a valid memory pointer
  2646. // Note: Take the GameNode lock so that nobody changes the flags
  2647. // for the buffers, or the buffers themselves out from under us.
  2648. ENTER_DPLGAMENODE();
  2649. if(!(lpgn->dwFlags & GN_SHARED_MEMORY_AVAILABLE))
  2650. {
  2651. // First we need to try to setup access to the buffers assuming
  2652. // they already exist (we were lobby launched). If this doesn't
  2653. // work, then we need to create them.
  2654. hr = PRV_SetupAllSharedMemory(lpgn);
  2655. if(FAILED(hr))
  2656. {
  2657. // We don't have any memory, so set it up
  2658. // HACK!!!! -- WaitForConnectionSettings may get called from the game
  2659. // without having been lobbied. If that is the case, we need to
  2660. // create the shared memory with the game's process ID (this process)
  2661. // so we'll set the lobby client flag to fake out the creation
  2662. if(!(lpgn->dwFlags & GN_LOBBY_CLIENT))
  2663. {
  2664. // Fake the setup routine by setting the lobby client flag
  2665. lpgn->dwFlags |= GN_LOBBY_CLIENT;
  2666. // Set our flag
  2667. bGameCreate = TRUE;
  2668. }
  2669. // Setup the shared buffers
  2670. hr = PRV_SetupAllSharedMemory(lpgn);
  2671. // HACK!!!! -- Reset the settings we changed to fake the setup routines
  2672. if(bGameCreate)
  2673. {
  2674. lpgn->dwFlags &= (~GN_LOBBY_CLIENT);
  2675. }
  2676. }
  2677. if(FAILED(hr))
  2678. {
  2679. LEAVE_DPLGAMENODE();
  2680. DPF_ERRVAL("Unable to access Connect Data memory, hr = 0x%08x", hr);
  2681. goto EXIT_WAIT_FOR_CONN_SETTINGS;
  2682. }
  2683. }
  2684. // Drop the lock
  2685. LEAVE_DPLGAMENODE();
  2686. // If we are in wait mode, and the caller wants to end it, do so,
  2687. // otherwise, just return success
  2688. WaitForSingleObject(lpgn->hConnectDataMutex, INFINITE);
  2689. lpConnControl = (LPDPLOBBYI_CONNCONTROL)lpgn->lpConnectDataBuffer;
  2690. if(lpConnControl->dwFlags & BC_WAIT_MODE)
  2691. {
  2692. if(dwFlags & DPLWAIT_CANCEL)
  2693. {
  2694. // Release Mutex
  2695. ReleaseMutex(lpgn->hConnectDataMutex);
  2696. // Take us out of wait mode
  2697. PRV_LeaveConnSettingsWaitMode(lpgn);
  2698. goto EXIT_WAIT_FOR_CONN_SETTINGS;
  2699. }
  2700. else
  2701. {
  2702. // Release Mutex
  2703. ReleaseMutex(lpgn->hConnectDataMutex);
  2704. // Might as well just return OK since we're already doing it
  2705. DPF_ERR("We're already in wait mode");
  2706. goto EXIT_WAIT_FOR_CONN_SETTINGS;
  2707. }
  2708. }
  2709. else
  2710. {
  2711. // We're not it wait mode, and the caller asked us to turn it off
  2712. if(dwFlags & DPLWAIT_CANCEL)
  2713. {
  2714. // Release Mutex
  2715. ReleaseMutex(lpgn->hConnectDataMutex);
  2716. DPF_ERR("Cannot turn off wait mode - we're not in wait mode");
  2717. hr = DPERR_UNAVAILABLE;
  2718. goto EXIT_WAIT_FOR_CONN_SETTINGS;
  2719. }
  2720. }
  2721. // Release Mutex
  2722. ReleaseMutex(lpgn->hConnectDataMutex);
  2723. // See if a lobby client exists on the other side, if it does, we
  2724. // need to tell him we are going into wait mode by sending him an
  2725. // AppTerminated message.
  2726. PRV_SendStandardSystemMessage(lpDPL, DPLSYS_APPTERMINATED, 0);
  2727. // Go into wait mode
  2728. PRV_EnterConnSettingsWaitMode(lpgn);
  2729. // Kick the receive thread to empty the buffer (just in case there
  2730. // are any messages in it)
  2731. SetEvent(lpgn->hLobbyWriteEvent);
  2732. // Spin waiting for the buffer to get emptied
  2733. while(bMessages)
  2734. {
  2735. // Grab the mutex for the lobby write buffer
  2736. WaitForSingleObject(lpgn->hLobbyWriteMutex, INFINITE);
  2737. lpBuffControl = (LPDPLOBBYI_BUFFERCONTROL)lpgn->lpLobbyWriteBuffer;
  2738. if(!lpBuffControl->dwMessages)
  2739. bMessages = FALSE;
  2740. // Drop the mutex
  2741. ReleaseMutex(lpgn->hLobbyWriteMutex);
  2742. if(bMessages)
  2743. {
  2744. // Now sleep to give the receive thread a chance to work
  2745. Sleep(50);
  2746. }
  2747. }
  2748. // Now clean out the message queue
  2749. PRV_CleanUpQueue(lpgn);
  2750. // Fall through
  2751. EXIT_WAIT_FOR_CONN_SETTINGS:
  2752. LEAVE_DPLOBBY();
  2753. return hr;
  2754. } // DPL_WaitForConnectionSettings