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