/* * universal multiplayer library level 0 implementation file * transport layer: TEN arena PC-windows 95 */ /* * Author: David Fournier * * Creation Date: 13/12/96 * */ #include "warnings.h" #include /* * we need this one to fill the interface structure * with pointers to the correct functions */ #include "Privl0ten.h" #include "PrivNetDef.h" #include "l0GlDef.h" #include "NetMemCo.h" #include "NetSock.h" #include "NetEnd.h" #include "PrivNetSer.h" #include #include #include #include #include #include #define TEN_MAXBUF 250 static char gs_bL0PCWin95TENInitState; #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 long SendBufSize; /* Virtual buffer size. */ static unsigned long LastTime; /* Time for the last eL0PCWin95TENEngine. */ static unsigned long SendMaxRate; /* Maximum rate. */ static unsigned short NbrPlayer; /* Number of player. */ static NetLib_tdstInfoPlayer *TabPlayer; /* Table of player. */ 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 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; }TENHead; #pragma pack() /* Convert a bloc memory into a hexa string. */ void memtohexa(void *mem,unsigned short sizemem,char *hexa) { unsigned char *p=(unsigned char *)mem; unsigned short c,i; for (i=0;i>4); if (c<10) hexa[i*2]=c+'0'; else hexa[i*2]=(c-10)+'A'; c=p[i]&0x0F; if (c<10) hexa[i*2+1]=c+'0'; else hexa[i*2+1]=(c-10)+'A'; } hexa[i*2]=0; } /* Convert a hexa string into a bloc memory. */ void hexatomem(void *mem,char *hexa) { unsigned char *p=(unsigned char *)mem; unsigned short c,i,sizehexa; sizehexa=strlen(hexa)/2; for (i=0;i='0') && (c<='9')) p[i]+=((c-'0')<<4); else p[i]+=(((c-'A')+10)<<4); c=hexa[i*2+1]; if ((c>='0') && (c<='9')) p[i]+=(c-'0'); else p[i]+=(c-'A')+10; } } /* Process the maximum send rate. */ static void ProcMaxRate(void) { unsigned short i,nbrp=0; unsigned long max=10000000L; unsigned long rate; for (i=0;irate)) max=rate; nbrp++; } } if (nbrp>1) SendMaxRate=max/(nbrp-1); else SendMaxRate=10000000L; SendMaxRate=5000; } /* Add a player info. */ static void AddPlayerInfo(NetLib_tduxPlayerId pid,NetLib_tduwJoinType joinType) { NetLib_tdstInfoPlayer *nTab; unsigned short i; if (pid+1>NbrPlayer) { nTab=(NetLib_tdstInfoPlayer *)pMalloc(sizeof(NetLib_tdstInfoPlayer)*(pid+1)); memcpy(nTab,TabPlayer,sizeof(NetLib_tdstInfoPlayer)*NbrPlayer); for (i=NbrPlayer;ieMessageType,"Size",size-sizeof(TENHead),"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: wL0PCWin95TENGetMessage ***************************************************************************** * Input: ppData, address of the pointer to retrieve a full message if * necessary. * Output: return 1 if getting a message and 0 otherwise. *****************************************************************************/ short wL0PCWin95TENGetMessage(tdstNetMessage **ppMes) { FIFO *cour; tdstNetMessage *pMes; TENHead *pSend; if (FIFObegin==NULL) { *ppMes=NULL; return 0; } else { pMes=(tdstNetMessage *)pMalloc(FIFObegin->size+sizeof(tdstNetMessage)-sizeof(TENHead)); if (pMes==NULL) { *ppMes=NULL; return -1; } pSend=(TENHead *)(FIFObegin+1); memcpy(pMes+1,pSend+1,FIFObegin->size-sizeof(TENHead)); pMes->m_uxReadReceiptId=pSend->m_ulReadReceiptId; 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(TENHead)); 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: eL0PCWin95TENReadData ***************************************************************************** * 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 eL0PCWin95TENReadData(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=wL0PCWin95TENGetMessage((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: eL0PCWin95TENSendData ***************************************************************************** * Input: uwChannel, channel to send the message into. * pData, pointer on the block to send * Output: an error condition. *****************************************************************************/ NetLib_tdeErrorStatus eL0PCWin95TENSendData(tduwNetChannel uwChannel, tdpPointer pData) { unsigned long ulBytestoSend; tdstNetMessage *pMes=(tdstNetMessage *)pData; int NumErr; TENHead *pSend; unsigned long NewTime; /* verify the validity of the Channel */ if(uwChannel>1) { #if defined(NET_USE_DEBUG) vDebugS(Net_C_Debug_Ten,"Invalid channel"); #endif return NetLib_E_es_InvalidChannel; } if (pMes->uxRecipientId==C_uxNetBroadcastId) uwChannel=1; ulBytestoSend= pMes->uwMessageSizeInBytes + sizeof(TENHead); NewTime=GetTickCount(); SendBufSize-=((NewTime-LastTime)*(float)SendMaxRate)/1000; LastTime=NewTime; if (SendBufSize<0) SendBufSize=0; if (SendBufSize>=TEN_MAXBUF) { #if defined(NET_USE_DEBUG) vDebugS(Net_C_Debug_Ten,"Controle de flot."); #endif return NetLib_E_es_BufferIsFull; } SendBufSize+=ulBytestoSend+20; pSend=(TENHead *)pMalloc(ulBytestoSend); if (!pSend) { #if defined(NET_USE_DEBUG) vDebugS(Net_C_Debug_Ten,"Manque de mémoire."); #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; if (uwChannel!=1) { #if defined(NET_USE_DEBUG) vDebugSISISI(Net_C_Debug_Ten,"Send to one player. Type",pMes->eMessageType,"Size",pMes->uwMessageSizeInBytes,"Recipient",pMes->uxRecipientId); #endif NumErr=tenArSendToPlayer(pMes->uxRecipientId,pSend,ulBytestoSend); } else { #if defined(NET_USE_DEBUG) vDebugSISISI(Net_C_Debug_Ten,"Send to all players. Type",pMes->eMessageType,"Size",pMes->uwMessageSizeInBytes,"Recipient",pMes->uxRecipientId); #endif NumErr=tenArSendToOtherPlayers(pSend,ulBytestoSend); } vFree((char *)pSend); if (NumErr) { #if defined(NET_USE_DEBUG) vDebugSI(Net_C_Debug_Ten,"Emission en attente",NumErr); #endif return NetLib_E_es_BufferIsFull; } /* free the message buffer */ vFree(pData); return NetLib_E_es_NoError; } /***************************************************************************** * Description: eL0PCWin95TENQueryChannelStatus * returns the current status of the channel ***************************************************************************** * Input: uwChannel, channel to query * Output: an error condition. *****************************************************************************/ NetLib_tdeErrorStatus eL0PCWin95TENQueryChannelStatus(tduwNetChannel uwChannel) { return NetLib_E_es_NoError; } /***************************************************************************** * Description: eL0PCWin95TENEngine ***************************************************************************** * Input: none * Output: none *****************************************************************************/ void eL0PCWin95TENEngine(void) { tenArIdleArena(); } /***************************************************************************** * Description : vL0PCWin95TENCloseProtocol(void) * TEN-Level 0 closing function ***************************************************************************** * Input : none * Output : none *****************************************************************************/ void vL0PCWin95TENCloseProtocol(void) { FIFO *p; while (FIFObegin) { p=FIFObegin->next; vFree((char*)FIFObegin); FIFObegin=p; } FIFObegin=NULL; FIFOend=&FIFObegin; vLevel1RemoveProtocol(E_Net_pr_Windows95TENProtocol); } /***************************************************************************** * Description: L0PCWin95TENNewPlayerCallBack ***************************************************************************** * Input: none * Output: none *****************************************************************************/ static void L0PCWin95TENNewPlayerCallBack(int pid, int isYou, char *options, char *termOptions, char *address, long uniqueId, char *joinType) { NetLib_tdstAddPlayerDesc PlDesc; char typeToken[16]; char hexa[500],mem[250]; short j=-1; #if defined(NET_USE_DEBUG) vDebugSI(Net_C_Debug_Ten,"NewPlayerCallBack Pid",pid); #endif sscanf(joinType, "%s", typeToken); /* peel off the first word*/ if (!strcmp(typeToken, "create")) j=NetLib_Join_Create; else if (!strcmp(typeToken, "join")) j=NetLib_Join_Join; else if (!strcmp(typeToken, "watch")) j=NetLib_Join_Watch; if (isYou) { #if defined(NET_USE_DEBUG) vDebugS(Net_C_Debug_Ten,"Is me"); #endif gs_InfoLocal.pid = pid; gs_InfoLocal.joinType = j; gs_uwCallBack|=1; } PlDesc.m_tduxPlayerId=pid; getStringParam(options,"NetlibOptions",hexa,sizeof(hexa)); hexatomem(mem,hexa); PlDesc.m_pPlayerDescriptionData=mem; PlDesc.m_uxPlayerDesciptionLength=strlen(hexa)/2; PlDesc.m_stL1AddPlayerDesc.m_eProtocol=E_Net_pr_Windows95TENProtocol; PlDesc.m_stL1AddPlayerDesc.m_vL0Param=NULL; PlDesc.IsYou=isYou; PlDesc.JoinType=j; PlDesc.Options=options; NetLib_eAddNewPlayer(&PlDesc); AddPlayerInfo((NetLib_tduxPlayerId)pid,(NetLib_tduwJoinType)j); } /***************************************************************************** * Description: L0PCWin95TENDoPlayerLeft * A user has left the game. ***************************************************************************** * Input: pid : ID of player who left the game. * Output: none *****************************************************************************/ static void L0PCWin95TENDoPlayerLeft(int pid) { #if defined(NET_USE_DEBUG) vDebugSI(Net_C_Debug_Ten,"DoPlayerLeft Pid",pid); #endif eNetDisconnectPlayer((NetLib_tduxPlayerId)pid,1); SupPlayerInfo((NetLib_tduxPlayerId)pid); } #include /***************************************************************************** * Description: eL0PCWin95TENDoAlert * Callback receive the error messages. *****************************************************************************/ static void eL0PCWin95TENDoAlert(int type, int err, char *msg) { printf("%s\n", msg); } /***************************************************************************** * Description: eL0PCWin95TENDebugMessage * Callback for verifyNoErr. *****************************************************************************/ static void eL0PCWin95TENDebugMessage(int msgLevel, char *msg) { fprintf(stderr, "%s", msg); } /***************************************************************************** * Description : eL0PCWin95TENSetGameOptions * Set the game options. ***************************************************************************** * Input : GameOptions : the game options. * Output : none. *****************************************************************************/ _NET_EXPORT_ void _NET_CALLING_CONV_ eL0PCWin95TENSetGameOptions(char *GameName,void *GameOptions,unsigned short SizeOptions) { char options[500],conv[500]; options[0]=0; addStringParam(options,sizeof(options),"name",GameName); memtohexa(GameOptions,SizeOptions,conv); addStringParam(options,sizeof(options),"NetlibOptions",conv); tenArRegisterGame(options); NetLib_eSetSessionDescription((unsigned char)SizeOptions,(char *)GameOptions,0); } /***************************************************************************** * Description : eL0PCWin95TENSetPlayerOptions * Set the player options. ***************************************************************************** * Input : GameOptions : the game options. * Output : none. *****************************************************************************/ _NET_EXPORT_ void _NET_CALLING_CONV_ eL0PCWin95TENSetPlayerOptions(char *PlayerName,char *PlayerOptions,unsigned short SizeOptions) { char options[500],conv[500]; options[0]=0; addStringParam(options,sizeof(options),"name",PlayerName); memtohexa(PlayerOptions,SizeOptions,conv); addStringParam(options,sizeof(options),"NetlibOptions",conv); tenArRegisterPlayer(options); do { tenArIdleArena(); }while (!((gs_uwCallBack&1) && ((gs_uwCallBack&4) || (gs_JoinType==NetLib_Join_Create)))); } /***************************************************************************** * Description: eL0PCWin95TENDoPregameHook * Callback for verifyNoErr. *****************************************************************************/ static void eL0PCWin95TENDoPregameHook(char *joinType, char *gameTermOptions, char *playerTermOptions, char *name, char *alias, char *address) { char typeToken[16]; sscanf(joinType, "%s", typeToken); /* peel off the first word*/ if (!strcmp(typeToken, "create")) gs_JoinType=NetLib_Join_Create; else if (!strcmp(typeToken, "join")) gs_JoinType=NetLib_Join_Join; else if (!strcmp(typeToken, "watch")) gs_JoinType=NetLib_Join_Watch; gs_uwCallBack|=2; } /***************************************************************************** * Description: eL0PCWin95TENDoGameOptions * Callback for recept change of game options. *****************************************************************************/ void eL0PCWin95TENDoGameOptions(char *options, char *termOptions) { char hexa[500],mem[250]; getStringParam(options,"NetlibOptions",hexa,sizeof(hexa)); hexatomem(mem,hexa); NetLib_eSetSessionDescription((char)(strlen(hexa)/2),mem,0); gs_uwCallBack|=4; } /***************************************************************************** * Description: vL0PCWin95TENAddNewPlayer ***************************************************************************** * Input: pChanel : adress of returned chanel. * par : extern parameter. * Output: error code. *****************************************************************************/ NetLib_tdeErrorStatus vL0PCWin95TENAddNewPlayer(tduwNetChannel *pChanel,void *par) { *pChanel=0; return NetLib_E_es_NoError; } /***************************************************************************** * Description: vL0PCWin95TENOpenProtocol * 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_ vL0PCWin95TENOpenProtocol(void) { tdstNetProtocolInterface *p_stProtocolInterface; #if defined(NET_USE_DEBUG) vDebugClear(Net_C_Debug_Ten); vDebugS(Net_C_Debug_Ten,"SetupInterface"); #endif p_stProtocolInterface=pstLevel1AddProtocol(); FIFObegin=NULL; FIFOend=&FIFObegin; /* logical identification of the protocol */ p_stProtocolInterface->eProtocol = E_Net_pr_Windows95TENProtocol; /* setup the function pointers */ p_stProtocolInterface->fn_uwStartChannelScan = uwL0PCWin95TENStartChannelScan; p_stProtocolInterface->fn_uwStartBroadcastChannelScan= uwL0PCWin95TENStartBroadcastChannelScan; p_stProtocolInterface->fn_uwNextChannel= uwL0PCWin95TENNextChannel; p_stProtocolInterface->fn_uwNextBroadcastChannel= uwL0PCWin95TENNextBroadcastChannel; p_stProtocolInterface->fn_eReadData= eL0PCWin95TENReadData; p_stProtocolInterface->fn_eSendData= eL0PCWin95TENSendData; p_stProtocolInterface->fn_eQueryChannelStatus= eL0PCWin95TENQueryChannelStatus; p_stProtocolInterface->fn_vLevel0NetEngine = eL0PCWin95TENEngine; p_stProtocolInterface->fn_vCloseChannel = vL0PCWin95TENCloseChannel; p_stProtocolInterface->fn_vLevel0CloseProtocol = vL0PCWin95TENCloseProtocol; p_stProtocolInterface->fn_eAddNewPlayer = vL0PCWin95TENAddNewPlayer; gs_bL0PCWin95TENInitState = 0x01; setDebugMsgRoutine(eL0PCWin95TENDebugMessage); tenArSetPregameHookRoutine(eL0PCWin95TENDoPregameHook); tenArSetGameOptionsRoutine(eL0PCWin95TENDoGameOptions); tenArSetPlayerLeftRoutine(L0PCWin95TENDoPlayerLeft); tenArSetPlayerEnteredRoutine(L0PCWin95TENNewPlayerCallBack); tenArSetIncomingPacketRoutine(L0PCWin95TENReadCallBack); tenArSetAlertMessageRoutine(eL0PCWin95TENDoAlert); } /***************************************************************************** * Description: eL0PCWin95TENStart * Start a game with TEN ***************************************************************************** * Input: ppGameOptions : Adresse of a pointer wich receive the adresse of * game options. * Output: join type. *****************************************************************************/ _NET_EXPORT_ NetLib_tduwJoinType _NET_CALLING_CONV_ eL0PCWin95TENStart(void) { #if defined(NET_USE_DEBUG) vDebugS(Net_C_Debug_Ten,"Start"); #endif SendMaxRate=10000000L; NbrPlayer=0; SendBufSize=0; TabPlayer=NULL; LastTime=GetTickCount(); FIFObegin=NULL; FIFOend=&FIFObegin; verifyNoErr(tenArInitArena("Lobby")); do { tenArIdleArena(); }while (!(gs_uwCallBack&2)); gs_bL0PCWin95TENInitState|=0x02; return gs_JoinType; } /***************************************************************************** * Description: eL0PCWin95TENFinish * Finish a game with TEN. ***************************************************************************** * Input: none * Output: none *****************************************************************************/ _NET_EXPORT_ void _NET_CALLING_CONV_ eL0PCWin95TENFinish(void) { FIFO *p; #if defined(NET_USE_DEBUG) vDebugS(Net_C_Debug_Ten,"Finish"); #endif vFree((char *)TabPlayer); while (FIFObegin) { p=FIFObegin->next; vFree((char*)FIFObegin); FIFObegin=p; } FIFObegin=NULL; FIFOend=&FIFObegin; tenArExitArena(); gs_bL0PCWin95TENInitState&=~0x02; NetLib_eFlushSendingList(500); NetLib_vDisconnectAllRemotePlayers(); } /***************************************************************************** * Description: eL0PCWin95TENGamePrivate * Refuse all new players in the game. *****************************************************************************/ _NET_EXPORT_ void _NET_CALLING_CONV_ eL0PCWin95TENGamePrivate(void) { char options[20]; options[0]=0; addStringParam(options,sizeof(options),"lock","y"); tenArSetGameState(options); } /***************************************************************************** * Description: eL0PCWin95TENGamePublic * Accept all new players in the game (if maximum number of player allow it). *****************************************************************************/ _NET_EXPORT_ void _NET_CALLING_CONV_ eL0PCWin95TENGamePublic(void) { char options[20]; options[0]=0; addStringParam(options,sizeof(options),"lock","n"); tenArSetGameState(options); } /***************************************************************************** * Description : eL0PCWin95TENIsProtocolSet * Check if the protocol is correctly initialize or not ***************************************************************************** * Input : none * Output : * NetLib_E_es_ProtocolNotInitialized : the vLevel0SetupWin95PCTENInterface has not * been called yet or it has failed, the application should call the level 2 * eInitializeGlobalData function. * NetLib_E_es_InitialisationSocketError : the eLevel0InitWin95PCTEN function has not been * called yet or it has failed, the application should call the level 2 * eInitializeGlobalData function * NetLib_E_es_NoPortSelected : the eL0PCWin95TENAddPort has not been called yet. The * protocol has been correctly initialised, it can be used, but it won't give any * results since no port is avaible. The application should call it. *****************************************************************************/ NetLib_tdeErrorStatus _NET_CALLING_CONV_ eL0PCWin95TENIsProtocolSet(void) { if(!(gs_bL0PCWin95TENInitState&0x01)) return NetLib_E_es_ProtocolNotInitialized; return NetLib_E_es_True; } /***************************************************************************** * Description : eL0PCWin95TENIsProtocolAvailable * Check if the protocol is avaible for use or not ***************************************************************************** * Input : A port number to test * Output : NetLib_E_es_True if the port is avaible * An error code otherwise *****************************************************************************/ NetLib_tdeErrorStatus _NET_CALLING_CONV_ eL0PCWin95TENIsProtocolAvailable(void) { if(!(gs_bL0PCWin95TENInitState&0x01)) return NetLib_E_es_ProtocolNotInitialized; return NetLib_E_es_True; } /***************************************************************************** * Description : eL0PCWin95TENPlayerInfo * Return player bandwith and latency. * Return non null value if error. *****************************************************************************/ int _NET_CALLING_CONV_ eL0PCWin95TENPlayerInfo(NetLib_tduxPlayerId PId,long *bandwhith,long *latency) { int r; size_t size=sizeof(long); r=tenArGetPlayerInfo(PId,kTenArPIBandwidth,bandwhith,&size); if (r!=0) return r; size=sizeof(long); r=tenArGetPlayerInfo(PId,kTenArPIAveLatency,latency,&size); return r; } /***************************************************************************** * Description : eL0PCWin95TENLaunchTenClient * Input : a quit callback function. *****************************************************************************/ void _NET_CALLING_CONV_ eL0PCWin95TENLaunchTenClient(void (*rtn)(void)) { tenBnSetExitRtn(rtn); tenBnStart(); }