/* * L0DPlay.c * * universal multiplayer library level 0 implementation file * transport layer: DirectPlay lobby aware PC-windows 95 */ /* * Author: Christophe Roguet * * Creation Date: 04/08/97 * */ #include "warnings.h" #include /* * we need this one to fill the interface structure * with pointers to the correct functions */ #include "PrivNetDef.h" #include "l0GlDef.h" #include "NetMemCo.h" #include "NetEnd.h" #include "PrivNetSer.h" #include #include #define IDIRECTPLAY2_OR_GREATER #include #define DPLAY_MAXBUF 250 static char gs_bL0PCWin95DPlayInitState; #pragma pack(push) #pragma pack(1) typedef struct { DPID DPlayPlayerId; /* DPlay ID of the local player*/ NetLib_tduxPlayerId uxNetLibPlayerId; /* NetLib ID of the local player */ NetLib_tduwJoinType joinType; /* Create, Join or Watch. */ }NetLib_tdstInfoPlayer; static NetLib_tdstInfoPlayer gs_InfoLocal; /* Informations of the local player. */ static long SendBufSize; /* Virtual buffer size. */ static unsigned long LastTime; /* Time for the last eL0PCWin95DPlayEngine. */ static unsigned long SendMaxRate; /* Maximum rate. */ static unsigned short NbrPlayer; /* Number of player. */ static NetLib_tdstInfoPlayer *TabPlayer; /* Table of player. */ typedef struct { NetLib_tduxPlayerId uxNetLibPlayerId; DPID DPlayPlayerId; NetLib_tdstAddPlayerDesc *pstPlDesc; char *pcDPlayPlayerName; } tdstConvLine; typedef struct { unsigned short uwMaxSize; unsigned short uwSize; tdstConvLine *dastConv; } tdstConvTable; static tdstConvTable gs_stConvTable; typedef struct stFIFO { struct stFIFO *next; /* Next message of FIFO. */ unsigned long size; /* Size of message. */ DPID DPlayIdFrom; /* Direct Play ID of sender. */ NetLib_tduxPlayerId uxSenderId; /* NetLib ID of sender */ }FIFO; static FIFO *FIFObegin; /* First message of file. */ static FIFO **FIFOend; /* Last pointer of the file. */ static unsigned short gs_uwCallBack; static NetLib_tduwJoinType gs_JoinType; typedef struct { NetLib_uxReadReceiptId m_ulReadReceiptId; unsigned long uxPriority:7; unsigned long uxReplaceType:7; unsigned long uxReplace:1; unsigned long eMessageType:5; unsigned long uxHeadBigEndian:1; unsigned long uxBodyBigEndian:1; unsigned long uxDPlaySpecific:2; } DPlayHead; typedef enum { NetLib_ePureNetLib, NetLib_eNewPlayerId, NetLib_eIdTable } tdeDPlayMsgType; typedef struct { DPID DPlayPlayerId; NetLib_tduxPlayerId uxNetLibPlayerId; } tdstConversionMsg; enum { NetLib_DPlayWaiting, NetLib_DPlayGo }; #pragma pack(pop) static LPDIRECTPLAY3 glpDP; /* directplay object pointer*/ static LPDPLCONNECTION glpdplConnection; /* connection settings*/ static LPDIRECTPLAYLOBBY2 glpDPL; /* lobby object pointer*/ static DPID pidLocalPlayer; /* Id of local player*/ static NetLib_tduxPlayerId gs_uxPlayerIdGen; /* used to generate player ids */ BOOL FAR PASCAL NewPlayerCallback(DPID PlayerId, DWORD dwPlayerType, LPCDPNAME lpName, DWORD dwFlags, LPVOID lpContext); void NetLib_RegisterNewPlayer1(DPID PlayerId, char *lpName); void NetLib_RegisterNewPlayer2(DPID DPlayPlayerId, NetLib_tduxPlayerId uxNetLibPlayerId); void NetLib_InitConversionTable(unsigned short uwMaxSize); void NetLib_DeinitConversionTable(); void NetLib_AddConversionEntry(DPID DPlayPlayerId, NetLib_tdstAddPlayerDesc *pstPlDesc, char *); void NetLib_SuppressConversionEntry(DPID DPlayerId); DPlayHead *NetLib_BuildConversionMsg(DPID IdTo, unsigned short *); DPID NetLib_NetLibToDPlay(NetLib_tduxPlayerId uxNetLibPlayerId); NetLib_tduxPlayerId NetLib_DPlayToNetLib(DPID DPlayPlayerId); NetLib_tduxPlayerId GeneratePlayerId(void); void NetLib_DPlayHandleNewMsg(FIFO *pstFIFOCell, DPID IdTo); void NetLib_DPlayHandleNewPlayerMsg(FIFO *pstFIFOCell, DPID IdTo); void NetLib_DPlayHandlePlayerLeftMsg(FIFO *pstFIFOCell, DPID IdTo); void NetLib_DPlayHandleSpecificMsg(FIFO *pstFIFOCell, DPID IdTo); /******************************************************************************************/ /* * DPLobbyCreate * * Wrapper for DirectPlayLobby DirectPlayLobbyCreate API. */ HRESULT DPLobbyCreate(void) { HRESULT hr=E_FAIL; LPDIRECTPLAYLOBBY local_lpDPL; /* directplay object pointer*/ hr = DirectPlayLobbyCreate(NULL, &local_lpDPL, NULL, NULL, 0); if(hr != DP_OK) { #ifdef NET_USE_DEBUG vDebugFormat(Net_C_Debug_DPlay, "DPLobbyCreate : DPLCreate : error %lx", hr); #endif return hr; } hr = IDirectPlayLobby_QueryInterface(local_lpDPL, &IID_IDirectPlayLobby2A, (LPVOID *)&glpDPL); if(hr != DP_OK) { #ifdef NET_USE_DEBUG vDebugFormat(Net_C_Debug_DPlay, "DPLobbyCreate : DPLQueryInterface : error %lx", hr); #endif return hr; } hr = IDirectPlayLobby_Release(local_lpDPL); if(hr != DP_OK) { #ifdef NET_USE_DEBUG vDebugFormat(Net_C_Debug_DPlay, "DPLobbyCreate : DPLRelease : error %lx", hr); #endif } return hr; } /******************************************************************************************/ /* * DPClose * * Wrapper for DirectPlay Close API */ HRESULT DPClose(void) { HRESULT hr=E_FAIL; /* release the direct play object*/ if (glpDP) { hr = IDirectPlay3_Close(glpDP); if(hr != DP_OK) { #ifdef NET_USE_DEBUG vDebugFormat(Net_C_Debug_DPlay, "DPClose : error %lx", hr); #endif } } return hr; } /******************************************************************************************/ /* * DPRelease * * Wrapper for DirectPlay Release API */ HRESULT DPRelease(void) { HRESULT hr=E_FAIL; /* release the lobby object*/ if (glpDP) { hr = IDirectPlay3_Release(glpDP); if(hr != DP_OK) { #ifdef NET_USE_DEBUG vDebugFormat(Net_C_Debug_DPlay, "DPRelease : error %lx", hr); #endif } glpDP = NULL; } return hr; } /******************************************************************************************/ /* * DPLobbyRelease * * Wrapper for DirectPlayLobby Release API */ HRESULT DPLobbyRelease(void) { HRESULT hr=E_FAIL; /* free our connection settings*/ if (glpdplConnection) { vFree(glpdplConnection); glpdplConnection = NULL; } /* release the lobby object*/ if (glpDPL) { hr = IDirectPlayLobby_Release(glpDPL); if(hr != DP_OK) { #ifdef NET_USE_DEBUG vDebugFormat(Net_C_Debug_DPlay, "DPLobbyRelease : DPLRelease : error %lx", hr); #endif } glpDPL = NULL; } return hr; } /******************************************************************************************/ /* * DPLobbyGetConnectionSettings * * Wrapper for DirectPlayLobby GetConnectionSettings API */ HRESULT DPLobbyGetConnectionSettings(void) { HRESULT hr=E_FAIL; DWORD dwSize; if (glpDPL) { /* get size for the connection settings structure*/ hr = IDirectPlayLobby_GetConnectionSettings(glpDPL, 0, NULL, &dwSize); if (DPERR_BUFFERTOOSMALL == hr) { /* if we already have one, free it*/ if (glpdplConnection) { vFree(glpdplConnection); glpdplConnection = NULL; } /* allocate memory for the new one*/ glpdplConnection = (LPDPLCONNECTION) pMalloc(dwSize); /* get the connection settings*/ if (glpdplConnection) hr = IDirectPlayLobby_GetConnectionSettings(glpDPL, 0, glpdplConnection, &dwSize); if(hr != DP_OK) { #ifdef NET_USE_DEBUG vDebugFormat(Net_C_Debug_DPlay, "DPLobbyGetConnectionSettings : DPLGetConnectionSettings : error %lx", hr); #endif } } else if(hr != DP_OK) { #ifdef NET_USE_DEBUG vDebugFormat(Net_C_Debug_DPlay, "DPLobbyGetConnectionSettings : DPLGetConnectionSettings : error %lx", hr); #endif } } return hr; } /******************************************************************************************/ /* * DPLobbyConnect * * Wrapper for DirectPlayLobby Connect API. */ HRESULT DPLobbyConnect(void) { HRESULT hr=E_FAIL; static LPDIRECTPLAY2 local_lpDP; /* directplay object pointer*/ if(glpDPL) { /* connect and retrieve a DirectPlay2 interface */ hr = IDirectPlayLobby_Connect(glpDPL, 0, &local_lpDP, NULL) ; if(hr != DP_OK) { #ifdef NET_USE_DEBUG vDebugFormat(Net_C_Debug_DPlay, "DPLobbyConnect : DPLConnect : error %lx", hr); #endif } hr = IDirectPlayLobby_Release(glpDPL); glpDPL=NULL; if(hr != DP_OK) { #ifdef NET_USE_DEBUG vDebugFormat(Net_C_Debug_DPlay, "DPLConnect : DPLRelease : error %lx", hr); #endif } /* retrieve a DirectPlay3 interface from the DirectPlay2 interface */ if(local_lpDP) { hr = IDirectPlay2_QueryInterface(local_lpDP, &IID_IDirectPlay3A, (LPVOID *)&glpDP); if(hr != DP_OK) { #ifdef NET_USE_DEBUG vDebugFormat(Net_C_Debug_DPlay, "DPLConnect : DP2QueryInterface : error %lx", hr); #endif } hr = IDirectPlay2_Release(local_lpDP); if(hr != DP_OK) { #ifdef NET_USE_DEBUG vDebugFormat(Net_C_Debug_DPlay, "DPLConnect : DP2Release: error %lx", hr); #endif } } } return hr; } /******************************************************************************************/ /* * DPCreatePlayer * wrapper for CreatePlayer */ HRESULT DPCreatePlayer(void) { HRESULT hr=E_FAIL; if(glpDP) { #ifdef NET_USE_DEBUG vDebugFormat(Net_C_Debug_DPlay, "DPCreatePlayer : ConnectionSettings"); vDebugFormat(Net_C_Debug_DPlay, "dwFlags: %lx", glpdplConnection->dwFlags); if(glpdplConnection->dwFlags==DPLCONNECTION_CREATESESSION) vDebugFormat(Net_C_Debug_DPlay, "DPLCONNECTION_CREATESESSION"); if(glpdplConnection->dwFlags==DPLCONNECTION_JOINSESSION) vDebugFormat(Net_C_Debug_DPlay, "DPLCONNECTION_JOINSESSION"); vDebugFormat(Net_C_Debug_DPlay, "PlayerName: %s", glpdplConnection->lpPlayerName->lpszShortNameA); #endif hr = IDirectPlay3_CreatePlayer(glpDP, &pidLocalPlayer, glpdplConnection->lpPlayerName, NULL, NULL, 0, 0); if(hr != DP_OK) { #ifdef NET_USE_DEBUG vDebugFormat(Net_C_Debug_DPlay, "DPCreatePlayer : error %lu", hr); #endif } if(glpdplConnection->dwFlags==DPLCONNECTION_CREATESESSION) gs_JoinType=NetLib_Join_Create; else if(glpdplConnection->dwFlags==DPLCONNECTION_JOINSESSION) gs_JoinType=NetLib_Join_Join; } return hr; } /******************************************************************************************/ /* * DPEnumPlayers * wrapper for DPEnumPlayers */ HRESULT DPEnumPlayers(void) { HRESULT hr=E_FAIL; if(glpDP) { hr = IDirectPlay3_EnumPlayers(glpDP, NULL, (LPDPENUMPLAYERSCALLBACK2)NewPlayerCallback, 0, 0); if(hr != DP_OK) { #ifdef NET_USE_DEBUG vDebugFormat(Net_C_Debug_DPlay, "DPEnumPlayers : DP3EnumPLayers : error %lx", hr); #endif } } return hr; } /******************************************************************************************/ BOOL FAR PASCAL NewPlayerCallback(DPID PlayerId, DWORD dwPlayerType, LPCDPNAME lpName, DWORD dwFlags, LPVOID lpContext) { #if defined(NET_USE_DEBUG) vDebugFormat(Net_C_Debug_DPlay,"NewPlayerCallBack : %lx", PlayerId); #endif if (PlayerId == pidLocalPlayer) { #if defined(NET_USE_DEBUG) vDebugS(Net_C_Debug_DPlay,"Is me"); #endif gs_InfoLocal.DPlayPlayerId = PlayerId; gs_InfoLocal.joinType = gs_JoinType; gs_uwCallBack|=1; } NetLib_RegisterNewPlayer1(PlayerId, lpName->lpszShortNameA); /* if we are the session host, we can finish registering ourself */ if (gs_JoinType==NetLib_Join_Create) NetLib_RegisterNewPlayer2(PlayerId, GeneratePlayerId()); return TRUE; /* request next player */ } /******************************************************************************/ void NetLib_RegisterNewPlayer1(DPID PlayerId, char *lpName) { NetLib_tdstAddPlayerDesc *pstPlDesc; char *acDPlayPlayerName; /* register a new player: first part */ /* add an entry to the conversion table */ /* and wait for the NetLib Player Id */ #if defined(NET_USE_DEBUG) vDebugFormat(Net_C_Debug_DPlay,"Register New Player 1: %lx / %s", PlayerId, lpName); #endif pstPlDesc=(NetLib_tdstAddPlayerDesc *)pMalloc(sizeof(NetLib_tdstAddPlayerDesc)); pstPlDesc->m_tduxPlayerId=C_uxNetInvalidId; pstPlDesc->m_pPlayerDescriptionData=C_pNull; /* at first, let's not deal with player description */ pstPlDesc->m_uxPlayerDesciptionLength=0; pstPlDesc->m_stL1AddPlayerDesc.m_eProtocol=E_Net_pr_Windows95DPlayProtocol; pstPlDesc->m_stL1AddPlayerDesc.m_vL0Param=NULL; pstPlDesc->IsYou=PlayerId == pidLocalPlayer; pstPlDesc->JoinType=gs_JoinType; pstPlDesc->Options=NULL; acDPlayPlayerName=strdup(lpName); NetLib_AddConversionEntry(PlayerId, pstPlDesc, acDPlayPlayerName); } /******************************************************************************/ void NetLib_RegisterNewPlayer2(DPID DPlayPlayerId, NetLib_tduxPlayerId uxNetLibPlayerId) { /* register a new player: part 2 */ /* complete the conversion table entry */ /* and transmit the information to higher levels */ unsigned short i; #if defined(NET_USE_DEBUG) vDebugFormat(Net_C_Debug_DPlay,"Register New Player 2: %lx / %lu", DPlayPlayerId, uxNetLibPlayerId); #endif if(DPlayPlayerId==gs_InfoLocal.DPlayPlayerId) gs_InfoLocal.uxNetLibPlayerId = uxNetLibPlayerId; for(i=0; im_tduxPlayerId=uxNetLibPlayerId; NetLib_eAddNewPlayer(gs_stConvTable.dastConv[i].pstPlDesc); vFree(gs_stConvTable.dastConv[i].pstPlDesc); gs_stConvTable.dastConv[i].pstPlDesc=(NetLib_tdstAddPlayerDesc *)C_pNull; NbrPlayer++; } else { #if defined(NET_USE_DEBUG) vDebugFormat(Net_C_Debug_DPlay,"Register New Player 2: no description for %lx / (i=%hu) ???", DPlayPlayerId, i); #endif } } } /******************************************************************************/ void NetLib_InitConversionTable(unsigned short uwMaxSize) { gs_stConvTable.uwSize=0; gs_stConvTable.uwMaxSize=uwMaxSize; gs_stConvTable.dastConv=(tdstConvLine *)pMalloc(gs_stConvTable.uwMaxSize*sizeof(tdstConvLine)); } /******************************************************************************/ void NetLib_DeinitConversionTable() { unsigned short i; for(i=0; iuxNetLibPlayerId=C_uxNetInvalidId; pstConvLine->DPlayPlayerId=DPlayPlayerId; pstConvLine->pstPlDesc=pstPlDesc; pstConvLine->pcDPlayPlayerName=pcDPlayPlayerName; gs_stConvTable.uwSize++; } /******************************************************************************/ void NetLib_SuppressConversionEntry(DPID DPlayPlayerId) { unsigned short i; for(i=0; iuxDPlaySpecific=NetLib_eIdTable; ConversionInfo=(tdstConversionMsg *)(pstSpecificMsg+1); for(i=0; iDPlayPlayerId=gs_stConvTable.dastConv[i].DPlayPlayerId; ConversionInfo->uxNetLibPlayerId=gs_stConvTable.dastConv[i].uxNetLibPlayerId; ConversionInfo++; } } return pstSpecificMsg; } /******************************************************************************/ DPID NetLib_NetLibToDPlay(NetLib_tduxPlayerId uxNetLibPlayerId) { unsigned short i; for(i=0; ilpSessionDesc->dwMaxPlayers) return NetLib_DPlayWaiting; else return NetLib_DPlayGo; } /*****************************************************************************/ /***************************************************************************** * Description: vL0PCWin95DPlayCloseChannel * frees a slot in the channel table ***************************************************************************** * Input: identifier of the channel * Output: none *****************************************************************************/ void vL0PCWin95DPlayCloseChannel(tduwNetChannel uwChannel) { } /***************************************************************************** * Description: uwL0PCWin95DPlayStartChannelScan * returns the identification number of the first channel for the protocol ***************************************************************************** * Input: none * Output: 0 *****************************************************************************/ tduwNetChannel uwL0PCWin95DPlayStartChannelScan(void) { if(!(gs_bL0PCWin95DPlayInitState&0x02)) return C_uwNetInvalidChannel; return 0; } /***************************************************************************** * Description: uwL0PCWin95DPlayNextChannel * returns the identification number of the channel following the last ***************************************************************************** * Input: uwLastScannedChannel, index returned by the previous call * Output: C_uwNetInvalidChannel *****************************************************************************/ tduwNetChannel uwL0PCWin95DPlayNextChannel(tduwNetChannel uwLastScannedChannel) { return C_uwNetInvalidChannel; } /***************************************************************************** * Description: uwL0PCWin95DPlayStartBroadcastChannelScan * returns the identification number of the first broadcast channel * for the protocol ***************************************************************************** * Input: none * Output: 0 *****************************************************************************/ tduwNetChannel uwL0PCWin95DPlayStartBroadcastChannelScan(void) { if(!(gs_bL0PCWin95DPlayInitState&0x02)) return C_uwNetInvalidChannel; return 1; } /***************************************************************************** * Description: uwL0PCWin95DPlayNextBroadcastChannel * returns the identification number of the broadcast channel following * the specified one ***************************************************************************** * Input: uwLastScannedChannel, index returned by the previous call * Output: identification of the channel following the specified one *****************************************************************************/ tduwNetChannel uwL0PCWin95DPlayNextBroadcastChannel(tduwNetChannel uwLastScannedChannel) { return C_uwNetInvalidChannel; } /***************************************************************************** * Description: wL0PCWin95DPlayGetMessage ***************************************************************************** * Input: ppData, address of the pointer to retrieve a full message if * necessary. * Output: return 1 if getting a message and 0 otherwise. *****************************************************************************/ short wL0PCWin95DPlayGetMessage(tdstNetMessage **ppMes) { FIFO *currentFIFOCell; tdstNetMessage *pstMessage; DPlayHead *pstDPlayHead; if (FIFObegin==NULL) { *ppMes=NULL; return 0; } else { /* avant tout, tester si l'expediteur est connu pour NetLib */ /* (et si on connait son propre id NetLib en local) */ pstMessage=(tdstNetMessage *)pMalloc(FIFObegin->size+sizeof(tdstNetMessage)-sizeof(DPlayHead)); pstDPlayHead=(DPlayHead *)(FIFObegin+1); if (pstMessage==NULL) { *ppMes=NULL; return -1; } pstMessage->m_uxReadReceiptId=pstDPlayHead->m_ulReadReceiptId; pstMessage->uxPriority=pstDPlayHead->uxPriority; pstMessage->uxReplace=pstDPlayHead->uxReplace; pstMessage->uxReplaceType=pstDPlayHead->uxReplaceType; pstMessage->eMessageType=pstDPlayHead->eMessageType; pstMessage->uxHeadBigEndian=pstDPlayHead->uxHeadBigEndian; pstMessage->uxBodyBigEndian=pstDPlayHead->uxBodyBigEndian; pstMessage->uwMessageSizeInBytes=(unsigned short)(FIFObegin->size-sizeof(DPlayHead)); pstMessage->uxSessionId=0; pstMessage->uxSenderId=FIFObegin->uxSenderId; pstMessage->uxRecipientId=gs_InfoLocal.uxNetLibPlayerId; memcpy(pstMessage+1, pstDPlayHead+1, pstMessage->uwMessageSizeInBytes); pstMessage->ulReserved=M_ulMakeReserved(M_uwProcessCheckSum(pstMessage), M_uwProcessCheckSumBody(pstMessage)); currentFIFOCell=FIFObegin->next; vFree((char *)FIFObegin); FIFObegin=currentFIFOCell; if (currentFIFOCell==NULL) FIFOend=&FIFObegin; *ppMes=pstMessage; #ifdef NET_USE_DEBUG vDebugFormat(Net_C_Debug_DPlay, "GetMessage(): Type %d, Size %d, Recipient %d", pstMessage->eMessageType, pstMessage->uwMessageSizeInBytes, pstMessage->uxRecipientId); #endif /* NET_USE_DEBUG */ return 1; } } /***************************************************************************** * Description: eL0PCWin95DPlayReadData ***************************************************************************** * Input: p_uwChannel, pointer on a field to store the identification of * a new channel in case of access to a newly created broacast channel * (irrelevant for the serial layer) * ppData, address of the pointer to retrieve a full message if * necessary. * Output: an error condition. *****************************************************************************/ NetLib_tdeErrorStatus eL0PCWin95DPlayReadData(tduwNetChannel *p_uwChannel, tdpPointer *ppData) { short r; /* verify the validity of the Channel */ if(*p_uwChannel>1) return NetLib_E_es_InvalidChannel; /* no : try to get a message from the FIFO */ r=wL0PCWin95DPlayGetMessage((tdstNetMessage **)ppData); if (r>0) return NetLib_E_es_NoError; else if (r==0) return NetLib_E_es_FIFOIsEmpty; else return NetLib_E_es_NotEnoughMemory; } /***************************************************************************** * Description: eL0PCWin95DPlaySendData ***************************************************************************** * Input: uwChannel, channel to send the message into. * pData, pointer on the block to send * Output: an error condition. *****************************************************************************/ NetLib_tdeErrorStatus eL0PCWin95DPlaySendData(tduwNetChannel uwChannel, tdpPointer pData) { unsigned long ulBytestoSend; tdstNetMessage *pMes=(tdstNetMessage *)pData; DPlayHead *pSend; unsigned long NewTime; DPID DPlayDestinationId; DWORD dwFlags; HRESULT hr; /* verify that the system has been initialized */ if(!(gs_bL0PCWin95DPlayInitState&0x02)) return NetLib_E_es_ProtocolNotInitialized; /* verify the validity of the Channel */ if(uwChannel>1) { #if defined(NET_USE_DEBUG) vDebugFormat(Net_C_Debug_DPlay,"Invalid channel %ud", uwChannel); #endif return NetLib_E_es_InvalidChannel; } if (pMes->uxRecipientId==C_uxNetBroadcastId) uwChannel=1; ulBytestoSend= pMes->uwMessageSizeInBytes + sizeof(DPlayHead); NewTime=GetTickCount(); SendBufSize-=((NewTime-LastTime)*(float)SendMaxRate)/1000; LastTime=NewTime; if (SendBufSize<0) SendBufSize=0; if (SendBufSize>=DPLAY_MAXBUF) { #if defined(NET_USE_DEBUG) vDebugS(Net_C_Debug_DPlay,"limiting throughput"); #endif return NetLib_E_es_BufferIsFull; } SendBufSize+=ulBytestoSend+20; pSend=(DPlayHead *)pMalloc(ulBytestoSend); if (!pSend) { #if defined(NET_USE_DEBUG) vDebugS(Net_C_Debug_DPlay,"out of memory"); #endif return NetLib_E_es_NotEnoughMemory; } memcpy(pSend+1,pMes+1,pMes->uwMessageSizeInBytes); pSend->m_ulReadReceiptId=pMes->m_uxReadReceiptId; pSend->uxPriority=pMes->uxPriority; pSend->uxReplace=pMes->uxReplace; pSend->uxReplaceType=pMes->uxReplaceType; pSend->eMessageType=pMes->eMessageType; pSend->uxHeadBigEndian=pMes->uxHeadBigEndian; pSend->uxBodyBigEndian=pMes->uxBodyBigEndian; pSend->uxDPlaySpecific=NetLib_ePureNetLib; if (uwChannel!=1) { DPlayDestinationId=NetLib_NetLibToDPlay(pMes->uxRecipientId); #if defined(NET_USE_DEBUG) vDebugFormat(Net_C_Debug_DPlay,"Send to one player. Type %d, Size %d, Recipient %d / %lx", pMes->eMessageType, pMes->uwMessageSizeInBytes, pMes->uxRecipientId, DPlayDestinationId); #endif dwFlags=DPSEND_GUARANTEED | DPSEND_OPENSTREAM; } else { #if defined(NET_USE_DEBUG) vDebugFormat(Net_C_Debug_DPlay,"Send to all players. Type %d, Size %d, Recipient %d", pMes->eMessageType, pMes->uwMessageSizeInBytes, pMes->uxRecipientId); #endif DPlayDestinationId=DPID_ALLPLAYERS; dwFlags=DPSEND_GUARANTEED; } hr=E_FAIL; if(glpDP) { hr=IDirectPlay3_Send( glpDP, gs_InfoLocal.DPlayPlayerId, DPlayDestinationId, dwFlags, (LPVOID)pSend, ulBytestoSend); if(hr != DP_OK) { #ifdef NET_USE_DEBUG vDebugFormat(Net_C_Debug_DPlay, "SendData : DP3Send : error %lx", hr); #endif } } vFree((char *)pSend); if (hr==DPERR_BUSY) { #if defined(NET_USE_DEBUG) vDebugFormat(Net_C_Debug_DPlay,"emission failure: send queue is full"); #endif return NetLib_E_es_BufferIsFull; } else if(hr<0) { #if defined(NET_USE_DEBUG) vDebugFormat(Net_C_Debug_DPlay,"emission failure: %lx", hr); #endif return NetLib_E_es_UnknownError; } /* free the message buffer */ vFree(pData); return NetLib_E_es_NoError; } /***************************************************************************** * Description: eL0PCWin95DPlayQueryChannelStatus * returns the current status of the channel ***************************************************************************** * Input: uwChannel, channel to query * Output: an error condition. *****************************************************************************/ NetLib_tdeErrorStatus eL0PCWin95DPlayQueryChannelStatus(tduwNetChannel uwChannel) { return NetLib_E_es_NoError; } /***************************************************************************** * Description: eL0PCWin95DPlayEngine ***************************************************************************** * Input: none * Output: none *****************************************************************************/ void eL0PCWin95DPlayEngine(void) { DPID IdFrom, IdTo; unsigned long ulBytesToRead; FIFO *pstFIFOCell; HRESULT hr; if(!(gs_bL0PCWin95DPlayInitState&0x02)) return; if(!glpDP) return; do { hr=IDirectPlay3_Receive(glpDP, &IdFrom, &IdTo, DPRECEIVE_ALL | DPRECEIVE_PEEK, NULL, &ulBytesToRead); if(hr==DPERR_BUFFERTOOSMALL) { pstFIFOCell=(FIFO *)pMalloc(ulBytesToRead+sizeof(FIFO)); if(pstFIFOCell==(FIFO *)C_pNull) return; hr=IDirectPlay3_Receive(glpDP, &IdFrom, &IdTo, DPRECEIVE_ALL, pstFIFOCell+1, &ulBytesToRead); pstFIFOCell->size=ulBytesToRead; pstFIFOCell->DPlayIdFrom=IdFrom; pstFIFOCell->next=(FIFO *)C_pNull; NetLib_DPlayHandleNewMsg(pstFIFOCell, IdTo); } else if(hr != DPERR_NOMESSAGES) { #ifdef NET_USE_DEBUG vDebugFormat(Net_C_Debug_DPlay,"reception failure: %lx", hr); #endif /* NET_USE_DEBUG */ } } while(hr==DP_OK); /* introduire une limite sur le nombre de messages lus par appel */ } /*****************************************************************************/ void NetLib_DPlayHandleNewMsg(FIFO *pstFIFOCell, DPID IdTo) { DPlayHead *pstDPlayMsg; #if defined(NET_USE_DEBUG) vDebugFormat(Net_C_Debug_DPlay,"HandleNewMsg()"); #endif /* first, handle Direct Play system messages */ if(pstFIFOCell->DPlayIdFrom==DPID_SYSMSG) { switch(((LPDPMSG_GENERIC)(pstFIFOCell+1))->dwType) { case DPSYS_CREATEPLAYERORGROUP: NetLib_DPlayHandleNewPlayerMsg(pstFIFOCell, IdTo); break; case DPSYS_DESTROYPLAYERORGROUP: NetLib_DPlayHandlePlayerLeftMsg(pstFIFOCell, IdTo); break; default: #if defined(NET_USE_DEBUG) vDebugFormat(Net_C_Debug_DPlay,"HandleNewMsg() : unhandled system message %lx", ((LPDPMSG_GENERIC)(pstDPlayMsg+1))->dwType); #endif ; } vFree(pstFIFOCell); } else { pstDPlayMsg=(DPlayHead *)(pstFIFOCell+1); if(pstDPlayMsg->uxDPlaySpecific != NetLib_ePureNetLib) { /* this is a message specific to the Direct Play subsystem: interpret it */ NetLib_DPlayHandleSpecificMsg(pstFIFOCell, IdTo); vFree(pstFIFOCell); } else { /* this is a normal NetLib message: */ /* translate the player Id and enqueue the message */ #if defined(NET_USE_DEBUG) vDebugFormat(Net_C_Debug_DPlay,"Received: Type %lu, Size %lu, Source %lx", pstDPlayMsg->eMessageType, pstFIFOCell->size-sizeof(DPlayHead), pstFIFOCell->DPlayIdFrom); #endif pstFIFOCell->uxSenderId=NetLib_DPlayToNetLib(pstFIFOCell->DPlayIdFrom); *FIFOend=pstFIFOCell; FIFOend=&(pstFIFOCell->next); } } } /*****************************************************************************/ void NetLib_DPlayHandleNewPlayerMsg(FIFO *pstFIFOCell, DPID IdTo) { HRESULT hr; LPDPMSG_CREATEPLAYERORGROUP pSysMsg; NetLib_tduxPlayerId uxNetLibPlayerId; tdstConversionMsg *ConversionInfo; DPlayHead *pstSpecificMsg; unsigned short uxSize; #if defined(NET_USE_DEBUG) vDebugFormat(Net_C_Debug_DPlay,"HandleNewPlayerMsg()"); #endif pSysMsg = (LPDPMSG_CREATEPLAYERORGROUP) (pstFIFOCell+1); /* if we host the session, then we have to generate a NetLib identifier for this player */ /* send this identifier to all other players */ /* and then send the list of all identifiers to this new player */ /* if we don't host the session, we remember that a new play has come */ /* and wait for a message from the host to know its NetLib identifier */ NetLib_RegisterNewPlayer1(pSysMsg->dpId, pSysMsg->dpnName.lpszShortNameA); if(gs_InfoLocal.joinType==NetLib_Join_Create) { /* we host the session: generate a new identifier for this player */ uxNetLibPlayerId=GeneratePlayerId(); NetLib_RegisterNewPlayer2(pSysMsg->dpId, uxNetLibPlayerId); /* send the new NetLib id to everyone */ pstSpecificMsg=(DPlayHead *)pMalloc(sizeof(DPlayHead)+sizeof(tdstConversionMsg)); if(pstSpecificMsg) { pstSpecificMsg->uxDPlaySpecific=NetLib_eNewPlayerId; ConversionInfo=(tdstConversionMsg *)(pstSpecificMsg+1); ConversionInfo->DPlayPlayerId=pSysMsg->dpId; ConversionInfo->uxNetLibPlayerId=uxNetLibPlayerId; hr=IDirectPlay3_Send( glpDP, gs_InfoLocal.DPlayPlayerId, DPID_ALLPLAYERS, DPSEND_GUARANTEED, (LPVOID)pstSpecificMsg, sizeof(DPlayHead)+sizeof(tdstConversionMsg)); if(hr != DP_OK) { #ifdef NET_USE_DEBUG vDebugFormat(Net_C_Debug_DPlay, "HandleNewPlayerMsg : DP3Send : error %lx", hr); #endif } } /* send all NetLib ids to the new player */ pstSpecificMsg=NetLib_BuildConversionMsg(pSysMsg->dpId, &uxSize); if(pstSpecificMsg) { hr=IDirectPlay3_Send( glpDP, gs_InfoLocal.DPlayPlayerId, pSysMsg->dpId, DPSEND_GUARANTEED, (LPVOID)pstSpecificMsg, uxSize); if(hr != DP_OK) { #ifdef NET_USE_DEBUG vDebugFormat(Net_C_Debug_DPlay, "HandleNewPlayerMsg : DP3Send : error %lx", hr); #endif } } } } /*****************************************************************************/ void NetLib_DPlayHandlePlayerLeftMsg(FIFO *pstFIFOCell, DPID IdTo) { LPDPMSG_DELETEPLAYERFROMGROUP pSysMsg; pSysMsg=(LPDPMSG_DELETEPLAYERFROMGROUP)(pstFIFOCell+1); #if defined(NET_USE_DEBUG) vDebugFormat(Net_C_Debug_DPlay,"HandlePlayerLeftMsg(): Player %lx", pSysMsg->dpIdPlayer); #endif NetLib_SuppressConversionEntry(pSysMsg->dpIdPlayer); NbrPlayer--; /* in normal conditions (i.e. the remote player made a call to DisconnectFromSession() */ /* the higher levels already have suppressed this player upon reception of the */ /* disconnection message */ } /*****************************************************************************/ void NetLib_DPlayHandleSpecificMsg(FIFO *pstFIFOCell, DPID IdTo) { DPlayHead *pstMsg; tdstConversionMsg *pstConversionMsg; unsigned int i, uwNbrOfLines; #if defined(NET_USE_DEBUG) vDebugFormat(Net_C_Debug_DPlay,"HandleSpecificMsg()"); #endif pstMsg=(DPlayHead *)(pstFIFOCell+1); switch(pstMsg->uxDPlaySpecific) { case NetLib_eNewPlayerId: pstConversionMsg=(tdstConversionMsg *)(pstMsg+1); NetLib_RegisterNewPlayer2(pstConversionMsg->DPlayPlayerId, pstConversionMsg->uxNetLibPlayerId); break; /* the message contains the NetLib ID of a new player */ case NetLib_eIdTable: pstConversionMsg=(tdstConversionMsg *)(pstMsg+1); uwNbrOfLines=(pstFIFOCell->size - sizeof(DPlayHead)) / sizeof(tdstConversionMsg); for(i=0; inext; vFree((char*)FIFObegin); FIFObegin=p; } FIFObegin=NULL; FIFOend=&FIFObegin; vLevel1RemoveProtocol(E_Net_pr_Windows95DPlayProtocol); } /***************************************************************************** * Description: vL0PCWin95DPlayAddNewPlayer ***************************************************************************** * Input: pChanel : adress of returned chanel. * par : extern parameter. * Output: error code. *****************************************************************************/ NetLib_tdeErrorStatus vL0PCWin95DPlayAddNewPlayer(tduwNetChannel *pChannel,void *par) { *pChannel=0; return NetLib_E_es_NoError; } /***************************************************************************** * Description: vL0PCWin95DPlayOpenProtocol * initialize the specified interface structure with the correct function * pointers to enable a protocol-independent data transmission process. ***************************************************************************** * Input: p_stProtocolInterface, pointer to the interface structure * uwPortNumber, port number to bind the socket to * Output: none *****************************************************************************/ void _NET_CALLING_CONV_ vL0PCWin95DPlayOpenProtocol(void) { tdstNetProtocolInterface *p_stProtocolInterface; #if defined(NET_USE_DEBUG) vDebugClear(Net_C_Debug_DPlay); vDebugS(Net_C_Debug_DPlay,"SetupInterface"); #endif p_stProtocolInterface=pstLevel1AddProtocol(); FIFObegin=NULL; FIFOend=&FIFObegin; /* logical identification of the protocol */ p_stProtocolInterface->eProtocol = E_Net_pr_Windows95DPlayProtocol; /* setup the function pointers */ p_stProtocolInterface->fn_uwStartChannelScan = uwL0PCWin95DPlayStartChannelScan; p_stProtocolInterface->fn_uwStartBroadcastChannelScan= uwL0PCWin95DPlayStartBroadcastChannelScan; p_stProtocolInterface->fn_uwNextChannel= uwL0PCWin95DPlayNextChannel; p_stProtocolInterface->fn_uwNextBroadcastChannel= uwL0PCWin95DPlayNextBroadcastChannel; p_stProtocolInterface->fn_eReadData= eL0PCWin95DPlayReadData; p_stProtocolInterface->fn_eSendData= eL0PCWin95DPlaySendData; p_stProtocolInterface->fn_eQueryChannelStatus= eL0PCWin95DPlayQueryChannelStatus; p_stProtocolInterface->fn_vLevel0NetEngine = eL0PCWin95DPlayEngine; p_stProtocolInterface->fn_vCloseChannel = vL0PCWin95DPlayCloseChannel; p_stProtocolInterface->fn_vLevel0CloseProtocol = vL0PCWin95DPlayCloseProtocol; p_stProtocolInterface->fn_eAddNewPlayer = vL0PCWin95DPlayAddNewPlayer; gs_bL0PCWin95DPlayInitState = 0x01; gs_uxPlayerIdGen=0; NetLib_InitConversionTable(8); } /***************************************************************************** * Description: eL0PCWin95DPlayStart * Start a game with DPlay ***************************************************************************** * Input: ppGameOptions : Adresse of a pointer wich receive the adresse of * game options. * Output: join type. *****************************************************************************/ _NET_EXPORT_ NetLib_tduwJoinType _NET_CALLING_CONV_ eL0PCWin95DPlayStart(char cMode) { int Status; #if defined(NET_USE_DEBUG) vDebugS(Net_C_Debug_DPlay,"Start"); #endif SendMaxRate=1000L; NbrPlayer=0; SendBufSize=0; TabPlayer=NULL; LastTime=GetTickCount(); FIFObegin=NULL; FIFOend=&FIFObegin; /* obtain a directplay lobby interface*/ if(DPLobbyCreate() != DP_OK) return -1; #ifdef NET_USE_DEBUG vDebugFormat(Net_C_Debug_DPlay, "DPLobbyCreate: OK"); #endif /* retrieve the session information */ if(DPLobbyGetConnectionSettings() != DP_OK) return -1; #ifdef NET_USE_DEBUG vDebugFormat(Net_C_Debug_DPlay, "DPLobbyGetConnectionSettings: OK"); #endif #ifdef NET_USE_DEBUG vDebugFormat(Net_C_Debug_DPlay, "session name : %s", glpdplConnection->lpSessionDesc->lpszSessionNameA); vDebugFormat(Net_C_Debug_DPlay, "max # players : %ld", glpdplConnection->lpSessionDesc->dwMaxPlayers); vDebugFormat(Net_C_Debug_DPlay, "current # players : %ld", glpdplConnection->lpSessionDesc->dwCurrentPlayers); #endif /* connect to the session */ if(DPLobbyConnect() != DP_OK) return -1; #ifdef NET_USE_DEBUG vDebugFormat(Net_C_Debug_DPlay, "DPLobbyConnect: OK"); #endif /* create the local player */ if(DPCreatePlayer() != DP_OK) return -1; #ifdef NET_USE_DEBUG vDebugFormat(Net_C_Debug_DPlay, "DPCreatePlayer: OK"); #endif /* wait for other players ? or not ? */ if(DPEnumPlayers() != DP_OK) return -1; #ifdef NET_USE_DEBUG vDebugFormat(Net_C_Debug_DPlay, "DPEnumPlayers: OK"); #endif if(cMode==NetLib_DPlayClosed) /* wait for all players to arrive*/ do { eL0PCWin95DPlayEngine(); Status=CheckPlayerNbr(); } while(Status==NetLib_DPlayWaiting); gs_bL0PCWin95DPlayInitState|=0x02; #if defined(NET_USE_DEBUG) vDebugS(Net_C_Debug_DPlay,"Start: returning"); #endif return gs_JoinType; } /***************************************************************************** * Description: eL0PCWin95DPlayFinish * Finish a game with DPlay. ***************************************************************************** * Input: none * Output: none *****************************************************************************/ _NET_EXPORT_ void _NET_CALLING_CONV_ eL0PCWin95DPlayFinish(void) { FIFO *p; #if defined(NET_USE_DEBUG) vDebugS(Net_C_Debug_DPlay,"Finish"); #endif vFree((char *)TabPlayer); while (FIFObegin) { p=FIFObegin->next; vFree((char*)FIFObegin); FIFObegin=p; } FIFObegin=NULL; FIFOend=&FIFObegin; gs_bL0PCWin95DPlayInitState&=~0x02; NetLib_eFlushSendingList(500); NetLib_vDisconnectAllRemotePlayers(); DPClose(); DPRelease(); DPLobbyRelease(); } /*****************************************************************************/ NetLib_tdeErrorStatus eL0PCWin95DPlayGetPlayerName(NetLib_tduxPlayerId uxPlayerId, char *pcString, unsigned short uwSize) { unsigned short i; for(i=0; i