1 /* DirectPlay & DirectPlayLobby messaging implementation
3 * Copyright 2000,2001 - Peter Hunnisett
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.
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.
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
20 * o Messaging interface required for both DirectPlay and DirectPlayLobby.
23 #include "dplayx_global.h"
25 typedef struct tagMSGTHREADINFO
31 } MSGTHREADINFO
, *LPMSGTHREADINFO
;
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
);
38 /* Create the message reception thread to allow the application to receive
39 * asynchronous message reception
41 DWORD
CreateLobbyMessageReceptionThread( HANDLE hNotifyEvent
, HANDLE hStart
,
42 HANDLE hDeath
, HANDLE hConnRead
)
45 LPMSGTHREADINFO lpThreadInfo
;
48 lpThreadInfo
= HeapAlloc( GetProcessHeap(), 0, sizeof( *lpThreadInfo
) );
49 if( lpThreadInfo
== NULL
)
54 /* The notify event may or may not exist. Depends if async comm or not */
56 !DuplicateHandle( GetCurrentProcess(), hNotifyEvent
,
57 GetCurrentProcess(), &lpThreadInfo
->hNotifyEvent
,
58 0, FALSE
, DUPLICATE_SAME_ACCESS
) )
60 ERR( "Unable to duplicate event handle\n" );
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
68 lpThreadInfo
->hStart
= hStart
;
69 lpThreadInfo
->hDeath
= hDeath
;
70 lpThreadInfo
->hSettingRead
= hConnRead
;
72 hThread
= CreateThread( NULL
, /* Security attribs */
74 DPL_MSG_ThreadMain
, /* Msg reception function */
75 lpThreadInfo
, /* Msg reception func parameter */
77 &dwMsgThreadId
/* Updated with thread id */
79 if ( hThread
== NULL
)
81 ERR( "Unable to create msg thread\n" );
91 HeapFree( GetProcessHeap(), 0, lpThreadInfo
);
96 static DWORD CALLBACK
DPL_MSG_ThreadMain( LPVOID lpContext
)
98 LPMSGTHREADINFO lpThreadInfo
= lpContext
;
101 TRACE( "Msg thread created. Waiting on app startup\n" );
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
)
107 FIXME( "Should signal app/wait creation failure (0x%08x)\n", dwWaitResult
);
111 /* Close this handle as it's not needed anymore */
112 CloseHandle( lpThreadInfo
->hStart
);
113 lpThreadInfo
->hStart
= 0;
115 /* Wait until the lobby knows what it is */
116 dwWaitResult
= WaitForSingleObject( lpThreadInfo
->hSettingRead
, INFINITE
);
117 if( dwWaitResult
== WAIT_TIMEOUT
)
119 ERR( "App Read connection setting timeout fail (0x%08x)\n", dwWaitResult
);
122 /* Close this handle as it's not needed anymore */
123 CloseHandle( lpThreadInfo
->hSettingRead
);
124 lpThreadInfo
->hSettingRead
= 0;
126 TRACE( "App created && initialized starting main message reception loop\n" );
131 GetMessageW( &lobbyMsg
, 0, 0, 0 );
135 TRACE( "Msg thread exiting!\n" );
136 HeapFree( GetProcessHeap(), 0, lpThreadInfo
);
141 /* DP messaging stuff */
142 static HANDLE
DP_MSG_BuildAndLinkReplyStruct( IDirectPlayImpl
*This
,
143 DP_MSG_REPLY_STRUCT_LIST
*lpReplyStructList
, WORD wReplyCommandId
)
145 lpReplyStructList
->replyExpected
.hReceipt
= CreateEventW( NULL
, FALSE
, FALSE
, NULL
);
146 lpReplyStructList
->replyExpected
.wExpectedReply
= wReplyCommandId
;
147 lpReplyStructList
->replyExpected
.lpReplyMsg
= NULL
;
148 lpReplyStructList
->replyExpected
.dwMsgBodySize
= 0;
150 /* Insert into the message queue while locked */
151 EnterCriticalSection( &This
->lock
);
152 DPQ_INSERT( This
->dp2
->repliesExpected
, lpReplyStructList
, repliesExpected
);
153 LeaveCriticalSection( &This
->lock
);
155 return lpReplyStructList
->replyExpected
.hReceipt
;
159 LPVOID
DP_MSG_CleanReplyStruct( LPDP_MSG_REPLY_STRUCT_LIST lpReplyStructList
,
160 LPVOID
* lplpReplyMsg
, LPDWORD lpdwMsgBodySize
)
162 CloseHandle( lpReplyStructList
->replyExpected
.hReceipt
);
164 *lplpReplyMsg
= lpReplyStructList
->replyExpected
.lpReplyMsg
;
165 *lpdwMsgBodySize
= lpReplyStructList
->replyExpected
.dwMsgBodySize
;
167 return lpReplyStructList
->replyExpected
.lpReplyMsg
;
170 HRESULT
DP_MSG_SendRequestPlayerId( IDirectPlayImpl
*This
, DWORD dwFlags
, DPID
*lpdpidAllocatedId
)
173 LPDPMSG_REQUESTNEWPLAYERID lpMsgBody
;
177 dwMsgSize
= This
->dp2
->spData
.dwSPHeaderSize
+ sizeof( *lpMsgBody
);
179 lpMsg
= HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY
, dwMsgSize
);
181 lpMsgBody
= (LPDPMSG_REQUESTNEWPLAYERID
)( (BYTE
*)lpMsg
+
182 This
->dp2
->spData
.dwSPHeaderSize
);
184 /* Compose dplay message envelope */
185 lpMsgBody
->envelope
.dwMagic
= DPMSGMAGIC_DPLAYMSG
;
186 lpMsgBody
->envelope
.wCommandId
= DPMSGCMD_REQUESTNEWPLAYERID
;
187 lpMsgBody
->envelope
.wVersion
= DPMSGVER_DP6
;
189 /* Compose the body of the message */
190 lpMsgBody
->dwFlags
= dwFlags
;
192 /* Send the message */
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
;
204 TRACE( "Asking for player id w/ dwFlags 0x%08x\n",
205 lpMsgBody
->dwFlags
);
207 DP_MSG_ExpectReply( This
, &data
, DPMSG_DEFAULT_WAIT_TIME
, DPMSGCMD_NEWPLAYERIDREPLY
,
208 &lpMsg
, &dwMsgSize
);
211 /* Need to examine the data and extract the new player id */
214 LPCDPMSG_NEWPLAYERIDREPLY lpcReply
;
218 *lpdpidAllocatedId
= lpcReply
->dpidNewPlayerId
;
220 TRACE( "Received reply for id = 0x%08x\n", lpcReply
->dpidNewPlayerId
);
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?
230 HeapFree( GetProcessHeap(), 0, lpMsg
);
236 HRESULT
DP_MSG_ForwardPlayerCreation( IDirectPlayImpl
*This
, DPID dpidServer
)
239 LPDPMSG_FORWARDADDPLAYER lpMsgBody
;
243 dwMsgSize
= This
->dp2
->spData
.dwSPHeaderSize
+ sizeof( *lpMsgBody
);
245 lpMsg
= HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY
, dwMsgSize
);
247 lpMsgBody
= (LPDPMSG_FORWARDADDPLAYER
)( (BYTE
*)lpMsg
+
248 This
->dp2
->spData
.dwSPHeaderSize
);
250 /* Compose dplay message envelope */
251 lpMsgBody
->envelope
.dwMagic
= DPMSGMAGIC_DPLAYMSG
;
252 lpMsgBody
->envelope
.wCommandId
= DPMSGCMD_FORWARDADDPLAYER
;
253 lpMsgBody
->envelope
.wVersion
= DPMSGVER_DP6
;
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
);
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",
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]
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;
288 lpMsgBody
->dpidAppServer2
= dpidServer
;
289 lpMsgBody
->unknown3
[0] = 0x0;
290 lpMsgBody
->unknown3
[0] = 0x0;
291 lpMsgBody
->unknown3
[0] = 0x20;
292 lpMsgBody
->unknown3
[0] = 0x0;
293 lpMsgBody
->unknown3
[0] = 0x0;
295 lpMsgBody
->dpidAppServer3
= dpidServer
;
296 lpMsgBody
->unknown4
[0] = 0x30;
297 lpMsgBody
->unknown4
[1] = 0xb;
298 lpMsgBody
->unknown4
[2] = 0x0;
300 lpMsgBody
->unknown4
[3] = NS_GetNsMagic( This
->dp2
->lpNameServerData
) -
302 TRACE( "Setting first magic to 0x%08x\n", lpMsgBody
->unknown4
[3] );
304 lpMsgBody
->unknown4
[4] = 0x0;
305 lpMsgBody
->unknown4
[5] = 0x0;
306 lpMsgBody
->unknown4
[6] = 0x0;
308 lpMsgBody
->unknown4
[7] = NS_GetNsMagic( This
->dp2
->lpNameServerData
);
309 TRACE( "Setting second magic to 0x%08x\n", lpMsgBody
->unknown4
[7] );
311 lpMsgBody
->unknown4
[8] = 0x0;
312 lpMsgBody
->unknown4
[9] = 0x0;
313 lpMsgBody
->unknown4
[10] = 0x0;
314 lpMsgBody
->unknown4
[11] = 0x0;
316 lpMsgBody
->unknown5
[0] = 0x0;
317 lpMsgBody
->unknown5
[1] = 0x0;
319 /* Send the message */
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
;
331 TRACE( "Sending forward player request with 0x%08x\n", dpidServer
);
333 lpMsg
= DP_MSG_ExpectReply( This
, &data
,
335 DPMSGCMD_GETNAMETABLEREPLY
,
336 &lpMsg
, &dwMsgSize
);
339 /* Need to examine the data and extract the new player id */
342 FIXME( "Name Table reply received: stub\n" );
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.
354 static void *DP_MSG_ExpectReply( IDirectPlayImpl
*This
, DPSP_SENDDATA
*lpData
, DWORD dwWaitTime
,
355 WORD wReplyCommandId
, void **lplpReplyMsg
, DWORD
*lpdwMsgBodySize
)
359 DP_MSG_REPLY_STRUCT_LIST replyStructList
;
362 /* Setup for receipt */
363 hMsgReceipt
= DP_MSG_BuildAndLinkReplyStruct( This
, &replyStructList
,
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
);
372 ERR( "Send failed: %s\n", DPLAYX_HresultToString( hr
) );
376 /* The reply message will trigger the hMsgReceipt event effectively switching
377 * control back to this thread. See DP_MSG_ReplyReceived.
379 dwWaitReturn
= WaitForSingleObject( hMsgReceipt
, dwWaitTime
);
380 if( dwWaitReturn
!= WAIT_OBJECT_0
)
382 ERR( "Wait failed 0x%08x\n", dwWaitReturn
);
387 return DP_MSG_CleanReplyStruct( &replyStructList
, lplpReplyMsg
, lpdwMsgBodySize
);
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.
394 void DP_MSG_ReplyReceived( IDirectPlayImpl
*This
, WORD wCommandId
, const void *lpcMsgBody
,
395 DWORD dwMsgBodySize
)
397 LPDP_MSG_REPLY_STRUCT_LIST lpReplyList
;
400 if( wCommandId
== DPMSGCMD_FORWARDADDPLAYER
)
406 /* Find, and immediately remove (to avoid double triggering), the appropriate entry. Call locked to
409 EnterCriticalSection( &This
->lock
);
410 DPQ_REMOVE_ENTRY( This
->dp2
->repliesExpected
, repliesExpected
, replyExpected
.wExpectedReply
,
411 ==, wCommandId
, lpReplyList
);
412 LeaveCriticalSection( &This
->lock
);
414 if( lpReplyList
!= NULL
)
416 lpReplyList
->replyExpected
.dwMsgBodySize
= dwMsgBodySize
;
417 lpReplyList
->replyExpected
.lpReplyMsg
= HeapAlloc( GetProcessHeap(),
420 CopyMemory( lpReplyList
->replyExpected
.lpReplyMsg
,
421 lpcMsgBody
, dwMsgBodySize
);
423 /* Signal the thread which sent the message that it has a reply */
424 SetEvent( lpReplyList
->replyExpected
.hReceipt
);
428 ERR( "No receipt event set - only expecting in reply mode\n" );
433 void DP_MSG_ToSelf( IDirectPlayImpl
*This
, DPID dpidSelf
)
436 LPDPMSG_SENDENVELOPE lpMsgBody
;
439 dwMsgSize
= This
->dp2
->spData
.dwSPHeaderSize
+ sizeof( *lpMsgBody
);
441 lpMsg
= HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY
, dwMsgSize
);
443 lpMsgBody
= (LPDPMSG_SENDENVELOPE
)( (BYTE
*)lpMsg
+
444 This
->dp2
->spData
.dwSPHeaderSize
);
446 /* Compose dplay message envelope */
447 lpMsgBody
->dwMagic
= DPMSGMAGIC_DPLAYMSG
;
448 lpMsgBody
->wCommandId
= DPMSGCMD_JUSTENVELOPE
;
449 lpMsgBody
->wVersion
= DPMSGVER_DP6
;
451 /* Send the message to ourselves */
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
;
463 lpMsg
= DP_MSG_ExpectReply( This
, &data
,
465 DPMSGCMD_JUSTENVELOPE
,
466 &lpMsg
, &dwMsgSize
);
470 void DP_MSG_ErrorReceived( IDirectPlayImpl
*This
, WORD wCommandId
, const void *lpMsgBody
,
471 DWORD dwMsgBodySize
)
473 LPCDPMSG_FORWARDADDPLAYERNACK lpcErrorMsg
;
475 lpcErrorMsg
= lpMsgBody
;
477 ERR( "Received error message %u. Error is %s\n",
478 wCommandId
, DPLAYX_HresultToString( lpcErrorMsg
->errorCode
) );