/* * universal multiplayer library level 0 implementation file * transport layer: Game Service UBI Soft PC-windows 95 */ /* * Author: David Fournier * * Creation Date: 2/1/97 * */ #include "warnings.h" #include /* * we need this one to fill the interface structure * with pointers to the correct functions */ #include "PrivL0GSUBI.h" #include "PrivL0TCP.h" #include "PrivNetDef.h" #include "L0GlDef.h" #include "NetMemCo.h" #include "NetSock.h" #include "NetEnd.h" #include "PrivNetSer.h" #include #include #include #define GSUBI_MAXBUF 250 typedef short tdPlayerId; /* Id for the game service. */ static char gs_bL0PCWin95GSUBIInitState; /* State of the level 0 game service module. */ #pragma pack(1) typedef struct { NetLib_tduxPlayerId pid; /* ID of the player. */ NetLib_tduwJoinType joinType; /* Create, Join or Watch. */ }NetLib_tdstInfoPlayer; static NetLib_tdstInfoPlayer gs_InfoLocal; /* Informations of the local player. */ static NetLib_tduxPlayerId gs_IdMaster; /* Master Id. */ typedef struct stFIFO { struct stFIFO *next; /* Next message of FIFO. */ unsigned long size; /* Size of message. */ NetLib_tduxPlayerId id; /* ID of sender. */ }FIFO; static FIFO *FIFObegin; /* First message of file. */ static FIFO **FIFOend; /* Last pointer of the file. */ static long SendBufSize; /* Virtual buffer size. */ static unsigned long LastTime; /* Time for the last eL0PCWin95TENEngine. */ static unsigned long SendMaxRate; /* Maximum rate. */ typedef struct { NetLib_uxReadReceiptId m_uxReadReceiptId; unsigned long uxPriority:7; unsigned long uxReplaceType:7; unsigned long uxReplace:1; unsigned long eMessageType:5; unsigned long uxHeadBigEndian:1; unsigned long uxBodyBigEndian:1; }GSUBIHead; #pragma pack() /***************************************************************************** * Description: vL0PCWin95GSUBICloseChannel * Not use for this protocol. ***************************************************************************** * Input: identifier of the channel * Output: none *****************************************************************************/ void vL0PCWin95GSUBICloseChannel(tduwNetChannel uwChannel) { } /***************************************************************************** * Description: uwL0PCWin95GSUBIStartChannelScan * returns the identification number of the first channel for the protocol ***************************************************************************** * Input: none * Output: 0 *****************************************************************************/ tduwNetChannel uwL0PCWin95GSUBIStartChannelScan(void) { if(!(gs_bL0PCWin95GSUBIInitState&0x02)) return C_uwNetInvalidChannel; return 0; } /***************************************************************************** * Description: uwL0PCWin95GSUBINextChannel * returns the identification number of the channel following the last * return always C_uwNetInvalidChannel because there is one channel. ***************************************************************************** * Input: uwLastScannedChannel, index returned by the previous call * Output: C_uwNetInvalidChannel *****************************************************************************/ tduwNetChannel uwL0PCWin95GSUBINextChannel(tduwNetChannel uwLastScannedChannel) { return C_uwNetInvalidChannel; } /***************************************************************************** * Description: uwL0PCWin95GSUBIStartBroadcastChannelScan * returns the identification number of the first broadcast channel * for the protocol ***************************************************************************** * Input: none * Output: 0 *****************************************************************************/ tduwNetChannel uwL0PCWin95GSUBIStartBroadcastChannelScan(void) { if(!(gs_bL0PCWin95GSUBIInitState&0x02)) return C_uwNetInvalidChannel; return 1; } /***************************************************************************** * Description: uwL0PCWin95GSUBINextBroadcastChannel * returns the identification number of the broadcast channel following * the specified one * return always C_uwNetInvalidChannel because there is one channel broadcast. ***************************************************************************** * Input: uwLastScannedChannel, index returned by the previous call * Output: identification of the channel following the specified one *****************************************************************************/ tduwNetChannel uwL0PCWin95GSUBINextBroadcastChannel(tduwNetChannel uwLastScannedChannel) { return C_uwNetInvalidChannel; } /***************************************************************************** * Description: L0PCWin95GSUBIReadCallBack * This function is call by the game service API when a message arrived. * The message is store in the incoming message list until the level one read it. ***************************************************************************** * Input: fromPid, id of the sender. * buf, adress of the buffer message. * size, size of the message. * Output: none. *****************************************************************************/ static void GS_FUNCTIONCALL L0PCWin95GSUBIReadCallBack(tdPlayerId fromPid, char *buf, short size) { FIFO *cour; #if defined(NET_USE_DEBUG) GSUBIHead *pSend=(GSUBIHead *)buf; vDebugSISISI(Net_C_Debug_GSUbi,"Receive Type",pSend->eMessageType,"Size",size-sizeof(GSUBIHead),"Source",fromPid); #endif cour=(FIFO *)pMalloc(sizeof(FIFO)+size); if (cour) { *FIFOend=cour; cour->next=NULL; cour->size=size; cour->id=fromPid; memcpy(cour+1,buf,size); FIFOend=&(cour->next); } } /***************************************************************************** * Description: wL0PCWin95GSUBIGetMessage * Get a message if it's possible. *****************************************************************************/ short wL0PCWin95GSUBIGetMessage(tdstNetMessage **ppMes) { FIFO *cour; tdstNetMessage *pMes; GSUBIHead *pSend; if (FIFObegin==NULL) { *ppMes=NULL; return 0; } else { pMes=(tdstNetMessage *)pMalloc(FIFObegin->size+sizeof(tdstNetMessage)-sizeof(GSUBIHead)); if (pMes==NULL) { *ppMes=NULL; return -1; } pSend=(GSUBIHead *)(FIFObegin+1); memcpy(pMes+1,pSend+1,FIFObegin->size-sizeof(GSUBIHead)); pMes->m_uxReadReceiptId=pSend->m_uxReadReceiptId; pMes->uxPriority=pSend->uxPriority; pMes->uxReplace=pSend->uxReplace; pMes->uxReplaceType=pSend->uxReplaceType; pMes->eMessageType=pSend->eMessageType; pMes->uxHeadBigEndian=pSend->uxHeadBigEndian; pMes->uxBodyBigEndian=pSend->uxBodyBigEndian; pMes->uxSessionId=0; pMes->uxSenderId=FIFObegin->id; pMes->uxRecipientId=gs_InfoLocal.pid; pMes->uwMessageSizeInBytes=(unsigned short)(FIFObegin->size-sizeof(GSUBIHead)); pMes->ulReserved=M_ulMakeReserved(M_uwProcessCheckSum(pMes), M_uwProcessCheckSumBody(pMes)); cour=FIFObegin->next; vFree((char *)FIFObegin); FIFObegin=cour; if (cour==NULL) FIFOend=&FIFObegin; *ppMes=pMes; return 1; } } /***************************************************************************** * Description: eL0PCWin95GSUBIReadData ***************************************************************************** * 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 eL0PCWin95GSUBIReadData(tduwNetChannel *p_uwChannel, tdpPointer *ppData) { short r; /* no : try to get a message from the FIFO */ r=wL0PCWin95GSUBIGetMessage((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: eL0PCWin95GSUBISendData ***************************************************************************** * Input: uwChannel, channel to send the message into. * pData, pointer on the block to send * Output: an error condition. *****************************************************************************/ NetLib_tdeErrorStatus eL0PCWin95GSUBISendData(tduwNetChannel uwChannel, tdpPointer pData) { unsigned long ulBytestoSend; tdstNetMessage *pMes=(tdstNetMessage *)pData; int NumErr=0; short RepFlag; GSUBIHead *pSend; unsigned long NewTime; NetLib_tduxPlayerId uxRecipient; if(!(gs_bL0PCWin95GSUBIInitState&0x02)) return NetLib_E_es_InvalidChannel; uxRecipient=pMes->uxRecipientId; /* verify the validity of the Channel */ if(uwChannel>1) { uxRecipient=uwChannel-2; uwChannel=0; } else if (uxRecipient==C_uxNetBroadcastId) uwChannel=1; ulBytestoSend= pMes->uwMessageSizeInBytes + sizeof(GSUBIHead); NewTime=GetTickCount(); SendBufSize-=((NewTime-LastTime)*(float)SendMaxRate)/1000; LastTime=NewTime; if (SendBufSize<0) SendBufSize=0; if (SendBufSize>=GSUBI_MAXBUF) { #if defined(NET_USE_DEBUG) vDebugS(Net_C_Debug_GSUbi,"Controle de flot."); #endif return NetLib_E_es_BufferIsFull; } SendBufSize+=ulBytestoSend; pSend=(GSUBIHead *)pMalloc(ulBytestoSend); if (!pSend) { #if defined(NET_USE_DEBUG) vDebugS(Net_C_Debug_GSUbi,"Manque de mémoire."); #endif return NetLib_E_es_NotEnoughMemory; } pSend->m_uxReadReceiptId=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; memcpy(pSend+1,pMes+1,pMes->uwMessageSizeInBytes); if (pMes->uxReplace) RepFlag=0x80 | pMes->uxPriority; else RepFlag=0; if (uwChannel!=1) { #if defined(NET_USE_DEBUG) vDebugSISISI(Net_C_Debug_GSUbi,"Send to one player. Type",pMes->eMessageType,"Size",pMes->uwMessageSizeInBytes,"Recipient",uxRecipient); #endif NumErr=GS_vSendToOnePlayer((short)(uxRecipient),(char *)pSend,(short)ulBytestoSend,RepFlag); } else { #if defined(NET_USE_DEBUG) vDebugSISISI(Net_C_Debug_GSUbi,"Send to all players. Type",pMes->eMessageType,"Size",pMes->uwMessageSizeInBytes,"Recipient",uxRecipient); #endif NumErr=GS_vSendToAllPlayers((char *)pSend,(short)ulBytestoSend,0,RepFlag); } vFree((char *)pSend); if (NumErr) { #if defined(NET_USE_DEBUG) vDebugSI(Net_C_Debug_GSUbi,"Emission en attente",NumErr); #endif return NetLib_E_es_BufferIsFull; } /* free the message buffer */ vFree(pData); return NetLib_E_es_NoError; } /***************************************************************************** * Description: eL0PCWin95GSUBIQueryChannelStatus * returns the current status of the channel ***************************************************************************** * Input: uwChannel, channel to query * Output: an error condition. *****************************************************************************/ NetLib_tdeErrorStatus eL0PCWin95GSUBIQueryChannelStatus(tduwNetChannel uwChannel) { return NetLib_E_es_NoError; } /***************************************************************************** * Description: eL0PCWin95GSUBIEngine ***************************************************************************** * Input: none * Output: none *****************************************************************************/ void eL0PCWin95GSUBIEngine(void) { char szBuffer[128]; if(gs_bL0PCWin95GSUBIInitState&0x02) GS_lEngine(szBuffer); } /***************************************************************************** * Description : vL0PCWin95GSUBICloseProtocol(void) * GSUBI-Level 0 closing function ***************************************************************************** * Input : none * Output : none *****************************************************************************/ void vL0PCWin95GSUBICloseProtocol(void) { FIFO *p; while (FIFObegin) { p=FIFObegin->next; vFree((char*)FIFObegin); FIFObegin=p; } FIFObegin=NULL; FIFOend=&FIFObegin; vLevel1RemoveProtocol(E_Net_pr_Windows95GSUBIProtocol); } /***************************************************************************** * Description: L0PCWin95GSUBINewPlayerCallBack * This function is call by the game service API when a new player join the session. ***************************************************************************** * Input: pid = id of the player. * name = name of the player for the game service. * options = options player for the game service. * size = size of the options. * isYou = true if the player is the local player. * isMaster = true if the player is the master. * Output: none *****************************************************************************/ void GS_FUNCTIONCALL L0PCWin95GSUBINewPlayerCallBack(tdPlayerId pid,char *name,char *options,short size,int isYou,int isMaster) { NetLib_tdstAddPlayerDesc PlDesc; short j=NetLib_Join_Join; char nameopt[500]; #if defined(NET_USE_DEBUG) vDebugSIS(Net_C_Debug_GSUbi,"NewPlayerCallBack Pid",pid,name); #endif if (isYou) { #if defined(NET_USE_DEBUG) vDebugS(Net_C_Debug_GSUbi,"Is me"); #endif gs_InfoLocal.pid = pid; gs_InfoLocal.joinType = j; } if (isMaster) { gs_IdMaster=pid; } PlDesc.m_tduxPlayerId=pid; /* PlDesc.m_pPlayerDescriptionData=options;*/ /* PlDesc.m_ucPlayerDesciptionLength=size;*/ PlDesc.m_pPlayerDescriptionData=NULL; PlDesc.m_uxPlayerDesciptionLength=0; PlDesc.m_stL1AddPlayerDesc.m_eProtocol=E_Net_pr_Windows95GSUBIProtocol; PlDesc.m_stL1AddPlayerDesc.m_vL0Param=NULL; PlDesc.IsYou=isYou; PlDesc.JoinType=j; sprintf(nameopt,"name '%s'",name); PlDesc.Options=nameopt; NetLib_eAddNewPlayer(&PlDesc); } /***************************************************************************** * Description: L0PCWin95GSUBIDoPlayerLeft * A user has left the game. This function is call by the game service API. ***************************************************************************** * Input: pid : ID of player who left the game. * Output: none *****************************************************************************/ static void GS_FUNCTIONCALL L0PCWin95GSUBIDoPlayerLeft(tdPlayerId pid, int isme) { #if defined(NET_USE_DEBUG) vDebugSI(Net_C_Debug_GSUbi,"DoPlayerLeft Pid",pid); #endif if (!isme) eNetDisconnectPlayer((NetLib_tduxPlayerId)(pid),1); } /***************************************************************************** * Description: vL0PCWin95GSUBIAddNewPlayer ***************************************************************************** * Input: pChanel : adress of returned chanel. * par : extern parameter. * Output: error code. *****************************************************************************/ NetLib_tdeErrorStatus vL0PCWin95GSUBIAddNewPlayer(tduwNetChannel *pChanel,void *par) { *pChanel=0; return NetLib_E_es_NoError; } /***************************************************************************** * Description: eL0PCWin95GSUBIConnectionResult * Return connection result from tcp to game service. * Level 0 TCP call this function when the result of connection is know. ***************************************************************************** * Input: Result * Output: none *****************************************************************************/ void vL0PCWin95GSUBIConnectionResult(int iRes) { GS_vNetLibConnectionResult(iRes); } /***************************************************************************** * Description: L0PCWin95GSUBIRateCallBack * This callback is call by the game service API for control the sending rate. ***************************************************************************** * Input: ulRate, maximum rate of emission. *****************************************************************************/ static void GS_FUNCTIONCALL L0PCWin95GSUBIRateCallBack(unsigned long ulRate) { #if defined(GSUBI_TRACE) DebugSI("Maximum rate",ulRate); #endif SendMaxRate=ulRate; } /***************************************************************************** * Description: vL0PCWin95GSUBIOpenProtocol * 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_ vL0PCWin95GSUBIOpenProtocol(void) { tdstNetProtocolInterface *p_stProtocolInterface; p_stProtocolInterface=pstLevel1AddProtocol(); FIFObegin=NULL; FIFOend=&FIFObegin; /* logical identification of the protocol */ p_stProtocolInterface->eProtocol = E_Net_pr_Windows95GSUBIProtocol; /* setup the function pointers */ p_stProtocolInterface->fn_uwStartChannelScan = uwL0PCWin95GSUBIStartChannelScan; p_stProtocolInterface->fn_uwStartBroadcastChannelScan= uwL0PCWin95GSUBIStartBroadcastChannelScan; p_stProtocolInterface->fn_uwNextChannel= uwL0PCWin95GSUBINextChannel; p_stProtocolInterface->fn_uwNextBroadcastChannel= uwL0PCWin95GSUBINextBroadcastChannel; p_stProtocolInterface->fn_eReadData= eL0PCWin95GSUBIReadData; p_stProtocolInterface->fn_eSendData= eL0PCWin95GSUBISendData; p_stProtocolInterface->fn_eQueryChannelStatus= eL0PCWin95GSUBIQueryChannelStatus; p_stProtocolInterface->fn_vLevel0NetEngine = eL0PCWin95GSUBIEngine; p_stProtocolInterface->fn_vCloseChannel = vL0PCWin95GSUBICloseChannel; p_stProtocolInterface->fn_vLevel0CloseProtocol = vL0PCWin95GSUBICloseProtocol; p_stProtocolInterface->fn_eAddNewPlayer = vL0PCWin95GSUBIAddNewPlayer; gs_bL0PCWin95GSUBIInitState = 0x01; #if defined(NET_USE_DEBUG) vDebugClear(Net_C_Debug_GSUbi); vDebugS(Net_C_Debug_GSUbi,"SetupInterface"); #endif vL0PCWin95TCPGSSetCallBackResult(vL0PCWin95GSUBIConnectionResult); GS_vSetRmPlayerCallBack(L0PCWin95GSUBIDoPlayerLeft); GS_vSetNewPlayerCallBack(L0PCWin95GSUBINewPlayerCallBack); GS_vSetMsgReceivedCallBack(L0PCWin95GSUBIReadCallBack); GS_vSetRateCtrlCallBack(L0PCWin95GSUBIRateCallBack); GS_vSetSetPlayerCallBack(vL0PCWin95TCPGSSetPLayer); GS_vSetSetChannelCallBack(vL0PCWin95TCPGSSetChannel); GS_vSetInitiateConnectionCallBack(vL0PCWin95TCPGSInitiateConnection); SendBufSize=0; LastTime=GetTickCount(); SendMaxRate=1000000L; } /***************************************************************************** * Description: eL0PCWin95GSUBIStart * Start a game with GSUBI. * Initialise the incoming message list. State flag are set for call to GS_lEngine. ***************************************************************************** * Input: none * Output: none *****************************************************************************/ void _NET_CALLING_CONV_ vL0PCWin95GSUBIStart(void) { FIFO *p; #if defined(NET_USE_DEBUG) vDebugS(Net_C_Debug_GSUbi,"Start"); #endif while (FIFObegin) { p=FIFObegin->next; vFree((char*)FIFObegin); FIFObegin=p; } FIFObegin=NULL; FIFOend=&FIFObegin; gs_bL0PCWin95GSUBIInitState|=0x02; SendBufSize=0; LastTime=GetTickCount(); SendMaxRate=1000000L; } /***************************************************************************** * Description: vL0PCWin95GSUBIFinish * Finish a game with GSUBI. * This function erase the list of incoming message, disconnect all remote players, * close all TCP port, and send message to game service for quit the game mode. ***************************************************************************** * Input: none * Output: none *****************************************************************************/ void _NET_CALLING_CONV_ vL0PCWin95GSUBIFinish(void) { FIFO *p; #if defined(NET_USE_DEBUG) vDebugS(Net_C_Debug_GSUbi,"Finish"); #endif while (FIFObegin) { p=FIFObegin->next; vFree((char*)FIFObegin); FIFObegin=p; } FIFObegin=NULL; FIFOend=&FIFObegin; GS_vQuitSession(); gs_bL0PCWin95GSUBIInitState&=~0x02; SendBufSize=0; LastTime=GetTickCount(); SendMaxRate=1000000L; NetLib_vDisconnectAllRemotePlayers(); vL0PCWin95TCPCloseListenPort(); } /***************************************************************************** * Description: eL0PCWin95GSUBIMasterId * Return the master Id. Master is the player who created the session for the * game service. *****************************************************************************/ NetLib_tduxPlayerId _NET_CALLING_CONV_ eL0PCWin95GSUBIMasterId(void) { return gs_IdMaster; } /***************************************************************************** * Description : eL0PCWin95GSUBIIsProtocolSet * Check if the protocol is correctly initialize or not ***************************************************************************** * Input : none * Output : * NetLib_E_es_ProtocolNotInitialized : the vLevel0SetupWin95PCGSUBIInterface has not * been called yet or it has failed, the application should call the level 2 * eInitializeGlobalData function. *****************************************************************************/ NetLib_tdeErrorStatus _NET_CALLING_CONV_ eL0PCWin95GSUBIIsProtocolSet(void) { if(!(gs_bL0PCWin95GSUBIInitState&0x01)) return NetLib_E_es_ProtocolNotInitialized; return NetLib_E_es_True; } /***************************************************************************** * Description : eL0PCWin95GSUBIIsProtocolAvailable * Not use for this protocol. ***************************************************************************** * Output : NetLib_E_es_True if the port is avaible * An error code otherwise *****************************************************************************/ NetLib_tdeErrorStatus _NET_CALLING_CONV_ eL0PCWin95GSUBIIsProtocolAvailable(void) { return NetLib_E_es_True; }