17 Sep 2009

This commit is contained in:
g-cont 2009-09-17 00:00:00 +04:00 committed by Alibek Omarov
parent c5728bf91e
commit 9cc19b4eec
25 changed files with 612 additions and 1019 deletions

View File

@ -95,7 +95,6 @@ typedef struct entity_state_s
float maxspeed; // min( pev->maxspeed, sv_maxspeed->value )
float health; // client health (other parms can be send by custom messages)
int weapons; // weapon flags
float cmdtime; // prediction stuff
float fov; // horizontal field of view
} entity_state_t;

View File

@ -191,11 +191,11 @@ void CL_LevelShot_f( void )
{
string checkname;
if( !clgame.need_levelshot ) return;
if( !cl.need_levelshot ) return;
// check for exist
com.sprintf( checkname, "levelshots/%s.jpg", cl.configstrings[CS_NAME] );
if( !FS_FileExists( checkname )) re->ScrShot( checkname, VID_LEVELSHOT );
clgame.need_levelshot = false; // done
cl.need_levelshot = false; // done
}
/*

View File

@ -58,6 +58,7 @@ void CL_WriteDemoHeader( const char *name )
MSG_WriteByte( &buf, svc_serverdata );
MSG_WriteLong( &buf, PROTOCOL_VERSION );
MSG_WriteLong( &buf, cl.servercount );
MSG_WriteLong( &buf, cl.serverframetime );
MSG_WriteShort( &buf, cl.playernum );
MSG_WriteString( &buf, cl.configstrings[CS_NAME] );

View File

@ -60,10 +60,10 @@ void CL_DeltaEntity( sizebuf_t *msg, frame_t *frame, int newnum, entity_state_t
// some data changes will force no lerping
if( state->ed_flags & ESF_NODELTA )
{
ent->pvClientData->msgnum = -1;
ent->pvClientData->serverframe = -99;
}
if( ent->pvClientData->msgnum != cl.frame.msgnum - 1 )
if( ent->pvClientData->serverframe != cl.frame.serverframe - 1 )
{
// duplicate the current state so lerping doesn't hurt anything
ent->pvClientData->prev = *state;
@ -73,10 +73,10 @@ void CL_DeltaEntity( sizebuf_t *msg, frame_t *frame, int newnum, entity_state_t
ent->pvClientData->prev = ent->pvClientData->current;
}
ent->pvClientData->msgnum = cl.frame.msgnum;
ent->pvClientData->serverframe = cl.frame.serverframe;
ent->pvClientData->current = *state;
// update edict fields
// update prvm fields
CL_UpdateEntityFields( ent );
}
@ -200,209 +200,45 @@ Determines the fraction between the last two messages that the objects
should be put at.
===============
*/
static float CL_LerpPoint1( void )
static float CL_LerpPoint( void )
{
float f, frac;
f = cl.time - cl.oldtime;
f = cl.mtime[0] - cl.mtime[1];
if( !f || SV_Active())
if( !f )
{
cl.time = cl.mtime[0];
return 1.0f;
}
if( f > 0.1 )
{
// dropped packet, or start of demo
// cl.mtime[1] = cl.mtime[0] - 0.1f;
cl.mtime[1] = cl.mtime[0] - 0.1f;
f = 0.1f;
}
frac = (cl.time - cl.oldtime) / f;
frac = (cl.time - cl.mtime[1]) / f;
if( frac < 0 )
{
if( frac < -0.01f )
{
// cl.time = cl.mtime[1];
cl.time = cl.mtime[1];
}
frac = 0.0f;
frac = 0;
}
else if( frac > 1.0f )
{
if( frac > 1.01f )
{
// cl.time = cl.mtime[0];
cl.time = cl.mtime[0];
}
frac = 1.0f;
}
return frac;
}
static float CL_LerpPoint2( void )
{
float lerpfrac;
// clamp time
if( cl.time > cl.frame.servertime )
{
if( cl_showclamp->integer ) Msg( "cl highclamp\n" );
// cl.time = cl.frame.servertime;
lerpfrac = 1.0f;
}
else if( cl.time < cl.frame.servertime - cl.frametime )
{
if( cl_showclamp->integer ) Msg( "cl lowclamp\n" );
// cl.time = cl.frame.servertime - cl.frametime;
lerpfrac = 0.0f;
}
else lerpfrac = 1.0f - (cl.frame.servertime - cl.time) / cl.frametime;
if( cl_paused->integer )
lerpfrac = 1.0f;
return lerpfrac;
}
/*
==================
CL_FirstSnapshot
==================
*/
void CL_FirstSnapshot( void )
{
cls.state = ca_active;
// set the timedelta so we are exactly on this first frame
cl.time_delta = cl.frame.servertime - cls.realtime;
cl.oldtime = cl.frame.servertime;
// getting a valid frame message ends the connection process
VectorCopy( cl.frame.ps.origin, cl.predicted_origin );
VectorCopy( cl.frame.ps.viewangles, cl.predicted_angles );
}
void CL_AdjustTimeDelta( void )
{
float newDelta;
float deltaDelta;
cl.has_newframe = false;
// the delta never drifts when replaying a demo
if( cls.demoplayback ) return;
newDelta = cl.frame.servertime - cls.realtime;
deltaDelta = fabs( newDelta - cl.time_delta );
if( deltaDelta > 0.5f )
{
cl.time_delta = newDelta;
cl.oldtime = cl.frame.servertime; // FIXME: is this a problem for cgame?
cl.time = cl.frame.servertime;
Msg( "<RESET> " );
}
else if( deltaDelta > 0.1f )
{
// fast adjust, cut the difference in half
cl.time_delta = ( cl.time_delta + newDelta ) / 2;
}
else
{
// slow drift adjust, only move 1 or 2 msec
// if any of the frames between this and the previous snapshot
// had to be extrapolated, nudge our sense of time back a little
// the granularity of +1 / -2 is too high for timescale modified frametimes
if( cl.frame_extrapolate )
{
cl.frame_extrapolate = false;
cl.time_delta -= 0.002f;
}
else
{
// otherwise, move our sense of time forward to minimize total latency
cl.time_delta += 0.001f;
}
}
}
/*
================
CL_SetClientTime
set right client time and calc lerp value
================
*/
void CL_SetClientTime( void )
{
float tn;
// getting a valid frame message ends the connection process
if( cls.state != ca_active )
{
if( cls.state != ca_connected ) return;
if( cls.demoplayback )
{
CL_ReadDemoMessage();
}
if( cl.has_newframe )
{
cl.has_newframe = false;
CL_FirstSnapshot();
}
if( cls.state != ca_active ) return;
}
// if we have gotten to this point, cl.snap is guaranteed to be valid
Com_Assert( !cl.frame.valid );
// allow pause in single player
if( SV_Active() && cl_paused->integer ) return;
// cl_timeNudge is a user adjustable cvar that allows more
// or less latency to be added in the interest of better
// smoothness or better responsiveness.
tn = cl_timenudge->integer * 0.001f;
if( tn < -0.03f ) tn = -0.03f;
else if( tn > 0.03f ) tn = 0.03f;
cl.time = cls.realtime + cl.time_delta - tn;
cl.frametime = cl.time - cl.oldtime;
if( cl.frametime < 0 ) cl.frametime = 0;
// guarantee that time will never flow backwards, even if
// serverTimeDelta made an adjustment or cl_timeNudge was changed
if( cl.time < cl.oldtime ) cl.time = cl.oldtime;
cl.oldtime = cl.time;
// note if we are almost past the latest frame (without timeNudge),
// so we will try and adjust back a bit when the next snapshot arrives
if( cls.realtime + cl.time_delta >= cl.frame.servertime - 0.005f )
cl.frame_extrapolate = true;
// if we have gotten new snapshots, drift serverTimeDelta
// don't do this every frame, or a period of packet loss would
// make a huge adjustment
if( cl.has_newframe ) CL_AdjustTimeDelta();
#if 1
cl.refdef.lerpfrac = CL_LerpPoint1();
#else
// set cl.refdef.lerpfrac
if( cl.oldframe )
{
float delta;
delta = (cl.frame.servertime - cl.oldframe->servertime);
if( delta == 0.0f ) cl.refdef.lerpfrac = 0.0f;
else cl.refdef.lerpfrac = (cl.time - cl.oldframe->servertime) / delta;
}
else cl.refdef.lerpfrac = 0.0f;
#endif
}
/*
================
CL_ParseFrame
@ -411,23 +247,20 @@ CL_ParseFrame
void CL_ParseFrame( sizebuf_t *msg )
{
int cmd, len, idx;
int delta_num;
int old_msgnum;
int packet_num;
edict_t *clent;
Mem_Set( &cl.frame, 0, sizeof( cl.frame ));
cl.frame.msgnum = cls.netchan.incoming_sequence;
cl.frame.servertime = MSG_ReadFloat( msg );
delta_num = MSG_ReadByte( msg );
cl.frame.serverframe = MSG_ReadLong( msg );
cl.serverframetime = MSG_ReadFloat( msg );
cl.frame.deltaframe = MSG_ReadLong( msg );
cl.surpressCount = MSG_ReadByte( msg );
cl.frame.servertime = cl.mtime[0]; // same as servertime
if( !delta_num ) cl.frame.deltaframe = -1;
else cl.frame.deltaframe = cl.frame.msgnum - delta_num;
// 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 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
@ -442,11 +275,11 @@ void CL_ParseFrame( sizebuf_t *msg )
// should never happen
MsgDev( D_INFO, "delta from invalid frame (not supposed to happen!).\n" );
}
if( cl.oldframe->msgnum != cl.frame.deltaframe )
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" );
MsgDev( D_INFO, "Delta frame too old.\n" );
}
else if( cl.parse_entities - cl.oldframe->parse_entities > MAX_PARSE_ENTITIES - 128 )
{
@ -455,14 +288,12 @@ void CL_ParseFrame( sizebuf_t *msg )
else cl.frame.valid = true; // valid delta parse
}
cl.time = bound( cl.frame.servertime - cl.serverframetime, cl.time, cl.frame.servertime );
// read areabits
len = MSG_ReadByte( msg );
MSG_ReadData( msg, &cl.frame.areabits, len );
if( cl.oldframe && !memcmp( cl.oldframe->areabits, cl.frame.areabits, sizeof( cl.frame.areabits )))
cl.render_flags |= RDF_OLDAREABITS;
else cl.render_flags &= ~RDF_OLDAREABITS;
// read clientindex
cmd = MSG_ReadByte( msg );
if( cmd != svc_playerinfo ) Host_Error( "CL_ParseFrame: not clientindex\n" );
@ -480,40 +311,20 @@ void CL_ParseFrame( sizebuf_t *msg )
if( cl.oldframe ) cl.frame.ps = MSG_ParseDeltaPlayer( &cl.oldframe->ps, &clent->pvClientData->current );
else cl.frame.ps = MSG_ParseDeltaPlayer( NULL, &clent->pvClientData->current );
// if not valid, dump the entire thing now that it has been properly read
if( !cl.frame.valid ) return;
// clear the valid flags of any snapshots between the last
// received and this one, so if there was a dropped packet
// it won't look like something valid to delta from next
// time we wrap around in the buffer
old_msgnum = cl.frame.msgnum + 1;
cl.frame.ping = 999;
if( cl.frame.msgnum - old_msgnum >= UPDATE_BACKUP )
old_msgnum = cl.frame.msgnum - UPDATE_MASK;
for( ; old_msgnum < cl.frame.msgnum; old_msgnum++ )
cl.frames[old_msgnum & UPDATE_MASK].valid = false;
// calculate ping time
for ( idx = 0; idx < UPDATE_BACKUP; idx++ )
{
packet_num = ( cls.netchan.outgoing_sequence - 1 - idx ) & UPDATE_MASK;
if( cl.frame.ps.cmdtime >= cl.outframes[packet_num].servertime )
{
cl.frame.ping = cls.realtime - cl.outframes[packet_num].realtime;
break;
}
}
if( cl_shownet->integer == 3 )
Msg( " snapshot:%i delta:%i ping:%i\n", cl.frame.msgnum, cl.frame.deltaframe, cl.frame.ping );
// save the frame off in the backup array for later delta comparisons
cl.frames[cl.frame.msgnum & UPDATE_MASK] = cl.frame;
cl.has_newframe = true;
cl.frames[cl.frame.serverframe & UPDATE_MASK] = cl.frame;
CL_CheckPredictionError();
if( cl.frame.valid )
{
if( cls.state != ca_active )
{
cls.state = ca_active;
// getting a valid frame message ends the connection process
VectorCopy( cl.frame.ps.origin, cl.predicted_origin );
VectorCopy( cl.frame.ps.viewangles, cl.predicted_angles );
}
CL_CheckPredictionError();
}
}
/*
@ -535,6 +346,11 @@ void CL_AddPacketEntities( frame_t *frame )
edict_t *ent;
int pnum;
if( cl_paused->integer )
cl.refdef.lerpfrac = 1.0f;
else cl.refdef.lerpfrac = 1.0 - (cl.frame.servertime - cl.time) / (float)cl.serverframetime;
// Msg( "cl.refdef.lerpfrac %g\n", cl.refdef.lerpfrac );
for( pnum = 0; pnum < frame->num_entities; pnum++ )
{
s1 = &cl_parse_entities[(frame->parse_entities + pnum)&(MAX_PARSE_ENTITIES-1)];
@ -549,6 +365,9 @@ void CL_AddPacketEntities( frame_t *frame )
// NOTE: skyportal entity never added to rendering
if( s1->ed_type == ED_SKYPORTAL ) cl.render_flags |= RDF_SKYPORTALINVIEW;
}
if( cl.oldframe && !memcmp( cl.oldframe->areabits, cl.frame.areabits, sizeof( cl.frame.areabits )))
cl.render_flags |= RDF_OLDAREABITS;
}
/*
@ -563,6 +382,8 @@ void CL_AddEntities( void )
if( cls.state != ca_active )
return;
cl.render_flags = 0;
CL_AddPacketEntities( &cl.frame );
clgame.dllFuncs.pfnCreateEntities();

View File

@ -725,18 +725,18 @@ void pfnDrawCenterPrint( void )
int l, x, y, w;
rgba_t color;
if( !clgame.centerPrintTime ) return;
CL_FadeAlpha( clgame.centerPrintTime, scr_centertime->value, color );
if( !cl.centerPrintTime ) return;
CL_FadeAlpha( cl.centerPrintTime, scr_centertime->value, color );
if( *( int *)color == 0xFFFFFFFF )
{
clgame.centerPrintTime = 0;
cl.centerPrintTime = 0;
return;
}
re->SetColor( color );
start = clgame.centerPrint;
y = clgame.centerPrintY - clgame.centerPrintLines * BIGCHAR_HEIGHT / 2;
start = cl.centerPrint;
y = cl.centerPrintY - cl.centerPrintLines * BIGCHAR_HEIGHT / 2;
while( 1 )
{
@ -750,12 +750,12 @@ void pfnDrawCenterPrint( void )
}
linebuffer[l] = 0;
w = clgame.centerPrintCharWidth * com.cstrlen( linebuffer );
w = cl.centerPrintCharWidth * com.cstrlen( linebuffer );
x = ( SCREEN_WIDTH - w )>>1;
SCR_DrawStringExt( x, y, clgame.centerPrintCharWidth, BIGCHAR_HEIGHT, linebuffer, color, false );
SCR_DrawStringExt( x, y, cl.centerPrintCharWidth, BIGCHAR_HEIGHT, linebuffer, color, false );
y += clgame.centerPrintCharWidth * 1.5;
y += cl.centerPrintCharWidth * 1.5;
while( *start && ( *start != '\n' )) start++;
if( !*start ) break;
start++;
@ -774,18 +774,18 @@ void pfnCenterPrint( const char *text, int y, int charWidth )
{
char *s;
com.strncpy( clgame.centerPrint, text, sizeof( clgame.centerPrint ));
clgame.centerPrintTime = cls.realtime;
clgame.centerPrintY = y;
clgame.centerPrintCharWidth = charWidth;
com.strncpy( cl.centerPrint, text, sizeof( cl.centerPrint ));
cl.centerPrintTime = cls.realtime;
cl.centerPrintY = y;
cl.centerPrintCharWidth = charWidth;
// count the number of lines for centering
clgame.centerPrintLines = 1;
s = clgame.centerPrint;
cl.centerPrintLines = 1;
s = cl.centerPrint;
while( *s )
{
if( *s == '\n' )
clgame.centerPrintLines++;
cl.centerPrintLines++;
s++;
}
}
@ -921,7 +921,7 @@ force to make levelshot
*/
void pfnMakeLevelShot( void )
{
if( !clgame.need_levelshot ) return;
if( !cl.need_levelshot ) return;
Con_ClearNotify();
@ -1424,7 +1424,7 @@ bool CL_LoadProgs( const char *name )
// 65535 unique strings should be enough ...
clgame.hStringTable = StringTable_Create( "Client", 0x10000 );
clgame.maxEntities = host.max_edicts; // FIXME: must come from CS_MAXENTITIES
clgame.maxClients = CL_GetMaxClients();
clgame.maxClients = Host_MaxClients();
clgame.edicts = Mem_Alloc( cls.mempool, sizeof( edict_t ) * clgame.maxEntities );
// register svc_bad message

View File

@ -58,16 +58,8 @@ CL_MouseEvent
*/
void CL_MouseEvent( int mx, int my )
{
if( UI_IsVisible( ))
{
// if the menu is visible, move the menu cursor
UI_MouseMove( mx, my );
}
else
{
cl.mouse_x[cl.mouse_step] += mx;
cl.mouse_y[cl.mouse_step] += my;
}
cl.mouse_x[cl.mouse_step] += mx;
cl.mouse_y[cl.mouse_step] += my;
}
/*
@ -97,30 +89,38 @@ void CL_MouseMove( usercmd_t *cmd )
rate = com.sqrt( mx * mx + my * my ) / 0.5f;
if( cl.frame.ps.health <= 0 ) return;
if( cl.data.mouse_sensitivity == 0.0f ) cl.data.mouse_sensitivity = 1.0f;
accel_sensitivity = m_sensitivity->value + rate * cl_mouseaccel->value;
accel_sensitivity *= cl.data.mouse_sensitivity; // scale by fov
mx *= accel_sensitivity;
my *= accel_sensitivity;
// add mouse X/Y movement to cmd
if(( in_strafe.state & 1 ) || (lookstrafe->integer && mlook_active))
cmd->sidemove += m_side->value * mx;
else cl.refdef.cl_viewangles[YAW] -= m_yaw->value * mx;
if( mlook_active ) clgame.dllFuncs.pfnStopPitchDrift();
if( mlook_active && !( in_strafe.state & 1 ))
if( UI_IsVisible( ))
{
cl.refdef.cl_viewangles[PITCH] += m_pitch->value * my;
cl.refdef.cl_viewangles[PITCH] = bound( -70, cl.refdef.cl_viewangles[PITCH], 80 );
// if the menu is visible, move the menu cursor
UI_MouseMove( mx, my );
}
else
else if( cls.key_dest != key_menu )
{
if(( in_strafe.state & 1 ) && cl.frame.ps.movetype == MOVETYPE_NOCLIP )
cmd->upmove -= m_forward->value * my;
else cmd->forwardmove -= m_forward->value * my;
if( cl.frame.ps.health <= 0 ) return;
if( cl.mouse_sens == 0.0f ) cl.mouse_sens = 1.0f;
accel_sensitivity = m_sensitivity->value + rate * cl_mouseaccel->value;
accel_sensitivity *= cl.mouse_sens; // scale by fov
mx *= accel_sensitivity;
my *= accel_sensitivity;
// add mouse X/Y movement to cmd
if(( in_strafe.state & 1 ) || (lookstrafe->integer && mlook_active))
cmd->sidemove += m_side->value * mx;
else cl.refdef.cl_viewangles[YAW] -= m_yaw->value * mx;
if( mlook_active ) clgame.dllFuncs.pfnStopPitchDrift();
if( mlook_active && !( in_strafe.state & 1 ))
{
cl.refdef.cl_viewangles[PITCH] += m_pitch->value * my;
cl.refdef.cl_viewangles[PITCH] = bound( -70, cl.refdef.cl_viewangles[PITCH], 80 );
}
else
{
if(( in_strafe.state & 1 ) && cl.frame.ps.movetype == MOVETYPE_NOCLIP )
cmd->upmove -= m_forward->value * my;
else cmd->forwardmove -= m_forward->value * my;
}
}
}
@ -497,21 +497,14 @@ CL_FinishMove
*/
void CL_FinishMove( usercmd_t *cmd )
{
static double extramsec = 0;
int ms;
int ms;
// send milliseconds of time to apply the move
extramsec += cls.frametime * 1000;
ms = extramsec;
extramsec -= ms; // fractional part is left for next frame
ms = cls.frametime * 1000;
if( ms > 250 ) ms = 100; // time was unreasonable
cmd->msec = ms;
// send the current server time so the amount of movement
// can be determined without allowing cheating
cmd->servertime = cl.time;
if( cls.key_dest == key_game )
cmd->buttons = CL_ButtonBits( 1 );
@ -520,6 +513,8 @@ void CL_FinishMove( usercmd_t *cmd )
cl.data.iKeyBits = CL_ButtonBits( 0 );
cl.data.iWeaponBits = cl.frame.ps.weapons;
cl.data.mouse_sensitivity = cl.mouse_sens;
VectorCopy( cl.refdef.cl_viewangles, cmd->angles );
VectorCopy( cl.refdef.cl_viewangles, cl.data.angles );
VectorCopy( cl.refdef.origin, cl.data.origin );
@ -528,6 +523,7 @@ void CL_FinishMove( usercmd_t *cmd )
CL_ResetButtonBits( cl.data.iKeyBits );
cl.refdef.fov_x = cl.data.fov;
cl.mouse_sens = cl.data.mouse_sensitivity;
in_cancel = 0;
}
@ -550,200 +546,6 @@ usercmd_t CL_CreateCmd( void )
return cmd;
}
/*
=================
CL_CreateNewCommands
Create a new usercmd_t structure for this frame
=================
*/
void CL_CreateNewCommands( void )
{
int cmdnum;
// no need to create usercmds until we have a gamestate
if( cls.state < ca_connected ) return;
// reset render flags here, to can add RDF_OLDAREABITS without temp variables
cl.render_flags = 0;
// generate a command for this frame
cl.cmd_number++;
cmdnum = cl.cmd_number & CMD_MASK;
cl.cmds[cmdnum] = CL_CreateCmd();
if( freelook->modified )
{
if( !mlook_active && lookspring->value )
clgame.dllFuncs.pfnStartPitchDrift();
}
if( cls.state == ca_connected )
{
// jsut update reliable
if( cls.netchan.message.cursize || cls.realtime - cls.netchan.last_sent > 1.0f )
Netchan_Transmit( &cls.netchan, 0, NULL );
}
}
/*
=================
CL_ReadyToSendPacket
Returns qfalse if we are over the maxpackets limit
and should choke back the bandwidth a bit by not sending
a packet this frame. All the commands will still get
delivered in the next packet, but saving a header and
getting more delta compression will reduce total bandwidth.
=================
*/
bool CL_ReadyToSendPacket( void )
{
int old_packetnum;
float delta;
// don't send anything if playing back a demo
if( cls.demoplayback || cls.state == ca_cinematic )
return false;
// if we are downloading, we send no less than 50ms between packets
if( cls.downloadtempname[0] && cls.realtime - cls.netchan.last_sent < 0.05f )
return false;
// if we don't have a valid gamestate yet, only send
// one packet a second
if( cls.state < ca_connected && !cls.downloadtempname[0] && cls.realtime - cls.netchan.last_sent < 1.0f )
return false;
// send every frame for loopbacks
if( NET_IsLocalAddress( cls.netchan.remote_address ))
return true;
// check for exceeding cl_maxpackets
if( cl_maxpackets->modified )
{
if( cl_maxpackets->integer < 15 )
Cvar_Set( "cl_maxpackets", "15" );
else if( cl_maxpackets->integer > 125 )
Cvar_Set( "cl_maxpackets", "125" );
cl_maxpackets->modified = false;
}
old_packetnum = (cls.netchan.outgoing_sequence - 1) & UPDATE_MASK;
delta = cls.realtime - cl.outframes[old_packetnum].realtime;
if( delta < (cl_maxpackets->integer * 0.001))
{
// the accumulated commands will go out in the next packet
return false;
}
return true;
}
/*
===================
CL_WritePacket
Create and send the command packet to the server
Including both the reliable commands and the usercmds
During normal gameplay, a client packet will contain something like:
1 clc_move
1 command count
<count * usercmds>
===================
*/
void CL_WritePacket( void )
{
sizebuf_t buf;
byte data[MAX_MSGLEN];
usercmd_t *oldcmd;
usercmd_t nullcmd;
int packetnum, old_packetnum;
int i, j, count, key;
// don't send anything if playing back a demo
if( cls.demoplayback || cls.state == ca_cinematic )
return;
Mem_Set( &nullcmd, 0, sizeof( nullcmd ));
oldcmd = &nullcmd;
// send a userinfo update if needed
if( userinfo_modified )
{
userinfo_modified = false;
MSG_WriteByte( &cls.netchan.message, clc_userinfo );
MSG_WriteString( &cls.netchan.message, Cvar_Userinfo( ));
}
MSG_Init( &buf, data, sizeof( data ));
// we want to send all the usercmds that were generated in the last
// few packet, so even if a couple packets are dropped in a row,
// all the cmds will make it to the server
if( cl_packetdup->modified )
{
if( cl_packetdup->integer < 0 )
Cvar_Set( "cl_packetdup", "0" );
else if( cl_packetdup->integer > 5 )
Cvar_Set( "cl_packetdup", "5" );
cl_packetdup->modified = false;
}
old_packetnum = (cls.netchan.outgoing_sequence - 1 - cl_packetdup->integer ) & UPDATE_MASK;
count = cl.cmd_number - cl.outframes[old_packetnum].cmd_number;
if( count > UPDATE_BACKUP )
{
count = UPDATE_BACKUP;
MsgDev( D_WARN, "MAX_USERCMDS limit exceeded\n" );
}
if( count >= 1 )
{
bool noDelta = false;
if( cl_nodelta->integer ) noDelta = true;
if( !cl.frame.valid || cls.demowaiting || cls.netchan.incoming_sequence != cl.frame.msgnum )
noDelta = true;
// begin a client move command
if( noDelta ) MSG_WriteByte( &buf, clc_move );
else MSG_WriteByte( &buf, clc_deltamove );
// save the position for a checksum byte
key = buf.cursize;
MSG_WriteByte( &buf, 0 );
// write the command count
MSG_WriteByte( &buf, count );
if( cl_shownet->integer == 4 )
Msg( " packet: %i %scmds\n", count, noDelta ? "" : "delta" );
// write all the commands, including the predicted command
for( i = 0; i < count; i++ )
{
j = (cl.cmd_number - count + i + 1) & CMD_MASK;
cl.refdef.cmd = &cl.cmds[j];
MSG_WriteDeltaUsercmd( &buf, oldcmd, cl.refdef.cmd );
oldcmd = cl.refdef.cmd;
}
// calculate a checksum over the move commands
buf.data[key] = CRC_Sequence( buf.data + key + 1, buf.cursize - key - 1, cls.netchan.outgoing_sequence );
}
// save out frames for ping calculation
packetnum = cls.netchan.outgoing_sequence & UPDATE_MASK;
cl.outframes[packetnum].realtime = cls.realtime;
cl.outframes[packetnum].servertime = oldcmd->servertime;
cl.outframes[packetnum].cmd_number = cl.cmd_number;
// deliver the message
Netchan_Transmit( &cls.netchan, buf.cursize, buf.data );
}
/*
=================
CL_SendCmd
@ -753,17 +555,86 @@ Called every frame to builds and sends a command packet to the server.
*/
void CL_SendCmd( void )
{
// don't send any message if not connected
if( cls.state < ca_connected ) return;
sizebuf_t buf;
byte data[128];
usercmd_t *oldcmd;
usercmd_t nullcmd;
int checksumIndex;
// don't send commands if paused
if( cl_paused->integer ) return;
// build a command even if not connected
// we create commands even if a demo is playing,
CL_CreateNewCommands();
// save this command off for prediction
cl.refdef.cmd = &cl.cmds[cls.netchan.outgoing_sequence & (CMD_BACKUP-1)];
cl.cmd_time[cls.netchan.outgoing_sequence & (CMD_BACKUP-1)] = cls.realtime;
*cl.refdef.cmd = CL_CreateCmd ();
// don't send a packet if the last packet was sent too recently
if( CL_ReadyToSendPacket( )) CL_WritePacket();
if( cls.state == ca_disconnected || cls.state == ca_connecting )
return;
// ignore commands for demo mode
if( cls.state == ca_cinematic || cls.demoplayback )
return;
if( cls.state == ca_connected )
{
// jsut update reliable
if( cls.netchan.message.cursize || cls.realtime - cls.netchan.last_sent > 1.0f )
Netchan_Transmit( &cls.netchan, 0, NULL );
return;
}
if( freelook->modified )
{
if( !mlook_active && lookspring->value )
clgame.dllFuncs.pfnStartPitchDrift();
}
// send a userinfo update if needed
if( userinfo_modified )
{
userinfo_modified = false;
MSG_WriteByte (&cls.netchan.message, clc_userinfo);
MSG_WriteString (&cls.netchan.message, Cvar_Userinfo());
}
// begin a client move command
MSG_Init( &buf, data, sizeof( data ));
MSG_WriteByte( &buf, clc_move );
// save the position for a checksum byte
checksumIndex = buf.cursize;
MSG_WriteByte( &buf, 0 );
// let the server know what the last frame we
// got was, so the next message can be delta compressed
if (cl_nodelta->value || !cl.frame.valid || cls.demowaiting)
{
MSG_WriteLong( &buf, -1 ); // no compression
}
else
{
MSG_WriteLong( &buf, cl.frame.serverframe );
}
// send this and the previous cmds in the message, so
// if the last packet was dropped, it can be recovered
cl.refdef.cmd = &cl.cmds[(cls.netchan.outgoing_sequence-2) & (CMD_BACKUP-1)];
Mem_Set( &nullcmd, 0, sizeof( nullcmd ));
MSG_WriteDeltaUsercmd( &buf, &nullcmd, cl.refdef.cmd );
oldcmd = cl.refdef.cmd;
cl.refdef.cmd = &cl.cmds[(cls.netchan.outgoing_sequence-1) & (CMD_BACKUP-1)];
MSG_WriteDeltaUsercmd( &buf, oldcmd, cl.refdef.cmd );
oldcmd = cl.refdef.cmd;
cl.refdef.cmd = &cl.cmds[(cls.netchan.outgoing_sequence) & (CMD_BACKUP-1)];
MSG_WriteDeltaUsercmd( &buf, oldcmd, cl.refdef.cmd );
// calculate a checksum over the move commands
buf.data[checksumIndex] = CRC_Sequence( buf.data + checksumIndex + 1, buf.cursize - checksumIndex - 1, cls.netchan.outgoing_sequence);
// deliver the message
Netchan_Transmit( &cls.netchan, buf.cursize, buf.data );
}
/*
@ -774,88 +645,86 @@ CL_InitInput
void CL_InitInput( void )
{
// mouse variables
m_filter = Cvar_Get( "m_filter", "0", 0, "enable mouse filter" );
m_filter = Cvar_Get("m_filter", "0", 0, "enable mouse filter" );
m_sensitivity = Cvar_Get( "m_sensitivity", "3", CVAR_ARCHIVE, "mouse in-game sensitivity" );
cl_mouseaccel = Cvar_Get( "cl_mouseaccelerate", "0", CVAR_ARCHIVE, "mouse accelerate factor" );
// centering
v_centermove = Cvar_Get( "v_centermove", "0.15", 0, "client center moving" );
v_centerspeed = Cvar_Get( "v_centerspeed", "500", 0, "client center speed" );
cl_nodelta = Cvar_Get( "cl_nodelta", "0", 0, "disable delta-compression for usercommnds" );
v_centermove = Cvar_Get ("v_centermove", "0.15", 0, "client center moving" );
v_centerspeed = Cvar_Get ("v_centerspeed", "500", 0, "client center speed" );
cl_nodelta = Cvar_Get ("cl_nodelta", "0", 0, "disable delta-compression for usercommnds" );
freelook = Cvar_Get( "freelook", "1", CVAR_ARCHIVE, "enables mouse look" );
lookspring = Cvar_Get( "lookspring", "0", CVAR_ARCHIVE, "allow look spring" );
lookstrafe = Cvar_Get( "lookstrafe", "0", CVAR_ARCHIVE, "allow look strafe" );
m_pitch = Cvar_Get( "m_pitch", "0.022", CVAR_ARCHIVE, "mouse pitch value" );
m_yaw = Cvar_Get( "m_yaw", "0.022", 0, "mouse yaw value" );
m_forward = Cvar_Get( "m_forward", "1", 0, "mouse forward speed" );
m_side = Cvar_Get( "m_side", "1", 0, "mouse side speed" );
m_pitch = Cvar_Get ("m_pitch", "0.022", CVAR_ARCHIVE, "mouse pitch value" );
m_yaw = Cvar_Get ("m_yaw", "0.022", 0, "mouse yaw value" );
m_forward = Cvar_Get ("m_forward", "1", 0, "mouse forward speed" );
m_side = Cvar_Get ("m_side", "1", 0, "mouse side speed" );
Cmd_AddCommand( "centerview", IN_CenterView, "gradually recenter view (stop looking up/down)" );
Cmd_AddCommand ("centerview", IN_CenterView, "gradually recenter view (stop looking up/down)" );
// input commands
Cmd_AddCommand( "+moveup", IN_UpDown, "swim upward" );
Cmd_AddCommand( "-moveup",IN_UpUp, "stop swimming upward" );
Cmd_AddCommand( "+movedown",IN_DownDown, "swim downward" );
Cmd_AddCommand( "-movedown",IN_DownUp, "stop swimming downward" );
Cmd_AddCommand( "+left",IN_LeftDown, "turn left" );
Cmd_AddCommand( "-left",IN_LeftUp, "stop turning left" );
Cmd_AddCommand( "+right",IN_RightDown, "turn right" );
Cmd_AddCommand( "-right",IN_RightUp, "stop turning right" );
Cmd_AddCommand( "+forward",IN_ForwardDown, "move forward" );
Cmd_AddCommand( "-forward",IN_ForwardUp, "stop moving forward" );
Cmd_AddCommand( "+back",IN_BackDown, "move backward" );
Cmd_AddCommand( "-back",IN_BackUp, "stop moving backward" );
Cmd_AddCommand( "+lookup", IN_LookupDown, "look upward" );
Cmd_AddCommand( "-lookup", IN_LookupUp, "stop looking upward" );
Cmd_AddCommand( "+lookdown", IN_LookdownDown, "look downward" );
Cmd_AddCommand( "-lookdown", IN_LookdownUp, "stop looking downward" );
Cmd_AddCommand( "+strafe", IN_StrafeDown, "activate strafing mode (move instead of turn)\n" );
Cmd_AddCommand( "-strafe", IN_StrafeUp, "deactivate strafing mode" );
Cmd_AddCommand( "+moveleft", IN_MoveleftDown, "strafe left" );
Cmd_AddCommand( "-moveleft", IN_MoveleftUp, "stop strafing left" );
Cmd_AddCommand( "+moveright", IN_MoverightDown, "strafe right" );
Cmd_AddCommand( "-moveright", IN_MoverightUp, "stop strafing right" );
Cmd_AddCommand( "+speed", IN_SpeedDown, "activate run mode (faster movement and turning)" );
Cmd_AddCommand( "-speed", IN_SpeedUp, "deactivate run mode" );
Cmd_AddCommand( "+attack", IN_AttackDown, "begin firing" );
Cmd_AddCommand( "-attack", IN_AttackUp, "stop firing" );
Cmd_AddCommand( "+attack2", IN_Attack2Down, "begin alternate firing" );
Cmd_AddCommand( "-attack2", IN_Attack2Up, "stop alternate firing" );
Cmd_AddCommand( "+use", IN_UseDown, "use item (doors, monsters, inventory, etc)" );
Cmd_AddCommand( "-use", IN_UseUp, "stop using item" );
Cmd_AddCommand( "+jump", IN_JumpDown, "jump" );
Cmd_AddCommand( "-jump", IN_JumpUp, "end jump (so you can jump again)" );
Cmd_AddCommand( "+duck", IN_DuckDown, "duck" );
Cmd_AddCommand( "-duck", IN_DuckUp, "end duck (so you can duck again)" );
Cmd_AddCommand( "+klook", IN_KLookDown, "activate keyboard looking mode, do not recenter view" );
Cmd_AddCommand( "-klook", IN_KLookUp, "deactivate keyboard looking mode" );
Cmd_AddCommand( "+reload", IN_ReloadDown, "reload current weapon" );
Cmd_AddCommand( "-reload", IN_ReloadUp, "continue reload weapon" );
Cmd_AddCommand( "+mlook", IN_MLookDown, "activate mouse looking mode, do not recenter view" );
Cmd_AddCommand( "-mlook", IN_MLookUp, "deactivate mouse looking mode" );
Cmd_AddCommand( "+alt1", IN_Alt1Down, "hold modyifycator" );
Cmd_AddCommand( "-alt1", IN_Alt1Up, "release modifycator" );
Cmd_AddCommand( "+break",IN_BreakDown, "cancel" );
Cmd_AddCommand( "-break",IN_BreakUp, "stop cancel" );
Cmd_AddCommand ("+moveup",IN_UpDown, "swim upward");
Cmd_AddCommand ("-moveup",IN_UpUp, "stop swimming upward");
Cmd_AddCommand ("+movedown",IN_DownDown, "swim downward");
Cmd_AddCommand ("-movedown",IN_DownUp, "stop swimming downward");
Cmd_AddCommand ("+left",IN_LeftDown, "turn left");
Cmd_AddCommand ("-left",IN_LeftUp, "stop turning left");
Cmd_AddCommand ("+right",IN_RightDown, "turn right");
Cmd_AddCommand ("-right",IN_RightUp, "stop turning right");
Cmd_AddCommand ("+forward",IN_ForwardDown, "move forward");
Cmd_AddCommand ("-forward",IN_ForwardUp, "stop moving forward");
Cmd_AddCommand ("+back",IN_BackDown, "move backward");
Cmd_AddCommand ("-back",IN_BackUp, "stop moving backward");
Cmd_AddCommand ("+lookup", IN_LookupDown, "look upward");
Cmd_AddCommand ("-lookup", IN_LookupUp, "stop looking upward");
Cmd_AddCommand ("+lookdown", IN_LookdownDown, "look downward");
Cmd_AddCommand ("-lookdown", IN_LookdownUp, "stop looking downward");
Cmd_AddCommand ("+strafe", IN_StrafeDown, "activate strafing mode (move instead of turn)\n");
Cmd_AddCommand ("-strafe", IN_StrafeUp, "deactivate strafing mode");
Cmd_AddCommand ("+moveleft", IN_MoveleftDown, "strafe left");
Cmd_AddCommand ("-moveleft", IN_MoveleftUp, "stop strafing left");
Cmd_AddCommand ("+moveright", IN_MoverightDown, "strafe right");
Cmd_AddCommand ("-moveright", IN_MoverightUp, "stop strafing right");
Cmd_AddCommand ("+speed", IN_SpeedDown, "activate run mode (faster movement and turning)");
Cmd_AddCommand ("-speed", IN_SpeedUp, "deactivate run mode");
Cmd_AddCommand ("+attack", IN_AttackDown, "begin firing");
Cmd_AddCommand ("-attack", IN_AttackUp, "stop firing");
Cmd_AddCommand ("+attack2", IN_Attack2Down, "begin alternate firing");
Cmd_AddCommand ("-attack2", IN_Attack2Up, "stop alternate firing");
Cmd_AddCommand ("+use", IN_UseDown, "use item (doors, monsters, inventory, etc)" );
Cmd_AddCommand ("-use", IN_UseUp, "stop using item" );
Cmd_AddCommand ("+jump", IN_JumpDown, "jump" );
Cmd_AddCommand ("-jump", IN_JumpUp, "end jump (so you can jump again)");
Cmd_AddCommand ("+duck", IN_DuckDown, "duck" );
Cmd_AddCommand ("-duck", IN_DuckUp, "end duck (so you can duck again)");
Cmd_AddCommand ("+klook", IN_KLookDown, "activate keyboard looking mode, do not recenter view");
Cmd_AddCommand ("-klook", IN_KLookUp, "deactivate keyboard looking mode");
Cmd_AddCommand ("+reload", IN_ReloadDown, "reload current weapon" );
Cmd_AddCommand ("-reload", IN_ReloadUp, "continue reload weapon" );
Cmd_AddCommand ("+mlook", IN_MLookDown, "activate mouse looking mode, do not recenter view" );
Cmd_AddCommand ("-mlook", IN_MLookUp, "deactivate mouse looking mode" );
Cmd_AddCommand ("+alt1", IN_Alt1Down, "hold modyifycator" );
Cmd_AddCommand ("-alt1", IN_Alt1Up, "release modifycator" );
Cmd_AddCommand ("+break",IN_BreakDown, "cancel" );
Cmd_AddCommand ("-break",IN_BreakUp, "stop cancel" );
}
/*
============
CL_InitServerCommands
FIXME: move this stuff into client.dll
============
*/
void CL_InitServerCommands( void )
{
Cmd_AddCommand( "impulse", NULL, "send impulse to a client" );
Cmd_AddCommand( "noclip", NULL, "enable or disable no clipping mode" );
Cmd_AddCommand( "fullupdate", NULL, "re-init HUD on start demo recording" );
Cmd_AddCommand( "give", NULL, "give specified item or weapon" );
Cmd_AddCommand( "intermission", NULL, "go to intermission" );
Cmd_AddCommand( "god", NULL, "classic cheat" );
Cmd_AddCommand ("impulse", NULL, "send impulse to a client" );
Cmd_AddCommand ("noclip", NULL, "enable or disable no clipping mode" );
Cmd_AddCommand ("fullupdate", NULL, "re-init HUD on start demo recording" );
Cmd_AddCommand ("give", NULL, "give specified item or weapon" );
Cmd_AddCommand ("intermission", NULL, "go to intermission" );
Cmd_AddCommand ("god", NULL, "classic cheat" );
}
/*
@ -865,45 +734,41 @@ CL_ShutdownInput
*/
void CL_ShutdownInput( void )
{
Cmd_RemoveCommand( "centerview" );
Cmd_RemoveCommand ("centerview" );
// input commands
Cmd_RemoveCommand( "+moveup" );
Cmd_RemoveCommand( "-moveup" );
Cmd_RemoveCommand( "+movedown" );
Cmd_RemoveCommand( "-movedown" );
Cmd_RemoveCommand( "+left" );
Cmd_RemoveCommand( "-left" );
Cmd_RemoveCommand( "+right" );
Cmd_RemoveCommand( "-right" );
Cmd_RemoveCommand( "+forward" );
Cmd_RemoveCommand( "-forward" );
Cmd_RemoveCommand( "+back" );
Cmd_RemoveCommand( "-back" );
Cmd_RemoveCommand( "+lookup" );
Cmd_RemoveCommand( "-lookup" );
Cmd_RemoveCommand( "+lookdown" );
Cmd_RemoveCommand( "-lookdown" );
Cmd_RemoveCommand( "+strafe" );
Cmd_RemoveCommand( "-strafe" );
Cmd_RemoveCommand( "+moveleft" );
Cmd_RemoveCommand( "-moveleft" );
Cmd_RemoveCommand( "+moveright" );
Cmd_RemoveCommand( "-moveright" );
Cmd_RemoveCommand( "+speed" );
Cmd_RemoveCommand( "-speed" );
Cmd_RemoveCommand( "+attack" );
Cmd_RemoveCommand( "-attack" );
Cmd_RemoveCommand( "+attack2" );
Cmd_RemoveCommand( "-attack2" );
Cmd_RemoveCommand( "+reload" );
Cmd_RemoveCommand( "-reload" );
Cmd_RemoveCommand( "+use" );
Cmd_RemoveCommand( "-use" );
Cmd_RemoveCommand( "+mlook" );
Cmd_RemoveCommand( "-mlook" );
Cmd_RemoveCommand( "+alt1" );
Cmd_RemoveCommand( "-alt1" );
Cmd_RemoveCommand( "+break" );
Cmd_RemoveCommand( "-break" );
Cmd_RemoveCommand ("+moveup" );
Cmd_RemoveCommand ("-moveup" );
Cmd_RemoveCommand ("+movedown" );
Cmd_RemoveCommand ("-movedown" );
Cmd_RemoveCommand ("+left" );
Cmd_RemoveCommand ("-left" );
Cmd_RemoveCommand ("+right" );
Cmd_RemoveCommand ("-right" );
Cmd_RemoveCommand ("+forward" );
Cmd_RemoveCommand ("-forward" );
Cmd_RemoveCommand ("+back" );
Cmd_RemoveCommand ("-back" );
Cmd_RemoveCommand ("+lookup" );
Cmd_RemoveCommand ("-lookup" );
Cmd_RemoveCommand ("+lookdown" );
Cmd_RemoveCommand ("-lookdown" );
Cmd_RemoveCommand ("+strafe" );
Cmd_RemoveCommand ("-strafe" );
Cmd_RemoveCommand ("+moveleft" );
Cmd_RemoveCommand ("-moveleft" );
Cmd_RemoveCommand ("+moveright" );
Cmd_RemoveCommand ("-moveright" );
Cmd_RemoveCommand ("+speed" );
Cmd_RemoveCommand ("-speed" );
Cmd_RemoveCommand ("+attack" );
Cmd_RemoveCommand ("-attack" );
Cmd_RemoveCommand ("+attack2" );
Cmd_RemoveCommand ("-attack2" );
Cmd_RemoveCommand ("+reload" );
Cmd_RemoveCommand ("-reload" );
Cmd_RemoveCommand ("+use" );
Cmd_RemoveCommand ("-use" );
Cmd_RemoveCommand ("+mlook" );
Cmd_RemoveCommand ("-mlook" );
}

View File

@ -26,8 +26,6 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
cvar_t *rcon_client_password;
cvar_t *rcon_address;
cvar_t *cl_maxpackets;
cvar_t *cl_packetdup;
cvar_t *cl_footsteps;
cvar_t *cl_timeout;
cvar_t *cl_predict;
@ -37,7 +35,6 @@ cvar_t *cl_maxfps;
cvar_t *cl_particles;
cvar_t *cl_particlelod;
cvar_t *cl_timenudge;
cvar_t *cl_shownet;
cvar_t *cl_showmiss;
cvar_t *cl_showclamp;
@ -459,16 +456,15 @@ CL_Reconnect_f
The server is changing levels
=================
*/
void CL_Reconnect_f( void )
void CL_Reconnect_f (void)
{
// if we are downloading, we don't change!
// this so we don't suddenly stop downloading a map
// if we are downloading, we don't change! This so we don't suddenly stop downloading a map
if( cls.download ) return;
S_StopAllSounds ();
if( cls.state == ca_connected )
{
Msg ("reconnecting...\n");
Msg( "reconnecting...\n" );
cls.state = ca_connected;
MSG_WriteByte( &cls.netchan.message, clc_stringcmd );
MSG_Print( &cls.netchan.message, "new" );
@ -665,9 +661,8 @@ void CL_ParseStatusMessage( netadr_t from, sizebuf_t *msg )
s = MSG_ReadString( msg );
Msg( "%s\n", s );
UI_AddServerToList( from, s );
// CL_ParseServerStatus( NET_AdrToString(from), s );
Msg ("%s\n", s);
CL_ParseServerStatus( NET_AdrToString(from), s );
}
//===================================================================
@ -918,7 +913,7 @@ void CL_ReadPackets( void )
{
if( ++cl.timeoutcount > 5 ) // timeoutcount saves debugger
{
Msg( "\nServer connection timed out.\n" );
Msg ("\nServer connection timed out.\n");
CL_Disconnect();
return;
}
@ -1079,11 +1074,9 @@ void CL_InitLocal( void )
// register our variables
cl_footsteps = Cvar_Get( "cl_footsteps", "1", 0, "disables player footsteps" );
cl_predict = Cvar_Get( "cl_predict", "1", CVAR_ARCHIVE, "disables client movement prediction" );
cl_maxfps = Cvar_Get( "cl_maxfps", "100", CVAR_ARCHIVE, "maximum client fps (refresh framerate too)" );
cl_maxfps = Cvar_Get( "cl_maxfps", "1000", 0, "maximum client fps" );
cl_particles = Cvar_Get( "cl_particles", "1", CVAR_ARCHIVE, "disables particle effects" );
cl_particlelod = Cvar_Get( "cl_lod_particle", "0", CVAR_ARCHIVE, "enables particle LOD (1, 2, 3)" );
cl_maxpackets = Cvar_Get( "cl_maxpackets", "30", CVAR_ARCHIVE, "usercmds sending frequency rate" );
cl_packetdup = Cvar_Get( "cl_packetdup", "2", CVAR_ARCHIVE, "number of repeating packets (2 is Q2 default value)" );
cl_upspeed = Cvar_Get( "cl_upspeed", "200", 0, "client upspeed limit" );
cl_forwardspeed = Cvar_Get( "cl_forwardspeed", "200", 0, "client forward speed limit" );
@ -1096,9 +1089,8 @@ void CL_InitLocal( void )
cl_shownet = Cvar_Get( "cl_shownet", "0", 0, "client show network packets" );
cl_showmiss = Cvar_Get( "cl_showmiss", "0", 0, "client show network errors" );
cl_showclamp = Cvar_Get( "cl_showclamp", "1", 0, "show client clamping" );
cl_showclamp = Cvar_Get( "cl_showclamp", "0", CVAR_ARCHIVE, "show client clamping" );
cl_timeout = Cvar_Get( "cl_timeout", "120", 0, "connect timeout (in-seconds)" );
cl_timenudge = Cvar_Get( "cl_timenudge", "0", CVAR_TEMP, "nudge server time to specified value" );
rcon_client_password = Cvar_Get( "rcon_password", "", 0, "remote control client password" );
rcon_address = Cvar_Get( "rcon_address", "", 0, "remote control address" );
@ -1165,22 +1157,6 @@ void CL_SendCommand( void )
// resend a connection request if necessary
CL_CheckForResend ();
CL_SetClientTime ();
}
/*
==================
CL_CalcFrameTime
==================
*/
double CL_CalcFrameTime( void )
{
if( cls.state == ca_connected )
return 0.1f; // don't flood packets out while connecting
if( cl_maxfps->value )
return 1.0 / (double)cl_maxfps->value;
return 0;
}
/*
@ -1191,29 +1167,17 @@ CL_Frame
*/
void CL_Frame( double time )
{
static double extratime = 0.001;
static double frametime;
double minframetime;
if( host.type == HOST_DEDICATED )
return;
extratime += time;
minframetime = CL_CalcFrameTime();
if( extratime < minframetime )
return;
// decide the simulation time
frametime = extratime - 0.001;
if( frametime < minframetime ) frametime = minframetime;
extratime -= frametime;
// decide the simulation time
cl.oldtime = cl.time;
cl.time += time; // can be merged by cl.frame.servertime
cls.realtime += time;
cls.realframetime = cls.frametime = frametime;
cls.frametime = time;
if( cls.frametime > (1.0f / 5)) cls.frametime = (1.0f / 5);
cl.time = bound( cl.frame.servertime - cl.serverframetime, cl.time, cl.frame.servertime );
if( cls.frametime > 0.2f ) cls.frametime = 0.2f;
// if in the debugger last frame, don't timeout
if( time > 5.0f ) cls.netchan.last_received = Sys_DoubleTime ();

View File

@ -248,6 +248,7 @@ void CL_ParseServerData( sizebuf_t *msg )
Host_Error( "Server use invalid protocol (%i should be %i)\n", i, PROTOCOL_VERSION );
cl.servercount = MSG_ReadLong( msg );
cl.serverframetime = MSG_ReadFloat( msg );
cl.playernum = MSG_ReadShort( msg );
str = MSG_ReadString( msg );
@ -261,7 +262,7 @@ void CL_ParseServerData( sizebuf_t *msg )
if( i == 3 )
{
Cvar_Set( "cl_levelshot_name", MAP_DEFAULT_SHADER ); // render a black screen
clgame.need_levelshot = true; // make levelshot
cl.need_levelshot = true; // make levelshot
}
// seperate the printfs so the server message can have a color
Msg("\35\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\37\n");

View File

@ -37,7 +37,7 @@ void CL_CheckPredictionError( void )
else
{
if (cl_showmiss->value && (delta[0] || delta[1] || delta[2]))
Msg ("prediction miss on %i: %i\n", cl.frame.msgnum, delta[0] + delta[1] + delta[2]);
Msg ("prediction miss on %i: %i\n", cl.frame.serverframe, delta[0] + delta[1] + delta[2]);
VectorCopy (cl.frame.ps.origin, cl.predicted_origins[frame]);

View File

@ -323,7 +323,7 @@ void SCR_DrawFPS( void )
if( cls.state != ca_active ) return;
if( !cl_showfps->integer ) return;
if( clgame.need_levelshot ) return;
if( cl.need_levelshot ) return;
newtime = Sys_DoubleTime();
if (newtime >= nexttime)

View File

@ -73,9 +73,9 @@ void V_SetupRefDef( void )
// find the previous frame to interpolate from
ps = &cl.frame.ps;
i = (cl.frame.msgnum - 1) & UPDATE_MASK;
i = (cl.frame.serverframe - 1) & UPDATE_MASK;
oldframe = &cl.frames[i];
if( oldframe->msgnum != cl.frame.msgnum - 1 || !oldframe->valid )
if( oldframe->serverframe != cl.frame.serverframe-1 || !oldframe->valid )
oldframe = &cl.frame; // previous frame was dropped or invalid
ops = &oldframe->ps;
@ -144,7 +144,7 @@ void V_SetupRefDef( void )
// smooth out stair climbing
delta = cls.realtime - cl.predicted_step_time;
if( delta < cl.frametime ) cl.refdef.vieworg[2] -= cl.predicted_step * (cl.frametime - delta) * 0.01f;
if( delta < cl.serverframetime ) cl.refdef.vieworg[2] -= cl.predicted_step * (cl.serverframetime - delta) * 0.01f;
}
}

View File

@ -42,34 +42,27 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
typedef struct frame_s
{
bool valid; // cleared if delta parsing was invalid
int ping; // frame ping
int msgnum; // server message number
float servertime; // server time
int deltaframe; // message number the delta is from
int serverframe;
double servertime;
int deltaframe;
byte areabits[MAX_MAP_AREA_BYTES]; // portalarea visibility bits
int num_entities;
int parse_entities; // non-masked index into cl_parse_entities array
entity_state_t ps; // player state
} frame_t;
typedef struct
{
int cmd_number; // cl.cmd_number when packet was sent
float servertime; // usercmd->servertime when packet was sent
float realtime; // cls.realtime when packet was sent
} outframe_t;
// console stuff
typedef struct field_s
{
int cursor;
int scroll;
int widthInChars;
char buffer[MAX_EDIT_LINE];
int cursor;
int scroll;
int widthInChars;
char buffer[MAX_EDIT_LINE];
int maxchars; // menu stuff
} field_t;
#define CMD_BACKUP 64 // allow a lot of command backups for very fast systems
#define CMD_MASK (CMD_BACKUP - 1)
#define CMD_BACKUP 64 // allow a lot of command backups for very fast systems
#define CMD_MASK (CMD_BACKUP - 1)
//
// the client_t structure is wiped completely at every
@ -81,54 +74,13 @@ typedef struct
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 has_newframe; // client has new frame from server
bool frame_extrapolate;
int parse_entities; // index (not anded off) into cl_parse_entities[]
frame_t frame; // received from server
frame_t *oldframe; // old frame to interpolate from
frame_t frames[UPDATE_BACKUP];
int cmd_number;
usercmd_t cmds[CMD_BACKUP]; // each mesage will send several old cmds
outframe_t outframes[UPDATE_BACKUP]; // information about each packet we have sent out
// mouse current position
int mouse_x[2];
int mouse_y[2];
int mouse_step;
int render_flags; // accumulated renderflags (will be clearing at end of frame)
ref_params_t refdef; // shared refdef
edict_t viewent; // viewmodel
client_data_t data; // hud data
double time; // matched with sv.time
double oldtime; // to prevent time from flowing bakcwards
float time_delta; // cl.time = cls.realtime + cl.time_delta
float frametime; // cl.frametime = cl.time - cl.oldtime
//
// server state information
//
int playernum;
int servercount; // server identification for prespawns
char configstrings[MAX_CONFIGSTRINGS][CS_SIZE];
//
// locally derived information from server state
//
cmodel_t *worldmodel;
cmodel_t *models[MAX_MODELS];
string_t edict_classnames[MAX_CLASSNAMES];
sound_t sound_precache[MAX_SOUNDS];
shader_t decal_shaders[MAX_DECALS];
// old stuff attempt to be removed
double mtime[2]; // the timestamp of the last two messages
vec3_t predicted_origins[CMD_BACKUP];// for debug comparing against server
float cmd_time[CMD_BACKUP]; // for netgraph ping calculation
int predicted_origins[CMD_BACKUP][3];// for debug comparing against server
float predicted_step; // for stair up smoothing
uint predicted_step_time;
@ -136,6 +88,49 @@ typedef struct
vec3_t predicted_origin; // generated by CL_PredictMovement
vec3_t predicted_angles;
vec3_t prediction_error;
frame_t frame; // received from server
frame_t *oldframe; // previous frame to lerping from
int surpressCount; // number of messages rate supressed
frame_t frames[UPDATE_BACKUP];
// mouse current position
int mouse_x[2];
int mouse_y[2];
int mouse_step;
float mouse_sens;
double mtime[2]; // the timestamp of the last two messages
double time; // this is the time value that the client
double oldtime; // cl.oldtime
// is rendering at. always <= cls.realtime
int render_flags; // clearing at end of frame
ref_params_t refdef; // shared refdef
edict_t viewent; // viewmodel
client_data_t data; // hud data
// misc 2d drawing stuff
float centerPrintTime;
int centerPrintCharWidth;
int centerPrintY;
char centerPrint[1024];
int centerPrintLines;
bool need_levelshot;
//
// server state information
//
int playernum;
int servercount; // server identification for prespawns
float serverframetime; // server frametime
char configstrings[MAX_CONFIGSTRINGS][CS_SIZE];
// locally derived information from server state
cmodel_t *models[MAX_MODELS];
cmodel_t *worldmodel;
string_t edict_classnames[MAX_CLASSNAMES];
sound_t sound_precache[MAX_SOUNDS];
shader_t decal_shaders[MAX_DECALS];
} client_t;
extern client_t cl;
@ -152,11 +147,11 @@ of server connections
typedef enum
{
ca_uninitialized = 0,
ca_disconnected, // not talking to a server
ca_connecting, // sending request packets to the server
ca_connected, // netchan_t established, waiting for svc_serverdata
ca_active, // game views should be displayed
ca_cinematic, // playing a cinematic, not connected to a server
ca_disconnected, // not talking to a server
ca_connecting, // sending request packets to the server
ca_connected, // netchan_t established, waiting for svc_serverdata
ca_active, // game views should be displayed
ca_cinematic, // playing a cinematic, not connected to a server
} connstate_t;
typedef enum
@ -165,12 +160,13 @@ typedef enum
dl_model,
dl_sound,
dl_generic,
} dltype_t; // download type
} dltype_t; // download type
// cl_private_edict_t
struct cl_priv_s
{
int msgnum; // if not current, this ent isn't in the frame
int serverframe; // if not current, this ent isn't in the frame
entity_state_t baseline; // delta from this if not from a previous frame
entity_state_t current;
entity_state_t prev; // will always be valid, but might just be a copy of current
@ -199,9 +195,9 @@ typedef enum { key_console = 0, key_game, key_hudmenu, key_message, key_menu } k
typedef struct
{
char name[CS_SIZE];
int number; // svc_ number
int size; // if size == -1, size come from first byte after svcnum
pfnUserMsgHook func; // user-defined function
int number; // svc_ number
int size; // if size == -1, size come from first byte after svcnum
pfnUserMsgHook func; // user-defined function
} user_message_t;
typedef struct
@ -223,14 +219,6 @@ typedef struct
int numMessages; // actual count of user messages
int hStringTable; // stringtable handle
// misc 2d drawing stuff
float centerPrintTime;
int centerPrintCharWidth;
int centerPrintY;
char centerPrint[1024];
int centerPrintLines;
bool need_levelshot;
// movement values from server
movevars_t movevars;
} clgame_static_t;
@ -246,7 +234,6 @@ typedef struct
int framecount;
double frametime; // seconds since last frame
double realframetime; // console and other things
double realtime;
int quakePort; // a 16 bit value that allows quake servers
@ -314,9 +301,6 @@ extern cvar_t *cl_font;
extern cvar_t *cl_anglespeedkey;
extern cvar_t *cl_timenudge;
extern cvar_t *cl_packetdup;
extern cvar_t *cl_maxpackets;
extern cvar_t *cl_showmiss;
extern cvar_t *cl_showclamp;
extern cvar_t *cl_particles;
@ -571,11 +555,10 @@ int CL_ContentsMask( const edict_t *passedict );
trace_t CL_Trace( const vec3_t s1, const vec3_t m1, const vec3_t m2, const vec3_t s2, int type, edict_t *e, int mask );
//
// cl_frame.c
// cl_ents.c
//
void CL_GetEntitySoundSpatialization( int ent, vec3_t origin, vec3_t velocity );
void CL_AddLoopingSounds( void );
void CL_SetClientTime( void );
//
// cl_fx.c

View File

@ -95,10 +95,14 @@ typedef struct host_parm_s
dword errorframe; // to avoid each-frame host error
string finalmsg; // server shutdown final message
double time;
double oldtime;
double frametime; // time between engine frames
dword framecount; // global framecount
HWND hWnd; // main window
int developer; // show all developer's message
word max_edicts; // FIXME
word max_edicts;
} host_parm_t;
extern host_parm_t host;

View File

@ -611,13 +611,13 @@ void Con_RunConsole( void )
if (con.finalFrac < con.displayFrac)
{
con.displayFrac -= con_speed->value * cls.realframetime;
con.displayFrac -= con_speed->value * cls.frametime;
if( con.finalFrac > con.displayFrac )
con.displayFrac = con.finalFrac;
}
else if( con.finalFrac > con.displayFrac )
{
con.displayFrac += con_speed->value * cls.realframetime;
con.displayFrac += con_speed->value * cls.frametime;
if( con.finalFrac < con.displayFrac )
con.displayFrac = con.finalFrac;
}

View File

@ -19,7 +19,6 @@ static net_field_t ent_fields[] =
{ ES_FIELD(ed_flags), NET_BYTE, true }, // stateflags_t #0 (4 bytes)
{ ES_FIELD(classname), NET_WORD, false },
{ ES_FIELD(soundindex), NET_WORD, false }, // 512 sounds ( OpenAL software limit is 255 )
{ ES_FIELD(cmdtime), NET_FLOAT, true }, // client only stuff
{ ES_FIELD(origin[0]), NET_FLOAT, false },
{ ES_FIELD(origin[1]), NET_FLOAT, false },
{ ES_FIELD(origin[2]), NET_FLOAT, false },

View File

@ -6,13 +6,13 @@
--------------------Configuration: engine - Win32 Debug--------------------
</h3>
<h3>Command Lines</h3>
Creating temporary file "C:\DOCUME~1\MIKE~1.MIK\LOCALS~1\Temp\RSP7BE7.tmp" with contents
Creating temporary file "C:\DOCUME~1\MIKE~1.MIK\LOCALS~1\Temp\RSP7FB0.tmp" with contents
[
/nologo /MDd /W3 /Gm /Gi /GX /ZI /Od /I "./" /I "common" /I "server" /I "client" /I "uimenu" /I "../public" /I "../common" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FR"..\temp\engine\!debug/" /Fo"..\temp\engine\!debug/" /Fd"..\temp\engine\!debug/" /FD /c
"D:\Xash3D\src_main\engine\client\cl_frame.c"
]
Creating command line "cl.exe @C:\DOCUME~1\MIKE~1.MIK\LOCALS~1\Temp\RSP7BE7.tmp"
Creating temporary file "C:\DOCUME~1\MIKE~1.MIK\LOCALS~1\Temp\RSP7BE8.tmp" with contents
Creating command line "cl.exe @C:\DOCUME~1\MIKE~1.MIK\LOCALS~1\Temp\RSP7FB0.tmp"
Creating temporary file "C:\DOCUME~1\MIKE~1.MIK\LOCALS~1\Temp\RSP7FB1.tmp" with contents
[
user32.lib msvcrtd.lib /nologo /subsystem:windows /dll /incremental:yes /pdb:"..\temp\engine\!debug/engine.pdb" /debug /machine:I386 /nodefaultlib:"msvcrt.lib" /out:"..\temp\engine\!debug/engine.dll" /implib:"..\temp\engine\!debug/engine.lib" /pdbtype:sept
"\Xash3D\src_main\temp\engine\!debug\cinematic.obj"
@ -73,13 +73,13 @@ user32.lib msvcrtd.lib /nologo /subsystem:windows /dll /incremental:yes /pdb:"..
"\Xash3D\src_main\temp\engine\!debug\ui_singleplayer.obj"
"\Xash3D\src_main\temp\engine\!debug\ui_video.obj"
]
Creating command line "link.exe @C:\DOCUME~1\MIKE~1.MIK\LOCALS~1\Temp\RSP7BE8.tmp"
Creating temporary file "C:\DOCUME~1\MIKE~1.MIK\LOCALS~1\Temp\RSP7BE9.bat" with contents
Creating command line "link.exe @C:\DOCUME~1\MIKE~1.MIK\LOCALS~1\Temp\RSP7FB1.tmp"
Creating temporary file "C:\DOCUME~1\MIKE~1.MIK\LOCALS~1\Temp\RSP7FB2.bat" with contents
[
@echo off
copy \Xash3D\src_main\temp\engine\!debug\engine.dll "D:\Xash3D\bin\engine.dll"
]
Creating command line "C:\DOCUME~1\MIKE~1.MIK\LOCALS~1\Temp\RSP7BE9.bat"
Creating command line "C:\DOCUME~1\MIKE~1.MIK\LOCALS~1\Temp\RSP7FB2.bat"
Compiling...
cl_frame.c
Linking...

View File

@ -24,8 +24,12 @@ dll_info_t render_dll = { "render.dll", NULL, "CreateAPI", NULL, NULL, 0, sizeof
dll_info_t vprogs_dll = { "vprogs.dll", NULL, "CreateAPI", NULL, NULL, 1, sizeof(vprogs_exp_t), sizeof(stdlib_api_t) };
dll_info_t vsound_dll = { "vsound.dll", NULL, "CreateAPI", NULL, NULL, 0, sizeof(vsound_exp_t), sizeof(stdlib_api_t) };
cvar_t *timescale;
cvar_t *host_serverstate;
cvar_t *host_cheats;
cvar_t *host_maxfps;
cvar_t *host_minfps;
cvar_t *host_ticrate;
cvar_t *host_framerate;
cvar_t *host_maxclients;
cvar_t *host_registered;
@ -308,6 +312,40 @@ void Host_EventLoop( void )
}
}
/*
===================
Host_FilterTime
Returns false if the time is too short to run a frame
===================
*/
bool Host_FilterTime( double time )
{
host.time += time;
if( host_maxfps->modified )
{
if( host_maxfps->integer < 10 )
Cvar_Set( "host_maxfps", "10" );
else if( host_maxfps->integer > 1000 )
Cvar_Set( "host_maxfps", "1000" );
host_maxfps->modified = false;
}
if( host.time - host.oldtime < (1.0 / host_maxfps->value))
return false; // framerate is too high
host.frametime = host.time - host.oldtime;
host.oldtime = host.time;
if( host_framerate->value > 0 )
host.frametime = host_framerate->value;
host.frametime = bound( 0.001, host.frametime, 0.1 );
return true;
}
/*
=================
Host_Frame
@ -318,16 +356,18 @@ void Host_Frame( double time )
if( setjmp( host.abortframe ))
return;
rand(); // keep the random time dependent
// decide the simulation time
if( !Host_FilterTime( time ))
return;
Host_EventLoop (); // process all system events
Cbuf_Execute (); // execute commands
if( host_framerate->value )
time = bound( 0.001, host_framerate->value, 1 );
SV_Frame ( time ); // server frame
CL_Frame ( time ); // client frame
VM_Frame ( time ); // vprogs frame
SV_Frame ( host.frametime ); // server frame
CL_Frame ( host.frametime ); // client frame
VM_Frame ( host.frametime ); // vprogs frame
host.framecount++;
}
@ -476,12 +516,16 @@ void Host_Init( const int argc, const char **argv )
}
host_cheats = Cvar_Get( "sv_cheats", "1", CVAR_SYSTEMINFO, "allow cheat variables to enable" );
host_minfps = Cvar_Get( "host_minfps", "10", CVAR_ARCHIVE, "host fps lower limit" );
host_maxfps = Cvar_Get( "host_maxfps", "100", CVAR_ARCHIVE, "host fps upper limit" );
host_ticrate = Cvar_Get( "sys_ticrate", "0.0138889", CVAR_SYSTEMINFO, "how long a server frame is in seconds" );
host_framerate = Cvar_Get( "host_framerate", "0", 0, "locks frame timing to this value in seconds" );
host_maxclients = Cvar_Get("host_maxclients", "1", CVAR_SERVERINFO|CVAR_LATCH, "server maxplayers limit" );
host_serverstate = Cvar_Get("host_serverstate", "0", CVAR_SERVERINFO, "displays current server state" );
host_registered = Cvar_Get( "registered", "1", CVAR_SYSTEMINFO, "indicate shareware version of game" );
timescale = Cvar_Get( "timescale", "1.0", 0, "slow-mo timescale" );
s = va( "^1Xash %g ^3%s", GI->version, buildstring );
s = va("^1Xash %g ^3%s", GI->version, buildstring );
Cvar_Get( "version", s, CVAR_SERVERINFO|CVAR_INIT, "engine current version" );
NET_Init();
@ -520,7 +564,7 @@ void Host_Main( void )
while( host.type != HOST_OFFLINE )
{
IN_Frame();
do
{
// timer resoultion at 1 msec
@ -529,7 +573,6 @@ void Host_Main( void )
} while( time < 0.001 );
Host_Frame( time );
oldtime = newtime;
}
}

View File

@ -68,7 +68,6 @@ typedef struct server_s
bool loadgame; // client begins should reuse existing entity
double time; // always sv.framenum * 50 msec
double oldtime; // prev.frame time
double frametime;
int framenum;
int net_framenum;
@ -93,17 +92,13 @@ typedef struct server_s
typedef struct
{
entity_state_t ps; // player state
int index; // client edict index
byte areabits[MAX_MAP_AREA_BYTES]; // portalarea visibility bits
int areabits_size;
int num_entities;
int first_entity; // into the circular sv_packet_entities[]
double senttime; // time the message was transmitted
float message_sent; // time the message was transmitted
float message_acked; // time the message was acked
size_t message_size; // used to rate drop packets
int index; // client edict index
} client_frame_t;
typedef struct sv_client_s
@ -111,15 +106,22 @@ typedef struct sv_client_s
cl_state_t state;
char userinfo[MAX_INFO_STRING]; // name, etc
int lastframe; // for delta compression
int skipframes; // client synchronyze with phys frame
usercmd_t lastcmd; // for filling in big drops
int spectator; // non-interactive (FIXME: make cs_spectator)
int spectator; // non-interactive
int commandMsec; // every seconds this is reset, if user
// commands exhaust it, assume time cheating
int frame_latency[LATENCY_COUNTS];
int ping;
int rate;
int message_size[RATE_MESSAGES]; // used to rate drop packets
float rate;
int surpressCount; // number of messages rate supressed
edict_t *edict; // EDICT_NUM(clientnum+1)
char name[32]; // extracted from userinfo, color string allowed
@ -132,17 +134,14 @@ typedef struct sv_client_s
client_frame_t frames[UPDATE_BACKUP]; // updates can be delta'd from here
byte *download; // file being downloaded
int downloadsize; // total bytes (can't use EOF because of paks)
int downloadcount; // bytes sent
byte *download; // file being downloaded
int downloadsize; // total bytes (can't use EOF because of paks)
int downloadcount; // bytes sent
int deltamessage; // frame last client usercmd message
double lastmessage; // sv.framenum when packet was last received
double lastmessage; // sv.framenum when packet was last received
double lastconnect;
double nextsnapshot; // send another snapshot when svs.realtime >= nextsnapshot
float snapshot_time; // requests a snapshot every snapshot_time unless rate choked
int challenge; // challenge of this user, randomly generated
int challenge; // challenge of this user, randomly generated
netchan_t netchan;
} sv_client_t;

View File

@ -96,7 +96,7 @@ void SV_DirectConnect( netadr_t from )
for( i = 0, cl = svs.clients; i < Host_MaxClients(); i++, cl++ )
{
if( cl->state == cs_free ) continue;
if( NET_CompareBaseAdr( from, cl->netchan.remote_address ) && (cl->netchan.qport == qport || from.port == cl->netchan.remote_address.port))
if( NET_CompareBaseAdr(from, cl->netchan.remote_address) && (cl->netchan.qport == qport || from.port == cl->netchan.remote_address.port))
{
if(!NET_IsLocalAddress( from ) && (svs.realtime - cl->lastconnect) < sv_reconnect_limit->value )
{
@ -203,8 +203,7 @@ gotnewcl:
newcl->state = cs_connected;
newcl->lastmessage = svs.realtime;
newcl->lastconnect = svs.realtime;
newcl->nextsnapshot = svs.realtime;
// if this was the first client on the server, or the last client
// the server can hold, send a heartbeat to the master.
for( i = 0, cl = svs.clients; i < Host_MaxClients(); i++, cl++ )
@ -539,8 +538,9 @@ void SV_New_f( sv_client_t *cl )
// send the serverdata
MSG_WriteByte( &cl->netchan.message, svc_serverdata );
MSG_WriteLong( &cl->netchan.message, PROTOCOL_VERSION );
MSG_WriteLong( &cl->netchan.message, PROTOCOL_VERSION);
MSG_WriteLong( &cl->netchan.message, svs.spawncount );
MSG_WriteFloat( &cl->netchan.message, sv.frametime );
MSG_WriteShort( &cl->netchan.message, playernum );
MSG_WriteString( &cl->netchan.message, sv.configstrings[CS_NAME] );
@ -555,7 +555,7 @@ void SV_New_f( sv_client_t *cl )
// begin fetching configstrings
MSG_WriteByte( &cl->netchan.message, svc_stufftext );
MSG_WriteString( &cl->netchan.message, va( "cmd configstrings %i %i\n", svs.spawncount, 0 ));
MSG_WriteString( &cl->netchan.message, va("cmd configstrings %i %i\n", svs.spawncount, 0 ));
}
}
@ -629,9 +629,9 @@ void SV_Baselines_f( sv_client_t *cl )
return;
}
start = com.atoi( Cmd_Argv( 2 ));
start = com.atoi(Cmd_Argv(2));
Mem_Set( &nullstate, 0, sizeof( nullstate ));
Mem_Set( &nullstate, 0, sizeof(nullstate));
// write a packet full of data
while( cl->netchan.message.cursize < MAX_MSGLEN / 2 && start < host.max_edicts )
@ -646,7 +646,7 @@ void SV_Baselines_f( sv_client_t *cl )
}
if( start == host.max_edicts ) com.snprintf( baseline, MAX_STRING, "precache %i\n", svs.spawncount );
else com.snprintf( baseline, MAX_STRING, "cmd baselines %i %i\n", svs.spawncount, start );
else com.snprintf( baseline, MAX_STRING, "cmd baselines %i %i\n",svs.spawncount, start );
// send next command
MSG_WriteByte( &cl->netchan.message, svc_stufftext );
@ -668,7 +668,6 @@ void SV_Begin_f( sv_client_t *cl )
return;
}
cl->state = cs_spawned;
cl->nextsnapshot = svs.realtime; // generate a snapshot immediately
SV_PutClientInServer( cl->edict );
}
@ -790,18 +789,10 @@ void SV_UserinfoChanged( sv_client_t *cl )
if( com.strlen( val ))
{
i = com.atoi( val );
cl->rate = bound ( 2500, i, 25000 );
cl->rate = i;
cl->rate = bound ( 100, cl->rate, 15000 );
}
else cl->rate = 5000; // default value (ISDN)
// snaps command
val = Info_ValueForKey( cl->userinfo, "snaps" );
if( com.strlen( val ))
{
i = com.atoi( val );
cl->snapshot_time = (1.0f / bound( 1, i, 30 ));
}
else cl->snapshot_time = 0.02f;
else cl->rate = 5000;
// msg command
val = Info_ValueForKey( cl->userinfo, "msg" );
@ -1420,71 +1411,63 @@ On very fast clients, there may be multiple usercmd packed into
each of the backup packets.
==================
*/
static void SV_ReadClientMove( sv_client_t *cl, sizebuf_t *msg, bool noDelta )
static void SV_ReadClientMove( sv_client_t *cl, sizebuf_t *msg )
{
int i, key, cmd_count;
int checksum1, checksum2;
usercmd_t cmds[UPDATE_BACKUP];
usercmd_t *cmd, *oldcmd;
usercmd_t nullcmd;
int checksumIndex, lastframe;
int checksum, calculatedChecksum;
usercmd_t nullcmd, oldest, oldcmd, newcmd;
int net_drop;
double latency;
if( noDelta ) cl->deltamessage = -1;
else cl->deltamessage = cl->netchan.incoming_acknowledged;
key = msg->readcount;
checksum1 = MSG_ReadByte( msg );
cmd_count = MSG_ReadByte( msg );
if( cmd_count < 1 ) return;
if( cmd_count > UPDATE_BACKUP )
checksumIndex = msg->readcount;
checksum = MSG_ReadByte( msg );
lastframe = MSG_ReadLong( msg );
if( lastframe != cl->lastframe )
{
// force to server will be crashed
MsgDev( D_ERROR, "too many usercmds received\n" );
return;
cl->lastframe = lastframe;
if( cl->lastframe > 0 )
{
latency = svs.realtime - cl->frames[cl->lastframe & UPDATE_MASK].senttime;
cl->frame_latency[cl->lastframe & (LATENCY_COUNTS-1)] = latency;
}
}
Mem_Set( &nullcmd, 0, sizeof( nullcmd ));
for( i = 0, oldcmd = &nullcmd; i < cmd_count; i++ )
{
cmd = &cmds[i];
MSG_ReadDeltaUsercmd( msg, oldcmd, cmd );
oldcmd = cmd;
}
// save time for ping calculation
cl->frames[cl->netchan.incoming_acknowledged & UPDATE_MASK].message_acked = svs.realtime;
MSG_ReadDeltaUsercmd( msg, &nullcmd, &oldest );
MSG_ReadDeltaUsercmd( msg, &oldest, &oldcmd );
MSG_ReadDeltaUsercmd( msg, &oldcmd, &newcmd );
if( cl->state != cs_spawned )
{
cl->deltamessage = -1;
cl->lastframe = -1;
return;
}
// if the checksum fails, ignore the rest of the packet
checksum2 = CRC_Sequence( msg->data + key + 1, msg->readcount - key - 1, cl->netchan.incoming_sequence );
if( checksum2 != checksum1 )
calculatedChecksum = CRC_Sequence( msg->data + checksumIndex + 1, msg->readcount - checksumIndex - 1, cl->netchan.incoming_sequence );
if( calculatedChecksum != checksum )
{
MsgDev( D_ERROR, "SV_UserMove: failed command checksum for %s (%d != %d)\n", cl->name, checksum2, checksum1 );
MsgDev( D_ERROR, "SV_UserMove: failed command checksum for %s (%d != %d)\n", cl->name, calculatedChecksum, checksum );
return;
}
// usually, the first couple commands will be duplicates
// of ones we have previously received, but the servertimes
// in the commands will cause them to be immediately discarded
for( i = 0; i < cmd_count; i++ )
if( !sv_paused->integer )
{
// if this is a cmd from before a map_restart ignore it
if( cmds[i].servertime > cmds[cmd_count-1].servertime )
continue;
net_drop = cl->netchan.dropped;
if( net_drop < 20 )
{
while( net_drop > 2 )
{
SV_Physics_ClientMove( cl, &cl->lastcmd );
net_drop--;
}
if( net_drop > 1 ) SV_Physics_ClientMove( cl, &oldest );
if( net_drop > 0 ) SV_Physics_ClientMove( cl, &oldcmd );
// don't execute if this is an old cmd which is already executed
// these old cmds are included when cl_packetdup > 0
if( cmds[i].servertime <= cl->lastcmd.servertime )
continue;
SV_Physics_ClientMove( cl, &cmds[i] );
}
SV_Physics_ClientMove( cl, &newcmd );
}
cl->lastcmd = newcmd;
}
/*
@ -1527,12 +1510,7 @@ void SV_ExecuteClientMessage( sv_client_t *cl, sizebuf_t *msg )
case clc_move:
if( move_issued ) return; // someone is trying to cheat...
move_issued = true;
SV_ReadClientMove( cl, msg, true );
break;
case clc_deltamove:
if( move_issued ) return; // someone is trying to cheat...
move_issued = true;
SV_ReadClientMove( cl, msg, false );
SV_ReadClientMove( cl, msg );
break;
case clc_stringcmd:
s = MSG_ReadString( msg );

View File

@ -431,8 +431,8 @@ void SV_Status_f( void )
if( !cl->state ) continue;
Msg( "%3i ", i);
Msg( "%5i ", (int)cl->edict->v.frags );
Msg( "%3i ", i );
Msg( "%5i ", cl->edict->v.frags );
if( cl->state == cs_connected ) Msg( "Connect" );
else if( cl->state == cs_zombie ) Msg( "Zombie " );
@ -444,7 +444,7 @@ void SV_Status_f( void )
Msg( "%s", cl->name );
l = 24 - com.strlen( cl->name );
for( j = 0; j < l; j++ ) Msg (" ");
for( j = 0; j < l; j++ ) Msg( " " );
Msg( "%g ", svs.realtime - cl->lastmessage );
s = NET_AdrToString( cl->netchan.remote_address );
Msg( "%s", s );
@ -467,7 +467,7 @@ void SV_ConSay_f( void )
sv_client_t *client;
int i;
if( Cmd_Argc() < 2 ) return;
if(Cmd_Argc() < 2) return;
com.strncpy( text, "console: ", MAX_SYSPATH );
p = Cmd_Args();
@ -491,7 +491,7 @@ void SV_ConSay_f( void )
SV_Heartbeat_f
==================
*/
void SV_Heartbeat_f( void )
void SV_Heartbeat_f (void)
{
svs.last_heartbeat = MAX_HEARTBEAT;
}
@ -505,7 +505,7 @@ Examine serverinfo string
*/
void SV_ServerInfo_f( void )
{
Msg( "Server info settings:\n" );
Msg("Server info settings:\n");
Info_Print( Cvar_Serverinfo());
}

View File

@ -325,38 +325,43 @@ void SV_WriteFrameToClient( sv_client_t *cl, sizebuf_t *msg )
int lastframe;
// this is the frame we are creating
frame = &cl->frames[cl->netchan.outgoing_sequence & UPDATE_MASK];
frame = &cl->frames[sv.framenum & UPDATE_MASK];
if( cl->deltamessage <= 0 || cl->state != cs_spawned )
if( cl->lastframe <= 0 )
{
// client is asking for a retransmit
oldframe = NULL;
lastframe = 0;
lastframe = -1;
}
else if( cl->netchan.outgoing_sequence - cl->deltamessage >= (UPDATE_BACKUP - 3))
else if( sv.framenum - cl->lastframe >= (UPDATE_BACKUP - 3))
{
// client hasn't gotten a good message through in a long time
MsgDev( D_WARN, "%s: Delta request from out of date packet\n", cl->name );
oldframe = NULL;
lastframe = 0;
lastframe = -1;
}
else
{ // we have a valid message to delta from
oldframe = &cl->frames[cl->deltamessage & UPDATE_MASK];
lastframe = cl->netchan.outgoing_sequence - cl->deltamessage;
oldframe = &cl->frames[cl->lastframe & UPDATE_MASK];
lastframe = cl->lastframe;
// the snapshot's entities may still have rolled off the buffer, though
if( oldframe->first_entity <= svs.next_client_entities - svs.num_client_entities )
{
MsgDev( D_WARN, "%s: delta request from out of date entities\n", cl->name );
MsgDev( D_WARN, "%s: delta request from out of date entities.\n", cl->name );
oldframe = NULL;
lastframe = 0;
}
}
MSG_WriteByte( msg, svc_time );
MSG_WriteFloat( msg, sv.time ); // send a servertime before each frame
MSG_WriteByte( msg, svc_frame );
MSG_WriteFloat( msg, sv.time ); // send a servertime for each frame
MSG_WriteByte( msg, lastframe ); // what we are delta'ing from
MSG_WriteLong( msg, sv.framenum );
MSG_WriteFloat( msg, sv.frametime );
MSG_WriteLong( msg, lastframe ); // what we are delta'ing from
MSG_WriteByte( msg, cl->surpressCount ); // rate dropped packets
cl->surpressCount = 0;
// send over the areabits
MSG_WriteByte( msg, frame->areabits_size ); // never more than 255 bytes
@ -402,8 +407,8 @@ void SV_BuildClientFrame( sv_client_t *cl )
sv.net_framenum++;
// this is the frame we are creating
frame = &cl->frames[cl->netchan.outgoing_sequence & UPDATE_MASK];
frame->message_sent = svs.realtime; // save it for ping calc later
frame = &cl->frames[sv.framenum & UPDATE_MASK];
frame->senttime = svs.realtime; // save it for ping calc later
// clear everything in this snapshot
frame_ents.num_entities = c_fullsend = 0;
@ -454,75 +459,6 @@ FRAME UPDATES
===============================================================================
*/
/*
====================
SV_RateMsec
Return the number of msec a given size message is supposed
to take to clear, based on the current rate
====================
*/
#define HEADER_RATE_BYTES 10 // sequence, qport etc
static float SV_RateTime( sv_client_t *cl, size_t msg_size )
{
float rate_time;
// individual messages will never be larger than fragment size
if( msg_size > MAX_MSGLEN ) msg_size = MAX_MSGLEN;
rate_time = (msg_size + HEADER_RATE_BYTES) / cl->rate;
return rate_time;
}
/*
=======================
SV_SendMessageToClient
Called by SV_SendClientDatagram
=======================
*/
void SV_SendMessageToClient( sizebuf_t *msg, sv_client_t *cl )
{
float rate_time;
// record information about the message
cl->frames[cl->netchan.outgoing_sequence & UPDATE_MASK].message_size = msg->cursize;
cl->frames[cl->netchan.outgoing_sequence & UPDATE_MASK].message_sent = svs.realtime;
cl->frames[cl->netchan.outgoing_sequence & UPDATE_MASK].message_acked = -1;
// send the datagram
Netchan_Transmit( &cl->netchan, msg->cursize, msg->data );
// set nextsnapshot based on rate and requested number of updates
// local clients get snapshots every frame
if( NET_IsLocalAddress( cl->netchan.remote_address ))
{
cl->nextsnapshot = svs.realtime + 0.1f; // FIXME: tune this value
return;
}
// normal rate / snapshotMsec calculation
rate_time = SV_RateTime( cl, msg->cursize );
// never send more packets than this, no matter what the rate is at
if( rate_time < cl->snapshot_time ) rate_time = cl->snapshot_time;
cl->nextsnapshot = svs.realtime + rate_time;
// don't pile up empty snapshots while connecting
if ( cl->state != cs_spawned )
{
// a gigantic connection message may have already put the nextsnapshot
// more than a second away, so don't shorten it
// do shorten if client is downloading
if( !cl->download && cl->nextsnapshot < svs.realtime + 1.0f )
cl->nextsnapshot = svs.realtime + 1.0f;
}
}
/*
=======================
SV_SendClientDatagram
@ -556,11 +492,43 @@ bool SV_SendClientDatagram( sv_client_t *cl )
MSG_Clear( &msg );
}
SV_SendMessageToClient( &msg, cl );
// send the datagram
Netchan_Transmit( &cl->netchan, msg.cursize, msg.data );
// record the size for rate estimation
cl->message_size[sv.framenum % RATE_MESSAGES] = msg.cursize;
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;
}
/*
=======================
SV_SendClientMessages
@ -590,8 +558,8 @@ void SV_SendClientMessages( void )
if( cl->state == cs_spawned )
{
if( svs.realtime < cl->nextsnapshot )
continue;
// don't overrun bandwidth
if( SV_RateDrop( cl )) continue;
SV_SendClientDatagram( cl );
}
else

View File

@ -167,7 +167,6 @@ void SV_SpawnServer( const char *server, const char *savename )
// wipe the entire per-level structure
Mem_Set( &sv, 0, sizeof( sv ));
svs.realtime = 0.0f;
// save name for levels that don't set message
com.strncpy( sv.configstrings[CS_NAME], server, CS_SIZE );
@ -180,12 +179,10 @@ void SV_SpawnServer( const char *server, const char *savename )
// needs to reconnect
if( svs.clients[i].state > cs_connected )
svs.clients[i].state = cs_connected;
svs.clients[i].deltamessage = -1;
svs.clients[i].nextsnapshot = svs.realtime; // generate a snapshot immediately
svs.clients[i].lastframe = -1;
}
sv.time = 1.0;
sv.frametime = 0.1f;
com.strncpy( sv.name, server, MAX_STRING );
FS_FileBase(server, sv.configstrings[CS_NAME]);
@ -222,7 +219,11 @@ void SV_SpawnServer( const char *server, const char *savename )
svgame.dllFuncs.pfnServerActivate( EDICT_NUM( 0 ), svgame.globals->numEntities, svgame.globals->maxClients );
// run two frames to allow everything to settle
for( i = 0; i < 2; i++ ) SV_Physics();
for( i = 0; i < 2; i++ )
{
sv.frametime = svgame.globals->frametime = host.frametime = 0.1;
SV_Physics();
}
// all precaches are complete
sv.state = ss_active;

View File

@ -1,6 +1,6 @@
//=======================================================================
// Copyright XashXT Group 2007 ©
// sv_main.c - server frame code
// sv_utils.c - server vm utils
//=======================================================================
#include "common.h"
@ -50,44 +50,24 @@ void SV_CalcPings( void )
int i, j;
sv_client_t *cl;
int total, count;
int delta;
// clamp fps counter
for( i = 0; i < Host_MaxClients(); i++ )
{
cl = &svs.clients[i];
if( cl->state != cs_spawned )
{
cl->ping = 999;
continue;
}
if( !cl->edict )
{
cl->ping = 999;
continue;
}
if( cl->edict->v.flags & FL_FAKECLIENT )
{
cl->ping = 0;
continue;
}
if( cl->state != cs_spawned ) continue;
total = 0;
count = 0;
for( j = 0; j < UPDATE_BACKUP; j++ )
total = count = 0;
for( j = 0; j < LATENCY_COUNTS; j++ )
{
if( cl->frames[j].message_acked <= 0 )
continue;
delta = cl->frames[j].message_acked - cl->frames[j].message_sent;
count++;
total += delta;
if( cl->frame_latency > 0 )
{
count++;
total += cl->frame_latency[j];
}
}
if( count )
{
cl->ping = total / count;
if( cl->ping > 999 ) cl->ping = 999;
}
else cl->ping = 999;
if( !count ) cl->ping = 0;
else cl->ping = total / count;
// let the game dll know about the ping
cl->edict->pvServerData->client->ping = cl->ping;
@ -194,6 +174,16 @@ void SV_CheckTimeouts( void )
float droppoint;
float zombiepoint;
if( sv_fps->modified )
{
if( sv_fps->value < 10 ) Cvar_Set( "sv_fps", "10" ); // too slow, also, netcode uses a byte
else if( sv_fps->value > 90 ) Cvar_Set( "sv_fps", "90" ); // abusive
sv_fps->modified = false;
}
// calc sv.frametime
sv.frametime = ( 1.0f / sv_fps->value );
droppoint = svs.realtime - timeout->value;
zombiepoint = svs.realtime - zombietime->value;
@ -263,12 +253,12 @@ bool SV_CheckPaused( void )
{
// don't pause
if( sv_paused->integer )
Cvar_Set( "sv_paused", "0" );
Cvar_Set( "paused", "0" );
return false;
}
if( !sv_paused->integer )
Cvar_Set( "sv_paused", "1" );
Cvar_Set( "paused", "1" );
return true;
}
@ -279,25 +269,18 @@ SV_RunGameFrame
*/
void SV_RunGameFrame( void )
{
// 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
// don't run if paused
if( !sv_paused->integer || Host_MaxClients() > 1 )
{
sv.framenum++;
sv.time = sv.framenum * sv.frametime;
SV_Physics();
if( SV_CheckPaused( )) return;
// never get more than one tic behind
if( sv.time < svs.realtime )
{
if( sv_showclamp->integer )
MsgDev( D_INFO, "sv highclamp\n" );
svs.realtime = sv.time;
}
sv.framenum++;
if( sv.frametime ) SV_Physics();
// never get more than one tic behind
if( sv.time < svs.realtime )
{
if( sv_showclamp->integer )
MsgDev( D_INFO, "sv highclamp\n" );
svs.realtime = sv.time;
}
}
@ -309,13 +292,10 @@ SV_Frame
*/
void SV_Frame( double time )
{
static double timeResidual = 0.0f;
// if server is not active, do nothing
if( !svs.initialized ) return;
// allow pause if only the local client is connected
if( SV_CheckPaused()) return;
svs.realtime += time;
// keep the random time dependent
rand ();
@ -326,41 +306,29 @@ void SV_Frame( double time )
// read packets from clients
SV_ReadPackets ();
if( sv_fps->modified )
// move autonomous things around if enough time has passed
if( svs.realtime < sv.time )
{
if( sv_fps->value < 10 ) Cvar_Set( "sv_fps", "10" ); // too slow, also, netcode uses a byte
else if( sv_fps->value > 100 ) Cvar_Set( "sv_fps", "100" ); // abusive
sv_fps->modified = false;
}
sv.frametime = (1.0f / sv_fps->value);
timeResidual += time;
if( host.type == HOST_DEDICATED && timeResidual < sv.frametime )
{
// NET_Sleep will give the OS time slices until either get a packet
// or time enough for a server frame has gone by
NET_Sleep( sv.frametime - timeResidual );
// never let the time get too far off
if( sv.time - svs.realtime > sv.frametime )
{
if( sv_showclamp->integer )
MsgDev( D_INFO, "sv lowclamp\n" );
svs.realtime = sv.time - sv.frametime;
}
NET_Sleep( sv.time - svs.realtime );
return;
}
// update ping based on the last known frame from all clients
SV_CalcPings ();
// let everything in the world think and move
while( timeResidual >= sv.frametime )
{
timeResidual -= sv.frametime;
svs.realtime += sv.frametime;
sv.time += sv.frametime;
SV_Physics();
sv.framenum++;
}
// give the clients some timeslices
SV_GiveMsec ();
// let everything in the world think and move
SV_RunGameFrame ();
// send messages back to the clients that had packets read this frame
SV_SendClientMessages ();
@ -470,7 +438,7 @@ void SV_Init( void )
Cvar_Get ("protocol", va("%i", PROTOCOL_VERSION), CVAR_SERVERINFO|CVAR_INIT, "displays server protocol version" );
Cvar_Get ("sv_aim", "1", 0, "enable auto-aiming" );
sv_fps = Cvar_Get( "sv_fps", "60", CVAR_ARCHIVE, "running physics engine at" );
sv_fps = Cvar_Get( "sv_fps", "72.1", CVAR_ARCHIVE, "running server physics at" );
sv_stepheight = Cvar_Get( "sv_stepheight", DEFAULT_STEPHEIGHT, CVAR_ARCHIVE|CVAR_LATCH, "how high you can step up" );
sv_playersonly = Cvar_Get( "playersonly", "0", 0, "freezes time, except for players" );
hostname = Cvar_Get ("sv_hostname", "unnamed", CVAR_SERVERINFO | CVAR_ARCHIVE, "host name" );

View File

@ -1676,15 +1676,12 @@ void SV_Physics_ClientMove( sv_client_t *client, usercmd_t *cmd )
{
edict_t *ent = client->edict;
// save current cmd
client->lastcmd = *cmd;
// call player physics, this needs the proper frametime
svgame.globals->frametime = sv.frametime;
SV_ClientThink( client, cmd );
// call standard client pre-think, with frametime = 0
svgame.globals->time = sv.time = cmd->servertime;
svgame.globals->time = sv.time;
svgame.globals->frametime = 0;
svgame.dllFuncs.pfnPlayerPreThink( ent );
svgame.globals->frametime = sv.frametime;
@ -1721,7 +1718,7 @@ void SV_Physics_ClientMove( sv_client_t *client, usercmd_t *cmd )
SV_TouchTriggers( ent );
// call standard player post-think, with frametime = 0
svgame.globals->time = sv.time = svs.realtime;
svgame.globals->time = sv.time;
svgame.globals->frametime = 0;
svgame.dllFuncs.pfnPlayerPostThink( ent );
svgame.globals->frametime = sv.frametime;
@ -1774,4 +1771,6 @@ void SV_Physics( void )
// decrement svgame.globals->numEntities if the highest number entities died
for( ; EDICT_NUM( svgame.globals->numEntities - 1)->free; svgame.globals->numEntities-- );
if( !sv_playersonly->integer ) sv.time += sv.frametime;
}

View File

@ -78,7 +78,7 @@ Beta 13.12.09
47. fixup slowly rendering OK
48. implement uimenu into engine.dll OK
49. implement new timers OK
50. fixup network packets rate OK
50. fixup network packets rate
51. fixup stair climbing OK
52. implement new user move system OK
53. finish RenderMode for shaders OK