15 Oct 2010
This commit is contained in:
parent
d8a292491d
commit
e03ae1c6e6
|
@ -54,7 +54,6 @@ struct cl_entity_s
|
||||||
int index; // Index into cl_entities ( always match actual slot )
|
int index; // Index into cl_entities ( always match actual slot )
|
||||||
int player; // True if this entity is a "player"
|
int player; // True if this entity is a "player"
|
||||||
|
|
||||||
int serverframe; // TEMPORARY PLACED HERE
|
|
||||||
entity_state_t baseline; // The original state from which to delta during an uncompressed message
|
entity_state_t baseline; // The original state from which to delta during an uncompressed message
|
||||||
entity_state_t prevstate; // The state information from the penultimate message received from the server
|
entity_state_t prevstate; // The state information from the penultimate message received from the server
|
||||||
entity_state_t curstate; // The state information from the last message received from server
|
entity_state_t curstate; // The state information from the last message received from server
|
||||||
|
|
|
@ -535,7 +535,7 @@ void CL_Record_f( void )
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else com.strncpy( demoname, name, sizeof( name ));
|
else com.strncpy( demoname, name, sizeof( demoname ));
|
||||||
|
|
||||||
// open the demo file
|
// open the demo file
|
||||||
com.sprintf( demopath, "demos/%s.dem", demoname );
|
com.sprintf( demopath, "demos/%s.dem", demoname );
|
||||||
|
|
|
@ -102,34 +102,20 @@ void CL_UpdateStudioVars( cl_entity_t *ent, entity_state_t *newstate )
|
||||||
VectorCopy( ent->curstate.angles, ent->latched.prevangles );
|
VectorCopy( ent->curstate.angles, ent->latched.prevangles );
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
bool CL_ParseDelta( sizebuf_t *msg, entity_state_t *from, entity_state_t *to, int newnum )
|
||||||
==================
|
|
||||||
CL_DeltaEntity
|
|
||||||
|
|
||||||
Parses deltas from the given base and adds the resulting entity
|
|
||||||
to the current frame
|
|
||||||
==================
|
|
||||||
*/
|
|
||||||
void CL_DeltaEntity( sizebuf_t *msg, frame_t *frame, int newnum, entity_state_t *old, bool unchanged )
|
|
||||||
{
|
{
|
||||||
cl_entity_t *ent;
|
cl_entity_t *ent;
|
||||||
entity_state_t *state;
|
int alive;
|
||||||
bool newent = (old) ? false : true;
|
int noInterp = false;
|
||||||
int result = 1;
|
|
||||||
|
|
||||||
ent = EDICT_NUM( newnum );
|
ent = EDICT_NUM( newnum );
|
||||||
state = &cl.entity_curstates[cl.parse_entities & (MAX_PARSE_ENTITIES-1)];
|
alive = MSG_ReadDeltaEntity( msg, from, to, newnum, sv_time( ));
|
||||||
|
|
||||||
if( newent ) old = &ent->baseline;
|
if( !alive )
|
||||||
|
|
||||||
if( unchanged ) *state = *old;
|
|
||||||
else result = MSG_ReadDeltaEntity( msg, old, state, newnum, sv_time( ));
|
|
||||||
|
|
||||||
if( !result )
|
|
||||||
{
|
{
|
||||||
if( newent ) Host_Error( "Cl_DeltaEntity: tried to release new entity\n" );
|
// entity was delta removed
|
||||||
|
|
||||||
if( state->number == -1 )
|
if( to->number == -1 )
|
||||||
{
|
{
|
||||||
// Msg( "Entity %s was removed from server\n", ent->curstate.classname );
|
// Msg( "Entity %s was removed from server\n", ent->curstate.classname );
|
||||||
CL_FreeEntity( ent );
|
CL_FreeEntity( ent );
|
||||||
|
@ -140,45 +126,128 @@ void CL_DeltaEntity( sizebuf_t *msg, frame_t *frame, int newnum, entity_state_t
|
||||||
ent->curstate.effects |= EF_NODRAW; // don't rendering
|
ent->curstate.effects |= EF_NODRAW; // don't rendering
|
||||||
clgame.dllFuncs.pfnUpdateOnRemove( ent );
|
clgame.dllFuncs.pfnUpdateOnRemove( ent );
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
// entity was delta removed
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cl.parse_entities++;
|
|
||||||
frame->num_entities++;
|
|
||||||
|
|
||||||
if( ent->index <= 0 )
|
if( ent->index <= 0 )
|
||||||
{
|
{
|
||||||
|
// spawn entity
|
||||||
CL_InitEntity( ent );
|
CL_InitEntity( ent );
|
||||||
state->effects |= EF_NOINTERP;
|
noInterp = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// some data changes will force no lerping
|
if( to->effects & EF_NOINTERP || noInterp )
|
||||||
if( state->effects & EF_NOINTERP )
|
ent->prevstate = *to; // duplicate the current state so lerping doesn't hurt anything
|
||||||
ent->serverframe = -99;
|
else ent->prevstate = ent->curstate; // shuffle the last state to previous
|
||||||
|
|
||||||
if( ent->serverframe != cl.frame.serverframe - 1 )
|
|
||||||
{
|
|
||||||
// duplicate the current state so lerping doesn't hurt anything
|
|
||||||
ent->prevstate = *state;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// shuffle the last state to previous
|
|
||||||
ent->prevstate = ent->curstate;
|
|
||||||
}
|
|
||||||
|
|
||||||
ent->serverframe = cl.frame.serverframe;
|
|
||||||
|
|
||||||
// NOTE: always check modelindex for new state not current
|
// NOTE: always check modelindex for new state not current
|
||||||
if( CM_GetModelType( state->modelindex ) == mod_studio )
|
if( CM_GetModelType( to->modelindex ) == mod_studio )
|
||||||
CL_UpdateStudioVars( ent, state );
|
CL_UpdateStudioVars( ent, to );
|
||||||
|
|
||||||
// set right current state
|
// set right current state
|
||||||
ent->curstate = *state;
|
ent->curstate = *to;
|
||||||
|
|
||||||
CL_LinkEdict( ent ); // relink entity
|
CL_LinkEdict( ent ); // relink entity
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
=======================
|
||||||
|
CL_ClearPacketEntities
|
||||||
|
=======================
|
||||||
|
*/
|
||||||
|
void CL_ClearPacketEntities( frame_t *frame )
|
||||||
|
{
|
||||||
|
packet_entities_t *packet;
|
||||||
|
|
||||||
|
ASSERT( frame != NULL );
|
||||||
|
|
||||||
|
packet = &frame->entities;
|
||||||
|
|
||||||
|
if( packet )
|
||||||
|
{
|
||||||
|
if( packet->entities != NULL )
|
||||||
|
Mem_Free( packet->entities );
|
||||||
|
|
||||||
|
packet->num_entities = 0;
|
||||||
|
packet->max_entities = 0;
|
||||||
|
packet->entities = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
=======================
|
||||||
|
CL_AllocPacketEntities
|
||||||
|
=======================
|
||||||
|
*/
|
||||||
|
void CL_AllocPacketEntities( frame_t *frame, int count )
|
||||||
|
{
|
||||||
|
packet_entities_t *packet;
|
||||||
|
|
||||||
|
ASSERT( frame != NULL );
|
||||||
|
|
||||||
|
packet = &frame->entities;
|
||||||
|
if( count < 1 ) count = 1;
|
||||||
|
|
||||||
|
// check if new frame has more entities than previous
|
||||||
|
if( packet->max_entities < count )
|
||||||
|
{
|
||||||
|
CL_ClearPacketEntities( frame );
|
||||||
|
packet->entities = Mem_Alloc( clgame.mempool, sizeof( entity_state_t ) * count );
|
||||||
|
packet->max_entities = count;
|
||||||
|
}
|
||||||
|
packet->num_entities = count;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
================
|
||||||
|
CL_ClearFrames
|
||||||
|
|
||||||
|
free client frames memory
|
||||||
|
================
|
||||||
|
*/
|
||||||
|
void CL_ClearFrames( void )
|
||||||
|
{
|
||||||
|
frame_t *frame;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for( i = 0, frame = cl.frames; i < CL_UPDATE_BACKUP; i++, frame++ )
|
||||||
|
{
|
||||||
|
CL_ClearPacketEntities( frame );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
=================
|
||||||
|
CL_FlushEntityPacket
|
||||||
|
=================
|
||||||
|
*/
|
||||||
|
void CL_FlushEntityPacket( sizebuf_t *msg )
|
||||||
|
{
|
||||||
|
int newnum;
|
||||||
|
entity_state_t from, to;
|
||||||
|
|
||||||
|
MsgDev( D_INFO, "FlushEntityPacket()\n" );
|
||||||
|
Mem_Set( &from, 0, sizeof( from ));
|
||||||
|
|
||||||
|
cl.frames[cl.parsecountmod].valid = false;
|
||||||
|
cl.validsequence = 0; // can't render a frame
|
||||||
|
|
||||||
|
// read it all, but ignore it
|
||||||
|
while( 1 )
|
||||||
|
{
|
||||||
|
newnum = BF_ReadWord( msg );
|
||||||
|
if( !newnum ) break; // done
|
||||||
|
|
||||||
|
if( BF_CheckOverflow( msg ))
|
||||||
|
Host_Error( "CL_FlushEntityPacket: read overflow\n" );
|
||||||
|
|
||||||
|
while( newnum >= clgame.numEntities )
|
||||||
|
clgame.numEntities++;
|
||||||
|
|
||||||
|
MSG_ReadDeltaEntity( msg, &from, &to, newnum, sv_time( ));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -189,40 +258,91 @@ An svc_packetentities has just been parsed, deal with the
|
||||||
rest of the data stream.
|
rest of the data stream.
|
||||||
==================
|
==================
|
||||||
*/
|
*/
|
||||||
void CL_ParsePacketEntities( sizebuf_t *msg, frame_t *oldframe, frame_t *newframe )
|
void CL_ParsePacketEntities( sizebuf_t *msg, bool delta )
|
||||||
{
|
{
|
||||||
int newnum;
|
frame_t *frame;
|
||||||
entity_state_t *oldstate;
|
int oldpacket, newpacket;
|
||||||
int oldindex, oldnum;
|
packet_entities_t *from, *to, dummy;
|
||||||
|
int oldindex, newindex;
|
||||||
|
int count, delta_sequence = -1;
|
||||||
|
int newnum, oldnum;
|
||||||
|
bool full = false;
|
||||||
|
|
||||||
newframe->parse_entities = cl.parse_entities;
|
// first, allocate packet for new frame
|
||||||
newframe->num_entities = 0;
|
count = BF_ReadWord( msg );
|
||||||
|
|
||||||
// delta from the entities present in oldframe
|
newpacket = cl.parsecountmod;
|
||||||
oldindex = 0;
|
frame = &cl.frames[newpacket];
|
||||||
oldstate = NULL;
|
frame->valid = true;
|
||||||
if( !oldframe )
|
|
||||||
|
if( delta )
|
||||||
{
|
{
|
||||||
oldnum = MAX_ENTNUMBER;
|
oldpacket = BF_ReadByte( msg );
|
||||||
|
|
||||||
|
if(( cl.delta_sequence & CL_UPDATE_MASK ) != ( oldpacket & CL_UPDATE_MASK ))
|
||||||
|
MsgDev( D_WARN, "CL_ParsePacketEntities: mismatch delta_sequence %i != %i\n", cl.delta_sequence, oldpacket );
|
||||||
|
}
|
||||||
|
else oldpacket = -1;
|
||||||
|
|
||||||
|
if( oldpacket != -1 )
|
||||||
|
{
|
||||||
|
int subtracted = ((( cls.netchan.incoming_sequence & 0xFF ) - oldpacket ) & 0xFF );
|
||||||
|
|
||||||
|
if( subtracted == 0 )
|
||||||
|
{
|
||||||
|
Host_Error( "CL_DeltaPacketEntities: update too old, connection dropped.\n" );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( subtracted >= CL_UPDATE_MASK )
|
||||||
|
{
|
||||||
|
// we can't use this, it is too old
|
||||||
|
CL_FlushEntityPacket( msg );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
from = &cl.frames[oldpacket & CL_UPDATE_MASK].entities;
|
||||||
|
|
||||||
|
// alloc room for save entities from prevframe too
|
||||||
|
if( from->num_entities > count )
|
||||||
|
count = from->num_entities;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if( oldindex >= oldframe->num_entities )
|
// this is a full update that we can start delta compressing from now
|
||||||
{
|
dummy.entities = NULL;
|
||||||
oldnum = MAX_ENTNUMBER;
|
dummy.num_entities = 0;
|
||||||
}
|
cls.demowaiting = false; // we can start recording now
|
||||||
else
|
cl.force_send_usercmd = true;
|
||||||
{
|
from = &dummy;
|
||||||
oldstate = &cl.entity_curstates[(oldframe->parse_entities+oldindex) & (MAX_PARSE_ENTITIES-1)];
|
full = true;
|
||||||
oldnum = oldstate->number;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CL_AllocPacketEntities( frame, count );
|
||||||
|
to = &frame->entities;
|
||||||
|
|
||||||
|
// mark current delta state
|
||||||
|
cl.validsequence = cls.netchan.incoming_sequence;
|
||||||
|
|
||||||
|
oldindex = 0;
|
||||||
|
newindex = 0;
|
||||||
|
to->num_entities = 0;
|
||||||
|
|
||||||
while( 1 )
|
while( 1 )
|
||||||
{
|
{
|
||||||
// read the entity index number
|
newnum = BF_ReadWord( msg );
|
||||||
newnum = BF_ReadShort( msg );
|
|
||||||
if( !newnum ) break; // end of packet entities
|
if( !newnum )
|
||||||
|
{
|
||||||
|
while( oldindex < from->num_entities )
|
||||||
|
{
|
||||||
|
// copy all the rest of the entities from the old packet
|
||||||
|
to->entities[newindex] = from->entities[oldindex];
|
||||||
|
newindex++;
|
||||||
|
oldindex++;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if( BF_CheckOverflow( msg ))
|
if( BF_CheckOverflow( msg ))
|
||||||
Host_Error( "CL_ParsePacketEntities: read overflow\n" );
|
Host_Error( "CL_ParsePacketEntities: read overflow\n" );
|
||||||
|
@ -230,91 +350,96 @@ void CL_ParsePacketEntities( sizebuf_t *msg, frame_t *oldframe, frame_t *newfram
|
||||||
while( newnum >= clgame.numEntities )
|
while( newnum >= clgame.numEntities )
|
||||||
clgame.numEntities++;
|
clgame.numEntities++;
|
||||||
|
|
||||||
while( oldnum < newnum )
|
oldnum = oldindex >= from->num_entities ? MAX_ENTNUMBER : from->entities[oldindex].number;
|
||||||
|
|
||||||
|
while( newnum > oldnum )
|
||||||
{
|
{
|
||||||
// one or more entities from the old packet are unchanged
|
if( full )
|
||||||
CL_DeltaEntity( msg, newframe, oldnum, oldstate, true );
|
{
|
||||||
|
MsgDev( D_WARN, "CL_ParsePacketEntities: oldcopy on full update\n" );
|
||||||
|
CL_FlushEntityPacket( msg );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
to->entities[newindex] = from->entities[oldindex];
|
||||||
|
newindex++;
|
||||||
oldindex++;
|
oldindex++;
|
||||||
|
oldnum = oldindex >= from->num_entities ? MAX_ENTNUMBER : from->entities[oldindex].number;
|
||||||
if( oldindex >= oldframe->num_entities )
|
|
||||||
{
|
|
||||||
oldnum = MAX_ENTNUMBER;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
oldstate = &cl.entity_curstates[(oldframe->parse_entities+oldindex) & (MAX_PARSE_ENTITIES-1)];
|
|
||||||
oldnum = oldstate->number;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if( oldnum == newnum )
|
if( newnum < oldnum )
|
||||||
{
|
{
|
||||||
// delta from previous state
|
cl_entity_t *ent;
|
||||||
CL_DeltaEntity( msg, newframe, newnum, oldstate, false );
|
|
||||||
oldindex++;
|
|
||||||
|
|
||||||
if( oldindex >= oldframe->num_entities )
|
// new from baseline
|
||||||
|
ent = EDICT_NUM( newnum );
|
||||||
|
|
||||||
|
if( !CL_ParseDelta( msg, &ent->baseline, &to->entities[newindex], newnum ))
|
||||||
{
|
{
|
||||||
oldnum = MAX_ENTNUMBER;
|
if( full )
|
||||||
}
|
{
|
||||||
else
|
MsgDev( D_WARN, "CL_ParsePacketEntities: remove on full update\n" );
|
||||||
{
|
CL_FlushEntityPacket( msg );
|
||||||
oldstate = &cl.entity_curstates[(oldframe->parse_entities+oldindex) & (MAX_PARSE_ENTITIES-1)];
|
return;
|
||||||
oldnum = oldstate->number;
|
}
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
newindex++;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if( oldnum > newnum )
|
if( newnum == oldnum )
|
||||||
{
|
{
|
||||||
// delta from baseline ?
|
// delta from previous
|
||||||
CL_DeltaEntity( msg, newframe, newnum, NULL, false );
|
if( full )
|
||||||
continue;
|
{
|
||||||
|
cl.validsequence = 0;
|
||||||
|
MsgDev( D_WARN, "CL_ParsePacketEntities: delta on full update\n" );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( !CL_ParseDelta( msg, &from->entities[oldindex], &to->entities[newindex], newnum ))
|
||||||
|
{
|
||||||
|
// entity was delta-removed
|
||||||
|
oldindex++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
newindex++;
|
||||||
|
oldindex++;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// any remaining entities in the old frame are copied over
|
to->num_entities = newindex; // done
|
||||||
while( oldnum != MAX_ENTNUMBER )
|
|
||||||
|
cl.frame = *frame;
|
||||||
|
if( !cl.frame.valid ) return;
|
||||||
|
|
||||||
|
if( cls.state != ca_active )
|
||||||
{
|
{
|
||||||
// one or more entities from the old packet are unchanged
|
cl_entity_t *player;
|
||||||
CL_DeltaEntity( msg, newframe, oldnum, oldstate, true );
|
|
||||||
oldindex++;
|
|
||||||
|
|
||||||
if( oldindex >= oldframe->num_entities )
|
// client entered the game
|
||||||
{
|
cls.state = ca_active;
|
||||||
oldnum = MAX_ENTNUMBER;
|
cl.force_refdef = true;
|
||||||
}
|
cls.changelevel = false; // changelevel is done
|
||||||
else
|
|
||||||
{
|
player = CL_GetLocalPlayer();
|
||||||
oldstate = &cl.entity_curstates[(oldframe->parse_entities+oldindex) & (MAX_PARSE_ENTITIES-1)];
|
SCR_MakeLevelShot(); // make levelshot if needs
|
||||||
oldnum = oldstate->number;
|
|
||||||
}
|
Cvar_SetValue( "scr_loading", 0.0f ); // reset progress bar
|
||||||
|
|
||||||
|
// getting a valid frame message ends the connection process
|
||||||
|
VectorCopy( player->origin, cl.predicted_origin );
|
||||||
|
VectorCopy( player->angles, cl.predicted_angles );
|
||||||
|
|
||||||
|
// request new HUD values
|
||||||
|
BF_WriteByte( &cls.netchan.message, clc_stringcmd );
|
||||||
|
BF_WriteString( &cls.netchan.message, "fullupdate" );
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
CL_CheckPredictionError();
|
||||||
===================
|
|
||||||
CL_ParseClientData
|
|
||||||
===================
|
|
||||||
*/
|
|
||||||
void CL_ParseClientData( frame_t *from, frame_t *to, sizebuf_t *msg )
|
|
||||||
{
|
|
||||||
clientdata_t *cd, *ocd;
|
|
||||||
clientdata_t dummy;
|
|
||||||
|
|
||||||
cd = &to->cd;
|
|
||||||
|
|
||||||
// clear to old value before delta parsing
|
|
||||||
if( !from )
|
|
||||||
{
|
|
||||||
ocd = &dummy;
|
|
||||||
Mem_Set( &dummy, 0, sizeof( dummy ));
|
|
||||||
}
|
|
||||||
else ocd = &from->cd;
|
|
||||||
|
|
||||||
MSG_ReadClientData( msg, ocd, cd, sv_time( ));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -324,63 +449,12 @@ CL_ParseFrame
|
||||||
*/
|
*/
|
||||||
void CL_ParseFrame( sizebuf_t *msg )
|
void CL_ParseFrame( sizebuf_t *msg )
|
||||||
{
|
{
|
||||||
int cmd;
|
|
||||||
cl_entity_t *clent;
|
|
||||||
|
|
||||||
Mem_Set( &cl.frame, 0, sizeof( cl.frame ));
|
Mem_Set( &cl.frame, 0, sizeof( cl.frame ));
|
||||||
|
|
||||||
cl.mtime[1] = cl.mtime[0];
|
cl.mtime[1] = cl.mtime[0];
|
||||||
cl.mtime[0] = BF_ReadFloat( msg );
|
cl.mtime[0] = BF_ReadFloat( msg );
|
||||||
cl.frame.serverframe = BF_ReadLong( msg );
|
|
||||||
cl.frame.deltaframe = BF_ReadLong( msg );
|
|
||||||
cl.surpressCount = BF_ReadByte( msg );
|
cl.surpressCount = BF_ReadByte( msg );
|
||||||
|
|
||||||
// If the frame is delta compressed from data that we
|
|
||||||
// no longer have available, we must suck up the rest of
|
|
||||||
// the frame, but not use it, then ask for a non-compressed
|
|
||||||
// message
|
|
||||||
if( cl.frame.deltaframe <= 0 )
|
|
||||||
{
|
|
||||||
cl.frame.valid = true; // uncompressed frame
|
|
||||||
cls.demowaiting = false; // we can start recording now
|
|
||||||
cl.oldframe = NULL;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
cl.oldframe = &cl.frames[cl.frame.deltaframe & CL_UPDATE_MASK];
|
|
||||||
if( !cl.oldframe->valid )
|
|
||||||
{
|
|
||||||
// should never happen
|
|
||||||
MsgDev( D_INFO, "delta from invalid frame (not supposed to happen!)\n" );
|
|
||||||
}
|
|
||||||
if( cl.oldframe->serverframe != cl.frame.deltaframe )
|
|
||||||
{
|
|
||||||
// The frame that the server did the delta from
|
|
||||||
// is too old, so we can't reconstruct it properly.
|
|
||||||
MsgDev( D_INFO, "delta frame too old\n" );
|
|
||||||
}
|
|
||||||
else if( cl.parse_entities - cl.oldframe->parse_entities > MAX_PARSE_ENTITIES - 128 )
|
|
||||||
{
|
|
||||||
MsgDev( D_INFO, "delta parse_entities too old\n" );
|
|
||||||
}
|
|
||||||
else cl.frame.valid = true; // valid delta parse
|
|
||||||
}
|
|
||||||
|
|
||||||
// read clientdata
|
|
||||||
cmd = BF_ReadByte( msg );
|
|
||||||
if( cmd != svc_clientdata ) Host_Error( "CL_ParseFrame: not cliendata[%d]\n", cmd );
|
|
||||||
CL_ParseClientData( cl.oldframe, &cl.frame, msg );
|
|
||||||
|
|
||||||
clent = CL_GetLocalPlayer(); // get client
|
|
||||||
|
|
||||||
// read packet entities
|
|
||||||
cmd = BF_ReadByte( msg );
|
|
||||||
if( cmd != svc_packetentities ) Host_Error( "CL_ParseFrame: not packetentities[%d]\n", cmd );
|
|
||||||
CL_ParsePacketEntities( msg, cl.oldframe, &cl.frame );
|
|
||||||
|
|
||||||
// save the frame off in the backup array for later delta comparisons
|
|
||||||
cl.frames[cl.frame.serverframe & CL_UPDATE_MASK] = cl.frame;
|
|
||||||
|
|
||||||
if( !cl.frame.valid ) return;
|
if( !cl.frame.valid ) return;
|
||||||
|
|
||||||
if( cls.state != ca_active )
|
if( cls.state != ca_active )
|
||||||
|
@ -433,17 +507,17 @@ void CL_AddPacketEntities( frame_t *frame )
|
||||||
if( !clent ) return;
|
if( !clent ) return;
|
||||||
|
|
||||||
// update client vars
|
// update client vars
|
||||||
clgame.dllFuncs.pfnTxferLocalOverrides( &clent->curstate, &cl.frame.cd );
|
clgame.dllFuncs.pfnTxferLocalOverrides( &clent->curstate, &cl.frame.clientdata );
|
||||||
|
|
||||||
// if viewmodel has changed update sequence here
|
// if viewmodel has changed update sequence here
|
||||||
if( clgame.viewent.curstate.modelindex != cl.frame.cd.viewmodel )
|
if( clgame.viewent.curstate.modelindex != cl.frame.clientdata.viewmodel )
|
||||||
{
|
{
|
||||||
cl_entity_t *view = &clgame.viewent;
|
cl_entity_t *view = &clgame.viewent;
|
||||||
CL_WeaponAnim( view->curstate.sequence, view->curstate.body );
|
CL_WeaponAnim( view->curstate.sequence, view->curstate.body );
|
||||||
}
|
}
|
||||||
|
|
||||||
// setup player viewmodel (only for local player!)
|
// setup player viewmodel (only for local player!)
|
||||||
clgame.viewent.curstate.modelindex = cl.frame.cd.viewmodel;
|
clgame.viewent.curstate.modelindex = cl.frame.clientdata.viewmodel;
|
||||||
|
|
||||||
for( e = 1; e < clgame.numEntities; e++ )
|
for( e = 1; e < clgame.numEntities; e++ )
|
||||||
{
|
{
|
||||||
|
|
|
@ -487,7 +487,7 @@ void CL_DrawCrosshair( void )
|
||||||
|
|
||||||
pPlayer = CL_GetLocalPlayer();
|
pPlayer = CL_GetLocalPlayer();
|
||||||
|
|
||||||
if( cl.frame.cd.deadflag != DEAD_NO || cl.frame.cd.flags & FL_FROZEN )
|
if( cl.frame.clientdata.deadflag != DEAD_NO || cl.frame.clientdata.flags & FL_FROZEN )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// any camera on
|
// any camera on
|
||||||
|
@ -743,8 +743,6 @@ void CL_InitEdicts( void )
|
||||||
ASSERT( clgame.entities == NULL );
|
ASSERT( clgame.entities == NULL );
|
||||||
|
|
||||||
CL_UPDATE_BACKUP = ( cl.maxclients == 1 ) ? SINGLEPLAYER_BACKUP : MULTIPLAYER_BACKUP;
|
CL_UPDATE_BACKUP = ( cl.maxclients == 1 ) ? SINGLEPLAYER_BACKUP : MULTIPLAYER_BACKUP;
|
||||||
|
|
||||||
cl.frames = Mem_Alloc( clgame.mempool, sizeof( frame_t ) * CL_UPDATE_BACKUP );
|
|
||||||
clgame.entities = Mem_Alloc( clgame.mempool, sizeof( cl_entity_t ) * clgame.maxEntities );
|
clgame.entities = Mem_Alloc( clgame.mempool, sizeof( cl_entity_t ) * clgame.maxEntities );
|
||||||
|
|
||||||
for( i = 0, e = clgame.entities; i < clgame.maxEntities; i++, e++ )
|
for( i = 0, e = clgame.entities; i < clgame.maxEntities; i++, e++ )
|
||||||
|
@ -767,10 +765,8 @@ void CL_FreeEdicts( void )
|
||||||
Mem_Free( clgame.entities );
|
Mem_Free( clgame.entities );
|
||||||
}
|
}
|
||||||
|
|
||||||
if( cl.frames ) Mem_Free( cl.frames );
|
|
||||||
clgame.numEntities = 0;
|
clgame.numEntities = 0;
|
||||||
clgame.entities = NULL;
|
clgame.entities = NULL;
|
||||||
cl.frames = NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1176,8 +1172,8 @@ static void pfnGetPlayerInfo( int ent_num, hud_player_info_t *pinfo )
|
||||||
pinfo->model = player->model;
|
pinfo->model = player->model;
|
||||||
|
|
||||||
pinfo->spectator = spec;
|
pinfo->spectator = spec;
|
||||||
pinfo->ping = com.atoi( Info_ValueForKey( player->userinfo, "ping" ));
|
pinfo->ping = player->ping;
|
||||||
pinfo->packetloss = com.atoi( Info_ValueForKey( player->userinfo, "loss" ));
|
pinfo->packetloss = player->packet_loss;
|
||||||
pinfo->topcolor = com.atoi( Info_ValueForKey( player->userinfo, "topcolor" ));
|
pinfo->topcolor = com.atoi( Info_ValueForKey( player->userinfo, "topcolor" ));
|
||||||
pinfo->bottomcolor = com.atoi( Info_ValueForKey( player->userinfo, "bottomcolor" ));
|
pinfo->bottomcolor = com.atoi( Info_ValueForKey( player->userinfo, "bottomcolor" ));
|
||||||
}
|
}
|
||||||
|
@ -1375,7 +1371,7 @@ pfnPhysInfo_ValueForKey
|
||||||
*/
|
*/
|
||||||
static const char* pfnPhysInfo_ValueForKey( const char *key )
|
static const char* pfnPhysInfo_ValueForKey( const char *key )
|
||||||
{
|
{
|
||||||
return Info_ValueForKey( cl.frame.cd.physinfo, key );
|
return Info_ValueForKey( cl.frame.clientdata.physinfo, key );
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1398,7 +1394,7 @@ value that come from server
|
||||||
*/
|
*/
|
||||||
static float pfnGetClientMaxspeed( void )
|
static float pfnGetClientMaxspeed( void )
|
||||||
{
|
{
|
||||||
return cl.frame.cd.maxspeed;
|
return cl.frame.clientdata.maxspeed;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1704,7 +1700,7 @@ pfnLocalPlayerDucking
|
||||||
*/
|
*/
|
||||||
int pfnLocalPlayerDucking( void )
|
int pfnLocalPlayerDucking( void )
|
||||||
{
|
{
|
||||||
return cl.frame.cd.bInDuck;
|
return cl.frame.clientdata.bInDuck;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -9,6 +9,9 @@
|
||||||
#include "net_encode.h"
|
#include "net_encode.h"
|
||||||
#include "input.h"
|
#include "input.h"
|
||||||
|
|
||||||
|
#define MAX_TOTAL_CMDS 16
|
||||||
|
#define MIN_CMD_RATE 10.0
|
||||||
|
#define MAX_CMD_BUFFER 4000
|
||||||
#define CONNECTION_PROBLEM_TIME 15.0 // 15 seconds
|
#define CONNECTION_PROBLEM_TIME 15.0 // 15 seconds
|
||||||
|
|
||||||
cvar_t *rcon_client_password;
|
cvar_t *rcon_client_password;
|
||||||
|
@ -20,9 +23,11 @@ cvar_t *cl_predict;
|
||||||
cvar_t *cl_showfps;
|
cvar_t *cl_showfps;
|
||||||
cvar_t *cl_nodelta;
|
cvar_t *cl_nodelta;
|
||||||
cvar_t *cl_crosshair;
|
cvar_t *cl_crosshair;
|
||||||
|
cvar_t *cl_cmdbackup;
|
||||||
cvar_t *cl_idealpitchscale;
|
cvar_t *cl_idealpitchscale;
|
||||||
cvar_t *cl_solid_players;
|
cvar_t *cl_solid_players;
|
||||||
cvar_t *cl_showmiss;
|
cvar_t *cl_showmiss;
|
||||||
|
cvar_t *cl_cmdrate;
|
||||||
cvar_t *userinfo;
|
cvar_t *userinfo;
|
||||||
|
|
||||||
//
|
//
|
||||||
|
@ -119,6 +124,40 @@ static float CL_LerpPoint( void )
|
||||||
return frac;
|
return frac;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
=================
|
||||||
|
CL_ComputePacketLoss
|
||||||
|
|
||||||
|
=================
|
||||||
|
*/
|
||||||
|
void CL_ComputePacketLoss( void )
|
||||||
|
{
|
||||||
|
int i, frm;
|
||||||
|
frame_t *frame;
|
||||||
|
int count = 0;
|
||||||
|
int lost = 0;
|
||||||
|
|
||||||
|
if( host.realtime < cls.packet_loss_recalc_time )
|
||||||
|
return;
|
||||||
|
|
||||||
|
// recalc every second
|
||||||
|
cls.packet_loss_recalc_time = host.realtime + 1.0;
|
||||||
|
|
||||||
|
// compuate packet loss
|
||||||
|
for( i = cls.netchan.incoming_sequence - CL_UPDATE_BACKUP+1; i <= cls.netchan.incoming_sequence; i++ )
|
||||||
|
{
|
||||||
|
frm = i;
|
||||||
|
frame = &cl.frames[frm & CL_UPDATE_MASK];
|
||||||
|
|
||||||
|
if( frame->receivedtime == -1 )
|
||||||
|
lost++;
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( count <= 0 ) cls.packet_loss = 0.0f;
|
||||||
|
else cls.packet_loss = ( 100.0f * (float)lost ) / (float)count;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
=======================================================================
|
=======================================================================
|
||||||
|
|
||||||
|
@ -231,10 +270,10 @@ usercmd_t CL_CreateCmd( void )
|
||||||
|
|
||||||
Mem_Set( &cmd, 0, sizeof( cmd ));
|
Mem_Set( &cmd, 0, sizeof( cmd ));
|
||||||
|
|
||||||
VectorCopy( cl.frame.cd.origin, cdata.origin );
|
VectorCopy( cl.frame.clientdata.origin, cdata.origin );
|
||||||
VectorCopy( cl.refdef.cl_viewangles, cdata.viewangles );
|
VectorCopy( cl.refdef.cl_viewangles, cdata.viewangles );
|
||||||
cdata.iWeaponBits = cl.frame.cd.weapons;
|
cdata.iWeaponBits = cl.frame.clientdata.weapons;
|
||||||
cdata.fov = cl.frame.cd.fov;
|
cdata.fov = cl.frame.clientdata.fov;
|
||||||
|
|
||||||
clgame.dllFuncs.pfnUpdateClientData( &cdata, cl_time( ));
|
clgame.dllFuncs.pfnUpdateClientData( &cdata, cl_time( ));
|
||||||
|
|
||||||
|
@ -256,6 +295,30 @@ usercmd_t CL_CreateCmd( void )
|
||||||
return cmd;
|
return cmd;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CL_WriteUsercmd( sizebuf_t *msg, int from, int to )
|
||||||
|
{
|
||||||
|
usercmd_t nullcmd;
|
||||||
|
usercmd_t *f, *t;
|
||||||
|
|
||||||
|
ASSERT( from == -1 || ( from >= 0 && from < MULTIPLAYER_BACKUP ) );
|
||||||
|
ASSERT( to >= 0 && to < MULTIPLAYER_BACKUP );
|
||||||
|
|
||||||
|
if( from == -1 )
|
||||||
|
{
|
||||||
|
Mem_Set( &nullcmd, 0, sizeof( nullcmd ));
|
||||||
|
f = &nullcmd;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
f = &cl.cmds[from];
|
||||||
|
}
|
||||||
|
|
||||||
|
t = &cl.cmds[to];
|
||||||
|
|
||||||
|
// write it into the buffer
|
||||||
|
MSG_WriteDeltaUsercmd( msg, f, t );
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
===================
|
===================
|
||||||
CL_WritePacket
|
CL_WritePacket
|
||||||
|
@ -273,12 +336,14 @@ During normal gameplay, a client packet will contain something like:
|
||||||
*/
|
*/
|
||||||
void CL_WritePacket( void )
|
void CL_WritePacket( void )
|
||||||
{
|
{
|
||||||
sizebuf_t buf;
|
sizebuf_t buf;
|
||||||
bool noDelta = false;
|
bool send_command = false;
|
||||||
byte data[MAX_MSGLEN];
|
byte data[MAX_CMD_BUFFER];
|
||||||
usercmd_t *cmd, *oldcmd;
|
int i, from, to, key, size;
|
||||||
usercmd_t nullcmd;
|
int numbackup = 2;
|
||||||
int key, size;
|
int numcmds;
|
||||||
|
int newcmds;
|
||||||
|
int cmdnumber;
|
||||||
|
|
||||||
// don't send anything if playing back a demo
|
// don't send anything if playing back a demo
|
||||||
if( cls.demoplayback || cls.state == ca_cinematic )
|
if( cls.demoplayback || cls.state == ca_cinematic )
|
||||||
|
@ -287,6 +352,7 @@ void CL_WritePacket( void )
|
||||||
if( cls.state == ca_disconnected || cls.state == ca_connecting )
|
if( cls.state == ca_disconnected || cls.state == ca_connecting )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
/*
|
||||||
if( cls.state == ca_connected )
|
if( cls.state == ca_connected )
|
||||||
{
|
{
|
||||||
// just update reliable
|
// just update reliable
|
||||||
|
@ -294,19 +360,49 @@ void CL_WritePacket( void )
|
||||||
Netchan_Transmit( &cls.netchan, 0, NULL );
|
Netchan_Transmit( &cls.netchan, 0, NULL );
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
CL_ComputePacketLoss ();
|
||||||
|
|
||||||
if( cl_nodelta->integer || !cl.frame.valid || cls.demowaiting )
|
if( cl_cmdrate->value < MIN_CMD_RATE )
|
||||||
noDelta = true;
|
|
||||||
|
|
||||||
if(( cls.netchan.outgoing_sequence - cls.netchan.incoming_acknowledged ) >= CMD_BACKUP )
|
|
||||||
{
|
{
|
||||||
if(( host.realtime - cls.netchan.last_received ) > CONNECTION_PROBLEM_TIME )
|
Cvar_SetValue( "cl_cmdrate", MIN_CMD_RATE );
|
||||||
|
}
|
||||||
|
|
||||||
|
BF_Init( &buf, "ClientData", data, sizeof( data ));
|
||||||
|
|
||||||
|
// Determine number of backup commands to send along
|
||||||
|
numbackup = bound( 0, cl_cmdbackup->integer, MAX_BACKUP_COMMANDS );
|
||||||
|
if( cls.state == ca_connected ) numbackup = 0;
|
||||||
|
|
||||||
|
// Check to see if we can actually send this command
|
||||||
|
|
||||||
|
// In single player, send commands as fast as possible
|
||||||
|
// Otherwise, only send when ready and when not choking bandwidth
|
||||||
|
if(( cl.maxclients == 1 ) || ( NET_IsLocalAddress( cls.netchan.remote_address ) && !host_limitlocal->integer ))
|
||||||
|
send_command = true;
|
||||||
|
else if(( host.realtime >= cls.nextcmdtime ) && Netchan_CanPacket( &cls.netchan ))
|
||||||
|
send_command = true;
|
||||||
|
|
||||||
|
if( cl.force_send_usercmd )
|
||||||
|
{
|
||||||
|
send_command = true;
|
||||||
|
cl.force_send_usercmd = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(( cls.netchan.outgoing_sequence - cls.netchan.incoming_acknowledged ) >= CL_UPDATE_MASK )
|
||||||
|
{
|
||||||
|
if(( host.realtime - cls.netchan.last_received ) > CONNECTION_PROBLEM_TIME && !cls.demoplayback )
|
||||||
{
|
{
|
||||||
MsgDev( D_WARN, "^1 Connection Problem^7\n" );
|
MsgDev( D_WARN, "^1 Connection Problem^7\n" );
|
||||||
noDelta = true; // request a fullupdate
|
cl.validsequence = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if( cl_nodelta->integer )
|
||||||
|
{
|
||||||
|
cl.validsequence = 0;
|
||||||
|
}
|
||||||
|
|
||||||
// send a userinfo update if needed
|
// send a userinfo update if needed
|
||||||
if( userinfo->modified )
|
if( userinfo->modified )
|
||||||
{
|
{
|
||||||
|
@ -315,44 +411,97 @@ void CL_WritePacket( void )
|
||||||
BF_WriteString( &cls.netchan.message, Cvar_Userinfo( ));
|
BF_WriteString( &cls.netchan.message, Cvar_Userinfo( ));
|
||||||
}
|
}
|
||||||
|
|
||||||
BF_Init( &buf, "ClientData", data, sizeof( data ));
|
if( send_command )
|
||||||
|
{
|
||||||
|
int outgoing_sequence;
|
||||||
|
|
||||||
// write new random_seed
|
if( cl_cmdrate->integer > 0 )
|
||||||
BF_WriteByte( &buf, clc_random_seed );
|
cls.nextcmdtime = host.realtime + ( 1.0f / cl_cmdrate->value );
|
||||||
BF_WriteUBitLong( &buf, cl.random_seed, 32 ); // full range
|
else cls.nextcmdtime = host.realtime; // always able to send right away
|
||||||
|
|
||||||
// begin a client move command
|
if( cls.lastoutgoingcommand == -1 )
|
||||||
BF_WriteByte( &buf, clc_move );
|
{
|
||||||
|
outgoing_sequence = cls.netchan.outgoing_sequence;
|
||||||
|
cls.lastoutgoingcommand = cls.netchan.outgoing_sequence;
|
||||||
|
}
|
||||||
|
else outgoing_sequence = cls.lastoutgoingcommand + 1;
|
||||||
|
|
||||||
// save the position for a checksum byte
|
// begin a client move command
|
||||||
key = BF_GetNumBytesWritten( &buf );
|
BF_WriteByte( &buf, clc_move );
|
||||||
BF_WriteByte( &buf, 0 );
|
|
||||||
|
|
||||||
// let the server know what the last frame we
|
// save the position for a checksum byte
|
||||||
// got was, so the next message can be delta compressed
|
key = BF_GetRealBytesWritten( &buf );
|
||||||
if( noDelta ) BF_WriteLong( &buf, -1 ); // no compression
|
BF_WriteByte( &buf, 0 );
|
||||||
else BF_WriteLong( &buf, cl.frame.serverframe );
|
|
||||||
|
|
||||||
// send this and the previous cmds in the message, so
|
// write packet lossage percentation
|
||||||
// if the last packet was dropped, it can be recovered
|
BF_WriteByte( &buf, cls.packet_loss );
|
||||||
cmd = &cl.cmds[(cls.netchan.outgoing_sequence - 2) & CMD_MASK];
|
|
||||||
Mem_Set( &nullcmd, 0, sizeof( nullcmd ));
|
|
||||||
MSG_WriteDeltaUsercmd( &buf, &nullcmd, cmd );
|
|
||||||
oldcmd = cmd;
|
|
||||||
|
|
||||||
cmd = &cl.cmds[(cls.netchan.outgoing_sequence - 1) & CMD_MASK];
|
// say how many backups we'll be sending
|
||||||
MSG_WriteDeltaUsercmd( &buf, oldcmd, cmd );
|
BF_WriteByte( &buf, numbackup );
|
||||||
oldcmd = cmd;
|
|
||||||
|
|
||||||
cmd = &cl.cmds[(cls.netchan.outgoing_sequence - 0) & CMD_MASK];
|
// how many real commands have queued up
|
||||||
MSG_WriteDeltaUsercmd( &buf, oldcmd, cmd );
|
newcmds = ( cls.netchan.outgoing_sequence - cls.lastoutgoingcommand );
|
||||||
|
|
||||||
// calculate a checksum over the move commands
|
// put an upper/lower bound on this
|
||||||
size = BF_GetNumBytesWritten( &buf ) - key - 1;
|
newcmds = bound( 0, newcmds, MAX_TOTAL_CMDS );
|
||||||
buf.pData[key] = CRC_Sequence( buf.pData + key + 1, size, cls.netchan.outgoing_sequence );
|
if( cls.state == ca_connected ) newcmds = 0;
|
||||||
|
|
||||||
// deliver the message
|
BF_WriteByte( &buf, newcmds );
|
||||||
Netchan_Transmit( &cls.netchan, BF_GetNumBytesWritten( &buf ), BF_GetData( &buf ));
|
|
||||||
|
numcmds = newcmds + numbackup;
|
||||||
|
from = -1;
|
||||||
|
|
||||||
|
for( i = numcmds - 1; i >= 0; i-- )
|
||||||
|
{
|
||||||
|
cmdnumber = ( cls.netchan.outgoing_sequence - i ) & CL_UPDATE_MASK;
|
||||||
|
|
||||||
|
to = cmdnumber;
|
||||||
|
CL_WriteUsercmd( &buf, from, to );
|
||||||
|
from = to;
|
||||||
|
|
||||||
|
if( BF_CheckOverflow( &buf ))
|
||||||
|
{
|
||||||
|
Host_Error( "CL_Move, overflowed command buffer (%i bytes)\n", MAX_CMD_BUFFER );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// calculate a checksum over the move commands
|
||||||
|
size = BF_GetRealBytesWritten( &buf ) - key - 1;
|
||||||
|
buf.pData[key] = CRC_Sequence( buf.pData + key + 1, size, cls.netchan.outgoing_sequence );
|
||||||
|
|
||||||
|
// message we are constructing.
|
||||||
|
i = cls.netchan.outgoing_sequence & CL_UPDATE_MASK;
|
||||||
|
|
||||||
|
// determine if we need to ask for a new set of delta's.
|
||||||
|
if( cl.validsequence && !( cls.demorecording && cls.demowaiting ))
|
||||||
|
{
|
||||||
|
cl.delta_sequence = cl.validsequence;
|
||||||
|
|
||||||
|
BF_WriteByte( &buf, clc_delta );
|
||||||
|
BF_WriteByte( &buf, cl.validsequence & 0xFF );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// request delta compression of entities
|
||||||
|
cl.delta_sequence = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( BF_CheckOverflow( &buf ))
|
||||||
|
{
|
||||||
|
Host_Error( "CL_Move, overflowed command buffer (%i bytes)\n", MAX_CMD_BUFFER );
|
||||||
|
}
|
||||||
|
|
||||||
|
// remember outgoing command that we are sending
|
||||||
|
cls.lastoutgoingcommand = cls.netchan.outgoing_sequence;
|
||||||
|
|
||||||
|
// deliver the message (or update reliable)
|
||||||
|
Netchan_Transmit( &cls.netchan, BF_GetNumBytesWritten( &buf ), BF_GetData( &buf ));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Increment sequence number so we can detect that we've held back packets.
|
||||||
|
cls.netchan.outgoing_sequence++;
|
||||||
|
}
|
||||||
|
|
||||||
// update download/upload slider.
|
// update download/upload slider.
|
||||||
Netchan_UpdateProgress( &cls.netchan );
|
Netchan_UpdateProgress( &cls.netchan );
|
||||||
|
@ -368,7 +517,7 @@ Called every frame to builds and sends a command packet to the server.
|
||||||
void CL_SendCmd( void )
|
void CL_SendCmd( void )
|
||||||
{
|
{
|
||||||
// we create commands even if a demo is playing,
|
// we create commands even if a demo is playing,
|
||||||
cl.refdef.cmd = &cl.cmds[cls.netchan.outgoing_sequence & CMD_MASK];
|
cl.refdef.cmd = &cl.cmds[cls.netchan.outgoing_sequence & CL_UPDATE_MASK];
|
||||||
*cl.refdef.cmd = CL_CreateCmd();
|
*cl.refdef.cmd = CL_CreateCmd();
|
||||||
|
|
||||||
// clc_move, userinfo etc
|
// clc_move, userinfo etc
|
||||||
|
@ -574,6 +723,7 @@ void CL_ClearState( void )
|
||||||
S_StopAllSounds ();
|
S_StopAllSounds ();
|
||||||
CL_ClearEffects ();
|
CL_ClearEffects ();
|
||||||
CL_FreeEdicts ();
|
CL_FreeEdicts ();
|
||||||
|
CL_ClearFrames ();
|
||||||
|
|
||||||
if( clgame.hInstance ) clgame.dllFuncs.pfnReset();
|
if( clgame.hInstance ) clgame.dllFuncs.pfnReset();
|
||||||
|
|
||||||
|
@ -956,6 +1106,12 @@ void CL_ConnectionlessPacket( netadr_t from, sizebuf_t *msg )
|
||||||
BF_WriteByte( &cls.netchan.message, clc_stringcmd );
|
BF_WriteByte( &cls.netchan.message, clc_stringcmd );
|
||||||
BF_WriteString( &cls.netchan.message, "new" );
|
BF_WriteString( &cls.netchan.message, "new" );
|
||||||
cls.state = ca_connected;
|
cls.state = ca_connected;
|
||||||
|
|
||||||
|
cl.validsequence = 0; // haven't gotten a valid frame update yet
|
||||||
|
cl.delta_sequence = -1; // we'll request a full delta from the baseline
|
||||||
|
cls.lastoutgoingcommand = -1; // we don't have a backed up cmd history yet
|
||||||
|
cls.nextcmdtime = host.realtime; // we can send a cmd right away
|
||||||
|
|
||||||
UI_SetActiveMenu( false );
|
UI_SetActiveMenu( false );
|
||||||
}
|
}
|
||||||
else if( !com.strcmp( c, "info" ))
|
else if( !com.strcmp( c, "info" ))
|
||||||
|
@ -1143,7 +1299,7 @@ CL_Physinfo_f
|
||||||
void CL_Physinfo_f( void )
|
void CL_Physinfo_f( void )
|
||||||
{
|
{
|
||||||
Msg( "Phys info settings:\n" );
|
Msg( "Phys info settings:\n" );
|
||||||
Info_Print( cl.frame.cd.physinfo );
|
Info_Print( cl.frame.clientdata.physinfo );
|
||||||
}
|
}
|
||||||
|
|
||||||
int precache_check; // for autodownload of precache items
|
int precache_check; // for autodownload of precache items
|
||||||
|
@ -1348,6 +1504,8 @@ void CL_InitLocal( void )
|
||||||
cl_showfps = Cvar_Get( "cl_showfps", "1", CVAR_ARCHIVE, "show client fps" );
|
cl_showfps = Cvar_Get( "cl_showfps", "1", CVAR_ARCHIVE, "show client fps" );
|
||||||
cl_lw = Cvar_Get( "cl_lw", "1", CVAR_ARCHIVE|CVAR_USERINFO, "enable client weapon predicting" );
|
cl_lw = Cvar_Get( "cl_lw", "1", CVAR_ARCHIVE|CVAR_USERINFO, "enable client weapon predicting" );
|
||||||
cl_smooth = Cvar_Get ("cl_smooth", "1", 0, "smooth up stair climbing and interpolate position in multiplayer" );
|
cl_smooth = Cvar_Get ("cl_smooth", "1", 0, "smooth up stair climbing and interpolate position in multiplayer" );
|
||||||
|
cl_cmdbackup = Cvar_Get( "cl_cmdbackup", "2", CVAR_ARCHIVE, "how many additional history commands are sent" );
|
||||||
|
cl_cmdrate = Cvar_Get( "cl_cmdrate", "30", CVAR_ARCHIVE, "Max number of command packets sent to server per second" );
|
||||||
Cvar_Get( "hud_scale", "0", CVAR_ARCHIVE|CVAR_LATCH, "scale hud at current resolution" );
|
Cvar_Get( "hud_scale", "0", CVAR_ARCHIVE|CVAR_LATCH, "scale hud at current resolution" );
|
||||||
|
|
||||||
// register our commands
|
// register our commands
|
||||||
|
|
|
@ -27,12 +27,12 @@ const char *svc_strings[256] =
|
||||||
"svc_stufftext",
|
"svc_stufftext",
|
||||||
"svc_setangle",
|
"svc_setangle",
|
||||||
"svc_serverdata",
|
"svc_serverdata",
|
||||||
"svc_addangle",
|
"svc_restore",
|
||||||
"svc_frame",
|
"svc_frame",
|
||||||
"svc_clientdata",
|
|
||||||
"svc_packetentities",
|
|
||||||
"svc_download",
|
|
||||||
"svc_usermessage",
|
"svc_usermessage",
|
||||||
|
"svc_clientdata",
|
||||||
|
"svc_download",
|
||||||
|
"svc_updatepings",
|
||||||
"svc_particle",
|
"svc_particle",
|
||||||
"svc_ambientsound",
|
"svc_ambientsound",
|
||||||
"svc_spawnstatic",
|
"svc_spawnstatic",
|
||||||
|
@ -53,11 +53,11 @@ const char *svc_strings[256] =
|
||||||
"svc_weaponanim",
|
"svc_weaponanim",
|
||||||
"svc_bspdecal",
|
"svc_bspdecal",
|
||||||
"svc_roomtype",
|
"svc_roomtype",
|
||||||
"svc_restore",
|
"svc_addangle",
|
||||||
"svc_unused39",
|
"svc_unused39",
|
||||||
"svc_unused40",
|
"svc_packetentities",
|
||||||
"svc_unused41",
|
"svc_deltapacketentities",
|
||||||
"svc_unused42",
|
"svc_chokecount",
|
||||||
"svc_unused43",
|
"svc_unused43",
|
||||||
"svc_unused44",
|
"svc_unused44",
|
||||||
"svc_unused45",
|
"svc_unused45",
|
||||||
|
@ -182,7 +182,7 @@ void CL_WriteMessageHistory( void )
|
||||||
|
|
||||||
for( i = 0; i < MSG_COUNT - 1; i++ )
|
for( i = 0; i < MSG_COUNT - 1; i++ )
|
||||||
{
|
{
|
||||||
thecmd &= CMD_MASK;
|
thecmd &= MSG_MASK;
|
||||||
old = &cls_message_debug.oldcmd[thecmd];
|
old = &cls_message_debug.oldcmd[thecmd];
|
||||||
|
|
||||||
MsgDev( D_INFO,"%i %04i %s\n", old->frame_number, old->starting_offset, CL_MsgInfo( old->command ));
|
MsgDev( D_INFO,"%i %04i %s\n", old->frame_number, old->starting_offset, CL_MsgInfo( old->command ));
|
||||||
|
@ -583,6 +583,85 @@ void CL_ParseServerData( sizebuf_t *msg )
|
||||||
Mem_Set( &clgame.oldmovevars, 0, sizeof( clgame.oldmovevars ));
|
Mem_Set( &clgame.oldmovevars, 0, sizeof( clgame.oldmovevars ));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
===================
|
||||||
|
CL_ParseClientData
|
||||||
|
===================
|
||||||
|
*/
|
||||||
|
void CL_ParseClientData( sizebuf_t *msg )
|
||||||
|
{
|
||||||
|
int i, j;
|
||||||
|
clientdata_t *from_cd, *to_cd;
|
||||||
|
weapon_data_t *from_wd, *to_wd;
|
||||||
|
weapon_data_t nullwd[32];
|
||||||
|
clientdata_t nullcd;
|
||||||
|
frame_t *frame;
|
||||||
|
int idx;
|
||||||
|
|
||||||
|
// this is the frame update that this message corresponds to
|
||||||
|
i = cls.netchan.incoming_sequence;
|
||||||
|
|
||||||
|
// did we drop some frames?
|
||||||
|
if( i > cl.last_incoming_sequence + 1 )
|
||||||
|
{
|
||||||
|
// mark as dropped
|
||||||
|
for( j = cl.last_incoming_sequence + 1; j < i; j++ )
|
||||||
|
{
|
||||||
|
if( cl.frames[j & CL_UPDATE_MASK].receivedtime >= 0.0 )
|
||||||
|
{
|
||||||
|
cl.frames[j & CL_UPDATE_MASK ].receivedtime = -1;
|
||||||
|
cl.frames[j & CL_UPDATE_MASK ].latency = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
cl.parsecount = i; // ack'd incoming messages.
|
||||||
|
cl.parsecountmod = cl.parsecount & CL_UPDATE_MASK; // index into window.
|
||||||
|
frame = &cl.frames[cl.parsecountmod]; // frame at index.
|
||||||
|
|
||||||
|
frame->time = cl.mtime[0]; // mark network received time
|
||||||
|
frame->receivedtime = host.realtime; // time now that we are parsing.
|
||||||
|
|
||||||
|
// Fixme, do this after all packets read for this frame?
|
||||||
|
cl.last_incoming_sequence = cls.netchan.incoming_sequence;
|
||||||
|
|
||||||
|
to_cd = &frame->clientdata;
|
||||||
|
to_wd = frame->weapondata;
|
||||||
|
|
||||||
|
// clear to old value before delta parsing
|
||||||
|
if( !BF_ReadOneBit( msg ))
|
||||||
|
{
|
||||||
|
Mem_Set( &nullcd, 0, sizeof( nullcd ));
|
||||||
|
Mem_Set( nullwd, 0, sizeof( nullwd ));
|
||||||
|
from_cd = &nullcd;
|
||||||
|
from_wd = nullwd;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int delta_sequence = BF_ReadByte( msg );
|
||||||
|
|
||||||
|
from_cd = &cl.frames[delta_sequence & CL_UPDATE_MASK].clientdata;
|
||||||
|
from_wd = cl.frames[delta_sequence & CL_UPDATE_MASK].weapondata;
|
||||||
|
|
||||||
|
if(( delta_sequence & CL_UPDATE_MASK ) != ( cl.delta_sequence & CL_UPDATE_MASK ))
|
||||||
|
MsgDev( D_WARN, "CL_ParseClientData: mismatch delta_sequence\n" );
|
||||||
|
}
|
||||||
|
|
||||||
|
MSG_ReadClientData( msg, from_cd, to_cd, sv_time( ));
|
||||||
|
|
||||||
|
for( i = 0; i < MAX_WEAPONS; i++ )
|
||||||
|
{
|
||||||
|
// check for end of weapondata (and clientdata_t message)
|
||||||
|
if( !BF_ReadOneBit( msg )) break;
|
||||||
|
|
||||||
|
// read the weapon idx
|
||||||
|
idx = BF_ReadUBitLong( msg, MAX_WEAPON_BITS );
|
||||||
|
|
||||||
|
MSG_ReadWeaponData( msg, &from_wd[idx], &to_wd[idx], sv_time( ));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
==================
|
==================
|
||||||
CL_ParseBaseline
|
CL_ParseBaseline
|
||||||
|
@ -755,6 +834,33 @@ void CL_UpdateUserinfo( sizebuf_t *msg )
|
||||||
else Mem_Set( player, 0, sizeof( *player ));
|
else Mem_Set( player, 0, sizeof( *player ));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
================
|
||||||
|
CL_UpdateUserPings
|
||||||
|
|
||||||
|
collect pings and packet lossage from clients
|
||||||
|
================
|
||||||
|
*/
|
||||||
|
void CL_UpdateUserPings( sizebuf_t *msg )
|
||||||
|
{
|
||||||
|
int i, slot;
|
||||||
|
player_info_t *player;
|
||||||
|
|
||||||
|
for( i = 0; i < MAX_CLIENTS; i++ )
|
||||||
|
{
|
||||||
|
if( !BF_ReadOneBit( msg )) break; // end of message
|
||||||
|
|
||||||
|
slot = BF_ReadUBitLong( msg, MAX_CLIENT_BITS );
|
||||||
|
|
||||||
|
if( slot >= MAX_CLIENTS )
|
||||||
|
Host_Error( "CL_ParseServerMessage: svc_updatepings > MAX_CLIENTS\n" );
|
||||||
|
|
||||||
|
player = &cl.players[slot];
|
||||||
|
player->ping = BF_ReadUBitLong( msg, 12 );
|
||||||
|
player->packet_loss = BF_ReadUBitLong( msg, 7 );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
==============
|
==============
|
||||||
CL_ServerInfo
|
CL_ServerInfo
|
||||||
|
@ -863,7 +969,7 @@ CL_ParseServerMessage
|
||||||
void CL_ParseServerMessage( sizebuf_t *msg )
|
void CL_ParseServerMessage( sizebuf_t *msg )
|
||||||
{
|
{
|
||||||
char *s;
|
char *s;
|
||||||
int i, cmd;
|
int i, j, cmd;
|
||||||
int param1, param2;
|
int param1, param2;
|
||||||
int bufStart;
|
int bufStart;
|
||||||
|
|
||||||
|
@ -898,7 +1004,6 @@ void CL_ParseServerMessage( sizebuf_t *msg )
|
||||||
Host_Error( "svc_bad\n" );
|
Host_Error( "svc_bad\n" );
|
||||||
break;
|
break;
|
||||||
case svc_nop:
|
case svc_nop:
|
||||||
MsgDev( D_ERROR, "CL_ParseServerMessage: user message out of bounds\n" );
|
|
||||||
break;
|
break;
|
||||||
case svc_disconnect:
|
case svc_disconnect:
|
||||||
CL_Drop ();
|
CL_Drop ();
|
||||||
|
@ -931,7 +1036,9 @@ void CL_ParseServerMessage( sizebuf_t *msg )
|
||||||
CL_ParseSoundPacket( msg, false );
|
CL_ParseSoundPacket( msg, false );
|
||||||
break;
|
break;
|
||||||
case svc_time:
|
case svc_time:
|
||||||
BF_ReadFloat( msg ); // time
|
cl.mtime[1] = cl.mtime[0];
|
||||||
|
cl.mtime[0] = BF_ReadFloat( msg );
|
||||||
|
break;
|
||||||
break;
|
break;
|
||||||
case svc_print:
|
case svc_print:
|
||||||
i = BF_ReadByte( msg );
|
i = BF_ReadByte( msg );
|
||||||
|
@ -953,18 +1060,21 @@ void CL_ParseServerMessage( sizebuf_t *msg )
|
||||||
case svc_addangle:
|
case svc_addangle:
|
||||||
CL_ParseAddAngle( msg );
|
CL_ParseAddAngle( msg );
|
||||||
break;
|
break;
|
||||||
case svc_frame:
|
|
||||||
CL_ParseFrame( msg );
|
|
||||||
break;
|
|
||||||
case svc_clientdata:
|
case svc_clientdata:
|
||||||
Host_Error( "svc_clientdata: out of place frame data\n" );
|
CL_ParseClientData( msg );
|
||||||
break;
|
break;
|
||||||
case svc_packetentities:
|
case svc_packetentities:
|
||||||
Host_Error( "svc_packetentities: out of place frame data\n" );
|
CL_ParsePacketEntities( msg, false );
|
||||||
|
break;
|
||||||
|
case svc_deltapacketentities:
|
||||||
|
CL_ParsePacketEntities( msg, true );
|
||||||
break;
|
break;
|
||||||
case svc_download:
|
case svc_download:
|
||||||
CL_ParseDownload( msg );
|
CL_ParseDownload( msg );
|
||||||
break;
|
break;
|
||||||
|
case svc_updatepings:
|
||||||
|
CL_UpdateUserPings( msg );
|
||||||
|
break;
|
||||||
case svc_usermessage:
|
case svc_usermessage:
|
||||||
CL_RegisterUserMessage( msg );
|
CL_RegisterUserMessage( msg );
|
||||||
break;
|
break;
|
||||||
|
@ -1032,6 +1142,18 @@ void CL_ParseServerMessage( sizebuf_t *msg )
|
||||||
param1 = BF_ReadShort( msg );
|
param1 = BF_ReadShort( msg );
|
||||||
Cvar_SetValue( "room_type", param1 );
|
Cvar_SetValue( "room_type", param1 );
|
||||||
break;
|
break;
|
||||||
|
case svc_chokecount:
|
||||||
|
i = BF_ReadByte( msg );
|
||||||
|
j = cls.netchan.incoming_acknowledged - 1;
|
||||||
|
for( ; i > 0 && j > cls.netchan.outgoing_sequence - CL_UPDATE_BACKUP; j-- )
|
||||||
|
{
|
||||||
|
if( cl.frames[j & CL_UPDATE_MASK].receivedtime != -3.0 )
|
||||||
|
{
|
||||||
|
cl.frames[j & CL_UPDATE_MASK].receivedtime = -2.0;
|
||||||
|
i--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
case svc_director:
|
case svc_director:
|
||||||
CL_ParseDirector( msg );
|
CL_ParseDirector( msg );
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -436,8 +436,8 @@ void CL_SetSolidEntities( void )
|
||||||
|
|
||||||
for( i = 0; i < 3; i++ )
|
for( i = 0; i < 3; i++ )
|
||||||
{
|
{
|
||||||
absmin[i] = cl.frame.cd.origin[i] - 1024;
|
absmin[i] = cl.frame.clientdata.origin[i] - 1024;
|
||||||
absmax[i] = cl.frame.cd.origin[i] + 1024;
|
absmax[i] = cl.frame.clientdata.origin[i] + 1024;
|
||||||
}
|
}
|
||||||
|
|
||||||
CL_CopyEntityToPhysEnt( &clgame.pmove->physents[0], &clgame.entities[0] );
|
CL_CopyEntityToPhysEnt( &clgame.pmove->physents[0], &clgame.entities[0] );
|
||||||
|
|
|
@ -69,7 +69,7 @@ void CL_CheckPredictionError( void )
|
||||||
|
|
||||||
// calculate the last usercmd_t we sent that the server has processed
|
// calculate the last usercmd_t we sent that the server has processed
|
||||||
frame = cls.netchan.incoming_acknowledged;
|
frame = cls.netchan.incoming_acknowledged;
|
||||||
frame &= CMD_MASK;
|
frame &= CL_UPDATE_MASK;
|
||||||
|
|
||||||
// compare what the server returned with what we had predicted it to be
|
// compare what the server returned with what we had predicted it to be
|
||||||
VectorSubtract( player->curstate.origin, cl.predicted_origins[frame], delta );
|
VectorSubtract( player->curstate.origin, cl.predicted_origins[frame], delta );
|
||||||
|
@ -106,7 +106,7 @@ void CL_SetIdealPitch( cl_entity_t *ent )
|
||||||
int i, j;
|
int i, j;
|
||||||
int step, dir, steps;
|
int step, dir, steps;
|
||||||
|
|
||||||
if( !( cl.frame.cd.flags & FL_ONGROUND ))
|
if( !( cl.frame.clientdata.flags & FL_ONGROUND ))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
angleval = ent->angles[YAW] * M_PI * 2 / 360;
|
angleval = ent->angles[YAW] * M_PI * 2 / 360;
|
||||||
|
@ -116,7 +116,7 @@ void CL_SetIdealPitch( cl_entity_t *ent )
|
||||||
{
|
{
|
||||||
top[0] = ent->origin[0] + cosval * (i + 3) * 12;
|
top[0] = ent->origin[0] + cosval * (i + 3) * 12;
|
||||||
top[1] = ent->origin[1] + sinval * (i + 3) * 12;
|
top[1] = ent->origin[1] + sinval * (i + 3) * 12;
|
||||||
top[2] = ent->origin[2] + cl.frame.cd.view_ofs[2];
|
top[2] = ent->origin[2] + cl.frame.clientdata.view_ofs[2];
|
||||||
|
|
||||||
bottom[0] = top[0];
|
bottom[0] = top[0];
|
||||||
bottom[1] = top[1];
|
bottom[1] = top[1];
|
||||||
|
@ -177,7 +177,7 @@ void CL_PredictMovement( void )
|
||||||
|
|
||||||
player = CL_GetLocalPlayer ();
|
player = CL_GetLocalPlayer ();
|
||||||
viewent = CL_GetEntityByIndex( cl.refdef.viewentity );
|
viewent = CL_GetEntityByIndex( cl.refdef.viewentity );
|
||||||
cd = &cl.frame.cd;
|
cd = &cl.frame.clientdata;
|
||||||
|
|
||||||
CL_SetIdealPitch( player );
|
CL_SetIdealPitch( player );
|
||||||
|
|
||||||
|
@ -230,7 +230,7 @@ void CL_PredictMovement( void )
|
||||||
// run frames
|
// run frames
|
||||||
while( ++ack < current )
|
while( ++ack < current )
|
||||||
{
|
{
|
||||||
frame = ack & CMD_MASK;
|
frame = ack & CL_UPDATE_MASK;
|
||||||
cmd = &cl.cmds[frame];
|
cmd = &cl.cmds[frame];
|
||||||
|
|
||||||
CL_PreRunCmd( player, cmd );
|
CL_PreRunCmd( player, cmd );
|
||||||
|
|
|
@ -33,11 +33,11 @@ void V_SetupRefDef( void )
|
||||||
|
|
||||||
clent = CL_GetLocalPlayer ();
|
clent = CL_GetLocalPlayer ();
|
||||||
|
|
||||||
VectorCopy( cl.frame.cd.punchangle, cl.refdef.punchangle );
|
VectorCopy( cl.frame.clientdata.punchangle, cl.refdef.punchangle );
|
||||||
|
|
||||||
cl.refdef.movevars = &clgame.movevars;
|
cl.refdef.movevars = &clgame.movevars;
|
||||||
cl.refdef.onground = ( cl.frame.cd.flags & FL_ONGROUND ) ? 1 : 0;
|
cl.refdef.onground = ( cl.frame.clientdata.flags & FL_ONGROUND ) ? 1 : 0;
|
||||||
cl.refdef.health = cl.frame.cd.health;
|
cl.refdef.health = cl.frame.clientdata.health;
|
||||||
cl.refdef.lerpfrac = cl.lerpFrac;
|
cl.refdef.lerpfrac = cl.lerpFrac;
|
||||||
cl.refdef.num_entities = clgame.numEntities;
|
cl.refdef.num_entities = clgame.numEntities;
|
||||||
cl.refdef.max_entities = clgame.maxEntities;
|
cl.refdef.max_entities = clgame.maxEntities;
|
||||||
|
@ -46,7 +46,7 @@ void V_SetupRefDef( void )
|
||||||
cl.refdef.frametime = cl.time - cl.oldtime;
|
cl.refdef.frametime = cl.time - cl.oldtime;
|
||||||
cl.refdef.demoplayback = cls.demoplayback;
|
cl.refdef.demoplayback = cls.demoplayback;
|
||||||
cl.refdef.smoothing = cl_smooth->integer;
|
cl.refdef.smoothing = cl_smooth->integer;
|
||||||
cl.refdef.waterlevel = cl.frame.cd.waterlevel;
|
cl.refdef.waterlevel = cl.frame.clientdata.waterlevel;
|
||||||
cl.refdef.flags = cl.render_flags;
|
cl.refdef.flags = cl.render_flags;
|
||||||
cl.refdef.viewsize = 120; // FIXME if you can
|
cl.refdef.viewsize = 120; // FIXME if you can
|
||||||
cl.refdef.nextView = 0;
|
cl.refdef.nextView = 0;
|
||||||
|
@ -73,8 +73,8 @@ void V_SetupRefDef( void )
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
VectorCopy( clent->origin, cl.refdef.simorg );
|
VectorCopy( clent->origin, cl.refdef.simorg );
|
||||||
VectorCopy( cl.frame.cd.view_ofs, cl.refdef.viewheight );
|
VectorCopy( cl.frame.clientdata.view_ofs, cl.refdef.viewheight );
|
||||||
VectorCopy( cl.frame.cd.velocity, cl.refdef.simvel );
|
VectorCopy( cl.frame.clientdata.velocity, cl.refdef.simvel );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -29,49 +29,61 @@
|
||||||
//=============================================================================
|
//=============================================================================
|
||||||
typedef struct frame_s
|
typedef struct frame_s
|
||||||
{
|
{
|
||||||
|
// received from server
|
||||||
|
double receivedtime; // time message was received, or -1
|
||||||
|
double latency;
|
||||||
|
double time; // server timestamp
|
||||||
|
|
||||||
|
clientdata_t clientdata; // message received that reflects performing
|
||||||
|
weapon_data_t weapondata[32];
|
||||||
|
packet_entities_t entities;
|
||||||
|
|
||||||
bool valid; // cleared if delta parsing was invalid
|
bool valid; // cleared if delta parsing was invalid
|
||||||
int serverframe;
|
|
||||||
int deltaframe;
|
|
||||||
int num_entities;
|
|
||||||
int parse_entities; // non-masked index into cl_parse_entities array
|
|
||||||
clientdata_t cd; // current client data
|
|
||||||
} frame_t;
|
} frame_t;
|
||||||
|
|
||||||
#define CMD_BACKUP 64 // allow a lot of command backups for very fast systems
|
#define CMD_BACKUP MULTIPLAYER_BACKUP // allow a lot of command backups for very fast systems
|
||||||
#define CMD_MASK (CMD_BACKUP - 1)
|
#define CMD_MASK (CMD_BACKUP - 1)
|
||||||
|
|
||||||
#define CL_UPDATE_MASK (CL_UPDATE_BACKUP - 1)
|
#define CL_UPDATE_MASK (CL_UPDATE_BACKUP - 1)
|
||||||
extern int CL_UPDATE_BACKUP;
|
extern int CL_UPDATE_BACKUP;
|
||||||
|
|
||||||
// the cl_parse_entities must be large enough to hold CL_UPDATE_BACKUP frames of
|
|
||||||
// entities, so that when a delta compressed message arives from the server
|
|
||||||
// it can be un-deltad from the original
|
|
||||||
#define MAX_PARSE_ENTITIES 2048
|
|
||||||
|
|
||||||
// the client_t structure is wiped completely at every
|
// the client_t structure is wiped completely at every
|
||||||
// server map change
|
// server map change
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
int timeoutcount;
|
int timeoutcount;
|
||||||
|
|
||||||
|
int servercount; // server identification for prespawns
|
||||||
|
int validsequence; // this is the sequence number of the last good
|
||||||
|
// world snapshot/update we got. If this is 0, we can't
|
||||||
|
// render a frame yet
|
||||||
|
int parsecount; // server message counter
|
||||||
|
int parsecountmod; // modulo with network window
|
||||||
|
double parsecounttime; // timestamp of parse
|
||||||
|
|
||||||
bool video_prepped; // false if on new level or new ref dll
|
bool video_prepped; // false if on new level or new ref dll
|
||||||
bool audio_prepped; // false if on new level or new snd dll
|
bool audio_prepped; // false if on new level or new snd dll
|
||||||
bool force_refdef; // vid has changed, so we can't use a paused refdef
|
bool force_refdef; // vid has changed, so we can't use a paused refdef
|
||||||
int parse_entities; // index (not anded off) into cl_parse_entities[]
|
|
||||||
|
|
||||||
usercmd_t cmds[CMD_BACKUP]; // each mesage will send several old cmds
|
int delta_sequence; // acknowledged sequence number
|
||||||
|
|
||||||
|
double mtime[2]; // the timestamp of the last two messages
|
||||||
|
|
||||||
|
|
||||||
|
int last_incoming_sequence;
|
||||||
|
|
||||||
|
bool force_send_usercmd;
|
||||||
|
|
||||||
frame_t frame; // received from server
|
frame_t frame; // received from server
|
||||||
frame_t *oldframe; // previous frame to lerping from
|
|
||||||
int surpressCount; // number of messages rate supressed
|
int surpressCount; // number of messages rate supressed
|
||||||
frame_t *frames; // alloced on svc_serverdata
|
frame_t frames[MULTIPLAYER_BACKUP]; // alloced on svc_serverdata
|
||||||
|
usercmd_t cmds[MULTIPLAYER_BACKUP]; // each mesage will send several old cmds
|
||||||
|
|
||||||
double time; // this is the time value that the client
|
double time; // this is the time value that the client
|
||||||
// is rendering at. always <= cls.realtime
|
// is rendering at. always <= cls.realtime
|
||||||
// a lerp point for other data
|
// a lerp point for other data
|
||||||
double oldtime; // previous cl.time, time-oldtime is used
|
double oldtime; // previous cl.time, time-oldtime is used
|
||||||
// to decay light values and smooth step ups
|
// to decay light values and smooth step ups
|
||||||
double mtime[2]; // the timestamp of the last two messages
|
|
||||||
|
|
||||||
int render_flags; // clearing at end of frame
|
int render_flags; // clearing at end of frame
|
||||||
float lerpFrac; // interpolation value
|
float lerpFrac; // interpolation value
|
||||||
|
@ -93,10 +105,8 @@ typedef struct
|
||||||
// server state information
|
// server state information
|
||||||
int playernum;
|
int playernum;
|
||||||
int maxclients;
|
int maxclients;
|
||||||
int servercount; // server identification for prespawns
|
|
||||||
int movemessages;
|
int movemessages;
|
||||||
char configstrings[MAX_CONFIGSTRINGS][CS_SIZE];
|
char configstrings[MAX_CONFIGSTRINGS][CS_SIZE];
|
||||||
entity_state_t entity_curstates[MAX_PARSE_ENTITIES]; // FIXME: this is must match with max_edicts ?
|
|
||||||
|
|
||||||
model_t *worldmodel; // pointer to world
|
model_t *worldmodel; // pointer to world
|
||||||
|
|
||||||
|
@ -291,6 +301,12 @@ typedef struct
|
||||||
int serverProtocol; // in case we are doing some kind of version hack
|
int serverProtocol; // in case we are doing some kind of version hack
|
||||||
int challenge; // from the server to use for connecting
|
int challenge; // from the server to use for connecting
|
||||||
|
|
||||||
|
float packet_loss;
|
||||||
|
double packet_loss_recalc_time;
|
||||||
|
|
||||||
|
float nextcmdtime; // when can we send the next command packet?
|
||||||
|
int lastoutgoingcommand; // sequence number of last outgoing command
|
||||||
|
|
||||||
// internal shaders
|
// internal shaders
|
||||||
shader_t fillShader; // used for emulate FillRGBA to avoid wrong draw-sort
|
shader_t fillShader; // used for emulate FillRGBA to avoid wrong draw-sort
|
||||||
shader_t pauseIcon; // draw 'paused' when game in-pause
|
shader_t pauseIcon; // draw 'paused' when game in-pause
|
||||||
|
@ -477,9 +493,10 @@ bool CL_IsPredicted( void );
|
||||||
//
|
//
|
||||||
// cl_frame.c
|
// cl_frame.c
|
||||||
//
|
//
|
||||||
void CL_ParseFrame( sizebuf_t *msg );
|
void CL_ParsePacketEntities( sizebuf_t *msg, bool delta );
|
||||||
void CL_UpdateStudioVars( cl_entity_t *ent, entity_state_t *newstate );
|
void CL_UpdateStudioVars( cl_entity_t *ent, entity_state_t *newstate );
|
||||||
bool CL_GetEntitySpatialization( int ent, vec3_t origin, vec3_t velocity );
|
bool CL_GetEntitySpatialization( int ent, vec3_t origin, vec3_t velocity );
|
||||||
|
void CL_ClearFrames( void );
|
||||||
|
|
||||||
//
|
//
|
||||||
// cl_tent.c
|
// cl_tent.c
|
||||||
|
|
|
@ -64,6 +64,7 @@ extern cvar_t *scr_width;
|
||||||
extern cvar_t *scr_height;
|
extern cvar_t *scr_height;
|
||||||
extern cvar_t *scr_download;
|
extern cvar_t *scr_download;
|
||||||
extern cvar_t *allow_download;
|
extern cvar_t *allow_download;
|
||||||
|
extern cvar_t *host_limitlocal;
|
||||||
extern cvar_t *host_maxfps;
|
extern cvar_t *host_maxfps;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -112,7 +112,7 @@ void BF_WriteOneBit( sizebuf_t *bf, int nValue )
|
||||||
|
|
||||||
void BF_WriteUBitLongExt( sizebuf_t *bf, uint curData, int numbits, bool bCheckRange )
|
void BF_WriteUBitLongExt( sizebuf_t *bf, uint curData, int numbits, bool bCheckRange )
|
||||||
{
|
{
|
||||||
#ifdef _DEBUG
|
#ifdef _NETDEBUG
|
||||||
// make sure it doesn't overflow.
|
// make sure it doesn't overflow.
|
||||||
if( bCheckRange && numbits < 32 )
|
if( bCheckRange && numbits < 32 )
|
||||||
{
|
{
|
||||||
|
@ -175,7 +175,7 @@ void BF_WriteSBitLong( sizebuf_t *bf, int data, int numbits )
|
||||||
// (Some old code writes direct integers right into the buffer).
|
// (Some old code writes direct integers right into the buffer).
|
||||||
if( data < 0 )
|
if( data < 0 )
|
||||||
{
|
{
|
||||||
#ifdef _DEBUG
|
#ifdef _NETDEBUG
|
||||||
if( numbits < 32 )
|
if( numbits < 32 )
|
||||||
{
|
{
|
||||||
// Make sure it doesn't overflow.
|
// Make sure it doesn't overflow.
|
||||||
|
|
|
@ -35,6 +35,7 @@ typedef struct
|
||||||
#define BF_WriteUBitLong( bf, data, bits ) BF_WriteUBitLongExt( bf, data, bits, true );
|
#define BF_WriteUBitLong( bf, data, bits ) BF_WriteUBitLongExt( bf, data, bits, true );
|
||||||
#define BF_StartReading BF_StartWriting
|
#define BF_StartReading BF_StartWriting
|
||||||
#define BF_GetNumBytesRead BF_GetNumBytesWritten
|
#define BF_GetNumBytesRead BF_GetNumBytesWritten
|
||||||
|
#define BF_GetRealBytesRead BF_GetRealBytesWritten
|
||||||
#define BF_GetNumBitsRead BF_GetNumBitsWritten
|
#define BF_GetNumBitsRead BF_GetNumBitsWritten
|
||||||
#define BF_ReadBitAngles BF_ReadBitVec3Coord
|
#define BF_ReadBitAngles BF_ReadBitVec3Coord
|
||||||
#define BF_ReadString( bf ) BF_ReadStringExt( bf, false )
|
#define BF_ReadString( bf ) BF_ReadStringExt( bf, false )
|
||||||
|
@ -81,7 +82,8 @@ bool BF_WriteString( sizebuf_t *bf, const char *pStr ); // returns false if it
|
||||||
bool BF_WriteDeltaMovevars( sizebuf_t *sb, struct movevars_s *from, struct movevars_s *to );
|
bool BF_WriteDeltaMovevars( sizebuf_t *sb, struct movevars_s *from, struct movevars_s *to );
|
||||||
|
|
||||||
// helper functions
|
// helper functions
|
||||||
_inline int BF_GetNumBytesWritten( sizebuf_t *bf ) { return BitByte( bf->iCurBit ); }
|
_inline int BF_GetNumBytesWritten( sizebuf_t *bf ) { return BitByte( bf->iCurBit ); }
|
||||||
|
_inline int BF_GetRealBytesWritten( sizebuf_t *bf ) { return bf->iCurBit >> 3; } // unpadded
|
||||||
_inline int BF_GetNumBitsWritten( sizebuf_t *bf ) { return bf->iCurBit; }
|
_inline int BF_GetNumBitsWritten( sizebuf_t *bf ) { return bf->iCurBit; }
|
||||||
_inline int BF_GetMaxBits( sizebuf_t *bf ) { return bf->nDataBits; }
|
_inline int BF_GetMaxBits( sizebuf_t *bf ) { return bf->nDataBits; }
|
||||||
_inline int BF_GetMaxBytes( sizebuf_t *bf ) { return bf->nDataBits >> 3; }
|
_inline int BF_GetMaxBytes( sizebuf_t *bf ) { return bf->nDataBits >> 3; }
|
||||||
|
|
|
@ -83,6 +83,7 @@ cvar_t *net_showdrop;
|
||||||
cvar_t *net_speeds;
|
cvar_t *net_speeds;
|
||||||
cvar_t *net_qport;
|
cvar_t *net_qport;
|
||||||
|
|
||||||
|
int net_drop;
|
||||||
netadr_t net_from;
|
netadr_t net_from;
|
||||||
sizebuf_t net_message;
|
sizebuf_t net_message;
|
||||||
byte *net_mempool;
|
byte *net_mempool;
|
||||||
|
@ -1556,9 +1557,11 @@ bool Netchan_Process( netchan_t *chan, sizebuf_t *msg )
|
||||||
}
|
}
|
||||||
|
|
||||||
// dropped packets don't keep the message from being used
|
// dropped packets don't keep the message from being used
|
||||||
chan->dropped = sequence - ( chan->incoming_sequence + 1 );
|
net_drop = sequence - ( chan->incoming_sequence + 1 );
|
||||||
if( chan->dropped > 0 )
|
if( net_drop > 0 )
|
||||||
{
|
{
|
||||||
|
chan->drop_count += 1;
|
||||||
|
|
||||||
if( net_showdrop->integer )
|
if( net_showdrop->integer )
|
||||||
{
|
{
|
||||||
Msg( "%s:Dropped %i packets at %i\n"
|
Msg( "%s:Dropped %i packets at %i\n"
|
||||||
|
|
|
@ -1359,6 +1359,81 @@ void MSG_ReadClientData( sizebuf_t *msg, clientdata_t *from, clientdata_t *to, f
|
||||||
/*
|
/*
|
||||||
=============================================================================
|
=============================================================================
|
||||||
|
|
||||||
|
weapon_data_t communication
|
||||||
|
|
||||||
|
=============================================================================
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
==================
|
||||||
|
MSG_WriteWeaponData
|
||||||
|
|
||||||
|
Writes current client data only for local client
|
||||||
|
Other clients can grab the client state from entity_state_t
|
||||||
|
==================
|
||||||
|
*/
|
||||||
|
void MSG_WriteWeaponData( sizebuf_t *msg, weapon_data_t *from, weapon_data_t *to, float timebase, int index )
|
||||||
|
{
|
||||||
|
delta_t *pField;
|
||||||
|
delta_info_t *dt;
|
||||||
|
int i, startBit;
|
||||||
|
int numChanges = 0;
|
||||||
|
|
||||||
|
dt = Delta_FindStruct( "weapon_data_t" );
|
||||||
|
ASSERT( dt && dt->bInitialized );
|
||||||
|
|
||||||
|
pField = dt->pFields;
|
||||||
|
ASSERT( pField );
|
||||||
|
|
||||||
|
// activate fields and call custom encode func
|
||||||
|
Delta_CustomEncode( dt, from, to );
|
||||||
|
|
||||||
|
startBit = msg->iCurBit;
|
||||||
|
|
||||||
|
BF_WriteOneBit( msg, 1 );
|
||||||
|
BF_WriteUBitLong( msg, index, MAX_WEAPON_BITS );
|
||||||
|
|
||||||
|
// process fields
|
||||||
|
for( i = 0; i < dt->numFields; i++, pField++ )
|
||||||
|
{
|
||||||
|
if( Delta_WriteField( msg, pField, from, to, timebase ))
|
||||||
|
numChanges++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we have no changes - kill the message
|
||||||
|
if( !numChanges ) BF_SeekToBit( msg, startBit );
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
==================
|
||||||
|
MSG_ReadWeaponData
|
||||||
|
|
||||||
|
Read the clientdata
|
||||||
|
==================
|
||||||
|
*/
|
||||||
|
void MSG_ReadWeaponData( sizebuf_t *msg, weapon_data_t *from, weapon_data_t *to, float timebase )
|
||||||
|
{
|
||||||
|
delta_t *pField;
|
||||||
|
delta_info_t *dt;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
dt = Delta_FindStruct( "weapon_data_t" );
|
||||||
|
ASSERT( dt && dt->bInitialized );
|
||||||
|
|
||||||
|
pField = dt->pFields;
|
||||||
|
ASSERT( pField );
|
||||||
|
|
||||||
|
*to = *from;
|
||||||
|
|
||||||
|
// process fields
|
||||||
|
for( i = 0; i < dt->numFields; i++, pField++ )
|
||||||
|
{
|
||||||
|
Delta_ReadField( msg, pField, from, to, timebase );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
=============================================================================
|
||||||
|
|
||||||
entity_state_t communication
|
entity_state_t communication
|
||||||
|
|
||||||
=============================================================================
|
=============================================================================
|
||||||
|
|
|
@ -100,6 +100,8 @@ bool MSG_WriteDeltaMovevars( sizebuf_t *msg, struct movevars_s *from, struct mov
|
||||||
void MSG_ReadDeltaMovevars( sizebuf_t *msg, struct movevars_s *from, struct movevars_s *to );
|
void MSG_ReadDeltaMovevars( sizebuf_t *msg, struct movevars_s *from, struct movevars_s *to );
|
||||||
void MSG_WriteClientData( sizebuf_t *msg, struct clientdata_s *from, struct clientdata_s *to, float timebase );
|
void MSG_WriteClientData( sizebuf_t *msg, struct clientdata_s *from, struct clientdata_s *to, float timebase );
|
||||||
void MSG_ReadClientData( sizebuf_t *msg, struct clientdata_s *from, struct clientdata_s *to, float timebase );
|
void MSG_ReadClientData( sizebuf_t *msg, struct clientdata_s *from, struct clientdata_s *to, float timebase );
|
||||||
|
void MSG_WriteWeaponData( sizebuf_t *msg, struct weapon_data_s *from, struct weapon_data_s *to, float timebase, int index );
|
||||||
|
void MSG_ReadWeaponData( sizebuf_t *msg, struct weapon_data_s *from, struct weapon_data_s *to, float timebase );
|
||||||
void MSG_WriteDeltaEntity( struct entity_state_s *from, struct entity_state_s *to, sizebuf_t *msg, bool force, float timebase );
|
void MSG_WriteDeltaEntity( struct entity_state_s *from, struct entity_state_s *to, sizebuf_t *msg, bool force, float timebase );
|
||||||
bool MSG_ReadDeltaEntity( sizebuf_t *msg, struct entity_state_s *from, struct entity_state_s *to, int num, float timebase );
|
bool MSG_ReadDeltaEntity( sizebuf_t *msg, struct entity_state_s *from, struct entity_state_s *to, int num, float timebase );
|
||||||
|
|
||||||
|
|
|
@ -119,7 +119,6 @@ typedef struct netchan_s
|
||||||
netadr_t remote_address; // address this channel is talking to.
|
netadr_t remote_address; // address this channel is talking to.
|
||||||
int qport; // qport value to write when transmitting
|
int qport; // qport value to write when transmitting
|
||||||
|
|
||||||
int dropped; // between last packet and previous
|
|
||||||
bool compress; // enable huffman compression
|
bool compress; // enable huffman compression
|
||||||
|
|
||||||
double last_received; // for timeouts
|
double last_received; // for timeouts
|
||||||
|
@ -183,6 +182,7 @@ extern netadr_t net_from;
|
||||||
extern sizebuf_t net_message;
|
extern sizebuf_t net_message;
|
||||||
extern byte net_message_buffer[MAX_MSGLEN];
|
extern byte net_message_buffer[MAX_MSGLEN];
|
||||||
extern cvar_t *net_speeds;
|
extern cvar_t *net_speeds;
|
||||||
|
extern int net_drop;
|
||||||
|
|
||||||
void Netchan_Init( void );
|
void Netchan_Init( void );
|
||||||
void Netchan_Shutdown( void );
|
void Netchan_Shutdown( void );
|
||||||
|
@ -197,6 +197,7 @@ void Netchan_OutOfBandPrint( int net_socket, netadr_t adr, char *format, ... );
|
||||||
bool Netchan_Process( netchan_t *chan, sizebuf_t *msg );
|
bool Netchan_Process( netchan_t *chan, sizebuf_t *msg );
|
||||||
void Netchan_UpdateProgress( netchan_t *chan );
|
void Netchan_UpdateProgress( netchan_t *chan );
|
||||||
bool Netchan_IncomingReady( netchan_t *chan );
|
bool Netchan_IncomingReady( netchan_t *chan );
|
||||||
|
bool Netchan_CanPacket( netchan_t *chan );
|
||||||
void Netchan_Clear( netchan_t *chan );
|
void Netchan_Clear( netchan_t *chan );
|
||||||
|
|
||||||
// huffman compression
|
// huffman compression
|
||||||
|
|
|
@ -8,61 +8,75 @@
|
||||||
#define PROTOCOL_VERSION 39
|
#define PROTOCOL_VERSION 39
|
||||||
|
|
||||||
// server to client
|
// server to client
|
||||||
#define svc_bad 0 // immediately crash client when received
|
#define svc_bad 0 // immediately crash client when received
|
||||||
#define svc_nop 1 // does nothing
|
#define svc_nop 1 // does nothing
|
||||||
#define svc_disconnect 2 // kick client from server
|
#define svc_disconnect 2 // kick client from server
|
||||||
#define svc_changing 3 // changelevel by server request
|
#define svc_changing 3 // changelevel by server request
|
||||||
#define svc_configstring 4 // [short] [string]
|
#define svc_configstring 4 // [short] [string]
|
||||||
#define svc_setview 5 // [short] entity number
|
#define svc_setview 5 // [short] entity number
|
||||||
#define svc_sound 6 // <see code>
|
#define svc_sound 6 // <see code>
|
||||||
#define svc_time 7 // [float] server time
|
#define svc_time 7 // [float] server time
|
||||||
#define svc_print 8 // [byte] id [string] null terminated string
|
#define svc_print 8 // [byte] id [string] null terminated string
|
||||||
#define svc_stufftext 9 // [string] stuffed into client's console buffer, should be \n terminated
|
#define svc_stufftext 9 // [string] stuffed into client's console buffer
|
||||||
#define svc_setangle 10 // [angle angle] set the view angle to this absolute value
|
#define svc_setangle 10 // [angle angle] set the view angle to this absolute value
|
||||||
#define svc_serverdata 11 // [long] protocol ...
|
#define svc_serverdata 11 // [long] protocol ...
|
||||||
#define svc_addangle 12 // [angle] add angles when client turn on mover
|
#define svc_restore 12 // restore saved game on the client
|
||||||
#define svc_frame 13 // begin a new server frame
|
#define svc_frame 13 // begin a new server frame
|
||||||
#define svc_clientdata 14 // [...]
|
#define svc_usermessage 14 // [string][byte] REG_USER_MSG stuff
|
||||||
#define svc_packetentities 15 // [...]
|
#define svc_clientdata 15 // [...]
|
||||||
#define svc_download 16 // [short] size [size bytes]
|
#define svc_download 16 // [short] size [size bytes]
|
||||||
#define svc_usermessage 17 // [string][byte] REG_USER_MSG stuff
|
#define svc_updatepings 17 // [bit][idx][ping][packet_loss]
|
||||||
#define svc_particle 18 // [float*3][char*3][byte][byte]
|
#define svc_particle 18 // [float*3][char*3][byte][byte]
|
||||||
#define svc_ambientsound 19 // <see code>
|
#define svc_ambientsound 19 // <see code>
|
||||||
#define svc_spawnstatic 20 // NOT IMPLEMENTED
|
#define svc_spawnstatic 20 // NOT IMPLEMENTED
|
||||||
#define svc_crosshairangle 21 // [short][short][short]
|
#define svc_crosshairangle 21 // [short][short][short]
|
||||||
#define svc_spawnbaseline 22 // <see code>
|
#define svc_spawnbaseline 22 // <see code>
|
||||||
#define svc_temp_entity 23 // <variable sized>
|
#define svc_temp_entity 23 // <variable sized>
|
||||||
#define svc_setpause 24 // [byte] 0 = unpaused, 1 = paused
|
#define svc_setpause 24 // [byte] 0 = unpaused, 1 = paused
|
||||||
#define svc_deltamovevars 25 // [movevars_t]
|
#define svc_deltamovevars 25 // [movevars_t]
|
||||||
#define svc_centerprint 26 // [string] to put in center of the screen
|
#define svc_centerprint 26 // [string] to put in center of the screen
|
||||||
#define svc_event 27 // playback event queue
|
#define svc_event 27 // playback event queue
|
||||||
#define svc_event_reliable 28 // playback event directly from message, not queue
|
#define svc_event_reliable 28 // playback event directly from message, not queue
|
||||||
#define svc_updateuserinfo 29 // [byte] playernum, [string] userinfo
|
#define svc_updateuserinfo 29 // [byte] playernum, [string] userinfo
|
||||||
#define svc_intermission 30 // empty message (event)
|
#define svc_intermission 30 // empty message (event)
|
||||||
#define svc_soundfade 31 // [float*4] sound fade parms
|
#define svc_soundfade 31 // [float*4] sound fade parms
|
||||||
#define svc_cdtrack 32 // [byte] track [byte] looptrack
|
#define svc_cdtrack 32 // [byte] track [byte] looptrack
|
||||||
#define svc_serverinfo 33 // [string] key [string] value
|
#define svc_serverinfo 33 // [string] key [string] value
|
||||||
#define svc_deltatable 34 // [table header][...]
|
#define svc_deltatable 34 // [table header][...]
|
||||||
#define svc_weaponanim 35 // [byte]iAnim [byte]body
|
#define svc_weaponanim 35 // [byte]iAnim [byte]body
|
||||||
#define svc_bspdecal 36 // [float*3][short][short][short]
|
#define svc_bspdecal 36 // [float*3][short][short][short]
|
||||||
#define svc_roomtype 37 // [short] room type
|
#define svc_roomtype 37 // [short] room type
|
||||||
#define svc_restore 38 // restore saved game on the client
|
#define svc_addangle 38 // [angle] add angles when client turn on mover
|
||||||
|
|
||||||
#define svc_director 51 // <variable sized>
|
#define svc_packetentities 40 // [short][...]
|
||||||
#define svc_lastmsg 64 // start user messages at this point
|
#define svc_deltapacketentities 41 // [short][byte][...]
|
||||||
|
#define svc_chokecount 42 // [byte]
|
||||||
|
|
||||||
|
#define svc_director 51 // <variable sized>
|
||||||
|
#define svc_lastmsg 64 // start user messages at this point
|
||||||
|
|
||||||
// client to server
|
// client to server
|
||||||
#define clc_bad 0 // immediately drop client when received
|
#define clc_bad 0 // immediately drop client when received
|
||||||
#define clc_nop 1
|
#define clc_nop 1
|
||||||
#define clc_move 2 // [[usercmd_t]
|
#define clc_move 2 // [[usercmd_t]
|
||||||
#define clc_delta 3 // [byte] sequence number, requests delta compression of message
|
#define clc_stringcmd 3 // [string] message
|
||||||
#define clc_userinfo 4 // [[userinfo string]
|
#define clc_delta 4 // [byte] sequence number, requests delta compression of message
|
||||||
#define clc_stringcmd 5 // [string] message
|
#define clc_resourcelist 5
|
||||||
#define clc_random_seed 6 // [long] random seed
|
#define clc_userinfo 6 // [[userinfo string] // FIXME: remove
|
||||||
|
#define clc_fileconsistency 7
|
||||||
|
#define clc_voicedata 8
|
||||||
|
#define clc_random_seed 9 // [long] random seed // FIXME: remove
|
||||||
|
|
||||||
// additional protocol data
|
// additional protocol data
|
||||||
#define MAX_CLIENT_BITS 5
|
#define MAX_CLIENT_BITS 5
|
||||||
#define MAX_CLIENTS (1<<MAX_CLIENT_BITS)
|
#define MAX_CLIENTS (1<<MAX_CLIENT_BITS)
|
||||||
|
|
||||||
|
#define MAX_WEAPON_BITS 5
|
||||||
|
#define MAX_WEAPONS (1<<MAX_WEAPON_BITS)
|
||||||
|
|
||||||
|
// Max number of history commands to send ( 2 by default ) in case of dropped packets
|
||||||
|
#define NUM_BACKUP_COMMAND_BITS 3
|
||||||
|
#define MAX_BACKUP_COMMANDS (1 << NUM_BACKUP_COMMAND_BITS)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
==============================================================
|
==============================================================
|
||||||
|
@ -74,8 +88,9 @@ DELTA-PACKET ENTITIES
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
int num_entities;
|
|
||||||
entity_state_t *entities;
|
entity_state_t *entities;
|
||||||
|
int max_entities; // this is a real allocated space
|
||||||
|
int num_entities;
|
||||||
} packet_entities_t;
|
} packet_entities_t;
|
||||||
|
|
||||||
#endif//PROTOCOL_H
|
#endif//PROTOCOL_H
|
|
@ -19,6 +19,7 @@ dll_info_t render_dll = { "", NULL, "CreateAPI", NULL, NULL, 0, sizeof(render_ex
|
||||||
dll_info_t vsound_dll = { "", NULL, "CreateAPI", NULL, NULL, 0, sizeof(vsound_exp_t), sizeof(stdlib_api_t) };
|
dll_info_t vsound_dll = { "", NULL, "CreateAPI", NULL, NULL, 0, sizeof(vsound_exp_t), sizeof(stdlib_api_t) };
|
||||||
|
|
||||||
cvar_t *host_serverstate;
|
cvar_t *host_serverstate;
|
||||||
|
cvar_t *host_limitlocal;
|
||||||
cvar_t *host_cheats;
|
cvar_t *host_cheats;
|
||||||
cvar_t *host_maxfps;
|
cvar_t *host_maxfps;
|
||||||
cvar_t *host_framerate;
|
cvar_t *host_framerate;
|
||||||
|
@ -742,6 +743,7 @@ void Host_Init( const int argc, const char **argv )
|
||||||
host_maxfps = Cvar_Get( "fps_max", "72", CVAR_ARCHIVE, "host fps upper limit" );
|
host_maxfps = Cvar_Get( "fps_max", "72", CVAR_ARCHIVE, "host fps upper limit" );
|
||||||
host_framerate = Cvar_Get( "host_framerate", "0", 0, "locks frame timing to this value in seconds" );
|
host_framerate = Cvar_Get( "host_framerate", "0", 0, "locks frame timing to this value in seconds" );
|
||||||
host_serverstate = Cvar_Get( "host_serverstate", "0", 0, "displays current server state" );
|
host_serverstate = Cvar_Get( "host_serverstate", "0", 0, "displays current server state" );
|
||||||
|
host_limitlocal = Cvar_Get( "host_limitlocal", "0", 0, "apply cl_cmdrate and rate to loopback connection" );
|
||||||
|
|
||||||
// content control
|
// content control
|
||||||
Cvar_Get( "violence_hgibs", "1", CVAR_INIT|CVAR_ARCHIVE, "content control disables human gibs" );
|
Cvar_Get( "violence_hgibs", "1", CVAR_INIT|CVAR_ARCHIVE, "content control disables human gibs" );
|
||||||
|
|
|
@ -71,7 +71,6 @@ typedef struct server_s
|
||||||
|
|
||||||
double time; // sv.time += sv.frametime
|
double time; // sv.time += sv.frametime
|
||||||
float frametime;
|
float frametime;
|
||||||
int framenum;
|
|
||||||
int net_framenum; // to avoid send edicts twice through portals
|
int net_framenum; // to avoid send edicts twice through portals
|
||||||
|
|
||||||
int hostflags; // misc server flags: predicting etc
|
int hostflags; // misc server flags: predicting etc
|
||||||
|
@ -105,13 +104,12 @@ typedef struct server_s
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
double senttime;
|
double senttime;
|
||||||
float ping_time;
|
float raw_ping;
|
||||||
|
float latency;
|
||||||
|
|
||||||
clientdata_t clientdata;
|
clientdata_t clientdata;
|
||||||
weapon_data_t weapondata[32];
|
weapon_data_t weapondata[32];
|
||||||
packet_entities_t entities;
|
packet_entities_t entities;
|
||||||
|
|
||||||
// legacy (needs to be removed)
|
|
||||||
float latency;
|
|
||||||
} client_frame_t;
|
} client_frame_t;
|
||||||
|
|
||||||
typedef struct sv_client_s
|
typedef struct sv_client_s
|
||||||
|
@ -120,15 +118,22 @@ typedef struct sv_client_s
|
||||||
|
|
||||||
char userinfo[MAX_INFO_STRING]; // name, etc (received from client)
|
char userinfo[MAX_INFO_STRING]; // name, etc (received from client)
|
||||||
char physinfo[MAX_INFO_STRING]; // set on server (transmit to client)
|
char physinfo[MAX_INFO_STRING]; // set on server (transmit to client)
|
||||||
|
|
||||||
bool send_message;
|
bool send_message;
|
||||||
bool skip_message;
|
bool skip_message;
|
||||||
|
|
||||||
|
bool local_weapons; // enable weapon predicting
|
||||||
|
bool lag_compensation; // enable lag compensation
|
||||||
|
bool hltv_proxy; // this is spectator proxy (hltv)
|
||||||
|
|
||||||
netchan_t netchan;
|
netchan_t netchan;
|
||||||
int chokecount; // number of messages rate supressed
|
int chokecount; // number of messages rate supressed
|
||||||
int delta_sequence; // -1 = no compression.
|
int delta_sequence; // -1 = no compression.
|
||||||
|
|
||||||
double next_messagetime; // time when we should send next world state update
|
double next_messagetime; // time when we should send next world state update
|
||||||
double next_messageinterval; // default time to wait for next message
|
double cl_updaterate; // default time to wait for next message
|
||||||
|
double next_checkpingtime; // time to send all players pings to client
|
||||||
|
double timebase; // client timebase
|
||||||
|
|
||||||
bool sendmovevars;
|
bool sendmovevars;
|
||||||
bool sendinfo;
|
bool sendinfo;
|
||||||
|
@ -136,17 +141,16 @@ typedef struct sv_client_s
|
||||||
bool fakeclient; // This client is a fake player controlled by the game DLL
|
bool fakeclient; // This client is a fake player controlled by the game DLL
|
||||||
|
|
||||||
int random_seed; // fpr predictable random values
|
int random_seed; // fpr predictable random values
|
||||||
int lastframe; // for delta compression
|
|
||||||
usercmd_t lastcmd; // for filling in big drops
|
usercmd_t lastcmd; // for filling in big drops
|
||||||
|
|
||||||
|
double last_cmdtime;
|
||||||
|
double last_movetime;
|
||||||
|
double next_movetime;
|
||||||
|
|
||||||
int modelindex; // custom playermodel index
|
int modelindex; // custom playermodel index
|
||||||
int packet_loss;
|
int packet_loss;
|
||||||
int ping;
|
float latency;
|
||||||
|
float ping;
|
||||||
int message_size[RATE_MESSAGES]; // used to rate drop packets
|
|
||||||
int rate;
|
|
||||||
|
|
||||||
int surpressCount; // number of messages rate supressed
|
|
||||||
|
|
||||||
float addangle; // add angles to client position
|
float addangle; // add angles to client position
|
||||||
|
|
||||||
|
@ -211,6 +215,22 @@ typedef struct
|
||||||
vec3_t angles;
|
vec3_t angles;
|
||||||
} sv_pushed_t;
|
} sv_pushed_t;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
bool active;
|
||||||
|
bool moving;
|
||||||
|
bool firstframe;
|
||||||
|
bool nointerp;
|
||||||
|
|
||||||
|
vec3_t mins;
|
||||||
|
vec3_t maxs;
|
||||||
|
|
||||||
|
vec3_t curpos;
|
||||||
|
vec3_t oldpos;
|
||||||
|
vec3_t newpos;
|
||||||
|
vec3_t finalpos;
|
||||||
|
} sv_interp_t;
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
// user messages stuff
|
// user messages stuff
|
||||||
|
@ -240,6 +260,7 @@ typedef struct
|
||||||
movevars_t movevars; // curstate
|
movevars_t movevars; // curstate
|
||||||
movevars_t oldmovevars; // oldstate
|
movevars_t oldmovevars; // oldstate
|
||||||
playermove_t *pmove; // pmove state
|
playermove_t *pmove; // pmove state
|
||||||
|
sv_interp_t interp[32]; // interpolate clients
|
||||||
|
|
||||||
sv_pushed_t pushed[256]; // no reason to keep array for all edicts
|
sv_pushed_t pushed[256]; // no reason to keep array for all edicts
|
||||||
// 256 it should be enough for any game situation
|
// 256 it should be enough for any game situation
|
||||||
|
@ -306,6 +327,11 @@ extern cvar_t *sv_maxspeed;
|
||||||
extern cvar_t *sv_maxclients;
|
extern cvar_t *sv_maxclients;
|
||||||
extern cvar_t *sv_skyname;
|
extern cvar_t *sv_skyname;
|
||||||
extern cvar_t *serverinfo;
|
extern cvar_t *serverinfo;
|
||||||
|
extern cvar_t *sv_failuretime;
|
||||||
|
extern cvar_t *sv_unlag;
|
||||||
|
extern cvar_t *sv_maxunlag;
|
||||||
|
extern cvar_t *sv_unlagpush;
|
||||||
|
extern cvar_t *sv_unlagsamples;
|
||||||
extern cvar_t *physinfo;
|
extern cvar_t *physinfo;
|
||||||
extern sv_client_t *sv_client;
|
extern sv_client_t *sv_client;
|
||||||
|
|
||||||
|
@ -376,15 +402,17 @@ void SV_GetChallenge( netadr_t from );
|
||||||
void SV_DirectConnect( netadr_t from );
|
void SV_DirectConnect( netadr_t from );
|
||||||
void SV_TogglePause( const char *msg );
|
void SV_TogglePause( const char *msg );
|
||||||
void SV_PutClientInServer( edict_t *ent );
|
void SV_PutClientInServer( edict_t *ent );
|
||||||
|
bool SV_ShouldUpdatePing( sv_client_t *cl );
|
||||||
void SV_FullClientUpdate( sv_client_t *cl, sizebuf_t *msg );
|
void SV_FullClientUpdate( sv_client_t *cl, sizebuf_t *msg );
|
||||||
void SV_FullUpdateMovevars( sv_client_t *cl, sizebuf_t *msg );
|
void SV_FullUpdateMovevars( sv_client_t *cl, sizebuf_t *msg );
|
||||||
|
void SV_GetPlayerStats( sv_client_t *cl, int *ping, int *packet_loss );
|
||||||
bool SV_ClientConnect( edict_t *ent, char *userinfo );
|
bool SV_ClientConnect( edict_t *ent, char *userinfo );
|
||||||
void SV_ClientThink( sv_client_t *cl, usercmd_t *cmd );
|
void SV_ClientThink( sv_client_t *cl, usercmd_t *cmd );
|
||||||
void SV_ExecuteClientMessage( sv_client_t *cl, sizebuf_t *msg );
|
void SV_ExecuteClientMessage( sv_client_t *cl, sizebuf_t *msg );
|
||||||
void SV_ConnectionlessPacket( netadr_t from, sizebuf_t *msg );
|
void SV_ConnectionlessPacket( netadr_t from, sizebuf_t *msg );
|
||||||
edict_t *SV_FakeConnect( const char *netname );
|
edict_t *SV_FakeConnect( const char *netname );
|
||||||
void SV_PreRunCmd( sv_client_t *cl, usercmd_t *ucmd );
|
void SV_PreRunCmd( sv_client_t *cl, usercmd_t *ucmd, int random_seed );
|
||||||
void SV_RunCmd( sv_client_t *cl, usercmd_t *ucmd );
|
void SV_RunCmd( sv_client_t *cl, usercmd_t *ucmd, int random_seed );
|
||||||
void SV_PostRunCmd( sv_client_t *cl );
|
void SV_PostRunCmd( sv_client_t *cl );
|
||||||
void SV_InitClientMove( void );
|
void SV_InitClientMove( void );
|
||||||
void SV_UpdateServerInfo( void );
|
void SV_UpdateServerInfo( void );
|
||||||
|
|
|
@ -9,6 +9,20 @@
|
||||||
#include "net_encode.h"
|
#include "net_encode.h"
|
||||||
#include "entity_types.h"
|
#include "entity_types.h"
|
||||||
|
|
||||||
|
const char *clc_strings[10] =
|
||||||
|
{
|
||||||
|
"clc_bad",
|
||||||
|
"clc_nop",
|
||||||
|
"clc_move",
|
||||||
|
"clc_stringcmd",
|
||||||
|
"clc_delta",
|
||||||
|
"clc_resourcelist",
|
||||||
|
"clc_userinfo",
|
||||||
|
"clc_fileconsistency",
|
||||||
|
"clc_voicedata",
|
||||||
|
"clc_random_seed",
|
||||||
|
};
|
||||||
|
|
||||||
typedef struct ucmd_s
|
typedef struct ucmd_s
|
||||||
{
|
{
|
||||||
const char *name;
|
const char *name;
|
||||||
|
@ -179,8 +193,6 @@ gotnewcl:
|
||||||
sv_client = newcl;
|
sv_client = newcl;
|
||||||
edictnum = (newcl - svs.clients) + 1;
|
edictnum = (newcl - svs.clients) + 1;
|
||||||
|
|
||||||
SV_ClearFrames( &newcl->frames );
|
|
||||||
|
|
||||||
ent = EDICT_NUM( edictnum );
|
ent = EDICT_NUM( edictnum );
|
||||||
newcl->edict = ent;
|
newcl->edict = ent;
|
||||||
newcl->challenge = challenge; // save challenge for checksumming
|
newcl->challenge = challenge; // save challenge for checksumming
|
||||||
|
@ -207,8 +219,11 @@ gotnewcl:
|
||||||
BF_Init( &newcl->datagram, "Datagram", newcl->datagram_buf, sizeof( newcl->datagram_buf )); // datagram buf
|
BF_Init( &newcl->datagram, "Datagram", newcl->datagram_buf, sizeof( newcl->datagram_buf )); // datagram buf
|
||||||
|
|
||||||
newcl->state = cs_connected;
|
newcl->state = cs_connected;
|
||||||
|
newcl->cl_updaterate = 0.05;
|
||||||
newcl->lastmessage = host.realtime;
|
newcl->lastmessage = host.realtime;
|
||||||
newcl->lastconnect = host.realtime;
|
newcl->lastconnect = host.realtime;
|
||||||
|
newcl->next_messagetime = host.realtime + newcl->cl_updaterate;
|
||||||
|
newcl->delta_sequence = -1;
|
||||||
|
|
||||||
// if this was the first client on the server, or the last client
|
// if this was the first client on the server, or the last client
|
||||||
// the server can hold, send a heartbeat to the master.
|
// the server can hold, send a heartbeat to the master.
|
||||||
|
@ -271,6 +286,7 @@ edict_t *SV_FakeConnect( const char *netname )
|
||||||
newcl->edict = ent;
|
newcl->edict = ent;
|
||||||
newcl->challenge = -1; // fake challenge
|
newcl->challenge = -1; // fake challenge
|
||||||
newcl->fakeclient = true;
|
newcl->fakeclient = true;
|
||||||
|
newcl->delta_sequence = -1;
|
||||||
ent->v.flags |= FL_FAKECLIENT; // mark it as fakeclient
|
ent->v.flags |= FL_FAKECLIENT; // mark it as fakeclient
|
||||||
|
|
||||||
// get the game a chance to reject this connection or modify the userinfo
|
// get the game a chance to reject this connection or modify the userinfo
|
||||||
|
@ -597,6 +613,81 @@ void SV_RemoteCommand( netadr_t from, sizebuf_t *msg )
|
||||||
SV_EndRedirect();
|
SV_EndRedirect();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
===================
|
||||||
|
SV_CalcPing
|
||||||
|
|
||||||
|
recalc ping on current client
|
||||||
|
===================
|
||||||
|
*/
|
||||||
|
int SV_CalcPing( sv_client_t *cl )
|
||||||
|
{
|
||||||
|
float ping = 0;
|
||||||
|
int i, count;
|
||||||
|
client_frame_t *frame;
|
||||||
|
|
||||||
|
// bots don't have a real ping
|
||||||
|
if( cl->fakeclient )
|
||||||
|
return 5;
|
||||||
|
|
||||||
|
count = 0;
|
||||||
|
|
||||||
|
for( i = 0; i < SV_UPDATE_BACKUP; i++ )
|
||||||
|
{
|
||||||
|
frame = &cl->frames[(cl->netchan.incoming_acknowledged - (i + 1)) & SV_UPDATE_MASK];
|
||||||
|
|
||||||
|
if( frame->raw_ping > 0 )
|
||||||
|
{
|
||||||
|
ping += frame->raw_ping;
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if( !count )
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return (( ping / count ) * 1000 );
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
===================
|
||||||
|
SV_EstablishTimeBase
|
||||||
|
|
||||||
|
Finangles latency and the like.
|
||||||
|
===================
|
||||||
|
*/
|
||||||
|
void SV_EstablishTimeBase( sv_client_t *cl, usercmd_t *ucmd, int numdrops, int numbackup, int newcmds )
|
||||||
|
{
|
||||||
|
double start;
|
||||||
|
double end;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
start = 0;
|
||||||
|
end = sv.time + sv.frametime;
|
||||||
|
|
||||||
|
if( numdrops < 24 )
|
||||||
|
{
|
||||||
|
while( numdrops > numbackup )
|
||||||
|
{
|
||||||
|
start += cl->lastcmd.msec / 1000.0;
|
||||||
|
numdrops--;
|
||||||
|
}
|
||||||
|
|
||||||
|
while( numdrops > 0 )
|
||||||
|
{
|
||||||
|
start += ucmd[numdrops + newcmds - 1].msec / 1000.0;
|
||||||
|
numdrops--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for( i = newcmds - 1; i >= 0; i-- )
|
||||||
|
{
|
||||||
|
start += ucmd[i].msec / 1000.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
cl->timebase = end - start;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
===================
|
===================
|
||||||
SV_FullClientUpdate
|
SV_FullClientUpdate
|
||||||
|
@ -652,6 +743,58 @@ void SV_FullUpdateMovevars( sv_client_t *cl, sizebuf_t *msg )
|
||||||
MSG_WriteDeltaMovevars( msg, &nullmovevars, &svgame.movevars );
|
MSG_WriteDeltaMovevars( msg, &nullmovevars, &svgame.movevars );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
===================
|
||||||
|
SV_ShouldUpdatePing
|
||||||
|
|
||||||
|
this calls SV_CalcPing, and returns true
|
||||||
|
if this person needs ping data.
|
||||||
|
===================
|
||||||
|
*/
|
||||||
|
bool SV_ShouldUpdatePing( sv_client_t *cl )
|
||||||
|
{
|
||||||
|
if( !cl->hltv_proxy )
|
||||||
|
{
|
||||||
|
SV_CalcPing( cl );
|
||||||
|
return cl->lastcmd.buttons & IN_SCORE; // they are viewing the scoreboard. Send them pings.
|
||||||
|
}
|
||||||
|
|
||||||
|
if( host.realtime > cl->next_checkpingtime )
|
||||||
|
{
|
||||||
|
cl->next_checkpingtime = host.realtime + 2.0;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
===================
|
||||||
|
SV_GetPlayerStats
|
||||||
|
|
||||||
|
This function and its static vars track some of the networking
|
||||||
|
conditions. I haven't bothered to trace it beyond that, because
|
||||||
|
this fucntion sucks pretty badly.
|
||||||
|
===================
|
||||||
|
*/
|
||||||
|
void SV_GetPlayerStats( sv_client_t *cl, int *ping, int *packet_loss )
|
||||||
|
{
|
||||||
|
static int last_ping[MAX_CLIENTS];
|
||||||
|
static int last_loss[MAX_CLIENTS];
|
||||||
|
int i;
|
||||||
|
|
||||||
|
i = cl - svs.clients;
|
||||||
|
|
||||||
|
if( cl->next_checkpingtime < host.realtime )
|
||||||
|
{
|
||||||
|
cl->next_checkpingtime = host.realtime + 2.0;
|
||||||
|
last_ping[i] = SV_CalcPing( cl );
|
||||||
|
last_loss[i] = cl->packet_loss;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( ping ) *ping = last_ping[i];
|
||||||
|
if( packet_loss ) *packet_loss = last_loss[i];
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
===========
|
===========
|
||||||
PutClientInServer
|
PutClientInServer
|
||||||
|
@ -698,6 +841,11 @@ void SV_PutClientInServer( edict_t *ent )
|
||||||
|
|
||||||
client->pViewEntity = NULL; // reset pViewEntity
|
client->pViewEntity = NULL; // reset pViewEntity
|
||||||
|
|
||||||
|
// reset client times
|
||||||
|
client->last_cmdtime = 0.0;
|
||||||
|
client->last_movetime = 0.0;
|
||||||
|
client->next_movetime = 0.0;
|
||||||
|
|
||||||
if( !client->fakeclient )
|
if( !client->fakeclient )
|
||||||
{
|
{
|
||||||
// copy signon buffer
|
// copy signon buffer
|
||||||
|
@ -1153,14 +1301,17 @@ void SV_UserinfoChanged( sv_client_t *cl, const char *userinfo )
|
||||||
// rate command
|
// rate command
|
||||||
val = Info_ValueForKey( cl->userinfo, "rate" );
|
val = Info_ValueForKey( cl->userinfo, "rate" );
|
||||||
if( com.strlen( val ))
|
if( com.strlen( val ))
|
||||||
cl->rate = bound ( 100, com.atoi( val ), 15000 );
|
cl->netchan.rate = bound( MIN_RATE, com.atoi( val ), MAX_RATE );
|
||||||
else cl->rate = 5000;
|
else cl->netchan.rate = DEFAULT_RATE;
|
||||||
|
|
||||||
// msg command
|
// msg command
|
||||||
val = Info_ValueForKey( cl->userinfo, "msg" );
|
val = Info_ValueForKey( cl->userinfo, "msg" );
|
||||||
if( com.strlen( val ))
|
if( com.strlen( val ))
|
||||||
cl->messagelevel = com.atoi( val );
|
cl->messagelevel = com.atoi( val );
|
||||||
|
|
||||||
|
cl->local_weapons = com.atoi( Info_ValueForKey( cl->userinfo, "cl_lw" )) ? true : false;
|
||||||
|
cl->lag_compensation = com.atoi( Info_ValueForKey( cl->userinfo, "cl_lc" )) ? true : false;
|
||||||
|
|
||||||
if( SV_IsValidEdict( ent ))
|
if( SV_IsValidEdict( ent ))
|
||||||
{
|
{
|
||||||
if( sv_maxclients->integer > 1 )
|
if( sv_maxclients->integer > 1 )
|
||||||
|
@ -1300,23 +1451,12 @@ each of the backup packets.
|
||||||
*/
|
*/
|
||||||
static void SV_ReadClientMove( sv_client_t *cl, sizebuf_t *msg )
|
static void SV_ReadClientMove( sv_client_t *cl, sizebuf_t *msg )
|
||||||
{
|
{
|
||||||
|
int key, size;
|
||||||
int checksum1, checksum2;
|
int checksum1, checksum2;
|
||||||
int key, lastframe, net_drop, size;
|
|
||||||
usercmd_t oldest, oldcmd, newcmd, nulcmd;
|
usercmd_t oldest, oldcmd, newcmd, nulcmd;
|
||||||
|
|
||||||
key = BF_GetNumBytesRead( msg );
|
key = BF_GetNumBytesRead( msg );
|
||||||
checksum1 = BF_ReadByte( msg );
|
checksum1 = BF_ReadByte( msg );
|
||||||
lastframe = BF_ReadLong( msg );
|
|
||||||
|
|
||||||
if( lastframe != cl->lastframe )
|
|
||||||
{
|
|
||||||
cl->lastframe = lastframe;
|
|
||||||
if( cl->lastframe > 0 )
|
|
||||||
{
|
|
||||||
client_frame_t *frame = &cl->frames[cl->lastframe & SV_UPDATE_MASK];
|
|
||||||
frame->latency = host.realtime - frame->senttime;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cl->packet_loss = SV_CalcPacketLoss( cl );
|
cl->packet_loss = SV_CalcPacketLoss( cl );
|
||||||
|
|
||||||
|
@ -1327,7 +1467,7 @@ static void SV_ReadClientMove( sv_client_t *cl, sizebuf_t *msg )
|
||||||
|
|
||||||
if( cl->state != cs_spawned )
|
if( cl->state != cs_spawned )
|
||||||
{
|
{
|
||||||
cl->lastframe = -1;
|
cl->delta_sequence = -1;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1342,24 +1482,21 @@ static void SV_ReadClientMove( sv_client_t *cl, sizebuf_t *msg )
|
||||||
|
|
||||||
if( !sv.paused )
|
if( !sv.paused )
|
||||||
{
|
{
|
||||||
SV_PreRunCmd( cl, &newcmd ); // get random_seed from newcmd
|
SV_PreRunCmd( cl, &newcmd, cl->random_seed ); // get random_seed from newcmd
|
||||||
|
|
||||||
net_drop = cl->netchan.dropped;
|
|
||||||
cl->netchan.dropped = 0; // reset counter
|
|
||||||
|
|
||||||
if( net_drop < 20 )
|
if( net_drop < 20 )
|
||||||
{
|
{
|
||||||
while( net_drop > 2 )
|
while( net_drop > 2 )
|
||||||
{
|
{
|
||||||
SV_RunCmd( cl, &cl->lastcmd );
|
SV_RunCmd( cl, &cl->lastcmd, cl->random_seed );
|
||||||
net_drop--;
|
net_drop--;
|
||||||
}
|
}
|
||||||
|
|
||||||
if( net_drop > 1 ) SV_RunCmd( cl, &oldest );
|
if( net_drop > 1 ) SV_RunCmd( cl, &oldest, cl->random_seed );
|
||||||
if( net_drop > 0 ) SV_RunCmd( cl, &oldcmd );
|
if( net_drop > 0 ) SV_RunCmd( cl, &oldcmd, cl->random_seed );
|
||||||
|
|
||||||
}
|
}
|
||||||
SV_RunCmd( cl, &newcmd );
|
SV_RunCmd( cl, &newcmd, cl->random_seed );
|
||||||
SV_PostRunCmd( cl );
|
SV_PostRunCmd( cl );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1367,6 +1504,131 @@ static void SV_ReadClientMove( sv_client_t *cl, sizebuf_t *msg )
|
||||||
cl->lastcmd.buttons = 0; // avoid multiple fires on lag
|
cl->lastcmd.buttons = 0; // avoid multiple fires on lag
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void SV_ParseClientMove( sv_client_t *cl, sizebuf_t *msg )
|
||||||
|
{
|
||||||
|
client_frame_t *frame;
|
||||||
|
int key, size, checksum1, checksum2;
|
||||||
|
int i, numbackup, newcmds, numcmds;
|
||||||
|
usercmd_t nullcmd, *from;
|
||||||
|
usercmd_t cmds[32], *to;
|
||||||
|
edict_t *player;
|
||||||
|
|
||||||
|
numbackup = 2;
|
||||||
|
player = cl->edict;
|
||||||
|
|
||||||
|
frame = &cl->frames[cl->netchan.incoming_acknowledged & SV_UPDATE_MASK];
|
||||||
|
Mem_Set( &nullcmd, 0, sizeof( usercmd_t ));
|
||||||
|
|
||||||
|
key = BF_GetRealBytesRead( msg );
|
||||||
|
checksum1 = BF_ReadByte( msg );
|
||||||
|
cl->packet_loss = BF_ReadByte( msg );
|
||||||
|
|
||||||
|
numbackup = BF_ReadByte( msg );
|
||||||
|
newcmds = BF_ReadByte( msg );
|
||||||
|
|
||||||
|
numcmds = numbackup + newcmds;
|
||||||
|
net_drop = net_drop + 1 - newcmds;
|
||||||
|
|
||||||
|
if( numcmds < 0 || numcmds > 28 )
|
||||||
|
{
|
||||||
|
MsgDev( D_ERROR, "%s sending too many commands %i\n", cl->name, numcmds );
|
||||||
|
SV_DropClient( cl );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
from = &nullcmd; // first cmd are starting from null-comressed usercmd_t
|
||||||
|
|
||||||
|
for( i = numcmds - 1; i >= 0; i-- )
|
||||||
|
{
|
||||||
|
to = &cmds[i];
|
||||||
|
MSG_ReadDeltaUsercmd( msg, from, to );
|
||||||
|
from = to; // get new baseline
|
||||||
|
}
|
||||||
|
|
||||||
|
if( cl->state != cs_spawned )
|
||||||
|
{
|
||||||
|
cl->delta_sequence = -1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if the checksum fails, ignore the rest of the packet
|
||||||
|
size = BF_GetRealBytesRead( msg ) - key - 1;
|
||||||
|
checksum2 = CRC_Sequence( msg->pData + key + 1, size, cl->netchan.incoming_sequence );
|
||||||
|
|
||||||
|
if( checksum2 != checksum1 )
|
||||||
|
{
|
||||||
|
MsgDev( D_ERROR, "SV_UserMove: failed command checksum for %s (%d != %d)\n", cl->name, checksum2, checksum1 );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check for pause or frozen
|
||||||
|
if( sv.paused || sv.loadgame || !CL_IsInGame() || ( player->v.flags & FL_FROZEN ))
|
||||||
|
{
|
||||||
|
for( i = 0; i < newcmds; i++ )
|
||||||
|
{
|
||||||
|
cmds[i].msec = 0;
|
||||||
|
cmds[i].forwardmove = 0;
|
||||||
|
cmds[i].sidemove = 0;
|
||||||
|
cmds[i].upmove = 0;
|
||||||
|
cmds[i].buttons = 0;
|
||||||
|
|
||||||
|
if( player->v.flags & FL_FROZEN )
|
||||||
|
cmds[i].impulse = 0;
|
||||||
|
|
||||||
|
VectorCopy( cmds[i].viewangles, player->v.v_angle );
|
||||||
|
}
|
||||||
|
net_drop = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
VectorCopy( cmds[0].viewangles, player->v.v_angle );
|
||||||
|
}
|
||||||
|
|
||||||
|
player->v.button = cmds[0].buttons;
|
||||||
|
player->v.light_level = cmds[0].lightlevel;
|
||||||
|
|
||||||
|
SV_EstablishTimeBase( cl, cmds, net_drop, numbackup, newcmds );
|
||||||
|
|
||||||
|
if( net_drop < 24 )
|
||||||
|
{
|
||||||
|
|
||||||
|
while( net_drop > numbackup )
|
||||||
|
{
|
||||||
|
SV_PreRunCmd( cl, &cl->lastcmd, 0 );
|
||||||
|
SV_RunCmd( cl, &cl->lastcmd, 0 );
|
||||||
|
SV_PostRunCmd( cl );
|
||||||
|
net_drop--;
|
||||||
|
}
|
||||||
|
|
||||||
|
while( net_drop > 0 )
|
||||||
|
{
|
||||||
|
i = net_drop + newcmds - 1;
|
||||||
|
SV_PreRunCmd( cl, &cmds[i], cl->netchan.incoming_sequence - i );
|
||||||
|
SV_RunCmd( cl, &cmds[i], cl->netchan.incoming_sequence - i );
|
||||||
|
SV_PostRunCmd( cl );
|
||||||
|
net_drop--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for( i = newcmds - 1; i >= 0; i-- )
|
||||||
|
{
|
||||||
|
SV_PreRunCmd( cl, &cmds[i], cl->netchan.incoming_sequence - i );
|
||||||
|
SV_RunCmd( cl, &cmds[i], cl->netchan.incoming_sequence - i );
|
||||||
|
SV_PostRunCmd( cl );
|
||||||
|
}
|
||||||
|
|
||||||
|
cl->lastcmd = cmds[0];
|
||||||
|
cl->lastcmd.buttons = 0; // avoid multiple fires on lag
|
||||||
|
|
||||||
|
// adjust latency time by 1/2 last client frame since
|
||||||
|
// the message probably arrived 1/2 through client's frame loop
|
||||||
|
frame->latency -= cl->lastcmd.msec * 0.5f / 1000.0f;
|
||||||
|
frame->latency = max( 0.0f, frame->latency );
|
||||||
|
|
||||||
|
if( player->v.animtime > sv.time + sv.frametime )
|
||||||
|
player->v.animtime = sv.time + sv.frametime;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
===================
|
===================
|
||||||
SV_ExecuteClientMessage
|
SV_ExecuteClientMessage
|
||||||
|
@ -1376,18 +1638,32 @@ Parse a client packet
|
||||||
*/
|
*/
|
||||||
void SV_ExecuteClientMessage( sv_client_t *cl, sizebuf_t *msg )
|
void SV_ExecuteClientMessage( sv_client_t *cl, sizebuf_t *msg )
|
||||||
{
|
{
|
||||||
int c, stringCmdCount = 0;
|
int c, stringCmdCount = 0;
|
||||||
bool move_issued = false;
|
bool move_issued = false;
|
||||||
char *s;
|
client_frame_t *frame;
|
||||||
|
char *s;
|
||||||
|
|
||||||
// make sure the reply sequence number matches the incoming sequence number
|
// calc ping time
|
||||||
if( cl->netchan.incoming_sequence >= cl->netchan.outgoing_sequence )
|
frame = &cl->frames[cl->netchan.incoming_acknowledged & SV_UPDATE_MASK];
|
||||||
cl->netchan.outgoing_sequence = cl->netchan.incoming_sequence;
|
|
||||||
else cl->send_message = false; // don't reply, sequences have slipped
|
|
||||||
|
|
||||||
// save time for ping calculations
|
// raw ping doesn't factor in message interval, either
|
||||||
cl->frames[cl->netchan.outgoing_sequence & SV_UPDATE_MASK].senttime = host.realtime;
|
frame->raw_ping = host.realtime - frame->senttime;
|
||||||
cl->frames[cl->netchan.outgoing_sequence & SV_UPDATE_MASK].latency = -1.0f;
|
|
||||||
|
// on first frame ( no senttime ) don't skew ping
|
||||||
|
if( frame->senttime == 0.0f )
|
||||||
|
{
|
||||||
|
frame->latency = 0.0f;
|
||||||
|
frame->raw_ping = 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
// don't skew ping based on signon stuff either
|
||||||
|
if(( host.realtime - cl->lastconnect ) < 2.0f && ( frame->latency > 0.0 ))
|
||||||
|
{
|
||||||
|
frame->latency = 0.0f;
|
||||||
|
frame->raw_ping = 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
cl->delta_sequence = -1; // no delta unless requested
|
||||||
|
|
||||||
// read optional clientCommand strings
|
// read optional clientCommand strings
|
||||||
while( cl->state != cs_zombie )
|
while( cl->state != cs_zombie )
|
||||||
|
@ -1412,10 +1688,14 @@ void SV_ExecuteClientMessage( sv_client_t *cl, sizebuf_t *msg )
|
||||||
case clc_userinfo:
|
case clc_userinfo:
|
||||||
SV_UserinfoChanged( cl, BF_ReadString( msg ));
|
SV_UserinfoChanged( cl, BF_ReadString( msg ));
|
||||||
break;
|
break;
|
||||||
|
case clc_delta:
|
||||||
|
cl->delta_sequence = BF_ReadByte( msg );
|
||||||
|
break;
|
||||||
case clc_move:
|
case clc_move:
|
||||||
if( move_issued ) return; // someone is trying to cheat...
|
if( move_issued ) return; // someone is trying to cheat...
|
||||||
move_issued = true;
|
move_issued = true;
|
||||||
SV_ReadClientMove( cl, msg );
|
// SV_ReadClientMove( cl, msg );
|
||||||
|
SV_ParseClientMove( cl, msg );
|
||||||
break;
|
break;
|
||||||
case clc_stringcmd:
|
case clc_stringcmd:
|
||||||
s = BF_ReadString( msg );
|
s = BF_ReadString( msg );
|
||||||
|
|
|
@ -20,7 +20,7 @@ typedef struct
|
||||||
static byte *clientpvs; // FatPVS
|
static byte *clientpvs; // FatPVS
|
||||||
static byte *clientphs; // FatPHS
|
static byte *clientphs; // FatPHS
|
||||||
|
|
||||||
int c_fullsend;
|
int c_fullsend; // just a debug counter
|
||||||
|
|
||||||
/*
|
/*
|
||||||
=======================
|
=======================
|
||||||
|
@ -61,6 +61,7 @@ void SV_ClearPacketEntities( client_frame_t *frame )
|
||||||
Mem_Free( packet->entities );
|
Mem_Free( packet->entities );
|
||||||
|
|
||||||
packet->num_entities = 0;
|
packet->num_entities = 0;
|
||||||
|
packet->max_entities = 0;
|
||||||
packet->entities = NULL;
|
packet->entities = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -77,15 +78,15 @@ void SV_AllocPacketEntities( client_frame_t *frame, int count )
|
||||||
ASSERT( frame != NULL );
|
ASSERT( frame != NULL );
|
||||||
|
|
||||||
packet = &frame->entities;
|
packet = &frame->entities;
|
||||||
|
|
||||||
if( packet->entities != NULL )
|
|
||||||
{
|
|
||||||
SV_ClearPacketEntities( frame );
|
|
||||||
}
|
|
||||||
|
|
||||||
if( count < 1 ) count = 1;
|
if( count < 1 ) count = 1;
|
||||||
|
|
||||||
packet->entities = Mem_Alloc( svgame.mempool, sizeof( entity_state_t ) * count );
|
// check if new frame has more entities than previous
|
||||||
|
if( packet->max_entities < count )
|
||||||
|
{
|
||||||
|
SV_ClearPacketEntities( frame );
|
||||||
|
packet->entities = Mem_Alloc( svgame.mempool, sizeof( entity_state_t ) * count );
|
||||||
|
packet->max_entities = count;
|
||||||
|
}
|
||||||
packet->num_entities = count;
|
packet->num_entities = count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -104,86 +105,21 @@ void SV_ClearFrames( client_frame_t **frames )
|
||||||
if( *frames == NULL )
|
if( *frames == NULL )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
for( i = 0, frame = *frames; i < SV_UPDATE_BACKUP; i++, frames++ )
|
for( i = 0, frame = *frames; i < SV_UPDATE_BACKUP; i++, frame++ )
|
||||||
{
|
{
|
||||||
SV_ClearPacketEntities( frame );
|
SV_ClearPacketEntities( frame );
|
||||||
frame->senttime = 0.0f;
|
|
||||||
frame->ping_time = -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Mem_Free( *frames );
|
Mem_Free( *frames );
|
||||||
*frames = NULL;
|
*frames = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
=============================================================================
|
|
||||||
|
|
||||||
Encode a client frame onto the network channel
|
|
||||||
|
|
||||||
=============================================================================
|
|
||||||
*/
|
|
||||||
/*
|
/*
|
||||||
=============
|
=============
|
||||||
SV_EmitPacketEntities
|
SV_AddEntitiesToPacket
|
||||||
|
|
||||||
Writes a delta update of an entity_state_t list to the message->
|
|
||||||
=============
|
=============
|
||||||
*/
|
*/
|
||||||
void SV_EmitPacketEntities( packet_entities_t *from, packet_entities_t *to, sizebuf_t *msg )
|
|
||||||
{
|
|
||||||
int oldindex, newindex;
|
|
||||||
int oldnum, newnum;
|
|
||||||
int oldmax;
|
|
||||||
|
|
||||||
BF_WriteByte( msg, svc_packetentities );
|
|
||||||
|
|
||||||
if( !from ) oldmax = 0;
|
|
||||||
else oldmax = from->num_entities;
|
|
||||||
|
|
||||||
newindex = 0;
|
|
||||||
oldindex = 0;
|
|
||||||
|
|
||||||
while( newindex < to->num_entities || oldindex < oldmax )
|
|
||||||
{
|
|
||||||
newnum = newindex >= to->num_entities ? MAX_ENTNUMBER : to->entities[newindex].number;
|
|
||||||
oldnum = oldindex >= oldmax ? MAX_ENTNUMBER : from->entities[oldindex].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
|
|
||||||
MSG_WriteDeltaEntity( &from->entities[oldindex], &to->entities[newindex], msg, false, sv_time( ));
|
|
||||||
oldindex++;
|
|
||||||
newindex++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if( newnum < oldnum )
|
|
||||||
{
|
|
||||||
// this is a new entity, send it from the baseline
|
|
||||||
MSG_WriteDeltaEntity( &svs.baselines[newnum], &to->entities[newindex], msg, true, sv_time( ));
|
|
||||||
newindex++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if( newnum > oldnum )
|
|
||||||
{
|
|
||||||
bool force;
|
|
||||||
|
|
||||||
if( EDICT_NUM( from->entities[oldindex].number )->free )
|
|
||||||
force = true; // entity completely removed from server
|
|
||||||
else force = false; // just removed from delta-message
|
|
||||||
|
|
||||||
// remove from message
|
|
||||||
MSG_WriteDeltaEntity( &from->entities[oldindex], NULL, msg, force, sv_time( ));
|
|
||||||
oldindex++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
BF_WriteWord( msg, 0 ); // end of packetentities
|
|
||||||
}
|
|
||||||
|
|
||||||
static void SV_AddEntitiesToPacket( edict_t *pViewEnt, edict_t *pClient, client_frame_t *frame, sv_ents_t *ents )
|
static void SV_AddEntitiesToPacket( edict_t *pViewEnt, edict_t *pClient, client_frame_t *frame, sv_ents_t *ents )
|
||||||
{
|
{
|
||||||
edict_t *ent;
|
edict_t *ent;
|
||||||
|
@ -207,8 +143,10 @@ static void SV_AddEntitiesToPacket( edict_t *pViewEnt, edict_t *pClient, client_
|
||||||
ASSERT( cl );
|
ASSERT( cl );
|
||||||
|
|
||||||
// setup hostflags
|
// setup hostflags
|
||||||
if( com.atoi( Info_ValueForKey( cl->userinfo, "cl_lw" )) == 1 )
|
if( cl->local_weapons )
|
||||||
|
{
|
||||||
sv.hostflags |= SVF_SKIPLOCALHOST;
|
sv.hostflags |= SVF_SKIPLOCALHOST;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
svgame.dllFuncs.pfnSetupVisibility( pViewEnt, pClient, &clientpvs, &clientphs );
|
svgame.dllFuncs.pfnSetupVisibility( pViewEnt, pClient, &clientpvs, &clientphs );
|
||||||
|
@ -254,7 +192,12 @@ static void SV_AddEntitiesToPacket( edict_t *pViewEnt, edict_t *pClient, client_
|
||||||
c_fullsend++; // debug counter
|
c_fullsend++; // debug counter
|
||||||
|
|
||||||
}
|
}
|
||||||
else MsgDev( D_ERROR, "too many entities in visible packet list\n" );
|
else
|
||||||
|
{
|
||||||
|
// visibility list is full
|
||||||
|
MsgDev( D_ERROR, "too many entities in visible packet list\n" );
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if( fullvis ) continue; // portal ents will be added anyway, ignore recursion
|
if( fullvis ) continue; // portal ents will be added anyway, ignore recursion
|
||||||
|
@ -269,7 +212,100 @@ static void SV_AddEntitiesToPacket( edict_t *pViewEnt, edict_t *pClient, client_
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void SV_EmitEvents( sv_client_t *cl, client_frame_t *frame, sizebuf_t *msg )
|
/*
|
||||||
|
=============================================================================
|
||||||
|
|
||||||
|
Encode a client frame onto the network channel
|
||||||
|
|
||||||
|
=============================================================================
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
=============
|
||||||
|
SV_EmitPacketEntities
|
||||||
|
|
||||||
|
Writes a delta update of an entity_state_t list to the message->
|
||||||
|
=============
|
||||||
|
*/
|
||||||
|
void SV_EmitPacketEntities( sv_client_t *cl, packet_entities_t *to, sizebuf_t *msg )
|
||||||
|
{
|
||||||
|
packet_entities_t *from;
|
||||||
|
client_frame_t *oldframe;
|
||||||
|
int oldindex, newindex;
|
||||||
|
int oldnum, newnum;
|
||||||
|
int oldmax;
|
||||||
|
|
||||||
|
// this is the frame that we are going to delta update from
|
||||||
|
if( cl->delta_sequence != -1 )
|
||||||
|
{
|
||||||
|
oldframe = &cl->frames[cl->delta_sequence & SV_UPDATE_MASK];
|
||||||
|
from = &oldframe->entities;
|
||||||
|
oldmax = from->num_entities;
|
||||||
|
|
||||||
|
BF_WriteByte( msg, svc_deltapacketentities );
|
||||||
|
BF_WriteWord( msg, to->num_entities );
|
||||||
|
BF_WriteByte( msg, cl->delta_sequence );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
oldmax = 0; // no delta update
|
||||||
|
from = NULL;
|
||||||
|
|
||||||
|
BF_WriteByte( msg, svc_packetentities );
|
||||||
|
BF_WriteWord( msg, to->num_entities );
|
||||||
|
}
|
||||||
|
|
||||||
|
newindex = 0;
|
||||||
|
oldindex = 0;
|
||||||
|
|
||||||
|
while( newindex < to->num_entities || oldindex < oldmax )
|
||||||
|
{
|
||||||
|
newnum = newindex >= to->num_entities ? MAX_ENTNUMBER : to->entities[newindex].number;
|
||||||
|
oldnum = oldindex >= oldmax ? MAX_ENTNUMBER : from->entities[oldindex].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
|
||||||
|
MSG_WriteDeltaEntity( &from->entities[oldindex], &to->entities[newindex], msg, false, sv_time( ));
|
||||||
|
oldindex++;
|
||||||
|
newindex++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( newnum < oldnum )
|
||||||
|
{
|
||||||
|
// this is a new entity, send it from the baseline
|
||||||
|
MSG_WriteDeltaEntity( &svs.baselines[newnum], &to->entities[newindex], msg, true, sv_time( ));
|
||||||
|
newindex++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( newnum > oldnum )
|
||||||
|
{
|
||||||
|
bool force;
|
||||||
|
|
||||||
|
if( EDICT_NUM( from->entities[oldindex].number )->free )
|
||||||
|
force = true; // entity completely removed from server
|
||||||
|
else force = false; // just removed from delta-message
|
||||||
|
|
||||||
|
// remove from message
|
||||||
|
MSG_WriteDeltaEntity( &from->entities[oldindex], NULL, msg, force, sv_time( ));
|
||||||
|
oldindex++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BF_WriteWord( msg, 0 ); // end of packetentities
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
=============
|
||||||
|
SV_EmitEvents
|
||||||
|
|
||||||
|
=============
|
||||||
|
*/
|
||||||
|
static void SV_EmitEvents( sv_client_t *cl, packet_entities_t *to, sizebuf_t *msg )
|
||||||
{
|
{
|
||||||
int i, ev;
|
int i, ev;
|
||||||
event_state_t *es;
|
event_state_t *es;
|
||||||
|
@ -315,144 +351,157 @@ static void SV_EmitEvents( sv_client_t *cl, client_frame_t *frame, sizebuf_t *ms
|
||||||
|
|
||||||
/*
|
/*
|
||||||
=============
|
=============
|
||||||
SV_WriteClientData
|
SV_EmitPings
|
||||||
|
|
||||||
=============
|
=============
|
||||||
*/
|
*/
|
||||||
void SV_WriteClientData( client_frame_t *from, client_frame_t *to, sizebuf_t *msg )
|
void SV_EmitPings( sizebuf_t *msg )
|
||||||
{
|
{
|
||||||
clientdata_t *cd, *ocd;
|
sv_client_t *cl;
|
||||||
clientdata_t dummy;
|
int packet_loss;
|
||||||
|
int i, ping;
|
||||||
|
|
||||||
cd = &to->clientdata;
|
BF_WriteByte( msg, svc_updatepings );
|
||||||
|
|
||||||
if( !from )
|
for( i = 0, cl = svs.clients; i < sv_maxclients->integer; i++, cl++ )
|
||||||
{
|
{
|
||||||
Mem_Set( &dummy, 0, sizeof( dummy ));
|
if( cl->state != cs_spawned ) continue;
|
||||||
ocd = &dummy;
|
|
||||||
|
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 );
|
||||||
}
|
}
|
||||||
else ocd = &from->clientdata;
|
|
||||||
|
// end marker
|
||||||
|
BF_WriteOneBit( msg, 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
==================
|
||||||
|
SV_WriteClientdataToMessage
|
||||||
|
|
||||||
|
==================
|
||||||
|
*/
|
||||||
|
void SV_WriteClientdataToMessage( sv_client_t *cl, sizebuf_t *msg )
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
|
||||||
|
Mem_Set( &nullcd, 0, sizeof( nullcd ));
|
||||||
|
|
||||||
|
clent = cl->edict;
|
||||||
|
frame = &( cl->frames[cl->netchan.outgoing_sequence & SV_UPDATE_MASK] );
|
||||||
|
|
||||||
|
frame->senttime = host.realtime;
|
||||||
|
frame->raw_ping = -1.0f;
|
||||||
|
frame->latency = -1.0f;
|
||||||
|
|
||||||
|
if( cl->chokecount != 0 )
|
||||||
|
{
|
||||||
|
BF_WriteByte( msg, svc_chokecount );
|
||||||
|
cl->chokecount = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 );
|
||||||
|
clent->v.effects |= EF_NOINTERP;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
BF_WriteByte( msg, svc_addangle );
|
||||||
|
BF_WriteBitAngle( msg, cl->addangle, 16 );
|
||||||
|
cl->addangle = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
clent->v.fixangle = 0; // reset fixangle
|
||||||
|
|
||||||
|
Mem_Set( &frame->clientdata, 0, sizeof( frame->clientdata ));
|
||||||
|
|
||||||
|
// update clientdata_t
|
||||||
|
svgame.dllFuncs.pfnUpdateClientData( clent, cl->local_weapons, &frame->clientdata );
|
||||||
|
|
||||||
BF_WriteByte( msg, svc_clientdata );
|
BF_WriteByte( msg, svc_clientdata );
|
||||||
MSG_WriteClientData( msg, ocd, cd, sv.time );
|
if( cl->hltv_proxy ) return; // don't send more nothing
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
if( cl->delta_sequence == -1 ) from_cd = &nullcd;
|
||||||
==================
|
else from_cd = &cl->frames[cl->delta_sequence & SV_UPDATE_MASK].clientdata;
|
||||||
SV_WriteFrameToClient
|
to_cd = &frame->clientdata;
|
||||||
==================
|
|
||||||
*/
|
|
||||||
void SV_WriteFrameToClient( sv_client_t *cl, sizebuf_t *msg )
|
|
||||||
{
|
|
||||||
client_frame_t *frame, *oldframe;
|
|
||||||
packet_entities_t *from, *to;
|
|
||||||
int lastframe;
|
|
||||||
|
|
||||||
// this is the frame we are creating
|
if( cl->delta_sequence == -1 )
|
||||||
frame = &cl->frames[sv.framenum & SV_UPDATE_MASK];
|
|
||||||
to = &frame->entities;
|
|
||||||
|
|
||||||
if( cl->lastframe <= 0 )
|
|
||||||
{
|
{
|
||||||
// client is asking for a retransmit
|
BF_WriteOneBit( msg, 0 ); // no delta-compression
|
||||||
oldframe = NULL;
|
|
||||||
lastframe = -1;
|
|
||||||
from = NULL;
|
|
||||||
}
|
|
||||||
else if( sv.framenum - cl->lastframe >= (SV_UPDATE_BACKUP - 3))
|
|
||||||
{
|
|
||||||
// client hasn't gotten a good message through in a long time
|
|
||||||
oldframe = NULL;
|
|
||||||
lastframe = -1;
|
|
||||||
from = NULL;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{ // we have a valid message to delta from
|
{
|
||||||
oldframe = &cl->frames[cl->lastframe & SV_UPDATE_MASK];
|
BF_WriteOneBit( msg, 1 ); // we are delta-ing from
|
||||||
lastframe = cl->lastframe;
|
BF_WriteByte( msg, cl->delta_sequence );
|
||||||
from = &oldframe->entities;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// delta encode the events
|
// write clientdata_t
|
||||||
SV_EmitEvents( cl, frame, msg );
|
MSG_WriteClientData( msg, from_cd, to_cd, sv.time );
|
||||||
|
|
||||||
BF_WriteByte( msg, svc_frame );
|
if( cl->local_weapons && svgame.dllFuncs.pfnGetWeaponData( clent, frame->weapondata ))
|
||||||
BF_WriteFloat( msg, (float)sv.time ); // send a servertime each frame
|
{
|
||||||
BF_WriteLong( msg, sv.framenum );
|
Mem_Set( &nullwd, 0, sizeof( nullwd ));
|
||||||
BF_WriteLong( msg, lastframe ); // what we are delta'ing from
|
|
||||||
BF_WriteByte( msg, cl->surpressCount ); // rate dropped packets
|
|
||||||
cl->surpressCount = 0;
|
|
||||||
|
|
||||||
// delta encode the clientdata
|
for( i = 0; i < 32; i++ )
|
||||||
SV_WriteClientData( oldframe, frame, msg );
|
{
|
||||||
|
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];
|
||||||
|
|
||||||
// delta encode the entities
|
MSG_WriteWeaponData( msg, from_wd, to_wd, sv.time, i );
|
||||||
SV_EmitPacketEntities( from, to, msg );
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// end marker
|
||||||
|
BF_WriteOneBit( msg, 0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
=============================================================================
|
==================
|
||||||
|
SV_WriteEntitiesToClient
|
||||||
|
|
||||||
Build a client frame structure
|
==================
|
||||||
|
|
||||||
=============================================================================
|
|
||||||
*/
|
*/
|
||||||
/*
|
void SV_WriteEntitiesToClient( sv_client_t *cl, sizebuf_t *msg )
|
||||||
=============
|
|
||||||
SV_BuildClientFrame
|
|
||||||
|
|
||||||
Decides which entities are going to be visible to the client,
|
|
||||||
and copies off the playerstate.
|
|
||||||
=============
|
|
||||||
*/
|
|
||||||
void SV_BuildClientFrame( sv_client_t *cl )
|
|
||||||
{
|
{
|
||||||
edict_t *clent;
|
edict_t *clent;
|
||||||
edict_t *viewent; // may be NULL
|
edict_t *viewent; // may be NULL
|
||||||
client_frame_t *frame;
|
client_frame_t *frame;
|
||||||
packet_entities_t *packet;
|
packet_entities_t *packet;
|
||||||
static sv_ents_t frame_ents;
|
static sv_ents_t frame_ents;
|
||||||
|
bool send_pings;
|
||||||
|
|
||||||
clent = cl->edict;
|
clent = cl->edict;
|
||||||
viewent = cl->pViewEntity;
|
viewent = cl->pViewEntity; // himself or trigger_camera
|
||||||
sv.net_framenum++;
|
|
||||||
|
|
||||||
if( !sv.paused )
|
frame = &cl->frames[cl->netchan.outgoing_sequence & SV_UPDATE_MASK];
|
||||||
{
|
|
||||||
// update client fixangle
|
|
||||||
switch( clent->v.fixangle )
|
|
||||||
{
|
|
||||||
case 1:
|
|
||||||
BF_WriteByte( &cl->netchan.message, svc_setangle );
|
|
||||||
BF_WriteBitAngle( &cl->netchan.message, clent->v.angles[0], 16 );
|
|
||||||
BF_WriteBitAngle( &cl->netchan.message, clent->v.angles[1], 16 );
|
|
||||||
clent->v.effects |= EF_NOINTERP;
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
BF_WriteByte( &cl->netchan.message, svc_addangle );
|
|
||||||
BF_WriteBitAngle( &cl->netchan.message, cl->addangle, 16 );
|
|
||||||
cl->addangle = 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
clent->v.fixangle = 0; // reset fixangle
|
|
||||||
}
|
|
||||||
|
|
||||||
// this is the frame we are creating
|
|
||||||
frame = &cl->frames[sv.framenum & SV_UPDATE_MASK];
|
|
||||||
frame->senttime = host.realtime; // save it for ping calc later
|
|
||||||
packet = &frame->entities;
|
packet = &frame->entities;
|
||||||
|
|
||||||
|
send_pings = SV_ShouldUpdatePing( cl );
|
||||||
|
|
||||||
|
sv.net_framenum++; // now all portal-through entities are invalidate
|
||||||
|
sv.hostflags &= ~SVF_PORTALPASS;
|
||||||
|
|
||||||
// clear everything in this snapshot
|
// clear everything in this snapshot
|
||||||
frame_ents.num_entities = c_fullsend = 0;
|
frame_ents.num_entities = c_fullsend = 0;
|
||||||
if( !SV_ClientFromEdict( clent, true )) return; // not in game yet
|
|
||||||
|
|
||||||
// update clientdata_t
|
|
||||||
svgame.dllFuncs.pfnUpdateClientData( clent, false, &frame->clientdata );
|
|
||||||
|
|
||||||
// add all the entities directly visible to the eye, which
|
// add all the entities directly visible to the eye, which
|
||||||
// may include portal entities that merge other viewpoints
|
// may include portal entities that merge other viewpoints
|
||||||
sv.hostflags &= ~SVF_PORTALPASS;
|
|
||||||
SV_AddEntitiesToPacket( viewent, clent, frame, &frame_ents );
|
SV_AddEntitiesToPacket( viewent, clent, frame, &frame_ents );
|
||||||
|
|
||||||
// if there were portals visible, there may be out of order entities
|
// if there were portals visible, there may be out of order entities
|
||||||
|
@ -464,6 +513,10 @@ void SV_BuildClientFrame( sv_client_t *cl )
|
||||||
// copy the entity states to client frame
|
// copy the entity states to client frame
|
||||||
SV_AllocPacketEntities( frame, frame_ents.num_entities );
|
SV_AllocPacketEntities( frame, frame_ents.num_entities );
|
||||||
Mem_Copy( packet->entities, frame_ents.entities, sizeof( entity_state_t ) * frame_ents.num_entities );
|
Mem_Copy( packet->entities, frame_ents.entities, sizeof( entity_state_t ) * frame_ents.num_entities );
|
||||||
|
|
||||||
|
SV_EmitPacketEntities( cl, packet, msg );
|
||||||
|
SV_EmitEvents( cl, packet, msg );
|
||||||
|
if( send_pings ) SV_EmitPings( msg );
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -478,30 +531,24 @@ FRAME UPDATES
|
||||||
SV_SendClientDatagram
|
SV_SendClientDatagram
|
||||||
=======================
|
=======================
|
||||||
*/
|
*/
|
||||||
bool SV_SendClientDatagram( sv_client_t *cl )
|
void SV_SendClientDatagram( sv_client_t *cl )
|
||||||
{
|
{
|
||||||
byte msg_buf[MAX_MSGLEN];
|
byte msg_buf[NET_MAX_PAYLOAD];
|
||||||
sizebuf_t msg;
|
sizebuf_t msg;
|
||||||
|
|
||||||
SV_BuildClientFrame( cl );
|
svs.currentPlayer = cl;
|
||||||
|
|
||||||
BF_Init( &msg, "Datagram", msg_buf, sizeof( msg_buf ));
|
BF_Init( &msg, "Datagram", msg_buf, sizeof( msg_buf ));
|
||||||
|
|
||||||
// send over all the relevant entity_state_t
|
// always send servertime at new frame
|
||||||
// and the player state
|
BF_WriteByte( &msg, svc_time );
|
||||||
SV_WriteFrameToClient( cl, &msg );
|
BF_WriteFloat( &msg, sv.time );
|
||||||
|
|
||||||
if( BF_CheckOverflow( &msg ))
|
SV_WriteClientdataToMessage( cl, &msg );
|
||||||
{
|
SV_WriteEntitiesToClient( cl, &msg );
|
||||||
// must have room left for the packet header
|
|
||||||
MsgDev( D_WARN, "msg overflowed for %s\n", cl->name );
|
|
||||||
BF_Clear( &msg );
|
|
||||||
}
|
|
||||||
|
|
||||||
// copy the accumulated multicast datagram
|
// copy the accumulated multicast datagram
|
||||||
// for this client out to the message
|
// for this client out to the message
|
||||||
// it is necessary for this to be after the WriteEntities
|
|
||||||
// so that entity references will be current
|
|
||||||
if( BF_CheckOverflow( &cl->datagram )) MsgDev( D_WARN, "datagram overflowed for %s\n", cl->name );
|
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 ));
|
else BF_WriteBits( &msg, BF_GetData( &cl->datagram ), BF_GetNumBitsWritten( &cl->datagram ));
|
||||||
BF_Clear( &cl->datagram );
|
BF_Clear( &cl->datagram );
|
||||||
|
@ -515,39 +562,6 @@ bool SV_SendClientDatagram( sv_client_t *cl )
|
||||||
|
|
||||||
// send the datagram
|
// send the datagram
|
||||||
Netchan_TransmitBits( &cl->netchan, BF_GetNumBitsWritten( &msg ), BF_GetData( &msg ));
|
Netchan_TransmitBits( &cl->netchan, BF_GetNumBitsWritten( &msg ), BF_GetData( &msg ));
|
||||||
|
|
||||||
// record the size for rate estimation
|
|
||||||
cl->message_size[sv.framenum % RATE_MESSAGES] = BF_GetNumBytesWritten( &msg );
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
=======================
|
|
||||||
SV_RateDrop
|
|
||||||
|
|
||||||
Returns true if the client is over its current
|
|
||||||
bandwidth estimation and should not be sent another packet
|
|
||||||
=======================
|
|
||||||
*/
|
|
||||||
bool SV_RateDrop( sv_client_t *cl )
|
|
||||||
{
|
|
||||||
int i, total = 0;
|
|
||||||
|
|
||||||
// never drop over the loopback
|
|
||||||
if( NET_IsLocalAddress( cl->netchan.remote_address ))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
for( i = 0; i < RATE_MESSAGES; i++ )
|
|
||||||
total += cl->message_size[i];
|
|
||||||
|
|
||||||
if( total > cl->rate )
|
|
||||||
{
|
|
||||||
cl->surpressCount++;
|
|
||||||
cl->message_size[sv.framenum % RATE_MESSAGES] = 0;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -573,6 +587,12 @@ void SV_UpdateToReliableMessages( void )
|
||||||
cl->sendinfo = false;
|
cl->sendinfo = false;
|
||||||
SV_FullClientUpdate( cl, &sv.reliable_datagram );
|
SV_FullClientUpdate( cl, &sv.reliable_datagram );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if( cl->sendmovevars )
|
||||||
|
{
|
||||||
|
cl->sendmovevars = false;
|
||||||
|
SV_FullUpdateMovevars( cl, &cl->netchan.message );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// clear the server datagram if it overflowed.
|
// clear the server datagram if it overflowed.
|
||||||
|
@ -614,27 +634,42 @@ void SV_SendClientMessages( void )
|
||||||
|
|
||||||
SV_UpdateToReliableMessages ();
|
SV_UpdateToReliableMessages ();
|
||||||
|
|
||||||
// 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++;
|
|
||||||
|
|
||||||
// send a message to each connected client
|
// send a message to each connected client
|
||||||
for( i = 0, cl = svs.clients; i < sv_maxclients->integer; i++, cl++ )
|
for( i = 0, cl = svs.clients; i < sv_maxclients->integer; i++, cl++ )
|
||||||
{
|
{
|
||||||
if( !cl->state ) continue;
|
if( !cl->state || cl->fakeclient )
|
||||||
|
|
||||||
if( !cl->edict || (cl->edict->v.flags & ( FL_FAKECLIENT|FL_SPECTATOR )))
|
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
svs.currentPlayer = cl;
|
if( cl->skip_message )
|
||||||
|
|
||||||
if( cl->sendmovevars )
|
|
||||||
{
|
{
|
||||||
cl->sendmovevars = false;
|
cl->skip_message = false;
|
||||||
SV_FullUpdateMovevars( cl, &cl->netchan.message );
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// if the reliable message overflowed, drop the client
|
// if the reliable message overflowed, drop the client
|
||||||
if( BF_CheckOverflow( &cl->netchan.message ))
|
if( BF_CheckOverflow( &cl->netchan.message ))
|
||||||
|
@ -642,26 +677,46 @@ void SV_SendClientMessages( void )
|
||||||
BF_Clear( &cl->netchan.message );
|
BF_Clear( &cl->netchan.message );
|
||||||
BF_Clear( &cl->datagram );
|
BF_Clear( &cl->datagram );
|
||||||
SV_BroadcastPrintf( PRINT_HIGH, "%s overflowed\n", cl->name );
|
SV_BroadcastPrintf( PRINT_HIGH, "%s overflowed\n", cl->name );
|
||||||
|
MsgDev( D_WARN, "reliable overflow for %s\n", cl->name );
|
||||||
SV_DropClient( cl );
|
SV_DropClient( cl );
|
||||||
cl->send_message = true;
|
cl->send_message = true;
|
||||||
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// only send messages if the client has sent one
|
// only send messages if the client has sent one
|
||||||
|
// and the bandwidth is not choked
|
||||||
if( !cl->send_message ) continue;
|
if( !cl->send_message ) continue;
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
|
||||||
if( cl->state == cs_spawned )
|
if( cl->state == cs_spawned )
|
||||||
{
|
{
|
||||||
// don't overrun bandwidth
|
|
||||||
if( SV_RateDrop( cl )) continue;
|
|
||||||
SV_SendClientDatagram( cl );
|
SV_SendClientDatagram( cl );
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if( cl->netchan.message.iCurBit || host.realtime - cl->netchan.last_sent > 1.0f )
|
Netchan_Transmit( &cl->netchan, 0, NULL );
|
||||||
Netchan_Transmit( &cl->netchan, 0, NULL );
|
|
||||||
}
|
}
|
||||||
// yes, message really sended
|
|
||||||
cl->send_message = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// reset current client
|
// reset current client
|
||||||
|
|
|
@ -2970,8 +2970,8 @@ void pfnRunPlayerMove( edict_t *pClient, const float *v_angle, float fmove, floa
|
||||||
|
|
||||||
cl->random_seed = Com_RandomLong( 0, 0x7fffffff ); // full range
|
cl->random_seed = Com_RandomLong( 0, 0x7fffffff ); // full range
|
||||||
|
|
||||||
SV_PreRunCmd( cl, &cmd );
|
SV_PreRunCmd( cl, &cmd, cl->random_seed );
|
||||||
SV_RunCmd( cl, &cmd );
|
SV_RunCmd( cl, &cmd, cl->random_seed );
|
||||||
SV_PostRunCmd( cl );
|
SV_PostRunCmd( cl );
|
||||||
|
|
||||||
cl->lastcmd = cmd;
|
cl->lastcmd = cmd;
|
||||||
|
@ -3418,9 +3418,7 @@ int pfnCanSkipPlayer( const edict_t *player )
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if( com.atoi( Info_ValueForKey( cl->userinfo, "cl_lw" )) == 1 )
|
return cl->local_weapons;
|
||||||
return true;
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -3551,7 +3549,7 @@ void pfnGetPlayerStats( const edict_t *pClient, int *ping, int *packet_loss )
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if( ping ) *ping = cl->ping;
|
if( ping ) *ping = cl->ping * 1000;
|
||||||
if( packet_loss ) *packet_loss = cl->packet_loss;
|
if( packet_loss ) *packet_loss = cl->packet_loss;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -122,7 +122,7 @@ void SV_ActivateServer( void )
|
||||||
if( svs.clients[i].state >= cs_connected )
|
if( svs.clients[i].state >= cs_connected )
|
||||||
{
|
{
|
||||||
Netchan_Clear( &svs.clients[i].netchan );
|
Netchan_Clear( &svs.clients[i].netchan );
|
||||||
svs.clients[i].lastframe = -1;
|
svs.clients[i].delta_sequence = -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -183,13 +183,10 @@ void SV_DeactivateServer( void )
|
||||||
|
|
||||||
svgame.dllFuncs.pfnServerDeactivate();
|
svgame.dllFuncs.pfnServerDeactivate();
|
||||||
|
|
||||||
// set client fields on player ents
|
|
||||||
for( i = 0; i < svgame.globals->maxClients; i++ )
|
for( i = 0; i < svgame.globals->maxClients; i++ )
|
||||||
{
|
{
|
||||||
// free client frames
|
// release client frames
|
||||||
if( svs.clients[i].frames )
|
SV_ClearFrames( &svs.clients[i].frames );
|
||||||
Mem_Free( svs.clients[i].frames );
|
|
||||||
svs.clients[i].frames = NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
svgame.globals->maxEntities = GI->max_edicts;
|
svgame.globals->maxEntities = GI->max_edicts;
|
||||||
|
@ -303,10 +300,6 @@ bool SV_SpawnServer( const char *mapname, const char *startspot )
|
||||||
// needs to reconnect
|
// needs to reconnect
|
||||||
if( svs.clients[i].state > cs_connected )
|
if( svs.clients[i].state > cs_connected )
|
||||||
svs.clients[i].state = cs_connected;
|
svs.clients[i].state = cs_connected;
|
||||||
svs.clients[i].lastframe = -1;
|
|
||||||
|
|
||||||
// release frames
|
|
||||||
SV_ClearFrames( &svs.clients[i].frames );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// make cvars consistant
|
// make cvars consistant
|
||||||
|
@ -447,13 +440,7 @@ void SV_InitGame( void )
|
||||||
// setup all the clients
|
// setup all the clients
|
||||||
ent = EDICT_NUM( i + 1 );
|
ent = EDICT_NUM( i + 1 );
|
||||||
SV_InitEdict( ent );
|
SV_InitEdict( ent );
|
||||||
|
|
||||||
SV_ClearFrames( &svs.clients[i].frames );
|
|
||||||
|
|
||||||
// make crosslinks
|
|
||||||
svs.clients[i].edict = ent;
|
svs.clients[i].edict = ent;
|
||||||
Mem_Set( &svs.clients[i].lastcmd, 0, sizeof( svs.clients[i].lastcmd ));
|
|
||||||
Mem_Set( &svs.clients[i].physinfo, 0, sizeof( svs.clients[i].physinfo ));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
svgame.numEntities = svgame.globals->maxClients + 1; // clients + world
|
svgame.numEntities = svgame.globals->maxClients + 1; // clients + world
|
||||||
|
|
|
@ -12,6 +12,10 @@
|
||||||
netadr_t master_adr[MAX_MASTERS]; // address of group servers
|
netadr_t master_adr[MAX_MASTERS]; // address of group servers
|
||||||
|
|
||||||
cvar_t *sv_zmax;
|
cvar_t *sv_zmax;
|
||||||
|
cvar_t *sv_unlag;
|
||||||
|
cvar_t *sv_maxunlag;
|
||||||
|
cvar_t *sv_unlagpush;
|
||||||
|
cvar_t *sv_unlagsamples;
|
||||||
cvar_t *sv_pausable;
|
cvar_t *sv_pausable;
|
||||||
cvar_t *sv_newunit;
|
cvar_t *sv_newunit;
|
||||||
cvar_t *sv_wateramp;
|
cvar_t *sv_wateramp;
|
||||||
|
@ -41,8 +45,10 @@ cvar_t *sv_check_errors;
|
||||||
cvar_t *sv_footsteps;
|
cvar_t *sv_footsteps;
|
||||||
cvar_t *public_server; // should heartbeats be sent
|
cvar_t *public_server; // should heartbeats be sent
|
||||||
cvar_t *sv_reconnect_limit; // minimum seconds between connect messages
|
cvar_t *sv_reconnect_limit; // minimum seconds between connect messages
|
||||||
|
cvar_t *sv_failuretime;
|
||||||
cvar_t *serverinfo;
|
cvar_t *serverinfo;
|
||||||
cvar_t *physinfo;
|
cvar_t *physinfo;
|
||||||
|
cvar_t *clockwindow;
|
||||||
|
|
||||||
// sky variables
|
// sky variables
|
||||||
cvar_t *sv_skycolor_r;
|
cvar_t *sv_skycolor_r;
|
||||||
|
@ -218,6 +224,47 @@ void SV_UpdateServerInfo( void )
|
||||||
serverinfo->modified = false;
|
serverinfo->modified = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
=================
|
||||||
|
SV_CheckCmdTimes
|
||||||
|
=================
|
||||||
|
*/
|
||||||
|
void SV_CheckCmdTimes( void )
|
||||||
|
{
|
||||||
|
sv_client_t *cl;
|
||||||
|
static double lastreset = 0;
|
||||||
|
double timewindow;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if( 1.0 > host.realtime - lastreset )
|
||||||
|
return;
|
||||||
|
|
||||||
|
lastreset = host.realtime;
|
||||||
|
|
||||||
|
for( i = 0, cl = svs.clients; i < sv_maxclients->integer; i++, cl++ )
|
||||||
|
{
|
||||||
|
if( cl->state != cs_spawned )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if( cl->last_cmdtime == 0.0 )
|
||||||
|
{
|
||||||
|
cl->last_cmdtime = host.realtime;
|
||||||
|
}
|
||||||
|
|
||||||
|
timewindow = cl->last_movetime + cl->last_cmdtime - host.realtime;
|
||||||
|
|
||||||
|
if( timewindow > clockwindow->value )
|
||||||
|
{
|
||||||
|
cl->next_movetime = clockwindow->value + host.realtime;
|
||||||
|
cl->last_movetime = host.realtime - cl->last_cmdtime;
|
||||||
|
}
|
||||||
|
else if( timewindow < -clockwindow->value )
|
||||||
|
{
|
||||||
|
cl->last_movetime = host.realtime - cl->last_cmdtime;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
=================
|
=================
|
||||||
SV_ReadPackets
|
SV_ReadPackets
|
||||||
|
@ -412,6 +459,9 @@ void Host_ServerFrame( void )
|
||||||
// check timeouts
|
// check timeouts
|
||||||
SV_CheckTimeouts ();
|
SV_CheckTimeouts ();
|
||||||
|
|
||||||
|
// check clients timewindow
|
||||||
|
SV_CheckCmdTimes ();
|
||||||
|
|
||||||
// read packets from clients
|
// read packets from clients
|
||||||
SV_ReadPackets ();
|
SV_ReadPackets ();
|
||||||
|
|
||||||
|
@ -572,6 +622,12 @@ void SV_Init( void )
|
||||||
serverinfo = Cvar_Get( "@serverinfo", "0", CVAR_READ_ONLY, "" ); // use ->modified value only
|
serverinfo = Cvar_Get( "@serverinfo", "0", CVAR_READ_ONLY, "" ); // use ->modified value only
|
||||||
public_server = Cvar_Get ("public", "0", 0, "change server type from private to public" );
|
public_server = Cvar_Get ("public", "0", 0, "change server type from private to public" );
|
||||||
sv_reconnect_limit = Cvar_Get ("sv_reconnect_limit", "3", CVAR_ARCHIVE, "max reconnect attempts" );
|
sv_reconnect_limit = Cvar_Get ("sv_reconnect_limit", "3", CVAR_ARCHIVE, "max reconnect attempts" );
|
||||||
|
sv_failuretime = Cvar_Get( "sv_failuretime", "0.5", 0, "after this long without a packet from client, don't send any more until client starts sending again" );
|
||||||
|
sv_unlag = Cvar_Get( "sv_unlag", "1", 0, "allow lag compensation on server-side" );
|
||||||
|
sv_maxunlag = Cvar_Get( "sv_maxunlag", "0.5", 0, "max latency which can be interpolated" );
|
||||||
|
sv_unlagpush = Cvar_Get( "sv_unlagpush", "0.0", 0, "unlag push bias" );
|
||||||
|
sv_unlagsamples = Cvar_Get( "sv_unlagsamples", "1", 0, "max samples to interpolate" );
|
||||||
|
clockwindow = Cvar_Get( "clockwindow", "0.5", 0, "timewindow to execute client moves" );
|
||||||
|
|
||||||
SV_ClearSaveDir (); // delete all temporary *.hl files
|
SV_ClearSaveDir (); // delete all temporary *.hl files
|
||||||
BF_Init( &net_message, "NetMessage", net_message_buffer, sizeof( net_message_buffer ));
|
BF_Init( &net_message, "NetMessage", net_message_buffer, sizeof( net_message_buffer ));
|
||||||
|
|
|
@ -613,10 +613,207 @@ static void PM_FinishMove( playermove_t *pmove, edict_t *clent )
|
||||||
else clent->v.groundentity = NULL;
|
else clent->v.groundentity = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SV_PreRunCmd( sv_client_t *cl, usercmd_t *ucmd )
|
int nofind = 0;
|
||||||
|
|
||||||
|
entity_state_t *SV_FindEntInPack( int index, packet_entities_t *PacksToSearch )
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for( i = 0; i < PacksToSearch->max_entities; i++ )
|
||||||
|
{
|
||||||
|
if( PacksToSearch->entities[i].number == index )
|
||||||
|
return(&(PacksToSearch->entities[i]));
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int SV_UnlagCheckTeleport( vec3_t old_pos, vec3_t new_pos )
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for( i = 0; i < 3; i++ )
|
||||||
|
{
|
||||||
|
if( fabs( old_pos[i] - new_pos[i] ) > 50 )
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SV_SetupMoveInterpolant( sv_client_t *cl )
|
||||||
|
{
|
||||||
|
int i, j, var_C_entindex;
|
||||||
|
float finalpush, lerp_msec, latency, temp, lerpFrac;
|
||||||
|
client_frame_t *frame, *var_24;
|
||||||
|
packet_entities_t *entities;
|
||||||
|
entity_state_t *state, *var_38_FoundEntity;
|
||||||
|
sv_client_t *check;
|
||||||
|
sv_interp_t *lerp;
|
||||||
|
vec3_t tempvec, tempvec2;
|
||||||
|
|
||||||
|
Mem_Set( svgame.interp, 0, sizeof( svgame.interp ));
|
||||||
|
nofind = 1;
|
||||||
|
|
||||||
|
// don't allow unlag in singleplayer
|
||||||
|
if( sv_maxclients->integer <= 1 ) return;
|
||||||
|
|
||||||
|
// unlag disabled by game request
|
||||||
|
if( !svgame.dllFuncs.pfnAllowLagCompensation() || !sv_unlag->integer )
|
||||||
|
return;
|
||||||
|
|
||||||
|
// unlag disabled for current client
|
||||||
|
if( cl->state != cs_spawned || !cl->lag_compensation )
|
||||||
|
return;
|
||||||
|
|
||||||
|
nofind = 0;
|
||||||
|
|
||||||
|
for( i = 0, check = svs.clients; i < sv_maxclients->integer; i++, cl++ )
|
||||||
|
{
|
||||||
|
if( check->state != cs_spawned || check == cl )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
lerp = &svgame.interp[i];
|
||||||
|
|
||||||
|
VectorCopy( check->edict->v.origin, lerp->oldpos );
|
||||||
|
VectorCopy( check->edict->v.absmin, lerp->mins );
|
||||||
|
VectorCopy( check->edict->v.absmax, lerp->maxs );
|
||||||
|
lerp->active = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( cl->latency > 1.5f )
|
||||||
|
latency = 1.5f;
|
||||||
|
else latency = cl->latency;
|
||||||
|
|
||||||
|
temp = sv_maxunlag->value;
|
||||||
|
|
||||||
|
if( temp > 0 && latency > temp )
|
||||||
|
latency = temp;
|
||||||
|
|
||||||
|
lerp_msec = cl->lastcmd.lerp_msec * 0.001f;
|
||||||
|
if( lerp_msec > 0.1f ) lerp_msec = 0.1f;
|
||||||
|
|
||||||
|
if( lerp_msec < cl->cl_updaterate )
|
||||||
|
lerp_msec = cl->cl_updaterate;
|
||||||
|
|
||||||
|
finalpush = sv_unlagpush->value + (( host.realtime - latency ) - lerp_msec );
|
||||||
|
|
||||||
|
if( finalpush > host.realtime )
|
||||||
|
finalpush = host.realtime;
|
||||||
|
|
||||||
|
frame = NULL;
|
||||||
|
|
||||||
|
for( var_24 = NULL, i = 0; i < SV_UPDATE_BACKUP; i++, var_24 = frame )
|
||||||
|
{
|
||||||
|
frame = &cl->frames[(cl->netchan.outgoing_sequence - 1) & SV_UPDATE_MASK];
|
||||||
|
entities = &frame->entities;
|
||||||
|
|
||||||
|
for( j = 0; j < entities->num_entities; j++ )
|
||||||
|
{
|
||||||
|
state = &entities->entities[j];
|
||||||
|
|
||||||
|
if( state->number <= 0 || state->number >= sv_maxclients->integer )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// you know, jge doesn't sound right given how the indexing normally works.
|
||||||
|
|
||||||
|
lerp = &svgame.interp[state->number-1];
|
||||||
|
|
||||||
|
if( lerp->nointerp )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if( state->health <= 0 || ( state->effects & EF_NOINTERP ))
|
||||||
|
lerp->nointerp = true;
|
||||||
|
|
||||||
|
if( !lerp->firstframe )
|
||||||
|
lerp->firstframe = true;
|
||||||
|
else if( SV_UnlagCheckTeleport( state->origin, lerp->finalpos ))
|
||||||
|
lerp->nointerp = true;
|
||||||
|
|
||||||
|
VectorCopy( state->origin, lerp->finalpos );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( finalpush > frame->senttime )
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if( i == SV_UPDATE_BACKUP || finalpush - frame->senttime > 1.0 )
|
||||||
|
{
|
||||||
|
Mem_Set( svgame.interp, 0, sizeof( svgame.interp ));
|
||||||
|
nofind = 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
entities = &frame->entities;
|
||||||
|
|
||||||
|
if( var_24 == NULL )
|
||||||
|
{
|
||||||
|
var_24 = frame;
|
||||||
|
lerpFrac = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if( var_24->senttime - frame->senttime == 0.0 )
|
||||||
|
{
|
||||||
|
lerpFrac = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
lerpFrac = (finalpush - frame->senttime) / (var_24->senttime - frame->senttime);
|
||||||
|
lerpFrac = bound( 0.0f, lerpFrac, 1.0f );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for( i = 0; i < entities->num_entities; i++ )
|
||||||
|
{
|
||||||
|
state = &entities->entities[i];
|
||||||
|
|
||||||
|
if( state->number <= 0 || state->number >= sv_maxclients->integer )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var_C_entindex = state->number - 1;
|
||||||
|
check = &svs.clients[var_C_entindex];
|
||||||
|
|
||||||
|
if( check->state != cs_spawned || check == cl )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
lerp = &svgame.interp[var_C_entindex];
|
||||||
|
|
||||||
|
if( !lerp->active || lerp->nointerp )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var_38_FoundEntity = SV_FindEntInPack( state->number, &var_24->entities );
|
||||||
|
|
||||||
|
if( var_38_FoundEntity == NULL )
|
||||||
|
{
|
||||||
|
tempvec[0] = state->origin[0];
|
||||||
|
tempvec[1] = state->origin[1];
|
||||||
|
tempvec[2] = state->origin[2];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
tempvec2[0] = var_38_FoundEntity->origin[0] - state->origin[0];
|
||||||
|
tempvec2[1] = var_38_FoundEntity->origin[1] - state->origin[1];
|
||||||
|
tempvec2[2] = var_38_FoundEntity->origin[2] - state->origin[2];
|
||||||
|
|
||||||
|
VectorMA( state->origin, lerpFrac, tempvec2, tempvec );
|
||||||
|
}
|
||||||
|
|
||||||
|
VectorCopy( tempvec, lerp->curpos );
|
||||||
|
VectorCopy( tempvec, lerp->newpos );
|
||||||
|
|
||||||
|
if( !VectorCompare( tempvec, check->edict->v.origin ))
|
||||||
|
{
|
||||||
|
VectorCopy( tempvec, check->edict->v.origin );
|
||||||
|
SV_LinkEdict( check->edict, false );
|
||||||
|
lerp->moving = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SV_PreRunCmd( sv_client_t *cl, usercmd_t *ucmd, int random_seed )
|
||||||
{
|
{
|
||||||
svgame.pmove->runfuncs = true; // FIXME: check cl_lc ?
|
svgame.pmove->runfuncs = true; // FIXME: check cl_lc ?
|
||||||
svgame.dllFuncs.pfnCmdStart( cl->edict, ucmd, cl->random_seed );
|
svgame.dllFuncs.pfnCmdStart( cl->edict, ucmd, random_seed );
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -624,13 +821,38 @@ void SV_PreRunCmd( sv_client_t *cl, usercmd_t *ucmd )
|
||||||
SV_RunCmd
|
SV_RunCmd
|
||||||
===========
|
===========
|
||||||
*/
|
*/
|
||||||
void SV_RunCmd( sv_client_t *cl, usercmd_t *ucmd )
|
void SV_RunCmd( sv_client_t *cl, usercmd_t *ucmd, int random_seed )
|
||||||
{
|
{
|
||||||
edict_t *clent;
|
edict_t *clent;
|
||||||
vec3_t oldvel;
|
vec3_t oldvel;
|
||||||
|
usercmd_t cmd;
|
||||||
|
int oldmsec;
|
||||||
|
|
||||||
clent = cl->edict;
|
clent = cl->edict;
|
||||||
if( !SV_IsValidEdict( clent )) return;
|
|
||||||
|
if( cl->next_movetime > host.realtime )
|
||||||
|
{
|
||||||
|
cl->last_movetime += ( ucmd->msec * 0.001f );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
cl->next_movetime = 0.0;
|
||||||
|
|
||||||
|
// chop up very long commands
|
||||||
|
if( ucmd->msec > 50 )
|
||||||
|
{
|
||||||
|
cmd = *ucmd;
|
||||||
|
oldmsec = ucmd->msec;
|
||||||
|
cmd.msec = oldmsec / 2;
|
||||||
|
|
||||||
|
SV_RunCmd( cl, &cmd, random_seed );
|
||||||
|
|
||||||
|
cmd.msec = oldmsec / 2 + (oldmsec & 1); // give them back thier msec.
|
||||||
|
cmd.impulse = 0;
|
||||||
|
SV_RunCmd( cl, &cmd, random_seed );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
PM_CheckMovingGround( clent, ucmd->msec * 0.001f );
|
PM_CheckMovingGround( clent, ucmd->msec * 0.001f );
|
||||||
|
|
||||||
|
|
|
@ -1060,8 +1060,8 @@ void ResizeTexture( s_texture_t *ptexture )
|
||||||
// dword alignment for each scan
|
// dword alignment for each scan
|
||||||
ptexture->skintop = ptexture->min_t;
|
ptexture->skintop = ptexture->min_t;
|
||||||
ptexture->skinleft = ptexture->min_s;
|
ptexture->skinleft = ptexture->min_s;
|
||||||
ptexture->skinwidth = (int)(ptexture->max_s - ptexture->min_s) + 1;
|
ptexture->skinwidth = ((int)(ptexture->max_s - ptexture->min_s + 1) + 3) & ~3;
|
||||||
ptexture->skinheight = (int)(ptexture->max_t - ptexture->min_t) + 1;
|
ptexture->skinheight = (int)(ptexture->max_t - ptexture->min_t + 1);
|
||||||
ptexture->size = ptexture->skinwidth * ptexture->skinheight + 256 * 3;
|
ptexture->size = ptexture->skinwidth * ptexture->skinheight + 256 * 3;
|
||||||
percent = ((ptexture->skinwidth * ptexture->skinheight) / (float)(ptexture->srcwidth * ptexture->srcheight)) * 100.0f;
|
percent = ((ptexture->skinwidth * ptexture->skinheight) / (float)(ptexture->srcwidth * ptexture->srcheight)) * 100.0f;
|
||||||
|
|
||||||
|
|
Reference in New Issue