This repository has been archived on 2022-06-27. You can view files and clone it, but cannot push or open issues or pull requests.
Xash3DArchive/engine/server/sv_frame.c

631 lines
16 KiB
C
Raw Normal View History

2008-07-09 22:00:00 +02:00
//=======================================================================
// Copyright XashXT Group 2008 <20>
// sv_frame.c - server world snapshot
//=======================================================================
#include "common.h"
2008-07-16 22:00:00 +02:00
#include "server.h"
2008-11-25 22:00:00 +01:00
#include "const.h"
2008-07-16 22:00:00 +02:00
2008-09-09 22:00:00 +02:00
#define MAX_VISIBLE_PACKET 1024
typedef struct
2008-07-16 22:00:00 +02:00
{
2008-09-09 22:00:00 +02:00
int num_entities;
int entities[MAX_VISIBLE_PACKET];
} sv_ents_t;
2008-07-16 22:00:00 +02:00
2008-11-14 22:00:00 +01:00
static byte *clientpvs; // FatPVS
2009-11-25 22:00:00 +01:00
static byte *clientphs; // FatPHS
2008-11-11 22:00:00 +01:00
2008-09-09 22:00:00 +02:00
int c_fullsend;
2008-07-16 22:00:00 +02:00
2008-09-09 22:00:00 +02:00
/*
=======================
SV_EntityNumbers
=======================
*/
static int SV_EntityNumbers( const void *a, const void *b )
{
int *ea, *eb;
2008-07-16 22:00:00 +02:00
2008-09-09 22:00:00 +02:00
ea = (int *)a;
eb = (int *)b;
2008-07-16 22:00:00 +02:00
2008-09-09 22:00:00 +02:00
if( *ea == *eb )
Host_Error( "SV_EntityNumbers: duplicated entity\n" );
if( *ea < *eb ) return -1;
return 1;
2008-07-16 22:00:00 +02:00
}
/*
=============================================================================
2008-12-15 22:00:00 +01:00
Copy entvars into entity state
2008-07-16 22:00:00 +02:00
=============================================================================
*/
2009-11-28 22:00:00 +01:00
void SV_UpdateEntityState( const edict_t *ent, bool baseline )
2008-07-16 22:00:00 +02:00
{
2009-12-01 22:00:00 +01:00
sv_client_t *client = SV_ClientFromEdict( ent, true );
2009-11-23 22:00:00 +01:00
2009-01-04 22:00:00 +01:00
if( !ent->pvServerData->s.classname )
ent->pvServerData->s.classname = SV_ClassIndex( STRING( ent->v.classname ));
2010-04-12 22:00:00 +02:00
if( client && !sv.paused )
2009-09-13 22:00:00 +02:00
{
2009-11-02 22:00:00 +01:00
SV_SetIdealPitch( client );
2009-09-18 22:00:00 +02:00
2010-04-12 22:00:00 +02:00
switch( ent->v.fixangle )
2009-09-18 22:00:00 +02:00
{
2010-04-12 22:00:00 +02:00
case 1:
2009-11-16 22:00:00 +01:00
MSG_WriteByte( &sv.multicast, svc_setangle );
MSG_WriteAngle32( &sv.multicast, ent->v.angles[0] );
MSG_WriteAngle32( &sv.multicast, ent->v.angles[1] );
2009-12-01 22:00:00 +01:00
MSG_DirectSend( MSG_ONE, vec3_origin, client->edict );
2010-04-12 22:00:00 +02:00
ent->pvServerData->s.ed_flags |= ESF_NO_PREDICTION;
break;
case 2:
MSG_WriteByte( &sv.multicast, svc_addangle );
2010-06-20 22:00:00 +02:00
MSG_WriteAngle32( &sv.multicast, client->addangle );
2010-04-12 22:00:00 +02:00
MSG_DirectSend( MSG_ONE, vec3_origin, client->edict );
2010-06-20 22:00:00 +02:00
client->addangle = 0;
2010-04-12 22:00:00 +02:00
break;
2009-09-18 22:00:00 +02:00
}
2010-04-12 22:00:00 +02:00
client->edict->v.fixangle = 0; // reset fixangle
2010-02-02 22:00:00 +01:00
if( client->modelindex )
{
// apply custom model if set
((edict_t *)ent)->v.modelindex = client->modelindex;
}
2009-09-13 22:00:00 +02:00
}
2009-11-28 22:00:00 +01:00
svgame.dllFuncs.pfnUpdateEntityState( &ent->pvServerData->s, (edict_t *)ent, baseline );
2009-07-15 22:00:00 +02:00
2009-08-22 22:00:00 +02:00
// always keep an actual
ent->pvServerData->s.number = ent->serialnumber;
2008-08-02 22:00:00 +02:00
}
2008-09-09 22:00:00 +02:00
/*
===============
SV_AddEntToSnapshot
===============
*/
2008-12-26 22:00:00 +01:00
static void SV_AddEntToSnapshot( sv_priv_t *svent, edict_t *ent, sv_ents_t *ents )
2008-08-02 22:00:00 +02:00
{
2008-09-09 22:00:00 +02:00
// if we have already added this entity to this snapshot, don't add again
if( svent->framenum == sv.net_framenum ) return;
svent->framenum = sv.net_framenum;
2008-08-02 22:00:00 +02:00
2008-09-09 22:00:00 +02:00
// if we are full, silently discard entities
2009-01-14 22:00:00 +01:00
if( ents->num_entities == MAX_VISIBLE_PACKET )
{
MsgDev( D_ERROR, "too many entities in visible packet list\n" );
return;
}
2008-08-02 22:00:00 +02:00
2009-01-14 22:00:00 +01:00
SV_UpdateEntityState( ent, false ); // copy entity state from progs
2008-12-15 22:00:00 +01:00
ents->entities[ents->num_entities] = ent->serialnumber;
2008-09-09 22:00:00 +02:00
ents->num_entities++;
2009-01-14 22:00:00 +01:00
c_fullsend++; // debug counter
2008-07-16 22:00:00 +02:00
}
/*
=============================================================================
Encode a client frame onto the network channel
=============================================================================
*/
/*
=============
SV_EmitPacketEntities
Writes a delta update of an entity_state_t list to the message->
=============
*/
2010-06-22 22:00:00 +02:00
void SV_EmitPacketEntities( client_frame_t *from, client_frame_t *to, sizebuf_t *msg )
2008-07-16 22:00:00 +02:00
{
entity_state_t *oldent, *newent;
int oldindex, newindex;
int oldnum, newnum;
int from_num_entities;
2010-06-22 22:00:00 +02:00
MSG_WriteByte( msg, svc_packetentities );
if( !from ) from_num_entities = 0;
else from_num_entities = from->num_entities;
2008-07-16 22:00:00 +02:00
2008-09-09 22:00:00 +02:00
newent = NULL;
oldent = NULL;
2008-07-16 22:00:00 +02:00
newindex = 0;
oldindex = 0;
while( newindex < to->num_entities || oldindex < from_num_entities )
{
if( newindex >= to->num_entities ) newnum = MAX_ENTNUMBER;
else
{
newent = &svs.client_entities[(to->first_entity+newindex)%svs.num_client_entities];
newnum = newent->number;
}
if( oldindex >= from_num_entities ) oldnum = MAX_ENTNUMBER;
else
{
oldent = &svs.client_entities[(from->first_entity+oldindex)%svs.num_client_entities];
oldnum = oldent->number;
}
if( newnum == oldnum )
{
// delta update from old position
// because the force parm is false, this will not result
// in any bytes being emited if the entity has not changed at all
2010-01-31 22:00:00 +01:00
MSG_WriteDeltaEntity( oldent, newent, msg, false, ( newent->number <= sv_maxclients->integer ));
2008-07-16 22:00:00 +02:00
oldindex++;
newindex++;
continue;
}
if( newnum < oldnum )
{
// this is a new entity, send it from the baseline
2008-07-17 22:00:00 +02:00
MSG_WriteDeltaEntity( &svs.baselines[newnum], newent, msg, true, true );
2008-07-16 22:00:00 +02:00
newindex++;
continue;
}
if( newnum > oldnum )
{
2008-11-25 22:00:00 +01:00
// remove from message
MSG_WriteDeltaEntity( oldent, NULL, msg, false, false );
2008-07-16 22:00:00 +02:00
oldindex++;
continue;
}
}
2008-10-19 22:00:00 +02:00
MSG_WriteBits( msg, 0, "svc_packetentities", NET_WORD ); // end of packetentities
2008-07-16 22:00:00 +02:00
}
2009-11-25 22:00:00 +01:00
static void SV_AddEntitiesToPacket( edict_t *pViewEnt, edict_t *pClient, client_frame_t *frame, sv_ents_t *ents, bool portal )
2008-09-09 22:00:00 +02:00
{
edict_t *ent;
2009-11-25 22:00:00 +01:00
byte *pset;
2010-05-22 22:00:00 +02:00
bool fullvis = false;
2008-09-09 22:00:00 +02:00
bool force = false;
2010-05-22 22:00:00 +02:00
sv_client_t *cl;
int e;
2008-09-09 22:00:00 +02:00
// during an error shutdown message we may need to transmit
// the shutdown message after the server has shutdown, so
// specfically check for it
if( !sv.state ) return;
2010-03-14 22:00:00 +01:00
if( pClient && !portal )
{
// portals can't change hostflags
2010-04-12 22:00:00 +02:00
sv.hostflags &= ~SVF_SKIPLOCALHOST;
2010-03-14 22:00:00 +01:00
cl = SV_ClientFromEdict( pClient, true );
Com_Assert( cl == NULL );
// setup hostflags
if( com.atoi( Info_ValueForKey( cl->userinfo, "cl_lw" )) == 1 )
sv.hostflags |= SVF_SKIPLOCALHOST;
}
2010-05-22 22:00:00 +02:00
svgame.dllFuncs.pfnSetupVisibility( pViewEnt, pClient, &clientpvs, &clientphs, portal );
if( !clientpvs ) fullvis = true;
2008-09-09 22:00:00 +02:00
2008-12-26 22:00:00 +01:00
for( e = 1; e < svgame.globals->numEntities; e++ )
2008-09-09 22:00:00 +02:00
{
2008-12-15 22:00:00 +01:00
ent = EDICT_NUM( e );
2008-12-17 22:00:00 +01:00
if( ent->free ) continue;
2008-09-09 22:00:00 +02:00
2008-12-15 22:00:00 +01:00
if( ent->serialnumber != e )
2008-09-09 22:00:00 +02:00
{
2009-11-25 22:00:00 +01:00
// this should never happens
MsgDev( D_NOTE, "fixing ent->serialnumber from %i to %i\n", ent->serialnumber, e );
2008-12-15 22:00:00 +01:00
ent->serialnumber = e;
2008-09-09 22:00:00 +02:00
}
2009-11-25 22:00:00 +01:00
// don't double add an entity through portals (already added)
if( ent->pvServerData->framenum == sv.net_framenum )
continue;
2008-11-15 22:00:00 +01:00
2009-11-25 22:00:00 +01:00
if( fullvis ) force = true;
else force = false; // clear forceflag
2008-09-09 22:00:00 +02:00
2009-11-25 22:00:00 +01:00
// NOTE: always add himslef to list
if( !portal && ( ent == pClient ))
force = true;
2008-09-09 22:00:00 +02:00
2009-11-25 22:00:00 +01:00
if( ent->v.flags & FL_PHS_FILTER )
pset = clientphs;
else pset = clientpvs;
2008-09-09 22:00:00 +02:00
2008-11-14 22:00:00 +01:00
if( !force )
2009-11-25 22:00:00 +01:00
{
// run custom user filter
2010-05-22 22:00:00 +02:00
if( !svgame.dllFuncs.pfnAddToFullPack( pViewEnt, pClient, ent, sv.hostflags, pset ))
2009-11-25 22:00:00 +01:00
continue;
2008-09-09 22:00:00 +02:00
}
2009-11-25 22:00:00 +01:00
SV_AddEntToSnapshot( ent->pvServerData, ent, ents ); // add it
2010-05-22 22:00:00 +02:00
if( fullvis ) continue; // portal ents will be added anyway, ignore recursion
2008-09-09 22:00:00 +02:00
// if its a portal entity, add everything visible from its camera position
2010-05-22 22:00:00 +02:00
if( !portal )
{
switch( ent->pvServerData->s.ed_type )
{
case ED_PORTAL:
case ED_SKYPORTAL:
SV_AddEntitiesToPacket( ent, pClient, frame, ents, true );
break;
}
}
2008-09-09 22:00:00 +02:00
}
}
2009-12-02 22:00:00 +01:00
static void SV_EmitEvents( sv_client_t *cl, client_frame_t *frame, sizebuf_t *msg )
{
int i, ev;
event_state_t *es;
event_info_t *info;
int ev_count = 0;
int c;
es = &cl->events;
// count events
for( ev = 0; ev < MAX_EVENT_QUEUE; ev++ )
{
info = &es->ei[ev];
if( info->index == 0 )
continue;
ev_count++;
}
// nothing to send
if( !ev_count ) return;
if( ev_count >= MAX_EVENT_QUEUE )
ev_count = MAX_EVENT_QUEUE - 1;
MSG_WriteByte( msg, svc_event ); // create message
MSG_WriteByte( msg, ev_count ); // Up to MAX_EVENT_QUEUE events
for( i = c = 0 ; i < MAX_EVENT_QUEUE; i++ )
{
info = &es->ei[i];
if( info->index == 0 )
continue;
// only send if there's room
if ( c < ev_count )
SV_PlaybackEvent( msg, info );
info->index = 0;
c++;
}
}
2008-07-16 22:00:00 +02:00
/*
==================
SV_WriteFrameToClient
==================
*/
void SV_WriteFrameToClient( sv_client_t *cl, sizebuf_t *msg )
{
client_frame_t *frame, *oldframe;
2010-06-22 22:00:00 +02:00
int lastframe;
// this is the frame we are creating
frame = &cl->frames[sv.framenum & SV_UPDATE_MASK];
2008-07-16 22:00:00 +02:00
2010-06-22 22:00:00 +02:00
if( cl->lastframe <= 0 )
{
// client is asking for a retransmit
oldframe = NULL;
lastframe = -1;
}
else if( sv.framenum - cl->lastframe >= (SV_UPDATE_BACKUP - 3))
2008-07-16 22:00:00 +02:00
{
2010-06-22 22:00:00 +02:00
// client hasn't gotten a good message through in a long time
oldframe = NULL;
lastframe = -1;
}
else
{ // we have a valid message to delta from
oldframe = &cl->frames[cl->lastframe & SV_UPDATE_MASK];
lastframe = cl->lastframe;
2008-09-09 22:00:00 +02:00
// the snapshot's entities may still have rolled off the buffer, though
if( oldframe->first_entity <= svs.next_client_entities - svs.num_client_entities )
{
2010-06-22 22:00:00 +02:00
MsgDev( D_WARN, "%s: ^7delta request from out of date entities.\n", cl->name );
2008-09-09 22:00:00 +02:00
oldframe = NULL;
2010-06-22 22:00:00 +02:00
lastframe = 0;
2008-09-09 22:00:00 +02:00
}
2008-07-16 22:00:00 +02:00
}
2009-11-23 22:00:00 +01:00
// refresh physinfo if needs
if( cl->physinfo_modified )
{
cl->physinfo_modified = false;
MSG_WriteByte( msg, svc_physinfo );
MSG_WriteString( msg, cl->physinfo );
}
2009-12-02 22:00:00 +01:00
// delta encode the events
SV_EmitEvents( cl, frame, msg );
2010-06-22 22:00:00 +02:00
MSG_WriteByte( msg, svc_frame );
MSG_WriteFloat( msg, (float)sv.time ); // send a servertime each frame
MSG_WriteLong( msg, sv.framenum );
MSG_WriteLong( msg, lastframe ); // what we are delta'ing from
MSG_WriteByte( msg, cl->surpressCount ); // rate dropped packets
MSG_WriteByte( msg, frame->index ); // send a client index
cl->surpressCount = 0;
2008-07-16 22:00:00 +02:00
2009-09-28 22:00:00 +02:00
// delta encode the entities
2010-06-22 22:00:00 +02:00
SV_EmitPacketEntities( oldframe, frame, msg );
2008-07-16 22:00:00 +02:00
}
/*
=============================================================================
Build a client frame structure
=============================================================================
*/
/*
=============
SV_BuildClientFrame
2010-05-22 22:00:00 +02:00
Decides which entities are going to be visible to the client,
and copies off the playerstate.
2008-07-16 22:00:00 +02:00
=============
*/
void SV_BuildClientFrame( sv_client_t *cl )
{
edict_t *ent;
edict_t *clent;
2009-11-25 22:00:00 +01:00
edict_t *viewent; // may be NULL
2008-07-16 22:00:00 +02:00
client_frame_t *frame;
entity_state_t *state;
2008-09-09 22:00:00 +02:00
sv_ents_t frame_ents;
int i;
2008-07-16 22:00:00 +02:00
clent = cl->edict;
2009-11-25 22:00:00 +01:00
viewent = cl->pViewEntity;
2008-09-09 22:00:00 +02:00
sv.net_framenum++;
2008-07-16 22:00:00 +02:00
// this is the frame we are creating
2010-06-22 22:00:00 +02:00
frame = &cl->frames[sv.framenum & SV_UPDATE_MASK];
frame->senttime = host.realtime; // save it for ping calc later
2008-07-16 22:00:00 +02:00
2008-09-09 22:00:00 +02:00
// clear everything in this snapshot
frame_ents.num_entities = c_fullsend = 0;
2008-12-26 22:00:00 +01:00
if( !clent->pvServerData->client ) return; // not in game yet
2008-09-09 22:00:00 +02:00
2009-01-03 22:00:00 +01:00
// grab the current player index
frame->index = NUM_FOR_EDICT( clent );
2008-09-09 22:00:00 +02:00
// add all the entities directly visible to the eye, which
// may include portal entities that merge other viewpoints
2009-11-25 22:00:00 +01:00
SV_AddEntitiesToPacket( viewent, clent, frame, &frame_ents, false );
2008-07-16 22:00:00 +02:00
2008-09-09 22:00:00 +02:00
// if there were portals visible, there may be out of order entities
// in the list which will need to be resorted for the delta compression
// to work correctly. This also catches the error condition
// of an entity being included twice.
qsort( frame_ents.entities, frame_ents.num_entities, sizeof( frame_ents.entities[0] ), SV_EntityNumbers );
2008-07-16 22:00:00 +02:00
2008-09-09 22:00:00 +02:00
// copy the entity states out
2008-07-16 22:00:00 +02:00
frame->num_entities = 0;
frame->first_entity = svs.next_client_entities;
2008-09-09 22:00:00 +02:00
for( i = 0; i < frame_ents.num_entities; i++ )
2008-07-16 22:00:00 +02:00
{
2008-12-15 22:00:00 +01:00
ent = EDICT_NUM( frame_ents.entities[i] );
2008-07-16 22:00:00 +02:00
// add it to the circular client_entities array
state = &svs.client_entities[svs.next_client_entities % svs.num_client_entities];
2008-12-26 22:00:00 +01:00
*state = ent->pvServerData->s;
2008-07-16 22:00:00 +02:00
svs.next_client_entities++;
2008-09-09 22:00:00 +02:00
// this should never hit, map should always be restarted first in SV_Frame
if( svs.next_client_entities >= 0x7FFFFFFE )
2009-02-03 22:00:00 +01:00
Host_Error( "svs.next_client_entities wrapped (sv.time limit is out)\n" );
2008-07-16 22:00:00 +02:00
frame->num_entities++;
}
}
/*
===============================================================================
FRAME UPDATES
===============================================================================
*/
/*
=======================
SV_SendClientDatagram
=======================
*/
bool SV_SendClientDatagram( sv_client_t *cl )
{
byte msg_buf[MAX_MSGLEN];
sizebuf_t msg;
SV_BuildClientFrame( cl );
2008-09-09 22:00:00 +02:00
MSG_Init( &msg, msg_buf, sizeof( msg_buf ));
2008-07-16 22:00:00 +02:00
// send over all the relevant entity_state_t
2008-07-30 22:00:00 +02:00
// and the player state
2008-07-16 22:00:00 +02:00
SV_WriteFrameToClient( cl, &msg );
2009-09-19 22:00:00 +02:00
// copy the accumulated reliable 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( cl->reliable.overflowed ) MsgDev( D_ERROR, "reliable datagram overflowed for %s\n", cl->name );
else MSG_WriteData( &msg, cl->reliable.data, cl->reliable.cursize );
MSG_Clear( &cl->reliable );
if( msg.overflowed )
{
// must have room left for the packet header
MsgDev( D_WARN, "msg overflowed for %s\n", cl->name );
MSG_Clear( &msg );
}
2008-07-16 22:00:00 +02:00
// 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( cl->datagram.overflowed ) MsgDev( D_WARN, "datagram overflowed for %s\n", cl->name );
else MSG_WriteData( &msg, cl->datagram.data, cl->datagram.cursize );
MSG_Clear( &cl->datagram );
if( msg.overflowed )
{
// must have room left for the packet header
MsgDev( D_WARN, "msg overflowed for %s\n", cl->name );
MSG_Clear( &msg );
}
2008-11-25 22:00:00 +01:00
2009-09-16 22:00:00 +02:00
// send the datagram
Netchan_Transmit( &cl->netchan, msg.cursize, msg.data );
2008-07-16 22:00:00 +02:00
return true;
}
/*
=======================
SV_SendClientMessages
=======================
*/
void SV_SendClientMessages( void )
{
sv_client_t *cl;
int i;
2009-11-25 22:00:00 +01:00
if( sv.state == ss_dead )
return;
2009-09-28 22:00:00 +02:00
2010-06-22 22:00:00 +02:00
// we always need to bump framenum, even if we
// don't run the world, otherwise the delta
// compression can get confused when a client
// has the "current" frame
sv.framenum++;
2008-07-16 22:00:00 +02:00
// send a message to each connected client
2009-09-25 22:00:00 +02:00
for( i = 0, cl = svs.clients; i < sv_maxclients->integer; i++, cl++ )
2008-07-16 22:00:00 +02:00
{
if( !cl->state ) continue;
2009-07-04 22:00:00 +02:00
2010-06-20 22:00:00 +02:00
if( !cl->edict || (cl->edict->v.flags & ( FL_FAKECLIENT|FL_SPECTATOR )))
2009-06-24 22:00:00 +02:00
continue;
2009-12-04 22:00:00 +01:00
// update any userinfo packets that have changed
if( cl->sendinfo )
{
cl->sendinfo = false;
SV_FullClientUpdate( cl, &sv.multicast );
}
2010-02-07 22:00:00 +01:00
if( cl->sendmovevars )
{
cl->sendmovevars = false;
SV_UpdatePhysinfo( cl, &sv.multicast );
}
2009-12-04 22:00:00 +01:00
2008-07-16 22:00:00 +02:00
// if the reliable message overflowed, drop the client
if( cl->netchan.message.overflowed )
{
MSG_Clear( &cl->netchan.message );
2009-09-19 22:00:00 +02:00
MSG_Clear( &cl->reliable );
2008-07-16 22:00:00 +02:00
MSG_Clear( &cl->datagram );
2009-06-24 22:00:00 +02:00
SV_BroadcastPrintf( PRINT_HIGH, "%s overflowed\n", cl->name );
2008-07-16 22:00:00 +02:00
SV_DropClient( cl );
2010-04-03 22:00:00 +02:00
cl->send_message = true;
2010-06-20 22:00:00 +02:00
cl->netchan.cleartime = 0; // don't choke this message
2008-07-16 22:00:00 +02:00
}
2010-04-03 22:00:00 +02:00
// only send messages if the client has sent one
if( !cl->send_message ) continue;
2010-06-20 22:00:00 +02:00
if( !sv.paused && !Netchan_CanPacket( &cl->netchan ))
{
cl->surpressCount++;
2010-06-22 22:00:00 +02:00
continue; // bandwidth choke
2010-06-20 22:00:00 +02:00
}
2008-08-02 22:00:00 +02:00
if( cl->state == cs_spawned )
2008-07-16 22:00:00 +02:00
{
SV_SendClientDatagram( cl );
}
else
{
2010-06-20 22:00:00 +02:00
if( cl->netchan.message.cursize )
2008-07-16 22:00:00 +02:00
Netchan_Transmit( &cl->netchan, 0, NULL );
}
2010-04-03 22:00:00 +02:00
// yes, message really sended
cl->send_message = false;
2008-07-16 22:00:00 +02:00
}
2010-03-24 22:00:00 +01:00
}
2010-04-03 22:00:00 +02:00
/*
=======================
SV_SendMessagesToAll
e.g. before changing level
=======================
*/
void SV_SendMessagesToAll( void )
{
int i;
sv_client_t *cl;
for( i = 0, cl = svs.clients; i < sv_maxclients->integer; i++, cl++ )
{
if( cl->state >= cs_connected )
cl->send_message = true;
}
SV_SendClientMessages();
}
2010-03-24 22:00:00 +01:00
/*
=======================
SV_InactivateClients
Purpose: Prepare for level transition, etc.
=======================
*/
void SV_InactivateClients( void )
{
int i;
sv_client_t *cl;
if( sv.state == ss_dead )
return;
// send a message to each connected client
for( i = 0, cl = svs.clients; i < sv_maxclients->integer; i++, cl++ )
{
if( !cl->state || !cl->edict ) continue;
2010-06-22 22:00:00 +02:00
if( !cl->edict || (cl->edict->v.flags & ( FL_FAKECLIENT|FL_SPECTATOR )))
2010-03-24 22:00:00 +01:00
continue;
if( svs.clients[i].state > cs_connected )
svs.clients[i].state = cs_connected;
// clear netchan message (but keep other buffers)
MSG_Clear( &cl->netchan.message );
}
2008-07-16 22:00:00 +02:00
}