2012-11-26 18:58:24 +00:00
/*
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
Doom 3 BFG Edition GPL Source Code
2012-11-28 15:47:07 +00:00
Copyright ( C ) 1993 - 2012 id Software LLC , a ZeniMax Media company .
2012-11-26 18:58:24 +00:00
2012-11-28 15:47:07 +00:00
This file is part of the Doom 3 BFG Edition GPL Source Code ( " Doom 3 BFG Edition Source Code " ) .
2012-11-26 18:58:24 +00:00
Doom 3 BFG Edition Source Code is free software : you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation , either version 3 of the License , or
( at your option ) any later version .
Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with Doom 3 BFG Edition Source Code . If not , see < http : //www.gnu.org/licenses/>.
In addition , the Doom 3 BFG Edition Source Code is also subject to certain additional terms . You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code . If not , please request a copy in writing from id Software at the address below .
If you have questions concerning this license or the applicable additional terms , you may contact in writing id Software LLC , c / o ZeniMax Media Inc . , Suite 120 , Rockville , Maryland 20850 USA .
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
*/
# include "../idlib/precompiled.h"
# pragma hdrstop
# include "Common_local.h"
idCVar net_clientMaxPrediction ( " net_clientMaxPrediction " , " 5000 " , CVAR_SYSTEM | CVAR_INTEGER | CVAR_NOCHEAT , " maximum number of milliseconds a client can predict ahead of server. " ) ;
idCVar net_snapRate ( " net_snapRate " , " 100 " , CVAR_SYSTEM | CVAR_INTEGER , " How many milliseconds between sending snapshots " ) ;
idCVar net_ucmdRate ( " net_ucmdRate " , " 40 " , CVAR_SYSTEM | CVAR_INTEGER , " How many milliseconds between sending usercmds " ) ;
idCVar net_debug_snapShotTime ( " net_debug_snapShotTime " , " 0 " , CVAR_BOOL | CVAR_ARCHIVE , " " ) ;
idCVar com_forceLatestSnap ( " com_forceLatestSnap " , " 0 " , CVAR_BOOL , " " ) ;
// Enables effective snap rate: dynamically adjust the client snap rate based on:
// -client FPS
// -server FPS (interpolated game time received / interval it was received over)
// -local buffered time (leave a cushion to absorb spikes, slow down when infront of it, speed up when behind it) ie: net_minBufferedSnapPCT_Static
2012-11-28 15:47:07 +00:00
idCVar net_effectiveSnapRateEnable ( " net_effectiveSnapRateEnable " , " 1 " , CVAR_BOOL , " Dynamically adjust client snaprate " ) ;
idCVar net_effectiveSnapRateDebug ( " net_effectiveSnapRateDebug " , " 0 " , CVAR_BOOL , " Debug " ) ;
2012-11-26 18:58:24 +00:00
// Min buffered snapshot time to keep as a percentage of the effective snaprate
// -ie we want to keep 50% of the amount of time difference between last two snaps.
// -we need to scale this because we may get throttled at the snaprate may change
// -Acts as a buffer to absorb spikes
2012-11-28 15:47:07 +00:00
idCVar net_minBufferedSnapPCT_Static ( " net_minBufferedSnapPCT_Static " , " 1.0 " , CVAR_FLOAT , " Min amount of snapshot buffer time we want need to buffer " ) ;
idCVar net_maxBufferedSnapMS ( " net_maxBufferedSnapMS " , " 336 " , CVAR_INTEGER , " Max time to allow for interpolation cushion " ) ;
idCVar net_minBufferedSnapWinPCT_Static ( " net_minBufferedSnapWinPCT_Static " , " 1.0 " , CVAR_FLOAT , " Min amount of snapshot buffer time we want need to buffer " ) ;
2012-11-26 18:58:24 +00:00
// Factor at which we catch speed up interpolation if we fall behind our optimal interpolation window
// -This is a static factor. We may experiment with a dynamic one that would be faster the farther you are from the ideal window
2012-11-28 15:47:07 +00:00
idCVar net_interpolationCatchupRate ( " net_interpolationCatchupRate " , " 1.3 " , CVAR_FLOAT , " Scale interpolationg rate when we fall behind " ) ;
idCVar net_interpolationFallbackRate ( " net_interpolationFallbackRate " , " 0.95 " , CVAR_FLOAT , " Scale interpolationg rate when we fall behind " ) ;
idCVar net_interpolationBaseRate ( " net_interpolationBaseRate " , " 1.0 " , CVAR_FLOAT , " Scale interpolationg rate when we fall behind " ) ;
2012-11-26 18:58:24 +00:00
2012-11-28 15:47:07 +00:00
// Enabled a dynamic ideal snap buffer window: we will scale the distance and size
idCVar net_optimalDynamic ( " net_optimalDynamic " , " 1 " , CVAR_BOOL , " How fast to add to our optimal time buffer when we are playing snapshots faster than server is feeding them to us " ) ;
2012-11-26 18:58:24 +00:00
// These values are used instead if net_optimalDynamic is 0 (don't scale by actual snap rate/interval)
2012-11-28 15:47:07 +00:00
idCVar net_optimalSnapWindow ( " net_optimalSnapWindow " , " 112 " , CVAR_FLOAT , " " ) ;
idCVar net_optimalSnapTime ( " net_optimalSnapTime " , " 112 " , CVAR_FLOAT , " How fast to add to our optimal time buffer when we are playing snapshots faster than server is feeding them to us " ) ;
2012-11-26 18:58:24 +00:00
// this is at what percentage of being ahead of the interpolation buffer that we start slowing down (we ramp down from 1.0 to 0.0 starting here)
// this is a percentage of the total cushion time.
2012-11-28 15:47:07 +00:00
idCVar net_interpolationSlowdownStart ( " net_interpolationSlowdownStart " , " 0.5 " , CVAR_FLOAT , " Scale interpolation rate when we fall behind " ) ;
2012-11-26 18:58:24 +00:00
// Extrapolation is now disabled
2012-11-28 15:47:07 +00:00
idCVar net_maxExtrapolationInMS ( " net_maxExtrapolationInMS " , " 0 " , CVAR_INTEGER , " Max time in MS that extrapolation is allowed to occur. " ) ;
2012-11-26 18:58:24 +00:00
static const int SNAP_USERCMDS = 8192 ;
/*
= = = = = = = = = = = = = = =
idCommonLocal : : IsMultiplayer
= = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
bool idCommonLocal : : IsMultiplayer ( )
{
idLobbyBase & lobby = session - > GetPartyLobbyBase ( ) ;
2012-11-26 18:58:24 +00:00
return ( ( ( lobby . GetMatchParms ( ) . matchFlags & MATCH_ONLINE ) ! = 0 ) & & ( session - > GetState ( ) > idSession : : IDLE ) ) ;
}
/*
= = = = = = = = = = = = = = =
idCommonLocal : : IsServer
= = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
bool idCommonLocal : : IsServer ( )
{
2012-11-26 18:58:24 +00:00
return IsMultiplayer ( ) & & session - > GetActingGameStateLobbyBase ( ) . IsHost ( ) ;
}
/*
= = = = = = = = = = = = = = =
idCommonLocal : : IsClient
= = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
bool idCommonLocal : : IsClient ( )
{
2012-11-26 18:58:24 +00:00
return IsMultiplayer ( ) & & session - > GetActingGameStateLobbyBase ( ) . IsPeer ( ) ;
}
/*
= = = = = = = = = = = = = = =
idCommonLocal : : SendSnapshots
= = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
int idCommonLocal : : GetSnapRate ( )
{
2012-11-26 18:58:24 +00:00
return net_snapRate . GetInteger ( ) ;
}
/*
= = = = = = = = = = = = = = =
idCommonLocal : : SendSnapshots
= = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
void idCommonLocal : : SendSnapshots ( )
{
if ( ! mapSpawned )
{
2012-11-26 18:58:24 +00:00
return ;
}
int currentTime = Sys_Milliseconds ( ) ;
2012-11-28 15:47:07 +00:00
if ( currentTime < nextSnapshotSendTime )
{
2012-11-26 18:58:24 +00:00
return ;
}
2012-11-28 15:47:07 +00:00
idLobbyBase & lobby = session - > GetActingGameStateLobbyBase ( ) ;
if ( ! lobby . IsHost ( ) )
{
2012-11-26 18:58:24 +00:00
return ;
}
2012-11-28 15:47:07 +00:00
if ( ! lobby . HasActivePeers ( ) )
{
2012-11-26 18:58:24 +00:00
return ;
}
idSnapShot ss ;
game - > ServerWriteSnapshot ( ss ) ;
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
session - > SendSnapshot ( ss ) ;
nextSnapshotSendTime = MSEC_ALIGN_TO_FRAME ( currentTime + net_snapRate . GetInteger ( ) ) ;
}
/*
= = = = = = = = = = = = = = =
idCommonLocal : : NetReceiveSnapshot
= = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
void idCommonLocal : : NetReceiveSnapshot ( class idSnapShot & ss )
{
2012-11-26 18:58:24 +00:00
ss . SetRecvTime ( Sys_Milliseconds ( ) ) ;
// If we are about to overwrite the oldest snap, then force a read, which will cause a pop on screen, but we have to do this.
2012-11-28 15:47:07 +00:00
if ( writeSnapshotIndex - readSnapshotIndex > = RECEIVE_SNAPSHOT_BUFFER_SIZE )
{
2012-11-26 18:58:24 +00:00
idLib : : Printf ( " Overwritting oldest snapshot %d with new snapshot %d \n " , readSnapshotIndex , writeSnapshotIndex ) ;
assert ( writeSnapshotIndex % RECEIVE_SNAPSHOT_BUFFER_SIZE = = readSnapshotIndex % RECEIVE_SNAPSHOT_BUFFER_SIZE ) ;
ProcessNextSnapshot ( ) ;
}
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
receivedSnaps [ writeSnapshotIndex % RECEIVE_SNAPSHOT_BUFFER_SIZE ] = ss ;
writeSnapshotIndex + + ;
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
// Force read the very first 2 snapshots
2012-11-28 15:47:07 +00:00
if ( readSnapshotIndex < 2 )
{
2012-11-26 18:58:24 +00:00
ProcessNextSnapshot ( ) ;
}
}
/*
= = = = = = = = = = = = = = =
idCommonLocal : : SendUsercmd
= = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
void idCommonLocal : : SendUsercmds ( int localClientNum )
{
if ( ! mapSpawned )
{
2012-11-26 18:58:24 +00:00
return ;
}
int currentTime = Sys_Milliseconds ( ) ;
2012-11-28 15:47:07 +00:00
if ( currentTime < nextUsercmdSendTime )
{
2012-11-26 18:58:24 +00:00
return ;
}
2012-11-28 15:47:07 +00:00
idLobbyBase & lobby = session - > GetActingGameStateLobbyBase ( ) ;
if ( lobby . IsHost ( ) )
{
2012-11-26 18:58:24 +00:00
return ;
}
// We always send the last NUM_USERCMD_SEND usercmds
// Which may result in duplicate usercmds being sent in the case of a low net_ucmdRate
// But the LZW compressor means the extra usercmds are not large and the redundancy can smooth packet loss
byte buffer [ idPacketProcessor : : MAX_FINAL_PACKET_SIZE ] ;
idBitMsg msg ( buffer , sizeof ( buffer ) ) ;
idSerializer ser ( msg , true ) ;
usercmd_t empty ;
2012-11-28 15:47:07 +00:00
usercmd_t * last = & empty ;
2012-11-26 18:58:24 +00:00
2012-11-28 15:47:07 +00:00
usercmd_t * cmdBuffer [ NUM_USERCMD_SEND ] ;
2012-11-26 18:58:24 +00:00
const int numCmds = userCmdMgr . GetPlayerCmds ( localClientNum , cmdBuffer , NUM_USERCMD_SEND ) ;
msg . WriteByte ( numCmds ) ;
2012-11-28 15:47:07 +00:00
for ( int i = 0 ; i < numCmds ; i + + )
{
2012-11-26 18:58:24 +00:00
cmdBuffer [ i ] - > Serialize ( ser , * last ) ;
last = cmdBuffer [ i ] ;
}
session - > SendUsercmds ( msg ) ;
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
nextUsercmdSendTime = MSEC_ALIGN_TO_FRAME ( currentTime + net_ucmdRate . GetInteger ( ) ) ;
}
/*
= = = = = = = = = = = = = = =
idCommonLocal : : NetReceiveUsercmds
= = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
void idCommonLocal : : NetReceiveUsercmds ( int peer , idBitMsg & msg )
{
2012-11-26 18:58:24 +00:00
int clientNum = Game ( ) - > MapPeerToClient ( peer ) ;
2012-11-28 15:47:07 +00:00
if ( clientNum = = - 1 )
{
2012-11-26 18:58:24 +00:00
idLib : : Warning ( " NetReceiveUsercmds: Could not find client for peer %d " , peer ) ;
return ;
}
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
NetReadUsercmds ( clientNum , msg ) ;
}
/*
= = = = = = = = = = = = = = =
idCommonLocal : : NetReceiveReliable
= = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
void idCommonLocal : : NetReceiveReliable ( int peer , int type , idBitMsg & msg )
{
2012-11-26 18:58:24 +00:00
int clientNum = Game ( ) - > MapPeerToClient ( peer ) ;
// Only servers care about the client num. Band-aid for problems related to the host's peerIndex being -1 on clients.
2012-11-28 15:47:07 +00:00
if ( common - > IsServer ( ) & & clientNum = = - 1 )
{
2012-11-26 18:58:24 +00:00
idLib : : Warning ( " NetReceiveReliable: Could not find client for peer %d " , peer ) ;
return ;
}
2012-11-28 15:47:07 +00:00
const byte * msgData = msg . GetReadData ( ) + msg . GetReadCount ( ) ;
2012-11-26 18:58:24 +00:00
int msgSize = msg . GetRemainingData ( ) ;
2012-11-28 15:47:07 +00:00
reliableMsg_t & reliable = reliableQueue . Alloc ( ) ;
2012-11-26 18:58:24 +00:00
reliable . client = clientNum ;
reliable . type = type ;
reliable . dataSize = msgSize ;
2012-11-28 15:47:07 +00:00
reliable . data = ( byte * ) Mem_Alloc ( msgSize , TAG_NETWORKING ) ;
2012-11-26 18:58:24 +00:00
memcpy ( reliable . data , msgData , msgSize ) ;
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idCommonLocal : : ProcessSnapshot
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
void idCommonLocal : : ProcessSnapshot ( idSnapShot & ss )
{
2012-11-26 18:58:24 +00:00
int time = Sys_Milliseconds ( ) ;
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
snapTime = time ;
snapPrevious = snapCurrent ;
snapCurrent . serverTime = ss . GetTime ( ) ;
snapRate = snapCurrent . serverTime - snapPrevious . serverTime ;
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
static int lastReceivedLocalTime = 0 ;
int timeSinceLastSnap = ( time - lastReceivedLocalTime ) ;
2012-11-28 15:47:07 +00:00
if ( net_debug_snapShotTime . GetBool ( ) )
{
idLib : : Printf ( " ^2ProcessSnapshot. delta serverTime: %d delta localTime: %d \n " , ( snapCurrent . serverTime - snapPrevious . serverTime ) , timeSinceLastSnap ) ;
2012-11-26 18:58:24 +00:00
}
lastReceivedLocalTime = time ;
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
/* JAF ?
for ( int i = 0 ; i < MAX_PLAYERS ; i + + ) {
idBitMsg msg ;
if ( ss . GetObjectMsgByID ( idSession : : SS_PLAYER + i , msg ) ) {
if ( msg . GetSize ( ) = = 0 ) {
snapCurrent . players [ i ] . valid = false ;
continue ;
}
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
idSerializer ser ( msg , false ) ;
SerializePlayer ( ser , snapCurrent . players [ i ] ) ;
snapCurrent . players [ i ] . valid = true ;
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
extern idCVar com_drawSnapshots ;
if ( com_drawSnapshots . GetInteger ( ) = = 3 ) {
console - > AddSnapObject ( " players " , msg . GetSize ( ) , ss . CompareObject ( & oldss , idSession : : SS_PLAYER + i ) ) ;
}
}
}
*/
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
// Read usercmds from other players
2012-11-28 15:47:07 +00:00
for ( int p = 0 ; p < MAX_PLAYERS ; p + + )
{
if ( p = = game - > GetLocalClientNum ( ) )
{
2012-11-26 18:58:24 +00:00
continue ;
}
idBitMsg msg ;
2012-11-28 15:47:07 +00:00
if ( ss . GetObjectMsgByID ( SNAP_USERCMDS + p , msg ) )
{
2012-11-26 18:58:24 +00:00
NetReadUsercmds ( p , msg ) ;
}
}
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
// Set server game time here so that it accurately reflects the time when this frame was saved out, in case any serialize function needs it.
2012-11-28 15:47:07 +00:00
int oldTime = Game ( ) - > GetServerGameTimeMs ( ) ;
2012-11-26 18:58:24 +00:00
Game ( ) - > SetServerGameTimeMs ( snapCurrent . serverTime ) ;
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
Game ( ) - > ClientReadSnapshot ( ss ) ; //, &oldss );
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
// Restore server game time
Game ( ) - > SetServerGameTimeMs ( oldTime ) ;
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
snapTimeDelta = ss . GetRecvTime ( ) - oldss . GetRecvTime ( ) ;
oldss = ss ;
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idCommonLocal : : NetReadUsercmds
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
void idCommonLocal : : NetReadUsercmds ( int clientNum , idBitMsg & msg )
{
if ( clientNum = = - 1 )
{
2012-11-26 18:58:24 +00:00
idLib : : Warning ( " NetReadUsercmds: Trying to read commands from invalid clientNum %d " , clientNum ) ;
return ;
}
// TODO: This shouldn't actually happen. Figure out why it does.
// Seen on clients when another client leaves a match.
2012-11-28 15:47:07 +00:00
if ( msg . GetReadData ( ) = = NULL )
{
2012-11-26 18:58:24 +00:00
return ;
}
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
idSerializer ser ( msg , false ) ;
usercmd_t fakeCmd ;
2012-11-28 15:47:07 +00:00
usercmd_t * base = & fakeCmd ;
2012-11-26 18:58:24 +00:00
usercmd_t lastCmd ;
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
bool gotNewCmd = false ;
idStaticList < usercmd_t , NUM_USERCMD_RELAY > newCmdBuffer ;
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
usercmd_t baseCmd = userCmdMgr . NewestUserCmdForPlayer ( clientNum ) ;
int curMilliseconds = baseCmd . clientGameMilliseconds ;
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
const int numCmds = msg . ReadByte ( ) ;
2012-11-28 15:47:07 +00:00
for ( int i = 0 ; i < numCmds ; i + + )
{
2012-11-26 18:58:24 +00:00
usercmd_t newCmd ;
newCmd . Serialize ( ser , * base ) ;
lastCmd = newCmd ;
base = & lastCmd ;
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
int newMilliseconds = newCmd . clientGameMilliseconds ;
2012-11-28 15:47:07 +00:00
if ( newMilliseconds > curMilliseconds )
{
if ( verify ( i < NUM_USERCMD_RELAY ) )
{
2012-11-26 18:58:24 +00:00
newCmdBuffer . Append ( newCmd ) ;
gotNewCmd = true ;
curMilliseconds = newMilliseconds ;
}
}
}
// Push the commands into the buffer.
2012-11-28 15:47:07 +00:00
for ( int i = 0 ; i < newCmdBuffer . Num ( ) ; + + i )
{
2012-11-26 18:58:24 +00:00
userCmdMgr . PutUserCmdForPlayer ( clientNum , newCmdBuffer [ i ] ) ;
}
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idCommonLocal : : ProcessNextSnapshot
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
void idCommonLocal : : ProcessNextSnapshot ( )
{
if ( readSnapshotIndex = = writeSnapshotIndex )
{
idLib : : Printf ( " No snapshots to process. \n " ) ;
2012-11-26 18:58:24 +00:00
return ; // No snaps to process
}
2012-11-28 15:47:07 +00:00
ProcessSnapshot ( receivedSnaps [ readSnapshotIndex % RECEIVE_SNAPSHOT_BUFFER_SIZE ] ) ;
2012-11-26 18:58:24 +00:00
readSnapshotIndex + + ;
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idCommonLocal : : CalcSnapTimeBuffered
Return the amount of game time left of buffered snapshots
totalBufferedTime - total amount of snapshot time ( includng what we ' ve already past in current interpolate )
totalRecvTime - total real time ( sys_milliseconds ) all of totalBufferedTime was received over
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
int idCommonLocal : : CalcSnapTimeBuffered ( int & totalBufferedTime , int & totalRecvTime )
{
2012-11-26 18:58:24 +00:00
totalBufferedTime = snapRate ;
totalRecvTime = snapTimeDelta ;
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
// oldSS = last ss we deserialized
2012-11-28 15:47:07 +00:00
int lastBuffTime = oldss . GetTime ( ) ;
2012-11-26 18:58:24 +00:00
int lastRecvTime = oldss . GetRecvTime ( ) ;
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
// receivedSnaps[readSnapshotIndex % RECEIVE_SNAPSHOT_BUFFER_SIZE] = next buffered snapshot we haven't processed yet (might not exist)
2012-11-28 15:47:07 +00:00
for ( int i = readSnapshotIndex ; i < writeSnapshotIndex ; i + + )
{
2012-11-26 18:58:24 +00:00
int buffTime = receivedSnaps [ i % RECEIVE_SNAPSHOT_BUFFER_SIZE ] . GetTime ( ) ;
int recvTime = receivedSnaps [ i % RECEIVE_SNAPSHOT_BUFFER_SIZE ] . GetRecvTime ( ) ;
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
totalBufferedTime + = buffTime - lastBuffTime ;
totalRecvTime + = recvTime - lastRecvTime ;
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
lastRecvTime = recvTime ;
lastBuffTime = buffTime ;
}
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
totalRecvTime = Max ( 1 , totalRecvTime ) ;
totalRecvTime = static_cast < float > ( initialBaseTicksPerSec ) * static_cast < float > ( totalRecvTime / 1000.0f ) ; // convert realMS to gameMS
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
// remove time we've already interpolated over
2012-11-28 15:47:07 +00:00
int timeLeft = totalBufferedTime - Min < int > ( snapRate , snapCurrentTime ) ;
2012-11-26 18:58:24 +00:00
//idLib::Printf( "CalcSnapTimeBuffered. timeLeft: %d totalRecvTime: %d, totalTimeBuffered: %d\n", timeLeft, totalRecvTime, totalBufferedTime );
return timeLeft ;
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idCommonLocal : : InterpolateSnapshot
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
void idCommonLocal : : InterpolateSnapshot ( netTimes_t & prev , netTimes_t & next , float fraction , bool predict )
{
2012-11-26 18:58:24 +00:00
int serverTime = Lerp ( prev . serverTime , next . serverTime , fraction ) ;
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
Game ( ) - > SetServerGameTimeMs ( serverTime ) ; // Set the global server time to the interpolated time of the server
Game ( ) - > SetInterpolation ( fraction , serverTime , prev . serverTime , next . serverTime ) ;
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
//Game()->RunFrame( &userCmdMgr, &ret, true );
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idCommonLocal : : RunNetworkSnapshotFrame
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
void idCommonLocal : : RunNetworkSnapshotFrame ( )
{
2012-11-26 18:58:24 +00:00
// Process any reliable messages we've received
2012-11-28 15:47:07 +00:00
for ( int i = 0 ; i < reliableQueue . Num ( ) ; i + + )
{
game - > ProcessReliableMessage ( reliableQueue [ i ] . client , reliableQueue [ i ] . type , idBitMsg ( ( const byte * ) reliableQueue [ i ] . data , reliableQueue [ i ] . dataSize ) ) ;
2012-11-26 18:58:24 +00:00
Mem_Free ( reliableQueue [ i ] . data ) ;
}
reliableQueue . Clear ( ) ;
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
// abuse the game timing to time presentable thinking on clients
time_gameFrame = Sys_Microseconds ( ) ;
time_maxGameFrame = 0 ;
count_numGameFrames = 0 ;
2012-11-28 15:47:07 +00:00
if ( snapPrevious . serverTime > = 0 )
{
int msec_interval = 1 + idMath : : Ftoi ( ( float ) initialBaseTicksPerSec ) ;
2012-11-26 18:58:24 +00:00
static int clientTimeResidual = 0 ;
static int lastTime = Sys_Milliseconds ( ) ;
int currentTime = Sys_Milliseconds ( ) ;
int deltaFrameTime = idMath : : ClampInt ( 1 , 33 , currentTime - lastTime ) ;
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
clientTimeResidual + = idMath : : ClampInt ( 0 , 50 , currentTime - lastTime ) ;
lastTime = currentTime ;
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
extern idCVar com_fixedTic ;
2012-11-28 15:47:07 +00:00
if ( com_fixedTic . GetBool ( ) )
{
2012-11-26 18:58:24 +00:00
clientTimeResidual = 0 ;
}
2012-11-28 15:47:07 +00:00
do
{
2012-11-26 18:58:24 +00:00
// If we are extrapolating and have fresher snapshots, then use the freshest one
2012-11-28 15:47:07 +00:00
while ( ( snapCurrentTime > = snapRate | | com_forceLatestSnap . GetBool ( ) ) & & readSnapshotIndex < writeSnapshotIndex )
{
2012-11-26 18:58:24 +00:00
snapCurrentTime - = snapRate ;
ProcessNextSnapshot ( ) ;
}
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
// this only matters when running < 60 fps
// JAF Game()->GetRenderWorld()->UpdateDeferredPositions();
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
// Clamp the current time so that it doesn't fall outside of our extrapolation bounds
2012-11-28 15:47:07 +00:00
snapCurrentTime = idMath : : ClampInt ( 0 , snapRate + Min ( ( int ) snapRate , ( int ) net_maxExtrapolationInMS . GetInteger ( ) ) , snapCurrentTime ) ;
if ( snapRate < = 0 )
{
idLib : : Warning ( " snapRate <= 0. Resetting to 100 " ) ;
2012-11-26 18:58:24 +00:00
snapRate = 100 ;
}
2012-11-28 15:47:07 +00:00
float fraction = ( float ) snapCurrentTime / ( float ) snapRate ;
if ( ! IsValid ( fraction ) )
{
idLib : : Warning ( " Interpolation Fraction invalid: snapCurrentTime %d / snapRate %d " , ( int ) snapCurrentTime , ( int ) snapRate ) ;
2012-11-26 18:58:24 +00:00
fraction = 0.0f ;
}
InterpolateSnapshot ( snapPrevious , snapCurrent , fraction , true ) ;
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
// Default to a snap scale of 1
float snapRateScale = net_interpolationBaseRate . GetFloat ( ) ;
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
snapTimeBuffered = CalcSnapTimeBuffered ( totalBufferedTime , totalRecvTime ) ;
2012-11-28 15:47:07 +00:00
effectiveSnapRate = static_cast < float > ( totalBufferedTime ) / static_cast < float > ( totalRecvTime ) ;
if ( net_minBufferedSnapPCT_Static . GetFloat ( ) > 0.0f )
{
2012-11-26 18:58:24 +00:00
optimalPCTBuffer = session - > GetTitleStorageFloat ( " net_minBufferedSnapPCT_Static " , net_minBufferedSnapPCT_Static . GetFloat ( ) ) ;
}
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
// Calculate optimal amount of buffered time we want
2012-11-28 15:47:07 +00:00
if ( net_optimalDynamic . GetBool ( ) )
{
2012-11-26 18:58:24 +00:00
optimalTimeBuffered = idMath : : ClampInt ( 0 , net_maxBufferedSnapMS . GetInteger ( ) , snapRate * optimalPCTBuffer ) ;
optimalTimeBufferedWindow = snapRate * net_minBufferedSnapWinPCT_Static . GetFloat ( ) ;
2012-11-28 15:47:07 +00:00
}
else
{
2012-11-26 18:58:24 +00:00
optimalTimeBuffered = net_optimalSnapTime . GetFloat ( ) ;
optimalTimeBufferedWindow = net_optimalSnapWindow . GetFloat ( ) ;
}
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
// Scale snapRate based on where we are in the buffer
2012-11-28 15:47:07 +00:00
if ( snapTimeBuffered < = optimalTimeBuffered )
{
if ( snapTimeBuffered < = idMath : : FLT_SMALLEST_NON_DENORMAL )
{
2012-11-26 18:58:24 +00:00
snapRateScale = 0 ;
2012-11-28 15:47:07 +00:00
}
else
{
2012-11-26 18:58:24 +00:00
snapRateScale = net_interpolationFallbackRate . GetFloat ( ) ;
// When we interpolate past our cushion of buffered snapshot, we want to slow smoothly slow the
// rate of interpolation. frac will go from 1.0 to 0.0 (if snapshots stop coming in).
float startSlowdown = ( net_interpolationSlowdownStart . GetFloat ( ) * optimalTimeBuffered ) ;
2012-11-28 15:47:07 +00:00
if ( startSlowdown > 0 & & snapTimeBuffered < startSlowdown )
{
2012-11-26 18:58:24 +00:00
float frac = idMath : : ClampFloat ( 0.0f , 1.0f , snapTimeBuffered / startSlowdown ) ;
2012-11-28 15:47:07 +00:00
if ( ! IsValid ( frac ) )
{
2012-11-26 18:58:24 +00:00
frac = 0.0f ;
}
snapRateScale = Square ( frac ) * snapRateScale ;
2012-11-28 15:47:07 +00:00
if ( ! IsValid ( snapRateScale ) )
{
2012-11-26 18:58:24 +00:00
snapRateScale = 0.0f ;
}
}
}
2012-11-28 15:47:07 +00:00
}
else if ( snapTimeBuffered > optimalTimeBuffered + optimalTimeBufferedWindow )
{
2012-11-26 18:58:24 +00:00
// Go faster
snapRateScale = net_interpolationCatchupRate . GetFloat ( ) ;
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
}
2012-11-28 15:47:07 +00:00
float delta_interpolate = ( float ) initialBaseTicksPerSec * snapRateScale ;
if ( net_effectiveSnapRateEnable . GetBool ( ) )
{
2012-11-26 18:58:24 +00:00
float deltaFrameGameMS = static_cast < float > ( initialBaseTicksPerSec ) * static_cast < float > ( deltaFrameTime / 1000.0f ) ;
delta_interpolate = ( deltaFrameGameMS * snapRateScale * effectiveSnapRate ) + snapCurrentResidual ;
2012-11-28 15:47:07 +00:00
if ( ! IsValid ( delta_interpolate ) )
{
2012-11-26 18:58:24 +00:00
delta_interpolate = 0.0f ;
}
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
snapCurrentResidual = idMath : : Frac ( delta_interpolate ) ; // fixme: snapCurrentTime should just be a float, but would require changes in d4 too
2012-11-28 15:47:07 +00:00
if ( ! IsValid ( snapCurrentResidual ) )
{
2012-11-26 18:58:24 +00:00
snapCurrentResidual = 0.0f ;
}
2012-11-28 15:47:07 +00:00
if ( net_effectiveSnapRateDebug . GetBool ( ) )
{
idLib : : Printf ( " %d/%.2f snapRateScale: %.2f effectiveSR: %.2f d.interp: %.2f snapTimeBuffered: %.2f res: %.2f \n " , deltaFrameTime , deltaFrameGameMS , snapRateScale , effectiveSnapRate , delta_interpolate , snapTimeBuffered , snapCurrentResidual ) ;
2012-11-26 18:58:24 +00:00
}
}
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
assert ( IsValid ( delta_interpolate ) ) ;
int interpolate_interval = idMath : : Ftoi ( delta_interpolate ) ;
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
snapCurrentTime + = interpolate_interval ; // advance interpolation time by the scaled interpolate_interval
clientTimeResidual - = msec_interval ; // advance local client residual time (fixed step)
2012-11-28 15:47:07 +00:00
}
while ( clientTimeResidual > = msec_interval ) ;
if ( clientTimeResidual < 0 )
{
2012-11-26 18:58:24 +00:00
clientTimeResidual = 0 ;
}
}
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
time_gameFrame = Sys_Microseconds ( ) - time_gameFrame ;
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idCommonLocal : : ExecuteReliableMessages
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
void idCommonLocal : : ExecuteReliableMessages ( )
{
2012-11-26 18:58:24 +00:00
// Process any reliable messages we've received
2012-11-28 15:47:07 +00:00
for ( int i = 0 ; i < reliableQueue . Num ( ) ; i + + )
{
reliableMsg_t & reliable = reliableQueue [ i ] ;
game - > ProcessReliableMessage ( reliable . client , reliable . type , idBitMsg ( ( const byte * ) reliable . data , reliable . dataSize ) ) ;
2012-11-26 18:58:24 +00:00
Mem_Free ( reliable . data ) ;
}
reliableQueue . Clear ( ) ;
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idCommonLocal : : ResetNetworkingState
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
void idCommonLocal : : ResetNetworkingState ( )
{
2012-11-26 18:58:24 +00:00
snapTime = 0 ;
snapTimeWrite = 0 ;
snapCurrentTime = 0 ;
snapCurrentResidual = 0.0f ;
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
snapTimeBuffered = 0.0f ;
effectiveSnapRate = 0.0f ;
totalBufferedTime = 0 ;
totalRecvTime = 0 ;
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
readSnapshotIndex = 0 ;
writeSnapshotIndex = 0 ;
snapRate = 100000 ;
optimalTimeBuffered = 0.0f ;
optimalPCTBuffer = 0.5f ;
optimalTimeBufferedWindow = 0.0 ;
// Clear snapshot queue
2012-11-28 15:47:07 +00:00
for ( int i = 0 ; i < RECEIVE_SNAPSHOT_BUFFER_SIZE ; i + + )
{
2012-11-26 18:58:24 +00:00
receivedSnaps [ i ] . Clear ( ) ;
}
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
userCmdMgr . SetDefaults ( ) ;
snapCurrent . localTime = - 1 ;
snapPrevious . localTime = - 1 ;
snapCurrent . serverTime = - 1 ;
snapPrevious . serverTime = - 1 ;
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
// Make sure our current snap state is cleared so state from last game doesn't carry over into new game
oldss . Clear ( ) ;
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
gameFrame = 0 ;
clientPrediction = 0 ;
nextUsercmdSendTime = 0 ;
nextSnapshotSendTime = 0 ;
}