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