1 /* DirectPlay & DirectPlayLobby messaging implementation 2 * 3 * Copyright 2000,2001 - Peter Hunnisett 4 * 5 * This library is free software; you can redistribute it and/or 6 * modify it under the terms of the GNU Lesser General Public 7 * License as published by the Free Software Foundation; either 8 * version 2.1 of the License, or (at your option) any later version. 9 * 10 * This library is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 * Lesser General Public License for more details. 14 * 15 * You should have received a copy of the GNU Lesser General Public 16 * License along with this library; if not, write to the Free Software 17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 18 * 19 * NOTES 20 * o Messaging interface required for both DirectPlay and DirectPlayLobby. 21 */ 22 23 #include "dplayx_global.h" 24 25 typedef struct tagMSGTHREADINFO 26 { 27 HANDLE hStart; 28 HANDLE hDeath; 29 HANDLE hSettingRead; 30 HANDLE hNotifyEvent; 31 } MSGTHREADINFO, *LPMSGTHREADINFO; 32 33 static DWORD CALLBACK DPL_MSG_ThreadMain( LPVOID lpContext ); 34 static void *DP_MSG_ExpectReply( IDirectPlayImpl *This, DPSP_SENDDATA *data, DWORD dwWaitTime, 35 WORD wReplyCommandId, void **lplpReplyMsg, DWORD *lpdwMsgBodySize ); 36 37 38 /* Create the message reception thread to allow the application to receive 39 * asynchronous message reception 40 */ 41 DWORD CreateLobbyMessageReceptionThread( HANDLE hNotifyEvent, HANDLE hStart, 42 HANDLE hDeath, HANDLE hConnRead ) 43 { 44 DWORD dwMsgThreadId; 45 LPMSGTHREADINFO lpThreadInfo; 46 HANDLE hThread; 47 48 lpThreadInfo = HeapAlloc( GetProcessHeap(), 0, sizeof( *lpThreadInfo ) ); 49 if( lpThreadInfo == NULL ) 50 { 51 return 0; 52 } 53 54 /* The notify event may or may not exist. Depends if async comm or not */ 55 if( hNotifyEvent && 56 !DuplicateHandle( GetCurrentProcess(), hNotifyEvent, 57 GetCurrentProcess(), &lpThreadInfo->hNotifyEvent, 58 0, FALSE, DUPLICATE_SAME_ACCESS ) ) 59 { 60 ERR( "Unable to duplicate event handle\n" ); 61 goto error; 62 } 63 64 /* These 3 handles don't need to be duplicated because we don't keep a 65 * reference to them where they're created. They're created specifically 66 * for the message thread 67 */ 68 lpThreadInfo->hStart = hStart; 69 lpThreadInfo->hDeath = hDeath; 70 lpThreadInfo->hSettingRead = hConnRead; 71 72 hThread = CreateThread( NULL, /* Security attribs */ 73 0, /* Stack */ 74 DPL_MSG_ThreadMain, /* Msg reception function */ 75 lpThreadInfo, /* Msg reception func parameter */ 76 0, /* Flags */ 77 &dwMsgThreadId /* Updated with thread id */ 78 ); 79 if ( hThread == NULL ) 80 { 81 ERR( "Unable to create msg thread\n" ); 82 goto error; 83 } 84 85 CloseHandle(hThread); 86 87 return dwMsgThreadId; 88 89 error: 90 91 HeapFree( GetProcessHeap(), 0, lpThreadInfo ); 92 93 return 0; 94 } 95 96 static DWORD CALLBACK DPL_MSG_ThreadMain( LPVOID lpContext ) 97 { 98 LPMSGTHREADINFO lpThreadInfo = lpContext; 99 DWORD dwWaitResult; 100 101 TRACE( "Msg thread created. Waiting on app startup\n" ); 102 103 /* Wait to ensure that the lobby application is started w/ 1 min timeout */ 104 dwWaitResult = WaitForSingleObject( lpThreadInfo->hStart, 10000 /* 10 sec */ ); 105 if( dwWaitResult == WAIT_TIMEOUT ) 106 { 107 FIXME( "Should signal app/wait creation failure (0x%08x)\n", dwWaitResult ); 108 goto end_of_thread; 109 } 110 111 /* Close this handle as it's not needed anymore */ 112 CloseHandle( lpThreadInfo->hStart ); 113 lpThreadInfo->hStart = 0; 114 115 /* Wait until the lobby knows what it is */ 116 dwWaitResult = WaitForSingleObject( lpThreadInfo->hSettingRead, INFINITE ); 117 if( dwWaitResult == WAIT_TIMEOUT ) 118 { 119 ERR( "App Read connection setting timeout fail (0x%08x)\n", dwWaitResult ); 120 } 121 122 /* Close this handle as it's not needed anymore */ 123 CloseHandle( lpThreadInfo->hSettingRead ); 124 lpThreadInfo->hSettingRead = 0; 125 126 TRACE( "App created && initialized starting main message reception loop\n" ); 127 128 for ( ;; ) 129 { 130 MSG lobbyMsg; 131 GetMessageW( &lobbyMsg, 0, 0, 0 ); 132 } 133 134 end_of_thread: 135 TRACE( "Msg thread exiting!\n" ); 136 HeapFree( GetProcessHeap(), 0, lpThreadInfo ); 137 138 return 0; 139 } 140 141 /* DP messaging stuff */ 142 static HANDLE DP_MSG_BuildAndLinkReplyStruct( IDirectPlayImpl *This, 143 DP_MSG_REPLY_STRUCT_LIST *lpReplyStructList, WORD wReplyCommandId ) 144 { 145 lpReplyStructList->replyExpected.hReceipt = CreateEventW( NULL, FALSE, FALSE, NULL ); 146 lpReplyStructList->replyExpected.wExpectedReply = wReplyCommandId; 147 lpReplyStructList->replyExpected.lpReplyMsg = NULL; 148 lpReplyStructList->replyExpected.dwMsgBodySize = 0; 149 150 /* Insert into the message queue while locked */ 151 EnterCriticalSection( &This->lock ); 152 DPQ_INSERT( This->dp2->repliesExpected, lpReplyStructList, repliesExpected ); 153 LeaveCriticalSection( &This->lock ); 154 155 return lpReplyStructList->replyExpected.hReceipt; 156 } 157 158 static 159 LPVOID DP_MSG_CleanReplyStruct( LPDP_MSG_REPLY_STRUCT_LIST lpReplyStructList, 160 LPVOID* lplpReplyMsg, LPDWORD lpdwMsgBodySize ) 161 { 162 CloseHandle( lpReplyStructList->replyExpected.hReceipt ); 163 164 *lplpReplyMsg = lpReplyStructList->replyExpected.lpReplyMsg; 165 *lpdwMsgBodySize = lpReplyStructList->replyExpected.dwMsgBodySize; 166 167 return lpReplyStructList->replyExpected.lpReplyMsg; 168 } 169 170 HRESULT DP_MSG_SendRequestPlayerId( IDirectPlayImpl *This, DWORD dwFlags, DPID *lpdpidAllocatedId ) 171 { 172 LPVOID lpMsg; 173 LPDPMSG_REQUESTNEWPLAYERID lpMsgBody; 174 DWORD dwMsgSize; 175 HRESULT hr = DP_OK; 176 177 dwMsgSize = This->dp2->spData.dwSPHeaderSize + sizeof( *lpMsgBody ); 178 179 lpMsg = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, dwMsgSize ); 180 181 lpMsgBody = (LPDPMSG_REQUESTNEWPLAYERID)( (BYTE*)lpMsg + 182 This->dp2->spData.dwSPHeaderSize ); 183 184 /* Compose dplay message envelope */ 185 lpMsgBody->envelope.dwMagic = DPMSGMAGIC_DPLAYMSG; 186 lpMsgBody->envelope.wCommandId = DPMSGCMD_REQUESTNEWPLAYERID; 187 lpMsgBody->envelope.wVersion = DPMSGVER_DP6; 188 189 /* Compose the body of the message */ 190 lpMsgBody->dwFlags = dwFlags; 191 192 /* Send the message */ 193 { 194 DPSP_SENDDATA data; 195 196 data.dwFlags = DPSEND_GUARANTEED; 197 data.idPlayerTo = 0; /* Name server */ 198 data.idPlayerFrom = 0; /* Sending from DP */ 199 data.lpMessage = lpMsg; 200 data.dwMessageSize = dwMsgSize; 201 data.bSystemMessage = TRUE; /* Allow reply to be sent */ 202 data.lpISP = This->dp2->spData.lpISP; 203 204 TRACE( "Asking for player id w/ dwFlags 0x%08x\n", 205 lpMsgBody->dwFlags ); 206 207 DP_MSG_ExpectReply( This, &data, DPMSG_DEFAULT_WAIT_TIME, DPMSGCMD_NEWPLAYERIDREPLY, 208 &lpMsg, &dwMsgSize ); 209 } 210 211 /* Need to examine the data and extract the new player id */ 212 if( SUCCEEDED(hr) ) 213 { 214 LPCDPMSG_NEWPLAYERIDREPLY lpcReply; 215 216 lpcReply = lpMsg; 217 218 *lpdpidAllocatedId = lpcReply->dpidNewPlayerId; 219 220 TRACE( "Received reply for id = 0x%08x\n", lpcReply->dpidNewPlayerId ); 221 222 /* FIXME: I think that the rest of the message has something to do 223 * with remote data for the player that perhaps I need to setup. 224 * However, with the information that is passed, all that it could 225 * be used for is a standardized initialization value, which I'm 226 * guessing we can do without. Unless the message content is the same 227 * for several different messages? 228 */ 229 230 HeapFree( GetProcessHeap(), 0, lpMsg ); 231 } 232 233 return hr; 234 } 235 236 HRESULT DP_MSG_ForwardPlayerCreation( IDirectPlayImpl *This, DPID dpidServer ) 237 { 238 LPVOID lpMsg; 239 LPDPMSG_FORWARDADDPLAYER lpMsgBody; 240 DWORD dwMsgSize; 241 HRESULT hr = DP_OK; 242 243 dwMsgSize = This->dp2->spData.dwSPHeaderSize + sizeof( *lpMsgBody ); 244 245 lpMsg = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, dwMsgSize ); 246 247 lpMsgBody = (LPDPMSG_FORWARDADDPLAYER)( (BYTE*)lpMsg + 248 This->dp2->spData.dwSPHeaderSize ); 249 250 /* Compose dplay message envelope */ 251 lpMsgBody->envelope.dwMagic = DPMSGMAGIC_DPLAYMSG; 252 lpMsgBody->envelope.wCommandId = DPMSGCMD_FORWARDADDPLAYER; 253 lpMsgBody->envelope.wVersion = DPMSGVER_DP6; 254 255 #if 0 256 { 257 LPBYTE lpPData; 258 DWORD dwDataSize; 259 260 /* SP Player remote data needs to be propagated at some point - is this the point? */ 261 IDirectPlaySP_GetSPPlayerData( This->dp2->spData.lpISP, 0, &lpPData, &dwDataSize, DPSET_REMOTE ); 262 263 ERR( "Player Data size is 0x%08lx\n" 264 "[%02x%02x %02x%02x %02x%02x %02x%02x %02x%02x %02x%02x %02x%02x %02x%02x]\n" 265 "[%02x%02x %02x%02x %02x%02x %02x%02x %02x%02x %02x%02x %02x%02x %02x%02x]\n", 266 267 dwDataSize, 268 lpPData[0], lpPData[1], lpPData[2], lpPData[3], lpPData[4], 269 lpPData[5], lpPData[6], lpPData[7], lpPData[8], lpPData[9], 270 lpPData[10], lpPData[11], lpPData[12], lpPData[13], lpPData[14], 271 lpPData[15], lpPData[16], lpPData[17], lpPData[18], lpPData[19], 272 lpPData[20], lpPData[21], lpPData[22], lpPData[23], lpPData[24], 273 lpPData[25], lpPData[26], lpPData[27], lpPData[28], lpPData[29], 274 lpPData[30], lpPData[31] 275 ); 276 DebugBreak(); 277 } 278 #endif 279 280 /* Compose body of message */ 281 lpMsgBody->dpidAppServer = dpidServer; 282 lpMsgBody->unknown2[0] = 0x0; 283 lpMsgBody->unknown2[1] = 0x1c; 284 lpMsgBody->unknown2[2] = 0x6c; 285 lpMsgBody->unknown2[3] = 0x50; 286 lpMsgBody->unknown2[4] = 0x9; 287 288 lpMsgBody->dpidAppServer2 = dpidServer; 289 lpMsgBody->unknown3[0] = 0x0; 290 lpMsgBody->unknown3[1] = 0x0; 291 lpMsgBody->unknown3[2] = 0x20; 292 lpMsgBody->unknown3[3] = 0x0; 293 lpMsgBody->unknown3[4] = 0x0; 294 295 lpMsgBody->dpidAppServer3 = dpidServer; 296 lpMsgBody->unknown4[0] = 0x30; 297 lpMsgBody->unknown4[1] = 0xb; 298 lpMsgBody->unknown4[2] = 0x0; 299 300 lpMsgBody->unknown4[3] = NS_GetNsMagic( This->dp2->lpNameServerData ) - 301 0x02000000; 302 TRACE( "Setting first magic to 0x%08x\n", lpMsgBody->unknown4[3] ); 303 304 lpMsgBody->unknown4[4] = 0x0; 305 lpMsgBody->unknown4[5] = 0x0; 306 lpMsgBody->unknown4[6] = 0x0; 307 308 lpMsgBody->unknown4[7] = NS_GetNsMagic( This->dp2->lpNameServerData ); 309 TRACE( "Setting second magic to 0x%08x\n", lpMsgBody->unknown4[7] ); 310 311 lpMsgBody->unknown4[8] = 0x0; 312 lpMsgBody->unknown4[9] = 0x0; 313 lpMsgBody->unknown4[10] = 0x0; 314 lpMsgBody->unknown4[11] = 0x0; 315 316 lpMsgBody->unknown5[0] = 0x0; 317 lpMsgBody->unknown5[1] = 0x0; 318 319 /* Send the message */ 320 { 321 DPSP_SENDDATA data; 322 323 data.dwFlags = DPSEND_GUARANTEED; 324 data.idPlayerTo = 0; /* Name server */ 325 data.idPlayerFrom = dpidServer; /* Sending from session server */ 326 data.lpMessage = lpMsg; 327 data.dwMessageSize = dwMsgSize; 328 data.bSystemMessage = TRUE; /* Allow reply to be sent */ 329 data.lpISP = This->dp2->spData.lpISP; 330 331 TRACE( "Sending forward player request with 0x%08x\n", dpidServer ); 332 333 lpMsg = DP_MSG_ExpectReply( This, &data, 334 DPMSG_WAIT_60_SECS, 335 DPMSGCMD_GETNAMETABLEREPLY, 336 &lpMsg, &dwMsgSize ); 337 } 338 339 /* Need to examine the data and extract the new player id */ 340 if( lpMsg != NULL ) 341 { 342 FIXME( "Name Table reply received: stub\n" ); 343 } 344 345 return hr; 346 } 347 348 /* Queue up a structure indicating that we want a reply of type wReplyCommandId. DPlay does 349 * not seem to offer any way of uniquely differentiating between replies of the same type 350 * relative to the request sent. There is an implicit assumption that there will be no 351 * ordering issues on sends and receives from the opposite machine. No wonder MS is not 352 * a networking company. 353 */ 354 static void *DP_MSG_ExpectReply( IDirectPlayImpl *This, DPSP_SENDDATA *lpData, DWORD dwWaitTime, 355 WORD wReplyCommandId, void **lplpReplyMsg, DWORD *lpdwMsgBodySize ) 356 { 357 HRESULT hr; 358 HANDLE hMsgReceipt; 359 DP_MSG_REPLY_STRUCT_LIST replyStructList; 360 DWORD dwWaitReturn; 361 362 /* Setup for receipt */ 363 hMsgReceipt = DP_MSG_BuildAndLinkReplyStruct( This, &replyStructList, 364 wReplyCommandId ); 365 366 TRACE( "Sending msg and expecting cmd %u in reply within %u ticks\n", 367 wReplyCommandId, dwWaitTime ); 368 hr = (*This->dp2->spData.lpCB->Send)( lpData ); 369 370 if( FAILED(hr) ) 371 { 372 ERR( "Send failed: %s\n", DPLAYX_HresultToString( hr ) ); 373 return NULL; 374 } 375 376 /* The reply message will trigger the hMsgReceipt event effectively switching 377 * control back to this thread. See DP_MSG_ReplyReceived. 378 */ 379 dwWaitReturn = WaitForSingleObject( hMsgReceipt, dwWaitTime ); 380 if( dwWaitReturn != WAIT_OBJECT_0 ) 381 { 382 ERR( "Wait failed 0x%08x\n", dwWaitReturn ); 383 return NULL; 384 } 385 386 /* Clean Up */ 387 return DP_MSG_CleanReplyStruct( &replyStructList, lplpReplyMsg, lpdwMsgBodySize ); 388 } 389 390 /* Determine if there is a matching request for this incoming message and then copy 391 * all important data. It is quite silly to have to copy the message, but the documents 392 * indicate that a copy is taken. Silly really. 393 */ 394 void DP_MSG_ReplyReceived( IDirectPlayImpl *This, WORD wCommandId, const void *lpcMsgBody, 395 DWORD dwMsgBodySize ) 396 { 397 LPDP_MSG_REPLY_STRUCT_LIST lpReplyList; 398 399 #if 0 400 if( wCommandId == DPMSGCMD_FORWARDADDPLAYER ) 401 { 402 DebugBreak(); 403 } 404 #endif 405 406 /* Find, and immediately remove (to avoid double triggering), the appropriate entry. Call locked to 407 * avoid problems. 408 */ 409 EnterCriticalSection( &This->lock ); 410 DPQ_REMOVE_ENTRY( This->dp2->repliesExpected, repliesExpected, replyExpected.wExpectedReply, 411 ==, wCommandId, lpReplyList ); 412 LeaveCriticalSection( &This->lock ); 413 414 if( lpReplyList != NULL ) 415 { 416 lpReplyList->replyExpected.dwMsgBodySize = dwMsgBodySize; 417 lpReplyList->replyExpected.lpReplyMsg = HeapAlloc( GetProcessHeap(), 418 HEAP_ZERO_MEMORY, 419 dwMsgBodySize ); 420 CopyMemory( lpReplyList->replyExpected.lpReplyMsg, 421 lpcMsgBody, dwMsgBodySize ); 422 423 /* Signal the thread which sent the message that it has a reply */ 424 SetEvent( lpReplyList->replyExpected.hReceipt ); 425 } 426 else 427 { 428 ERR( "No receipt event set - only expecting in reply mode\n" ); 429 DebugBreak(); 430 } 431 } 432 433 void DP_MSG_ToSelf( IDirectPlayImpl *This, DPID dpidSelf ) 434 { 435 LPVOID lpMsg; 436 LPDPMSG_SENDENVELOPE lpMsgBody; 437 DWORD dwMsgSize; 438 439 dwMsgSize = This->dp2->spData.dwSPHeaderSize + sizeof( *lpMsgBody ); 440 441 lpMsg = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, dwMsgSize ); 442 443 lpMsgBody = (LPDPMSG_SENDENVELOPE)( (BYTE*)lpMsg + 444 This->dp2->spData.dwSPHeaderSize ); 445 446 /* Compose dplay message envelope */ 447 lpMsgBody->dwMagic = DPMSGMAGIC_DPLAYMSG; 448 lpMsgBody->wCommandId = DPMSGCMD_JUSTENVELOPE; 449 lpMsgBody->wVersion = DPMSGVER_DP6; 450 451 /* Send the message to ourselves */ 452 { 453 DPSP_SENDDATA data; 454 455 data.dwFlags = 0; 456 data.idPlayerTo = dpidSelf; /* Sending to session server */ 457 data.idPlayerFrom = 0; /* Sending from session server */ 458 data.lpMessage = lpMsg; 459 data.dwMessageSize = dwMsgSize; 460 data.bSystemMessage = TRUE; /* Allow reply to be sent */ 461 data.lpISP = This->dp2->spData.lpISP; 462 463 lpMsg = DP_MSG_ExpectReply( This, &data, 464 DPMSG_WAIT_5_SECS, 465 DPMSGCMD_JUSTENVELOPE, 466 &lpMsg, &dwMsgSize ); 467 } 468 } 469 470 void DP_MSG_ErrorReceived( IDirectPlayImpl *This, WORD wCommandId, const void *lpMsgBody, 471 DWORD dwMsgBodySize ) 472 { 473 LPCDPMSG_FORWARDADDPLAYERNACK lpcErrorMsg; 474 475 lpcErrorMsg = lpMsgBody; 476 477 ERR( "Received error message %u. Error is %s\n", 478 wCommandId, DPLAYX_HresultToString( lpcErrorMsg->errorCode) ); 479 DebugBreak(); 480 } 481