Kawe Mazidjatari b3a68ed095 Add EABase, EAThread and DirtySDK to R5sdk
DirtySDK (EA's Dirty Sockets library) will be used for the LiveAPI implementation, and depends on: EABase, EAThread.
2024-04-05 18:29:03 +02:00

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);
}