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

1018 lines
26 KiB
C
Raw Normal View History

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];
2017-03-16 22:00:00 +01:00
byte sended[MAX_EDICTS_BYTES];
2008-09-09 22:00:00 +02:00
} sv_ents_t;
2008-07-16 22:00:00 +02:00
2010-10-14 22:00:00 +02:00
int c_fullsend; // just a debug counter
2017-02-05 22:00:00 +01:00
int c_notsend;
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
=============
*/
2016-11-21 22:00:00 +01:00
static void SV_AddEntitiesToPacket( edict_t *pViewEnt, edict_t *pClient, client_frame_t *frame, sv_ents_t *ents, qboolean from_client )
2008-09-09 22:00:00 +02:00
{
edict_t *ent;
2016-11-21 22:00:00 +01:00
byte *clientpvs;
byte *clientphs;
2010-10-26 22:00:00 +02:00
qboolean fullvis = false;
2012-05-17 22:00:00 +02:00
sv_client_t *cl = NULL;
2018-04-25 23:00:00 +02:00
qboolean player;
2010-08-15 22:00:00 +02:00
entity_state_t *state;
2016-11-21 22:00:00 +01:00
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
2012-05-17 22:00:00 +02:00
// specifically check for it
2016-11-21 22:00:00 +01:00
if( sv.state == ss_dead )
return;
2008-09-09 22:00:00 +02:00
2012-05-17 22:00:00 +02:00
cl = SV_ClientFromEdict( pClient, true );
2012-12-16 21:00:00 +01:00
2018-03-05 22:00:00 +01:00
ASSERT( cl != NULL );
2012-05-17 22:00:00 +02:00
2016-11-21 22:00:00 +01:00
// portals can't change hostflags
2018-03-05 22:00:00 +01:00
if( from_client )
2010-03-14 22:00:00 +01:00
{
// setup hostflags
2016-11-21 22:00:00 +01:00
if( FBitSet( cl->flags, FCL_LOCAL_WEAPONS ))
SetBits( sv.hostflags, SVF_SKIPLOCALHOST );
else ClearBits( sv.hostflags, SVF_SKIPLOCALHOST );
2012-05-17 22:00:00 +02:00
2016-11-21 22:00:00 +01:00
// reset viewents each frame
cl->num_viewents = 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
2018-03-06 22:00:00 +01:00
// g-cont: of course we can send world but not want to do it :-)
2010-08-15 22:00:00 +02:00
for( e = 1; e < svgame.numEntities; e++ )
2008-09-09 22:00:00 +02:00
{
2016-11-21 22:00:00 +01:00
byte *pset;
2008-12-15 22:00:00 +01:00
ent = EDICT_NUM( e );
2016-11-21 22:00:00 +01:00
2017-02-21 22:00:00 +01:00
// don't double add an entity through portals (in case this already added)
2017-03-16 22:00:00 +01:00
if( CHECKVISBIT( ents->sended, e ))
2009-11-25 22:00:00 +01:00
continue;
2008-11-15 22:00:00 +01:00
2018-04-25 23:00:00 +02:00
if( e >= 1 && e <= svs.maxclients )
player = 1;
else player = 0;
if( player )
{
sv_client_t *cl = &svs.clients[e - 1];
if( cl->state != cs_spawned )
continue;
if( FBitSet( cl->flags, FCL_HLTV_PROXY ))
continue;
}
2016-11-21 22:00:00 +01:00
if( FBitSet( 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
2010-06-28 22:00:00 +02:00
// add entity to the net packet
2018-04-25 23: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
2017-03-16 22:00:00 +01:00
SETVISBIT( ents->sended, e );
2010-06-28 22:00:00 +02:00
2016-11-21 22:00:00 +01:00
if( SV_IsValidEdict( ent->v.aiment ) && FBitSet( ent->v.aiment->v.effects, EF_MERGE_VISIBILITY ))
2012-05-17 22:00:00 +02:00
{
2018-03-05 22:00:00 +01:00
if( cl->num_viewents < MAX_VIEWENTS )
2012-05-17 22:00:00 +02:00
{
2016-11-21 22:00:00 +01:00
cl->viewentity[cl->num_viewents] = ent->v.aiment;
cl->num_viewents++;
2012-05-17 22:00:00 +02:00
}
}
2010-06-28 22:00:00 +02:00
// if we are full, silently discard entities
2018-09-20 23:00:00 +02:00
if( ents->num_entities < ( MAX_VISIBLE_PACKET - 1 ))
2010-06-28 22:00:00 +02:00
{
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
2017-02-05 22:00:00 +01:00
// continue counting entities,
// so we know how many it's ovreflowed
c_notsend++;
2010-10-14 22:00:00 +02:00
}
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
2016-11-21 22:00:00 +01:00
if( from_client && FBitSet( ent->v.effects, EF_MERGE_VISIBILITY ))
2010-05-22 22:00:00 +02:00
{
2016-11-21 22:00:00 +01:00
SetBits( sv.hostflags, SVF_MERGE_VISIBILITY );
SV_AddEntitiesToPacket( ent, pClient, frame, ents, false );
ClearBits( sv.hostflags, SVF_MERGE_VISIBILITY );
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
=============================================================================
*/
2018-06-17 23:00:00 +02:00
/*
=============
SV_FindBestBaseline
trying to deltas with previous entities
=============
*/
2018-03-06 22:00:00 +01:00
int SV_FindBestBaseline( sv_client_t *cl, int index, entity_state_t **baseline, entity_state_t *to, client_frame_t *frame, qboolean player )
2018-02-20 22:00:00 +01:00
{
int bestBitCount;
int i, bitCount;
int bestfound, j;
bestBitCount = j = Delta_TestBaseline( *baseline, to, player, sv.time );
bestfound = index;
// lookup backward for previous 64 states and try to interpret current delta as baseline
for( i = index - 1; bestBitCount > 0 && i >= 0 && ( index - i ) < ( MAX_CUSTOM_BASELINES - 1 ); i-- )
{
// don't worry about underflow in circular buffer
entity_state_t *test = &svs.packet_entities[(frame->first_entity+i) % svs.num_client_entities];
if( to->entityType == test->entityType )
{
bitCount = Delta_TestBaseline( test, to, player, sv.time );
if( bitCount < bestBitCount )
{
bestBitCount = bitCount;
bestfound = i;
}
}
}
// using delta from previous entity as baseline for current
if( index != bestfound )
*baseline = &svs.packet_entities[(frame->first_entity+bestfound) % svs.num_client_entities];
return index - bestfound;
}
2018-06-17 23:00:00 +02:00
/*
=============
SV_FindBestBaselineForStatic
trying to deltas with previous static entities
=============
*/
int SV_FindBestBaselineForStatic( int index, entity_state_t **baseline, entity_state_t *to )
{
int bestBitCount;
int i, bitCount;
int bestfound, j;
bestBitCount = j = Delta_TestBaseline( *baseline, to, false, sv.time );
bestfound = index;
// lookup backward for previous 64 states and try to interpret current delta as baseline
for( i = index - 1; bestBitCount > 0 && i >= 0 && ( index - i ) < ( MAX_CUSTOM_BASELINES - 1 ); i-- )
{
// don't worry about underflow in circular buffer
entity_state_t *test = &svs.static_entities[i];
bitCount = Delta_TestBaseline( test, to, false, sv.time );
if( bitCount < bestBitCount )
{
bestBitCount = bitCount;
bestfound = i;
}
}
// using delta from previous entity as baseline for current
if( index != bestfound )
*baseline = &svs.static_entities[bestfound];
return index - bestfound;
}
2010-10-14 22:00:00 +02:00
/*
=============
SV_EmitPacketEntities
Writes a delta update of an entity_state_t list to the message->
=============
*/
2018-03-05 22:00:00 +01:00
static 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;
2018-02-20 22:00:00 +01:00
int i, oldnum, newnum;
qboolean player;
2017-02-21 22:00:00 +01:00
int oldmax;
2010-10-15 22:00:00 +02:00
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];
2017-02-21 22:00:00 +01:00
oldmax = 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
2016-11-21 22:00:00 +01:00
if( from->first_entity <= ( svs.next_client_entities - svs.num_client_entities ))
2010-10-15 22:00:00 +02:00
{
2018-03-05 22:00:00 +01:00
Con_DPrintf( S_WARN "%s: delta request from out of date entities.\n", cl->name );
2017-02-05 22:00:00 +01:00
MSG_BeginServerCmd( msg, svc_packetentities );
2017-03-29 23:00:00 +02:00
MSG_WriteUBitLong( msg, to->num_entities - 1, MAX_VISIBLE_PACKET_BITS );
2017-02-21 22:00:00 +01:00
from = NULL;
oldmax = 0;
2010-10-15 22:00:00 +02:00
}
else
{
2017-02-05 22:00:00 +01:00
MSG_BeginServerCmd( msg, svc_deltapacketentities );
2017-03-29 23:00:00 +02:00
MSG_WriteUBitLong( msg, to->num_entities - 1, MAX_VISIBLE_PACKET_BITS );
2016-11-14 22:00:00 +01:00
MSG_WriteByte( msg, cl->delta_sequence );
2010-10-15 22:00:00 +02:00
}
2010-10-14 22:00:00 +02:00
}
else
{
from = NULL;
2017-02-21 22:00:00 +01:00
oldmax = 0;
2010-10-14 22:00:00 +02:00
2017-02-05 22:00:00 +01:00
MSG_BeginServerCmd( msg, svc_packetentities );
2017-03-29 23:00:00 +02:00
MSG_WriteUBitLong( msg, to->num_entities - 1, MAX_VISIBLE_PACKET_BITS );
2010-10-14 22:00:00 +02:00
}
2010-10-15 22:00:00 +02:00
newent = NULL;
oldent = NULL;
2010-10-14 22:00:00 +02:00
newindex = 0;
oldindex = 0;
2017-02-21 22:00:00 +01:00
while( newindex < to->num_entities || oldindex < oldmax )
2010-10-14 22:00:00 +02:00
{
2010-10-15 22:00:00 +02:00
if( newindex >= to->num_entities )
{
newnum = MAX_ENTNUMBER;
2018-02-20 22:00:00 +01:00
player = false;
2010-10-15 22:00:00 +02:00
}
else
{
2016-11-21 22:00:00 +01:00
newent = &svs.packet_entities[(to->first_entity+newindex) % svs.num_client_entities];
2018-02-20 22:00:00 +01:00
player = SV_IsPlayerIndex( newent->number );
2010-10-15 22:00:00 +02:00
newnum = newent->number;
}
2017-02-21 22:00:00 +01:00
if( oldindex >= oldmax )
2010-10-15 22:00:00 +02:00
{
oldnum = MAX_ENTNUMBER;
}
else
{
2016-11-21 22:00:00 +01:00
oldent = &svs.packet_entities[(from->first_entity+oldindex) % svs.num_client_entities];
2010-10-15 22:00:00 +02:00
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
2018-02-20 22:00:00 +01:00
MSG_WriteDeltaEntity( oldent, newent, msg, false, player, sv.time, 0 );
2010-10-14 22:00:00 +02:00
oldindex++;
newindex++;
continue;
}
if( newnum < oldnum )
{
2018-02-20 22:00:00 +01:00
entity_state_t *baseline = &svs.baselines[newnum];
const char *classname = SV_ClassName( EDICT_NUM( newnum ));
int offset = 0;
// trying to reduce message by select optimal baseline
2018-03-13 22:00:00 +01:00
if( !sv_instancedbaseline.value || !sv.num_instanced || sv.last_valid_baseline > newnum )
2018-02-20 22:00:00 +01:00
{
2018-03-06 22:00:00 +01:00
offset = SV_FindBestBaseline( cl, newindex, &baseline, newent, to, player );
2018-02-20 22:00:00 +01:00
}
else
{
2018-03-13 22:00:00 +01:00
for( i = 0; i < sv.num_instanced; i++ )
2018-02-20 22:00:00 +01:00
{
2018-03-13 22:00:00 +01:00
if( !Q_strcmp( classname, sv.instanced[i].classname ))
2018-02-20 22:00:00 +01:00
{
2018-03-13 22:00:00 +01:00
baseline = &sv.instanced[i].baseline;
2018-02-20 22:00:00 +01:00
offset = -i;
break;
}
}
}
2010-10-14 22:00:00 +02:00
// this is a new entity, send it from the baseline
2018-02-20 22:00:00 +01:00
MSG_WriteDeltaEntity( baseline, newent, msg, true, player, sv.time, offset );
2010-10-14 22:00:00 +02:00
newindex++;
continue;
}
if( newnum > oldnum )
{
2017-08-12 23:00:00 +02:00
edict_t *ed = EDICT_NUM( oldent->number );
2018-02-20 22:00:00 +01:00
qboolean force = false;
2010-10-14 22:00:00 +02:00
2017-02-21 22:00:00 +01:00
// check if entity completely removed from server
2017-08-12 23:00:00 +02:00
if( ed->free || FBitSet( ed->v.flags, FL_KILLME ))
2017-02-21 22:00:00 +01:00
force = true;
2010-10-14 22:00:00 +02:00
// remove from message
2018-02-20 22:00:00 +01:00
MSG_WriteDeltaEntity( oldent, NULL, msg, force, false, sv.time, 0 );
2010-10-14 22:00:00 +02:00
oldindex++;
continue;
}
}
2018-02-20 22:00:00 +01:00
MSG_WriteUBitLong( msg, LAST_EDICT, MAX_ENTITY_BITS ); // end of packetentities
2010-10-14 22:00:00 +02:00
}
/*
=============
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;
2016-11-17 22:00:00 +01:00
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++ )
{
2017-02-13 22:00:00 +01: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
2017-02-13 22:00:00 +01:00
if ( ev_count >= MAX_EVENT_QUEUE / 2 )
ev_count = ( MAX_EVENT_QUEUE / 2 ) - 1;
2011-10-09 22:00:00 +02:00
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++ )
{
2016-11-21 22:00:00 +01:00
state = &svs.packet_entities[(to->first_entity+j) % svs.num_client_entities];
2011-10-09 22:00:00 +02:00
if( state->number == ent_index )
break;
}
2017-02-13 22:00:00 +01:00
if( j < to->num_entities )
2011-10-09 22:00:00 +02:00
{
info->packet_index = j;
info->args.ducking = 0;
2016-11-21 22:00:00 +01:00
if( !FBitSet( info->args.flags, FEVENT_ORIGIN ))
2011-10-09 22:00:00 +02:00
VectorClear( info->args.origin );
2016-11-21 22:00:00 +01:00
if( !FBitSet( info->args.flags, FEVENT_ANGLES ))
2011-10-09 22:00:00 +02:00
VectorClear( info->args.angles );
VectorClear( info->args.velocity );
}
2017-02-13 22:00:00 +01:00
else
{
// couldn't find
info->packet_index = to->num_entities;
info->args.entindex = ent_index;
}
2011-10-09 22:00:00 +02:00
}
2009-12-02 22:00:00 +01:00
2017-02-05 22:00:00 +01:00
MSG_BeginServerCmd( msg, svc_event ); // create message
2016-11-14 22:00:00 +01:00
MSG_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 )
{
2016-11-14 22:00:00 +01:00
MSG_WriteUBitLong( msg, info->index, MAX_EVENT_BITS ); // 1024 events
2011-10-09 22:00:00 +02:00
if( info->packet_index == -1 )
{
2016-11-14 22:00:00 +01:00
MSG_WriteOneBit( msg, 0 );
2011-10-09 22:00:00 +02:00
}
else
{
2016-11-14 22:00:00 +01:00
MSG_WriteOneBit( msg, 1 );
MSG_WriteUBitLong( msg, info->packet_index, MAX_ENTITY_BITS );
2011-10-09 22:00:00 +02:00
2016-11-17 22:00:00 +01:00
if( !memcmp( &nullargs, &info->args, sizeof( event_args_t )))
2011-10-09 22:00:00 +02:00
{
2016-11-14 22:00:00 +01:00
MSG_WriteOneBit( msg, 0 );
2011-10-09 22:00:00 +02:00
}
else
{
2016-11-14 22:00:00 +01:00
MSG_WriteOneBit( msg, 1 );
2011-10-09 22:00:00 +02:00
MSG_WriteDeltaEvent( msg, &nullargs, &info->args );
}
}
if( info->fire_time )
{
2016-11-14 22:00:00 +01:00
MSG_WriteOneBit( msg, 1 );
2017-02-13 22:00:00 +01:00
MSG_WriteWord( msg, ( info->fire_time * 100.0f ));
2011-10-09 22:00:00 +02:00
}
2016-11-14 22:00:00 +01:00
else MSG_WriteOneBit( msg, 0 );
2011-10-09 22:00:00 +02:00
}
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
2017-02-07 22:00:00 +01:00
MSG_BeginServerCmd( msg, svc_pings );
2010-08-04 22:00:00 +02:00
2017-02-12 22:00:00 +01:00
for( i = 0, cl = svs.clients; i < svs.maxclients; 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
2016-11-14 22:00:00 +01:00
MSG_WriteOneBit( msg, 1 );
MSG_WriteUBitLong( msg, i, MAX_CLIENT_BITS );
MSG_WriteUBitLong( msg, ping, 12 );
MSG_WriteUBitLong( msg, packet_loss, 7 );
2010-08-04 22:00:00 +02:00
}
2010-10-14 22:00:00 +02:00
// end marker
2016-11-14 22:00:00 +01:00
MSG_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
2016-11-17 22:00:00 +01:00
memset( &nullcd, 0, sizeof( nullcd ));
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;
2015-12-26 22:00:00 +01:00
frame->ping_time = -1.0f;
2018-03-05 22:00:00 +01:00
clent = cl->edict;
2010-10-14 22:00:00 +02:00
if( cl->chokecount != 0 )
2008-07-16 22:00:00 +02:00
{
2017-03-02 22:00:00 +01:00
MSG_BeginServerCmd( msg, svc_choke );
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:
2017-02-05 22:00:00 +01:00
MSG_BeginServerCmd( msg, svc_setangle );
2018-03-05 22:00:00 +01:00
MSG_WriteVec3Angles( msg, clent->v.angles );
2010-10-14 22:00:00 +02:00
break;
case 2:
2017-02-05 22:00:00 +01:00
MSG_BeginServerCmd( msg, svc_addangle );
2017-02-21 22:00:00 +01:00
MSG_WriteBitAngle( msg, clent->v.avelocity[YAW], 16 );
clent->v.avelocity[YAW] = 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
2016-11-21 22:00:00 +01:00
2016-11-17 22:00:00 +01:00
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
2016-11-21 22:00:00 +01:00
svgame.dllFuncs.pfnUpdateClientData( clent, FBitSet( cl->flags, FCL_LOCAL_WEAPONS ), &frame->clientdata );
2009-12-02 22:00:00 +01:00
2017-02-05 22:00:00 +01:00
MSG_BeginServerCmd( msg, svc_clientdata );
2016-11-21 22:00:00 +01:00
if( FBitSet( cl->flags, FCL_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 )
{
2016-11-14 22:00:00 +01:00
MSG_WriteOneBit( msg, 0 ); // no delta-compression
2010-10-14 22:00:00 +02:00
}
else
{
2016-11-14 22:00:00 +01:00
MSG_WriteOneBit( msg, 1 ); // we are delta-ing from
MSG_WriteByte( msg, cl->delta_sequence );
2010-10-14 22:00:00 +02:00
}
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
2016-11-21 22:00:00 +01:00
if( FBitSet( cl->flags, FCL_LOCAL_WEAPONS ) && svgame.dllFuncs.pfnGetWeaponData( clent, frame->weapondata ))
2010-10-14 22:00:00 +02:00
{
2016-11-17 22:00:00 +01:00
memset( &nullwd, 0, sizeof( nullwd ));
2008-07-16 22:00:00 +02:00
2016-11-21 22:00:00 +01:00
for( i = 0; i < MAX_LOCAL_WEAPONS; i++ )
2010-10-14 22:00:00 +02:00
{
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
2016-11-14 22:00:00 +01:00
MSG_WriteOneBit( msg, 0 );
2010-10-14 22:00:00 +02:00
}
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
{
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
2016-11-21 22:00:00 +01:00
frame = &cl->frames[cl->netchan.outgoing_sequence & SV_UPDATE_MASK];
2010-10-14 22:00:00 +02:00
send_pings = SV_ShouldUpdatePing( cl );
2017-02-21 22:00:00 +01:00
memset( frame_ents.sended, 0, sizeof( frame_ents.sended ));
2016-11-21 22:00:00 +01:00
ClearBits( sv.hostflags, SVF_MERGE_VISIBILITY );
2010-10-14 22:00:00 +02:00
2008-09-09 22:00:00 +02:00
// clear everything in this snapshot
2017-02-05 22:00:00 +01:00
frame_ents.num_entities = c_fullsend = c_notsend = 0;
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
2018-03-05 22:00:00 +01:00
SV_AddEntitiesToPacket( cl->pViewEntity, cl->edict, frame, &frame_ents, true );
2017-02-05 22:00:00 +01:00
if( c_notsend != cl->ignored_ents )
{
if( c_notsend > 0 )
2018-03-05 22:00:00 +01:00
Con_Printf( S_ERROR "Too many entities in visible packet list. Ignored %d entities\n", c_notsend );
2017-02-05 22:00:00 +01:00
cl->ignored_ents = c_notsend;
}
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
2016-11-21 22:00:00 +01:00
// it will break all connected clients, but it takes more than one week to overflow it
2016-08-13 23:00:00 +02:00
if(( (uint)svs.next_client_entities ) + frame_ents.num_entities >= 0x7FFFFFFE )
{
svs.next_client_entities = 0;
2017-02-21 22:00:00 +01:00
// delta is broken for now, cannot keep connected clients
2018-03-04 22:00:00 +01:00
SV_FinalMessage( "Server will restart due delta is outdated\n", true );
2016-08-13 23:00:00 +02:00
}
2016-11-21 22:00:00 +01:00
// copy the entity states out
2010-10-15 22:00:00 +02:00
frame->first_entity = svs.next_client_entities;
2017-02-21 22:00:00 +01:00
frame->num_entities = 0;
2010-10-15 22:00:00 +02:00
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++;
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
{
2018-03-11 22:00:00 +01:00
byte msg_buf[MAX_DATAGRAM];
sizebuf_t msg;
2008-07-16 22:00:00 +02:00
2016-11-14 22:00:00 +01:00
MSG_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
2017-02-05 22:00:00 +01:00
MSG_BeginServerCmd( &msg, svc_time );
2016-11-14 22:00:00 +01:00
MSG_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
2017-02-05 22:00:00 +01:00
if( MSG_CheckOverflow( &cl->datagram ))
{
2018-03-11 22:00:00 +01:00
Con_Printf( S_WARN "%s overflowed for %s\n", MSG_GetName( &cl->datagram ), cl->name );
2017-02-05 22:00:00 +01:00
}
else
{
if( MSG_GetNumBytesWritten( &cl->datagram ) < MSG_GetNumBytesLeft( &msg ))
MSG_WriteBits( &msg, MSG_GetData( &cl->datagram ), MSG_GetNumBitsWritten( &cl->datagram ));
else MsgDev( D_WARN, "Ignoring unreliable datagram for %s, would overflow on msg\n", cl->name );
}
2016-11-14 22:00:00 +01:00
MSG_Clear( &cl->datagram );
2008-07-16 22:00:00 +02:00
2016-11-14 22:00:00 +01:00
if( MSG_CheckOverflow( &msg ))
2008-07-16 22:00:00 +02:00
{
// must have room left for the packet header
2018-03-17 22:00:00 +01:00
Con_Printf( S_ERROR "%s overflowed for %s\n", MSG_GetName( &msg ), cl->name );
2016-11-14 22:00:00 +01:00
MSG_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
2016-11-14 22:00:00 +01:00
Netchan_TransmitBits( &cl->netchan, MSG_GetNumBitsWritten( &msg ), MSG_GetData( &msg ));
2010-07-02 22:00:00 +02:00
}
2017-02-05 22:00:00 +01:00
/*
=======================
SV_UpdateUserInfo
=======================
*/
void SV_UpdateUserInfo( sv_client_t *cl )
{
SV_FullClientUpdate( cl, &sv.reliable_datagram );
ClearBits( cl->flags, FCL_RESEND_USERINFO );
cl->next_sendinfotime = host.realtime + 1.0;
}
2010-10-09 22:00:00 +02:00
/*
=======================
SV_UpdateToReliableMessages
=======================
*/
void SV_UpdateToReliableMessages( void )
{
sv_client_t *cl;
2016-11-21 22:00:00 +01:00
int i;
2010-10-09 22:00:00 +02:00
// check for changes to be sent over the reliable streams to all clients
2017-02-12 22:00:00 +01:00
for( i = 0, cl = svs.clients; i < svs.maxclients; i++, cl++ )
2010-10-09 22:00:00 +02:00
{
if( !cl->edict ) continue; // not in game yet
if( cl->state != cs_spawned )
continue;
2017-02-05 22:00:00 +01:00
if( FBitSet( cl->flags, FCL_RESEND_USERINFO ) && cl->next_sendinfotime <= host.realtime )
2010-10-09 22:00:00 +02:00
{
2017-02-05 22:00:00 +01:00
if( MSG_GetNumBytesLeft( &sv.reliable_datagram ) >= ( Q_strlen( cl->userinfo ) + 6 ))
SV_UpdateUserInfo( cl );
2010-10-09 22:00:00 +02:00
}
2010-10-14 22:00:00 +02:00
2016-11-21 22:00:00 +01:00
if( FBitSet( cl->flags, FCL_RESEND_MOVEVARS ))
2010-10-14 22:00:00 +02:00
{
SV_FullUpdateMovevars( cl, &cl->netchan.message );
2016-11-21 22:00:00 +01:00
ClearBits( cl->flags, FCL_RESEND_MOVEVARS );
}
2010-10-09 22:00:00 +02:00
}
2011-04-05 22:00:00 +02:00
// clear the server datagram if it overflowed.
2017-02-05 22:00:00 +01:00
if( MSG_CheckOverflow( &sv.datagram ))
2011-04-05 22:00:00 +02:00
{
2017-02-05 22:00:00 +01:00
MsgDev( D_ERROR, "sv.datagram overflowed!\n" );
MSG_Clear( &sv.datagram );
}
// clear the server datagram if it overflowed.
if( MSG_CheckOverflow( &sv.spec_datagram ))
{
MsgDev( D_ERROR, "sv.spec_datagram overflowed!\n" );
MSG_Clear( &sv.spec_datagram );
2011-04-05 22:00:00 +02:00
}
2010-10-09 22:00:00 +02:00
// now send the reliable and server datagrams to all clients.
2017-02-12 22:00:00 +01:00
for( i = 0, cl = svs.clients; i < svs.maxclients; i++, cl++ )
2010-10-09 22:00:00 +02:00
{
2016-11-21 22:00:00 +01:00
if( cl->state < cs_connected || FBitSet( cl->flags, FCL_FAKECLIENT ))
2010-10-09 22:00:00 +02:00
continue; // reliables go to all connected or spawned
2017-02-05 22:00:00 +01:00
if( MSG_GetNumBytesWritten( &sv.reliable_datagram ) < MSG_GetNumBytesLeft( &cl->netchan.message ))
{
MSG_WriteBits( &cl->netchan.message, MSG_GetBuf( &sv.reliable_datagram ), MSG_GetNumBitsWritten( &sv.reliable_datagram ));
}
else
{
Netchan_CreateFragments( &cl->netchan, &sv.reliable_datagram );
}
if( MSG_GetNumBytesWritten( &sv.datagram ) < MSG_GetNumBytesLeft( &cl->datagram ))
{
MSG_WriteBits( &cl->datagram, MSG_GetBuf( &sv.datagram ), MSG_GetNumBitsWritten( &sv.datagram ));
}
else
{
MsgDev( D_WARN, "Ignoring unreliable datagram for %s, would overflow\n", cl->name );
}
2011-04-05 22:00:00 +02:00
2016-11-21 22:00:00 +01:00
if( FBitSet( cl->flags, FCL_HLTV_PROXY ))
2017-02-05 22:00:00 +01:00
{
if( MSG_GetNumBytesWritten( &sv.spec_datagram ) < MSG_GetNumBytesLeft( &cl->datagram ))
{
MSG_WriteBits( &cl->datagram, MSG_GetBuf( &sv.spec_datagram ), MSG_GetNumBitsWritten( &sv.spec_datagram ));
}
else
{
MsgDev( D_WARN, "Ignoring spectator datagram for %s, would overflow\n", cl->name );
}
}
2010-10-09 22:00:00 +02:00
}
// now clear the reliable and datagram buffers.
2016-11-14 22:00:00 +01:00
MSG_Clear( &sv.reliable_datagram );
2017-02-05 22:00:00 +01:00
MSG_Clear( &sv.spec_datagram );
MSG_Clear( &sv.datagram );
2010-10-09 22:00:00 +02:00
}
2008-07-16 22:00:00 +02:00
/*
=======================
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-10-09 22:00:00 +02:00
SV_UpdateToReliableMessages ();
2008-07-16 22:00:00 +02:00
// send a message to each connected client
2018-03-05 22:00:00 +01:00
for( i = 0, sv.current_client = svs.clients; i < svs.maxclients; i++, sv.current_client++ )
2008-07-16 22:00:00 +02:00
{
2018-03-05 22:00:00 +01:00
cl = sv.current_client;
2018-04-25 23:00:00 +02:00
if( cl->state <= cs_zombie || FBitSet( cl->flags, FCL_FAKECLIENT ))
2009-06-24 22:00:00 +02:00
continue;
2016-11-21 22:00:00 +01:00
if( FBitSet( cl->flags, FCL_SKIP_NET_MESSAGE ))
2010-10-14 22:00:00 +02:00
{
2016-11-21 22:00:00 +01:00
ClearBits( cl->flags, FCL_SKIP_NET_MESSAGE );
2010-10-14 22:00:00 +02:00
continue;
}
2010-06-23 22:00:00 +02:00
2018-04-19 23:00:00 +02:00
if( !host_limitlocal->value && NET_IsLocalAddress( cl->netchan.remote_address ))
SetBits( cl->flags, FCL_SEND_NET_MESSAGE );
2010-10-14 22:00:00 +02:00
if( cl->state == cs_spawned )
{
2017-02-13 22:00:00 +01:00
if(( host.realtime + sv.frametime ) >= cl->next_messagetime )
2016-11-21 22:00:00 +01:00
SetBits( cl->flags, FCL_SEND_NET_MESSAGE );
2010-10-14 22:00:00 +02:00
}
2009-12-04 22:00:00 +01:00
2008-07-16 22:00:00 +02:00
// if the reliable message overflowed, drop the client
2016-11-14 22:00:00 +01:00
if( MSG_CheckOverflow( &cl->netchan.message ))
2008-07-16 22:00:00 +02:00
{
2016-11-14 22:00:00 +01:00
MSG_Clear( &cl->netchan.message );
MSG_Clear( &cl->datagram );
2018-02-27 22:00:00 +01:00
SV_BroadcastPrintf( NULL, "%s overflowed\n", cl->name );
2010-10-14 22:00:00 +02:00
MsgDev( D_WARN, "reliable overflow for %s\n", cl->name );
2018-03-14 22:00:00 +01:00
SV_DropClient( cl, false );
2016-11-21 22:00:00 +01:00
SetBits( cl->flags, FCL_SEND_NET_MESSAGE );
cl->netchan.cleartime = 0.0; // don't choke this message
2010-10-14 22:00:00 +02:00
}
2016-11-21 22:00:00 +01:00
else if( FBitSet( cl->flags, FCL_SEND_NET_MESSAGE ))
2010-10-14 22:00:00 +02:00
{
// 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.
2017-02-12 22:00:00 +01:00
if( sv_failuretime.value < ( host.realtime - cl->netchan.last_received ))
2016-11-21 22:00:00 +01:00
ClearBits( cl->flags, FCL_SEND_NET_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
2010-10-14 22:00:00 +02:00
// and the bandwidth is not choked
2016-11-21 22:00:00 +01:00
if( FBitSet( cl->flags, FCL_SEND_NET_MESSAGE ))
2010-10-14 22:00:00 +02:00
{
2016-11-21 22:00:00 +01:00
// bandwidth choke active?
2018-03-11 22:00:00 +01:00
if( !Netchan_CanPacket( &cl->netchan, cl->state == cs_spawned ))
2016-11-21 22:00:00 +01:00
{
cl->chokecount++;
continue;
}
2010-10-14 22:00:00 +02:00
2016-11-21 22:00:00 +01:00
// now that we were able to send, reset timer to point to next possible send time.
2017-02-13 22:00:00 +01:00
cl->next_messagetime = host.realtime + sv.frametime + cl->cl_updaterate;
2016-11-21 22:00:00 +01:00
ClearBits( cl->flags, FCL_SEND_NET_MESSAGE );
2010-10-14 22:00:00 +02:00
2016-11-21 22:00:00 +01:00
// NOTE: we should send frame even if server is not simulated to prevent overflow
if( cl->state == cs_spawned )
SV_SendClientDatagram( cl );
2018-03-11 22:00:00 +01:00
else Netchan_TransmitBits( &cl->netchan, 0, NULL ); // just update reliable
2008-07-16 22:00:00 +02:00
}
}
2010-06-23 22:00:00 +02:00
// reset current client
2018-03-05 22:00:00 +01:00
sv.current_client = NULL;
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 )
{
sv_client_t *cl;
2016-11-21 22:00:00 +01:00
int i;
2010-04-03 22:00:00 +02:00
2010-10-17 22:00:00 +02:00
if( sv.state == ss_dead )
return;
2017-02-12 22:00:00 +01:00
for( i = 0, cl = svs.clients; i < svs.maxclients; i++, cl++ )
2010-04-03 22:00:00 +02:00
{
if( cl->state >= cs_connected )
2016-11-21 22:00:00 +01:00
SetBits( cl->flags, FCL_SEND_NET_MESSAGE );
2010-04-03 22:00:00 +02:00
}
2016-11-21 22:00:00 +01:00
2010-04-03 22:00:00 +02:00
SV_SendClientMessages();
}
2010-10-17 22:00:00 +02:00
/*
=======================
SV_SkipUpdates
used before changing level
=======================
*/
void SV_SkipUpdates( void )
{
sv_client_t *cl;
2016-11-21 22:00:00 +01:00
int i;
2010-10-17 22:00:00 +02:00
if( sv.state == ss_dead )
return;
2017-02-12 22:00:00 +01:00
for( i = 0, cl = svs.clients; i < svs.maxclients; i++, cl++ )
2010-10-17 22:00:00 +02:00
{
2016-11-21 22:00:00 +01:00
if( cl->state != cs_spawned || FBitSet( cl->flags, FCL_FAKECLIENT ))
2010-10-17 22:00:00 +02:00
continue;
2016-11-21 22:00:00 +01:00
SetBits( cl->flags, FCL_SKIP_NET_MESSAGE );
2010-10-17 22:00:00 +02:00
}
}
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
2017-02-12 22:00:00 +01:00
for( i = 0, cl = svs.clients; i < svs.maxclients; i++, cl++ )
2010-03-24 22:00:00 +01:00
{
2012-12-16 21:00:00 +01:00
if( !cl->state || !cl->edict )
continue;
2010-03-24 22:00:00 +01:00
2016-11-21 22:00:00 +01:00
if( !cl->edict || FBitSet( cl->edict->v.flags, FL_FAKECLIENT ))
2010-03-24 22:00:00 +01:00
continue;
2018-03-14 22:00:00 +01:00
if( cl->state > cs_connected )
cl->state = cs_connected;
2010-03-24 22:00:00 +01:00
2018-03-14 22:00:00 +01:00
COM_ClearCustomizationList( &cl->customdata, false );
memset( cl->physinfo, 0, MAX_PHYSINFO_STRING );
2018-09-08 23:00:00 +02:00
// NOTE: many mods sending messages that must be applied on a next level
// e.g. CryOfFear sending HideHud and PlayMp3 that affected after map change
if( svgame.globals->changelevel )
continue;
2016-11-14 22:00:00 +01:00
MSG_Clear( &cl->netchan.message );
2018-02-25 22:00:00 +01:00
MSG_Clear( &cl->datagram );
2010-03-24 22:00:00 +01:00
}
2008-07-16 22:00:00 +02:00
}