1 /* DirectPlay & DirectPlayLobby messaging implementation
3 * Copyright 2000 - Peter Hunnisett
5 * <presently under construction - contact hunnise@nortelnetworks.com>
11 #include "debugtools.h"
17 #include "dplayx_messages.h"
18 #include "dplay_global.h"
19 #include "dplayx_global.h"
21 DEFAULT_DEBUG_CHANNEL(dplay
);
23 typedef struct tagMSGTHREADINFO
29 } MSGTHREADINFO
, *LPMSGTHREADINFO
;
31 static DWORD CALLBACK
DPL_MSG_ThreadMain( LPVOID lpContext
);
32 static LPVOID
DP_MSG_ExpectReply( IDirectPlay2AImpl
* This
, LPDPSP_SENDDATA data
,
33 DWORD dwWaitTime
, WORD wReplyCommandId
,
34 LPVOID
* lplpReplyMsg
, LPDWORD lpdwMsgBodySize
);
37 /* Create the message reception thread to allow the application to receive
38 * asynchronous message reception
40 DWORD
CreateLobbyMessageReceptionThread( HANDLE hNotifyEvent
, HANDLE hStart
,
41 HANDLE hDeath
, HANDLE hConnRead
)
44 LPMSGTHREADINFO lpThreadInfo
;
46 lpThreadInfo
= HeapAlloc( GetProcessHeap(), 0, sizeof( *lpThreadInfo
) );
47 if( lpThreadInfo
== NULL
)
52 /* The notify event may or may not exist. Depends if async comm or not */
54 !DuplicateHandle( GetCurrentProcess(), hNotifyEvent
,
55 GetCurrentProcess(), &lpThreadInfo
->hNotifyEvent
,
56 0, FALSE
, DUPLICATE_SAME_ACCESS
) )
58 ERR( "Unable to duplicate event handle\n" );
62 /* These 3 handles don't need to be duplicated because we don't keep a
63 * reference to them where they're created. They're created specifically
64 * for the message thread
66 lpThreadInfo
->hStart
= hStart
;
67 lpThreadInfo
->hDeath
= hDeath
;
68 lpThreadInfo
->hSettingRead
= hConnRead
;
70 if( !CreateThread( NULL
, /* Security attribs */
72 DPL_MSG_ThreadMain
, /* Msg reception function */
73 lpThreadInfo
, /* Msg reception func parameter */
75 &dwMsgThreadId
/* Updated with thread id */
79 ERR( "Unable to create msg thread\n" );
83 /* FIXME: Should I be closing the handle to the thread or does that
84 terminate the thread? */
90 HeapFree( GetProcessHeap(), 0, lpThreadInfo
);
95 static DWORD CALLBACK
DPL_MSG_ThreadMain( LPVOID lpContext
)
97 LPMSGTHREADINFO lpThreadInfo
= (LPMSGTHREADINFO
)lpContext
;
100 TRACE( "Msg thread created. Waiting on app startup\n" );
102 /* Wait to ensure that the lobby application is started w/ 1 min timeout */
103 dwWaitResult
= WaitForSingleObject( lpThreadInfo
->hStart
, 10000 /* 10 sec */ );
104 if( dwWaitResult
== WAIT_TIMEOUT
)
106 FIXME( "Should signal app/wait creation failure (0x%08lx)\n", dwWaitResult
);
110 /* Close this handle as it's not needed anymore */
111 CloseHandle( lpThreadInfo
->hStart
);
112 lpThreadInfo
->hStart
= 0;
114 /* Wait until the lobby knows what it is */
115 dwWaitResult
= WaitForSingleObject( lpThreadInfo
->hSettingRead
, INFINITE
);
116 if( dwWaitResult
== WAIT_TIMEOUT
)
118 ERR( "App Read connection setting timeout fail (0x%08lx)\n", dwWaitResult
);
121 /* Close this handle as it's not needed anymore */
122 CloseHandle( lpThreadInfo
->hSettingRead
);
123 lpThreadInfo
->hSettingRead
= 0;
125 TRACE( "App created && intialized starting main message reception loop\n" );
130 GetMessageW( &lobbyMsg
, 0, 0, 0 );
134 TRACE( "Msg thread exiting!\n" );
135 HeapFree( GetProcessHeap(), 0, lpThreadInfo
);
140 /* DP messageing stuff */
141 static HANDLE
DP_MSG_BuildAndLinkReplyStruct( IDirectPlay2Impl
* This
,
142 LPDP_MSG_REPLY_STRUCT_LIST lpReplyStructList
,
143 WORD wReplyCommandId
);
144 static LPVOID
DP_MSG_CleanReplyStruct( LPDP_MSG_REPLY_STRUCT_LIST lpReplyStructList
,
145 LPVOID
* lplpReplyMsg
, LPDWORD lpdwMsgBodySize
);
149 HANDLE
DP_MSG_BuildAndLinkReplyStruct( IDirectPlay2Impl
* This
,
150 LPDP_MSG_REPLY_STRUCT_LIST lpReplyStructList
, WORD wReplyCommandId
)
152 lpReplyStructList
->replyExpected
.hReceipt
= CreateEventA( NULL
, FALSE
, FALSE
, NULL
);
153 lpReplyStructList
->replyExpected
.wExpectedReply
= wReplyCommandId
;
154 lpReplyStructList
->replyExpected
.lpReplyMsg
= NULL
;
155 lpReplyStructList
->replyExpected
.dwMsgBodySize
= 0;
157 /* Insert into the message queue while locked */
158 EnterCriticalSection( &This
->unk
->DP_lock
);
159 DPQ_INSERT( This
->dp2
->replysExpected
, lpReplyStructList
, replysExpected
);
160 LeaveCriticalSection( &This
->unk
->DP_lock
);
162 return lpReplyStructList
->replyExpected
.hReceipt
;
166 LPVOID
DP_MSG_CleanReplyStruct( LPDP_MSG_REPLY_STRUCT_LIST lpReplyStructList
,
167 LPVOID
* lplpReplyMsg
, LPDWORD lpdwMsgBodySize
)
169 CloseHandle( lpReplyStructList
->replyExpected
.hReceipt
);
171 *lplpReplyMsg
= lpReplyStructList
->replyExpected
.lpReplyMsg
;
172 *lpdwMsgBodySize
= lpReplyStructList
->replyExpected
.dwMsgBodySize
;
174 return lpReplyStructList
->replyExpected
.lpReplyMsg
;
177 HRESULT
DP_MSG_SendRequestPlayerId( IDirectPlay2AImpl
* This
, DWORD dwFlags
,
178 LPDPID lpdpidAllocatedId
)
181 LPDPMSG_REQUESTNEWPLAYERID lpMsgBody
;
185 dwMsgSize
= This
->dp2
->spData
.dwSPHeaderSize
+ sizeof( *lpMsgBody
);
187 lpMsg
= HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY
, dwMsgSize
);
189 lpMsgBody
= (LPDPMSG_REQUESTNEWPLAYERID
)( (BYTE
*)lpMsg
+
190 This
->dp2
->spData
.dwSPHeaderSize
);
192 /* Compose dplay message envelope */
193 lpMsgBody
->envelope
.dwMagic
= DPMSGMAGIC_DPLAYMSG
;
194 lpMsgBody
->envelope
.wCommandId
= DPMSGCMD_REQUESTNEWPLAYERID
;
195 lpMsgBody
->envelope
.wVersion
= DPMSGVER_DP6
;
197 /* Compose the body of the message */
198 lpMsgBody
->dwFlags
= dwFlags
;
200 /* Send the message */
204 data
.dwFlags
= DPSEND_GUARANTEED
;
205 data
.idPlayerTo
= 0; /* Name server */
206 data
.idPlayerFrom
= 0; /* Sending from DP */
207 data
.lpMessage
= lpMsg
;
208 data
.dwMessageSize
= dwMsgSize
;
209 data
.bSystemMessage
= TRUE
; /* Allow reply to be sent */
210 data
.lpISP
= This
->dp2
->spData
.lpISP
;
212 TRACE( "Asking for player id w/ dwFlags 0x%08lx\n",
213 lpMsgBody
->dwFlags
);
216 DP_MSG_ExpectReply( This
, &data
, DPMSG_DEFAULT_WAIT_TIME
, DPMSGCMD_NEWPLAYERIDREPLY
,
217 &lpMsg
, &dwMsgSize
);
220 /* Need to examine the data and extract the new player id */
223 LPCDPMSG_NEWPLAYERIDREPLY lpcReply
;
225 lpcReply
= (LPCDPMSG_NEWPLAYERIDREPLY
)lpMsg
;
227 *lpdpidAllocatedId
= lpcReply
->dpidNewPlayerId
;
229 TRACE( "Received reply for id = 0x%08lx\n", lpcReply
->dpidNewPlayerId
);
231 /* FIXME: I think that the rest of the message has something to do
232 * with remote data for the player that perhaps I need to setup.
233 * However, with the information that is passed, all that it could
234 * be used for is a standardized intialization value, which I'm
235 * guessing we can do without. Unless the message content is the same
236 * for several different messages?
239 HeapFree( GetProcessHeap(), 0, lpMsg
);
245 HRESULT
DP_MSG_ForwardPlayerCreation( IDirectPlay2AImpl
* This
, DPID dpidServer
)
248 LPDPMSG_FORWARDADDPLAYER lpMsgBody
;
252 dwMsgSize
= This
->dp2
->spData
.dwSPHeaderSize
+ sizeof( *lpMsgBody
);
254 lpMsg
= HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY
, dwMsgSize
);
256 lpMsgBody
= (LPDPMSG_FORWARDADDPLAYER
)( (BYTE
*)lpMsg
+
257 This
->dp2
->spData
.dwSPHeaderSize
);
259 /* Compose dplay message envelope */
260 lpMsgBody
->envelope
.dwMagic
= DPMSGMAGIC_DPLAYMSG
;
261 lpMsgBody
->envelope
.wCommandId
= DPMSGCMD_FORWARDADDPLAYER
;
262 lpMsgBody
->envelope
.wVersion
= DPMSGVER_DP6
;
269 /* SP Player remote data needs to be propagated at some point - is this the point? */
270 IDirectPlaySP_GetSPPlayerData( This
->dp2
->spData
.lpISP
, dpidServer
, (LPVOID
*)&lpPData
, &dwDataSize
, DPSET_REMOTE
);
272 ERR( "Player Data size is 0x%08lx\n"
273 "[%02x%02x %02x%02x %02x%02x %02x%02x %02x%02x %02x%02x %02x%02x %02x%02x]\n"
274 "[%02x%02x %02x%02x %02x%02x %02x%02x %02x%02x %02x%02x %02x%02x %02x%02x]\n",
277 lpPData
[0], lpPData
[1], lpPData
[2], lpPData
[3], lpPData
[4],
278 lpPData
[5], lpPData
[6], lpPData
[7], lpPData
[8], lpPData
[9],
279 lpPData
[10], lpPData
[11], lpPData
[12], lpPData
[13], lpPData
[14],
280 lpPData
[15], lpPData
[16], lpPData
[17], lpPData
[18], lpPData
[19],
281 lpPData
[20], lpPData
[21], lpPData
[22], lpPData
[23], lpPData
[24],
282 lpPData
[25], lpPData
[26], lpPData
[27], lpPData
[28], lpPData
[29],
283 lpPData
[30], lpPData
[31]
289 /* Compose body of message */
290 lpMsgBody
->dpidAppServer
= dpidServer
;
291 lpMsgBody
->unknown2
[0] = 0x0;
292 lpMsgBody
->unknown2
[1] = 0x1c;
293 lpMsgBody
->unknown2
[2] = 0x6c;
294 lpMsgBody
->unknown2
[3] = 0x50;
295 lpMsgBody
->unknown2
[4] = 0x9;
297 lpMsgBody
->dpidAppServer2
= dpidServer
;
298 lpMsgBody
->unknown3
[0] = 0x0;
299 lpMsgBody
->unknown3
[0] = 0x0;
300 lpMsgBody
->unknown3
[0] = 0x20;
301 lpMsgBody
->unknown3
[0] = 0x0;
302 lpMsgBody
->unknown3
[0] = 0x0;
304 lpMsgBody
->dpidAppServer3
= dpidServer
;
305 lpMsgBody
->unknown4
[0] = 0x30;
306 lpMsgBody
->unknown4
[1] = 0xb;
307 lpMsgBody
->unknown4
[2] = 0x0;
308 lpMsgBody
->unknown4
[3] = 0x1e090002;
309 lpMsgBody
->unknown4
[4] = 0x0;
310 lpMsgBody
->unknown4
[5] = 0x0;
311 lpMsgBody
->unknown4
[6] = 0x0;
312 lpMsgBody
->unknown4
[7] = 0x32090002;
313 lpMsgBody
->unknown4
[8] = 0x0;
314 lpMsgBody
->unknown4
[9] = 0x0;
315 lpMsgBody
->unknown4
[10] = 0x0;
316 lpMsgBody
->unknown4
[11] = 0x0;
317 lpMsgBody
->unknown4
[12] = 0x0;
319 lpMsgBody
->unknown5
[0] = 0x0;
320 lpMsgBody
->unknown5
[1] = 0x0;
322 /* Send the message */
326 data
.dwFlags
= DPSEND_GUARANTEED
;
327 data
.idPlayerTo
= 0; /* Name server */
328 data
.idPlayerFrom
= dpidServer
; /* Sending from session server */
329 data
.lpMessage
= lpMsg
;
330 data
.dwMessageSize
= dwMsgSize
;
331 data
.bSystemMessage
= TRUE
; /* Allow reply to be sent */
332 data
.lpISP
= This
->dp2
->spData
.lpISP
;
334 lpMsg
= DP_MSG_ExpectReply( This
, &data
,
336 DPMSGCMD_GETNAMETABLEREPLY
,
337 &lpMsg
, &dwMsgSize
);
340 /* Need to examine the data and extract the new player id */
343 FIXME( "Name Table reply received: stub\n" );
349 /* Queue up a structure indicating that we want a reply of type wReplyCommandId. DPlay does
350 * not seem to offer any way of uniquely differentiating between replies of the same type
351 * relative to the request sent. There is an implicit assumption that there will be no
352 * ordering issues on sends and receives from the opposite machine. No wonder MS is not
353 * a networking company.
356 LPVOID
DP_MSG_ExpectReply( IDirectPlay2AImpl
* This
, LPDPSP_SENDDATA lpData
,
357 DWORD dwWaitTime
, WORD wReplyCommandId
,
358 LPVOID
* lplpReplyMsg
, LPDWORD lpdwMsgBodySize
)
362 DP_MSG_REPLY_STRUCT_LIST replyStructList
;
365 /* Setup for receipt */
366 hMsgReceipt
= DP_MSG_BuildAndLinkReplyStruct( This
, &replyStructList
,
369 TRACE( "Sending msg and expecting cmd %u in reply within %lu ticks\n",
370 wReplyCommandId
, dwWaitTime
);
371 hr
= (*This
->dp2
->spData
.lpCB
->Send
)( lpData
);
375 ERR( "Request for new playerID send failed: %s\n",
376 DPLAYX_HresultToString( hr
) );
380 dwWaitReturn
= WaitForSingleObject( hMsgReceipt
, dwWaitTime
);
381 if( dwWaitReturn
!= WAIT_OBJECT_0
)
383 ERR( "Wait failed 0x%08lx\n", dwWaitReturn
);
388 return DP_MSG_CleanReplyStruct( &replyStructList
, lplpReplyMsg
, lpdwMsgBodySize
);
391 /* Determine if there is a matching request for this incomming message and then copy
392 * all important data. It is quite silly to have to copy the message, but the documents
393 * indicate that a copy is taken. Silly really.
395 void DP_MSG_ReplyReceived( IDirectPlay2AImpl
* This
, WORD wCommandId
,
396 LPCVOID lpcMsgBody
, DWORD dwMsgBodySize
)
398 LPDP_MSG_REPLY_STRUCT_LIST lpReplyList
;
401 if( wCommandId
== DPMSGCMD_FORWARDADDPLAYER
)
407 /* Find, and immediately remove (to avoid double triggering), the appropriate entry. Call locked to
410 EnterCriticalSection( &This
->unk
->DP_lock
);
411 DPQ_REMOVE_ENTRY( This
->dp2
->replysExpected
, replysExpected
, replyExpected
.wExpectedReply
,\
412 ==, wCommandId
, lpReplyList
);
413 LeaveCriticalSection( &This
->unk
->DP_lock
);
415 if( lpReplyList
!= NULL
)
417 lpReplyList
->replyExpected
.dwMsgBodySize
= dwMsgBodySize
;
418 lpReplyList
->replyExpected
.lpReplyMsg
= HeapAlloc( GetProcessHeap(),
421 CopyMemory( lpReplyList
->replyExpected
.lpReplyMsg
,
422 lpcMsgBody
, dwMsgBodySize
);
424 /* Signal the thread which sent the message that it has a reply */
425 SetEvent( lpReplyList
->replyExpected
.hReceipt
);
429 ERR( "No receipt event set - only expecting in reply mode\n" );
435 void DP_MSG_ErrorReceived( IDirectPlay2AImpl
* This
, WORD wCommandId
,
436 LPCVOID lpMsgBody
, DWORD dwMsgBodySize
)
438 LPCDPMSG_FORWARDADDPLAYERNACK lpcErrorMsg
;
440 lpcErrorMsg
= (LPCDPMSG_FORWARDADDPLAYERNACK
)lpMsgBody
;
442 ERR( "Received error message %u. Error is %s\n",
443 wCommandId
, DPLAYX_HresultToString( lpcErrorMsg
->errorCode
) );