Skip to content

Commit

Permalink
Merge pull request #431 from avaraline/fix-autoLT
Browse files Browse the repository at this point in the history
Fixes to Auto LT
  • Loading branch information
tra authored Feb 14, 2025
2 parents b10a9af + 923daba commit 4c9e9ec
Show file tree
Hide file tree
Showing 7 changed files with 45 additions and 26 deletions.
6 changes: 1 addition & 5 deletions src/game/CAvaraGame.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -686,7 +686,6 @@ void CAvaraGame::ReadGamePrefs() {
}
sensitivity = pow(2.0, gApplication->Get<double>(kMouseSensitivityTag));
SDL_Log("mouse sensitivity multiplier = %.2lf\n", sensitivity);
latencyTolerance = gApplication->Get<double>(kLatencyToleranceTag);
}

void CAvaraGame::ResumeGame() {
Expand Down Expand Up @@ -1072,7 +1071,7 @@ void CAvaraGame::SetFrameLatency(short newFrameLatency, short maxChange, CPlayer
static int reduceLatencyCounter = 0;
static int increaseLatencyCounter = 0;
if (newLatency < latencyTolerance) {
static const int REDUCE_LATENCY_COUNT = 8;
static const int REDUCE_LATENCY_COUNT = 2;
// need REDUCE_LATENCY_COUNT consecutive requests to reduce latency
if (maxChange == MAX_LATENCY || ++reduceLatencyCounter >= REDUCE_LATENCY_COUNT) {
latencyTolerance = std::max(latencyTolerance-maxChange, std::max(newLatency, double(0.0)));
Expand All @@ -1092,9 +1091,6 @@ void CAvaraGame::SetFrameLatency(short newFrameLatency, short maxChange, CPlayer
std::ostringstream ltOss;
ltOss << std::fixed << std::setprecision(int(1/(2*fpsScale))) << latencyTolerance;

// save as application preference (which also makes it show up on the UI)
gApplication->Set(kLatencyToleranceTag, latencyTolerance);

// if it changed
if (latencyTolerance != oldLatency && statusRequest == kPlayingStatus) {
std::ostringstream oss;
Expand Down
1 change: 1 addition & 0 deletions src/game/CAvaraGame.h
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,7 @@ class CAvaraGame {
double sensitivity;

double latencyTolerance;
short initialFrameLatency = 0;

ScoreInterfaceReasons scoreReason;
ScoreInterfaceReasons killReason;
Expand Down
14 changes: 10 additions & 4 deletions src/game/CNetManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
#define AUTOLATENCYDELAY 448 // msec (divisible by 64)
#define LOWERLATENCYCOUNT 2
#define HIGHERLATENCYCOUNT (0.25 * AUTOLATENCYPERIOD / itsGame->frameTime) // 25% of frames
#define DECREASELATENCYPERIOD (itsGame->TimeToFrameCount(AUTOLATENCYPERIOD*16)) // 16 consecutive votes ≈ 61 sec
#define DECREASELATENCYPERIOD (itsGame->TimeToFrameCount(AUTOLATENCYPERIOD*2)) // 2 consecutive votes ≈ 7.7 sec


#if ROUTE_THRU_SERVER
Expand Down Expand Up @@ -824,6 +824,9 @@ void CNetManager::AutoLatencyControl(FrameNumber frameNumber, Boolean didWait) {
// but addOneLatency helps account for deficiencies in the calculation by measuring how often clients had to wait too long for packets to arrive
short maxFrameLatency = addOneLatency + itsGame->RoundTripToFrameLatency(maxRoundTripLatency);

// treat kLatencyToleranceTag as upper limit when in AutoLT mode
maxFrameLatency = std::min<short>(maxFrameLatency, gApplication->Get<double>(kLatencyToleranceTag)/itsGame->fpsScale);

DBG_Log("lt", " fn=%d RTT=%d, Classic LT=%.2lf, add=%lf --> FL=%d\n",
frameNumber, maxRoundTripLatency,
(maxRoundTripLatency) / (2.0*CLASSICFRAMETIME), addOneLatency*itsGame->fpsScale, maxFrameLatency);
Expand Down Expand Up @@ -929,7 +932,7 @@ void CNetManager::SendPingCommand(int pingTrips) {
// there & back = 2 trips... send less to/from players in game
// a "poke" is a one-way ping, for just keeping the connection open with less traffic
int pokeTrips = 1;
int pokeDist = activePlayersDistribution;
int pokeDist = itsGame->IsPlaying() ? activePlayersDistribution : 0;

// send periodic poke to those who have NOT finished logging in in hopes that it will help get their connection going
pokeDist |= ~totalDistribution;
Expand Down Expand Up @@ -1303,6 +1306,7 @@ void CNetManager::DoConfig(short senderSlot) {
// transmitting latencyTolerance in terms of frameLatency to keep it as a short value on transmission
itsGame->SetFrameTime(theConfig->frameTime);
itsGame->SetFrameLatency(theConfig->frameLatency, -1);
SDL_Log("DoConfig LT = %lf\n", itsGame->latencyTolerance);
itsGame->SetSpawnOrder((SpawnOrder)theConfig->spawnOrder);
latencyVoteFrame = itsGame->NextFrameForPeriod(AUTOLATENCYPERIOD);
}
Expand All @@ -1312,8 +1316,10 @@ void CNetManager::UpdateLocalConfig() {
CPlayerManager *thePlayerManager = playerTable[itsCommManager->myId].get();

config.frameLatency = gApplication
? gApplication->Get<float>(kLatencyToleranceTag) / itsGame->fpsScale
: 0;
? gApplication->Get<float>(kLatencyToleranceTag) / itsGame->fpsScale : 0;
if (IsAutoLatencyEnabled()) {
config.frameLatency = std::min<short>(config.frameLatency, itsGame->initialFrameLatency);
}
config.frameTime = itsGame->frameTime;
config.spawnOrder = gApplication ? gApplication->Get<short>(kSpawnOrder) : ksHybrid;
config.hullType = gApplication ? gApplication->Number(kHullTypeTag) : 0;
Expand Down
4 changes: 2 additions & 2 deletions src/gui/CRosterWindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -229,8 +229,8 @@ void CRosterWindow::UpdateRoster() {

if (maxRtt > 0 && theNet->IsAutoLatencyEnabled() && !theGame->IsPlaying()) {
// set initial frame latency from client ping/RTT times
maxRtt = std::min(maxRtt+CLASSICFRAMETIME, long(CLASSICFRAMETIME*2*4)); // max of 4 LT on the UI
theGame->SetFrameLatency(theGame->RoundTripToFrameLatency(maxRtt), -1);
maxRtt = std::min(maxRtt, long(CLASSICFRAMETIME*2*2.5)); // max of 2.5 LT
theGame->initialFrameLatency = theGame->RoundTripToFrameLatency(maxRtt);
}

if (theGame->loadedFilename.compare(currentLevel) != 0) {
Expand Down
12 changes: 6 additions & 6 deletions src/gui/CServerWindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,25 +60,25 @@ CServerWindow::CServerWindow(CApplication *app) : CWindow(app, "Server") {
latencyBox->setEnabled(true);
latencyBox->setCallback([this](std::string value) -> bool {
double newLT = std::stod(value);
// let SetFrameLatency() enforce limits on latencyTolerance AND set the pref
// let SetFrameLatency() enforce limits on latencyTolerance
gCurrentGame->SetFrameLatency(std::ceil(newLT/gCurrentGame->fpsScale), -1);

// it might be modified on a bad input so retrieve the computed value
latencyBox->setValue(std::to_string(gCurrentGame->latencyTolerance).substr(0, 5));

// save the pref
gApplication->Set(kLatencyToleranceTag, gCurrentGame->latencyTolerance);

return true;
});

autoLatencyBox = new nanogui::CheckBox(this, "Auto Latency", [this, app](bool checked) {
autoLatencyBox = new nanogui::CheckBox(this, "Auto Latency", [&, app](bool checked) {
long options = app->Number(kServerOptionsTag);
if (checked) {
options |= 1 << kUseAutoLatencyBit;
latencyBox->setEditable(false);
latencyBox->setEnabled(false);
}
else {
options &= ~(long)(1 << kUseAutoLatencyBit);
latencyBox->setEditable(true);
latencyBox->setEnabled(true);
}
app->Set(kServerOptionsTag, options);
((CAvaraAppImpl *)app)->GetNet()->ChangedServerOptions(options);
Expand Down
26 changes: 18 additions & 8 deletions src/net/CUDPComm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1950,22 +1950,32 @@ void CUDPComm::Reconfigure() {
long CUDPComm::GetMaxRoundTrip(short distribution, short *slowPlayerId) {
float maxTrip = 0;
CUDPConnection *conn;
// if set, use "rttx" value to multiply standard deviations
float mult = Debug::GetValue("rttx") / 10.0; // rttx=25 --> mult=2.5
if (mult < 0) {
// 1.3*stdev = ~90.3% prob
mult = 1.3;
}

for (conn = connections; conn; conn = conn->next) {
if (conn->port && (distribution & (1 << conn->myId))) {
// add in 3.0*stdev (~99.7% prob) but max it at CLASSICFRAMETIME (don't add more than 0.5 to LT)
// note: is this really a erlang distribution? if so, what's the proper equation?
float stdev = sqrt(conn->varRoundTripTime);
float rtt = conn->meanRoundTripTime + std::min(3.0*stdev, (1.0*CLASSICFRAMECLOCK));
// add in mult*stdev but max it at CLASSICFRAMETIME (so we don't add more than 0.5 to LT)
// note: is this really a erlang distribution? if so, what's the proper equation?
float rtt = conn->meanRoundTripTime + std::min<float>(mult*stdev, (1.0*CLASSICFRAMECLOCK));
if (rtt > maxTrip) {
maxTrip = rtt;
if (slowPlayerId != nullptr) {
*slowPlayerId = conn->myId;
// SDL_Log("meanRTT[%d] = %.1f, stdevRTT = %.1f, maxRtt=%.1f\n",
// conn->myId,
// conn->meanRoundTripTime*MSEC_PER_GET_CLOCK,
// stdev*MSEC_PER_GET_CLOCK,
// rtt*MSEC_PER_GET_CLOCK);
DBG_Log("rtt", "RTT[%d] = %.1f(%.2f) + min(%.1f * %.1f(%.2f), 64(0.5)) = %.1f(%.2fLT)\n",
conn->myId,
conn->meanRoundTripTime*MSEC_PER_GET_CLOCK,
conn->meanRoundTripTime*MSEC_PER_GET_CLOCK / (2*CLASSICFRAMETIME),
mult,
stdev*MSEC_PER_GET_CLOCK,
stdev*MSEC_PER_GET_CLOCK / (2*CLASSICFRAMETIME),
rtt*MSEC_PER_GET_CLOCK,
rtt*MSEC_PER_GET_CLOCK / (2*CLASSICFRAMETIME));
}
}
}
Expand Down
8 changes: 7 additions & 1 deletion src/net/CUDPConnection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
#define kMaxReceiveQueueLength kMaxTransmitQueueLength // receive as much as is sent

#define RTTSMOOTHFACTOR_UP 100
#define RTTSMOOTHFACTOR_DOWN 200
#define RTTSMOOTHFACTOR_DOWN 100
#define COUNTSMOOTHFACTOR 1000

#define MAX_RESENDS_WITHOUT_RECEIVE 3
Expand Down Expand Up @@ -391,6 +391,12 @@ void CUDPConnection::ValidatePacket(UDPPacketInfo *thePacket, int32_t when) {
myId, thePacket->packet.command, roundTrip, meanRoundTripTime, sqrt(varRoundTripTime), uint16_t(maxValid));
#endif
} else if (commandMultiplier > 0) {
if (thePacket->packet.command == kpPing) {
// decrease ping roundTrip by about 20% because pings aren't as fast as urgent game packets and we
// want to start the game at about the right LT (based on average ping times)
roundTrip *= 0.8;
}

// compute an exponential moving average & variance of the roundTrip time
// see: https://fanf2.user.srcf.net/hermes/doc/antiforgery/stats.pdf
float difference = roundTrip - meanRoundTripTime;
Expand Down

0 comments on commit 4c9e9ec

Please sign in to comment.