dplayx: Adjusted timers to the values in the documentation
[wine/gsoc_dplay.git] / dlls / dplayx / dplayx_messages.c
blob6006c414b4c6dc67717e76b0d3e34b98afd4705e
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
19 * NOTES
20 * o Messaging interface required for both DirectPlay and DirectPlayLobby.
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"
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
41 HANDLE hStart;
42 HANDLE hDeath;
43 HANDLE hSettingRead;
44 HANDLE hNotifyEvent;
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 )
59 DWORD dwMsgThreadId;
60 LPMSGTHREADINFO lpThreadInfo;
62 lpThreadInfo = HeapAlloc( GetProcessHeap(), 0, sizeof( *lpThreadInfo ) );
63 if( lpThreadInfo == NULL )
65 return 0;
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 ) )
74 ERR( "Unable to duplicate event handle\n" );
75 goto error;
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 */
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 */
95 ERR( "Unable to create msg thread\n" );
96 goto error;
99 /* FIXME: Should I be closing the handle to the thread or does that
100 terminate the thread? */
102 return dwMsgThreadId;
104 error:
106 HeapFree( GetProcessHeap(), 0, lpThreadInfo );
108 return 0;
111 static DWORD CALLBACK DPL_MSG_ThreadMain( LPVOID lpContext )
113 LPMSGTHREADINFO lpThreadInfo = lpContext;
114 DWORD dwWaitResult;
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 );
123 goto end_of_thread;
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" );
143 for ( ;; )
145 MSG lobbyMsg;
146 GetMessageW( &lobbyMsg, 0, 0, 0 );
149 end_of_thread:
150 TRACE( "Msg thread exiting!\n" );
151 HeapFree( GetProcessHeap(), 0, lpThreadInfo );
153 return 0;
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 );
164 static
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;
181 static
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 )
196 LPVOID lpMsg;
197 LPDPSP_MSG_REQUESTPLAYERID lpMsgBody;
198 DWORD dwMsgSize;
199 HRESULT hr = DP_OK;
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 */
218 DPSP_SENDDATA data;
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 */
235 if( SUCCEEDED(hr) )
237 LPCDPSP_MSG_REQUESTPLAYERREPLY lpcReply;
239 lpcReply = lpMsg;
241 *lpdpidAllocatedId = lpcReply->ID;
243 TRACE( "Received reply for id = 0x%08x\n", lpcReply->ID );
245 HeapFree( GetProcessHeap(), 0, lpMsg );
248 return hr;
251 HRESULT DP_MSG_ForwardPlayerCreation( IDirectPlay2AImpl* This, DPID dpidServer )
253 LPVOID lpMsg;
254 LPDPSP_MSG_ADDFORWARDREQUEST lpMsgBody;
255 DWORD dwMsgSize;
256 HRESULT hr = DP_OK;
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 */
271 FIXME( "TODO\n" );
273 /* Send the message */
275 DPSP_SENDDATA data;
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,
289 DPMSGCMD_ADDFORWARD,
290 &lpMsg, &dwMsgSize );
293 /* Need to examine the data and extract the new player id */
294 if( lpMsg != NULL )
296 FIXME( "Name Table reply received: stub\n" );
299 return hr;
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.
308 static
309 LPVOID DP_MSG_ExpectReply( IDirectPlay2AImpl* This, LPDPSP_SENDDATA lpData,
310 DWORD dwWaitTime, WORD wReplyCommandId,
311 LPVOID* lplpReplyMsg, LPDWORD lpdwMsgBodySize )
313 HRESULT hr;
314 HANDLE hMsgReceipt;
315 DP_MSG_REPLY_STRUCT_LIST replyStructList;
316 DWORD dwWaitReturn;
318 /* Setup for receipt */
319 hMsgReceipt = DP_MSG_BuildAndLinkReplyStruct( This, &replyStructList,
320 wReplyCommandId );
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 );
326 if( FAILED(hr) )
328 ERR( "Send failed: %s\n", DPLAYX_HresultToString( hr ) );
329 return NULL;
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 );
339 return NULL;
342 /* Clean Up */
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;
355 #if 0
356 if( wCommandId == DPMSGCMD_ADDFORWARDREQUEST )
358 DebugBreak();
360 #endif
362 /* Find, and immediately remove (to avoid double triggering), the appropriate entry. Call locked to
363 * avoid problems.
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(),
374 HEAP_ZERO_MEMORY,
375 dwMsgBodySize );
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 );
382 else
384 ERR( "No receipt event set - only expecting in reply mode\n" );
385 DebugBreak();