1 /* DirectPlay & DirectPlayLobby messaging implementation
3 * Copyright 2000,2001 - Peter Hunnisett
5 * <presently under construction - contact hunnise@nortelnetworks.com>
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 #include "wine/debug.h"
30 #include "dplayx_messages.h"
31 #include "dplay_global.h"
32 #include "dplayx_global.h"
33 #include "name_server.h"
35 WINE_DEFAULT_DEBUG_CHANNEL(dplay
);
37 typedef struct tagMSGTHREADINFO
43 } MSGTHREADINFO
, *LPMSGTHREADINFO
;
45 static DWORD CALLBACK
DPL_MSG_ThreadMain( LPVOID lpContext
);
46 static LPVOID
DP_MSG_ExpectReply( IDirectPlay2AImpl
* This
, LPDPSP_SENDDATA data
,
47 DWORD dwWaitTime
, WORD wReplyCommandId
,
48 LPVOID
* lplpReplyMsg
, LPDWORD lpdwMsgBodySize
);
51 /* Create the message reception thread to allow the application to receive
52 * asynchronous message reception
54 DWORD
CreateLobbyMessageReceptionThread( HANDLE hNotifyEvent
, HANDLE hStart
,
55 HANDLE hDeath
, HANDLE hConnRead
)
58 LPMSGTHREADINFO lpThreadInfo
;
60 lpThreadInfo
= HeapAlloc( GetProcessHeap(), 0, sizeof( *lpThreadInfo
) );
61 if( lpThreadInfo
== NULL
)
66 /* The notify event may or may not exist. Depends if async comm or not */
68 !DuplicateHandle( GetCurrentProcess(), hNotifyEvent
,
69 GetCurrentProcess(), &lpThreadInfo
->hNotifyEvent
,
70 0, FALSE
, DUPLICATE_SAME_ACCESS
) )
72 ERR( "Unable to duplicate event handle\n" );
76 /* These 3 handles don't need to be duplicated because we don't keep a
77 * reference to them where they're created. They're created specifically
78 * for the message thread
80 lpThreadInfo
->hStart
= hStart
;
81 lpThreadInfo
->hDeath
= hDeath
;
82 lpThreadInfo
->hSettingRead
= hConnRead
;
84 if( !CreateThread( NULL
, /* Security attribs */
86 DPL_MSG_ThreadMain
, /* Msg reception function */
87 lpThreadInfo
, /* Msg reception func parameter */
89 &dwMsgThreadId
/* Updated with thread id */
93 ERR( "Unable to create msg thread\n" );
97 /* FIXME: Should I be closing the handle to the thread or does that
98 terminate the thread? */
100 return dwMsgThreadId
;
104 HeapFree( GetProcessHeap(), 0, lpThreadInfo
);
109 static DWORD CALLBACK
DPL_MSG_ThreadMain( LPVOID lpContext
)
111 LPMSGTHREADINFO lpThreadInfo
= (LPMSGTHREADINFO
)lpContext
;
114 TRACE( "Msg thread created. Waiting on app startup\n" );
116 /* Wait to ensure that the lobby application is started w/ 1 min timeout */
117 dwWaitResult
= WaitForSingleObject( lpThreadInfo
->hStart
, 10000 /* 10 sec */ );
118 if( dwWaitResult
== WAIT_TIMEOUT
)
120 FIXME( "Should signal app/wait creation failure (0x%08lx)\n", dwWaitResult
);
124 /* Close this handle as it's not needed anymore */
125 CloseHandle( lpThreadInfo
->hStart
);
126 lpThreadInfo
->hStart
= 0;
128 /* Wait until the lobby knows what it is */
129 dwWaitResult
= WaitForSingleObject( lpThreadInfo
->hSettingRead
, INFINITE
);
130 if( dwWaitResult
== WAIT_TIMEOUT
)
132 ERR( "App Read connection setting timeout fail (0x%08lx)\n", dwWaitResult
);
135 /* Close this handle as it's not needed anymore */
136 CloseHandle( lpThreadInfo
->hSettingRead
);
137 lpThreadInfo
->hSettingRead
= 0;
139 TRACE( "App created && intialized starting main message reception loop\n" );
144 GetMessageW( &lobbyMsg
, 0, 0, 0 );
148 TRACE( "Msg thread exiting!\n" );
149 HeapFree( GetProcessHeap(), 0, lpThreadInfo
);
154 /* DP messageing stuff */
155 static HANDLE
DP_MSG_BuildAndLinkReplyStruct( IDirectPlay2Impl
* This
,
156 LPDP_MSG_REPLY_STRUCT_LIST lpReplyStructList
,
157 WORD wReplyCommandId
);
158 static LPVOID
DP_MSG_CleanReplyStruct( LPDP_MSG_REPLY_STRUCT_LIST lpReplyStructList
,
159 LPVOID
* lplpReplyMsg
, LPDWORD lpdwMsgBodySize
);
163 HANDLE
DP_MSG_BuildAndLinkReplyStruct( IDirectPlay2Impl
* This
,
164 LPDP_MSG_REPLY_STRUCT_LIST lpReplyStructList
, WORD wReplyCommandId
)
166 lpReplyStructList
->replyExpected
.hReceipt
= CreateEventA( NULL
, FALSE
, FALSE
, NULL
);
167 lpReplyStructList
->replyExpected
.wExpectedReply
= wReplyCommandId
;
168 lpReplyStructList
->replyExpected
.lpReplyMsg
= NULL
;
169 lpReplyStructList
->replyExpected
.dwMsgBodySize
= 0;
171 /* Insert into the message queue while locked */
172 EnterCriticalSection( &This
->unk
->DP_lock
);
173 DPQ_INSERT( This
->dp2
->replysExpected
, lpReplyStructList
, replysExpected
);
174 LeaveCriticalSection( &This
->unk
->DP_lock
);
176 return lpReplyStructList
->replyExpected
.hReceipt
;
180 LPVOID
DP_MSG_CleanReplyStruct( LPDP_MSG_REPLY_STRUCT_LIST lpReplyStructList
,
181 LPVOID
* lplpReplyMsg
, LPDWORD lpdwMsgBodySize
)
183 CloseHandle( lpReplyStructList
->replyExpected
.hReceipt
);
185 *lplpReplyMsg
= lpReplyStructList
->replyExpected
.lpReplyMsg
;
186 *lpdwMsgBodySize
= lpReplyStructList
->replyExpected
.dwMsgBodySize
;
188 return lpReplyStructList
->replyExpected
.lpReplyMsg
;
191 HRESULT
DP_MSG_SendRequestPlayerId( IDirectPlay2AImpl
* This
, DWORD dwFlags
,
192 LPDPID lpdpidAllocatedId
)
195 LPDPMSG_REQUESTNEWPLAYERID lpMsgBody
;
199 dwMsgSize
= This
->dp2
->spData
.dwSPHeaderSize
+ sizeof( *lpMsgBody
);
201 lpMsg
= HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY
, dwMsgSize
);
203 lpMsgBody
= (LPDPMSG_REQUESTNEWPLAYERID
)( (BYTE
*)lpMsg
+
204 This
->dp2
->spData
.dwSPHeaderSize
);
206 /* Compose dplay message envelope */
207 lpMsgBody
->envelope
.dwMagic
= DPMSGMAGIC_DPLAYMSG
;
208 lpMsgBody
->envelope
.wCommandId
= DPMSGCMD_REQUESTNEWPLAYERID
;
209 lpMsgBody
->envelope
.wVersion
= DPMSGVER_DP6
;
211 /* Compose the body of the message */
212 lpMsgBody
->dwFlags
= dwFlags
;
214 /* Send the message */
218 data
.dwFlags
= DPSEND_GUARANTEED
;
219 data
.idPlayerTo
= 0; /* Name server */
220 data
.idPlayerFrom
= 0; /* Sending from DP */
221 data
.lpMessage
= lpMsg
;
222 data
.dwMessageSize
= dwMsgSize
;
223 data
.bSystemMessage
= TRUE
; /* Allow reply to be sent */
224 data
.lpISP
= This
->dp2
->spData
.lpISP
;
226 TRACE( "Asking for player id w/ dwFlags 0x%08lx\n",
227 lpMsgBody
->dwFlags
);
229 DP_MSG_ExpectReply( This
, &data
, DPMSG_DEFAULT_WAIT_TIME
, DPMSGCMD_NEWPLAYERIDREPLY
,
230 &lpMsg
, &dwMsgSize
);
233 /* Need to examine the data and extract the new player id */
236 LPCDPMSG_NEWPLAYERIDREPLY lpcReply
;
238 lpcReply
= (LPCDPMSG_NEWPLAYERIDREPLY
)lpMsg
;
240 *lpdpidAllocatedId
= lpcReply
->dpidNewPlayerId
;
242 TRACE( "Received reply for id = 0x%08lx\n", lpcReply
->dpidNewPlayerId
);
244 /* FIXME: I think that the rest of the message has something to do
245 * with remote data for the player that perhaps I need to setup.
246 * However, with the information that is passed, all that it could
247 * be used for is a standardized intialization value, which I'm
248 * guessing we can do without. Unless the message content is the same
249 * for several different messages?
252 HeapFree( GetProcessHeap(), 0, lpMsg
);
258 HRESULT
DP_MSG_ForwardPlayerCreation( IDirectPlay2AImpl
* This
, DPID dpidServer
)
261 LPDPMSG_FORWARDADDPLAYER lpMsgBody
;
265 dwMsgSize
= This
->dp2
->spData
.dwSPHeaderSize
+ sizeof( *lpMsgBody
);
267 lpMsg
= HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY
, dwMsgSize
);
269 lpMsgBody
= (LPDPMSG_FORWARDADDPLAYER
)( (BYTE
*)lpMsg
+
270 This
->dp2
->spData
.dwSPHeaderSize
);
272 /* Compose dplay message envelope */
273 lpMsgBody
->envelope
.dwMagic
= DPMSGMAGIC_DPLAYMSG
;
274 lpMsgBody
->envelope
.wCommandId
= DPMSGCMD_FORWARDADDPLAYER
;
275 lpMsgBody
->envelope
.wVersion
= DPMSGVER_DP6
;
282 /* SP Player remote data needs to be propagated at some point - is this the point? */
283 IDirectPlaySP_GetSPPlayerData( This
->dp2
->spData
.lpISP
, 0, (LPVOID
*)&lpPData
, &dwDataSize
, DPSET_REMOTE
);
285 ERR( "Player Data size is 0x%08lx\n"
286 "[%02x%02x %02x%02x %02x%02x %02x%02x %02x%02x %02x%02x %02x%02x %02x%02x]\n"
287 "[%02x%02x %02x%02x %02x%02x %02x%02x %02x%02x %02x%02x %02x%02x %02x%02x]\n",
290 lpPData
[0], lpPData
[1], lpPData
[2], lpPData
[3], lpPData
[4],
291 lpPData
[5], lpPData
[6], lpPData
[7], lpPData
[8], lpPData
[9],
292 lpPData
[10], lpPData
[11], lpPData
[12], lpPData
[13], lpPData
[14],
293 lpPData
[15], lpPData
[16], lpPData
[17], lpPData
[18], lpPData
[19],
294 lpPData
[20], lpPData
[21], lpPData
[22], lpPData
[23], lpPData
[24],
295 lpPData
[25], lpPData
[26], lpPData
[27], lpPData
[28], lpPData
[29],
296 lpPData
[30], lpPData
[31]
302 /* Compose body of message */
303 lpMsgBody
->dpidAppServer
= dpidServer
;
304 lpMsgBody
->unknown2
[0] = 0x0;
305 lpMsgBody
->unknown2
[1] = 0x1c;
306 lpMsgBody
->unknown2
[2] = 0x6c;
307 lpMsgBody
->unknown2
[3] = 0x50;
308 lpMsgBody
->unknown2
[4] = 0x9;
310 lpMsgBody
->dpidAppServer2
= dpidServer
;
311 lpMsgBody
->unknown3
[0] = 0x0;
312 lpMsgBody
->unknown3
[0] = 0x0;
313 lpMsgBody
->unknown3
[0] = 0x20;
314 lpMsgBody
->unknown3
[0] = 0x0;
315 lpMsgBody
->unknown3
[0] = 0x0;
317 lpMsgBody
->dpidAppServer3
= dpidServer
;
318 lpMsgBody
->unknown4
[0] = 0x30;
319 lpMsgBody
->unknown4
[1] = 0xb;
320 lpMsgBody
->unknown4
[2] = 0x0;
322 lpMsgBody
->unknown4
[3] = NS_GetNsMagic( This
->dp2
->lpNameServerData
) -
324 TRACE( "Setting first magic to 0x%08lx\n", lpMsgBody
->unknown4
[3] );
326 lpMsgBody
->unknown4
[4] = 0x0;
327 lpMsgBody
->unknown4
[5] = 0x0;
328 lpMsgBody
->unknown4
[6] = 0x0;
331 lpMsgBody
->unknown4
[7] = NS_GetOtherMagic( This
->dp2
->lpNameServerData
)
333 lpMsgBody
->unknown4
[7] = NS_GetNsMagic( This
->dp2
->lpNameServerData
);
335 TRACE( "Setting second magic to 0x%08lx\n", lpMsgBody
->unknown4
[7] );
337 lpMsgBody
->unknown4
[8] = 0x0;
338 lpMsgBody
->unknown4
[9] = 0x0;
339 lpMsgBody
->unknown4
[10] = 0x0;
340 lpMsgBody
->unknown4
[11] = 0x0;
341 lpMsgBody
->unknown4
[12] = 0x0;
343 lpMsgBody
->unknown5
[0] = 0x0;
344 lpMsgBody
->unknown5
[1] = 0x0;
346 /* Send the message */
350 data
.dwFlags
= DPSEND_GUARANTEED
;
351 data
.idPlayerTo
= 0; /* Name server */
352 data
.idPlayerFrom
= dpidServer
; /* Sending from session server */
353 data
.lpMessage
= lpMsg
;
354 data
.dwMessageSize
= dwMsgSize
;
355 data
.bSystemMessage
= TRUE
; /* Allow reply to be sent */
356 data
.lpISP
= This
->dp2
->spData
.lpISP
;
358 TRACE( "Sending forward player request with 0x%08lx\n", dpidServer
);
360 lpMsg
= DP_MSG_ExpectReply( This
, &data
,
362 DPMSGCMD_GETNAMETABLEREPLY
,
363 &lpMsg
, &dwMsgSize
);
366 /* Need to examine the data and extract the new player id */
369 FIXME( "Name Table reply received: stub\n" );
375 /* Queue up a structure indicating that we want a reply of type wReplyCommandId. DPlay does
376 * not seem to offer any way of uniquely differentiating between replies of the same type
377 * relative to the request sent. There is an implicit assumption that there will be no
378 * ordering issues on sends and receives from the opposite machine. No wonder MS is not
379 * a networking company.
382 LPVOID
DP_MSG_ExpectReply( IDirectPlay2AImpl
* This
, LPDPSP_SENDDATA lpData
,
383 DWORD dwWaitTime
, WORD wReplyCommandId
,
384 LPVOID
* lplpReplyMsg
, LPDWORD lpdwMsgBodySize
)
388 DP_MSG_REPLY_STRUCT_LIST replyStructList
;
391 /* Setup for receipt */
392 hMsgReceipt
= DP_MSG_BuildAndLinkReplyStruct( This
, &replyStructList
,
395 TRACE( "Sending msg and expecting cmd %u in reply within %lu ticks\n",
396 wReplyCommandId
, dwWaitTime
);
397 hr
= (*This
->dp2
->spData
.lpCB
->Send
)( lpData
);
401 ERR( "Send failed: %s\n", DPLAYX_HresultToString( hr
) );
405 /* The reply message will trigger the hMsgReceipt event effectively switching
406 * control back to this thread. See DP_MSG_ReplyReceived.
408 dwWaitReturn
= WaitForSingleObject( hMsgReceipt
, dwWaitTime
);
409 if( dwWaitReturn
!= WAIT_OBJECT_0
)
411 ERR( "Wait failed 0x%08lx\n", dwWaitReturn
);
416 return DP_MSG_CleanReplyStruct( &replyStructList
, lplpReplyMsg
, lpdwMsgBodySize
);
419 /* Determine if there is a matching request for this incomming message and then copy
420 * all important data. It is quite silly to have to copy the message, but the documents
421 * indicate that a copy is taken. Silly really.
423 void DP_MSG_ReplyReceived( IDirectPlay2AImpl
* This
, WORD wCommandId
,
424 LPCVOID lpcMsgBody
, DWORD dwMsgBodySize
)
426 LPDP_MSG_REPLY_STRUCT_LIST lpReplyList
;
429 if( wCommandId
== DPMSGCMD_FORWARDADDPLAYER
)
435 /* Find, and immediately remove (to avoid double triggering), the appropriate entry. Call locked to
438 EnterCriticalSection( &This
->unk
->DP_lock
);
439 DPQ_REMOVE_ENTRY( This
->dp2
->replysExpected
, replysExpected
, replyExpected
.wExpectedReply
,\
440 ==, wCommandId
, lpReplyList
);
441 LeaveCriticalSection( &This
->unk
->DP_lock
);
443 if( lpReplyList
!= NULL
)
445 lpReplyList
->replyExpected
.dwMsgBodySize
= dwMsgBodySize
;
446 lpReplyList
->replyExpected
.lpReplyMsg
= HeapAlloc( GetProcessHeap(),
449 CopyMemory( lpReplyList
->replyExpected
.lpReplyMsg
,
450 lpcMsgBody
, dwMsgBodySize
);
452 /* Signal the thread which sent the message that it has a reply */
453 SetEvent( lpReplyList
->replyExpected
.hReceipt
);
457 ERR( "No receipt event set - only expecting in reply mode\n" );
462 void DP_MSG_ToSelf( IDirectPlay2AImpl
* This
, DPID dpidSelf
)
465 LPDPMSG_SENDENVELOPE lpMsgBody
;
468 dwMsgSize
= This
->dp2
->spData
.dwSPHeaderSize
+ sizeof( *lpMsgBody
);
470 lpMsg
= HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY
, dwMsgSize
);
472 lpMsgBody
= (LPDPMSG_SENDENVELOPE
)( (BYTE
*)lpMsg
+
473 This
->dp2
->spData
.dwSPHeaderSize
);
475 /* Compose dplay message envelope */
476 lpMsgBody
->dwMagic
= DPMSGMAGIC_DPLAYMSG
;
477 lpMsgBody
->wCommandId
= DPMSGCMD_JUSTENVELOPE
;
478 lpMsgBody
->wVersion
= DPMSGVER_DP6
;
480 /* Send the message to ourselves */
485 data
.idPlayerTo
= dpidSelf
; /* Sending to session server */
486 data
.idPlayerFrom
= 0; /* Sending from session server */
487 data
.lpMessage
= lpMsg
;
488 data
.dwMessageSize
= dwMsgSize
;
489 data
.bSystemMessage
= TRUE
; /* Allow reply to be sent */
490 data
.lpISP
= This
->dp2
->spData
.lpISP
;
492 lpMsg
= DP_MSG_ExpectReply( This
, &data
,
494 DPMSGCMD_JUSTENVELOPE
,
495 &lpMsg
, &dwMsgSize
);
499 void DP_MSG_ErrorReceived( IDirectPlay2AImpl
* This
, WORD wCommandId
,
500 LPCVOID lpMsgBody
, DWORD dwMsgBodySize
)
502 LPCDPMSG_FORWARDADDPLAYERNACK lpcErrorMsg
;
504 lpcErrorMsg
= (LPCDPMSG_FORWARDADDPLAYERNACK
)lpMsgBody
;
506 ERR( "Received error message %u. Error is %s\n",
507 wCommandId
, DPLAYX_HresultToString( lpcErrorMsg
->errorCode
) );