mirror of
https://github.com/Mauler125/r5sdk.git
synced 2025-02-09 19:15:03 +01:00
DirtySDK (EA's Dirty Sockets library) will be used for the LiveAPI implementation, and depends on: EABase, EAThread.
2240 lines
79 KiB
C
2240 lines
79 KiB
C
/*H********************************************************************************/
|
|
/*!
|
|
\File voiptunnel.c
|
|
|
|
\Description
|
|
This module implements the main logic for the VoipTunnel server.
|
|
|
|
Description forthcoming.
|
|
|
|
\Copyright
|
|
Copyright (c) 2006 Electronic Arts Inc.
|
|
|
|
\Version 03/24/2006 (jbrookes) First Version
|
|
*/
|
|
/********************************************************************************H*/
|
|
|
|
/*** Include files ****************************************************************/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
|
|
#include "DirtySDK/platform.h"
|
|
#include "DirtySDK/dirtysock/dirtylib.h"
|
|
#include "DirtySDK/dirtysock/dirtymem.h"
|
|
#include "DirtySDK/dirtysock/dirtynet.h"
|
|
#include "DirtySDK/dirtysock/netconn.h"
|
|
|
|
#include "DirtySDK/voip/voipdef.h"
|
|
#include "DirtySDK/voip/voiptunnel.h"
|
|
#include "voippriv.h"
|
|
#include "voippacket.h"
|
|
|
|
/*** Defines **********************************************************************/
|
|
|
|
//! define this as TRUE to enable lookup table test code
|
|
#define VOIPTUNNEL_LOOKUP_TEST (FALSE)
|
|
|
|
//! voiptunnel broadcast flags
|
|
#define VOIPTUNNEL_GAMEFLAG_BROADCAST_ALL 0
|
|
#define VOIPTUNNEL_GAMEFLAG_BROADCAST_ALLOTHERS 1
|
|
#define VOIPTUNNEL_GAMEFLAG_SEND_SINGLE 2
|
|
#define VOIPTUNNEL_GAMEFLAG_SEND_MULTI 4
|
|
#define VOIPTUNNEL_GAMEFLAG_MASK 0xf
|
|
|
|
#define VOIPTUNNEL_GAMEFLAG_VDP 16
|
|
|
|
/*** Type Definitions *************************************************************/
|
|
|
|
//! VoIP packet data that this module cares about (header + first 4 bytes)
|
|
typedef struct VoipTunnelPacketT
|
|
{
|
|
VoipPacketHeadT Head; //!< packet header
|
|
uint8_t aRemoteClientId[4]; //!< present in all packets except for mic packets, which have the send mask here instead
|
|
} VoipTunnelPacketT;
|
|
|
|
//! client lookup table element
|
|
typedef struct VoipTunnelLookupElemT
|
|
{
|
|
uint32_t uClientId;
|
|
uint32_t uClientIdx;
|
|
} VoipTunnelLookupElemT;
|
|
|
|
//! module state
|
|
struct VoipTunnelRefT
|
|
{
|
|
// module memory group
|
|
int32_t iMemGroup; //!< module mem group id
|
|
void *pMemGroupUserData; //!< user data associated with mem group
|
|
|
|
SocketT *pVoipSocket; //!< virtual socket for receiving tunneled voip data
|
|
VoipTunnelCallbackT *pCallback; //!< optional voiptunnel event callback
|
|
void *pUserData; //!< user data for voiptunnel callback
|
|
uint32_t uLocalClientId; //!< local client id (used for tunnel connectivity)
|
|
uint16_t uVoipPort; //!< virtual voip port
|
|
uint16_t uVoiceRecvTimeout; //!< number of milliseconds before clearing RECVVOICE flag
|
|
uint8_t uDebugLevel; //!< debug level
|
|
uint8_t bPortSniff; //!< TRUE if port sniffing enabled, else FALSE (default FALSE)
|
|
uint8_t _pad[2];
|
|
int32_t iNumClients; //!< number of tunnel clients
|
|
int32_t iNumTalkingClients; //!< number of "talking" clients (i.e. client with VOIPTUNNEL_CLIENTFLAG_RECVVOICE flag set)
|
|
int32_t iMaxClients; //!< maximum number tunnel clients
|
|
int32_t iMaxVoiceBroadcasters; //!< maximum number of players can be talking at once in a single game
|
|
VoipTunnelLookupElemT *pLookupTable; //!< fast-lookup table
|
|
int32_t iNumGames; //!< current number of games
|
|
int32_t iMaxGames; //!< maximum number of supported gameservers
|
|
uint32_t uVoiceDataDropMetric; //!< the number of voice packets that were not rebroadcast globally due to max broadcasters constraint
|
|
uint32_t uVoiceMaxTalkersMetric;//!< the number of time the max broadcasters event was reached
|
|
VoipTunnelGameT *pGameList; //!< list of games
|
|
VoipTunnelClientT ClientList[1]; //!< variable-length tunnel client list -- MUST COME LAST IN THE STRUCTURE
|
|
};
|
|
|
|
/*** Variables ********************************************************************/
|
|
|
|
|
|
/*** Private Functions ************************************************************/
|
|
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function _VoipTunnelEventCallback
|
|
|
|
\Description
|
|
Call user event callback, if it is available.
|
|
|
|
\Input *pVoipTunnel - voiptunnel ref
|
|
\Input eEvent - event triggering callback
|
|
\Input *pClient - pointer to client associated with event
|
|
\Input iDataSize - size of packet data associated with event
|
|
|
|
\Version 03/01/2007 (jbrookes)
|
|
*/
|
|
/********************************************************************************F*/
|
|
static void _VoipTunnelEventCallback(VoipTunnelRefT *pVoipTunnel, VoipTunnelEventE eEvent, VoipTunnelClientT *pClient, int32_t iDataSize)
|
|
{
|
|
if (pVoipTunnel->pCallback != NULL)
|
|
{
|
|
VoipTunnelEventDataT EventData;
|
|
EventData.eEvent = eEvent;
|
|
EventData.pClient = pClient;
|
|
EventData.iDataSize = iDataSize;
|
|
pVoipTunnel->pCallback(pVoipTunnel, &EventData, pVoipTunnel->pUserData);
|
|
}
|
|
}
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function _VoipTunnelClientListSend
|
|
|
|
\Description
|
|
Broadcast input data to all other clients in our session.
|
|
|
|
\Input *pVoipTunnel - voiptunnel ref
|
|
\Input *pSrcClient - pointer to client that sent the data
|
|
\Input uDstClientId - clientId to send to, if SEND_SINGLE
|
|
\Input *pPacketData - pointer to packet data
|
|
\Input iPacketSize - size of packet data
|
|
\Input *pAddr - address data came from
|
|
\Input uSendFlag - send flags
|
|
|
|
\Version 03/31/2006 (jbrookes)
|
|
*/
|
|
/********************************************************************************F*/
|
|
static void _VoipTunnelClientListSend(VoipTunnelRefT *pVoipTunnel, VoipTunnelClientT *pSrcClient, uint32_t uDstClientId, const char *pPacketData, int32_t iPacketSize, struct sockaddr *pAddr, uint32_t uSendFlag)
|
|
{
|
|
VoipTunnelClientT *pClient;
|
|
int32_t iClient, iResult;
|
|
|
|
// if vdp, restore the header
|
|
if (uSendFlag & VOIPTUNNEL_GAMEFLAG_VDP)
|
|
{
|
|
pPacketData -= 2;
|
|
iPacketSize += 2;
|
|
}
|
|
|
|
// unicast or broadcast?
|
|
if (!(uSendFlag & VOIPTUNNEL_GAMEFLAG_SEND_SINGLE))
|
|
{
|
|
VoipTunnelGameT *pGame = &pVoipTunnel->pGameList[pSrcClient->iGameIdx];
|
|
|
|
// forward data data to other clients in session
|
|
for (iClient = 0; iClient < VOIPTUNNEL_MAXGROUPSIZE; iClient++)
|
|
{
|
|
// make sure the client is active
|
|
if (pGame->bClientActive[iClient] == FALSE)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// ref client
|
|
if ((pClient = VoipTunnelClientListMatchId(pVoipTunnel, pGame->aClientList[iClient])) == NULL)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// don't send data to source client
|
|
if ((uSendFlag & VOIPTUNNEL_GAMEFLAG_BROADCAST_ALLOTHERS) && (pClient == pSrcClient))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// if send mask not set for this client, skip them
|
|
if ((pSrcClient->uSendMask & (1 << (unsigned)iClient)) == 0)
|
|
{
|
|
NetPrintfVerbose((pVoipTunnel->uDebugLevel, 1, "voiptunnel: client send mask mute of voice from clientId=0x%08x to clientId=0x%08x\n", pSrcClient->uClientId, pClient->uClientId));
|
|
continue;
|
|
}
|
|
|
|
// if game send mask not set for this client, skip them
|
|
if ((pSrcClient->uGameSendMask & (1 << (unsigned)iClient)) == 0)
|
|
{
|
|
NetPrintfVerbose((pVoipTunnel->uDebugLevel, 1, "voiptunnel: game send mask mute of voice from clientId=0x%08x to clientId=0x%08x\n", pSrcClient->uClientId, pClient->uClientId));
|
|
continue;
|
|
}
|
|
|
|
// rewrite address to send to this client
|
|
SockaddrInSetAddr(pAddr, pClient->uRemoteAddr);
|
|
// if port sniffing is enabled, rewrite port as well
|
|
if (pVoipTunnel->bPortSniff)
|
|
{
|
|
// don't send if we don't have a port to send to
|
|
if (pClient->uRemoteVoipPort == 0)
|
|
{
|
|
continue;
|
|
}
|
|
SockaddrInSetPort(pAddr, pClient->uRemoteVoipPort);
|
|
}
|
|
|
|
if ((iResult = SocketSendto(pVoipTunnel->pVoipSocket, pPacketData, iPacketSize, 0, pAddr, sizeof(*pAddr))) == iPacketSize)
|
|
{
|
|
// call user callback
|
|
_VoipTunnelEventCallback(pVoipTunnel, VOIPTUNNEL_EVENT_SENDVOICE, pClient, iPacketSize);
|
|
}
|
|
else
|
|
{
|
|
NetPrintf(("voiptunnel: send of %d byte voice packet from clientId=0x%08x to clientId=0x%08x failed (err=%d)\n", iPacketSize, pSrcClient->uClientId, pClient->uClientId, iResult));
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ((pClient = VoipTunnelClientListMatchId(pVoipTunnel, uDstClientId)) != NULL)
|
|
{
|
|
// rewrite address to send to this client
|
|
SockaddrInSetAddr(pAddr, pClient->uRemoteAddr);
|
|
// if port sniffing is enabled, rewrite port as well
|
|
if (pVoipTunnel->bPortSniff)
|
|
{
|
|
// don't send if we don't have a port to send to
|
|
if (pClient->uRemoteVoipPort == 0)
|
|
{
|
|
return;
|
|
}
|
|
SockaddrInSetPort(pAddr, pClient->uRemoteVoipPort);
|
|
}
|
|
|
|
if ((iResult = SocketSendto(pVoipTunnel->pVoipSocket, pPacketData, iPacketSize, 0, pAddr, sizeof(*pAddr))) == iPacketSize)
|
|
{
|
|
// call user callback
|
|
_VoipTunnelEventCallback(pVoipTunnel, VOIPTUNNEL_EVENT_SENDVOICE, pClient, iPacketSize);
|
|
}
|
|
else
|
|
{
|
|
NetPrintf(("voiptunnel: send of %d byte voice packet from clientId=0x%08x to clientId=0x%08x failed (err=%d)\n", iPacketSize, pSrcClient->uClientId, pClient->uClientId, iResult));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
NetPrintf(("voiptunnel: unable to ref client with id=0x%08x for unicast send\n", uDstClientId));
|
|
}
|
|
}
|
|
}
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function _VoipTunnelValidatePacketType
|
|
|
|
\Description
|
|
Returns whether the data points to a valid packet type or not.
|
|
|
|
\Input *pPacketData - pointer to type to evaluate
|
|
|
|
\Version 08/24/2006 (jbrookes)
|
|
*/
|
|
/********************************************************************************F*/
|
|
static int32_t _VoipTunnelValidatePacketType(const char *pPacketData)
|
|
{
|
|
return(!memcmp(pPacketData, "CO", 2) || !memcmp(pPacketData, "DSC", 3) || !memcmp(pPacketData, "PNG", 3) || !memcmp(pPacketData, "MIC", 3));
|
|
}
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function _VoipTunnelValidatePacket
|
|
|
|
\Description
|
|
Locate packet data and validate packet
|
|
|
|
\Input *pVoipTunnel - voip tunnel
|
|
\Input *pPacketData - pointer to packet head
|
|
\Input iPacketSize - size of received data
|
|
\Input pVdpHeader - vdp header
|
|
|
|
\Output
|
|
const VoipTunnelPacketT *pPacket - pointer to packet
|
|
|
|
\Version 08/24/2006 (jbrookes)
|
|
*/
|
|
/********************************************************************************F*/
|
|
static VoipTunnelPacketT *_VoipTunnelValidatePacket(VoipTunnelRefT *pVoipTunnel, char *pPacketData, int32_t iPacketSize, uint32_t *pVdpHeader)
|
|
{
|
|
// try and determine if there is a VDP header or not
|
|
if (_VoipTunnelValidatePacketType(pPacketData+2))
|
|
{
|
|
// VDP header... so adjust the pointer to account for it
|
|
NetPrintfVerbose((pVoipTunnel->uDebugLevel, 2, "voiptunnel: vdp header detected\n"));
|
|
pPacketData += 2;
|
|
iPacketSize -= 2;
|
|
*pVdpHeader = TRUE;
|
|
}
|
|
else if (_VoipTunnelValidatePacketType(pPacketData))
|
|
{
|
|
NetPrintfVerbose((pVoipTunnel->uDebugLevel, 2, "voiptunnel: no vdp header\n"));
|
|
*pVdpHeader = FALSE;
|
|
}
|
|
else
|
|
{
|
|
NetPrintf(("voiptunnel: unknown VoIP packet type; discarding\n"));
|
|
return(NULL);
|
|
}
|
|
|
|
// validate size
|
|
if (iPacketSize < (signed)sizeof(VoipTunnelPacketT))
|
|
{
|
|
NetPrintf(("voiptunnel: voip packet size is too small; discarding\n"));
|
|
return(NULL);
|
|
}
|
|
|
|
return((VoipTunnelPacketT *)pPacketData);
|
|
}
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function _VoipTunnelGetClientId
|
|
|
|
\Description
|
|
Read client ID from voice packet
|
|
|
|
\Input *pPacket - pointer to packet
|
|
\Input iPacketSize - size of received data
|
|
|
|
\Output
|
|
int32_t - clientId, or zero
|
|
|
|
\Version 05/16/2006 (jbrookes)
|
|
*/
|
|
/********************************************************************************F*/
|
|
static uint32_t _VoipTunnelGetClientId(const VoipTunnelPacketT *pPacket, int32_t iPacketSize)
|
|
{
|
|
uint32_t uClientId;
|
|
|
|
// extract the client identifier
|
|
uClientId = pPacket->Head.aClientId[0] << 24;
|
|
uClientId |= pPacket->Head.aClientId[1] << 16;
|
|
uClientId |= pPacket->Head.aClientId[2] << 8;
|
|
uClientId |= pPacket->Head.aClientId[3];
|
|
|
|
// return to caller
|
|
return(uClientId);
|
|
}
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function _VoipTunnelLookupSort
|
|
|
|
\Description
|
|
qsort callback used to sort fast lookup array.
|
|
|
|
\Input *_pElem0 - pointer to first element to compare
|
|
\Input *_pElem1 - pointer to second element to compare
|
|
|
|
\Output
|
|
int32_t - sort value (one or minus one)
|
|
|
|
\Version 04/03/2007 (jbrookes)
|
|
*/
|
|
/********************************************************************************F*/
|
|
static int32_t _VoipTunnelLookupSort(const void *_pElem0, const void *_pElem1)
|
|
{
|
|
const VoipTunnelLookupElemT *pElem0 = (const VoipTunnelLookupElemT *)_pElem0;
|
|
const VoipTunnelLookupElemT *pElem1 = (const VoipTunnelLookupElemT *)_pElem1;
|
|
|
|
return((pElem0->uClientId > pElem1->uClientId) ? 1 : -1);
|
|
}
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function _VoipTunnelLookupClient
|
|
|
|
\Description
|
|
Lookup a client given the clientId using binary search on sorted array.
|
|
|
|
\Input *pVoipTunnel - module state
|
|
\Input uClientId - client identifier
|
|
|
|
\Output
|
|
VoipTunnelClientT * - pointer to found client, or NULL if not found
|
|
|
|
\Version 04/03/2007 (jbrookes)
|
|
*/
|
|
/********************************************************************************F*/
|
|
static VoipTunnelClientT *_VoipTunnelLookupClient(VoipTunnelRefT *pVoipTunnel, uint32_t uClientId)
|
|
{
|
|
int32_t iCheck, iLow, iHigh;
|
|
uint32_t uCheckId;
|
|
|
|
// execute binary search on sorted lookup table
|
|
for (iLow = 0, iHigh = pVoipTunnel->iNumClients-1; iLow <= iHigh; )
|
|
{
|
|
iCheck = iLow + ((iHigh - iLow) / 2);
|
|
if ((uCheckId = pVoipTunnel->pLookupTable[iCheck].uClientId) > uClientId)
|
|
{
|
|
iHigh = iCheck - 1;
|
|
}
|
|
else if (uCheckId < uClientId)
|
|
{
|
|
iLow = iCheck + 1;
|
|
}
|
|
else
|
|
{
|
|
#if VOIPTUNNEL_LOOKUP_TEST
|
|
NetPrintf(("voiptunnel: lookup found client id=0x%08x\n", uClientId));
|
|
#endif
|
|
return(&pVoipTunnel->ClientList[pVoipTunnel->pLookupTable[iCheck].uClientIdx]);
|
|
}
|
|
}
|
|
|
|
// not found
|
|
NetPrintfVerbose((pVoipTunnel->uDebugLevel, 1, "voiptunnel: lookup could not find client id=0x%08x\n", uClientId));
|
|
return(NULL);
|
|
}
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function _VoipTunnelLookupBuild
|
|
|
|
\Description
|
|
Build a lookup table sorted by clientId for fast lookups.
|
|
|
|
\Input *pVoipTunnel - module state
|
|
|
|
\Version 04/03/2007 (jbrookes)
|
|
*/
|
|
/********************************************************************************F*/
|
|
static void _VoipTunnelLookupBuild(VoipTunnelRefT *pVoipTunnel)
|
|
{
|
|
int32_t iClient;
|
|
|
|
// only build it if the table has been allocated
|
|
if (pVoipTunnel->pLookupTable == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// build unsorted table
|
|
for (iClient = 0; iClient < pVoipTunnel->iNumClients; iClient++)
|
|
{
|
|
pVoipTunnel->pLookupTable[iClient].uClientId = pVoipTunnel->ClientList[iClient].uClientId;
|
|
pVoipTunnel->pLookupTable[iClient].uClientIdx = (unsigned)iClient;
|
|
}
|
|
|
|
// sort it by clientId
|
|
qsort(pVoipTunnel->pLookupTable, iClient, sizeof(pVoipTunnel->pLookupTable[0]), _VoipTunnelLookupSort);
|
|
}
|
|
|
|
#if VOIPTUNNEL_LOOKUP_TEST
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function _VoipTunnelLookupTest
|
|
|
|
\Description
|
|
Test code to run lookup code through a few simple test cases.
|
|
|
|
\Input *pVoipTunnel - module state
|
|
|
|
\Version 04/03/2007 (jbrookes)
|
|
*/
|
|
/********************************************************************************F*/
|
|
static void _VoipTunnelLookupTest(VoipTunnelRefT *pVoipTunnel)
|
|
{
|
|
VoipTunnelLookupElemT _LookupTable[32];
|
|
|
|
// set up temp table for testing
|
|
pVoipTunnel->pLookupTable = _LookupTable;
|
|
|
|
// test no elements
|
|
pVoipTunnel->iNumClients = 0;
|
|
_VoipTunnelLookupBuild(pVoipTunnel);
|
|
_VoipTunnelLookupClient(pVoipTunnel, 0);
|
|
|
|
// test one element
|
|
pVoipTunnel->iNumClients = 1;
|
|
pVoipTunnel->ClientList[0].uClientId = 0xdeadbeef;
|
|
_VoipTunnelLookupBuild(pVoipTunnel);
|
|
_VoipTunnelLookupClient(pVoipTunnel, 0xdeadbeef);
|
|
|
|
// test two elements
|
|
pVoipTunnel->iNumClients = 2;
|
|
pVoipTunnel->ClientList[1].uClientId = 0xcacabeef;
|
|
_VoipTunnelLookupBuild(pVoipTunnel);
|
|
_VoipTunnelLookupClient(pVoipTunnel, 0xdeadbeef);
|
|
_VoipTunnelLookupClient(pVoipTunnel, 0xcacabeef);
|
|
|
|
// test three elements
|
|
pVoipTunnel->iNumClients = 3;
|
|
pVoipTunnel->ClientList[2].uClientId = 0xf000beef;
|
|
_VoipTunnelLookupBuild(pVoipTunnel);
|
|
_VoipTunnelLookupClient(pVoipTunnel, 0xdeadbeef);
|
|
_VoipTunnelLookupClient(pVoipTunnel, 0xcacabeef);
|
|
_VoipTunnelLookupClient(pVoipTunnel, 0xf000beef);
|
|
|
|
// test many elements
|
|
pVoipTunnel->iNumClients = 8;
|
|
pVoipTunnel->ClientList[3].uClientId = 0xf000b00f;
|
|
pVoipTunnel->ClientList[4].uClientId = 0xf000baaf;
|
|
pVoipTunnel->ClientList[5].uClientId = 0xeaa0beef;
|
|
pVoipTunnel->ClientList[6].uClientId = 0x0000beef;
|
|
pVoipTunnel->ClientList[7].uClientId = 0x1000beef;
|
|
_VoipTunnelLookupBuild(pVoipTunnel);
|
|
_VoipTunnelLookupClient(pVoipTunnel, 0xdeadbeef);
|
|
_VoipTunnelLookupClient(pVoipTunnel, 0xcacabeef);
|
|
_VoipTunnelLookupClient(pVoipTunnel, 0xf000beef);
|
|
_VoipTunnelLookupClient(pVoipTunnel, 0xf000b00f);
|
|
_VoipTunnelLookupClient(pVoipTunnel, 0xf000baaf);
|
|
_VoipTunnelLookupClient(pVoipTunnel, 0xeaa0beef);
|
|
_VoipTunnelLookupClient(pVoipTunnel, 0x0000beef);
|
|
_VoipTunnelLookupClient(pVoipTunnel, 0x1000beef);
|
|
|
|
// reset
|
|
pVoipTunnel->iNumClients = 0;
|
|
pVoipTunnel->pLookupTable = NULL;
|
|
ds_memclr(pVoipTunnel->ClientList, sizeof(pVoipTunnel->ClientList));
|
|
}
|
|
#endif
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function _VoipTunnelReleaseVoipBroadcastingSlotForTargetClient
|
|
|
|
\Description
|
|
Releases the broadcasting slot that the client possesses as an originator
|
|
when sending voip to the target client (consumer).
|
|
|
|
\Input *pVoipTunnel - module state
|
|
\Input *pSourceClient - source client
|
|
\Input iTargetClientIndex - client index to remove
|
|
|
|
\Output
|
|
int32_t - updated sendmask for the source client.
|
|
|
|
\Notes
|
|
voice squelching: There are only 4 broadcasting slots per consumer client.
|
|
Only 4 originator clients at a time can send voip to a specific
|
|
consumer client.
|
|
|
|
\Version 02/01/2019 (amakoukji)
|
|
*/
|
|
/********************************************************************************F*/
|
|
static uint32_t _VoipTunnelReleaseVoipBroadcastingSlotForTargetClient(VoipTunnelRefT *pVoipTunnel, VoipTunnelClientT *pSourceClient, int32_t iTargetClientIndex)
|
|
{
|
|
// if this is a client we're talking to
|
|
if ((pSourceClient->uSendMask) & (1 << iTargetClientIndex))
|
|
{
|
|
VoipTunnelClientT *pTargetClient = VoipTunnelClientListMatchId(pVoipTunnel, pSourceClient->aClientIds[iTargetClientIndex]);
|
|
|
|
NetPrintfVerbose((pVoipTunnel->uDebugLevel, 1, "voiptunnel: 0x%08x is not talking to 0x%08x anymore.\n", pSourceClient->uClientId, pTargetClient ? pTargetClient->uClientId : 0xFFFFFFFF));
|
|
|
|
pSourceClient->uSendMask &= ~(1 << iTargetClientIndex);
|
|
if (pTargetClient)
|
|
{
|
|
pTargetClient->iNumTalker--;
|
|
if (pTargetClient->uFlags & VOIPTUNNEL_CLIENTFLAG_MAX_VOICES_REACHED)
|
|
{
|
|
pTargetClient->uFlags &= ~VOIPTUNNEL_CLIENTFLAG_MAX_VOICES_REACHED;
|
|
_VoipTunnelEventCallback(pVoipTunnel, VOIPTUNNEL_EVENT_AVLBVOICE, pTargetClient, pSourceClient->iGameIdx);
|
|
NetPrintfVerbose((pVoipTunnel->uDebugLevel, 1, "voiptunnel: 0x%08x is accepting new talkers again.\n", pTargetClient->uClientId));
|
|
}
|
|
}
|
|
}
|
|
|
|
return(pSourceClient->uSendMask);
|
|
}
|
|
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function _VoipTunnelAcquireVoipBroadcastingSlot
|
|
|
|
\Description
|
|
Allocates sendmask slots to the target clients if some are available.
|
|
|
|
\Input *pVoipTunnel - module state
|
|
\Input *pClient - client ref
|
|
\Input uTargetSendMask - the clients we want to send to
|
|
|
|
\Output
|
|
int32_t - acquired sendmask for the required client.
|
|
|
|
\Notes
|
|
voice squelching: There are only 4 broadcasting slots per consumer client.
|
|
Only 4 originator clients at a time can send voip to a specific
|
|
consumer client.
|
|
|
|
\Version 01/15/2010 (cvienneau)
|
|
*/
|
|
/********************************************************************************F*/
|
|
static uint32_t _VoipTunnelAcquireVoipBroadcastingSlot(VoipTunnelRefT *pVoipTunnel, VoipTunnelClientT *pClient, uint32_t uTargetSendMask)
|
|
{
|
|
int32_t iIndex;
|
|
uint32_t uAcquiredSendMask = pClient->uSendMask;
|
|
VoipTunnelGameT *pGame = &pVoipTunnel->pGameList[pClient->iGameIdx];
|
|
uint32_t uToReleaseMask = 0;
|
|
uint32_t uToAddMask = 0;
|
|
|
|
if (uAcquiredSendMask == uTargetSendMask)
|
|
{
|
|
return(uAcquiredSendMask);
|
|
}
|
|
|
|
// if there's one or more targets we no longer need, release
|
|
uToReleaseMask = (uAcquiredSendMask & (~uTargetSendMask));
|
|
if (uToReleaseMask != 0)
|
|
{
|
|
for (iIndex = 0; iIndex < VOIPTUNNEL_MAXGROUPSIZE; iIndex++)
|
|
{
|
|
// if this is a client we'd like to release but haven't yet
|
|
if (uToReleaseMask & (1 << iIndex))
|
|
{
|
|
uAcquiredSendMask = _VoipTunnelReleaseVoipBroadcastingSlotForTargetClient(pVoipTunnel, pClient, iIndex);
|
|
}
|
|
}
|
|
}
|
|
|
|
// if there's one or more target we have not yet acquired, try to acquire
|
|
uToAddMask = ((~uAcquiredSendMask) & uTargetSendMask);
|
|
if (uToAddMask != 0)
|
|
{
|
|
for (iIndex = 0; iIndex < VOIPTUNNEL_MAXGROUPSIZE; iIndex++)
|
|
{
|
|
// if this is a client we'd like to talk to, but don't already
|
|
if (uToAddMask & (1 << iIndex))
|
|
{
|
|
VoipTunnelClientT *pTargetClient = VoipTunnelClientListMatchId(pVoipTunnel, pClient->aClientIds[iIndex]);
|
|
|
|
if (pTargetClient)
|
|
{
|
|
if (pTargetClient->iNumTalker < pVoipTunnel->iMaxVoiceBroadcasters)
|
|
{
|
|
pTargetClient->iNumTalker++;
|
|
|
|
uAcquiredSendMask |= (1 << iIndex);
|
|
NetPrintfVerbose((pVoipTunnel->uDebugLevel, 1, "voiptunnel: 0x%08x is now talking to 0x%08x\n", pClient->uClientId, pTargetClient->uClientId));
|
|
}
|
|
else
|
|
{
|
|
// we've just reached the maximum number of clients allowed to send, send an event and set the flag
|
|
if (!(pTargetClient->uFlags & VOIPTUNNEL_CLIENTFLAG_MAX_VOICES_REACHED))
|
|
{
|
|
pTargetClient->uFlags |= VOIPTUNNEL_CLIENTFLAG_MAX_VOICES_REACHED;
|
|
pVoipTunnel->uVoiceMaxTalkersMetric++;
|
|
_VoipTunnelEventCallback(pVoipTunnel, VOIPTUNNEL_EVENT_MAXDVOICE, pTargetClient, pClient->iGameIdx);
|
|
|
|
NetPrintfVerbose((pVoipTunnel->uDebugLevel, 1, "voiptunnel: 0x%08x is not taking new talkers (will not get voice from 0x%08x)\n", pTargetClient->uClientId, pClient->uClientId));
|
|
}
|
|
|
|
pVoipTunnel->uVoiceDataDropMetric++;
|
|
pGame->uVoiceDataDropMetric++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return(uAcquiredSendMask);
|
|
}
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function _VoipTunnelReleaseAllVoipBroadcastingSlots
|
|
|
|
\Description
|
|
Releases all broadcasting slots that the client possesses as an originator.
|
|
|
|
\Input *pVoipTunnel - module state
|
|
\Input *pClient - client ref
|
|
|
|
\Notes
|
|
voice squelching: There are only 4 broadcasting slot per consumer clients.
|
|
Only 4 originator clients at a time can send voip to a specific
|
|
consumer client.
|
|
|
|
\Version 01/15/2010 (cvienneau)
|
|
*/
|
|
/********************************************************************************F*/
|
|
static void _VoipTunnelReleaseAllVoipBroadcastingSlots(VoipTunnelRefT *pVoipTunnel, VoipTunnelClientT *pClient)
|
|
{
|
|
int32_t iIndex;
|
|
|
|
for(iIndex = 0; iIndex < VOIPTUNNEL_MAXGROUPSIZE; iIndex++)
|
|
{
|
|
// if this is a client we're talking to
|
|
if ((pClient->uSendMask) & (1 << iIndex))
|
|
{
|
|
_VoipTunnelReleaseVoipBroadcastingSlotForTargetClient(pVoipTunnel, pClient, iIndex);
|
|
}
|
|
}
|
|
}
|
|
\
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function _VoipTunnelRouteVoipPacket
|
|
|
|
\Description
|
|
Routes incoming VoIP packet to appropriate destination(s).
|
|
|
|
\Input *pVoipTunnel - voip tunnel ref
|
|
\Input *pClient - source client
|
|
\Input *pPacket - voip packet
|
|
\Input iPacketSize - size of packet
|
|
\Input *pRecvAddr - source address
|
|
\Input uCurTick - current tick
|
|
\Input bVdpHeader - TRUE if there was a VDP header, else FALSE
|
|
|
|
\Version 03/27/2006 (jbrookes)
|
|
*/
|
|
/********************************************************************************F*/
|
|
static void _VoipTunnelRouteVoipPacket(VoipTunnelRefT *pVoipTunnel, VoipTunnelClientT *pClient, const VoipTunnelPacketT *pPacket, int32_t iPacketSize, struct sockaddr *pRecvAddr, uint32_t uCurTick, uint32_t bVdpHeader)
|
|
{
|
|
uint32_t uSendFlag = (bVdpHeader) ? VOIPTUNNEL_GAMEFLAG_VDP : 0;
|
|
|
|
// see if we have a packet type that is unicast
|
|
if (!memcmp(pPacket->Head.aType, "CO", 2) || !memcmp(pPacket->Head.aType, "PNG", 3) || !memcmp(pPacket->Head.aType, "DSC", 3))
|
|
{
|
|
uint32_t uRemoteClientId;
|
|
|
|
// extract voip packet version from connection packet if we haven't done so already
|
|
if ((pPacket->Head.aType[0] == 'C') && (pClient->uPacketVersion == 0))
|
|
{
|
|
pClient->uPacketVersion = pPacket->Head.aType[2];
|
|
NetPrintf(("voiptunnel: detected voip packet format '%c' from clientId=0x%08x\n", (char)pClient->uPacketVersion, pClient->uClientId));
|
|
}
|
|
|
|
// extract the remote client identifier
|
|
uRemoteClientId = pPacket->aRemoteClientId[0] << 24;
|
|
uRemoteClientId |= pPacket->aRemoteClientId[1] << 16;
|
|
uRemoteClientId |= pPacket->aRemoteClientId[2] << 8;
|
|
uRemoteClientId |= pPacket->aRemoteClientId[3];
|
|
|
|
NetPrintfVerbose((pVoipTunnel->uDebugLevel, pPacket->Head.aType[0] == 'P' ? 2 : 1, "voiptunnel: forwarding %d byte unicast packet of type %c%c%c from clientId=0x%08x to clientId=0x%08x\n",
|
|
iPacketSize, pPacket->Head.aType[0], pPacket->Head.aType[1], pPacket->Head.aType[2],
|
|
pClient->uClientId, uRemoteClientId));
|
|
|
|
// forward to other clients in our group
|
|
_VoipTunnelClientListSend(pVoipTunnel, pClient, uRemoteClientId, (const char *)pPacket, iPacketSize, pRecvAddr, uSendFlag|VOIPTUNNEL_GAMEFLAG_SEND_SINGLE);
|
|
}
|
|
else if (!memcmp(pPacket->Head.aType, "MIC", 3))
|
|
{
|
|
uint32_t uSendMask = (uint32_t)-1;
|
|
|
|
// update recv voice timestamp and mark that we are receiving voip mic data from this client
|
|
pClient->uLastRecvVoice = uCurTick;
|
|
if ((pClient->uFlags & VOIPTUNNEL_CLIENTFLAG_RECVVOICE) == 0)
|
|
{
|
|
pClient->uFlags |= VOIPTUNNEL_CLIENTFLAG_RECVVOICE;
|
|
pVoipTunnel->iNumTalkingClients++;
|
|
pVoipTunnel->pGameList[pClient->iGameIdx].iNumTalkingClients++;
|
|
}
|
|
|
|
|
|
// extract send mask if packet version supports it
|
|
if (pClient->uPacketVersion >= (unsigned)'c')
|
|
{
|
|
uSendMask = pPacket->aRemoteClientId[0] << 24;
|
|
uSendMask |= pPacket->aRemoteClientId[1] << 16;
|
|
uSendMask |= pPacket->aRemoteClientId[2] << 8;
|
|
uSendMask |= pPacket->aRemoteClientId[3];
|
|
}
|
|
|
|
// update the client we have permission to send to
|
|
pClient->uSendMask = _VoipTunnelAcquireVoipBroadcastingSlot(pVoipTunnel, pClient, uSendMask);
|
|
|
|
if (pClient->uSendMask != 0)
|
|
{
|
|
NetPrintfVerbose((pVoipTunnel->uDebugLevel, 1, "voiptunnel: broadcasting voip packet %d from clientId=0x%08x\n", pPacket->aRemoteClientId[0], pClient->uClientId));
|
|
|
|
// broadcast to all other clients in our group
|
|
_VoipTunnelClientListSend(pVoipTunnel, pClient, 0, (const char *)pPacket, iPacketSize, pRecvAddr, uSendFlag|VOIPTUNNEL_GAMEFLAG_BROADCAST_ALLOTHERS);
|
|
}
|
|
else
|
|
{
|
|
NetPrintfVerbose((pVoipTunnel->uDebugLevel, 1, "voiptunnel: failed to acquire voip broadcasting slot for voip packet %d from clientId=0x%08x\n", pPacket->aRemoteClientId[0], pClient->uClientId));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
NetPrintf(("voiptunnel: unknown VoIP packet type; ignoring\n"));
|
|
}
|
|
}
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function _VoipTunnelVoipRecvCallback
|
|
|
|
\Description
|
|
Callback handler for data received on the virtual Voip socket.
|
|
|
|
\Input *pSocket - pointer to socket
|
|
\Input iFlags - unused
|
|
\Input *_pRef - voiptunnel ref
|
|
|
|
\Output
|
|
int32_t - zero
|
|
|
|
\Version 03/27/2006 (jbrookes)
|
|
*/
|
|
/********************************************************************************F*/
|
|
static int32_t _VoipTunnelVoipRecvCallback(SocketT *pSocket, int32_t iFlags, void *_pRef)
|
|
{
|
|
VoipTunnelRefT *pVoipTunnel = (VoipTunnelRefT *)_pRef;
|
|
int32_t iAddrLen = sizeof(struct sockaddr), iRecvLen;
|
|
char aPacketData[SOCKET_MAXUDPRECV];
|
|
const VoipTunnelPacketT *pPacket;
|
|
VoipTunnelClientT *pClient;
|
|
struct sockaddr RecvAddr;
|
|
uint32_t uClientId, bVdpHeader=0;
|
|
uint32_t uCurTick = NetTick();
|
|
|
|
// got any input?
|
|
if ((iRecvLen = SocketRecvfrom(pSocket, aPacketData, sizeof(aPacketData), 0, &RecvAddr, &iAddrLen)) <= 0)
|
|
{
|
|
return(0);
|
|
}
|
|
|
|
// validate the packet
|
|
if ((pPacket = _VoipTunnelValidatePacket(pVoipTunnel, aPacketData, iRecvLen, &bVdpHeader)) == NULL)
|
|
{
|
|
return(0);
|
|
}
|
|
|
|
// extract client identifier from voip packet header
|
|
uClientId = _VoipTunnelGetClientId(pPacket, iRecvLen);
|
|
|
|
// if we don't have a client with this id yet, bail
|
|
if ((pClient = VoipTunnelClientListMatchId(pVoipTunnel, uClientId)) == NULL)
|
|
{
|
|
NetPrintf(("voiptunnel: ignoring '%c%c%c' voip packet from %a:%d with unregistered id=0x%08x\n",
|
|
pPacket->Head.aType[0], pPacket->Head.aType[1], pPacket->Head.aType[2],
|
|
SockaddrInGetAddr(&RecvAddr), SockaddrInGetPort(&RecvAddr), uClientId));
|
|
return(0);
|
|
}
|
|
|
|
// if the address isn't set yet, set it now
|
|
if (pClient->uRemoteAddr != (unsigned)SockaddrInGetAddr(&RecvAddr))
|
|
{
|
|
// get source client address
|
|
pClient->uRemoteAddr = SockaddrInGetAddr(&RecvAddr);
|
|
NetPrintf(("voiptunnel: matching clientId=0x%08x to addr %a\n", pClient->uClientId, pClient->uRemoteAddr));
|
|
_VoipTunnelEventCallback(pVoipTunnel, VOIPTUNNEL_EVENT_MATCHADDR, pClient, 0);
|
|
}
|
|
// if the voip port isn't set yet, set it now
|
|
if (pClient->uRemoteVoipPort != SockaddrInGetPort(&RecvAddr))
|
|
{
|
|
// get source client port
|
|
pClient->uRemoteVoipPort = SockaddrInGetPort(&RecvAddr);
|
|
NetPrintf(("voiptunnel: matching clientId=0x%08x to port %d\n", pClient->uClientId, pClient->uRemoteVoipPort));
|
|
_VoipTunnelEventCallback(pVoipTunnel, VOIPTUNNEL_EVENT_MATCHPORT, pClient, 0);
|
|
}
|
|
|
|
// call user callback
|
|
_VoipTunnelEventCallback(pVoipTunnel, VOIPTUNNEL_EVENT_RECVVOICE, pClient, iRecvLen);
|
|
|
|
// forward voip data to others in our group according to packet type
|
|
_VoipTunnelRouteVoipPacket(pVoipTunnel, pClient, pPacket, iRecvLen, &RecvAddr, uCurTick, bVdpHeader);
|
|
|
|
// update source client timestamp
|
|
pClient->uLastUpdate = uCurTick;
|
|
return(0);
|
|
}
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function _VoipTunnelPrintGameClientList
|
|
|
|
\Description
|
|
Prints the clients in the game
|
|
|
|
\Input *pVoipTunnel - voiptunnel ref
|
|
\Input iGameIdx - index of game print clients from
|
|
|
|
\Version 01/28/2016 (eesponda)
|
|
*/
|
|
/********************************************************************************F*/
|
|
static void _VoipTunnelPrintGameClientList(VoipTunnelRefT *pVoipTunnel, int32_t iGameIdx)
|
|
{
|
|
int32_t iClient;
|
|
const VoipTunnelGameT *pGame = &pVoipTunnel->pGameList[iGameIdx];
|
|
|
|
NetPrintf(("voiptunnel: game %d clients\n", iGameIdx));
|
|
for (iClient = 0; iClient < VOIPTUNNEL_MAXGROUPSIZE; iClient++)
|
|
{
|
|
if (pGame->aClientList[iClient] != 0)
|
|
{
|
|
NetPrintf(("voiptunnel: [%d] 0x%08x %s\n", iClient, pGame->aClientList[iClient], pGame->bClientActive[iClient] ? "ACTIVE" : "INACTIVE"));
|
|
}
|
|
}
|
|
}
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function _VoipTunnelClientGameDel
|
|
|
|
\Description
|
|
Remove client from the game list
|
|
|
|
\Input *pVoipTunnel - voiptunnel ref
|
|
\Input *pClient - client to remove
|
|
\Input iGameIdx - index of game to remove client from
|
|
|
|
\Output
|
|
int32_t - negative=error, else success
|
|
|
|
\Version 06/10/2007 (jbrookes)
|
|
*/
|
|
/********************************************************************************F*/
|
|
static int32_t _VoipTunnelClientGameDel(VoipTunnelRefT *pVoipTunnel, VoipTunnelClientT *pClient, int32_t iGameIdx)
|
|
{
|
|
// remove entry from game list
|
|
VoipTunnelGameT *pGame = &pVoipTunnel->pGameList[iGameIdx];
|
|
int32_t iGameClient;
|
|
|
|
// scan array to find client id
|
|
for (iGameClient = 0; iGameClient < VOIPTUNNEL_MAXGROUPSIZE; iGameClient++)
|
|
{
|
|
// found client?
|
|
if (pGame->aClientList[iGameClient] == pClient->uClientId)
|
|
{
|
|
NetPrintf(("voiptunnel: removing clientId=0x%08x from game %d\n", pClient->uClientId, iGameIdx));
|
|
|
|
// if the user was talking as they left, free up that broadcasting slot
|
|
_VoipTunnelReleaseAllVoipBroadcastingSlots(pVoipTunnel, pClient);
|
|
|
|
// expire recvvoice flag?
|
|
if (pClient->uFlags & VOIPTUNNEL_CLIENTFLAG_RECVVOICE)
|
|
{
|
|
pClient->uFlags &= ~VOIPTUNNEL_CLIENTFLAG_RECVVOICE;
|
|
pVoipTunnel->iNumTalkingClients--;
|
|
pVoipTunnel->pGameList[iGameIdx].iNumTalkingClients--;
|
|
|
|
// catch a potential error if this number goes below 0.
|
|
// this occured in GOS-31044 so we place this safeguard here
|
|
// if this occurs the metric can no longer be relied upon to be
|
|
// accurate but should still be close to the appropriate value,
|
|
// rather than negative, which will be problematic for the dashboard.
|
|
if (pVoipTunnel->iNumTalkingClients < 0)
|
|
{
|
|
NetPrintf(("voiptunnel: [%p] error tracking number of talking clients, game %d\n", pVoipTunnel, iGameIdx));
|
|
pVoipTunnel->iNumTalkingClients = 0;
|
|
}
|
|
}
|
|
|
|
// clear entry from game list
|
|
pGame->aClientList[iGameClient] = 0;
|
|
pGame->bClientActive[iGameClient] = FALSE;
|
|
|
|
// decrement client count
|
|
pGame->iNumClients -= 1;
|
|
// debug echo of game client list after delete
|
|
if (pVoipTunnel->uDebugLevel > 0)
|
|
{
|
|
_VoipTunnelPrintGameClientList(pVoipTunnel, iGameIdx);
|
|
}
|
|
// update send masks for other clients in this game
|
|
for (iGameClient = 0; iGameClient < VOIPTUNNEL_MAXGROUPSIZE; iGameClient++)
|
|
{
|
|
// make sure the client is active
|
|
if (pGame->bClientActive[iGameClient] == FALSE)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// ref the client
|
|
if ((pClient = VoipTunnelClientListMatchId(pVoipTunnel, pGame->aClientList[iGameClient])) != NULL)
|
|
{
|
|
// refresh send mask
|
|
VoipTunnelClientRefreshSendMask(pVoipTunnel, pClient);
|
|
}
|
|
}
|
|
return(0);
|
|
}
|
|
}
|
|
return(-1);
|
|
}
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function _VoipTunnelSuspendGame
|
|
|
|
\Description
|
|
Suspends the current active game
|
|
|
|
\Input *pVoipTunnel - voiptunnel ref
|
|
\Input *pClient - the client we are updating
|
|
|
|
\Output
|
|
int32_t - zero=success, else=failure
|
|
|
|
\Version 01/19/2016 (eesponda)
|
|
*/
|
|
/********************************************************************************F*/
|
|
static int32_t _VoipTunnelSuspendGame(VoipTunnelRefT *pVoipTunnel, VoipTunnelClientT *pClient)
|
|
{
|
|
int32_t iGameClient;
|
|
VoipTunnelSuspendInfoT *pSuspendInfo = &pClient->aSuspendedData[pClient->iNumSuspended];
|
|
VoipTunnelGameT *pGame = &pVoipTunnel->pGameList[pClient->iGameIdx];
|
|
|
|
if (pSuspendInfo->iGameIdx >= 0)
|
|
{
|
|
NetPrintf(("voiptunnel: invalid data in suspend information for client id=0x%08x game already suspended at this slot\n", pClient->uClientId));
|
|
return(-1);
|
|
}
|
|
|
|
// refcount the client
|
|
NetPrintf(("voiptunnel: refcounting client id=0x%08x suspending game %d\n", pClient->uClientId, pClient->iGameIdx));
|
|
|
|
// update suspended information
|
|
pSuspendInfo->iGameIdx = pClient->iGameIdx;
|
|
ds_strnzcpy(pSuspendInfo->strTunnelKey, pClient->strTunnelKey, sizeof(pSuspendInfo->strTunnelKey));
|
|
ds_memcpy_s(pSuspendInfo->aClientIds, sizeof(pSuspendInfo->aClientIds), pClient->aClientIds, sizeof(pClient->aClientIds));
|
|
pSuspendInfo->iNumClients = pClient->iNumClients;
|
|
pClient->iNumSuspended += 1;
|
|
|
|
// deactivate client in game for gamesend mask calculation
|
|
for (iGameClient = 0; iGameClient < VOIPTUNNEL_MAXGROUPSIZE; iGameClient += 1)
|
|
{
|
|
if (pGame->aClientList[iGameClient] == pClient->uClientId)
|
|
{
|
|
pGame->bClientActive[iGameClient] = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
// update send masks for other clients in this game (old)
|
|
for (iGameClient = 0; iGameClient < VOIPTUNNEL_MAXGROUPSIZE; iGameClient += 1)
|
|
{
|
|
VoipTunnelClientT *pClientInfo;
|
|
|
|
// make sure the client is active
|
|
if (pGame->bClientActive[iGameClient] == FALSE)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// ref the client
|
|
if ((pClientInfo = VoipTunnelClientListMatchId(pVoipTunnel, pGame->aClientList[iGameClient])) != NULL)
|
|
{
|
|
// refresh send mask
|
|
VoipTunnelClientRefreshSendMask(pVoipTunnel, pClientInfo);
|
|
}
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function _VoipTunnelUnsuspendGame
|
|
|
|
\Description
|
|
Takes the last suspended game for the client and makes it active
|
|
|
|
\Input *pVoipTunnel - voiptunnel ref
|
|
\Input *pClient - the client we are updating
|
|
|
|
\Version 01/19/2016 (eesponda)
|
|
*/
|
|
/********************************************************************************F*/
|
|
static void _VoipTunnelUnsuspendGame(VoipTunnelRefT *pVoipTunnel, VoipTunnelClientT *pClient)
|
|
{
|
|
int32_t iGameClient;
|
|
VoipTunnelSuspendInfoT *pSuspendInfo = &pClient->aSuspendedData[pClient->iNumSuspended - 1];
|
|
VoipTunnelGameT *pGame = &pVoipTunnel->pGameList[pSuspendInfo->iGameIdx];
|
|
|
|
NetPrintf(("voiptunnel: decrementing refcount client id=0x%08x unsuspending game %d\n", pClient->uClientId, pSuspendInfo->iGameIdx));
|
|
|
|
// make suspend information the active information
|
|
pClient->iGameIdx = pSuspendInfo->iGameIdx;
|
|
ds_strnzcpy(pClient->strTunnelKey, pSuspendInfo->strTunnelKey, sizeof(pClient->strTunnelKey));
|
|
ds_memcpy(pClient->aClientIds, pSuspendInfo->aClientIds, sizeof(pClient->aClientIds));
|
|
pClient->iNumClients = pSuspendInfo->iNumClients;
|
|
|
|
// active our client in new active game
|
|
for (iGameClient = 0; iGameClient < VOIPTUNNEL_MAXGROUPSIZE; iGameClient += 1)
|
|
{
|
|
// found client?
|
|
if (pGame->aClientList[iGameClient] == pClient->uClientId)
|
|
{
|
|
pGame->bClientActive[iGameClient] = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// update send masks for active clients
|
|
for (iGameClient = 0; iGameClient < VOIPTUNNEL_MAXGROUPSIZE; iGameClient += 1)
|
|
{
|
|
VoipTunnelClientT *pClientInfo;
|
|
|
|
if (pGame->bClientActive[iGameClient] == FALSE)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if ((pClientInfo = VoipTunnelClientListMatchId(pVoipTunnel, pGame->aClientList[iGameClient])) != NULL)
|
|
{
|
|
VoipTunnelClientRefreshSendMask(pVoipTunnel, pClientInfo);
|
|
}
|
|
}
|
|
|
|
// clear out the data
|
|
ds_memset(pSuspendInfo, -1, sizeof(*pSuspendInfo));
|
|
|
|
// update number of suspended games
|
|
pClient->iNumSuspended -= 1;
|
|
}
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function _VoipTunnelSocketOpen
|
|
|
|
\Description
|
|
Open a DirtySock socket.
|
|
|
|
\Input *pVoipTunnel - module state
|
|
\Input uPort - port to bind
|
|
\Input *pCallback - socket callback function
|
|
|
|
\Output
|
|
SocketT * - socket ref, or NULL
|
|
|
|
\Version 03/27/2006 (jbrookes)
|
|
*/
|
|
/********************************************************************************F*/
|
|
static SocketT *_VoipTunnelSocketOpen(VoipTunnelRefT *pVoipTunnel, uint16_t uPort, int32_t (*pCallback)(SocketT *pSocket, int32_t iFlags, void *pRef))
|
|
{
|
|
struct sockaddr BindAddr;
|
|
SocketT *pSocket;
|
|
int32_t iResult;
|
|
|
|
// open the socket
|
|
if ((pSocket = SocketOpen(AF_INET, SOCK_DGRAM, IPPROTO_IP)) == NULL)
|
|
{
|
|
NetPrintf(("voiptunnel: unable to open socket\n"));
|
|
return(NULL);
|
|
}
|
|
|
|
// bind socket to specified port
|
|
SockaddrInit(&BindAddr, AF_INET);
|
|
SockaddrInSetPort(&BindAddr, uPort);
|
|
if ((iResult = SocketBind(pSocket, &BindAddr, sizeof(BindAddr))) != SOCKERR_NONE)
|
|
{
|
|
NetPrintf(("voiptunnel: error %d binding to port\n", iResult));
|
|
SocketClose(pSocket);
|
|
return(NULL);
|
|
}
|
|
|
|
// set up for socket callback events
|
|
SocketCallback(pSocket, CALLB_RECV, 100, pVoipTunnel, pCallback);
|
|
|
|
// return ref to caller
|
|
return(pSocket);
|
|
}
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function _VoipTunnelClientListUpdate
|
|
|
|
\Description
|
|
Perform update processing on client list
|
|
|
|
\Input *pVoipTunnel - module state
|
|
|
|
\Version 09/18/2006 (jbrookes)
|
|
*/
|
|
/********************************************************************************F*/
|
|
static void _VoipTunnelClientListUpdate(VoipTunnelRefT *pVoipTunnel)
|
|
{
|
|
VoipTunnelClientT *pClient;
|
|
uint32_t uCurTick;
|
|
int32_t iClient;
|
|
|
|
// loop through clientlist
|
|
for (iClient = 0, uCurTick = NetTick(); iClient < pVoipTunnel->iNumClients; iClient++)
|
|
{
|
|
// ref client
|
|
pClient = &pVoipTunnel->ClientList[iClient];
|
|
|
|
// expire recvvoice flag?
|
|
if (pClient->uFlags & VOIPTUNNEL_CLIENTFLAG_RECVVOICE)
|
|
{
|
|
if (NetTickDiff(uCurTick, pClient->uLastRecvVoice) > (int32_t)pVoipTunnel->uVoiceRecvTimeout)
|
|
{
|
|
NetPrintfVerbose((pVoipTunnel->uDebugLevel, 1, "voiptunnel: stopped receiving voice from clientId=0x%08x\n", pClient->uClientId));
|
|
pClient->uFlags &= ~VOIPTUNNEL_CLIENTFLAG_RECVVOICE;
|
|
pVoipTunnel->iNumTalkingClients--;
|
|
pVoipTunnel->pGameList[pClient->iGameIdx].iNumTalkingClients--;
|
|
_VoipTunnelReleaseAllVoipBroadcastingSlots(pVoipTunnel, pClient);
|
|
|
|
// catch a potential error if this number goes below 0.
|
|
// this occured in GOS-31044 so we place this safeguard here
|
|
// if this occurs the metric can no longer be relied upon to be
|
|
// accurate but should still be close to the appropriate value,
|
|
// rather than negative, which will be problematic for the dashboard.
|
|
if (pVoipTunnel->iNumTalkingClients < 0)
|
|
{
|
|
NetPrintf(("voiptunnel: [%p] error tracking number of talking clients\n", pVoipTunnel));
|
|
pVoipTunnel->iNumTalkingClients = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* check for connection death (have previously received data, but have not received
|
|
data for at least the timeout period. timeout is client timeout plus five seconds
|
|
so that clients that are being removed from the game don't produce this warning */
|
|
if ((pClient->uRemoteVoipPort != 0) && ((signed)(uCurTick - pClient->uLastUpdate) > 15*1000))
|
|
{
|
|
NetPrintf(("voiptunnel: clientId=0x%08x voice connection has gone dead\n", pClient->uClientId));
|
|
/* clear voip point so 1) we don't send to them 2) we don't warn again and 3) in
|
|
the unlikely event data starts flowing again somehow, we get notified by a new
|
|
match event */
|
|
pClient->uRemoteVoipPort = 0;
|
|
_VoipTunnelEventCallback(pVoipTunnel, VOIPTUNNEL_EVENT_DEADVOICE, pClient, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*** Public functions *************************************************************/
|
|
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function VoipTunnelCreate
|
|
|
|
\Description
|
|
Create the VoipTunnel module.
|
|
|
|
\Input uVoipPort - local virtual voip port
|
|
\Input iMaxClients - max number of clients allowed
|
|
\Input iMaxGames - max number of games allowed
|
|
|
|
\Output
|
|
VoipTunnelRefT * - new module state pointer, or null
|
|
|
|
\Version 04/06/2006 (jbrookes)
|
|
*/
|
|
/********************************************************************************F*/
|
|
VoipTunnelRefT *VoipTunnelCreate(uint32_t uVoipPort, int32_t iMaxClients, int32_t iMaxGames)
|
|
{
|
|
VoipTunnelRefT *pVoipTunnel;
|
|
int32_t iMemSize = sizeof(*pVoipTunnel) + (sizeof(pVoipTunnel->ClientList[0]) * (iMaxClients - 1));
|
|
int32_t iGameListSize;
|
|
int32_t iMemGroup;
|
|
void *pMemGroupUserData;
|
|
|
|
// Query current mem group data
|
|
DirtyMemGroupQuery(&iMemGroup, &pMemGroupUserData);
|
|
|
|
// allocate and init module state
|
|
if ((pVoipTunnel = DirtyMemAlloc(iMemSize, VOIPTUNNEL_MEMID, iMemGroup, pMemGroupUserData)) == NULL)
|
|
{
|
|
NetPrintf(("voiptunnel: could not allocate module state\n"));
|
|
return(NULL);
|
|
}
|
|
ds_memclr(pVoipTunnel, iMemSize);
|
|
pVoipTunnel->iMemGroup = iMemGroup;
|
|
pVoipTunnel->pMemGroupUserData = pMemGroupUserData;
|
|
pVoipTunnel->uVoipPort = uVoipPort;
|
|
pVoipTunnel->iMaxClients = iMaxClients;
|
|
pVoipTunnel->iMaxGames = iMaxGames;
|
|
pVoipTunnel->iMaxVoiceBroadcasters = VOIPTUNNEL_MAX_BROADCASTING_VOICES_DEFAULT;
|
|
pVoipTunnel->uVoiceRecvTimeout = VOIPTUNNEL_RECVVOICE_TIMEOUT_DEFAULT;
|
|
|
|
// test lookup table
|
|
#if VOIPTUNNEL_LOOKUP_TEST
|
|
_VoipTunnelLookupTest(pVoipTunnel);
|
|
#endif
|
|
|
|
// allocate game list
|
|
iGameListSize = sizeof(*pVoipTunnel->pGameList) * iMaxGames;
|
|
if ((pVoipTunnel->pGameList = DirtyMemAlloc(iGameListSize, VOIPTUNNEL_MEMID, pVoipTunnel->iMemGroup, pVoipTunnel->pMemGroupUserData)) == NULL)
|
|
{
|
|
NetPrintf(("voiptunnel: unable to allocate game list\n"));
|
|
// destroy anything that was started
|
|
VoipTunnelDestroy(pVoipTunnel);
|
|
return(NULL);
|
|
}
|
|
ds_memset(pVoipTunnel->pGameList, -1, iGameListSize);
|
|
|
|
// allocate fast lookup table if warranted
|
|
if (iMaxClients >= 32)
|
|
{
|
|
if ((pVoipTunnel->pLookupTable = DirtyMemAlloc(sizeof(*pVoipTunnel->pLookupTable) * iMaxClients, VOIPTUNNEL_MEMID, pVoipTunnel->iMemGroup, pVoipTunnel->pMemGroupUserData)) == NULL)
|
|
{
|
|
NetPrintf(("voiptunnel: unable to allocate fast lookup table\n"));
|
|
}
|
|
}
|
|
|
|
// create voip socket
|
|
if ((pVoipTunnel->pVoipSocket = _VoipTunnelSocketOpen(pVoipTunnel, uVoipPort, _VoipTunnelVoipRecvCallback)) == NULL)
|
|
{
|
|
// destroy anything that was started
|
|
VoipTunnelDestroy(pVoipTunnel);
|
|
return(NULL);
|
|
}
|
|
|
|
// return ref to caller
|
|
return(pVoipTunnel);
|
|
}
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function VoipTunnelCallback
|
|
|
|
\Description
|
|
Set optional voiptunnel event callback
|
|
|
|
\Input *pVoipTunnel - voiptunnel ref
|
|
\Input *pCallback - callback pointer
|
|
\Input *pUserData - callback user data
|
|
|
|
\Version 03/24/2006 (jbrookes)
|
|
*/
|
|
/********************************************************************************F*/
|
|
void VoipTunnelCallback(VoipTunnelRefT *pVoipTunnel, VoipTunnelCallbackT *pCallback, void *pUserData)
|
|
{
|
|
pVoipTunnel->pCallback = pCallback;
|
|
pVoipTunnel->pUserData = pUserData;
|
|
}
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function VoipTunnelDestroy
|
|
|
|
\Description
|
|
Shutdown this VoipTunnel.
|
|
|
|
\Input *pVoipTunnel - voiptunnel ref
|
|
|
|
\Version 03/24/2006 (jbrookes)
|
|
*/
|
|
/********************************************************************************F*/
|
|
void VoipTunnelDestroy(VoipTunnelRefT *pVoipTunnel)
|
|
{
|
|
// destroy lookup table
|
|
if (pVoipTunnel->pLookupTable != NULL)
|
|
{
|
|
DirtyMemFree(pVoipTunnel->pLookupTable, VOIPTUNNEL_MEMID, pVoipTunnel->iMemGroup, pVoipTunnel->pMemGroupUserData);
|
|
}
|
|
// destroy game list
|
|
if (pVoipTunnel->pGameList != NULL)
|
|
{
|
|
DirtyMemFree(pVoipTunnel->pGameList, VOIPTUNNEL_MEMID, pVoipTunnel->iMemGroup, pVoipTunnel->pMemGroupUserData);
|
|
}
|
|
// destroy virtual voip socket
|
|
if (pVoipTunnel->pVoipSocket != NULL)
|
|
{
|
|
SocketClose(pVoipTunnel->pVoipSocket);
|
|
}
|
|
// dispose of module memory
|
|
DirtyMemFree(pVoipTunnel, VOIPTUNNEL_MEMID, pVoipTunnel->iMemGroup, pVoipTunnel->pMemGroupUserData);
|
|
}
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function VoipTunnelClientListAdd
|
|
|
|
\Description
|
|
Add a new client to the client list
|
|
|
|
\Input *pVoipTunnel - voiptunnel ref
|
|
\Input *pClientInfo - new client info
|
|
\Input **ppNewClient - [out] new client pointer (optional; can be NULL)
|
|
|
|
\Output
|
|
int32_t - negative=error, else success
|
|
|
|
\Version 03/27/2006 (jbrookes)
|
|
*/
|
|
/********************************************************************************F*/
|
|
int32_t VoipTunnelClientListAdd(VoipTunnelRefT *pVoipTunnel, const VoipTunnelClientT *pClientInfo, VoipTunnelClientT **ppNewClient)
|
|
{
|
|
return(VoipTunnelClientListAdd2(pVoipTunnel, pClientInfo, ppNewClient, 0));
|
|
}
|
|
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function VoipTunnelClientListAdd2
|
|
|
|
\Description
|
|
Add a new client to the client list
|
|
|
|
\Input *pVoipTunnel - voiptunnel ref, at the first empty spot past a given index
|
|
\Input *pClientInfo - new client info
|
|
\Input **ppNewClient - [out] new client pointer (optional; can be NULL)
|
|
\Input iStartIndex - index to add at
|
|
|
|
\Output
|
|
int32_t - negative=error, else success
|
|
|
|
\Version 10/18/2011 (jrainy)
|
|
*/
|
|
/********************************************************************************F*/
|
|
int32_t VoipTunnelClientListAdd2(VoipTunnelRefT *pVoipTunnel, const VoipTunnelClientT *pClientInfo, VoipTunnelClientT **ppNewClient, int32_t iStartIndex)
|
|
{
|
|
VoipTunnelClientT *pNewClient;
|
|
VoipTunnelGameT *pGame;
|
|
int32_t iGameClient;
|
|
|
|
// make sure there's room in the list
|
|
if (pVoipTunnel->iNumClients >= pVoipTunnel->iMaxClients)
|
|
{
|
|
NetPrintf(("voiptunnel: max client limit exceeded\n"));
|
|
return(-1);
|
|
}
|
|
|
|
// make sure game index is valid
|
|
if (pClientInfo->iGameIdx >= pVoipTunnel->iMaxGames)
|
|
{
|
|
NetPrintf(("voiptunnel: invalid game index %d\n", pClientInfo->iGameIdx));
|
|
return(-2);
|
|
}
|
|
|
|
// make sure game has been initialized and there's room in the game
|
|
pGame = &pVoipTunnel->pGameList[pClientInfo->iGameIdx];
|
|
if (pGame->iNumClients < 0)
|
|
{
|
|
NetPrintf(("voiptunnel: could not add clientId=0x%08x to game %d when game has not been initialized\n", pClientInfo->uClientId, pClientInfo->iGameIdx));
|
|
return(-3);
|
|
}
|
|
if (pGame->iNumClients >= VOIPTUNNEL_MAXGROUPSIZE)
|
|
{
|
|
NetPrintf(("voiptunnel: max game size exceeded trying to add clientId=0x%08x to game %d\n", pClientInfo->uClientId, pClientInfo->iGameIdx));
|
|
return(-4);
|
|
}
|
|
|
|
// make sure client does not already appear in the game list
|
|
for (iGameClient = 0; iGameClient < VOIPTUNNEL_MAXGROUPSIZE; iGameClient++)
|
|
{
|
|
if (pClientInfo->uClientId == pGame->aClientList[iGameClient])
|
|
{
|
|
NetPrintf(("voiptunnel: could not add clientId=0x%08x to game %d when client is already in the game at index %d\n", pClientInfo->uClientId, pClientInfo->iGameIdx, iGameClient));
|
|
return(-5);
|
|
}
|
|
}
|
|
|
|
if (iStartIndex < 0)
|
|
{
|
|
NetPrintf(("voiptunnel: could not accept negative start index %d\n", iStartIndex));
|
|
return(-7);
|
|
}
|
|
|
|
// check for clients that are already tracked by the voiptunnel
|
|
if ((pNewClient = VoipTunnelClientListMatchId(pVoipTunnel, pClientInfo->uClientId)) == NULL)
|
|
{
|
|
// allocate new entry
|
|
NetPrintf(("voiptunnel: adding new client id=0x%08x\n", pClientInfo->uClientId));
|
|
pNewClient = &pVoipTunnel->ClientList[pVoipTunnel->iNumClients];
|
|
|
|
// initialize new entry
|
|
ds_memcpy_s(pNewClient, sizeof(*pNewClient), pClientInfo, sizeof(*pClientInfo));
|
|
|
|
// default send mask to all disabled
|
|
// at every packet received, this will be recomputed
|
|
pNewClient->uSendMask = 0;
|
|
pNewClient->iNumTalker = 0;
|
|
|
|
// increment client counts
|
|
pVoipTunnel->iNumClients += 1;
|
|
|
|
// initialize suspended data
|
|
ds_memset(pNewClient->aSuspendedData, -1, sizeof(pNewClient->aSuspendedData));
|
|
}
|
|
else if (pNewClient->iNumSuspended >= VOIPTUNNEL_MAXSUSPENDED)
|
|
{
|
|
NetPrintf(("voiptunnel: no more space to suspend game %d for clientId=0x%08x\n", pNewClient->uClientId, pClientInfo->iGameIdx));
|
|
return(-8);
|
|
}
|
|
else
|
|
{
|
|
if (_VoipTunnelSuspendGame(pVoipTunnel, pNewClient) == 0)
|
|
{
|
|
// update current information
|
|
pNewClient->iGameIdx = pClientInfo->iGameIdx;
|
|
ds_strnzcpy(pNewClient->strTunnelKey, pClientInfo->strTunnelKey, sizeof(pNewClient->strTunnelKey));
|
|
ds_memclr(pNewClient->aClientIds, sizeof(pNewClient->aClientIds)); // reset the ids so it can be updated later
|
|
pNewClient->iNumClients = 0;
|
|
}
|
|
else
|
|
{
|
|
// we couldn't suspend, treat as failure
|
|
return(-9);
|
|
}
|
|
}
|
|
|
|
// add client to game list in first available slot
|
|
for (iGameClient = iStartIndex; (iGameClient < VOIPTUNNEL_MAXGROUPSIZE) && (pGame->aClientList[iGameClient] != 0); iGameClient++)
|
|
;
|
|
if (iGameClient < VOIPTUNNEL_MAXGROUPSIZE)
|
|
{
|
|
pGame->aClientList[iGameClient] = pNewClient->uClientId;
|
|
pGame->bClientActive[iGameClient] = TRUE;
|
|
}
|
|
else
|
|
{
|
|
NetPrintf(("voiptunnel: could not add clientId=0x%08x to game list for game %d\n", pNewClient->uClientId, pClientInfo->iGameIdx));
|
|
return(-6);
|
|
}
|
|
|
|
// increment game counts
|
|
pGame->iNumClients += 1;
|
|
|
|
// debug echo of game client list after add
|
|
if (pVoipTunnel->uDebugLevel > 0)
|
|
{
|
|
_VoipTunnelPrintGameClientList(pVoipTunnel, pNewClient->iGameIdx);
|
|
}
|
|
|
|
// rebuild lookup table
|
|
_VoipTunnelLookupBuild(pVoipTunnel);
|
|
|
|
// notify application of add
|
|
_VoipTunnelEventCallback(pVoipTunnel, VOIPTUNNEL_EVENT_ADDCLIENT, pNewClient, 0);
|
|
|
|
// write back new client pointer
|
|
if (ppNewClient != NULL)
|
|
{
|
|
*ppNewClient = pNewClient;
|
|
}
|
|
// return success
|
|
return(0);
|
|
}
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function VoipTunnelClientListDel
|
|
|
|
\Description
|
|
Remove client from the client list given the context of a game
|
|
|
|
\Input *pVoipTunnel - voiptunnel ref
|
|
\Input *pClient - client to remove
|
|
\Input iGameIdx - index of the game we are removing client from
|
|
|
|
\Version 04/10/2006 (jbrookes)
|
|
*/
|
|
/********************************************************************************F*/
|
|
void VoipTunnelClientListDel(VoipTunnelRefT *pVoipTunnel, VoipTunnelClientT *pClient, int32_t iGameIdx)
|
|
{
|
|
int32_t iGame = -1;
|
|
// get client index from client pointer
|
|
int32_t iClient = (int32_t)(pClient - pVoipTunnel->ClientList);
|
|
|
|
// make sure index is valid
|
|
if ((iClient < 0) || (iClient >= pVoipTunnel->iMaxClients))
|
|
{
|
|
NetPrintf(("voiptunnel: invalid client index %d specified for deletion\n", iClient));
|
|
return;
|
|
}
|
|
|
|
// if invalid then take the active game index from the client
|
|
if (iGameIdx < 0)
|
|
{
|
|
iGameIdx = pClient->iGameIdx;
|
|
}
|
|
|
|
// if we are not removing from the active game then we have to
|
|
// make sure to find the game in the suspended data
|
|
if (pClient->iGameIdx != iGameIdx)
|
|
{
|
|
for (iGame = 0; iGame < pClient->iNumSuspended; iGame += 1)
|
|
{
|
|
if (pClient->aSuspendedData[iGame].iGameIdx == iGameIdx)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
if (iGame == pClient->iNumSuspended)
|
|
{
|
|
NetPrintfVerbose((pVoipTunnel->uDebugLevel, 1, "voiptunnel: could not find game %d for client 0x%08x suspended data\n", iGameIdx, pClient->uClientId));
|
|
return;
|
|
}
|
|
}
|
|
|
|
// notify application of upcoming deletion to allow for deletion of any associate resources
|
|
// pass in the slot (non-zero based) to signal if we are removing active/inactive game
|
|
_VoipTunnelEventCallback(pVoipTunnel, VOIPTUNNEL_EVENT_DELCLIENT, pClient, iGame+1);
|
|
|
|
// remove entry from game
|
|
if (_VoipTunnelClientGameDel(pVoipTunnel, pClient, iGameIdx) < 0)
|
|
{
|
|
NetPrintf(("voiptunnel: could not find clientId=0x%08x in game %d\n", pClient->uClientId, pClient->iGameIdx));
|
|
}
|
|
|
|
// if deleting from the client's active game use the normal delete logic
|
|
if (pClient->iGameIdx == iGameIdx)
|
|
{
|
|
// unsuspend a game if any are available
|
|
if (pClient->iNumSuspended != 0)
|
|
{
|
|
_VoipTunnelUnsuspendGame(pVoipTunnel, pClient);
|
|
return;
|
|
}
|
|
|
|
// remove entry from client list
|
|
if (iClient != (pVoipTunnel->iNumClients - 1))
|
|
{
|
|
int32_t iNumMoveClients = (pVoipTunnel->iNumClients - 1) - iClient;
|
|
|
|
// move the clients to remove the gap
|
|
memmove(pClient, pClient + 1, iNumMoveClients * sizeof(VoipTunnelClientT));
|
|
}
|
|
// decrement count
|
|
pVoipTunnel->iNumClients -= 1;
|
|
|
|
// rebuild lookup table
|
|
_VoipTunnelLookupBuild(pVoipTunnel);
|
|
}
|
|
// otherwise remove the suspended game from being tracked
|
|
else
|
|
{
|
|
// only remove the gap if not at the end
|
|
if (iGame != (pClient->iNumSuspended - 1))
|
|
{
|
|
int32_t iNumMove = (pClient->iNumSuspended - 1) - iGame;
|
|
|
|
// move the information to remove the gap
|
|
memmove(&pClient->aSuspendedData[iGame], &pClient->aSuspendedData[iGame+1], iNumMove * sizeof(pClient->aSuspendedData[0]));
|
|
}
|
|
else
|
|
{
|
|
/* since we are removing from the end (the slot is not being overwritten), we need to clear out the data so
|
|
further logic does not believe we are suspended at this slot */
|
|
ds_memset(&pClient->aSuspendedData[pClient->iNumSuspended - 1], -1, sizeof(*pClient->aSuspendedData));
|
|
}
|
|
// decrement count
|
|
pClient->iNumSuspended -= 1;
|
|
}
|
|
}
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function VoipTunnelGameListAdd
|
|
|
|
\Description
|
|
Add a new game.
|
|
|
|
\Input *pVoipTunnel - voiptunnel ref
|
|
\Input iGameIdx - index of game whose clients are to be removed
|
|
|
|
\Output
|
|
int32_t - negative=error, else success
|
|
|
|
\Version 04/27/2006 (jbrookes)
|
|
*/
|
|
/********************************************************************************F*/
|
|
int32_t VoipTunnelGameListAdd(VoipTunnelRefT *pVoipTunnel, int32_t iGameIdx)
|
|
{
|
|
return(VoipTunnelGameListAdd2(pVoipTunnel, iGameIdx, 0));
|
|
}
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function VoipTunnelGameListAdd2
|
|
|
|
\Description
|
|
Add a new game.
|
|
|
|
\Input *pVoipTunnel - voiptunnel ref
|
|
\Input iGameIdx - index of game whose clients are to be removed
|
|
\Input uGameId - unique game identifier
|
|
|
|
\Output
|
|
int32_t - negative=error, else success
|
|
|
|
\Version 12/01/2015 (eesponda)
|
|
*/
|
|
/********************************************************************************F*/
|
|
int32_t VoipTunnelGameListAdd2(VoipTunnelRefT *pVoipTunnel, int32_t iGameIdx, uint32_t uGameId)
|
|
{
|
|
if (pVoipTunnel->iNumGames >= pVoipTunnel->iMaxGames)
|
|
{
|
|
NetPrintf(("voiptunnel: game list overflow -- could not add game\n"));
|
|
return(-1);
|
|
}
|
|
if (pVoipTunnel->pGameList[iGameIdx].iNumClients != -1)
|
|
{
|
|
NetPrintf(("voiptunnel: refusing game %d add request; game is already active\n", iGameIdx));
|
|
return(-2);
|
|
}
|
|
pVoipTunnel->iNumGames++;
|
|
ds_memclr(&pVoipTunnel->pGameList[iGameIdx], sizeof(pVoipTunnel->pGameList[0]));
|
|
pVoipTunnel->pGameList[iGameIdx].uGameId = uGameId;
|
|
|
|
// notify application of add
|
|
_VoipTunnelEventCallback(pVoipTunnel, VOIPTUNNEL_EVENT_ADDGAME, NULL, iGameIdx);
|
|
|
|
return(0);
|
|
}
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function VoipTunnelGameListDel
|
|
|
|
\Description
|
|
Remove all clients from the client list who have the given GameServer index
|
|
|
|
\Input *pVoipTunnel - voiptunnel ref
|
|
\Input iGameIdx - index of game whose clients are to be removed
|
|
|
|
\Output
|
|
int32_t - number of clients deleted
|
|
|
|
\Version 04/27/2006 (jbrookes)
|
|
*/
|
|
/********************************************************************************F*/
|
|
int32_t VoipTunnelGameListDel(VoipTunnelRefT *pVoipTunnel, int32_t iGameIdx)
|
|
{
|
|
int32_t iClient, iClientsDeleted;
|
|
if( iGameIdx < pVoipTunnel->iMaxGames)
|
|
{
|
|
// iterate through client list, and delete all clients associated with iGameServer
|
|
for (iClient = 0, iClientsDeleted = 0; iClient < pVoipTunnel->iNumClients; )
|
|
{
|
|
VoipTunnelClientT *pClient = &pVoipTunnel->ClientList[iClient];
|
|
// not a client we're looking for?
|
|
if (pClient->iGameIdx == iGameIdx)
|
|
{
|
|
// delete client from list
|
|
VoipTunnelClientListDel(pVoipTunnel, pClient, iGameIdx);
|
|
|
|
// increment deleted count
|
|
iClientsDeleted += 1;
|
|
}
|
|
else
|
|
{
|
|
// delete client from list (suspended)
|
|
VoipTunnelClientListDel(pVoipTunnel, pClient, iGameIdx);
|
|
|
|
// move to next client as when in an inactive game a client is not removed
|
|
iClient++;
|
|
}
|
|
}
|
|
|
|
// notify application of upcoming deletion to allow for deletion of any associated resources
|
|
_VoipTunnelEventCallback(pVoipTunnel, VOIPTUNNEL_EVENT_DELGAME, NULL, iGameIdx);
|
|
|
|
// wipe game and mark as unallocated
|
|
ds_memclr(&pVoipTunnel->pGameList[iGameIdx], sizeof(pVoipTunnel->pGameList[0]));
|
|
pVoipTunnel->pGameList[iGameIdx].iNumClients = -1;
|
|
pVoipTunnel->iNumGames--;
|
|
}
|
|
else
|
|
{
|
|
iClientsDeleted = 0;
|
|
NetPrintf(("voiptunnel: skipping delete of game %d, out of bounds.\n", iGameIdx));
|
|
}
|
|
|
|
// return number of clients deleted
|
|
return(iClientsDeleted);
|
|
}
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function VoipTunnelClientListMatchAddr
|
|
|
|
\Description
|
|
Return client matching given address.
|
|
|
|
\Input *pVoipTunnel - voiptunnel ref
|
|
\Input uRemoteAddr - address incoming packet originated from
|
|
|
|
\Output
|
|
VoipTunnelClientT * - pointer to new client, or NULL
|
|
|
|
\Version 03/27/2006 (jbrookes)
|
|
*/
|
|
/********************************************************************************F*/
|
|
VoipTunnelClientT *VoipTunnelClientListMatchAddr(VoipTunnelRefT *pVoipTunnel, uint32_t uRemoteAddr)
|
|
{
|
|
VoipTunnelClientT *pClient;
|
|
int32_t iClientId;
|
|
|
|
// iterate through client list and look for a match
|
|
for (iClientId = 0, pClient = &pVoipTunnel->ClientList[iClientId]; iClientId < pVoipTunnel->iNumClients; iClientId++, pClient++)
|
|
{
|
|
// is there a match?
|
|
if (pClient->uRemoteAddr == uRemoteAddr)
|
|
{
|
|
return(pClient);
|
|
}
|
|
}
|
|
|
|
// no match
|
|
return(NULL);
|
|
}
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function VoipTunnelClientListMatchId
|
|
|
|
\Description
|
|
Return client matching given address.
|
|
|
|
\Input *pVoipTunnel - voiptunnel ref
|
|
\Input uClientId - unique client identifier
|
|
|
|
\Output
|
|
VoipTunnelClientT * - pointer to matching client, or NULL
|
|
|
|
\Version 03/27/2006 (jbrookes)
|
|
*/
|
|
/********************************************************************************F*/
|
|
VoipTunnelClientT *VoipTunnelClientListMatchId(VoipTunnelRefT *pVoipTunnel, uint32_t uClientId)
|
|
{
|
|
VoipTunnelClientT *pClient;
|
|
int32_t iClient;
|
|
|
|
// ignore invalid clientId
|
|
if (uClientId == 0)
|
|
{
|
|
return(NULL);
|
|
}
|
|
|
|
// use lookup?
|
|
if (pVoipTunnel->pLookupTable != NULL)
|
|
{
|
|
pClient = _VoipTunnelLookupClient(pVoipTunnel, uClientId);
|
|
}
|
|
else
|
|
{
|
|
// iterate through client list and look for a match
|
|
for (iClient = 0, pClient = NULL; iClient < pVoipTunnel->iNumClients; iClient++)
|
|
{
|
|
// is there a match?
|
|
if (pVoipTunnel->ClientList[iClient].uClientId == uClientId)
|
|
{
|
|
pClient = &pVoipTunnel->ClientList[iClient];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// return client to caller, or NULL if no match
|
|
return(pClient);
|
|
}
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function VoipTunnelClientListMatchIndex
|
|
|
|
\Description
|
|
Return client at the given index.
|
|
|
|
\Input *pVoipTunnel - voiptunnel ref
|
|
\Input uClientIndex - index of client in client list
|
|
|
|
\Output
|
|
VoipTunnelClientT * - pointer to client, or NULL
|
|
|
|
\Version 03/13/2007 (jbrookes)
|
|
*/
|
|
/********************************************************************************F*/
|
|
VoipTunnelClientT *VoipTunnelClientListMatchIndex(VoipTunnelRefT *pVoipTunnel, uint32_t uClientIndex)
|
|
{
|
|
VoipTunnelClientT *pClient = NULL;
|
|
if (uClientIndex < (unsigned)pVoipTunnel->iNumClients)
|
|
{
|
|
pClient = &pVoipTunnel->ClientList[uClientIndex];
|
|
}
|
|
return(pClient);
|
|
}
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function VoipTunnelClientListMatchSockaddr
|
|
|
|
\Description
|
|
Return client matching the address and port in the specified sockaddr
|
|
|
|
\Input *pVoipTunnel - voiptunnel ref
|
|
\Input *pSockaddr - sockaddr containing addr and port to match
|
|
|
|
\Output
|
|
VoipTunnelClientT * - pointer to matching client, or NULL
|
|
|
|
\Version 03/13/2007 (jbrookes)
|
|
*/
|
|
/********************************************************************************F*/
|
|
VoipTunnelClientT *VoipTunnelClientListMatchSockaddr(VoipTunnelRefT *pVoipTunnel, struct sockaddr *pSockaddr)
|
|
{
|
|
VoipTunnelClientT *pClient;
|
|
int32_t iClientId;
|
|
uint32_t uAddr = SockaddrInGetAddr(pSockaddr);
|
|
uint16_t uPort = SockaddrInGetPort(pSockaddr);
|
|
|
|
// iterate through client list and look for a match
|
|
for (iClientId = 0, pClient = &pVoipTunnel->ClientList[iClientId]; iClientId < pVoipTunnel->iNumClients; iClientId++, pClient++)
|
|
{
|
|
// is there a match?
|
|
if ((pClient->uRemoteAddr == uAddr) && (pClient->uRemoteGamePort == uPort))
|
|
{
|
|
return(pClient);
|
|
}
|
|
}
|
|
|
|
// no match
|
|
return(NULL);
|
|
}
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function VoipTunnelClientListMatchFunc
|
|
|
|
\Description
|
|
Return client matching criteria of matching function
|
|
|
|
\Input *pVoipTunnel - voiptunnel ref
|
|
\Input *pMatchFunc - function to call to indicate if we have a match or not
|
|
\Input *pUserData - user data to pass to match function
|
|
|
|
\Output
|
|
VoipTunnelClientT * - pointer to found client, or NULL
|
|
|
|
\Version 04/10/2007 (jbrookes)
|
|
*/
|
|
/********************************************************************************F*/
|
|
VoipTunnelClientT *VoipTunnelClientListMatchFunc(VoipTunnelRefT *pVoipTunnel, VoipTunnelMatchFuncT *pMatchFunc, void *pUserData)
|
|
{
|
|
VoipTunnelClientT *pClient;
|
|
int32_t iClientId;
|
|
|
|
// iterate through client list and look for a match
|
|
for (iClientId = 0, pClient = &pVoipTunnel->ClientList[iClientId]; iClientId < pVoipTunnel->iNumClients; iClientId++, pClient++)
|
|
{
|
|
// is there a match?
|
|
if (pMatchFunc(pClient, pUserData) == 0)
|
|
{
|
|
return(pClient);
|
|
}
|
|
}
|
|
|
|
// no match
|
|
return(NULL);
|
|
}
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function VoipTunnelGameListMatchIndex
|
|
|
|
\Description
|
|
Return game at the given index.
|
|
|
|
\Input *pVoipTunnel - voiptunnel ref
|
|
\Input iGameIdx - index of game in game list
|
|
|
|
\Output
|
|
VoipTunnelGameT * - pointer to game, or NULL
|
|
|
|
\Version 12/01/2015 (eesponda)
|
|
*/
|
|
/********************************************************************************F*/
|
|
VoipTunnelGameT *VoipTunnelGameListMatchIndex(VoipTunnelRefT *pVoipTunnel, int32_t iGameIdx)
|
|
{
|
|
VoipTunnelGameT *pGame = NULL;
|
|
if (iGameIdx < pVoipTunnel->iMaxGames)
|
|
{
|
|
pGame = &pVoipTunnel->pGameList[iGameIdx];
|
|
}
|
|
return(pGame);
|
|
}
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function VoipTunnelGameListMatchId
|
|
|
|
\Description
|
|
Return game matching given game identifier.
|
|
|
|
\Input *pVoipTunnel - voiptunnel ref
|
|
\Input uGameId - unique game identifier
|
|
\Input *pGameIdx - [out] index the game was at
|
|
|
|
\Output
|
|
VoipTunnelGameT * - pointer to matching game, or NULL
|
|
|
|
\Version 12/01/2015 (eesponda)
|
|
*/
|
|
/********************************************************************************F*/
|
|
VoipTunnelGameT *VoipTunnelGameListMatchId(VoipTunnelRefT *pVoipTunnel, uint32_t uGameId, int32_t *pGameIdx)
|
|
{
|
|
VoipTunnelGameT *pGame;
|
|
int32_t iGameIdx;
|
|
|
|
// ignore invalid game id
|
|
if (uGameId == 0)
|
|
{
|
|
return(NULL);
|
|
}
|
|
// initialize if valid
|
|
if (pGameIdx != NULL)
|
|
{
|
|
*pGameIdx = -1;
|
|
}
|
|
|
|
// iterate through game list and look for a match
|
|
for (iGameIdx = 0, pGame = NULL; iGameIdx < pVoipTunnel->iMaxGames; iGameIdx += 1)
|
|
{
|
|
if (pVoipTunnel->pGameList[iGameIdx].uGameId == uGameId)
|
|
{
|
|
pGame = &pVoipTunnel->pGameList[iGameIdx];
|
|
if (pGameIdx != NULL)
|
|
{
|
|
*pGameIdx = iGameIdx;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
return(pGame);
|
|
}
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function VoipTunnelClientRefreshSendMask
|
|
|
|
\Description
|
|
Let api know client's send list has been updated
|
|
|
|
\Input *pVoipTunnel - voiptunnel ref
|
|
\Input *pClient - client whose send list has been updated
|
|
|
|
\Version 06/10/2007 (jbrookes)
|
|
*/
|
|
/********************************************************************************F*/
|
|
void VoipTunnelClientRefreshSendMask(VoipTunnelRefT *pVoipTunnel, VoipTunnelClientT *pClient)
|
|
{
|
|
VoipTunnelGameT *pGame = &pVoipTunnel->pGameList[pClient->iGameIdx];
|
|
uint32_t bInList, uGameClientId, uGameSendMask;
|
|
int32_t iGameClient, iSendClient;
|
|
|
|
// iterate through game list and rebuild client's send mask
|
|
for (iGameClient = 0, uGameSendMask = 0; iGameClient < VOIPTUNNEL_MAXGROUPSIZE; iGameClient++)
|
|
{
|
|
// skip empty or inactive entries
|
|
if (pGame->aClientList[iGameClient] == 0 || pGame->bClientActive[iGameClient] == FALSE)
|
|
{
|
|
continue;
|
|
}
|
|
// iterate through client's send list
|
|
for (bInList = FALSE, iSendClient = 0, uGameClientId = pGame->aClientList[iGameClient]; iSendClient < VOIPTUNNEL_MAXGROUPSIZE; iSendClient++)
|
|
{
|
|
if (uGameClientId == (unsigned)pClient->aClientIds[iSendClient])
|
|
{
|
|
bInList = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
// update game send mask
|
|
uGameSendMask |= bInList << iGameClient;
|
|
}
|
|
// write back updated mask
|
|
NetPrintf(("voiptunnel: setting clientId=0x%08x gamesend mask to 0x%08x\n", pClient->uClientId, uGameSendMask));
|
|
pClient->uGameSendMask = uGameSendMask;
|
|
}
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function VoipTunnelStatus
|
|
|
|
\Description
|
|
Get module status
|
|
|
|
\Input *pVoipTunnel - pointer to module state
|
|
\Input iSelect - status selector
|
|
\Input iValue - selector specific
|
|
\Input *pBuf - [out] - selector specific
|
|
\Input iBufSize - size of output buffer
|
|
|
|
\Output
|
|
int32_t - selector specific
|
|
|
|
\Notes
|
|
iControl can be one of the following:
|
|
|
|
\verbatim
|
|
'game' - copies game info for game index=iValue into buffer; returns zero on success, negative on failure
|
|
'vddm' - retuns the number of voip packets that were not rebroadcast due to limits on max number of senders per game
|
|
'vmtm' - returns the voip max talker metric (number of times the limit on talkers was reached)
|
|
'mgam' - returns the max number of games the voiptunnel supports
|
|
'musr' - returns the max number of clients the voiptunnel supports
|
|
'ngam' - returns the total game count
|
|
'nusr' - if iValue==-1, returns total user count, else returns count of users w/ gameserver==iValue
|
|
'sock' - returns voip socket ref (copied into buffer), if available
|
|
'talk' - returns the current number of active clients that are tagged as "talking"
|
|
\endverbatim
|
|
|
|
\Version 04/10/2006 (jbrookes)
|
|
*/
|
|
/********************************************************************************F*/
|
|
int32_t VoipTunnelStatus(VoipTunnelRefT *pVoipTunnel, int32_t iSelect, int32_t iValue, void *pBuf, int32_t iBufSize)
|
|
{
|
|
if ((iSelect == 'game') && (pBuf != NULL) && (iBufSize == sizeof(VoipTunnelGameT)) && (iValue < pVoipTunnel->iMaxGames))
|
|
{
|
|
ds_memcpy(pBuf, &pVoipTunnel->pGameList[iValue], iBufSize);
|
|
return(0);
|
|
}
|
|
if (iSelect == 'mgam')
|
|
{
|
|
return(pVoipTunnel->iMaxGames);
|
|
}
|
|
if (iSelect == 'musr')
|
|
{
|
|
return(pVoipTunnel->iMaxClients);
|
|
}
|
|
if (iSelect == 'ngam')
|
|
{
|
|
return(pVoipTunnel->iNumGames);
|
|
}
|
|
if (iSelect == 'nusr')
|
|
{
|
|
int32_t iNumClients;
|
|
if (iValue == -1)
|
|
{
|
|
iNumClients = pVoipTunnel->iNumClients;
|
|
}
|
|
else if (iValue < pVoipTunnel->iMaxGames)
|
|
{
|
|
iNumClients = pVoipTunnel->pGameList[iValue].iNumClients;
|
|
}
|
|
else
|
|
{
|
|
NetPrintf(("voiptunnel: invalid game index (%d) passed to VoipTunnelStatus('nusr') selector\n", iValue));
|
|
iNumClients = -1;
|
|
}
|
|
return(iNumClients);
|
|
}
|
|
if (iSelect == 'vddm')
|
|
{
|
|
return(pVoipTunnel->uVoiceDataDropMetric);
|
|
}
|
|
if (iSelect == 'vmtm')
|
|
{
|
|
return(pVoipTunnel->uVoiceMaxTalkersMetric);
|
|
}
|
|
if ((iSelect == 'sock') && (pBuf != NULL) && (iBufSize >= (signed)sizeof(pVoipTunnel->pVoipSocket)))
|
|
{
|
|
ds_memcpy(pBuf, &pVoipTunnel->pVoipSocket, sizeof(pVoipTunnel->pVoipSocket));
|
|
return(0);
|
|
}
|
|
if (iSelect == 'talk')
|
|
{
|
|
int32_t iNumTalkingClients = -1; // default to error
|
|
if (iValue == -1)
|
|
{
|
|
iNumTalkingClients = pVoipTunnel->iNumTalkingClients;
|
|
}
|
|
else if (iValue < pVoipTunnel->iMaxGames)
|
|
{
|
|
iNumTalkingClients = pVoipTunnel->pGameList[iValue].iNumTalkingClients;
|
|
}
|
|
else
|
|
{
|
|
NetPrintf(("voiptunnel: invalid game index (%d) passed to VoipTunnelStatus('talk')\n", iValue));
|
|
}
|
|
return(iNumTalkingClients);
|
|
}
|
|
return(-1);
|
|
}
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function VoipTunnelControl
|
|
|
|
\Description
|
|
Control the module
|
|
|
|
\Input *pVoipTunnel - pointer to module state
|
|
\Input iControl - control selector
|
|
\Input iValue - selector specific
|
|
\Input iValue2 - selector specific
|
|
\Input *pValue - selector specific
|
|
|
|
\Notes
|
|
iControl can be one of the following:
|
|
|
|
\verbatim
|
|
'bvma' - set the maximum number of simultaneous voice broadcasters per game
|
|
'rtim' - set voice receive timeout value (iValue = timeout in ms)
|
|
'spam' - set debug level (iValue = level)
|
|
'xnat' - set port sniffing enable/disable (iValue = TRUE/FALSE, default FALSE)
|
|
\endverbatim
|
|
|
|
\Version 04/09/2006 (jbrookes)
|
|
*/
|
|
/********************************************************************************F*/
|
|
int32_t VoipTunnelControl(VoipTunnelRefT *pVoipTunnel, int32_t iControl, int32_t iValue, int32_t iValue2, const void *pValue)
|
|
{
|
|
if (iControl == 'bvma')
|
|
{
|
|
NetPrintf(("voiptunnel: maximum number of simultaneous voice broadcasters changed from %d to %d\n", pVoipTunnel->iMaxVoiceBroadcasters, iValue));
|
|
pVoipTunnel->iMaxVoiceBroadcasters = iValue;
|
|
return(0);
|
|
}
|
|
if (iControl == 'rtim')
|
|
{
|
|
pVoipTunnel->uVoiceRecvTimeout = iValue;
|
|
return(0);
|
|
}
|
|
if (iControl == 'spam')
|
|
{
|
|
pVoipTunnel->uDebugLevel = iValue;
|
|
return(0);
|
|
}
|
|
if (iControl == 'xnat')
|
|
{
|
|
NetPrintf(("voiptunnel: port sniffing %s\n", iValue ? "enabled" : "disabled"));
|
|
pVoipTunnel->bPortSniff = iValue;
|
|
return(0);
|
|
}
|
|
return(-1);
|
|
}
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function VoipTunnelUpdate
|
|
|
|
\Description
|
|
Update the module
|
|
|
|
\Input *pVoipTunnel - voiptunnel ref
|
|
|
|
\Version 03/24/2006 (jbrookes)
|
|
*/
|
|
/********************************************************************************F*/
|
|
void VoipTunnelUpdate(VoipTunnelRefT *pVoipTunnel)
|
|
{
|
|
// update clientlist
|
|
_VoipTunnelClientListUpdate(pVoipTunnel);
|
|
}
|
|
|