Added dynamic tic stabilization

Attempts to balance periods of rough traffic by putting in an artificial delay, smoothing playback but further increasing input delay. This can be disabled with net_ticbalance.
This commit is contained in:
Boondorl 2025-06-30 00:05:53 -04:00 committed by Ricardo Luís Vaz Silva
commit 0e23b900bb
3 changed files with 65 additions and 3 deletions

View file

@ -11,6 +11,7 @@ enum ENetConstants
BACKUPTICS = 35 * 5, // Remember up to 5 seconds of data.
MAXTICDUP = 3,
MAXSENDTICS = 35 * 1, // Only send up to 1 second of data at a time.
STABILITYTICS = 12,
LOCALCMDTICS = (BACKUPTICS * MAXTICDUP),
MAX_MSGLEN = 14000,
};

View file

@ -116,6 +116,14 @@ int LastSentConsistency = 0; // Last consistency we sent out. If < CurrentC
int CurrentConsistency = 0; // Last consistency we generated.
FClientNetState ClientStates[MAXPLAYERS] = {};
// Try and stabilize uneven connections by checking for spikes in available
// sequences. If they're found, try and average out a buffer to prioritize
// making the experience smoother over very stop and go heavy.
static int StabilityBuffer = 0;
static int PrevAvailableDiff = 0;
static size_t CurStabilityTic = 0u;
static int StabilityTics[STABILITYTICS] = {};
// If we're sending a packet to ourselves, store it here instead. This is the simplest way to execute
// playback as it means in the world running code itself all player commands are built the exact same way
// instead of having to rely on pulling from the correct local buffers. It also ensures all commands are
@ -157,7 +165,7 @@ extern bool advancedemo;
CVAR(Bool, vid_dontdowait, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
CVAR(Bool, vid_lowerinbackground, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
CVAR(Bool, net_ticbalance, false, CVAR_SERVERINFO | CVAR_NOSAVE) // Currently deprecated, but may be brought back later.
CVAR(Bool, net_ticbalance, true, CVAR_SERVERINFO | CVAR_NOSAVE)
CVAR(Bool, net_extratic, false, CVAR_SERVERINFO | CVAR_NOSAVE)
CVAR(Bool, net_limitsaves, true, CVAR_SERVERINFO | CVAR_NOSAVE)
CVAR(Bool, net_repeatableactioncooldown, true, CVAR_SERVERINFO | CVAR_NOSAVE)
@ -395,6 +403,9 @@ void Net_ClearBuffers()
LastEnterTic = LastGameUpdate = EnterTic;
gametic = ClientTic = 0;
SkipCommandTimer = SkipCommandAmount = CommandsAhead = 0;
StabilityBuffer = PrevAvailableDiff = 0;
CurStabilityTic = 0u;
memset(StabilityTics, 0, sizeof(StabilityTics));
NetEvents.ResetStream();
CutsceneReady = 0u;
@ -502,6 +513,9 @@ void Net_ResetCommands(bool midTic)
bCommandsReset = midTic;
++CurrentLobbyID;
SkipCommandTimer = SkipCommandAmount = CommandsAhead = 0;
StabilityBuffer = PrevAvailableDiff = 0;
CurStabilityTic = 0u;
memset(StabilityTics, 0, sizeof(StabilityTics));
int tic = gametic / TicDup;
if (midTic)
@ -1292,6 +1306,7 @@ static bool Net_UpdateStatus()
if (updated)
{
lowestDiff -= StabilityBuffer;
if (lowestDiff > 0)
{
if (SkipCommandTimer++ > TICRATE / 2)
@ -2090,6 +2105,47 @@ static bool ShouldStabilizeTick()
&& gameaction != ga_worlddone && gameaction != ga_completed && gameaction != ga_screenshot && gameaction != ga_fullconsole;
}
// If the connection has been unstable then let the game lag behind for a little bit
// while we wait for it to stabilize, otherwise everything will appear to jitter around.
static void CalculateNetStabilityBuffer(int diff)
{
if (!netgame || demoplayback)
{
StabilityBuffer = 0;
return;
}
if (diff < 0)
diff = 0;
if (!(gametic % TicDup))
{
StabilityTics[CurStabilityTic++ % STABILITYTICS] = diff > PrevAvailableDiff ? diff : 0;
PrevAvailableDiff = diff;
}
// If we're not balancing latency, just give an extra tic for padding
// and nothing else.
if (!net_ticbalance)
{
StabilityBuffer = 1;
return;
}
double total = 0.0;
int unstableCount = 0;
for (int t : StabilityTics)
{
if (t > 0)
{
++unstableCount;
total += t;
}
}
StabilityBuffer = unstableCount > 0 ? static_cast<int>(ceil(total / unstableCount)) : 0;
}
//
// TryRunTics
//
@ -2148,8 +2204,12 @@ void TryRunTics()
// If the amount of tics to run is falling behind the amount of available tics,
// speed the playsim up a bit to help catch up.
int runTics = min<int>(totalTics, availableTics);
if (totalTics > 0 && totalTics < availableTics && !singletics)
++runTics;
if (!singletics && totalTics > 0)
{
CalculateNetStabilityBuffer(availableTics - totalTics);
if (totalTics < availableTics - StabilityBuffer)
++runTics;
}
// Test player prediction code in singleplayer
// by running the gametic behind the ClientTic

View file

@ -2440,6 +2440,7 @@ OptionMenu NetworkOptions protected
StaticText " "
StaticText "$NETMNU_HOSTOPTIONS", 1
Option "$NETMNU_EXTRATICS", "net_extratic", "OnOff"
Option "$NETMNU_BALANCETICS", "net_ticbalance", "OnOff"
Option "$NETMNU_LIMITCONVO", "net_limitconversations", "OnOff"
Option "$NETMNU_REPEATCOOLDOWN", "net_repeatableactioncooldown", "OnOff"
Option "$NETMNU_DISABLEPAUSE", "net_disablepause", "PauseTypes"