diff options
author | Tim Angus <tim@ngus.net> | 2009-10-03 11:52:53 +0000 |
---|---|---|
committer | Tim Angus <tim@ngus.net> | 2013-01-03 00:15:28 +0000 |
commit | b47a49a03370e7ea42f47623b9f72a5ca799f0e7 (patch) | |
tree | 9d64d778ded2971d7ebd05970d903d886bd81436 /src/server | |
parent | 09ceb08b95978feb0a9b737f22ac0f662c7465d6 (diff) |
* Merge ioq3-r1423
+ IPv6
+ VoIP
+ Stereo rendering
+ Other minor stuff
Diffstat (limited to 'src/server')
-rw-r--r-- | src/server/server.h | 32 | ||||
-rw-r--r-- | src/server/sv_ccmds.c | 118 | ||||
-rw-r--r-- | src/server/sv_client.c | 241 | ||||
-rw-r--r-- | src/server/sv_init.c | 9 | ||||
-rw-r--r-- | src/server/sv_main.c | 18 | ||||
-rw-r--r-- | src/server/sv_rankings.c | 2 | ||||
-rw-r--r-- | src/server/sv_snapshot.c | 4 |
7 files changed, 293 insertions, 131 deletions
diff --git a/src/server/server.h b/src/server/server.h index 331937e4..bdcd8729 100644 --- a/src/server/server.h +++ b/src/server/server.h @@ -34,6 +34,18 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #define MAX_ENT_CLUSTERS 16 +#ifdef 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; @@ -168,6 +180,14 @@ typedef struct client_s { netchan_buffer_t *netchan_start_queue; netchan_buffer_t **netchan_end_queue; +#ifdef 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; @@ -250,6 +270,11 @@ extern cvar_t *sv_pure; extern cvar_t *sv_lanForceRate; extern cvar_t *sv_dequeuePeriod; +#ifdef USE_VOIP +extern cvar_t *sv_voip; +#endif + + //=========================================================== // @@ -292,8 +317,6 @@ void SV_GetChallenge( netadr_t from ); void SV_DirectConnect( netadr_t from ); -void SV_AuthorizeIpPacket( netadr_t from ); - void SV_ExecuteClientMessage( client_t *cl, msg_t *msg ); void SV_UserinfoChanged( client_t *cl ); @@ -305,6 +328,11 @@ void SV_ClientThink (client_t *cl, usercmd_t *cmd); void SV_WriteDownloadToClient( client_t *cl , msg_t *msg ); +#ifdef USE_VOIP +void SV_WriteVoipToClient( client_t *cl, msg_t *msg ); +#endif + + // // sv_ccmds.c // diff --git a/src/server/sv_ccmds.c b/src/server/sv_ccmds.c index 33926d57..60de4275 100644 --- a/src/server/sv_ccmds.c +++ b/src/server/sv_ccmds.c @@ -386,122 +386,6 @@ static void SV_Kick_f( void ) { /* ================== -SV_Ban_f - -Ban a user from being able to play on this server through the auth -server -================== -*/ -static void SV_Ban_f( void ) { - client_t *cl; - - // make sure server is running - if ( !com_sv_running->integer ) { - Com_Printf( "Server is not running.\n" ); - return; - } - - if ( Cmd_Argc() != 2 ) { - Com_Printf ("Usage: banUser <player name>\n"); - return; - } - - cl = SV_GetPlayerByHandle(); - - if (!cl) { - return; - } - - if( cl->netchan.remoteAddress.type == NA_LOOPBACK ) { - SV_SendServerCommand(NULL, "print \"%s\"", "Cannot kick host player\n"); - return; - } - - //FIXME: there is no auth server in Tremulous -#if 0 - // look up the authorize server's IP - if ( !svs.authorizeAddress.ip[0] && svs.authorizeAddress.type != NA_BAD ) { - Com_Printf( "Resolving %s\n", AUTHORIZE_SERVER_NAME ); - if ( !NET_StringToAdr( AUTHORIZE_SERVER_NAME, &svs.authorizeAddress ) ) { - Com_Printf( "Couldn't resolve address\n" ); - return; - } - svs.authorizeAddress.port = BigShort( PORT_AUTHORIZE ); - Com_Printf( "%s resolved to %i.%i.%i.%i:%i\n", AUTHORIZE_SERVER_NAME, - svs.authorizeAddress.ip[0], svs.authorizeAddress.ip[1], - svs.authorizeAddress.ip[2], svs.authorizeAddress.ip[3], - BigShort( svs.authorizeAddress.port ) ); - } - - // otherwise send their ip to the authorize server - if ( svs.authorizeAddress.type != NA_BAD ) { - NET_OutOfBandPrint( NS_SERVER, svs.authorizeAddress, - "banUser %i.%i.%i.%i", cl->netchan.remoteAddress.ip[0], cl->netchan.remoteAddress.ip[1], - cl->netchan.remoteAddress.ip[2], cl->netchan.remoteAddress.ip[3] ); - Com_Printf("%s was banned from coming back\n", cl->name); - } -#endif -} - -/* -================== -SV_BanNum_f - -Ban a user from being able to play on this server through the auth -server -================== -*/ -static void SV_BanNum_f( void ) { - client_t *cl; - - // make sure server is running - if ( !com_sv_running->integer ) { - Com_Printf( "Server is not running.\n" ); - return; - } - - if ( Cmd_Argc() != 2 ) { - Com_Printf ("Usage: banClient <client number>\n"); - return; - } - - cl = SV_GetPlayerByNum(); - if ( !cl ) { - return; - } - if( cl->netchan.remoteAddress.type == NA_LOOPBACK ) { - SV_SendServerCommand(NULL, "print \"%s\"", "Cannot kick host player\n"); - return; - } - - //FIXME: there is no auth server in Tremulous -#if 0 - // look up the authorize server's IP - if ( !svs.authorizeAddress.ip[0] && svs.authorizeAddress.type != NA_BAD ) { - Com_Printf( "Resolving %s\n", AUTHORIZE_SERVER_NAME ); - if ( !NET_StringToAdr( AUTHORIZE_SERVER_NAME, &svs.authorizeAddress ) ) { - Com_Printf( "Couldn't resolve address\n" ); - return; - } - svs.authorizeAddress.port = BigShort( PORT_AUTHORIZE ); - Com_Printf( "%s resolved to %i.%i.%i.%i:%i\n", AUTHORIZE_SERVER_NAME, - svs.authorizeAddress.ip[0], svs.authorizeAddress.ip[1], - svs.authorizeAddress.ip[2], svs.authorizeAddress.ip[3], - BigShort( svs.authorizeAddress.port ) ); - } - - // otherwise send their ip to the authorize server - if ( svs.authorizeAddress.type != NA_BAD ) { - NET_OutOfBandPrint( NS_SERVER, svs.authorizeAddress, - "banUser %i.%i.%i.%i", cl->netchan.remoteAddress.ip[0], cl->netchan.remoteAddress.ip[1], - cl->netchan.remoteAddress.ip[2], cl->netchan.remoteAddress.ip[3] ); - Com_Printf("%s was banned from coming back\n", cl->name); - } -#endif -} - -/* -================== SV_KickNum_f Kick a user off of the server FIXME: move to game @@ -729,8 +613,6 @@ void SV_AddOperatorCommands( void ) { Cmd_AddCommand ("heartbeat", SV_Heartbeat_f); Cmd_AddCommand ("kick", SV_Kick_f); Cmd_AddCommand ("kickAll", SV_KickAll_f); - Cmd_AddCommand ("banUser", SV_Ban_f); - Cmd_AddCommand ("banClient", SV_BanNum_f); Cmd_AddCommand ("clientkick", SV_KickNum_f); Cmd_AddCommand ("status", SV_Status_f); Cmd_AddCommand ("serverinfo", SV_Serverinfo_f); diff --git a/src/server/sv_client.c b/src/server/sv_client.c index 6aa7797b..ca8dd86d 100644 --- a/src/server/sv_client.c +++ b/src/server/sv_client.c @@ -106,7 +106,7 @@ void SV_DirectConnect( netadr_t from ) { char *ip; Com_DPrintf ("SVC_DirectConnect ()\n"); - + Q_strncpyz( userinfo, Cmd_Argv(1), sizeof(userinfo) ); version = atoi( Info_ValueForKey( userinfo, "protocol" ) ); @@ -155,9 +155,8 @@ void SV_DirectConnect( netadr_t from ) { for (i=0 ; i<MAX_CHALLENGES ; i++) { if (NET_CompareAdr(from, svs.challenges[i].adr)) { - if ( challenge == svs.challenges[i].challenge ) { - break; // good - } + if ( challenge == svs.challenges[i].challenge ) + break; } } if (i == MAX_CHALLENGES) { @@ -656,11 +655,13 @@ void SV_WriteDownloadToClient( client_t *cl , msg_t *msg ) } } + cl->download = 0; + // We open the file here if ( !(sv_allowDownload->integer & DLF_ENABLE) || (sv_allowDownload->integer & DLF_NO_UDP) || idPack || unreferenced || - ( cl->downloadSize = FS_SV_FOpenFileRead( cl->downloadName, &cl->download ) ) <= 0 ) { + ( cl->downloadSize = FS_SV_FOpenFileRead( cl->downloadName, &cl->download ) ) < 0 ) { // cannot auto-download file if(unreferenced) { @@ -703,6 +704,10 @@ void SV_WriteDownloadToClient( client_t *cl , msg_t *msg ) MSG_WriteString( msg, errorMessage ); *cl->downloadName = 0; + + if(cl->download) + FS_FCloseFile(cl->download); + return; } @@ -825,6 +830,58 @@ void SV_WriteDownloadToClient( client_t *cl , msg_t *msg ) } } +#ifdef 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; + + // You have to start with a svc_EOF, so legacy clients drop the + // rest of this packet. Otherwise, those without VoIP support will + // see the svc_voip command, then panic and disconnect. + // Generally we don't send VoIP packets to legacy clients, but this + // serves as both a safety measure and a means to keep demo files + // compatible. + MSG_WriteByte( msg, svc_EOF ); + MSG_WriteByte( msg, svc_extension ); + 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 @@ -1072,6 +1129,13 @@ void SV_UserinfoChanged( client_t *cl ) { cl->snapshotMsec = 50; } +#ifdef USE_VOIP + // in the future, (val) will be a protocol version string, so only + // accept explicitly 1, not generally non-zero. + val = Info_ValueForKey (cl->userinfo, "cl_voip"); + cl->hasVoip = (atoi(val) == 1) ? qtrue : qfalse; +#endif + // TTimo // maintain the IP information // the banning code relies on this being consistently present @@ -1107,6 +1171,39 @@ static void SV_UpdateUserinfo_f( client_t *cl ) { VM_Call( gvm, GAME_CLIENT_USERINFO_CHANGED, cl - svs.clients ); } + +#ifdef 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 ); @@ -1122,6 +1219,10 @@ static ucmd_t ucmds[] = { {"stopdl", SV_StopDownload_f}, {"donedl", SV_DoneDownload_f}, +#ifdef USE_VOIP + {"voip", SV_Voip_f}, +#endif + {NULL, NULL} }; @@ -1344,6 +1445,118 @@ static void SV_UserMove( client_t *cl, msg_t *msg, qboolean delta ) { } +#ifdef 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 + + + /* =========================================================================== @@ -1428,9 +1641,23 @@ void SV_ExecuteClientMessage( client_t *cl, msg_t *msg ) { // read optional clientCommand strings do { c = MSG_ReadByte( msg ); + + // See if this is an extension command after the EOF, which means we + // got data that a legacy server should ignore. + if ((c == clc_EOF) && (MSG_LookaheadByte( msg ) == clc_extension)) { + MSG_ReadByte( msg ); // throw the clc_extension byte away. + c = MSG_ReadByte( msg ); // something legacy servers can't do! + // sometimes you get a clc_extension at end of stream...dangling + // bits in the huffman decoder giving a bogus value? + if (c == -1) { + c = clc_EOF; + } + } + if ( c == clc_EOF ) { break; } + if ( c != clc_clientCommand ) { break; } @@ -1447,6 +1674,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 ); + } else if ( c == clc_voip ) { +#ifdef USE_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/src/server/sv_init.c b/src/server/sv_init.c index 5d760519..d9930f29 100644 --- a/src/server/sv_init.c +++ b/src/server/sv_init.c @@ -287,6 +287,9 @@ void SV_Startup( void ) { } Cvar_Set( "sv_running", "1" ); + + // Join the ipv6 multicast group now that a map is running so clients can scan for us on the local network. + NET_JoinMulticast6(); } @@ -607,6 +610,10 @@ 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 ); +#ifdef USE_VOIP + sv_voip = Cvar_Get ("sv_voip", "1", CVAR_SYSTEMINFO | CVAR_LATCH); + Cvar_CheckRange( sv_voip, 0, 1, qtrue ); +#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 ); @@ -687,6 +694,8 @@ void SV_Shutdown( char *finalmsg ) { Com_Printf( "----- Server Shutdown (%s) -----\n", finalmsg ); + NET_LeaveMulticast6(); + if ( svs.clients && !com_errorEntered ) { SV_FinalMessage( finalmsg ); } diff --git a/src/server/sv_main.c b/src/server/sv_main.c index 2940d95c..7b66aa6f 100644 --- a/src/server/sv_main.c +++ b/src/server/sv_main.c @@ -23,6 +23,10 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #include "server.h" +#ifdef 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 @@ -247,16 +251,14 @@ void SV_MasterHeartbeat( void ) { sv_master[i]->modified = qfalse; Com_Printf( "Resolving %s\n", sv_master[i]->string ); - if ( !NET_StringToAdr( sv_master[i]->string, &adr[i] ) ) { + if ( !NET_StringToAdr( sv_master[i]->string, &adr[i], NA_UNSPEC ) ) { Com_Printf( "Couldn't resolve address: %s\n", sv_master[i]->string ); continue; } if ( !strchr( sv_master[i]->string, ':' ) ) { adr[i].port = BigShort( PORT_MASTER ); } - Com_Printf( "%s resolved to %i.%i.%i.%i:%i\n", sv_master[i]->string, - adr[i].ip[0], adr[i].ip[1], adr[i].ip[2], adr[i].ip[3], - BigShort( adr[i].port ) ); + Com_Printf( "%s resolved to %s\n", sv_master[i]->string, NET_AdrToStringwPort(adr[i])); } @@ -300,7 +302,7 @@ void SV_MasterGameStat( const char *data ) return; // only dedicated servers send stats Com_Printf( "Resolving %s\n", MASTER_SERVER_NAME ); - if( !NET_StringToAdr( MASTER_SERVER_NAME, &adr ) ) + if( !NET_StringToAdr( MASTER_SERVER_NAME, &adr, NA_IP ) ) { Com_Printf( "Couldn't resolve address: %s\n", MASTER_SERVER_NAME ); return; @@ -417,6 +419,12 @@ void SVC_Info( netadr_t from ) { va("%i", sv_maxclients->integer - sv_privateClients->integer ) ); Info_SetValueForKey( infostring, "pure", va("%i", sv_pure->integer ) ); +#ifdef USE_VOIP + if (sv_voip->integer) { + 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/src/server/sv_rankings.c b/src/server/sv_rankings.c index 20d24fd4..7846cccf 100644 --- a/src/server/sv_rankings.c +++ b/src/server/sv_rankings.c @@ -1524,7 +1524,7 @@ static void SV_RankError( const char* fmt, ... ) char text[1024]; va_start( arg_ptr, fmt ); - vsprintf( text, fmt, arg_ptr ); + Q_vsnprintf(text, sizeof(text), fmt, arg_ptr ); va_end( arg_ptr ); Com_DPrintf( "****************************************\n" ); diff --git a/src/server/sv_snapshot.c b/src/server/sv_snapshot.c index 47471ba5..037b772d 100644 --- a/src/server/sv_snapshot.c +++ b/src/server/sv_snapshot.c @@ -648,6 +648,10 @@ void SV_SendClientSnapshot( client_t *client ) { // Add any download data if the client is downloading SV_WriteDownloadToClient( client, &msg ); +#ifdef USE_VOIP + SV_WriteVoipToClient( client, &msg ); +#endif + // check for overflow if ( msg.overflowed ) { Com_Printf ("WARNING: msg overflowed for %s\n", client->name); |