From 9cc19b4eec1ab6a319b5c2bbd629fce6a1bf706c Mon Sep 17 00:00:00 2001 From: g-cont Date: Thu, 17 Sep 2009 00:00:00 +0400 Subject: [PATCH] 17 Sep 2009 --- common/entity_state.h | 1 - engine/client/cl_cmds.c | 4 +- engine/client/cl_demo.c | 1 + engine/client/cl_frame.c | 275 ++++--------------- engine/client/cl_game.c | 34 +-- engine/client/cl_input.c | 549 ++++++++++++++------------------------ engine/client/cl_main.c | 62 +---- engine/client/cl_parse.c | 3 +- engine/client/cl_phys.c | 2 +- engine/client/cl_scrn.c | 2 +- engine/client/cl_view.c | 6 +- engine/client/client.h | 151 +++++------ engine/common.h | 6 +- engine/common/con_main.c | 4 +- engine/common/net_msg.c | 1 - engine/engine.plg | 12 +- engine/host.c | 63 ++++- engine/server/server.h | 33 ++- engine/server/sv_client.c | 120 ++++----- engine/server/sv_cmds.c | 12 +- engine/server/sv_frame.c | 138 ++++------ engine/server/sv_init.c | 11 +- engine/server/sv_main.c | 130 ++++----- engine/server/sv_phys.c | 9 +- todo.log | 2 +- 25 files changed, 612 insertions(+), 1019 deletions(-) diff --git a/common/entity_state.h b/common/entity_state.h index 51372754..7c8f0ae1 100644 --- a/common/entity_state.h +++ b/common/entity_state.h @@ -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; diff --git a/engine/client/cl_cmds.c b/engine/client/cl_cmds.c index a3621317..b25734a8 100644 --- a/engine/client/cl_cmds.c +++ b/engine/client/cl_cmds.c @@ -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 } /* diff --git a/engine/client/cl_demo.c b/engine/client/cl_demo.c index 962556ba..4aac3c3e 100644 --- a/engine/client/cl_demo.c +++ b/engine/client/cl_demo.c @@ -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] ); diff --git a/engine/client/cl_frame.c b/engine/client/cl_frame.c index 1e04145a..20a3bb84 100644 --- a/engine/client/cl_frame.c +++ b/engine/client/cl_frame.c @@ -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( " " ); - } - 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(); diff --git a/engine/client/cl_game.c b/engine/client/cl_game.c index d6e5bc6d..8c910d33 100644 --- a/engine/client/cl_game.c +++ b/engine/client/cl_game.c @@ -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 diff --git a/engine/client/cl_input.c b/engine/client/cl_input.c index 9698e750..69ef6eb9 100644 --- a/engine/client/cl_input.c +++ b/engine/client/cl_input.c @@ -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 - -=================== -*/ -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" ); } \ No newline at end of file diff --git a/engine/client/cl_main.c b/engine/client/cl_main.c index 0f48ec8d..a39712c8 100644 --- a/engine/client/cl_main.c +++ b/engine/client/cl_main.c @@ -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 (); diff --git a/engine/client/cl_parse.c b/engine/client/cl_parse.c index b22fd11b..52c317b0 100644 --- a/engine/client/cl_parse.c +++ b/engine/client/cl_parse.c @@ -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"); diff --git a/engine/client/cl_phys.c b/engine/client/cl_phys.c index a50172a3..49bb8350 100644 --- a/engine/client/cl_phys.c +++ b/engine/client/cl_phys.c @@ -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]); diff --git a/engine/client/cl_scrn.c b/engine/client/cl_scrn.c index d5a7a734..f9964e3e 100644 --- a/engine/client/cl_scrn.c +++ b/engine/client/cl_scrn.c @@ -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) diff --git a/engine/client/cl_view.c b/engine/client/cl_view.c index fc53e315..8b032f7f 100644 --- a/engine/client/cl_view.c +++ b/engine/client/cl_view.c @@ -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; } } diff --git a/engine/client/client.h b/engine/client/client.h index f90f9860..8419e263 100644 --- a/engine/client/client.h +++ b/engine/client/client.h @@ -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 diff --git a/engine/common.h b/engine/common.h index b50ae362..ae0783af 100644 --- a/engine/common.h +++ b/engine/common.h @@ -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; diff --git a/engine/common/con_main.c b/engine/common/con_main.c index e999b44e..8d731509 100644 --- a/engine/common/con_main.c +++ b/engine/common/con_main.c @@ -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; } diff --git a/engine/common/net_msg.c b/engine/common/net_msg.c index eaa7600e..021f9c5e 100644 --- a/engine/common/net_msg.c +++ b/engine/common/net_msg.c @@ -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 }, diff --git a/engine/engine.plg b/engine/engine.plg index 81abdd56..d49d7527 100644 --- a/engine/engine.plg +++ b/engine/engine.plg @@ -6,13 +6,13 @@ --------------------Configuration: engine - Win32 Debug--------------------

Command Lines

-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... diff --git a/engine/host.c b/engine/host.c index 858294cf..e8e440e5 100644 --- a/engine/host.c +++ b/engine/host.c @@ -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; } } diff --git a/engine/server/server.h b/engine/server/server.h index 44da62c3..56a613fc 100644 --- a/engine/server/server.h +++ b/engine/server/server.h @@ -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; diff --git a/engine/server/sv_client.c b/engine/server/sv_client.c index 5403aee1..be6d6367 100644 --- a/engine/server/sv_client.c +++ b/engine/server/sv_client.c @@ -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 ); diff --git a/engine/server/sv_cmds.c b/engine/server/sv_cmds.c index b0027492..2651a432 100644 --- a/engine/server/sv_cmds.c +++ b/engine/server/sv_cmds.c @@ -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()); } diff --git a/engine/server/sv_frame.c b/engine/server/sv_frame.c index 16d2d030..3b184008 100644 --- a/engine/server/sv_frame.c +++ b/engine/server/sv_frame.c @@ -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 diff --git a/engine/server/sv_init.c b/engine/server/sv_init.c index 853cbdd1..217d857e 100644 --- a/engine/server/sv_init.c +++ b/engine/server/sv_init.c @@ -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; diff --git a/engine/server/sv_main.c b/engine/server/sv_main.c index 300b05cd..bc654983 100644 --- a/engine/server/sv_main.c +++ b/engine/server/sv_main.c @@ -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" ); diff --git a/engine/server/sv_phys.c b/engine/server/sv_phys.c index 0128b528..32600ae5 100644 --- a/engine/server/sv_phys.c +++ b/engine/server/sv_phys.c @@ -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; } \ No newline at end of file diff --git a/todo.log b/todo.log index 74b4f8aa..9a79462b 100644 --- a/todo.log +++ b/todo.log @@ -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