From 10afaeef337e0d19b37cb7dbe3fccee48bf18a43 Mon Sep 17 00:00:00 2001 From: icculus Date: Sun, 1 Jun 2008 07:51:23 +0000 Subject: Initial patch for in-game VoIP support! git-svn-id: svn://svn.icculus.org/quake3/trunk@1348 edf5b092-35ff-0310-97b2-ce42778d08ea --- code/server/server.h | 30 +++++++ code/server/sv_client.c | 202 ++++++++++++++++++++++++++++++++++++++++++++++ code/server/sv_init.c | 3 + code/server/sv_main.c | 8 ++ code/server/sv_snapshot.c | 4 + 5 files changed, 247 insertions(+) (limited to 'code/server') diff --git a/code/server/server.h b/code/server/server.h index 3d03aee..61fd882 100644 --- a/code/server/server.h +++ b/code/server/server.h @@ -33,6 +33,18 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #define MAX_ENT_CLUSTERS 16 +#if USE_VOIP +typedef struct voipServerPacket_s +{ + int generation; + int sequence; + int frames; + int len; + int sender; + byte data[1024]; +} voipServerPacket_t; +#endif + typedef struct svEntity_s { struct worldSector_s *worldSector; struct svEntity_s *nextEntityInWorldSector; @@ -167,6 +179,14 @@ typedef struct client_s { netchan_buffer_t *netchan_start_queue; netchan_buffer_t **netchan_end_queue; +#if USE_VOIP + qboolean hasVoip; + qboolean muteAllVoip; + qboolean ignoreVoipFromClient[MAX_CLIENTS]; + voipServerPacket_t voipPacket[64]; // !!! FIXME: WAY too much memory! + int queuedVoipPackets; +#endif + int oldServerTime; qboolean csUpdated[MAX_CONFIGSTRINGS+1]; } client_t; @@ -264,6 +284,11 @@ extern cvar_t *sv_strictAuth; extern serverBan_t serverBans[SERVER_MAXBANS]; extern int serverBansCount; +#if USE_VOIP +extern cvar_t *sv_voip; +#endif + + //=========================================================== // @@ -320,6 +345,11 @@ void SV_ClientThink (client_t *cl, usercmd_t *cmd); void SV_WriteDownloadToClient( client_t *cl , msg_t *msg ); +#if USE_VOIP +void SV_WriteVoipToClient( client_t *cl, msg_t *msg ); +#endif + + // // sv_ccmds.c // diff --git a/code/server/sv_client.c b/code/server/sv_client.c index c9915b9..6f932d1 100644 --- a/code/server/sv_client.c +++ b/code/server/sv_client.c @@ -1083,6 +1083,50 @@ void SV_WriteDownloadToClient( client_t *cl , msg_t *msg ) } } +#if USE_VOIP +/* +================== +SV_WriteVoipToClient + +Check to see if there is any VoIP queued for a client, and send if there is. +================== +*/ +void SV_WriteVoipToClient( client_t *cl, msg_t *msg ) +{ + voipServerPacket_t *packet = &cl->voipPacket[0]; + int totalbytes = 0; + int i; + + if (*cl->downloadName) { + cl->queuedVoipPackets = 0; + return; // no VoIP allowed if download is going, to save bandwidth. + } + + // Write as many VoIP packets as we reasonably can... + for (i = 0; i < cl->queuedVoipPackets; i++, packet++) { + totalbytes += packet->len; + if (totalbytes > MAX_DOWNLOAD_BLKSIZE) + break; + + MSG_WriteByte( msg, svc_voip ); + MSG_WriteShort( msg, packet->sender ); + MSG_WriteByte( msg, (byte) packet->generation ); + MSG_WriteLong( msg, packet->sequence ); + MSG_WriteByte( msg, packet->frames ); + MSG_WriteShort( msg, packet->len ); + MSG_WriteData( msg, packet->data, packet->len ); + } + + // !!! FIXME: I hate this queue system. + cl->queuedVoipPackets -= i; + if (cl->queuedVoipPackets > 0) { + memmove( &cl->voipPacket[0], &cl->voipPacket[i], + sizeof (voipServerPacket_t) * i); + } +} +#endif + + /* ================= SV_Disconnect_f @@ -1326,6 +1370,11 @@ void SV_UserinfoChanged( client_t *cl ) { cl->snapshotMsec = 50; } +#if USE_VOIP + val = Info_ValueForKey (cl->userinfo, "voip"); + cl->hasVoip = (strlen(val) && atoi(val)) ? qtrue : qfalse; +#endif + // TTimo // maintain the IP information // the banning code relies on this being consistently present @@ -1361,6 +1410,39 @@ static void SV_UpdateUserinfo_f( client_t *cl ) { VM_Call( gvm, GAME_CLIENT_USERINFO_CHANGED, cl - svs.clients ); } + +#if USE_VOIP +static +void SV_UpdateVoipIgnore(client_t *cl, const char *idstr, qboolean ignore) +{ + if ((*idstr >= '0') && (*idstr <= '9')) { + const int id = atoi(idstr); + if ((id >= 0) && (id < MAX_CLIENTS)) { + cl->ignoreVoipFromClient[id] = ignore; + } + } +} + +/* +================== +SV_UpdateUserinfo_f +================== +*/ +static void SV_Voip_f( client_t *cl ) { + const char *cmd = Cmd_Argv(1); + if (strcmp(cmd, "ignore") == 0) { + SV_UpdateVoipIgnore(cl, Cmd_Argv(2), qtrue); + } else if (strcmp(cmd, "unignore") == 0) { + SV_UpdateVoipIgnore(cl, Cmd_Argv(2), qfalse); + } else if (strcmp(cmd, "muteall") == 0) { + cl->muteAllVoip = qtrue; + } else if (strcmp(cmd, "unmuteall") == 0) { + cl->muteAllVoip = qfalse; + } +} +#endif + + typedef struct { char *name; void (*func)( client_t *cl ); @@ -1376,6 +1458,10 @@ static ucmd_t ucmds[] = { {"stopdl", SV_StopDownload_f}, {"donedl", SV_DoneDownload_f}, +#if USE_VOIP + {"voip", SV_Voip_f}, +#endif + {NULL, NULL} }; @@ -1596,6 +1682,118 @@ static void SV_UserMove( client_t *cl, msg_t *msg, qboolean delta ) { } +#if USE_VOIP +static +qboolean SV_ShouldIgnoreVoipSender(const client_t *cl) +{ + if (!sv_voip->integer) + return qtrue; // VoIP disabled on this server. + else if (!cl->hasVoip) // client doesn't have VoIP support?! + return qtrue; + + // !!! FIXME: implement player blacklist. + + return qfalse; // don't ignore. +} + +static +void SV_UserVoip( client_t *cl, msg_t *msg ) { + const int sender = (int) (cl - svs.clients); + const int generation = MSG_ReadByte(msg); + const int sequence = MSG_ReadLong(msg); + const int frames = MSG_ReadByte(msg); + const int recip1 = MSG_ReadLong(msg); + const int recip2 = MSG_ReadLong(msg); + const int recip3 = MSG_ReadLong(msg); + const int packetsize = MSG_ReadShort(msg); + byte encoded[sizeof (cl->voipPacket[0].data)]; + client_t *client = NULL; + voipServerPacket_t *packet = NULL; + int i; + + if (generation < 0) + return; // short/invalid packet, bail. + else if (sequence < 0) + return; // short/invalid packet, bail. + else if (frames < 0) + return; // short/invalid packet, bail. + else if (recip1 < 0) + return; // short/invalid packet, bail. + else if (recip2 < 0) + return; // short/invalid packet, bail. + else if (recip3 < 0) + return; // short/invalid packet, bail. + else if (packetsize < 0) + return; // short/invalid packet, bail. + + if (packetsize > sizeof (encoded)) { // overlarge packet? + int bytesleft = packetsize; + while (bytesleft) { + int br = bytesleft; + if (br > sizeof (encoded)) + br = sizeof (encoded); + MSG_ReadData(msg, encoded, br); + bytesleft -= br; + } + return; // overlarge packet, bail. + } + + MSG_ReadData(msg, encoded, packetsize); + + if (SV_ShouldIgnoreVoipSender(cl)) + return; // Blacklisted, disabled, etc. + + // !!! FIXME: see if we read past end of msg... + + // !!! FIXME: reject if not speex narrowband codec. + // !!! FIXME: decide if this is bogus data? + + // (the three recip* values are 31 bits each (ignores sign bit so we can + // get a -1 error from MSG_ReadLong() ... ), allowing for 93 clients.) + assert( sv_maxclients->integer < 93 ); + + // decide who needs this VoIP packet sent to them... + for (i = 0, client = svs.clients; i < sv_maxclients->integer ; i++, client++) { + if (client->state != CS_ACTIVE) + continue; // not in the game yet, don't send to this guy. + else if (i == sender) + continue; // don't send voice packet back to original author. + else if (!client->hasVoip) + continue; // no VoIP support, or support disabled. + else if (client->muteAllVoip) + continue; // client is ignoring everyone. + else if (client->ignoreVoipFromClient[sender]) + continue; // client is ignoring this talker. + else if (*cl->downloadName) // !!! FIXME: possible to DoS? + continue; // no VoIP allowed if downloading, to save bandwidth. + else if ( ((i >= 0) && (i < 31)) && ((recip1 & (1 << (i-0))) == 0) ) + continue; // not addressed to this player. + else if ( ((i >= 31) && (i < 62)) && ((recip2 & (1 << (i-31))) == 0) ) + continue; // not addressed to this player. + else if ( ((i >= 62) && (i < 93)) && ((recip3 & (1 << (i-62))) == 0) ) + continue; // not addressed to this player. + + // Transmit this packet to the client. + // !!! FIXME: I don't like this queueing system. + if (client->queuedVoipPackets >= (sizeof (client->voipPacket) / sizeof (client->voipPacket[0]))) { + Com_Printf("Too many VoIP packets queued for client #%d\n", i); + continue; // no room for another packet right now. + } + + packet = &client->voipPacket[client->queuedVoipPackets]; + packet->sender = sender; + packet->frames = frames; + packet->len = packetsize; + packet->generation = generation; + packet->sequence = sequence; + memcpy(packet->data, encoded, packetsize); + client->queuedVoipPackets++; + } +} +#endif + + + /* =========================================================================== @@ -1699,6 +1897,10 @@ void SV_ExecuteClientMessage( client_t *cl, msg_t *msg ) { SV_UserMove( cl, msg, qtrue ); } else if ( c == clc_moveNoDelta ) { SV_UserMove( cl, msg, qfalse ); +#if USE_VOIP + } else if ( c == clc_voip ) { + SV_UserVoip( cl, msg ); +#endif } else if ( c != clc_EOF ) { Com_Printf( "WARNING: bad command byte for client %i\n", (int) (cl - svs.clients) ); } diff --git a/code/server/sv_init.c b/code/server/sv_init.c index aca7691..d46f273 100644 --- a/code/server/sv_init.c +++ b/code/server/sv_init.c @@ -654,6 +654,9 @@ void SV_Init (void) { Cvar_Get ("sv_cheats", "1", CVAR_SYSTEMINFO | CVAR_ROM ); sv_serverid = Cvar_Get ("sv_serverid", "0", CVAR_SYSTEMINFO | CVAR_ROM ); sv_pure = Cvar_Get ("sv_pure", "1", CVAR_SYSTEMINFO ); +#if USE_VOIP + sv_voip = Cvar_Get ("sv_voip", "1", CVAR_SYSTEMINFO ); +#endif Cvar_Get ("sv_paks", "", CVAR_SYSTEMINFO | CVAR_ROM ); Cvar_Get ("sv_pakNames", "", CVAR_SYSTEMINFO | CVAR_ROM ); Cvar_Get ("sv_referencedPaks", "", CVAR_SYSTEMINFO | CVAR_ROM ); diff --git a/code/server/sv_main.c b/code/server/sv_main.c index 552c12b..a12da03 100644 --- a/code/server/sv_main.c +++ b/code/server/sv_main.c @@ -22,6 +22,10 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #include "server.h" +#if USE_VOIP +cvar_t *sv_voip; +#endif + serverStatic_t svs; // persistant server info server_t sv; // local server vm_t *gvm = NULL; // game virtual machine @@ -407,6 +411,10 @@ void SVC_Info( netadr_t from ) { Info_SetValueForKey( infostring, "gametype", va("%i", sv_gametype->integer ) ); Info_SetValueForKey( infostring, "pure", va("%i", sv_pure->integer ) ); +#if USE_VOIP + Info_SetValueForKey( infostring, "voip", va("%i", sv_voip->integer ) ); +#endif + if( sv_minPing->integer ) { Info_SetValueForKey( infostring, "minPing", va("%i", sv_minPing->integer) ); } diff --git a/code/server/sv_snapshot.c b/code/server/sv_snapshot.c index 80b6af4..71fdc8b 100644 --- a/code/server/sv_snapshot.c +++ b/code/server/sv_snapshot.c @@ -653,6 +653,10 @@ void SV_SendClientSnapshot( client_t *client ) { // Add any download data if the client is downloading SV_WriteDownloadToClient( client, &msg ); +#if USE_VOIP + SV_WriteVoipToClient( client, &msg ); +#endif + // check for overflow if ( msg.overflowed ) { Com_Printf ("WARNING: msg overflowed for %s\n", client->name); -- cgit v1.2.3