mirror of
https://github.com/ValveSoftware/GameNetworkingSockets.git
synced 2026-05-29 16:20:34 +00:00
Add a new jitter simulation that does "clumping"
P4:9002451
This commit is contained in:
@@ -1402,15 +1402,53 @@ enum ESteamNetworkingConfigValue
|
||||
k_ESteamNetworkingConfig_FakePacketLag_Send = 4,
|
||||
k_ESteamNetworkingConfig_FakePacketLag_Recv = 5,
|
||||
|
||||
/// [global float] 0-100 Percentage of packets we will add additional delay
|
||||
/// to (causing them to be reordered)
|
||||
/// Simulated jitter/clumping.
|
||||
///
|
||||
/// For each packet, a jitter value is determined (which may
|
||||
/// be zero). This amount is added as extra delay to the
|
||||
/// packet. When a subsequent packet is queued, it receives its
|
||||
/// own random jitter amount from the current time. if this would
|
||||
/// result in the packets being delivered out of order, the later
|
||||
/// packet queue time is adjusted to happen after the first packet.
|
||||
/// Thus simulating jitter by itself will not reorder packets, but it
|
||||
/// can "clump" them.
|
||||
///
|
||||
/// - Avg: A random jitter time is generated using an exponential
|
||||
/// distribution using this value as the mean (ms). The default
|
||||
/// is zero, which disables random jitter.
|
||||
/// - Max: Limit the random jitter time to this value (ms).
|
||||
/// - Pct: odds (0-100) that a random jitter value for the packet
|
||||
/// will be generated. Otherwise, a jitter value of zero
|
||||
/// is used, and the packet will only be delayed by the jitter
|
||||
/// system if necessary to retain order, due to the jitter of a
|
||||
/// previous packet.
|
||||
///
|
||||
/// All values are [global float]
|
||||
///
|
||||
/// Fake jitter is simulated after fake lag, but before reordering.
|
||||
k_ESteamNetworkingConfig_FakePacketJitter_Send_Avg = 53,
|
||||
k_ESteamNetworkingConfig_FakePacketJitter_Send_Max = 54,
|
||||
k_ESteamNetworkingConfig_FakePacketJitter_Send_Pct = 55,
|
||||
k_ESteamNetworkingConfig_FakePacketJitter_Recv_Avg = 56,
|
||||
k_ESteamNetworkingConfig_FakePacketJitter_Recv_Max = 57,
|
||||
k_ESteamNetworkingConfig_FakePacketJitter_Recv_Pct = 58,
|
||||
|
||||
/// [global float] 0-100 Percentage of packets we will add additional
|
||||
/// delay to. If other packet(s) are sent/received within this delay
|
||||
/// window (that doesn't also randomly receive the same extra delay),
|
||||
/// then the packets become reordered.
|
||||
///
|
||||
/// This mechanism is primarily intended to generate out-of-order
|
||||
/// packets. To simulate random jitter, use the FakePacketJitter.
|
||||
/// Fake packet reordering is applied after fake lag and jitter
|
||||
k_ESteamNetworkingConfig_FakePacketReorder_Send = 6,
|
||||
k_ESteamNetworkingConfig_FakePacketReorder_Recv = 7,
|
||||
|
||||
/// [global int32] Extra delay, in ms, to apply to reordered packets.
|
||||
/// [global int32] Extra delay, in ms, to apply to reordered
|
||||
/// packets. The same time value is used for sending and receiving.
|
||||
k_ESteamNetworkingConfig_FakePacketReorder_Time = 8,
|
||||
|
||||
/// [global float 0--100] Globally duplicate some percentage of packets we send
|
||||
/// [global float 0--100] Globally duplicate some percentage of packets.
|
||||
k_ESteamNetworkingConfig_FakePacketDup_Send = 26,
|
||||
k_ESteamNetworkingConfig_FakePacketDup_Recv = 27,
|
||||
|
||||
|
||||
@@ -56,6 +56,12 @@ DEFINE_GLOBAL_CONFIGVAL( int32, FakeRateLimit_Send_Burst, 16*1024, 0, 1024*1024
|
||||
DEFINE_GLOBAL_CONFIGVAL( int32, FakeRateLimit_Recv_Rate, 0, 0, 1024*1024*1024 );
|
||||
DEFINE_GLOBAL_CONFIGVAL( int32, FakeRateLimit_Recv_Burst, 16*1024, 0, 1024*1024 );
|
||||
DEFINE_GLOBAL_CONFIGVAL( int32, OutOfOrderCorrectionWindowMicroseconds, 1000, 0, 50*1000 );
|
||||
DEFINE_GLOBAL_CONFIGVAL( float, FakePacketJitter_Send_Avg, 0.0f, 0.0f, 2000.0f );
|
||||
DEFINE_GLOBAL_CONFIGVAL( float, FakePacketJitter_Send_Max, 100.0f, 0.0f, 5000.0f );
|
||||
DEFINE_GLOBAL_CONFIGVAL( float, FakePacketJitter_Send_Pct, 75.0f, 0.0f, 100.0f );
|
||||
DEFINE_GLOBAL_CONFIGVAL( float, FakePacketJitter_Recv_Avg, 0.0f, 0.0f, 2000.0f );
|
||||
DEFINE_GLOBAL_CONFIGVAL( float, FakePacketJitter_Recv_Max, 100.0f, 0.0f, 5000.0f );
|
||||
DEFINE_GLOBAL_CONFIGVAL( float, FakePacketJitter_Recv_Pct, 75.0f, 0.0f, 100.0f );
|
||||
|
||||
DEFINE_GLOBAL_CONFIGVAL( void *, Callback_AuthStatusChanged, nullptr );
|
||||
#ifdef STEAMNETWORKINGSOCKETS_ENABLE_STEAMNETWORKINGMESSAGES
|
||||
|
||||
@@ -1306,7 +1306,7 @@ class CPacketLagger : private IThinker
|
||||
public:
|
||||
~CPacketLagger() { Clear(); }
|
||||
|
||||
void LagPacket( CRawUDPSocketImpl *pSock, const netadr_t &adr, int msDelay, int nChunks, const iovec *pChunks )
|
||||
void LagPacket( CRawUDPSocketImpl *pSock, const netadr_t &adr, SteamNetworkingMicroseconds usecTime, int nChunks, const iovec *pChunks )
|
||||
{
|
||||
SteamNetworkingGlobalLock::AssertHeldByCurrentThread( "LagPacket" );
|
||||
|
||||
@@ -1326,16 +1326,6 @@ public:
|
||||
return;
|
||||
}
|
||||
|
||||
if ( msDelay < 1 )
|
||||
{
|
||||
AssertMsg( false, "Packet lag time must be positive!" );
|
||||
msDelay = 1;
|
||||
}
|
||||
|
||||
// Limit to something sane
|
||||
msDelay = std::min( msDelay, 5000 );
|
||||
const SteamNetworkingMicroseconds usecTime = SteamNetworkingSockets_GetLocalTimestamp() + msDelay * 1000;
|
||||
|
||||
CLaggedPacket *pkt = new CLaggedPacket( pSock, adr, usecTime, cbPkt );
|
||||
|
||||
// Gather the payload data data into the buffer
|
||||
@@ -1759,6 +1749,29 @@ void WakeServiceThread()
|
||||
#endif
|
||||
}
|
||||
|
||||
inline SteamNetworkingMicroseconds RandomJitter( const GlobalConfigValue<float> &ValAvg, const GlobalConfigValue<float> &ValMax, const GlobalConfigValue<float> &ValPct )
|
||||
{
|
||||
// The defaults disable jitter by setting the *average* to 0, so check that first.
|
||||
if ( likely( ValAvg.Get() ) <= 0.0f )
|
||||
return 0;
|
||||
if ( ValMax.Get() <= 0.0f )
|
||||
return 0;
|
||||
if ( !RandomBoolWithOdds( ValPct.Get() ) )
|
||||
return 0;
|
||||
|
||||
// Unscaled exponential distribution
|
||||
float r = WeakRandomFloat( 0.000001f, 1.0f ); // log of 0 is undefined, set the minimum to a value that can be subtracted from 1. (Something close to FLT_EPSILON.)
|
||||
float x = -logf( r );
|
||||
if ( !( x > 0.0f ) ) // Written "backwards" just in case math blows up
|
||||
return 0;
|
||||
|
||||
// Scale and clamp
|
||||
float flJitterMS = std::min( x*ValAvg.Get(), ValMax.Get() );
|
||||
|
||||
// Convert to integer microseconds
|
||||
return (SteamNetworkingMicroseconds)( flJitterMS * 1000.0f );
|
||||
}
|
||||
|
||||
bool CRawUDPSocketImpl::BSendRawPacketGather( int nChunks, const iovec *pChunks, const netadr_t &adrTo ) const
|
||||
{
|
||||
SteamNetworkingGlobalLock::AssertHeldByCurrentThread();
|
||||
@@ -1797,28 +1810,47 @@ bool CRawUDPSocketImpl::BSendRawPacketGather( int nChunks, const iovec *pChunks,
|
||||
if ( RandomBoolWithOdds( GlobalConfig::FakePacketLoss_Send.Get() ) )
|
||||
return true;
|
||||
|
||||
// Fake lag?
|
||||
int32 nPacketFakeLagTotal = GlobalConfig::FakePacketLag_Send.Get();
|
||||
|
||||
// Check for simulating random packet reordering
|
||||
// Read convars and decide if we're going to simulate any lag, jitter, reordering, or duplication
|
||||
int msFakeLag = GlobalConfig::FakePacketLag_Send.Get();
|
||||
SteamNetworkingMicroseconds usecReorderLag = 0;
|
||||
if ( RandomBoolWithOdds( GlobalConfig::FakePacketReorder_Send.Get() ) )
|
||||
{
|
||||
nPacketFakeLagTotal += GlobalConfig::FakePacketReorder_Time.Get();
|
||||
}
|
||||
usecReorderLag = GlobalConfig::FakePacketReorder_Time.Get()*1000;
|
||||
SteamNetworkingMicroseconds usecJitter = RandomJitter( GlobalConfig::FakePacketJitter_Send_Avg, GlobalConfig::FakePacketJitter_Send_Max, GlobalConfig::FakePacketJitter_Send_Pct );
|
||||
bool bDup = RandomBoolWithOdds( GlobalConfig::FakePacketDup_Send.Get() );
|
||||
|
||||
// Check for simulating random packet duplication
|
||||
if ( RandomBoolWithOdds( GlobalConfig::FakePacketDup_Send.Get() ) )
|
||||
// Anything active?
|
||||
static SteamNetworkingMicroseconds s_usecMinNextJitteredTime;
|
||||
if ( unlikely( msFakeLag > 0 || usecReorderLag > 0 || usecJitter > 0 || bDup || s_usecMinNextJitteredTime != 0 ) )
|
||||
{
|
||||
int32 nDupLag = nPacketFakeLagTotal + WeakRandomInt( 0, GlobalConfig::FakePacketDup_TimeMax.Get() );
|
||||
nDupLag = std::max( 1, nDupLag );
|
||||
s_packetLagQueueSend.LagPacket( const_cast<CRawUDPSocketImpl *>( this ), adrTo, nDupLag, nChunks, pChunks );
|
||||
}
|
||||
SteamNetworkingMicroseconds usecNow = SteamNetworkingSockets_GetLocalTimestamp();
|
||||
SteamNetworkingMicroseconds usecWhenProcess = usecNow + msFakeLag*1000;
|
||||
usecJitter = std::max( usecJitter, s_usecMinNextJitteredTime - usecWhenProcess );
|
||||
if ( usecJitter > 0 )
|
||||
{
|
||||
usecWhenProcess += usecJitter;
|
||||
s_usecMinNextJitteredTime = usecWhenProcess + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
// End of clump, clear this so we can switch back to the
|
||||
// fast path once options are turned off
|
||||
s_usecMinNextJitteredTime = 0;
|
||||
}
|
||||
usecWhenProcess += usecReorderLag;
|
||||
|
||||
// Lag the original packet?
|
||||
if ( nPacketFakeLagTotal > 0 )
|
||||
{
|
||||
s_packetLagQueueSend.LagPacket( const_cast<CRawUDPSocketImpl *>( this ), adrTo, nPacketFakeLagTotal, nChunks, pChunks );
|
||||
return true;
|
||||
// Check for simulating random packet duplication
|
||||
if ( bDup )
|
||||
{
|
||||
SteamNetworkingMicroseconds usecDupLag = 1 + (SteamNetworkingMicroseconds)WeakRandomFloat( 0.0f, GlobalConfig::FakePacketDup_TimeMax.Get()*1000.0f );
|
||||
s_packetLagQueueSend.LagPacket( const_cast<CRawUDPSocketImpl *>( this ), adrTo, usecWhenProcess + usecDupLag, nChunks, pChunks );
|
||||
}
|
||||
|
||||
// Lag the original packet?
|
||||
if ( usecWhenProcess > usecNow )
|
||||
{
|
||||
s_packetLagQueueSend.LagPacket( const_cast<CRawUDPSocketImpl *>( this ), adrTo, usecWhenProcess, nChunks, pChunks );
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Now really send it
|
||||
@@ -2346,43 +2378,62 @@ static bool DrainSocket( CRawUDPSocketImpl *pSock )
|
||||
pSock->TracePkt( false, info.m_adrFrom, 1, &tmp );
|
||||
}
|
||||
|
||||
int32 nPacketFakeLagTotal = GlobalConfig::FakePacketLag_Recv.Get();
|
||||
|
||||
// Check for simulating random packet reordering
|
||||
// Read convars and decide if we're going to simulate any lag, jitter, reordering, or duplication
|
||||
int msFakeLag = GlobalConfig::FakePacketLag_Recv.Get();
|
||||
SteamNetworkingMicroseconds usecReorderLag = 0;
|
||||
if ( RandomBoolWithOdds( GlobalConfig::FakePacketReorder_Recv.Get() ) )
|
||||
usecReorderLag = GlobalConfig::FakePacketReorder_Time.Get()*1000;
|
||||
SteamNetworkingMicroseconds usecJitter = RandomJitter( GlobalConfig::FakePacketJitter_Recv_Avg, GlobalConfig::FakePacketJitter_Recv_Max, GlobalConfig::FakePacketJitter_Recv_Pct );
|
||||
bool bDup = RandomBoolWithOdds( GlobalConfig::FakePacketDup_Recv.Get() );
|
||||
|
||||
// Anything active?
|
||||
static SteamNetworkingMicroseconds s_usecMinNextJitteredTime;
|
||||
if ( unlikely( msFakeLag > 0 || usecReorderLag > 0 || usecJitter > 0 || bDup || s_usecMinNextJitteredTime != 0 ) )
|
||||
{
|
||||
nPacketFakeLagTotal += GlobalConfig::FakePacketReorder_Time.Get();
|
||||
SteamNetworkingMicroseconds usecNow = SteamNetworkingSockets_GetLocalTimestamp();
|
||||
SteamNetworkingMicroseconds usecWhenProcess = usecNow + msFakeLag*1000;
|
||||
usecJitter = std::max( usecJitter, s_usecMinNextJitteredTime - usecWhenProcess );
|
||||
if ( usecJitter > 0 )
|
||||
{
|
||||
usecWhenProcess += usecJitter;
|
||||
s_usecMinNextJitteredTime = usecWhenProcess + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
// End of clump, clear this so we can switch back to the
|
||||
// fast path once options are turned off
|
||||
s_usecMinNextJitteredTime = 0;
|
||||
}
|
||||
usecWhenProcess += usecReorderLag;
|
||||
|
||||
// Check for simulating random packet duplication
|
||||
if ( bDup )
|
||||
{
|
||||
SteamNetworkingMicroseconds usecDupLag = 1 + (SteamNetworkingMicroseconds)WeakRandomFloat( 0.0f, GlobalConfig::FakePacketDup_TimeMax.Get()*1000.0f );
|
||||
iovec temp;
|
||||
temp.iov_len = ret;
|
||||
temp.iov_base = buf;
|
||||
s_packetLagQueueRecv.LagPacket( pSock, info.m_adrFrom, usecWhenProcess + usecDupLag, 1, &temp );
|
||||
}
|
||||
|
||||
// Lag the original packet?
|
||||
if ( usecWhenProcess > usecNow )
|
||||
{
|
||||
iovec temp;
|
||||
temp.iov_len = ret;
|
||||
temp.iov_base = buf;
|
||||
s_packetLagQueueRecv.LagPacket( pSock, info.m_adrFrom, usecWhenProcess, 1, &temp );
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Check for simulating random packet duplication
|
||||
if ( RandomBoolWithOdds( GlobalConfig::FakePacketDup_Recv.Get() ) )
|
||||
{
|
||||
int32 nDupLag = nPacketFakeLagTotal + WeakRandomInt( 0, GlobalConfig::FakePacketDup_TimeMax.Get() );
|
||||
nDupLag = std::max( 1, nDupLag );
|
||||
iovec temp;
|
||||
temp.iov_len = ret;
|
||||
temp.iov_base = buf;
|
||||
s_packetLagQueueRecv.LagPacket( pSock, info.m_adrFrom, nDupLag, 1, &temp );
|
||||
}
|
||||
|
||||
// Check for simulating lag
|
||||
if ( nPacketFakeLagTotal > 0 )
|
||||
{
|
||||
iovec temp;
|
||||
temp.iov_len = ret;
|
||||
temp.iov_base = buf;
|
||||
s_packetLagQueueRecv.LagPacket( pSock, info.m_adrFrom, nPacketFakeLagTotal, 1, &temp );
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
info.m_pPkt = buf;
|
||||
info.m_cbPkt = ret;
|
||||
info.m_usecNow = usecRecvFromEnd;
|
||||
info.m_pSock = pSock;
|
||||
info.m_bQueuedForOutOfOrder = false;
|
||||
pSock->m_callback( info );
|
||||
}
|
||||
// Process the packet now
|
||||
info.m_pPkt = buf;
|
||||
info.m_cbPkt = ret;
|
||||
info.m_usecNow = usecRecvFromEnd;
|
||||
info.m_pSock = pSock;
|
||||
info.m_bQueuedForOutOfOrder = false;
|
||||
pSock->m_callback( info );
|
||||
|
||||
#ifdef STEAMNETWORKINGSOCKETS_LOWLEVEL_TIME_SOCKET_CALLS
|
||||
SteamNetworkingMicroseconds usecProcessPacketEnd = SteamNetworkingSockets_GetLocalTimestamp();
|
||||
|
||||
@@ -927,6 +927,13 @@ namespace GlobalConfig
|
||||
extern GlobalConfigValue<int32> FakePacketReorder_Time;
|
||||
extern GlobalConfigValue<float> FakePacketDup_Send;
|
||||
extern GlobalConfigValue<float> FakePacketDup_Recv;
|
||||
extern GlobalConfigValue<float> FakePacketJitter_Send_Avg;
|
||||
extern GlobalConfigValue<float> FakePacketJitter_Send_Max;
|
||||
extern GlobalConfigValue<float> FakePacketJitter_Send_Pct;
|
||||
extern GlobalConfigValue<float> FakePacketJitter_Recv_Avg;
|
||||
extern GlobalConfigValue<float> FakePacketJitter_Recv_Max;
|
||||
extern GlobalConfigValue<float> FakePacketJitter_Recv_Pct;
|
||||
|
||||
extern GlobalConfigValue<int32> FakePacketDup_TimeMax;
|
||||
extern GlobalConfigValue<int32> PacketTraceMaxBytes;
|
||||
extern GlobalConfigValue<int32> FakeRateLimit_Send_Rate;
|
||||
|
||||
Reference in New Issue
Block a user