Überarbeite sv_send.c

This commit is contained in:
Yamagi Burmeister 2010-11-26 08:20:11 +00:00
parent cb0159014d
commit d899229399
1 changed files with 436 additions and 396 deletions

View File

@ -1,563 +1,603 @@
/* /*
Copyright (C) 1997-2001 Id Software, Inc. * Copyright (C) 1997-2001 Id Software, Inc.
*
This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or modify
modify it under the terms of the GNU General Public License * it under the terms of the GNU General Public License as published by
as published by the Free Software Foundation; either version 2 * the Free Software Foundation; either version 2 of the License, or (at
of the License, or (at your option) any later version. * your option) any later version.
*
This program is distributed in the hope that it will be useful, * This program is distributed in the hope that it will be useful, but
but WITHOUT ANY WARRANTY; without even the implied warranty of * WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*
See the GNU General Public License for more details. * See the GNU General Public License for more details.
*
You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software * along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*/ *
// sv_main.c -- server main program * =======================================================================
*
* Message sending and multiplexing.
*
* =======================================================================
*/
#include "header/server.h" #include "header/server.h"
/* char sv_outputbuf [ SV_OUTPUTBUF_LENGTH ];
=============================================================================
Com_Printf redirection void
SV_FlushRedirect ( int sv_redirected, char *outputbuf )
=============================================================================
*/
char sv_outputbuf[SV_OUTPUTBUF_LENGTH];
void SV_FlushRedirect (int sv_redirected, char *outputbuf)
{ {
if (sv_redirected == RD_PACKET) if ( sv_redirected == RD_PACKET )
{ {
Netchan_OutOfBandPrint (NS_SERVER, net_from, "print\n%s", outputbuf); Netchan_OutOfBandPrint( NS_SERVER, net_from, "print\n%s", outputbuf );
} }
else if (sv_redirected == RD_CLIENT) else if ( sv_redirected == RD_CLIENT )
{ {
MSG_WriteByte (&sv_client->netchan.message, svc_print); MSG_WriteByte( &sv_client->netchan.message, svc_print );
MSG_WriteByte (&sv_client->netchan.message, PRINT_HIGH); MSG_WriteByte( &sv_client->netchan.message, PRINT_HIGH );
MSG_WriteString (&sv_client->netchan.message, outputbuf); MSG_WriteString( &sv_client->netchan.message, outputbuf );
} }
} }
/* /*
============================================================================= * Sends text across to be displayed if the level passes
*/
EVENT MESSAGES void
SV_ClientPrintf ( client_t *cl, int level, char *fmt, ... )
=============================================================================
*/
/*
=================
SV_ClientPrintf
Sends text across to be displayed if the level passes
=================
*/
void SV_ClientPrintf (client_t *cl, int level, char *fmt, ...)
{ {
va_list argptr; va_list argptr;
char string[1024]; char string [ 1024 ];
if (level < cl->messagelevel) if ( level < cl->messagelevel )
{
return; return;
}
va_start (argptr,fmt); va_start( argptr, fmt );
vsprintf (string, fmt,argptr); vsprintf( string, fmt, argptr );
va_end (argptr); va_end( argptr );
MSG_WriteByte (&cl->netchan.message, svc_print); MSG_WriteByte( &cl->netchan.message, svc_print );
MSG_WriteByte (&cl->netchan.message, level); MSG_WriteByte( &cl->netchan.message, level );
MSG_WriteString (&cl->netchan.message, string); MSG_WriteString( &cl->netchan.message, string );
} }
/* /*
================= * Sends text to all active clients
SV_BroadcastPrintf */
void
Sends text to all active clients SV_BroadcastPrintf ( int level, char *fmt, ... )
=================
*/
void SV_BroadcastPrintf (int level, char *fmt, ...)
{ {
va_list argptr; va_list argptr;
char string[2048]; char string [ 2048 ];
client_t *cl; client_t *cl;
int i; int i;
va_start (argptr,fmt); va_start( argptr, fmt );
vsprintf (string, fmt,argptr); vsprintf( string, fmt, argptr );
va_end (argptr); va_end( argptr );
// echo to console /* echo to console */
if (dedicated->value) if ( dedicated->value )
{ {
char copy[1024]; char copy [ 1024 ];
int i; int i;
// mask off high bits /* mask off high bits */
for (i=0 ; i<1023 && string[i] ; i++) for ( i = 0; i < 1023 && string [ i ]; i++ )
copy[i] = string[i]&127; {
copy[i] = 0; copy [ i ] = string [ i ] & 127;
Com_Printf ("%s", copy); }
copy [ i ] = 0;
Com_Printf( "%s", copy );
} }
for (i=0, cl = svs.clients ; i<maxclients->value; i++, cl++) for ( i = 0, cl = svs.clients; i < maxclients->value; i++, cl++ )
{ {
if (level < cl->messagelevel) if ( level < cl->messagelevel )
{
continue; continue;
if (cl->state != cs_spawned) }
if ( cl->state != cs_spawned )
{
continue; continue;
MSG_WriteByte (&cl->netchan.message, svc_print); }
MSG_WriteByte (&cl->netchan.message, level);
MSG_WriteString (&cl->netchan.message, string); MSG_WriteByte( &cl->netchan.message, svc_print );
MSG_WriteByte( &cl->netchan.message, level );
MSG_WriteString( &cl->netchan.message, string );
} }
} }
/* /*
================= * Sends text to all active clients
SV_BroadcastCommand */
void
Sends text to all active clients SV_BroadcastCommand ( char *fmt, ... )
=================
*/
void SV_BroadcastCommand (char *fmt, ...)
{ {
va_list argptr; va_list argptr;
char string[1024]; char string [ 1024 ];
if (!sv.state) if ( !sv.state )
{
return; return;
va_start (argptr,fmt); }
vsprintf (string, fmt,argptr);
va_end (argptr);
MSG_WriteByte (&sv.multicast, svc_stufftext); va_start( argptr, fmt );
MSG_WriteString (&sv.multicast, string); vsprintf( string, fmt, argptr );
SV_Multicast (NULL, MULTICAST_ALL_R); va_end( argptr );
MSG_WriteByte( &sv.multicast, svc_stufftext );
MSG_WriteString( &sv.multicast, string );
SV_Multicast( NULL, MULTICAST_ALL_R );
} }
/* /*
================= * Sends the contents of sv.multicast to a subset of the clients,
SV_Multicast * then clears sv.multicast.
*
Sends the contents of sv.multicast to a subset of the clients, * MULTICAST_ALL same as broadcast (origin can be NULL)
then clears sv.multicast. * MULTICAST_PVS send to clients potentially visible from org
* MULTICAST_PHS send to clients potentially hearable from org
MULTICAST_ALL same as broadcast (origin can be NULL) */
MULTICAST_PVS send to clients potentially visible from org void
MULTICAST_PHS send to clients potentially hearable from org SV_Multicast ( vec3_t origin, multicast_t to )
=================
*/
void SV_Multicast (vec3_t origin, multicast_t to)
{ {
client_t *client; client_t *client;
byte *mask; byte *mask;
int leafnum, cluster; int leafnum, cluster;
int j; int j;
qboolean reliable; qboolean reliable;
int area1, area2; int area1, area2;
reliable = false; reliable = false;
if (to != MULTICAST_ALL_R && to != MULTICAST_ALL) if ( ( to != MULTICAST_ALL_R ) && ( to != MULTICAST_ALL ) )
{ {
leafnum = CM_PointLeafnum (origin); leafnum = CM_PointLeafnum( origin );
area1 = CM_LeafArea (leafnum); area1 = CM_LeafArea( leafnum );
} }
else else
{ {
leafnum = 0; // just to avoid compiler warnings leafnum = 0; /* just to avoid compiler warnings */
area1 = 0; area1 = 0;
} }
// if doing a serverrecord, store everything /* if doing a serverrecord, store everything */
if (svs.demofile) if ( svs.demofile )
SZ_Write (&svs.demo_multicast, sv.multicast.data, sv.multicast.cursize);
switch (to)
{ {
case MULTICAST_ALL_R: SZ_Write( &svs.demo_multicast, sv.multicast.data, sv.multicast.cursize );
reliable = true; // intentional fallthrough
case MULTICAST_ALL:
leafnum = 0;
mask = NULL;
break;
case MULTICAST_PHS_R:
reliable = true; // intentional fallthrough
case MULTICAST_PHS:
leafnum = CM_PointLeafnum (origin);
cluster = CM_LeafCluster (leafnum);
mask = CM_ClusterPHS (cluster);
break;
case MULTICAST_PVS_R:
reliable = true; // intentional fallthrough
case MULTICAST_PVS:
leafnum = CM_PointLeafnum (origin);
cluster = CM_LeafCluster (leafnum);
mask = CM_ClusterPVS (cluster);
break;
default:
mask = NULL;
Com_Error (ERR_FATAL, "SV_Multicast: bad to:%i", to);
} }
// send the data to all relevent clients switch ( to )
for (j = 0, client = svs.clients; j < maxclients->value; j++, client++)
{ {
if (client->state == cs_free || client->state == cs_zombie) case MULTICAST_ALL_R:
continue; reliable = true; /* intentional fallthrough */
if (client->state != cs_spawned && !reliable) case MULTICAST_ALL:
continue; leafnum = 0;
mask = NULL;
break;
if (mask) case MULTICAST_PHS_R:
reliable = true; /* intentional fallthrough */
case MULTICAST_PHS:
leafnum = CM_PointLeafnum( origin );
cluster = CM_LeafCluster( leafnum );
mask = CM_ClusterPHS( cluster );
break;
case MULTICAST_PVS_R:
reliable = true; /* intentional fallthrough */
case MULTICAST_PVS:
leafnum = CM_PointLeafnum( origin );
cluster = CM_LeafCluster( leafnum );
mask = CM_ClusterPVS( cluster );
break;
default:
mask = NULL;
Com_Error( ERR_FATAL, "SV_Multicast: bad to:%i", to );
}
/* send the data to all relevent clients */
for ( j = 0, client = svs.clients; j < maxclients->value; j++, client++ )
{
if ( ( client->state == cs_free ) || ( client->state == cs_zombie ) )
{ {
leafnum = CM_PointLeafnum (client->edict->s.origin); continue;
cluster = CM_LeafCluster (leafnum);
area2 = CM_LeafArea (leafnum);
if (!CM_AreasConnected (area1, area2))
continue;
if ( mask && (!(mask[cluster>>3] & (1<<(cluster&7)) ) ) )
continue;
} }
if (reliable) if ( ( client->state != cs_spawned ) && !reliable )
SZ_Write (&client->netchan.message, sv.multicast.data, sv.multicast.cursize); {
continue;
}
if ( mask )
{
leafnum = CM_PointLeafnum( client->edict->s.origin );
cluster = CM_LeafCluster( leafnum );
area2 = CM_LeafArea( leafnum );
if ( !CM_AreasConnected( area1, area2 ) )
{
continue;
}
if ( mask && ( !( mask [ cluster >> 3 ] & ( 1 << ( cluster & 7 ) ) ) ) )
{
continue;
}
}
if ( reliable )
{
SZ_Write( &client->netchan.message, sv.multicast.data, sv.multicast.cursize );
}
else else
SZ_Write (&client->datagram, sv.multicast.data, sv.multicast.cursize); {
SZ_Write( &client->datagram, sv.multicast.data, sv.multicast.cursize );
}
} }
SZ_Clear (&sv.multicast); SZ_Clear( &sv.multicast );
} }
/* /*
================== * Each entity can have eight independant sound sources, like voice,
SV_StartSound * weapon, feet, etc.
*
Each entity can have eight independant sound sources, like voice, * If cahnnel & 8, the sound will be sent to everyone, not just
weapon, feet, etc. * things in the PHS.
*
If cahnnel & 8, the sound will be sent to everyone, not just * Channel 0 is an auto-allocate channel, the others override anything
things in the PHS. * already running on that entity/channel pair.
*
FIXME: if entity isn't in PHS, they must be forced to be sent or * An attenuation of 0 will play full volume everywhere in the level.
have the origin explicitly sent. * Larger attenuations will drop off. (max 4 attenuation)
*
Channel 0 is an auto-allocate channel, the others override anything * Timeofs can range from 0.0 to 0.1 to cause sounds to be started
already running on that entity/channel pair. * later in the frame than they normally would.
*
An attenuation of 0 will play full volume everywhere in the level. * If origin is NULL, the origin is determined from the entity origin
Larger attenuations will drop off. (max 4 attenuation) * or the midpoint of the entity box for bmodels.
*/
Timeofs can range from 0.0 to 0.1 to cause sounds to be started void
later in the frame than they normally would. SV_StartSound ( vec3_t origin, edict_t *entity, int channel, int soundindex, float volume, float attenuation, float timeofs )
If origin is NULL, the origin is determined from the entity origin
or the midpoint of the entity box for bmodels.
==================
*/
void SV_StartSound (vec3_t origin, edict_t *entity, int channel,
int soundindex, float volume,
float attenuation, float timeofs)
{ {
int sendchan; int sendchan;
int flags; int flags;
int i; int i;
int ent; int ent;
vec3_t origin_v; vec3_t origin_v;
qboolean use_phs; qboolean use_phs;
if (volume < 0 || volume > 1.0) if ( ( volume < 0 ) || ( volume > 1.0 ) )
Com_Error (ERR_FATAL, "SV_StartSound: volume = %f", volume); {
Com_Error( ERR_FATAL, "SV_StartSound: volume = %f", volume );
}
if (attenuation < 0 || attenuation > 4) if ( ( attenuation < 0 ) || ( attenuation > 4 ) )
Com_Error (ERR_FATAL, "SV_StartSound: attenuation = %f", attenuation); {
Com_Error( ERR_FATAL, "SV_StartSound: attenuation = %f", attenuation );
}
if (timeofs < 0 || timeofs > 0.255) if ( ( timeofs < 0 ) || ( timeofs > 0.255 ) )
Com_Error (ERR_FATAL, "SV_StartSound: timeofs = %f", timeofs); {
Com_Error( ERR_FATAL, "SV_StartSound: timeofs = %f", timeofs );
}
ent = NUM_FOR_EDICT(entity); ent = NUM_FOR_EDICT( entity );
if (channel & 8) // no PHS flag if ( channel & 8 ) /* no PHS flag */
{ {
use_phs = false; use_phs = false;
channel &= 7; channel &= 7;
} }
else else
{
use_phs = true; use_phs = true;
}
sendchan = (ent<<3) | (channel&7); sendchan = ( ent << 3 ) | ( channel & 7 );
flags = 0; flags = 0;
if (volume != DEFAULT_SOUND_PACKET_VOLUME)
if ( volume != DEFAULT_SOUND_PACKET_VOLUME )
{
flags |= SND_VOLUME; flags |= SND_VOLUME;
if (attenuation != DEFAULT_SOUND_PACKET_ATTENUATION) }
if ( attenuation != DEFAULT_SOUND_PACKET_ATTENUATION )
{
flags |= SND_ATTENUATION; flags |= SND_ATTENUATION;
}
// the client doesn't know that bmodels have weird origins /* the client doesn't know that bmodels have weird origins
// the origin can also be explicitly set the origin can also be explicitly set */
if ( (entity->svflags & SVF_NOCLIENT) if ( ( entity->svflags & SVF_NOCLIENT ) ||
|| (entity->solid == SOLID_BSP) ( entity->solid == SOLID_BSP ) ||
|| origin ) origin )
{
flags |= SND_POS; flags |= SND_POS;
}
// always send the entity number for channel overrides /* always send the entity number for channel overrides */
flags |= SND_ENT; flags |= SND_ENT;
if (timeofs) if ( timeofs )
{
flags |= SND_OFFSET; flags |= SND_OFFSET;
}
// use the entity origin unless it is a bmodel or explicitly specified /* use the entity origin unless it is a bmodel or explicitly specified */
if (!origin) if ( !origin )
{ {
origin = origin_v; origin = origin_v;
if (entity->solid == SOLID_BSP)
if ( entity->solid == SOLID_BSP )
{ {
for (i=0 ; i<3 ; i++) for ( i = 0; i < 3; i++ )
origin_v[i] = entity->s.origin[i]+0.5f*(entity->mins[i]+entity->maxs[i]); {
origin_v [ i ] = entity->s.origin [ i ] + 0.5f * ( entity->mins [ i ] + entity->maxs [ i ] );
}
} }
else else
{ {
VectorCopy (entity->s.origin, origin_v); VectorCopy( entity->s.origin, origin_v );
} }
} }
MSG_WriteByte (&sv.multicast, svc_sound); MSG_WriteByte( &sv.multicast, svc_sound );
MSG_WriteByte (&sv.multicast, flags); MSG_WriteByte( &sv.multicast, flags );
MSG_WriteByte (&sv.multicast, soundindex); MSG_WriteByte( &sv.multicast, soundindex );
if (flags & SND_VOLUME) if ( flags & SND_VOLUME )
MSG_WriteByte (&sv.multicast, volume*255);
if (flags & SND_ATTENUATION)
MSG_WriteByte (&sv.multicast, attenuation*64);
if (flags & SND_OFFSET)
MSG_WriteByte (&sv.multicast, timeofs*1000);
if (flags & SND_ENT)
MSG_WriteShort (&sv.multicast, sendchan);
if (flags & SND_POS)
MSG_WritePos (&sv.multicast, origin);
// if the sound doesn't attenuate,send it to everyone
// (global radio chatter, voiceovers, etc)
if (attenuation == ATTN_NONE)
use_phs = false;
if (channel & CHAN_RELIABLE)
{ {
if (use_phs) MSG_WriteByte( &sv.multicast, volume * 255 );
SV_Multicast (origin, MULTICAST_PHS_R); }
if ( flags & SND_ATTENUATION )
{
MSG_WriteByte( &sv.multicast, attenuation * 64 );
}
if ( flags & SND_OFFSET )
{
MSG_WriteByte( &sv.multicast, timeofs * 1000 );
}
if ( flags & SND_ENT )
{
MSG_WriteShort( &sv.multicast, sendchan );
}
if ( flags & SND_POS )
{
MSG_WritePos( &sv.multicast, origin );
}
/* if the sound doesn't attenuate,send it to everyone
(global radio chatter, voiceovers, etc) */
if ( attenuation == ATTN_NONE )
{
use_phs = false;
}
if ( channel & CHAN_RELIABLE )
{
if ( use_phs )
{
SV_Multicast( origin, MULTICAST_PHS_R );
}
else else
SV_Multicast (origin, MULTICAST_ALL_R); {
SV_Multicast( origin, MULTICAST_ALL_R );
}
} }
else else
{ {
if (use_phs) if ( use_phs )
SV_Multicast (origin, MULTICAST_PHS); {
SV_Multicast( origin, MULTICAST_PHS );
}
else else
SV_Multicast (origin, MULTICAST_ALL); {
SV_Multicast( origin, MULTICAST_ALL );
}
} }
} }
qboolean
/* SV_SendClientDatagram ( client_t *client )
===============================================================================
FRAME UPDATES
===============================================================================
*/
/*
=======================
SV_SendClientDatagram
=======================
*/
qboolean SV_SendClientDatagram (client_t *client)
{ {
byte msg_buf[MAX_MSGLEN]; byte msg_buf [ MAX_MSGLEN ];
sizebuf_t msg; sizebuf_t msg;
SV_BuildClientFrame (client); SV_BuildClientFrame( client );
SZ_Init (&msg, msg_buf, sizeof(msg_buf)); SZ_Init( &msg, msg_buf, sizeof ( msg_buf ) );
msg.allowoverflow = true; msg.allowoverflow = true;
// send over all the relevant entity_state_t /* send over all the relevant entity_state_t
// and the player_state_t and the player_state_t */
SV_WriteFrameToClient (client, &msg); SV_WriteFrameToClient( client, &msg );
// copy the accumulated multicast datagram /* copy the accumulated multicast datagram
// for this client out to the message for this client out to the message
// it is necessary for this to be after the WriteEntities it is necessary for this to be after the WriteEntities
// so that entity references will be current so that entity references will be current */
if (client->datagram.overflowed) if ( client->datagram.overflowed )
Com_Printf ("WARNING: datagram overflowed for %s\n", client->name); {
Com_Printf( "WARNING: datagram overflowed for %s\n", client->name );
}
else else
SZ_Write (&msg, client->datagram.data, client->datagram.cursize); {
SZ_Clear (&client->datagram); SZ_Write( &msg, client->datagram.data, client->datagram.cursize );
if (msg.overflowed)
{ // must have room left for the packet header
Com_Printf ("WARNING: msg overflowed for %s\n", client->name);
SZ_Clear (&msg);
} }
// send the datagram SZ_Clear( &client->datagram );
Netchan_Transmit (&client->netchan, msg.cursize, msg.data);
// record the size for rate estimation if ( msg.overflowed )
client->message_size[sv.framenum % RATE_MESSAGES] = msg.cursize; {
/* must have room left for the packet header */
Com_Printf( "WARNING: msg overflowed for %s\n", client->name );
SZ_Clear( &msg );
}
return true; /* send the datagram */
Netchan_Transmit( &client->netchan, msg.cursize, msg.data );
/* record the size for rate estimation */
client->message_size [ sv.framenum % RATE_MESSAGES ] = msg.cursize;
return ( true );
} }
void
/* SV_DemoCompleted ( void )
==================
SV_DemoCompleted
==================
*/
void SV_DemoCompleted (void)
{ {
if (sv.demofile) if ( sv.demofile )
{ {
FS_FCloseFile ((size_t)sv.demofile); FS_FCloseFile( (size_t) sv.demofile );
sv.demofile = NULL; sv.demofile = NULL;
} }
SV_Nextserver ();
SV_Nextserver();
} }
/* /*
======================= * Returns true if the client is over its current
SV_RateDrop * bandwidth estimation and should not be sent another packet
*/
Returns true if the client is over its current qboolean
bandwidth estimation and should not be sent another packet SV_RateDrop ( client_t *c )
=======================
*/
qboolean SV_RateDrop (client_t *c)
{ {
int total; int total;
int i; int i;
// never drop over the loopback /* never drop over the loopback */
if (c->netchan.remote_address.type == NA_LOOPBACK) if ( c->netchan.remote_address.type == NA_LOOPBACK )
return false; {
return ( false );
}
total = 0; total = 0;
for (i = 0 ; i < RATE_MESSAGES ; i++) for ( i = 0; i < RATE_MESSAGES; i++ )
{ {
total += c->message_size[i]; total += c->message_size [ i ];
} }
if (total > c->rate) if ( total > c->rate )
{ {
c->surpressCount++; c->surpressCount++;
c->message_size[sv.framenum % RATE_MESSAGES] = 0; c->message_size [ sv.framenum % RATE_MESSAGES ] = 0;
return true; return ( true );
} }
return false; return ( false );
} }
/* void
======================= SV_SendClientMessages ( void )
SV_SendClientMessages
=======================
*/
void SV_SendClientMessages (void)
{ {
int i; int i;
client_t *c; client_t *c;
int msglen; int msglen;
byte msgbuf[MAX_MSGLEN]; byte msgbuf [ MAX_MSGLEN ];
size_t r; size_t r;
msglen = 0; msglen = 0;
// read the next demo message if needed /* read the next demo message if needed */
if (sv.demofile && sv.state == ss_demo) if ( sv.demofile && ( sv.state == ss_demo ) )
{ {
if (sv_paused->value) if ( sv_paused->value )
{
msglen = 0; msglen = 0;
}
else else
{ {
// get the next message /* get the next message */
r = FS_FRead (&msglen, 4, 1, (size_t)sv.demofile); r = FS_FRead( &msglen, 4, 1, (size_t) sv.demofile );
if (r != 4)
if ( r != 4 )
{ {
SV_DemoCompleted (); SV_DemoCompleted();
return; return;
} }
msglen = LittleLong (msglen);
if (msglen == -1) msglen = LittleLong( msglen );
if ( msglen == -1 )
{ {
SV_DemoCompleted (); SV_DemoCompleted();
return; return;
} }
if (msglen > MAX_MSGLEN)
Com_Error (ERR_DROP, "SV_SendClientMessages: msglen > MAX_MSGLEN"); if ( msglen > MAX_MSGLEN )
r = FS_FRead (msgbuf, msglen, 1, (size_t)sv.demofile);
if (r != msglen)
{ {
SV_DemoCompleted (); Com_Error( ERR_DROP, "SV_SendClientMessages: msglen > MAX_MSGLEN" );
}
r = FS_FRead( msgbuf, msglen, 1, (size_t) sv.demofile );
if ( r != msglen )
{
SV_DemoCompleted();
return; return;
} }
} }
} }
// send a message to each connected client /* send a message to each connected client */
for (i=0, c = svs.clients ; i<maxclients->value; i++, c++) for ( i = 0, c = svs.clients; i < maxclients->value; i++, c++ )
{ {
if (!c->state) if ( !c->state )
continue;
// if the reliable message overflowed,
// drop the client
if (c->netchan.message.overflowed)
{ {
SZ_Clear (&c->netchan.message); continue;
SZ_Clear (&c->datagram);
SV_BroadcastPrintf (PRINT_HIGH, "%s overflowed\n", c->name);
SV_DropClient (c);
} }
if (sv.state == ss_cinematic /* if the reliable message overflowed,
|| sv.state == ss_demo drop the client */
|| sv.state == ss_pic if ( c->netchan.message.overflowed )
)
Netchan_Transmit (&c->netchan, msglen, msgbuf);
else if (c->state == cs_spawned)
{ {
// don't overrun bandwidth SZ_Clear( &c->netchan.message );
if (SV_RateDrop (c)) SZ_Clear( &c->datagram );
continue; SV_BroadcastPrintf( PRINT_HIGH, "%s overflowed\n", c->name );
SV_DropClient( c );
}
SV_SendClientDatagram (c); if ( ( sv.state == ss_cinematic ) ||
( sv.state == ss_demo ) ||
( sv.state == ss_pic )
)
{
Netchan_Transmit( &c->netchan, msglen, msgbuf );
}
else if ( c->state == cs_spawned )
{
/* don't overrun bandwidth */
if ( SV_RateDrop( c ) )
{
continue;
}
SV_SendClientDatagram( c );
} }
else else
{ {
// just update reliable if needed /* just update reliable if needed */
if (c->netchan.message.cursize || curtime - c->netchan.last_sent > 1000 ) if ( c->netchan.message.cursize || ( curtime - c->netchan.last_sent > 1000 ) )
Netchan_Transmit (&c->netchan, 0, NULL); {
Netchan_Transmit( &c->netchan, 0, NULL );
}
} }
} }
} }