2011-05-09 22:00:00 +02:00
|
|
|
/*
|
|
|
|
sv_frame.c - server world snapshot
|
|
|
|
Copyright (C) 2008 Uncle Mike
|
|
|
|
|
|
|
|
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 3 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.
|
|
|
|
*/
|
2008-07-09 22:00:00 +02:00
|
|
|
|
|
|
|
#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"
|
2010-08-04 22:00:00 +02:00
|
|
|
#include "net_encode.h"
|
2008-07-16 22:00:00 +02:00
|
|
|
|
2008-09-09 22:00:00 +02:00
|
|
|
typedef struct
|
2008-07-16 22:00:00 +02:00
|
|
|
{
|
2010-08-16 22:00:00 +02:00
|
|
|
int num_entities;
|
|
|
|
entity_state_t entities[MAX_VISIBLE_PACKET];
|
2008-09-09 22:00:00 +02:00
|
|
|
} 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
|
|
|
|
2010-10-14 22:00:00 +02:00
|
|
|
int c_fullsend; // just a debug counter
|
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 )
|
|
|
|
{
|
2010-08-16 22:00:00 +02:00
|
|
|
int ent1, ent2;
|
|
|
|
|
|
|
|
ent1 = ((entity_state_t *)a)->number;
|
|
|
|
ent2 = ((entity_state_t *)b)->number;
|
2008-07-16 22:00:00 +02:00
|
|
|
|
2010-08-16 22:00:00 +02:00
|
|
|
if( ent1 == ent2 )
|
|
|
|
Host_Error( "SV_SortEntities: duplicated entity\n" );
|
2008-07-16 22:00:00 +02:00
|
|
|
|
2010-08-16 22:00:00 +02:00
|
|
|
if( ent1 < ent2 )
|
|
|
|
return -1;
|
2008-09-09 22:00:00 +02:00
|
|
|
return 1;
|
2008-07-16 22:00:00 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
=============
|
2010-10-14 22:00:00 +02:00
|
|
|
SV_AddEntitiesToPacket
|
2008-07-16 22:00:00 +02:00
|
|
|
|
|
|
|
=============
|
|
|
|
*/
|
2010-08-15 22:00:00 +02:00
|
|
|
static void SV_AddEntitiesToPacket( edict_t *pViewEnt, edict_t *pClient, client_frame_t *frame, sv_ents_t *ents )
|
2008-09-09 22:00:00 +02:00
|
|
|
{
|
|
|
|
edict_t *ent;
|
2009-11-25 22:00:00 +01:00
|
|
|
byte *pset;
|
2010-10-26 22:00:00 +02:00
|
|
|
qboolean fullvis = false;
|
2012-05-17 22:00:00 +02:00
|
|
|
sv_client_t *netclient;
|
|
|
|
sv_client_t *cl = NULL;
|
2010-08-15 22:00:00 +02:00
|
|
|
entity_state_t *state;
|
2010-10-09 22:00:00 +02:00
|
|
|
int e, player;
|
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
|
2012-05-17 22:00:00 +02:00
|
|
|
// specifically check for it
|
2008-09-09 22:00:00 +02:00
|
|
|
if( !sv.state ) return;
|
|
|
|
|
2012-05-17 22:00:00 +02:00
|
|
|
cl = SV_ClientFromEdict( pClient, true );
|
|
|
|
ASSERT( cl );
|
|
|
|
|
2010-08-15 22:00:00 +02:00
|
|
|
if( pClient && !( sv.hostflags & SVF_PORTALPASS ))
|
2010-03-14 22:00:00 +01:00
|
|
|
{
|
|
|
|
// 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
|
|
|
|
|
|
|
// setup hostflags
|
2010-10-14 22:00:00 +02:00
|
|
|
if( cl->local_weapons )
|
|
|
|
{
|
2010-03-14 22:00:00 +01:00
|
|
|
sv.hostflags |= SVF_SKIPLOCALHOST;
|
2010-10-14 22:00:00 +02:00
|
|
|
}
|
2012-05-17 22:00:00 +02:00
|
|
|
|
|
|
|
// reset cameras each frame
|
|
|
|
cl->num_cameras = 0;
|
2010-03-14 22:00:00 +01:00
|
|
|
}
|
|
|
|
|
2010-08-15 22:00:00 +02:00
|
|
|
svgame.dllFuncs.pfnSetupVisibility( pViewEnt, pClient, &clientpvs, &clientphs );
|
2010-05-22 22:00:00 +02:00
|
|
|
if( !clientpvs ) fullvis = true;
|
2008-09-09 22:00:00 +02:00
|
|
|
|
2010-08-15 22:00:00 +02:00
|
|
|
for( e = 1; e < svgame.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
|
|
|
|
2009-11-25 22:00:00 +01:00
|
|
|
// don't double add an entity through portals (already added)
|
2011-04-05 22:00:00 +02:00
|
|
|
// HACHACK: use pushmsec to keep net_framenum
|
|
|
|
if( ent->v.pushmsec == sv.net_framenum )
|
2009-11-25 22:00:00 +01:00
|
|
|
continue;
|
2008-11-15 22:00:00 +01:00
|
|
|
|
2010-10-26 22:00:00 +02:00
|
|
|
if( ent->v.effects & EF_REQUEST_PHS )
|
2009-11-25 22:00:00 +01:00
|
|
|
pset = clientphs;
|
|
|
|
else pset = clientpvs;
|
2008-09-09 22:00:00 +02:00
|
|
|
|
2010-08-16 22:00:00 +02:00
|
|
|
state = &ents->entities[ents->num_entities];
|
2010-08-15 22:00:00 +02:00
|
|
|
netclient = SV_ClientFromEdict( ent, true );
|
2010-10-09 22:00:00 +02:00
|
|
|
player = ( netclient != NULL );
|
2010-08-15 22:00:00 +02:00
|
|
|
|
2010-06-28 22:00:00 +02:00
|
|
|
// add entity to the net packet
|
2010-10-09 22:00:00 +02:00
|
|
|
if( svgame.dllFuncs.pfnAddToFullPack( state, e, ent, pClient, sv.hostflags, player, pset ))
|
2009-11-25 22:00:00 +01:00
|
|
|
{
|
2010-06-28 22:00:00 +02:00
|
|
|
// to prevent adds it twice through portals
|
2011-04-05 22:00:00 +02:00
|
|
|
ent->v.pushmsec = sv.net_framenum;
|
2010-06-28 22:00:00 +02:00
|
|
|
|
2010-07-24 22:00:00 +02:00
|
|
|
if( netclient && netclient->modelindex ) // apply custom model if present
|
2010-08-15 22:00:00 +02:00
|
|
|
state->modelindex = netclient->modelindex;
|
2010-07-24 22:00:00 +02:00
|
|
|
|
2012-05-17 22:00:00 +02:00
|
|
|
if( SV_IsValidEdict( ent->v.aiment ) && ( ent->v.aiment->v.effects & EF_MERGE_VISIBILITY ))
|
|
|
|
{
|
|
|
|
if( cl != NULL && cl->num_cameras < MAX_CAMERAS )
|
|
|
|
{
|
|
|
|
cl->cameras[cl->num_cameras] = ent->v.aiment;
|
|
|
|
cl->num_cameras++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-06-28 22:00:00 +02:00
|
|
|
// if we are full, silently discard entities
|
|
|
|
if( ents->num_entities < MAX_VISIBLE_PACKET )
|
|
|
|
{
|
2010-08-16 22:00:00 +02:00
|
|
|
ents->num_entities++; // entity accepted
|
|
|
|
c_fullsend++; // debug counter
|
2010-06-28 22:00:00 +02:00
|
|
|
|
|
|
|
}
|
2010-10-14 22:00:00 +02:00
|
|
|
else
|
|
|
|
{
|
|
|
|
// visibility list is full
|
|
|
|
MsgDev( D_ERROR, "too many entities in visible packet list\n" );
|
|
|
|
break;
|
|
|
|
}
|
2008-09-09 22:00:00 +02:00
|
|
|
}
|
|
|
|
|
2010-05-22 22:00:00 +02:00
|
|
|
if( fullvis ) continue; // portal ents will be added anyway, ignore recursion
|
2010-08-15 22:00:00 +02:00
|
|
|
|
2008-09-09 22:00:00 +02:00
|
|
|
// if its a portal entity, add everything visible from its camera position
|
2010-08-15 22:00:00 +02:00
|
|
|
if( !( sv.hostflags & SVF_PORTALPASS ) && ent->v.effects & EF_MERGE_VISIBILITY )
|
2010-05-22 22:00:00 +02:00
|
|
|
{
|
2010-08-15 22:00:00 +02:00
|
|
|
sv.hostflags |= SVF_PORTALPASS;
|
|
|
|
SV_AddEntitiesToPacket( ent, pClient, frame, ents );
|
|
|
|
sv.hostflags &= ~SVF_PORTALPASS;
|
2010-05-22 22:00:00 +02:00
|
|
|
}
|
2008-09-09 22:00:00 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-10-14 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-10-15 22:00:00 +02:00
|
|
|
void SV_EmitPacketEntities( sv_client_t *cl, client_frame_t *to, sizebuf_t *msg )
|
2010-10-14 22:00:00 +02:00
|
|
|
{
|
2010-10-15 22:00:00 +02:00
|
|
|
entity_state_t *oldent, *newent;
|
2010-10-14 22:00:00 +02:00
|
|
|
int oldindex, newindex;
|
|
|
|
int oldnum, newnum;
|
2010-10-15 22:00:00 +02:00
|
|
|
int from_num_entities;
|
|
|
|
client_frame_t *from;
|
2010-10-14 22:00:00 +02:00
|
|
|
|
|
|
|
// this is the frame that we are going to delta update from
|
|
|
|
if( cl->delta_sequence != -1 )
|
|
|
|
{
|
2010-10-15 22:00:00 +02:00
|
|
|
from = &cl->frames[cl->delta_sequence & SV_UPDATE_MASK];
|
|
|
|
from_num_entities = from->num_entities;
|
2010-10-14 22:00:00 +02:00
|
|
|
|
2010-10-15 22:00:00 +02:00
|
|
|
// the snapshot's entities may still have rolled off the buffer, though
|
|
|
|
if( from->first_entity <= svs.next_client_entities - svs.num_client_entities )
|
|
|
|
{
|
|
|
|
MsgDev( D_WARN, "%s: delta request from out of date entities.\n", cl->name );
|
|
|
|
|
|
|
|
from = NULL;
|
|
|
|
from_num_entities = 0;
|
|
|
|
|
|
|
|
BF_WriteByte( msg, svc_packetentities );
|
|
|
|
BF_WriteWord( msg, to->num_entities );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
BF_WriteByte( msg, svc_deltapacketentities );
|
|
|
|
BF_WriteWord( msg, to->num_entities );
|
|
|
|
BF_WriteByte( msg, cl->delta_sequence );
|
|
|
|
}
|
2010-10-14 22:00:00 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
from = NULL;
|
2010-10-15 22:00:00 +02:00
|
|
|
from_num_entities = 0;
|
2010-10-14 22:00:00 +02:00
|
|
|
|
|
|
|
BF_WriteByte( msg, svc_packetentities );
|
|
|
|
BF_WriteWord( msg, to->num_entities );
|
|
|
|
}
|
|
|
|
|
2010-10-15 22:00:00 +02:00
|
|
|
newent = NULL;
|
|
|
|
oldent = NULL;
|
2010-10-14 22:00:00 +02:00
|
|
|
newindex = 0;
|
|
|
|
oldindex = 0;
|
|
|
|
|
2010-10-15 22:00:00 +02:00
|
|
|
while( newindex < to->num_entities || oldindex < from_num_entities )
|
2010-10-14 22:00:00 +02:00
|
|
|
{
|
2010-10-15 22:00:00 +02:00
|
|
|
if( newindex >= to->num_entities )
|
|
|
|
{
|
|
|
|
newnum = MAX_ENTNUMBER;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
newent = &svs.packet_entities[(to->first_entity+newindex)%svs.num_client_entities];
|
|
|
|
newnum = newent->number;
|
|
|
|
}
|
|
|
|
|
|
|
|
if( oldindex >= from_num_entities )
|
|
|
|
{
|
|
|
|
oldnum = MAX_ENTNUMBER;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
oldent = &svs.packet_entities[(from->first_entity+oldindex)%svs.num_client_entities];
|
|
|
|
oldnum = oldent->number;
|
|
|
|
}
|
2010-10-14 22:00:00 +02:00
|
|
|
|
|
|
|
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
|
2011-03-28 22:00:00 +02:00
|
|
|
MSG_WriteDeltaEntity( oldent, newent, msg, false, SV_IsPlayerIndex( newent->number ), sv.time );
|
2010-10-14 22:00:00 +02:00
|
|
|
oldindex++;
|
|
|
|
newindex++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if( newnum < oldnum )
|
|
|
|
{
|
|
|
|
// this is a new entity, send it from the baseline
|
2010-10-20 22:00:00 +02:00
|
|
|
MSG_WriteDeltaEntity( &svs.baselines[newnum], newent, msg, true, SV_IsPlayerIndex( newent->number ), sv.time );
|
2010-10-14 22:00:00 +02:00
|
|
|
newindex++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if( newnum > oldnum )
|
|
|
|
{
|
2010-10-26 22:00:00 +02:00
|
|
|
qboolean force;
|
2010-10-14 22:00:00 +02:00
|
|
|
|
2010-10-15 22:00:00 +02:00
|
|
|
if( EDICT_NUM( oldent->number )->free )
|
2010-10-14 22:00:00 +02:00
|
|
|
force = true; // entity completely removed from server
|
|
|
|
else force = false; // just removed from delta-message
|
|
|
|
|
|
|
|
// remove from message
|
2010-10-20 22:00:00 +02:00
|
|
|
MSG_WriteDeltaEntity( oldent, NULL, msg, force, false, sv.time );
|
2010-10-14 22:00:00 +02:00
|
|
|
oldindex++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
BF_WriteWord( msg, 0 ); // end of packetentities
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
=============
|
|
|
|
SV_EmitEvents
|
|
|
|
|
|
|
|
=============
|
|
|
|
*/
|
2010-10-15 22:00:00 +02:00
|
|
|
static void SV_EmitEvents( sv_client_t *cl, client_frame_t *to, sizebuf_t *msg )
|
2009-12-02 22:00:00 +01:00
|
|
|
{
|
|
|
|
event_state_t *es;
|
|
|
|
event_info_t *info;
|
2011-10-09 22:00:00 +02:00
|
|
|
entity_state_t *state;
|
|
|
|
event_args_t nullargs;
|
2009-12-02 22:00:00 +01:00
|
|
|
int ev_count = 0;
|
2011-10-09 22:00:00 +02:00
|
|
|
int count, ent_index;
|
|
|
|
int i, j, ev;
|
|
|
|
|
|
|
|
Q_memset( &nullargs, 0, sizeof( nullargs ));
|
2009-12-02 22:00:00 +01:00
|
|
|
|
|
|
|
es = &cl->events;
|
|
|
|
|
|
|
|
// count events
|
|
|
|
for( ev = 0; ev < MAX_EVENT_QUEUE; ev++ )
|
|
|
|
{
|
2011-10-09 22:00:00 +02:00
|
|
|
if( es->ei[ev].index ) ev_count++;
|
2009-12-02 22:00:00 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// nothing to send
|
2011-10-09 22:00:00 +02:00
|
|
|
if( !ev_count ) return; // nothing to send
|
|
|
|
|
|
|
|
if( ev_count >= 31 ) ev_count = 31;
|
|
|
|
|
|
|
|
for( i = 0; i < MAX_EVENT_QUEUE; i++ )
|
|
|
|
{
|
|
|
|
info = &es->ei[i];
|
|
|
|
if( info->index == 0 )
|
|
|
|
continue;
|
2009-12-02 22:00:00 +01:00
|
|
|
|
2011-10-09 22:00:00 +02:00
|
|
|
ent_index = info->entity_index;
|
|
|
|
|
|
|
|
for( j = 0; j < to->num_entities; j++ )
|
|
|
|
{
|
|
|
|
state = &svs.packet_entities[(to->first_entity+j)%svs.num_client_entities];
|
|
|
|
if( state->number == ent_index )
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if( j >= to->num_entities )
|
|
|
|
{
|
|
|
|
// couldn't find
|
|
|
|
info->packet_index = to->num_entities;
|
|
|
|
info->args.entindex = ent_index;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
info->packet_index = j;
|
|
|
|
info->args.ducking = 0;
|
|
|
|
|
|
|
|
if(!( info->args.flags & FEVENT_ORIGIN ))
|
|
|
|
VectorClear( info->args.origin );
|
|
|
|
|
|
|
|
if(!( info->args.flags & FEVENT_ANGLES ))
|
|
|
|
VectorClear( info->args.angles );
|
|
|
|
|
|
|
|
VectorClear( info->args.velocity );
|
|
|
|
}
|
|
|
|
}
|
2009-12-02 22:00:00 +01:00
|
|
|
|
2010-08-04 22:00:00 +02:00
|
|
|
BF_WriteByte( msg, svc_event ); // create message
|
2011-10-09 22:00:00 +02:00
|
|
|
BF_WriteUBitLong( msg, ev_count, 5 ); // up to MAX_EVENT_QUEUE events
|
2009-12-02 22:00:00 +01:00
|
|
|
|
2011-10-09 22:00:00 +02:00
|
|
|
for( count = i = 0; i < MAX_EVENT_QUEUE; i++ )
|
2009-12-02 22:00:00 +01:00
|
|
|
{
|
|
|
|
info = &es->ei[i];
|
|
|
|
|
|
|
|
if( info->index == 0 )
|
2011-10-09 22:00:00 +02:00
|
|
|
{
|
|
|
|
info->packet_index = -1;
|
|
|
|
info->entity_index = -1;
|
2009-12-02 22:00:00 +01:00
|
|
|
continue;
|
2011-10-09 22:00:00 +02:00
|
|
|
}
|
2009-12-02 22:00:00 +01:00
|
|
|
|
|
|
|
// only send if there's room
|
2011-10-09 22:00:00 +02:00
|
|
|
if( count < ev_count )
|
|
|
|
{
|
|
|
|
BF_WriteUBitLong( msg, info->index, MAX_EVENT_BITS ); // 1024 events
|
|
|
|
|
|
|
|
if( info->packet_index == -1 )
|
|
|
|
{
|
|
|
|
BF_WriteOneBit( msg, 0 );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
BF_WriteOneBit( msg, 1 );
|
|
|
|
BF_WriteUBitLong( msg, info->packet_index, MAX_ENTITY_BITS );
|
|
|
|
|
|
|
|
if( !memcmp( &nullargs, &info->args, sizeof( event_args_t )))
|
|
|
|
{
|
|
|
|
BF_WriteOneBit( msg, 0 );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
BF_WriteOneBit( msg, 1 );
|
|
|
|
MSG_WriteDeltaEvent( msg, &nullargs, &info->args );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if( info->fire_time )
|
|
|
|
{
|
|
|
|
BF_WriteOneBit( msg, 1 );
|
|
|
|
BF_WriteWord( msg, Q_rint( info->fire_time * 100.0f ));
|
|
|
|
}
|
|
|
|
else BF_WriteOneBit( msg, 0 );
|
|
|
|
}
|
2009-12-02 22:00:00 +01:00
|
|
|
|
|
|
|
info->index = 0;
|
2011-10-09 22:00:00 +02:00
|
|
|
info->packet_index = -1;
|
|
|
|
info->entity_index = -1;
|
|
|
|
count++;
|
2009-12-02 22:00:00 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-08-04 22:00:00 +02:00
|
|
|
/*
|
|
|
|
=============
|
2010-10-14 22:00:00 +02:00
|
|
|
SV_EmitPings
|
2010-08-04 22:00:00 +02:00
|
|
|
|
|
|
|
=============
|
|
|
|
*/
|
2010-10-14 22:00:00 +02:00
|
|
|
void SV_EmitPings( sizebuf_t *msg )
|
2010-08-04 22:00:00 +02:00
|
|
|
{
|
2010-10-14 22:00:00 +02:00
|
|
|
sv_client_t *cl;
|
|
|
|
int packet_loss;
|
|
|
|
int i, ping;
|
2010-08-04 22:00:00 +02:00
|
|
|
|
2010-10-14 22:00:00 +02:00
|
|
|
BF_WriteByte( msg, svc_updatepings );
|
2010-08-04 22:00:00 +02:00
|
|
|
|
2010-10-14 22:00:00 +02:00
|
|
|
for( i = 0, cl = svs.clients; i < sv_maxclients->integer; i++, cl++ )
|
2010-08-04 22:00:00 +02:00
|
|
|
{
|
2011-04-05 22:00:00 +02:00
|
|
|
if( cl->state != cs_spawned )
|
|
|
|
continue;
|
2010-10-14 22:00:00 +02:00
|
|
|
|
|
|
|
SV_GetPlayerStats( cl, &ping, &packet_loss );
|
|
|
|
|
|
|
|
// there are 25 bits for each client
|
|
|
|
BF_WriteOneBit( msg, 1 );
|
|
|
|
BF_WriteUBitLong( msg, i, MAX_CLIENT_BITS );
|
|
|
|
BF_WriteUBitLong( msg, ping, 12 );
|
|
|
|
BF_WriteUBitLong( msg, packet_loss, 7 );
|
2010-08-04 22:00:00 +02:00
|
|
|
}
|
|
|
|
|
2010-10-14 22:00:00 +02:00
|
|
|
// end marker
|
|
|
|
BF_WriteOneBit( msg, 0 );
|
2010-08-04 22:00:00 +02:00
|
|
|
}
|
|
|
|
|
2008-07-16 22:00:00 +02:00
|
|
|
/*
|
|
|
|
==================
|
2010-10-14 22:00:00 +02:00
|
|
|
SV_WriteClientdataToMessage
|
|
|
|
|
2008-07-16 22:00:00 +02:00
|
|
|
==================
|
|
|
|
*/
|
2010-10-14 22:00:00 +02:00
|
|
|
void SV_WriteClientdataToMessage( sv_client_t *cl, sizebuf_t *msg )
|
2008-07-16 22:00:00 +02:00
|
|
|
{
|
2010-10-14 22:00:00 +02:00
|
|
|
clientdata_t nullcd;
|
|
|
|
clientdata_t *from_cd, *to_cd;
|
|
|
|
weapon_data_t nullwd;
|
|
|
|
weapon_data_t *from_wd, *to_wd;
|
|
|
|
client_frame_t *frame;
|
|
|
|
edict_t *clent;
|
|
|
|
int i;
|
2010-06-22 22:00:00 +02:00
|
|
|
|
2011-03-10 22:00:00 +01:00
|
|
|
Q_memset( &nullcd, 0, sizeof( nullcd ));
|
2008-07-16 22:00:00 +02:00
|
|
|
|
2010-10-14 22:00:00 +02:00
|
|
|
clent = cl->edict;
|
2011-03-30 22:00:00 +02:00
|
|
|
frame = &cl->frames[cl->netchan.outgoing_sequence & SV_UPDATE_MASK];
|
2010-10-14 22:00:00 +02:00
|
|
|
|
|
|
|
frame->senttime = host.realtime;
|
|
|
|
frame->raw_ping = -1.0f;
|
|
|
|
frame->latency = -1.0f;
|
|
|
|
|
|
|
|
if( cl->chokecount != 0 )
|
2008-07-16 22:00:00 +02:00
|
|
|
{
|
2010-10-14 22:00:00 +02:00
|
|
|
BF_WriteByte( msg, svc_chokecount );
|
2011-04-10 22:00:00 +02:00
|
|
|
BF_WriteByte( msg, cl->chokecount );
|
2010-10-14 22:00:00 +02:00
|
|
|
cl->chokecount = 0;
|
2010-06-22 22:00:00 +02:00
|
|
|
}
|
2010-10-14 22:00:00 +02:00
|
|
|
|
|
|
|
// update client fixangle
|
|
|
|
switch( clent->v.fixangle )
|
|
|
|
{
|
|
|
|
case 1:
|
|
|
|
BF_WriteByte( msg, svc_setangle );
|
|
|
|
BF_WriteBitAngle( msg, clent->v.angles[0], 16 );
|
|
|
|
BF_WriteBitAngle( msg, clent->v.angles[1], 16 );
|
2011-07-07 22:00:00 +02:00
|
|
|
BF_WriteBitAngle( msg, clent->v.angles[2], 16 );
|
2010-10-14 22:00:00 +02:00
|
|
|
clent->v.effects |= EF_NOINTERP;
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
BF_WriteByte( msg, svc_addangle );
|
2011-04-05 22:00:00 +02:00
|
|
|
BF_WriteBitAngle( msg, clent->v.avelocity[1], 16 );
|
|
|
|
clent->v.avelocity[1] = 0.0f;
|
2010-10-14 22:00:00 +02:00
|
|
|
break;
|
2008-07-16 22:00:00 +02:00
|
|
|
}
|
2010-10-20 22:00:00 +02:00
|
|
|
|
2010-10-14 22:00:00 +02:00
|
|
|
clent->v.fixangle = 0; // reset fixangle
|
2011-04-05 22:00:00 +02:00
|
|
|
clent->v.pushmsec = 0; // reset net framenum
|
2011-03-10 22:00:00 +01:00
|
|
|
Q_memset( &frame->clientdata, 0, sizeof( frame->clientdata ));
|
2008-07-16 22:00:00 +02:00
|
|
|
|
2010-10-14 22:00:00 +02:00
|
|
|
// update clientdata_t
|
|
|
|
svgame.dllFuncs.pfnUpdateClientData( clent, cl->local_weapons, &frame->clientdata );
|
2009-12-02 22:00:00 +01:00
|
|
|
|
2010-10-14 22:00:00 +02:00
|
|
|
BF_WriteByte( msg, svc_clientdata );
|
|
|
|
if( cl->hltv_proxy ) return; // don't send more nothing
|
2008-07-16 22:00:00 +02:00
|
|
|
|
2010-10-14 22:00:00 +02:00
|
|
|
if( cl->delta_sequence == -1 ) from_cd = &nullcd;
|
|
|
|
else from_cd = &cl->frames[cl->delta_sequence & SV_UPDATE_MASK].clientdata;
|
|
|
|
to_cd = &frame->clientdata;
|
2010-08-04 22:00:00 +02:00
|
|
|
|
2010-10-14 22:00:00 +02:00
|
|
|
if( cl->delta_sequence == -1 )
|
|
|
|
{
|
|
|
|
BF_WriteOneBit( msg, 0 ); // no delta-compression
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
BF_WriteOneBit( msg, 1 ); // we are delta-ing from
|
|
|
|
BF_WriteByte( msg, cl->delta_sequence );
|
|
|
|
}
|
2008-07-16 22:00:00 +02:00
|
|
|
|
2010-10-14 22:00:00 +02:00
|
|
|
// write clientdata_t
|
|
|
|
MSG_WriteClientData( msg, from_cd, to_cd, sv.time );
|
2008-07-16 22:00:00 +02:00
|
|
|
|
2010-10-14 22:00:00 +02:00
|
|
|
if( cl->local_weapons && svgame.dllFuncs.pfnGetWeaponData( clent, frame->weapondata ))
|
|
|
|
{
|
2011-03-10 22:00:00 +01:00
|
|
|
Q_memset( &nullwd, 0, sizeof( nullwd ));
|
2008-07-16 22:00:00 +02:00
|
|
|
|
2010-10-14 22:00:00 +02:00
|
|
|
for( i = 0; i < 32; i++ )
|
|
|
|
{
|
|
|
|
if( cl->delta_sequence == -1 ) from_wd = &nullwd;
|
|
|
|
else from_wd = &cl->frames[cl->delta_sequence & SV_UPDATE_MASK].weapondata[i];
|
|
|
|
to_wd = &frame->weapondata[i];
|
|
|
|
|
|
|
|
MSG_WriteWeaponData( msg, from_wd, to_wd, sv.time, i );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// end marker
|
|
|
|
BF_WriteOneBit( msg, 0 );
|
|
|
|
}
|
2008-07-16 22:00:00 +02:00
|
|
|
|
|
|
|
/*
|
2010-10-14 22:00:00 +02:00
|
|
|
==================
|
|
|
|
SV_WriteEntitiesToClient
|
2008-07-16 22:00:00 +02:00
|
|
|
|
2010-10-14 22:00:00 +02:00
|
|
|
==================
|
2008-07-16 22:00:00 +02:00
|
|
|
*/
|
2010-10-14 22:00:00 +02:00
|
|
|
void SV_WriteEntitiesToClient( sv_client_t *cl, sizebuf_t *msg )
|
2008-07-16 22:00:00 +02:00
|
|
|
{
|
|
|
|
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;
|
2010-10-15 22:00:00 +02:00
|
|
|
entity_state_t *state;
|
2010-08-16 22:00:00 +02:00
|
|
|
static sv_ents_t frame_ents;
|
2010-10-15 22:00:00 +02:00
|
|
|
int i, send_pings;
|
2008-07-16 22:00:00 +02:00
|
|
|
|
|
|
|
clent = cl->edict;
|
2010-10-14 22:00:00 +02:00
|
|
|
viewent = cl->pViewEntity; // himself or trigger_camera
|
2010-06-28 22:00:00 +02:00
|
|
|
|
2010-10-14 22:00:00 +02:00
|
|
|
frame = &cl->frames[cl->netchan.outgoing_sequence & SV_UPDATE_MASK];
|
|
|
|
|
|
|
|
send_pings = SV_ShouldUpdatePing( cl );
|
|
|
|
|
|
|
|
sv.net_framenum++; // now all portal-through entities are invalidate
|
|
|
|
sv.hostflags &= ~SVF_PORTALPASS;
|
|
|
|
|
2008-09-09 22:00:00 +02:00
|
|
|
// clear everything in this snapshot
|
|
|
|
frame_ents.num_entities = c_fullsend = 0;
|
|
|
|
|
|
|
|
// add all the entities directly visible to the eye, which
|
|
|
|
// may include portal entities that merge other viewpoints
|
2010-08-15 22:00:00 +02:00
|
|
|
SV_AddEntitiesToPacket( viewent, clent, frame, &frame_ents );
|
2010-10-14 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
|
|
|
|
2010-10-15 22:00:00 +02:00
|
|
|
// copy the entity states out
|
|
|
|
frame->num_entities = 0;
|
|
|
|
frame->first_entity = svs.next_client_entities;
|
|
|
|
|
|
|
|
for( i = 0; i < frame_ents.num_entities; i++ )
|
|
|
|
{
|
|
|
|
// add it to the circular packet_entities array
|
|
|
|
state = &svs.packet_entities[svs.next_client_entities % svs.num_client_entities];
|
|
|
|
*state = frame_ents.entities[i];
|
|
|
|
svs.next_client_entities++;
|
|
|
|
|
|
|
|
// this should never hit, map should always be restarted first in SV_Frame
|
|
|
|
if( svs.next_client_entities >= 0x7FFFFFFE )
|
|
|
|
Host_Error( "svs.next_client_entities wrapped\n" );
|
|
|
|
frame->num_entities++;
|
|
|
|
}
|
2010-10-14 22:00:00 +02:00
|
|
|
|
2010-10-15 22:00:00 +02:00
|
|
|
SV_EmitPacketEntities( cl, frame, msg );
|
|
|
|
SV_EmitEvents( cl, frame, msg );
|
2010-10-14 22:00:00 +02:00
|
|
|
if( send_pings ) SV_EmitPings( msg );
|
2008-07-16 22:00:00 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
===============================================================================
|
|
|
|
|
|
|
|
FRAME UPDATES
|
|
|
|
|
|
|
|
===============================================================================
|
|
|
|
*/
|
|
|
|
/*
|
|
|
|
=======================
|
|
|
|
SV_SendClientDatagram
|
|
|
|
=======================
|
|
|
|
*/
|
2010-10-14 22:00:00 +02:00
|
|
|
void SV_SendClientDatagram( sv_client_t *cl )
|
2008-07-16 22:00:00 +02:00
|
|
|
{
|
2010-10-14 22:00:00 +02:00
|
|
|
byte msg_buf[NET_MAX_PAYLOAD];
|
2010-08-06 22:00:00 +02:00
|
|
|
sizebuf_t msg;
|
2008-07-16 22:00:00 +02:00
|
|
|
|
2010-10-14 22:00:00 +02:00
|
|
|
svs.currentPlayer = cl;
|
2011-04-21 22:00:00 +02:00
|
|
|
svs.currentPlayerNum = (cl - svs.clients);
|
2008-07-16 22:00:00 +02:00
|
|
|
|
2010-08-04 22:00:00 +02:00
|
|
|
BF_Init( &msg, "Datagram", msg_buf, sizeof( msg_buf ));
|
2008-07-16 22:00:00 +02:00
|
|
|
|
2010-10-14 22:00:00 +02:00
|
|
|
// always send servertime at new frame
|
|
|
|
BF_WriteByte( &msg, svc_time );
|
|
|
|
BF_WriteFloat( &msg, sv.time );
|
2008-07-16 22:00:00 +02:00
|
|
|
|
2010-10-14 22:00:00 +02:00
|
|
|
SV_WriteClientdataToMessage( cl, &msg );
|
|
|
|
SV_WriteEntitiesToClient( cl, &msg );
|
2009-09-19 22:00:00 +02:00
|
|
|
|
2008-07-16 22:00:00 +02:00
|
|
|
// copy the accumulated multicast datagram
|
|
|
|
// for this client out to the message
|
2010-08-04 22:00:00 +02:00
|
|
|
if( BF_CheckOverflow( &cl->datagram )) MsgDev( D_WARN, "datagram overflowed for %s\n", cl->name );
|
|
|
|
else BF_WriteBits( &msg, BF_GetData( &cl->datagram ), BF_GetNumBitsWritten( &cl->datagram ));
|
|
|
|
BF_Clear( &cl->datagram );
|
2008-07-16 22:00:00 +02:00
|
|
|
|
2010-08-04 22:00:00 +02:00
|
|
|
if( BF_CheckOverflow( &msg ))
|
2008-07-16 22:00:00 +02:00
|
|
|
{
|
|
|
|
// must have room left for the packet header
|
|
|
|
MsgDev( D_WARN, "msg overflowed for %s\n", cl->name );
|
2010-08-04 22:00:00 +02:00
|
|
|
BF_Clear( &msg );
|
2008-07-16 22:00:00 +02:00
|
|
|
}
|
2008-11-25 22:00:00 +01:00
|
|
|
|
2009-09-16 22:00:00 +02:00
|
|
|
// send the datagram
|
2010-08-04 22:00:00 +02:00
|
|
|
Netchan_TransmitBits( &cl->netchan, BF_GetNumBitsWritten( &msg ), BF_GetData( &msg ));
|
2010-07-02 22:00:00 +02:00
|
|
|
}
|
|
|
|
|
2010-10-09 22:00:00 +02:00
|
|
|
/*
|
|
|
|
=======================
|
|
|
|
SV_UpdateToReliableMessages
|
|
|
|
=======================
|
|
|
|
*/
|
|
|
|
void SV_UpdateToReliableMessages( void )
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
sv_client_t *cl;
|
|
|
|
|
|
|
|
// check for changes to be sent over the reliable streams to all clients
|
|
|
|
for( i = 0, cl = svs.clients; i < sv_maxclients->integer; i++, cl++ )
|
|
|
|
{
|
|
|
|
if( !cl->edict ) continue; // not in game yet
|
|
|
|
|
|
|
|
if( cl->state != cs_spawned )
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if( cl->sendinfo )
|
|
|
|
{
|
|
|
|
cl->sendinfo = false;
|
|
|
|
SV_FullClientUpdate( cl, &sv.reliable_datagram );
|
|
|
|
}
|
2010-10-14 22:00:00 +02:00
|
|
|
|
|
|
|
if( cl->sendmovevars )
|
|
|
|
{
|
|
|
|
cl->sendmovevars = false;
|
|
|
|
SV_FullUpdateMovevars( cl, &cl->netchan.message );
|
|
|
|
}
|
2010-10-09 22:00:00 +02:00
|
|
|
}
|
|
|
|
|
2010-10-22 22:00:00 +02:00
|
|
|
// 1% chanse for simulate random network bugs
|
|
|
|
if( sv.write_bad_message && Com_RandomLong( 0, 512 ) == 443 )
|
|
|
|
{
|
|
|
|
// just for network debugging (send only for local client)
|
|
|
|
BF_WriteByte( &sv.datagram, svc_bad );
|
|
|
|
BF_WriteLong( &sv.datagram, rand( )); // send some random data
|
|
|
|
BF_WriteString( &sv.datagram, host.finalmsg ); // send final message
|
|
|
|
sv.write_bad_message = false;
|
|
|
|
}
|
|
|
|
|
2010-10-09 22:00:00 +02:00
|
|
|
// clear the server datagram if it overflowed.
|
|
|
|
if( BF_CheckOverflow( &sv.datagram ))
|
|
|
|
{
|
|
|
|
MsgDev( D_ERROR, "sv.datagram overflowed!\n" );
|
|
|
|
BF_Clear( &sv.datagram );
|
|
|
|
}
|
|
|
|
|
2011-04-05 22:00:00 +02:00
|
|
|
// clear the server datagram if it overflowed.
|
|
|
|
if( BF_CheckOverflow( &sv.spectator_datagram ))
|
|
|
|
{
|
|
|
|
MsgDev( D_ERROR, "sv.spectator_datagram overflowed!\n" );
|
|
|
|
BF_Clear( &sv.spectator_datagram );
|
|
|
|
}
|
|
|
|
|
2010-10-09 22:00:00 +02:00
|
|
|
// now send the reliable and server datagrams to all clients.
|
|
|
|
for( i = 0, cl = svs.clients; i < sv_maxclients->integer; i++, cl++ )
|
|
|
|
{
|
|
|
|
if( cl->state < cs_connected || cl->fakeclient )
|
|
|
|
continue; // reliables go to all connected or spawned
|
|
|
|
|
|
|
|
BF_WriteBits( &cl->netchan.message, BF_GetData( &sv.reliable_datagram ), BF_GetNumBitsWritten( &sv.reliable_datagram ));
|
|
|
|
BF_WriteBits( &cl->datagram, BF_GetData( &sv.datagram ), BF_GetNumBitsWritten( &sv.datagram ));
|
2011-04-05 22:00:00 +02:00
|
|
|
|
|
|
|
if( cl->hltv_proxy )
|
|
|
|
{
|
|
|
|
BF_WriteBits( &cl->datagram, BF_GetData( &sv.spectator_datagram ), BF_GetNumBitsWritten( &sv.spectator_datagram ));
|
|
|
|
}
|
2010-10-09 22:00:00 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// now clear the reliable and datagram buffers.
|
2011-04-05 22:00:00 +02:00
|
|
|
BF_Clear( &sv.spectator_datagram );
|
2010-10-09 22:00:00 +02:00
|
|
|
BF_Clear( &sv.reliable_datagram );
|
|
|
|
BF_Clear( &sv.datagram );
|
|
|
|
}
|
|
|
|
|
2008-07-16 22:00:00 +02:00
|
|
|
/*
|
|
|
|
=======================
|
|
|
|
SV_SendClientMessages
|
|
|
|
=======================
|
|
|
|
*/
|
|
|
|
void SV_SendClientMessages( void )
|
|
|
|
{
|
|
|
|
sv_client_t *cl;
|
|
|
|
int i;
|
|
|
|
|
2010-06-23 22:00:00 +02:00
|
|
|
svs.currentPlayer = NULL;
|
2011-04-21 22:00:00 +02:00
|
|
|
svs.currentPlayerNum = 0;
|
2010-06-23 22:00:00 +02:00
|
|
|
|
2009-11-25 22:00:00 +01:00
|
|
|
if( sv.state == ss_dead )
|
|
|
|
return;
|
2009-09-28 22:00:00 +02:00
|
|
|
|
2010-10-09 22:00:00 +02:00
|
|
|
SV_UpdateToReliableMessages ();
|
|
|
|
|
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
|
|
|
{
|
2010-10-14 22:00:00 +02:00
|
|
|
if( !cl->state || cl->fakeclient )
|
2009-06-24 22:00:00 +02:00
|
|
|
continue;
|
|
|
|
|
2010-10-14 22:00:00 +02:00
|
|
|
if( cl->skip_message )
|
|
|
|
{
|
|
|
|
cl->skip_message = false;
|
|
|
|
continue;
|
|
|
|
}
|
2010-06-23 22:00:00 +02:00
|
|
|
|
2010-10-14 22:00:00 +02:00
|
|
|
if( !host_limitlocal->integer && NET_IsLocalAddress( cl->netchan.remote_address ))
|
|
|
|
cl->send_message = true;
|
|
|
|
|
|
|
|
if( cl->state == cs_spawned )
|
|
|
|
{
|
|
|
|
// Try to send a message as soon as we can.
|
|
|
|
// If the target time for sending is within the next frame interval ( based on last frame ),
|
|
|
|
// trigger the send now. Note that in single player,
|
|
|
|
// send_message is also set to true any time a packet arrives from the client.
|
|
|
|
float time_unti_next_message = cl->next_messagetime - (host.realtime + host.frametime);
|
|
|
|
|
|
|
|
if( time_unti_next_message <= 0.0f )
|
|
|
|
cl->send_message = true;
|
|
|
|
|
|
|
|
// something got hosed
|
|
|
|
if( time_unti_next_message > 2.0f )
|
|
|
|
cl->send_message = true;
|
|
|
|
}
|
2009-12-04 22:00:00 +01:00
|
|
|
|
2008-07-16 22:00:00 +02:00
|
|
|
// if the reliable message overflowed, drop the client
|
2010-08-04 22:00:00 +02:00
|
|
|
if( BF_CheckOverflow( &cl->netchan.message ))
|
2008-07-16 22:00:00 +02:00
|
|
|
{
|
2010-08-04 22:00:00 +02:00
|
|
|
BF_Clear( &cl->netchan.message );
|
|
|
|
BF_Clear( &cl->datagram );
|
2009-06-24 22:00:00 +02:00
|
|
|
SV_BroadcastPrintf( PRINT_HIGH, "%s overflowed\n", cl->name );
|
2010-10-14 22:00:00 +02:00
|
|
|
MsgDev( D_WARN, "reliable overflow for %s\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-10-14 22:00:00 +02:00
|
|
|
cl->netchan.cleartime = 0; // don't choke this message
|
|
|
|
}
|
|
|
|
else if( cl->send_message )
|
|
|
|
{
|
|
|
|
// If we haven't gotten a message in sv_failuretime seconds, then stop sending messages to this client
|
|
|
|
// until we get another packet in from the client. This prevents crash/drop and reconnect where they are
|
|
|
|
// being hosed with "sequenced packet without connection" packets.
|
|
|
|
if(( host.realtime - cl->netchan.last_received ) > sv_failuretime->value )
|
|
|
|
cl->send_message = false;
|
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
|
2010-10-14 22:00:00 +02:00
|
|
|
// and the bandwidth is not choked
|
2010-04-03 22:00:00 +02:00
|
|
|
if( !cl->send_message ) continue;
|
|
|
|
|
2010-10-14 22:00:00 +02:00
|
|
|
// Bandwidth choke active?
|
|
|
|
if( !Netchan_CanPacket( &cl->netchan ))
|
|
|
|
{
|
|
|
|
cl->chokecount++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
cl->send_message = false;
|
|
|
|
|
|
|
|
// Now that we were able to send, reset timer to point to next possible send time.
|
|
|
|
cl->next_messagetime = host.realtime + host.frametime + cl->cl_updaterate;
|
|
|
|
|
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
|
|
|
|
{
|
2011-04-05 22:00:00 +02:00
|
|
|
// jsut update reliable
|
2010-10-14 22:00:00 +02:00
|
|
|
Netchan_Transmit( &cl->netchan, 0, NULL );
|
2008-07-16 22:00:00 +02:00
|
|
|
}
|
|
|
|
}
|
2010-06-23 22:00:00 +02:00
|
|
|
|
|
|
|
// reset current client
|
|
|
|
svs.currentPlayer = NULL;
|
2011-04-21 22:00:00 +02:00
|
|
|
svs.currentPlayerNum = 0;
|
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;
|
|
|
|
|
2010-10-17 22:00:00 +02:00
|
|
|
if( sv.state == ss_dead )
|
|
|
|
return;
|
|
|
|
|
2010-04-03 22:00:00 +02:00
|
|
|
for( i = 0, cl = svs.clients; i < sv_maxclients->integer; i++, cl++ )
|
|
|
|
{
|
|
|
|
if( cl->state >= cs_connected )
|
|
|
|
cl->send_message = true;
|
|
|
|
}
|
|
|
|
SV_SendClientMessages();
|
|
|
|
}
|
|
|
|
|
2010-10-17 22:00:00 +02:00
|
|
|
/*
|
|
|
|
=======================
|
|
|
|
SV_SkipUpdates
|
|
|
|
|
|
|
|
used before changing level
|
|
|
|
=======================
|
|
|
|
*/
|
|
|
|
void SV_SkipUpdates( void )
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
sv_client_t *cl;
|
|
|
|
|
|
|
|
if( sv.state == ss_dead )
|
|
|
|
return;
|
|
|
|
|
|
|
|
for( i = 0, cl = svs.clients; i < sv_maxclients->integer; i++, cl++ )
|
|
|
|
{
|
|
|
|
if( cl->state != cs_spawned || cl->fakeclient )
|
|
|
|
continue;
|
|
|
|
cl->skip_message = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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;
|
|
|
|
|
2011-02-20 22:00:00 +01:00
|
|
|
if( !cl->edict || (cl->edict->v.flags & FL_FAKECLIENT))
|
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)
|
2010-08-04 22:00:00 +02:00
|
|
|
BF_Clear( &cl->netchan.message );
|
2010-03-24 22:00:00 +01:00
|
|
|
}
|
2008-07-16 22:00:00 +02:00
|
|
|
}
|