1 /* DirectPlay & DirectPlayLobby messaging implementation
3 * Copyright 2000 - Peter Hunnisett
5 * <presently under construction - contact hunnise@nortelnetworks.com>
10 #include "debugtools.h"
16 #include "dplayx_messages.h"
17 #include "dplay_global.h"
18 #include "dplayx_global.h"
20 DEFAULT_DEBUG_CHANNEL(dplay);
22 typedef struct tagMSGTHREADINFO
28 } MSGTHREADINFO, *LPMSGTHREADINFO;
30 static DWORD CALLBACK DPL_MSG_ThreadMain( LPVOID lpContext );
31 static LPVOID DP_MSG_ExpectReply( IDirectPlay2AImpl* This, LPDPSP_SENDDATA data,
32 DWORD dwWaitTime, WORD wReplyCommandId,
33 LPVOID* lplpReplyMsg, LPDWORD lpdwMsgBodySize );
36 /* Create the message reception thread to allow the application to receive
37 * asynchronous message reception
39 DWORD CreateLobbyMessageReceptionThread( HANDLE hNotifyEvent, HANDLE hStart,
40 HANDLE hDeath, HANDLE hConnRead )
43 LPMSGTHREADINFO lpThreadInfo;
45 lpThreadInfo = HeapAlloc( GetProcessHeap(), 0, sizeof( *lpThreadInfo ) );
46 if( lpThreadInfo == NULL )
51 /* The notify event may or may not exist. Depends if async comm or not */
53 !DuplicateHandle( GetCurrentProcess(), hNotifyEvent,
54 GetCurrentProcess(), &lpThreadInfo->hNotifyEvent,
55 0, FALSE, DUPLICATE_SAME_ACCESS ) )
57 ERR( "Unable to duplicate event handle\n" );
61 /* These 3 handles don't need to be duplicated because we don't keep a
62 * reference to them where they're created. They're created specifically
63 * for the message thread
65 lpThreadInfo->hStart = hStart;
66 lpThreadInfo->hDeath = hDeath;
67 lpThreadInfo->hSettingRead = hConnRead;
69 if( !CreateThread( NULL, /* Security attribs */
71 DPL_MSG_ThreadMain, /* Msg reception function */
72 lpThreadInfo, /* Msg reception func parameter */
74 &dwMsgThreadId /* Updated with thread id */
78 ERR( "Unable to create msg thread\n" );
82 /* FIXME: Should I be closing the handle to the thread or does that
83 terminate the thread? */
89 HeapFree( GetProcessHeap(), 0, lpThreadInfo );
94 static DWORD CALLBACK DPL_MSG_ThreadMain( LPVOID lpContext )
96 LPMSGTHREADINFO lpThreadInfo = (LPMSGTHREADINFO)lpContext;
99 TRACE( "Msg thread created. Waiting on app startup\n" );
101 /* Wait to ensure that the lobby application is started w/ 1 min timeout */
102 dwWaitResult = WaitForSingleObject( lpThreadInfo->hStart, 10000 /* 10 sec */ );
103 if( dwWaitResult == WAIT_TIMEOUT )
105 FIXME( "Should signal app/wait creation failure (0x%08lx)\n", dwWaitResult );
109 /* Close this handle as it's not needed anymore */
110 CloseHandle( lpThreadInfo->hStart );
111 lpThreadInfo->hStart = 0;
113 /* Wait until the lobby knows what it is */
114 dwWaitResult = WaitForSingleObject( lpThreadInfo->hSettingRead, INFINITE );
115 if( dwWaitResult == WAIT_TIMEOUT )
117 ERR( "App Read connection setting timeout fail (0x%08lx)\n", dwWaitResult );
120 /* Close this handle as it's not needed anymore */
121 CloseHandle( lpThreadInfo->hSettingRead );
122 lpThreadInfo->hSettingRead = 0;
124 TRACE( "App created && intialized starting main message reception loop\n" );
130 HANDLE hNullHandle = NULL;
132 HANDLE hNullHandle = 0;
135 GetMessageW( &lobbyMsg, hNullHandle, 0, 0 );
139 TRACE( "Msg thread exiting!\n" );
140 HeapFree( GetProcessHeap(), 0, lpThreadInfo );
145 /* DP messageing stuff */
146 static HANDLE DP_MSG_BuildAndLinkReplyStruct( IDirectPlay2Impl* This,
147 LPDP_MSG_REPLY_STRUCT_LIST lpReplyStructList,
148 WORD wReplyCommandId );
149 static LPVOID DP_MSG_CleanReplyStruct( LPDP_MSG_REPLY_STRUCT_LIST lpReplyStructList,
150 LPVOID* lplpReplyMsg, LPDWORD lpdwMsgBodySize );
154 HANDLE DP_MSG_BuildAndLinkReplyStruct( IDirectPlay2Impl* This,
155 LPDP_MSG_REPLY_STRUCT_LIST lpReplyStructList, WORD wReplyCommandId )
157 lpReplyStructList->replyExpected.hReceipt = CreateEventA( NULL, FALSE, FALSE, NULL );
158 lpReplyStructList->replyExpected.wExpectedReply = wReplyCommandId;
159 lpReplyStructList->replyExpected.lpReplyMsg = NULL;
160 lpReplyStructList->replyExpected.dwMsgBodySize = 0;
162 /* Insert into the message queue while locked */
163 EnterCriticalSection( &This->unk->DP_lock );
164 DPQ_INSERT( This->dp2->replysExpected, lpReplyStructList, replysExpected );
165 LeaveCriticalSection( &This->unk->DP_lock );
167 return lpReplyStructList->replyExpected.hReceipt;
171 LPVOID DP_MSG_CleanReplyStruct( LPDP_MSG_REPLY_STRUCT_LIST lpReplyStructList,
172 LPVOID* lplpReplyMsg, LPDWORD lpdwMsgBodySize )
174 CloseHandle( lpReplyStructList->replyExpected.hReceipt );
176 *lplpReplyMsg = lpReplyStructList->replyExpected.lpReplyMsg;
177 *lpdwMsgBodySize = lpReplyStructList->replyExpected.dwMsgBodySize;
179 return lpReplyStructList->replyExpected.lpReplyMsg;
182 HRESULT DP_MSG_SendRequestPlayerId( IDirectPlay2AImpl* This, DWORD dwFlags,
183 LPDPID lpdpidAllocatedId )
186 LPDPMSG_REQUESTNEWPLAYERID lpMsgBody;
190 dwMsgSize = This->dp2->spData.dwSPHeaderSize + sizeof( *lpMsgBody );
192 lpMsg = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, dwMsgSize );
194 lpMsgBody = (LPDPMSG_REQUESTNEWPLAYERID)( (BYTE*)lpMsg +
195 This->dp2->spData.dwSPHeaderSize );
197 /* Compose dplay message envelope */
198 lpMsgBody->envelope.dwMagic = DPMSGMAGIC_DPLAYMSG;
199 lpMsgBody->envelope.wCommandId = DPMSGCMD_REQUESTNEWPLAYERID;
200 lpMsgBody->envelope.wVersion = DPMSGVER_DP6;
202 /* Compose the body of the message */
203 lpMsgBody->dwFlags = dwFlags;
205 /* Send the message */
209 data.dwFlags = DPSEND_GUARANTEED;
210 data.idPlayerTo = 0; /* Name server */
211 data.idPlayerFrom = 0; /* Sending from DP */
212 data.lpMessage = lpMsg;
213 data.dwMessageSize = dwMsgSize;
214 data.bSystemMessage = TRUE; /* Allow reply to be sent */
215 data.lpISP = This->dp2->spData.lpISP;
217 TRACE( "Asking for player id w/ dwFlags 0x%08lx\n",
218 lpMsgBody->dwFlags );
221 DP_MSG_ExpectReply( This, &data, DPMSG_DEFAULT_WAIT_TIME, DPMSGCMD_NEWPLAYERIDREPLY,
222 &lpMsg, &dwMsgSize );
225 /* Need to examine the data and extract the new player id */
228 LPCDPMSG_NEWPLAYERIDREPLY lpcReply;
230 lpcReply = (LPCDPMSG_NEWPLAYERIDREPLY)lpMsg;
232 *lpdpidAllocatedId = lpcReply->dpidNewPlayerId;
234 TRACE( "Received reply for id = 0x%08lx\n", lpcReply->dpidNewPlayerId );
236 /* FIXME: I think that the rest of the message has something to do
237 * with remote data for the player that perhaps I need to setup.
238 * However, with the information that is passed, all that it could
239 * be used for is a standardized intialization value, which I'm
240 * guessing we can do without. Unless the message content is the same
241 * for several different messages?
244 HeapFree( GetProcessHeap(), 0, lpMsg );
250 HRESULT DP_MSG_ForwardPlayerCreation( IDirectPlay2AImpl* This, DPID dpidServer )
253 LPDPMSG_FORWARDADDPLAYER lpMsgBody;
257 dwMsgSize = This->dp2->spData.dwSPHeaderSize + sizeof( *lpMsgBody );
259 lpMsg = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, dwMsgSize );
261 lpMsgBody = (LPDPMSG_FORWARDADDPLAYER)( (BYTE*)lpMsg +
262 This->dp2->spData.dwSPHeaderSize );
264 /* Compose dplay message envelope */
265 lpMsgBody->envelope.dwMagic = DPMSGMAGIC_DPLAYMSG;
266 lpMsgBody->envelope.wCommandId = DPMSGCMD_FORWARDADDPLAYER;
267 lpMsgBody->envelope.wVersion = DPMSGVER_DP6;
274 /* SP Player remote data needs to be propagated at some point - is this the point? */
275 IDirectPlaySP_GetSPPlayerData( This->dp2->spData.lpISP, dpidServer, (LPVOID*)&lpPData, &dwDataSize, DPSET_REMOTE );
277 ERR( "Player Data size is 0x%08lx\n"
278 "[%02x%02x %02x%02x %02x%02x %02x%02x %02x%02x %02x%02x %02x%02x %02x%02x]\n"
279 "[%02x%02x %02x%02x %02x%02x %02x%02x %02x%02x %02x%02x %02x%02x %02x%02x]\n",
282 lpPData[0], lpPData[1], lpPData[2], lpPData[3], lpPData[4],
283 lpPData[5], lpPData[6], lpPData[7], lpPData[8], lpPData[9],
284 lpPData[10], lpPData[11], lpPData[12], lpPData[13], lpPData[14],
285 lpPData[15], lpPData[16], lpPData[17], lpPData[18], lpPData[19],
286 lpPData[20], lpPData[21], lpPData[22], lpPData[23], lpPData[24],
287 lpPData[25], lpPData[26], lpPData[27], lpPData[28], lpPData[29],
288 lpPData[30], lpPData[31]
294 /* Compose body of message */
295 lpMsgBody->dpidAppServer = dpidServer;
296 lpMsgBody->unknown2[0] = 0x0;
297 lpMsgBody->unknown2[1] = 0x1c;
298 lpMsgBody->unknown2[2] = 0x6c;
299 lpMsgBody->unknown2[3] = 0x50;
300 lpMsgBody->unknown2[4] = 0x9;
302 lpMsgBody->dpidAppServer2 = dpidServer;
303 lpMsgBody->unknown3[0] = 0x0;
304 lpMsgBody->unknown3[0] = 0x0;
305 lpMsgBody->unknown3[0] = 0x20;
306 lpMsgBody->unknown3[0] = 0x0;
307 lpMsgBody->unknown3[0] = 0x0;
309 lpMsgBody->dpidAppServer3 = dpidServer;
310 lpMsgBody->unknown4[0] = 0x30;
311 lpMsgBody->unknown4[1] = 0xb;
312 lpMsgBody->unknown4[2] = 0x0;
313 lpMsgBody->unknown4[3] = 0x1e090002;
314 lpMsgBody->unknown4[4] = 0x0;
315 lpMsgBody->unknown4[5] = 0x0;
316 lpMsgBody->unknown4[6] = 0x0;
317 lpMsgBody->unknown4[7] = 0x32090002;
318 lpMsgBody->unknown4[8] = 0x0;
319 lpMsgBody->unknown4[9] = 0x0;
320 lpMsgBody->unknown4[10] = 0x0;
321 lpMsgBody->unknown4[11] = 0x0;
322 lpMsgBody->unknown4[12] = 0x0;
324 lpMsgBody->unknown5[0] = 0x0;
325 lpMsgBody->unknown5[1] = 0x0;
327 /* Send the message */
331 data.dwFlags = DPSEND_GUARANTEED;
332 data.idPlayerTo = 0; /* Name server */
333 data.idPlayerFrom = dpidServer; /* Sending from session server */
334 data.lpMessage = lpMsg;
335 data.dwMessageSize = dwMsgSize;
336 data.bSystemMessage = TRUE; /* Allow reply to be sent */
337 data.lpISP = This->dp2->spData.lpISP;
339 lpMsg = DP_MSG_ExpectReply( This, &data,
341 DPMSGCMD_GETNAMETABLEREPLY,
342 &lpMsg, &dwMsgSize );
345 /* Need to examine the data and extract the new player id */
348 FIXME( "Name Table reply received: stub\n" );
354 /* Queue up a structure indicating that we want a reply of type wReplyCommandId. DPlay does
355 * not seem to offer any way of uniquely differentiating between replies of the same type
356 * relative to the request sent. There is an implicit assumption that there will be no
357 * ordering issues on sends and receives from the opposite machine. No wonder MS is not
358 * a networking company.
361 LPVOID DP_MSG_ExpectReply( IDirectPlay2AImpl* This, LPDPSP_SENDDATA lpData,
362 DWORD dwWaitTime, WORD wReplyCommandId,
363 LPVOID* lplpReplyMsg, LPDWORD lpdwMsgBodySize )
367 DP_MSG_REPLY_STRUCT_LIST replyStructList;
370 /* Setup for receipt */
371 hMsgReceipt = DP_MSG_BuildAndLinkReplyStruct( This, &replyStructList,
374 TRACE( "Sending msg and expecting cmd %u in reply within %lu ticks\n",
375 wReplyCommandId, dwWaitTime );
376 hr = (*This->dp2->spData.lpCB->Send)( lpData );
380 ERR( "Request for new playerID send failed: %s\n",
381 DPLAYX_HresultToString( hr ) );
385 dwWaitReturn = WaitForSingleObject( hMsgReceipt, dwWaitTime );
386 if( dwWaitReturn != WAIT_OBJECT_0 )
388 ERR( "Wait failed 0x%08lx\n", dwWaitReturn );
393 return DP_MSG_CleanReplyStruct( &replyStructList, lplpReplyMsg, lpdwMsgBodySize );
396 /* Determine if there is a matching request for this incomming message and then copy
397 * all important data. It is quite silly to have to copy the message, but the documents
398 * indicate that a copy is taken. Silly really.
400 void DP_MSG_ReplyReceived( IDirectPlay2AImpl* This, WORD wCommandId,
401 LPCVOID lpcMsgBody, DWORD dwMsgBodySize )
403 LPDP_MSG_REPLY_STRUCT_LIST lpReplyList;
406 if( wCommandId == DPMSGCMD_FORWARDADDPLAYER )
412 /* Find, and immediately remove (to avoid double triggering), the appropriate entry. Call locked to
415 EnterCriticalSection( &This->unk->DP_lock );
416 DPQ_REMOVE_ENTRY( This->dp2->replysExpected, replysExpected, replyExpected.wExpectedReply,\
417 ==, wCommandId, lpReplyList );
418 LeaveCriticalSection( &This->unk->DP_lock );
420 if( lpReplyList != NULL )
422 lpReplyList->replyExpected.dwMsgBodySize = dwMsgBodySize;
423 lpReplyList->replyExpected.lpReplyMsg = HeapAlloc( GetProcessHeap(),
426 CopyMemory( lpReplyList->replyExpected.lpReplyMsg,
427 lpcMsgBody, dwMsgBodySize );
429 /* Signal the thread which sent the message that it has a reply */
430 SetEvent( lpReplyList->replyExpected.hReceipt );
434 ERR( "No receipt event set - only expecting in reply mode\n" );
440 void DP_MSG_ErrorReceived( IDirectPlay2AImpl* This, WORD wCommandId,
441 LPCVOID lpMsgBody, DWORD dwMsgBodySize )
443 LPCDPMSG_FORWARDADDPLAYERNACK lpcErrorMsg;
445 lpcErrorMsg = (LPCDPMSG_FORWARDADDPLAYERNACK)lpMsgBody;
447 ERR( "Received error message %u. Error is %s\n",
448 wCommandId, DPLAYX_HresultToString( lpcErrorMsg->errorCode) );