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.
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"
37 WINE_DEFAULT_DEBUG_CHANNEL(dplay
);
39 typedef struct tagMSGTHREADINFO
45 } MSGTHREADINFO
, *LPMSGTHREADINFO
;
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
);
53 /* Create the message reception thread to allow the application to receive
54 * asynchronous message reception
56 DWORD
CreateLobbyMessageReceptionThread( HANDLE hNotifyEvent
, HANDLE hStart
,
57 HANDLE hDeath
, HANDLE hConnRead
)
60 LPMSGTHREADINFO lpThreadInfo
;
62 lpThreadInfo
= HeapAlloc( GetProcessHeap(), 0, sizeof( *lpThreadInfo
) );
63 if( lpThreadInfo
== NULL
)
68 /* The notify event may or may not exist. Depends if async comm or not */
70 !DuplicateHandle( GetCurrentProcess(), hNotifyEvent
,
71 GetCurrentProcess(), &lpThreadInfo
->hNotifyEvent
,
72 0, FALSE
, DUPLICATE_SAME_ACCESS
) )
74 ERR( "Unable to duplicate event handle\n" );
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
82 lpThreadInfo
->hStart
= hStart
;
83 lpThreadInfo
->hDeath
= hDeath
;
84 lpThreadInfo
->hSettingRead
= hConnRead
;
86 if( !CreateThread( NULL
, /* Security attribs */
88 DPL_MSG_ThreadMain
, /* Msg reception function */
89 lpThreadInfo
, /* Msg reception func parameter */
91 &dwMsgThreadId
/* Updated with thread id */
95 ERR( "Unable to create msg thread\n" );
99 /* FIXME: Should I be closing the handle to the thread or does that
100 terminate the thread? */
102 return dwMsgThreadId
;
106 HeapFree( GetProcessHeap(), 0, lpThreadInfo
);
111 static DWORD CALLBACK
DPL_MSG_ThreadMain( LPVOID lpContext
)
113 LPMSGTHREADINFO lpThreadInfo
= lpContext
;
116 TRACE( "Msg thread created. Waiting on app startup\n" );
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
)
122 FIXME( "Should signal app/wait creation failure (0x%08x)\n", dwWaitResult
);
126 /* Close this handle as it's not needed anymore */
127 CloseHandle( lpThreadInfo
->hStart
);
128 lpThreadInfo
->hStart
= 0;
130 /* Wait until the lobby knows what it is */
131 dwWaitResult
= WaitForSingleObject( lpThreadInfo
->hSettingRead
, INFINITE
);
132 if( dwWaitResult
== WAIT_TIMEOUT
)
134 ERR( "App Read connection setting timeout fail (0x%08x)\n", dwWaitResult
);
137 /* Close this handle as it's not needed anymore */
138 CloseHandle( lpThreadInfo
->hSettingRead
);
139 lpThreadInfo
->hSettingRead
= 0;
141 TRACE( "App created && initialized starting main message reception loop\n" );
146 GetMessageW( &lobbyMsg
, 0, 0, 0 );
150 TRACE( "Msg thread exiting!\n" );
151 HeapFree( GetProcessHeap(), 0, lpThreadInfo
);
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
);
165 HANDLE
DP_MSG_BuildAndLinkReplyStruct( IDirectPlay2Impl
* This
,
166 LPDP_MSG_REPLY_STRUCT_LIST lpReplyStructList
, WORD wReplyCommandId
)
168 lpReplyStructList
->replyExpected
.hReceipt
= CreateEventW( NULL
, FALSE
, FALSE
, NULL
);
169 lpReplyStructList
->replyExpected
.wExpectedReply
= wReplyCommandId
;
170 lpReplyStructList
->replyExpected
.lpReplyMsg
= NULL
;
171 lpReplyStructList
->replyExpected
.dwMsgBodySize
= 0;
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
);
178 return lpReplyStructList
->replyExpected
.hReceipt
;
182 LPVOID
DP_MSG_CleanReplyStruct( LPDP_MSG_REPLY_STRUCT_LIST lpReplyStructList
,
183 LPVOID
* lplpReplyMsg
, LPDWORD lpdwMsgBodySize
)
185 CloseHandle( lpReplyStructList
->replyExpected
.hReceipt
);
187 *lplpReplyMsg
= lpReplyStructList
->replyExpected
.lpReplyMsg
;
188 *lpdwMsgBodySize
= lpReplyStructList
->replyExpected
.dwMsgBodySize
;
190 return lpReplyStructList
->replyExpected
.lpReplyMsg
;
193 HRESULT
DP_MSG_SendRequestPlayerId( IDirectPlay2AImpl
* This
, DWORD dwFlags
,
194 LPDPID lpdpidAllocatedId
)
197 LPDPSP_MSG_REQUESTPLAYERID lpMsgBody
;
201 dwMsgSize
= This
->dp2
->spData
.dwSPHeaderSize
+ sizeof( *lpMsgBody
);
203 lpMsg
= HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY
, dwMsgSize
);
205 lpMsgBody
= (LPDPSP_MSG_REQUESTPLAYERID
)( (LPBYTE
)lpMsg
+
206 This
->dp2
->spData
.dwSPHeaderSize
);
208 /* Compose dplay message envelope */
209 lpMsgBody
->envelope
.dwMagic
= DPMSG_SIGNATURE
;
210 lpMsgBody
->envelope
.wCommandId
= DPMSGCMD_REQUESTPLAYERID
;
211 lpMsgBody
->envelope
.wVersion
= DX61AVERSION
;
213 /* Compose the body of the message */
214 lpMsgBody
->Flags
= dwFlags
;
216 /* Send the message */
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
;
228 TRACE( "Asking for player id w/ Flags 0x%08x\n", lpMsgBody
->Flags
);
230 DP_MSG_ExpectReply( This
, &data
, DPMSG_RELIABLE_API_TIMER
, DPMSGCMD_REQUESTPLAYERREPLY
,
231 &lpMsg
, &dwMsgSize
);
234 /* Need to examine the data and extract the new player id */
237 LPCDPSP_MSG_REQUESTPLAYERREPLY lpcReply
;
241 *lpdpidAllocatedId
= lpcReply
->ID
;
243 TRACE( "Received reply for id = 0x%08x\n", lpcReply
->ID
);
245 HeapFree( GetProcessHeap(), 0, lpMsg
);
251 HRESULT
DP_MSG_ForwardPlayerCreation( IDirectPlay2AImpl
* This
, DPID dpidServer
)
254 LPDPSP_MSG_ADDFORWARDREQUEST lpMsgBody
;
258 dwMsgSize
= This
->dp2
->spData
.dwSPHeaderSize
+ sizeof( *lpMsgBody
);
260 lpMsg
= HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY
, dwMsgSize
);
262 lpMsgBody
= (LPDPSP_MSG_ADDFORWARDREQUEST
)( (LPBYTE
)lpMsg
+
263 This
->dp2
->spData
.dwSPHeaderSize
);
265 /* Compose dplay message envelope */
266 lpMsgBody
->envelope
.dwMagic
= DPMSG_SIGNATURE
;
267 lpMsgBody
->envelope
.wCommandId
= DPMSGCMD_ADDFORWARDREQUEST
;
268 lpMsgBody
->envelope
.wVersion
= DX61AVERSION
;
270 /* Compose body of message */
273 /* Send the message */
277 data
.dwFlags
= DPSEND_GUARANTEED
;
278 data
.idPlayerTo
= 0; /* Name server */
279 data
.idPlayerFrom
= dpidServer
; /* Sending from session server */
280 data
.lpMessage
= lpMsg
;
281 data
.dwMessageSize
= dwMsgSize
;
282 data
.bSystemMessage
= TRUE
; /* Allow reply to be sent */
283 data
.lpISP
= This
->dp2
->spData
.lpISP
;
285 TRACE( "Sending forward player request with 0x%08x\n", dpidServer
);
287 lpMsg
= DP_MSG_ExpectReply( This
, &data
,
288 DPMSG_RELIABLE_API_TIMER
,
290 &lpMsg
, &dwMsgSize
);
293 /* Need to examine the data and extract the new player id */
296 FIXME( "Name Table reply received: stub\n" );
302 /* Queue up a structure indicating that we want a reply of type wReplyCommandId. DPlay does
303 * not seem to offer any way of uniquely differentiating between replies of the same type
304 * relative to the request sent. There is an implicit assumption that there will be no
305 * ordering issues on sends and receives from the opposite machine. No wonder MS is not
306 * a networking company.
309 LPVOID
DP_MSG_ExpectReply( IDirectPlay2AImpl
* This
, LPDPSP_SENDDATA lpData
,
310 DWORD dwWaitTime
, WORD wReplyCommandId
,
311 LPVOID
* lplpReplyMsg
, LPDWORD lpdwMsgBodySize
)
315 DP_MSG_REPLY_STRUCT_LIST replyStructList
;
318 /* Setup for receipt */
319 hMsgReceipt
= DP_MSG_BuildAndLinkReplyStruct( This
, &replyStructList
,
322 TRACE( "Sending msg and expecting cmd %u in reply within %u ticks\n",
323 wReplyCommandId
, dwWaitTime
);
324 hr
= (*This
->dp2
->spData
.lpCB
->Send
)( lpData
);
328 ERR( "Send failed: %s\n", DPLAYX_HresultToString( hr
) );
332 /* The reply message will trigger the hMsgReceipt event effectively switching
333 * control back to this thread. See DP_MSG_ReplyReceived.
335 dwWaitReturn
= WaitForSingleObject( hMsgReceipt
, dwWaitTime
);
336 if( dwWaitReturn
!= WAIT_OBJECT_0
)
338 ERR( "Wait failed 0x%08x\n", dwWaitReturn
);
343 return DP_MSG_CleanReplyStruct( &replyStructList
, lplpReplyMsg
, lpdwMsgBodySize
);
346 /* Determine if there is a matching request for this incoming message and then copy
347 * all important data. It is quite silly to have to copy the message, but the documents
348 * indicate that a copy is taken. Silly really.
350 void DP_MSG_ReplyReceived( IDirectPlay2AImpl
* This
, WORD wCommandId
,
351 LPCVOID lpcMsgBody
, DWORD dwMsgBodySize
)
353 LPDP_MSG_REPLY_STRUCT_LIST lpReplyList
;
356 if( wCommandId
== DPMSGCMD_ADDFORWARDREQUEST
)
362 /* Find, and immediately remove (to avoid double triggering), the appropriate entry. Call locked to
365 EnterCriticalSection( &This
->unk
->DP_lock
);
366 DPQ_REMOVE_ENTRY( This
->dp2
->replysExpected
, replysExpected
, replyExpected
.wExpectedReply
,
367 ==, wCommandId
, lpReplyList
);
368 LeaveCriticalSection( &This
->unk
->DP_lock
);
370 if( lpReplyList
!= NULL
)
372 lpReplyList
->replyExpected
.dwMsgBodySize
= dwMsgBodySize
;
373 lpReplyList
->replyExpected
.lpReplyMsg
= HeapAlloc( GetProcessHeap(),
376 CopyMemory( lpReplyList
->replyExpected
.lpReplyMsg
,
377 lpcMsgBody
, dwMsgBodySize
);
379 /* Signal the thread which sent the message that it has a reply */
380 SetEvent( lpReplyList
->replyExpected
.hReceipt
);
384 ERR( "No receipt event set - only expecting in reply mode\n" );