From 72526ba4f5e5247b077ee0f8d604b47637247536 Mon Sep 17 00:00:00 2001 From: Peter Hunnisett Date: Tue, 26 Sep 2000 23:11:48 +0000 Subject: [PATCH] - Remove winmm hack in dplay code - Fix up some missing holes in the code - More message implementation - Status documentation update --- dlls/Makefile.in | 2 +- dlls/dplayx/Makefile.in | 2 +- dlls/dplayx/dplay.c | 81 +++++++++++++++++++++----- dlls/dplayx/dplaysp.c | 8 ++- dlls/dplayx/dplaysp.h | 34 ++++++----- dlls/dplayx/dplayx.spec | 3 +- dlls/dplayx/dplayx_messages.c | 32 ++++++++-- dlls/dplayx/dplayx_messages.h | 19 +++--- dlls/dplayx/dplayx_queue.h | 72 ++++++++++++++++++++--- dlls/dplayx/dplobby.c | 13 +++-- dlls/dplayx/name_server.c | 100 ++++++++++++++++++++++++-------- documentation/status/directplay | 52 ++++++++++------- 12 files changed, 314 insertions(+), 104 deletions(-) diff --git a/dlls/Makefile.in b/dlls/Makefile.in index c3538178819..e43f350d96d 100644 --- a/dlls/Makefile.in +++ b/dlls/Makefile.in @@ -380,7 +380,7 @@ crtdll/libcrtdll.so: libkernel32.so ddraw/libddraw.so: libole32.so libuser32.so libx11drv.so libgdi32.so libkernel32.so dinput/libdinput.so: libuser32.so libkernel32.so dplay/libdplay.so: libdplayx.so -dplayx/libdplayx.so: libole32.so libadvapi32.so libkernel32.so +dplayx/libdplayx.so: libwinmm.so libole32.so libadvapi32.so libkernel32.so dsound/libdsound.so: libwinmm.so libkernel32.so gdi/libgdi32.so: libkernel32.so icmp/libicmp.so: libkernel32.so diff --git a/dlls/dplayx/Makefile.in b/dlls/dplayx/Makefile.in index d95b19940e1..0f3889ce092 100644 --- a/dlls/dplayx/Makefile.in +++ b/dlls/dplayx/Makefile.in @@ -4,7 +4,7 @@ SRCDIR = @srcdir@ VPATH = @srcdir@ MODULE = dplayx SOVERSION = 1.0 -IMPORTS = ole32 advapi32 kernel32 +IMPORTS = winmm ole32 advapi32 kernel32 C_SRCS = \ dpclassfactory.c \ diff --git a/dlls/dplayx/dplay.c b/dlls/dplayx/dplay.c index cec6ac4fc78..63c3a58932d 100644 --- a/dlls/dplayx/dplay.c +++ b/dlls/dplayx/dplay.c @@ -622,6 +622,8 @@ HRESULT DP_HandleMessage( IDirectPlay2Impl* This, LPCVOID lpMessageBody, This, lpMessageBody, dwMessageBodySize, lpMessageHeader, wCommandId, wVersion ); + DebugBreak(); + switch( wCommandId ) { case DPMSGCMD_REQUESTNEWPLAYERID: @@ -638,7 +640,25 @@ HRESULT DP_HandleMessage( IDirectPlay2Impl* This, LPCVOID lpMessageBody, HEAP_ZERO_MEMORY, *lpdwMsgSize ); - FIXME( "Ignoring dwFlags in msg\n" ); + FIXME( "Ignoring dwFlags in request msg\n" ); + +#if 0 + /* This is just a test. See how large the SPData is and send it */ + { + LPVOID lpData; + DWORD dwDataSize; + HRESULT hr; + + hr = IDirectPlaySP_GetSPData( This->dp2->spData.lpISP, &lpData, + &dwDataSize, DPSET_REMOTE ); + + if( FAILED(hr) ) + { + ERR( "Unable to get remote SPData %s\n", DPLAYX_HresultToString(hr) ); + } + + } +#endif /* Setup the reply */ lpReply = (LPDPMSG_NEWPLAYERIDREPLY)( (BYTE*)(*lplpReply) + @@ -648,10 +668,10 @@ HRESULT DP_HandleMessage( IDirectPlay2Impl* This, LPCVOID lpMessageBody, lpReply->envelope.wCommandId = DPMSGCMD_NEWPLAYERIDREPLY; lpReply->envelope.wVersion = DPMSGVER_DP6; -#if 0 - /* FIXME: Need to know the proper contents of the message! */ lpReply->dpidNewPlayerId = DP_NextObjectId(); -#endif + + TRACE( "Allocating new playerid 0x%08lx from remote request\n", + lpReply->dpidNewPlayerId ); break; } @@ -659,8 +679,6 @@ HRESULT DP_HandleMessage( IDirectPlay2Impl* This, LPCVOID lpMessageBody, case DPMSGCMD_NEWPLAYERIDREPLY: { - DebugBreak(); - if( This->dp2->hMsgReceipt ) { /* This is a hack only */ @@ -672,7 +690,7 @@ HRESULT DP_HandleMessage( IDirectPlay2Impl* This, LPCVOID lpMessageBody, } else { - ERR( "No receipt event set\n" ); + ERR( "No receipt event set - only expecting in reply mode\n" ); } break; @@ -1304,6 +1322,7 @@ static HRESULT WINAPI DP_IF_CreatePlayer DWORD dwFlags, BOOL bAnsi ) { + HANDLE hr = DP_OK; lpPlayerData lpPData; lpPlayerList lpPList; @@ -1341,7 +1360,7 @@ static HRESULT WINAPI DP_IF_CreatePlayer } else { - HRESULT hr = DP_MSG_SendRequestPlayerId( This, dwFlags, lpidPlayer ); + hr = DP_MSG_SendRequestPlayerId( This, dwFlags, lpidPlayer ); if( FAILED(hr) ) { @@ -1409,7 +1428,34 @@ static HRESULT WINAPI DP_IF_CreatePlayer data.lpSPMessageHeader = lpMsgHdr; data.lpISP = This->dp2->spData.lpISP; - (*This->dp2->spData.lpCB->CreatePlayer)( &data ); + hr = (*This->dp2->spData.lpCB->CreatePlayer)( &data ); + } + + if( FAILED(hr) ) + { + ERR( "Failed to create player with sp: %s\n", DPLAYX_HresultToString(hr) ); + return hr; + } + + /* Now let the SP know that this player is a member of the system group */ + if( This->dp2->spData.lpCB->AddPlayerToGroup ) + { + DPSP_ADDPLAYERTOGROUPDATA data; + + data.idPlayer = *lpidPlayer; + data.idGroup = DPID_SYSTEM_GROUP; + data.lpISP = This->dp2->spData.lpISP; + + TRACE( "Calling SP AddPlayerToGroup (sys group)\n" ); + + hr = (*This->dp2->spData.lpCB->AddPlayerToGroup)( &data ); + } + + if( FAILED(hr) ) + { + ERR( "Failed to add player to sys groupwith sp: %s\n", + DPLAYX_HresultToString(hr) ); + return hr; } /* Inform all other peers of the creation of a new player. If there are @@ -1435,11 +1481,11 @@ static HRESULT WINAPI DP_IF_CreatePlayer /* FIXME: Correct to just use send effectively? */ /* FIXME: Should size include data w/ message or just message "header" */ /* FIXME: Check return code */ - DP_SendEx( This, DPID_SERVERPLAYER, DPID_ALLPLAYERS, 0, &msg, sizeof( msg ), - 0, 0, NULL, NULL, bAnsi ); + hr = DP_SendEx( This, DPID_SERVERPLAYER, DPID_ALLPLAYERS, 0, &msg, + sizeof( msg ), 0, 0, NULL, NULL, bAnsi ); } - return DP_OK; + return hr; } static HRESULT WINAPI DirectPlay2AImpl_CreatePlayer @@ -1936,6 +1982,9 @@ static void DP_InvokeEnumSessionCallbacks FIXME( ": not checking for conditions\n" ); + /* Not sure if this should be pruning but it's convenient */ + NS_PruneSessionCache( lpNSInfo ); + NS_ResetSessionEnumeration( lpNSInfo ); /* Enumerate all sessions */ @@ -2663,7 +2712,7 @@ static HRESULT WINAPI DP_SecureOpen if( dwFlags & DPOPEN_JOIN ) { - DPID dpidServerId = DPID_SERVERPLAYER; + DPID dpidServerId = DPID_UNKNOWN; /* Create the server player for this interface. This way we can receive * messages for this session. @@ -2685,6 +2734,12 @@ static HRESULT WINAPI DP_SecureOpen 0, DPPLAYER_SERVERPLAYER, bAnsi ); } + if( FAILED(hr) ) + { + ERR( "Couldn't create name server/system player: %s\n", + DPLAYX_HresultToString(hr) ); + } + return hr; } diff --git a/dlls/dplayx/dplaysp.c b/dlls/dplayx/dplaysp.c index 1148dc865ca..2a21a012737 100644 --- a/dlls/dplayx/dplaysp.c +++ b/dlls/dplayx/dplaysp.c @@ -18,7 +18,7 @@ /* FIXME: Need to add interface locking inside procedures */ -DEFAULT_DEBUG_CHANNEL(dplay); +DEFAULT_DEBUG_CHANNEL(dplay) /* Prototypes */ static BOOL DPSP_CreateIUnknown( LPVOID lpSP ); @@ -369,6 +369,8 @@ static HRESULT WINAPI IDirectPlaySPImpl_GetSPPlayerData FIXME( "(%p)->(0x%08lx,%p,%p,0x%08lx): stub\n", This, idPlayer, lplpData, lpdwDataSize, dwFlags ); + /* What to do in the case where there is nothing set yet? */ + *lplpData = This->sp->lpPlayerData; *lpdwDataSize = This->sp->dwPlayerDataSize; @@ -440,6 +442,8 @@ static HRESULT WINAPI IDirectPlaySPImpl_HandleMessage break; } + case DPMSGCMD_GETNAMETABLE: + case DPMSGCMD_GETNAMETABLEREPLY: case DPMSGCMD_NEWPLAYERIDREPLY: case DPMSGCMD_REQUESTNEWPLAYERID: { @@ -796,6 +800,8 @@ static HRESULT WINAPI IDirectPlaySPImpl_GetSPData } #endif + /* FIXME: What to do in the case where this isn't initialized yet? */ + /* Yes, we're supposed to return a pointer to the memory we have stored! */ if( dwFlags == DPSET_REMOTE ) { diff --git a/dlls/dplayx/dplaysp.h b/dlls/dplayx/dplaysp.h index 9072e3007d5..aaa7874f5c4 100644 --- a/dlls/dplayx/dplaysp.h +++ b/dlls/dplayx/dplaysp.h @@ -55,23 +55,27 @@ typedef BOOL (CALLBACK* LPENUMMRUCALLBACK)( LPCVOID lpData, ICOM_DEFINE(IDirectPlaySP,IUnknown) #undef ICOM_INTERFACE + +/* NOTE: The microsoft provided header file doesn't have these access + * functions + */ /*** IUnknown methods ***/ -#define IDirectPlaySP_QueryInterface(p,a,b) ICOM_CALL2(QueryInterface,p,a,b) -#define IDirectPlaySP_AddRef(p) ICOM_CALL (AddRef,p) -#define IDirectPlaySP_Release(p) ICOM_CALL (Release,p) +#define IDirectPlaySP_QueryInterface(p,a,b) ICOM_CALL2(QueryInterface,p,a,b) +#define IDirectPlaySP_AddRef(p) ICOM_CALL (AddRef,p) +#define IDirectPlaySP_Release(p) ICOM_CALL (Release,p) /*** IDirectPlaySP methods ***/ -#define IDirectPlaySP_AddMRUEntry ICOM_CALL5(AddMRUEntry,p,a,b,c,d,e) -#define IDirectPlaySP_CreateAddress ICOM_CALL6(CreateAddress,p,a,b,c,d,e,f) -#define IDirectPlaySP_EnumAddress ICOM_CALL4(EnumAddress,p,a,b,c,d) -#define IDirectPlaySP_EnumMRUEntries ICOM_CALL4(EnumMRUEntries,p,a,b,c,d) -#define IDirectPlaySP_GetPlayerFlags ICOM_CALL2(GetPlayerFlags,p,a,b) -#define IDirectPlaySP_GetSPPlayerData ICOM_CALL4(GetSPPlayerData,p,a,b,c,d) -#define IDirectPlaySP_HandleMessage ICOM_CALL3(HandleMessage,p,a,b,c) -#define IDirectPlaySP_SetSPPlayerData ICOM_CALL4(SetSPPlayerData,p,a,b,c,d) -#define IDirectPlaySP_CreateCompoundAddress ICOM_CALL4(CreateCompoundAddress,p,a,b,c,d) -#define IDirectPlaySP_GetSPData ICOM_CALL3(GetSPData,p,a,b,c) -#define IDirectPlaySP_SetSPData ICOM_CALL3(SetSPData,p,a,b,c) -#define IDirectPlaySP_SendComplete ICOM_CALL2(SendComplete,p,a,b) +#define IDirectPlaySP_AddMRUEntry(p,a,b,c,d,e) ICOM_CALL5(AddMRUEntry,p,a,b,c,d,e) +#define IDirectPlaySP_CreateAddress(p,a,b,c,d,e,f) ICOM_CALL6(CreateAddress,p,a,b,c,d,e,f) +#define IDirectPlaySP_EnumAddress(p,a,b,c,d) ICOM_CALL4(EnumAddress,p,a,b,c,d) +#define IDirectPlaySP_EnumMRUEntries(p,a,b,c,d) ICOM_CALL4(EnumMRUEntries,p,a,b,c,d) +#define IDirectPlaySP_GetPlayerFlags(p,a,b) ICOM_CALL2(GetPlayerFlags,p,a,b) +#define IDirectPlaySP_GetSPPlayerData(p,a,b,c,d) ICOM_CALL4(GetSPPlayerData,p,a,b,c,d) +#define IDirectPlaySP_HandleMessage(p,a,b,c) ICOM_CALL3(HandleMessage,p,a,b,c) +#define IDirectPlaySP_SetSPPlayerData(p,a,b,c,d) ICOM_CALL4(SetSPPlayerData,p,a,b,c,d) +#define IDirectPlaySP_CreateCompoundAddress(p,a,b,c,d) ICOM_CALL4(CreateCompoundAddress,p,a,b,c,d) +#define IDirectPlaySP_GetSPData(p,a,b,c) ICOM_CALL3(GetSPData,p,a,b,c) +#define IDirectPlaySP_SetSPData(p,a,b,c) ICOM_CALL3(SetSPData,p,a,b,c) +#define IDirectPlaySP_SendComplete(p,a,b) ICOM_CALL2(SendComplete,p,a,b) /* SP Callback stuff */ diff --git a/dlls/dplayx/dplayx.spec b/dlls/dplayx/dplayx.spec index c52887fe599..1c75becbe4c 100644 --- a/dlls/dplayx/dplayx.spec +++ b/dlls/dplayx/dplayx.spec @@ -2,6 +2,7 @@ name dplayx type win32 init DPLAYX_LibMain +import winmm.dll import ole32.dll import advapi32.dll import kernel32.dll @@ -11,7 +12,7 @@ import kernel32.dll 3 stdcall DirectPlayEnumerateW(ptr ptr) DirectPlayEnumerateW 4 stdcall DirectPlayLobbyCreateA(ptr ptr ptr ptr long) DirectPlayLobbyCreateA 5 stdcall DirectPlayLobbyCreateW(ptr ptr ptr ptr long) DirectPlayLobbyCreateW - 6 stub gdwDPlaySPRefCount + 6 extern gdwDPlaySPRefCount gdwDPlaySPRefCount 9 stdcall DirectPlayEnumerate(ptr ptr) DirectPlayEnumerateA 10 stdcall DllCanUnloadNow() DPLAYX_DllCanUnloadNow 11 stdcall DllGetClassObject(ptr ptr ptr) DPLAYX_DllGetClassObject diff --git a/dlls/dplayx/dplayx_messages.c b/dlls/dplayx/dplayx_messages.c index c4a4ac04661..4f63d0139e1 100644 --- a/dlls/dplayx/dplayx_messages.c +++ b/dlls/dplayx/dplayx_messages.c @@ -149,10 +149,6 @@ HRESULT DP_MSG_SendRequestPlayerId( IDirectPlay2AImpl* This, DWORD dwFlags, DWORD dwWaitReturn; HRESULT hr = DP_OK; - FIXME( "semi stub\n" ); - - DebugBreak(); - dwMsgSize = This->dp2->spData.dwSPHeaderSize + sizeof( *lpMsgBody ); lpMsg = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, dwMsgSize ); @@ -185,6 +181,8 @@ HRESULT DP_MSG_SendRequestPlayerId( IDirectPlay2AImpl* This, DWORD dwFlags, /* Setup for receipt */ This->dp2->hMsgReceipt = CreateEventA( NULL, FALSE, FALSE, NULL ); + + TRACE( "Sending request for player id\n" ); hr = (*This->dp2->spData.lpCB->Send)( &data ); @@ -192,6 +190,7 @@ HRESULT DP_MSG_SendRequestPlayerId( IDirectPlay2AImpl* This, DWORD dwFlags, { ERR( "Request for new playerID send failed: %s\n", DPLAYX_HresultToString( hr ) ); + return DPERR_NOCONNECTION; } } @@ -199,13 +198,36 @@ HRESULT DP_MSG_SendRequestPlayerId( IDirectPlay2AImpl* This, DWORD dwFlags, if( dwWaitReturn != WAIT_OBJECT_0 ) { ERR( "Wait failed 0x%08lx\n", dwWaitReturn ); + hr = DPERR_TIMEOUT; } CloseHandle( This->dp2->hMsgReceipt ); This->dp2->hMsgReceipt = 0; /* Need to examine the data and extract the new player id */ - /* I just hope that dplay doesn't return the whole new player! */ + if( !FAILED(hr) ) + { + LPCDPMSG_NEWPLAYERIDREPLY lpcReply; + + lpcReply = (LPCDPMSG_NEWPLAYERIDREPLY)This->dp2->lpMsgReceived; + + *lpdpidAllocatedId = lpcReply->dpidNewPlayerId; + + TRACE( "Received reply for id = 0x%08lx\n", lpcReply->dpidNewPlayerId ); + + /* FIXME: I think that the rest of the message has something to do + * with remote data for the player that perhaps I need to setup. + */ +#if 0 + /* Set the passed service provider data */ + IDirectPlaySP_SetSPData( This->dp2->spData.lpISP, data, + msgsize, DPSET_REMOTE ); + +#endif + + HeapFree( GetProcessHeap(), 0, This->dp2->lpMsgReceived ); + This->dp2->lpMsgReceived = NULL; + } return hr; } diff --git a/dlls/dplayx/dplayx_messages.h b/dlls/dplayx/dplayx_messages.h index 770343543b1..c1812d62e6e 100644 --- a/dlls/dplayx/dplayx_messages.h +++ b/dlls/dplayx/dplayx_messages.h @@ -19,9 +19,6 @@ DWORD CreateLobbyMessageReceptionThread( HANDLE hNotifyEvent, HANDLE hStart, #define DPMSGCMD_ENUMSESSIONSREPLY 1 #define DPMSGCMD_ENUMSESSIONSREQUEST 2 -#define DPMSGCMD_GETSETNAMETABLE 3 /* Request info from NS about - existing players/groups etc. Is - also used for reply */ #define DPMSGCMD_REQUESTNEWPLAYERID 5 @@ -34,7 +31,9 @@ DWORD CreateLobbyMessageReceptionThread( HANDLE hNotifyEvent, HANDLE hStart, #define DPMSGCMD_ENUMGROUPS 17 -#define DPMSGCMD_FORWARDCREATEPLAYER 19 /* This may be a get name table req */ +#define DPMSGCMD_GETNAMETABLE 19 + +#define DPMSGCMD_GETNAMETABLEREPLY 29 /* This is what DP 6 defines it as. Don't know what it means. All messages * defined below are DPMSGVER_DP6. @@ -48,7 +47,7 @@ DWORD CreateLobbyMessageReceptionThread( HANDLE hNotifyEvent, HANDLE hStart, * the message. */ -/* Size is 4 bytes */ +/* Size is 8 bytes */ typedef struct tagDPMSG_SENDENVELOPE { DWORD dwMagic; @@ -126,15 +125,17 @@ typedef struct tagDPMSG_REQUESTNEWPLAYERID } DPMSG_REQUESTNEWPLAYERID, *LPDPMSG_REQUESTNEWPLAYERID; typedef const DPMSG_REQUESTNEWPLAYERID* LPCDPMSG_REQUESTNEWPLAYERID; -/* 64 byte - ~18 header ~= 46 bytes msg */ +/* 48 bytes msg */ typedef struct tagDPMSG_NEWPLAYERIDREPLY { DPMSG_SENDENVELOPE envelope; -#if 0 DPID dpidNewPlayerId; -#else - BYTE unknown[38]; +#if 1 + /* Assume that this is data that is tacked on to the end of the message + * that comes from the SP remote data stored that needs to be propagated. + */ + BYTE unknown[36]; /* This appears to always be 0 - not sure though */ #endif } DPMSG_NEWPLAYERIDREPLY, *LPDPMSG_NEWPLAYERIDREPLY; diff --git a/dlls/dplayx/dplayx_queue.h b/dlls/dplayx/dplayx_queue.h index 142713a6801..0ef9b46ee7e 100644 --- a/dlls/dplayx/dplayx_queue.h +++ b/dlls/dplayx/dplayx_queue.h @@ -77,7 +77,7 @@ do { \ */ #define DPQ_FIND_ENTRY( head, elm, field, fieldCompareOperator, fieldToCompare, rc )\ do { \ - (rc) = (head).lpQHFirst; /* NULL head? */ \ + (rc) = DPQ_FIRST(head); /* NULL head? */ \ \ while( rc ) \ { \ @@ -96,6 +96,39 @@ do { \ } \ } while(0) +/* head - pointer to DPQ_HEAD struct + * elm - how to find the next element + * field - to be concatenated to rc to compare with fieldToCompare + * fieldToCompare - The value that we're comparing against + * compare_cb - Callback to invoke to determine if comparision should continue. + * Callback must be defined with DPQ_DECL_COMPARECB. + * rc - Variable to put the return code. Same type as (head).lpQHFirst + */ +#define DPQ_FIND_ENTRY_CB( head, elm, field, compare_cb, fieldToCompare, rc )\ +do { \ + (rc) = DPQ_FIRST(head); /* NULL head? */ \ + \ + while( rc ) \ + { \ + /* What we're searching for? */ \ + if( compare_cb( &((rc)->field), &(fieldToCompare) ) ) \ + { \ + break; /* no more */ \ + } \ + \ + /* End of list check */ \ + if( ( (rc) = (rc)->elm.lpQNext ) == (head).lpQHFirst ) \ + { \ + rc = NULL; \ + break; \ + } \ + } \ +} while(0) + +/* How to define the method to be passed to DPQ_DELETEQ */ +#define DPQ_DECL_COMPARECB( name, type ) BOOL name( const type* elem1, const type* elem2 ) + + /* head - pointer to DPQ_HEAD struct * elm - how to find the next element * field - to be concatenated to rc to compare with fieldToEqual @@ -115,19 +148,42 @@ do { \ } \ } while(0) +/* head - pointer to DPQ_HEAD struct + * elm - how to find the next element + * field - to be concatenated to rc to compare with fieldToCompare + * fieldToCompare - The value that we're comparing against + * compare_cb - Callback to invoke to determine if comparision should continue. + * Callback must be defined with DPQ_DECL_COMPARECB. + * rc - Variable to put the return code. Same type as (head).lpQHFirst + */ +#define DPQ_REMOVE_ENTRY_CB( head, elm, field, compare_cb, fieldToCompare, rc )\ +do { \ + DPQ_FIND_ENTRY_CB( head, elm, field, compare_cb, fieldToCompare, rc );\ + \ + /* Was the element found? */ \ + if( rc ) \ + { \ + DPQ_REMOVE( head, rc, elm ); \ + } \ +} while(0) + + /* Delete the entire queue * head - pointer to the head of the queue * field - field to access the next elements of the queue * type - type of the pointer to the element element * df - a delete function to be called. Declared with DPQ_DECL_DELETECB. */ -#define DPQ_DELETEQ( head, field, type, df ) \ -while( !DPQ_IS_EMPTY(head) ) \ -{ \ - type holder = (head).lpQHFirst; \ - DPQ_REMOVE( head, holder, field ); \ - df( holder ); \ -} +#define DPQ_DELETEQ( head, field, type, df ) \ +do \ +{ \ + while( !DPQ_IS_EMPTY(head) ) \ + { \ + type holder = DPQ_FIRST(head); \ + DPQ_REMOVE( head, holder, field ); \ + df( holder ); \ + } \ +} while(0) /* How to define the method to be passed to DPQ_DELETEQ */ #define DPQ_DECL_DELETECB( name, type ) void name( type elem ) diff --git a/dlls/dplayx/dplobby.c b/dlls/dplayx/dplobby.c index 238757d2e10..3a451fefe41 100644 --- a/dlls/dplayx/dplobby.c +++ b/dlls/dplayx/dplobby.c @@ -1137,7 +1137,7 @@ BOOL DPL_CreateAndSetLobbyHandles( DWORD dwDestProcessId, HANDLE hDestProcess, LPHANDLE lphRead ) { /* These are the handles for the created process */ - HANDLE hAppStart, hAppDeath, hAppRead; + HANDLE hAppStart, hAppDeath, hAppRead, hTemp; SECURITY_ATTRIBUTES s_attrib; s_attrib.nLength = sizeof( s_attrib ); @@ -1145,9 +1145,14 @@ BOOL DPL_CreateAndSetLobbyHandles( DWORD dwDestProcessId, HANDLE hDestProcess, s_attrib.bInheritHandle = TRUE; /* FIXME: Is there a handle leak here? */ - *lphStart = CreateEventA( &s_attrib, TRUE, FALSE, NULL ); - *lphDeath = CreateEventA( &s_attrib, TRUE, FALSE, NULL ); - *lphRead = CreateEventA( &s_attrib, TRUE, FALSE, NULL ); + hTemp = CreateEventA( &s_attrib, TRUE, FALSE, NULL ); + *lphStart = ConvertToGlobalHandle( hTemp ); + + hTemp = CreateEventA( &s_attrib, TRUE, FALSE, NULL ); + *lphDeath = ConvertToGlobalHandle( hTemp ); + + hTemp = CreateEventA( &s_attrib, TRUE, FALSE, NULL ); + *lphRead = ConvertToGlobalHandle( hTemp ); if( ( !DuplicateHandle( GetCurrentProcess(), *lphStart, hDestProcess, &hAppStart, diff --git a/dlls/dplayx/name_server.c b/dlls/dplayx/name_server.c index acae34d5adb..529b414d32a 100644 --- a/dlls/dplayx/name_server.c +++ b/dlls/dplayx/name_server.c @@ -11,6 +11,7 @@ #include "winbase.h" #include "debugtools.h" #include "heap.h" +#include "mmsystem.h" #include "dplayx_global.h" #include "name_server.h" @@ -43,16 +44,25 @@ struct NSCache }; typedef struct NSCache NSCache, *lpNSCache; +/* Function prototypes */ +DPQ_DECL_DELETECB( cbDeleteNSNodeFromHeap, lpNSCacheData ); + /* Name Server functions * --------------------- */ void NS_SetLocalComputerAsNameServer( LPCDPSESSIONDESC2 lpsd ) { #if 0 + /* FIXME: Remove this method? */ DPLAYX_SetLocalSession( lpsd ); #endif } +DPQ_DECL_COMPARECB( cbUglyPig, GUID ) +{ + return IsEqualGUID( elem1, elem2 ); +} + /* Store the given NS remote address for future reference */ void NS_SetRemoteComputerAsNameServer( LPVOID lpNSAddrHdr, DWORD dwHdrSize, @@ -65,8 +75,21 @@ void NS_SetRemoteComputerAsNameServer( LPVOID lpNSAddrHdr, TRACE( "%p, %p, %p\n", lpNSAddrHdr, lpMsg, lpNSInfo ); /* FIXME: Should check to see if the reply is for an existing session. If - * so we just update the contents and update the timestamp. + * so we remove the old and add the new so oldest is at front. */ + + /* See if we can find this session. If we can, remove it as it's a dup */ + DPQ_REMOVE_ENTRY_CB( lpCache->first, next, data->guidInstance, cbUglyPig, + lpMsg->sd.guidInstance, lpCacheNode ); + + if( lpCacheNode != NULL ) + { + TRACE( "Duplicate session entry for %s removed - updated version kept\n", + debugstr_guid( &lpCacheNode->data->guidInstance ) ); + cbDeleteNSNodeFromHeap( lpCacheNode ); + } + + /* Add this to the list */ lpCacheNode = (lpNSCacheData)HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof( *lpCacheNode ) ); @@ -96,7 +119,7 @@ void NS_SetRemoteComputerAsNameServer( LPVOID lpNSAddrHdr, HEAP_ZERO_MEMORY, (LPWSTR)(lpMsg+1) ); - lpCacheNode->dwTime = GetTickCount(); + lpCacheNode->dwTime = timeGetTime(); DPQ_INSERT(lpCache->first, lpCacheNode, next ); @@ -116,6 +139,10 @@ LPVOID NS_GetNSAddr( LPVOID lpNSInfo ) /* Ok. Cheat and don't search for the correct stuff just take the first. * FIXME: In the future how are we to know what is _THE_ enum we used? + * This is going to have to go into dplay somehow. Perhaps it + * comes back with app server id for the join command! Oh...that + * must be it. That would make this method obsolete once that's + * in place. */ return lpCache->first.lpQHFirst->lpNSAddrHdr; @@ -159,17 +186,17 @@ HRESULT NS_SendSessionRequestBroadcast( LPCGUID lpcGuid, return (lpSpData->lpCB->EnumSessions)( &data ); } -DPQ_DECL_DELETECB( cbDeleteNSNodeFromHeap, lpNSCacheData ); +/* Delete a name server node which has been allocated on the heap */ DPQ_DECL_DELETECB( cbDeleteNSNodeFromHeap, lpNSCacheData ) { + /* NOTE: This proc doesn't deal with the walking pointer */ + /* FIXME: Memory leak on data (contained ptrs) */ HeapFree( GetProcessHeap(), 0, elem->data ); HeapFree( GetProcessHeap(), 0, elem->lpNSAddrHdr ); HeapFree( GetProcessHeap(), 0, elem ); } - - /* Render all data in a session cache invalid */ void NS_InvalidateSessionCache( LPVOID lpNSInfo ) { @@ -216,7 +243,6 @@ void NS_DeleteSessionCache( LPVOID lpNSInfo ) /* Reinitialize the present pointer for this cache */ void NS_ResetSessionEnumeration( LPVOID lpNSInfo ) { - ((lpNSCache)lpNSInfo)->present = ((lpNSCache)lpNSInfo)->first.lpQHFirst; } @@ -244,33 +270,60 @@ LPDPSESSIONDESC2 NS_WalkSessions( LPVOID lpNSInfo ) /* This method should check to see if there are any sessions which are * older than the criteria. If so, just delete that information. */ +/* FIXME: This needs to be called by some periodic timer */ void NS_PruneSessionCache( LPVOID lpNSInfo ) { lpNSCache lpCache = lpNSInfo; - lpNSCacheData lpCacheEntry; - DWORD dwPresentTime = GetTickCount(); -#if defined( HACK_TIMEGETTIME ) - DWORD dwPruneTime = dwPresentTime - 2; /* One iteration with safety */ -#else - DWORD dwPruneTime = dwPresentTime - 10000 /* 10 secs? */; -#endif + const DWORD dwPresentTime = timeGetTime(); + const DWORD dwPrunePeriod = 60000; /* is 60 secs enough? */ + const DWORD dwPruneTime = dwPresentTime - dwPrunePeriod; - FIXME( ": semi stub\n" ); - - /* FIXME: This doesn't handle time roll over correctly */ - /* FIXME: Session memory leak on delete */ - do + /* This silly little algorithm is based on the fact we keep entries in + * the queue in a time based order. It also assumes that it is not possible + * to wrap around over yourself (which is not unreasonable). + * The if statements verify if the first entry in the queue is less + * than dwPrunePeriod old depending on the "clock" roll over. + */ + for( ;; ) { - DPQ_FIND_ENTRY( lpCache->first, next, dwTime, <=, dwPruneTime, lpCacheEntry ); + lpNSCacheData lpFirstData; + + if( DPQ_IS_EMPTY(lpCache->first) ) + { + /* Nothing to prune */ + break; + } + + if( dwPruneTime > dwPresentTime ) /* 0 <= dwPresentTime <= dwPrunePeriod */ + { + if( ( DPQ_FIRST(lpCache->first)->dwTime <= dwPresentTime ) || + ( DPQ_FIRST(lpCache->first)->dwTime > dwPruneTime ) + ) + { + /* Less than dwPrunePeriod old - keep */ + break; + } + } + else /* dwPrunePeriod <= dwPresentTime <= max dword */ + { + if( ( DPQ_FIRST(lpCache->first)->dwTime <= dwPresentTime ) && + ( DPQ_FIRST(lpCache->first)->dwTime > dwPruneTime ) + ) + { + /* Less than dwPrunePeriod old - keep */ + break; + } + } + + lpFirstData = DPQ_FIRST(lpCache->first); + DPQ_REMOVE( lpCache->first, DPQ_FIRST(lpCache->first), next ); + cbDeleteNSNodeFromHeap( lpFirstData ); } - while( lpCacheEntry != NULL ); } - - -/* Message stuff */ +/* NAME SERVER Message stuff */ void NS_ReplyToEnumSessionsRequest( LPVOID lpMsg, LPDPSP_REPLYDATA lpReplyData, IDirectPlay2Impl* lpDP ) @@ -318,4 +371,3 @@ void NS_ReplyToEnumSessionsRequest( LPVOID lpMsg, lstrcpyW( (LPWSTR)(rmsg+1), string ); } - diff --git a/documentation/status/directplay b/documentation/status/directplay index 1540f7a5925..532f9149661 100644 --- a/documentation/status/directplay +++ b/documentation/status/directplay @@ -44,6 +44,12 @@ dplayx_messages.h, dplayx_messages.c: Messaging interface required for both DirectPlay and DirectPlayLobby. +dplaysp.c, +dplaysp.h: COM interface between DPLAYX and the service provider dlls. dplaysp.h is + something that should be included into any service provider. Basically + the COM interface is partially documented but is generally lacking in the + finer points. + Presently the architectural relationship between this files is a little shakey, but isn't so sufficiently bad that it needs fixing yet. @@ -56,7 +62,17 @@ However, now that address separation is a reality, all binary samples provided in the sdk can be used. I have had success spawning processes and one directx7 example will allow creation of an app and allow another to join it. Unfortunately, there isn't much for it to be able to do give the state of -inter lobby messaging. +inter directlobby or directplay messaging. + +Messages which work: + +For session enumeration: DPMSGCMD_ENUMSESSIONSREPLY & DPMSGCMD_ENUMSESSIONSREQUEST + have most fields understood, but not all. Everything _seems_ + to work. + +For playerid requesting: DPMSGCMD_REQUESTNEWPLAYERID & DPMSGCMD_NEWPLAYERIDREPLY + barely work. This needs to be completed for sessions to be + able to actually start. A small issue will be the fact that DirectX 6.1(ie. DirectPlay4) introduces a layer of functionality inside the DP objects which provide guaranteed protocol delivery. This is @@ -96,18 +112,21 @@ TODO: share registry implementation (or at least simplify). - Add in appropriate RegCloseKey calls for all the opening we're doing... - Fix all the buffer sizes for registry calls. They're off by one - but in a safe direction. - - Find out how to call the service provider dlls - they don't have a published interface! + - (done) Find out how to call the service provider dlls - they don't have a published interface! - Fix race condition on interface destruction - Handles need to be correctly reference counted - Need to check if we need to deallocate any list objects when destroying dplay interface - RunApplication process spawning needs to have correct syncronization. - Need to get inter lobby messages working. + - Decypher dplay messages between applications and implement... + - Need to implement lobby session spawning. ENHANCEMENTS: - Improve footprint and realtime blocking by setting up a seperate data share between lobby application and client since there can be multiple apps per - client. + client. Also get rid of offset dependency by making data offset independent + somehow. - Handle everything in UNICODE (as server does) and do conversions for ANSI interfaces. Should cut down on dplayx code base and maintanability (marginally) and could be used to improve efficiency of dialog with the server (it wouldn't @@ -124,34 +143,23 @@ Programs to make work: Next API to implement on a per SDK program basis: override.exe - - fixme:dplay:DirectPlayCreate Modem binding not supported yet - - DirectPlay3AImpl_InitializeConnection - - DirectPlay2AImpl_Open - - ? + - (calls dpserial so basic problems with that remain) + - after that ? dplaunch.exe - - Just needs final process startup messages to be exchanged correctly! + - I think that everything is basically working. The launched app may not + work on the other hand. lserver.exe - - IDirectPlayLobby2WImpl_Connect - - fixme:dplay:DirectPlayCreate Modem binding not supported yet - - IDirectPlay3WImpl_CreatePlayer - - IDirectPlay3WImpl_CreateGroup - - IDirectPlay3WImpl_SetGroupData - - IDirectPlay3WImpl_Send - - ? + - Missing messaging portion. Everything else works. bellhop.exe - - DP_SendSessionRequestBroadcast (implement the name server stuff) + - Need to implement lobby server support. - ? dpslots.exe - - IDirectPlayLobby3AImpl_ConnectEx - - ? - - override.exe - - DirectPlayCreate Service provider binding not supported yet - - ? + - Works as host except for message stuff + - Missing correct handling of receipt of playerid Other TODO: