Fix tee briefly appearing at previous position when joining

Previously, when connecting to servers repeatedly, the local tee would appear at the position it had on the previous server for a short time when joining.

This (and potentially other bugs) are fixed by clearing all game related `CGameClient` member variables in the `OnReset` function. Additionally, the `OnReset` function is now used in the `OnInit` function to ensure everything is initialized correctly when starting the client and to avoid duplicating the code.

In particular, this bug was limited to use of `cl_predict 1`, because the predicted gameworlds were not being reset when disconnecting, causing the predicted world and character from the previous server to be used. This bug was introduced in version 15.6, which added the predicted gameworlds in #1620. Closes #4339.
This commit is contained in:
Robert Müller 2024-04-11 20:43:02 +02:00
parent e4fd16e6a5
commit 5da64c54fd
3 changed files with 138 additions and 95 deletions

View file

@ -203,9 +203,6 @@ void CGameClient::OnConsoleInit()
Console()->Chain("cl_text_entities_size", ConchainClTextEntitiesSize, this);
Console()->Chain("cl_menu_map", ConchainMenuMap, this);
//
m_SuppressEvents = false;
}
void CGameClient::OnInit()
@ -294,8 +291,6 @@ void CGameClient::OnInit()
++CompCounter;
}
char aBuf[256];
m_GameSkinLoaded = false;
m_ParticlesSkinLoaded = false;
m_EmoticonsSkinLoaded = false;
@ -321,21 +316,9 @@ void CGameClient::OnInit()
m_Menus.RenderLoading(pLoadingDDNetCaption, Localize("Initializing assets"), 1);
}
for(auto &pComponent : m_vpAll)
pComponent->OnReset();
m_ServerMode = SERVERMODE_PURE;
m_aDDRaceMsgSent[0] = false;
m_aDDRaceMsgSent[1] = false;
m_aShowOthers[0] = SHOW_OTHERS_NOT_SET;
m_aShowOthers[1] = SHOW_OTHERS_NOT_SET;
m_aSwitchStateTeam[0] = -1;
m_aSwitchStateTeam[1] = -1;
m_LastZoom = .0;
m_LastScreenAspect = .0;
m_LastDummyConnected = false;
m_GameWorld.m_pCollision = Collision();
m_GameWorld.m_pTuningList = m_aTuningList;
OnReset();
// Set free binds to DDRace binds if it's active
m_Binds.SetDDRaceBinds(true);
@ -363,12 +346,10 @@ void CGameClient::OnInit()
}
int64_t End = time_get();
char aBuf[256];
str_format(aBuf, sizeof(aBuf), "initialisation finished after %.2fms", ((End - Start) * 1000) / (float)time_freq());
Console()->Print(IConsole::OUTPUT_LEVEL_DEBUG, "gameclient", aBuf);
m_GameWorld.m_pCollision = Collision();
m_GameWorld.m_pTuningList = m_aTuningList;
m_MapImages.SetTextureScale(g_Config.m_ClTextEntitiesSize);
// Aggressively try to grab window again since some Windows users report
@ -528,8 +509,6 @@ void CGameClient::OnConnected()
Client()->SetLoadingStateDetail(IClient::LOADING_STATE_DETAIL_GETTING_READY);
m_Menus.RenderLoading(pConnectCaption, Localize("Sending initial client info"), 0, false);
m_ServerMode = SERVERMODE_PURE;
// send the initial info
SendInfo(true);
// we should keep this in for now, because otherwise you can't spectate
@ -537,10 +516,6 @@ void CGameClient::OnConnected()
// snap
Client()->Rcon("crashmeplx");
m_GameWorld.Clear();
m_GameWorld.m_WorldConfig.m_InfiniteAmmo = true;
mem_zero(&m_GameInfo, sizeof(m_GameInfo));
m_PredictedDummyId = -1;
ConfigManager()->ResetGameSettings();
LoadMapSettings();
@ -550,48 +525,94 @@ void CGameClient::OnConnected()
void CGameClient::OnReset()
{
m_aLastNewPredictedTick[0] = -1;
m_aLastNewPredictedTick[1] = -1;
m_aLocalTuneZone[0] = 0;
m_aLocalTuneZone[1] = 0;
m_aExpectingTuningForZone[0] = -1;
m_aExpectingTuningForZone[1] = -1;
m_aReceivedTuning[0] = false;
m_aReceivedTuning[1] = false;
m_aTuning[0] = CTuningParams();
m_aTuning[1] = CTuningParams();
InvalidateSnapshot();
for(auto &Client : m_aClients)
Client.Reset();
m_EditorMovementDelay = 5;
for(auto &pComponent : m_vpAll)
pComponent->OnReset();
m_PredictedTick = -1;
std::fill(std::begin(m_aLastNewPredictedTick), std::end(m_aLastNewPredictedTick), -1);
m_DemoSpecId = SPEC_FOLLOW;
m_aFlagDropTick[TEAM_RED] = 0;
m_aFlagDropTick[TEAM_BLUE] = 0;
m_LastRoundStartTick = -1;
m_LastFlagCarrierRed = -4;
m_LastFlagCarrierBlue = -4;
std::fill(std::begin(m_aCheckInfo), std::end(m_aCheckInfo), -1);
// m_aDDNetVersionStr is initialized once in OnInit
std::fill(std::begin(m_aLastPos), std::end(m_aLastPos), vec2(0.0f, 0.0f));
std::fill(std::begin(m_aLastActive), std::end(m_aLastActive), false);
m_GameOver = false;
m_GamePaused = false;
m_PrevLocalId = -1;
m_SuppressEvents = false;
m_NewTick = false;
m_NewPredictedTick = false;
m_aFlagDropTick[TEAM_RED] = 0;
m_aFlagDropTick[TEAM_BLUE] = 0;
m_ServerMode = SERVERMODE_PURE;
mem_zero(&m_GameInfo, sizeof(m_GameInfo));
m_DemoSpecId = SPEC_FOLLOW;
m_LocalCharacterPos = vec2(0.0f, 0.0f);
m_PredictedPrevChar.Reset();
m_PredictedChar.Reset();
// m_Snap was cleared in InvalidateSnapshot
std::fill(std::begin(m_aLocalTuneZone), std::end(m_aLocalTuneZone), 0);
std::fill(std::begin(m_aReceivedTuning), std::end(m_aReceivedTuning), false);
std::fill(std::begin(m_aExpectingTuningForZone), std::end(m_aExpectingTuningForZone), -1);
std::fill(std::begin(m_aExpectingTuningSince), std::end(m_aExpectingTuningSince), 0);
std::fill(std::begin(m_aTuning), std::end(m_aTuning), CTuningParams());
for(auto &Client : m_aClients)
Client.Reset();
for(auto &Stats : m_aStats)
Stats.Reset();
m_NextChangeInfo = 0;
std::fill(std::begin(m_aLocalIds), std::end(m_aLocalIds), -1);
m_DummyInput = {};
m_HammerInput = {};
m_DummyFire = 0;
m_ReceivedDDNetPlayer = false;
m_Teams.Reset();
m_aDDRaceMsgSent[0] = false;
m_aDDRaceMsgSent[1] = false;
m_aShowOthers[0] = SHOW_OTHERS_NOT_SET;
m_aShowOthers[1] = SHOW_OTHERS_NOT_SET;
m_GameWorld.Clear();
m_GameWorld.m_WorldConfig.m_InfiniteAmmo = true;
m_PredictedWorld.CopyWorld(&m_GameWorld);
m_PrevPredictedWorld.CopyWorld(&m_PredictedWorld);
m_LastZoom = .0;
m_LastScreenAspect = .0;
m_vSnapEntities.clear();
std::fill(std::begin(m_aDDRaceMsgSent), std::end(m_aDDRaceMsgSent), false);
std::fill(std::begin(m_aShowOthers), std::end(m_aShowOthers), SHOW_OTHERS_NOT_SET);
std::fill(std::begin(m_aLastUpdateTick), std::end(m_aLastUpdateTick), 0);
m_PredictedDummyId = -1;
m_IsDummySwapping = false;
m_CharOrder.Reset();
std::fill(std::begin(m_aSwitchStateTeam), std::end(m_aSwitchStateTeam), -1);
// m_aTuningList is reset in LoadMapSettings
m_LastZoom = 0.0f;
m_LastScreenAspect = 0.0f;
m_LastDummyConnected = false;
m_ReceivedDDNetPlayer = false;
m_MultiViewPersonalZoom = 0;
m_MultiViewActivated = false;
m_MultiView.m_IsInit = false;
for(auto &pComponent : m_vpAll)
pComponent->OnReset();
Editor()->ResetMentions();
Editor()->ResetIngameMoved();
@ -2081,7 +2102,7 @@ void CGameClient::OnPredict()
}
// detect mispredictions of other players and make corrections smoother when possible
if(g_Config.m_ClAntiPingSmooth && Predict() && AntiPingPlayers() && m_NewTick && absolute(m_PredictedTick - Client()->PredGameTick(g_Config.m_ClDummy)) <= 1 && absolute(Client()->GameTick(g_Config.m_ClDummy) - Client()->PrevGameTick(g_Config.m_ClDummy)) <= 2)
if(g_Config.m_ClAntiPingSmooth && Predict() && AntiPingPlayers() && m_NewTick && m_PredictedTick >= MIN_TICK && absolute(m_PredictedTick - Client()->PredGameTick(g_Config.m_ClDummy)) <= 1 && absolute(Client()->GameTick(g_Config.m_ClDummy) - Client()->PrevGameTick(g_Config.m_ClDummy)) <= 2)
{
int PredTime = clamp(Client()->GetPredictionTime(), 0, 800);
float SmoothPace = 4 - 1.5f * PredTime / 800.f; // smoothing pace (a lower value will make the smoothing quicker)
@ -2190,16 +2211,15 @@ void CGameClient::CClientStats::Reset()
m_JoinTick = 0;
m_IngameTicks = 0;
m_Active = false;
std::fill(std::begin(m_aFragsWith), std::end(m_aFragsWith), 0);
std::fill(std::begin(m_aDeathsFrom), std::end(m_aDeathsFrom), 0);
m_Frags = 0;
m_Deaths = 0;
m_Suicides = 0;
m_BestSpree = 0;
m_CurrentSpree = 0;
for(int j = 0; j < NUM_WEAPONS; j++)
{
m_aFragsWith[j] = 0;
m_aDeathsFrom[j] = 0;
}
m_FlagGrabs = 0;
m_FlagCaptures = 0;
}
@ -2228,26 +2248,19 @@ void CGameClient::CClientData::UpdateRenderInfo(bool IsTeamPlay)
void CGameClient::CClientData::Reset()
{
m_aName[0] = 0;
m_aClan[0] = 0;
m_UseCustomColor = 0;
m_ColorBody = 0;
m_ColorFeet = 0;
m_aName[0] = '\0';
m_aClan[0] = '\0';
m_Country = -1;
m_aSkinName[0] = '\0';
m_SkinColor = 0;
m_Team = 0;
m_Angle = 0;
m_Emoticon = 0;
m_EmoticonStartTick = -1;
m_EmoticonStartFraction = 0;
m_Active = false;
m_ChatIgnore = false;
m_EmoticonIgnore = false;
m_Friend = false;
m_Foe = false;
m_AuthLevel = AUTHED_NO;
m_Afk = false;
m_Paused = false;
m_Spec = false;
m_SkinInfo.Reset();
m_EmoticonStartTick = -1;
m_Solo = false;
m_Jetpack = false;
@ -2267,14 +2280,40 @@ void CGameClient::CClientData::Reset()
m_DeepFrozen = false;
m_LiveFrozen = false;
m_Predicted.Reset();
m_PrevPredicted.Reset();
m_SkinInfo.Reset();
m_RenderInfo.Reset();
m_Angle = 0.0f;
m_Active = false;
m_ChatIgnore = false;
m_EmoticonIgnore = false;
m_Friend = false;
m_Foe = false;
m_AuthLevel = AUTHED_NO;
m_Afk = false;
m_Paused = false;
m_Spec = false;
std::fill(std::begin(m_aSwitchStates), std::end(m_aSwitchStates), 0);
m_Snapped.m_Tick = -1;
m_Evolved.m_Tick = -1;
m_SpecChar = vec2(0, 0);
m_RenderCur.m_Tick = -1;
m_RenderPrev.m_Tick = -1;
m_RenderPos = vec2(0.0f, 0.0f);
m_IsPredicted = false;
m_IsPredictedLocal = false;
std::fill(std::begin(m_aSmoothStart), std::end(m_aSmoothStart), 0);
std::fill(std::begin(m_aSmoothLen), std::end(m_aSmoothLen), 0);
std::fill(std::begin(m_aPredPos), std::end(m_aPredPos), vec2(0.0f, 0.0f));
std::fill(std::begin(m_aPredTick), std::end(m_aPredTick), 0);
m_SpecCharPresent = false;
mem_zero(m_aSwitchStates, sizeof(m_aSwitchStates));
UpdateRenderInfo(false);
m_SpecChar = vec2(0.0f, 0.0f);
}
void CGameClient::SendSwitchTeam(int Team)

View file

@ -216,8 +216,8 @@ private:
static void ConchainMenuMap(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
// only used in OnPredict
vec2 m_aLastPos[MAX_CLIENTS] = {{0, 0}};
bool m_aLastActive[MAX_CLIENTS] = {false};
vec2 m_aLastPos[MAX_CLIENTS];
bool m_aLastActive[MAX_CLIENTS];
// only used in OnNewSnapshot
bool m_GameOver = false;
@ -269,9 +269,6 @@ public:
bool m_NewPredictedTick;
int m_aFlagDropTick[2];
// TODO: move this
CTuningParams m_aTuning[NUM_DUMMIES];
enum
{
SERVERMODE_PURE = 0,
@ -347,6 +344,7 @@ public:
bool m_aReceivedTuning[NUM_DUMMIES];
int m_aExpectingTuningForZone[NUM_DUMMIES];
int m_aExpectingTuningSince[NUM_DUMMIES];
CTuningParams m_aTuning[NUM_DUMMIES];
// client data
struct CClientData
@ -364,6 +362,7 @@ public:
int m_Emoticon;
float m_EmoticonStartFraction;
int m_EmoticonStartTick;
bool m_Solo;
bool m_Jetpack;
bool m_CollisionDisabled;
@ -406,9 +405,6 @@ public:
CNetObj_Character m_Snapped;
CNetObj_Character m_Evolved;
void UpdateRenderInfo(bool IsTeamPlay);
void Reset();
// rendered characters
CNetObj_Character m_RenderCur;
CNetObj_Character m_RenderPrev;
@ -421,6 +417,9 @@ public:
int m_aPredTick[200];
bool m_SpecCharPresent;
vec2 m_SpecChar;
void UpdateRenderInfo(bool IsTeamPlay);
void Reset();
};
CClientData m_aClients[MAX_CLIENTS];
@ -652,7 +651,7 @@ public:
};
SClientGameSkin m_GameSkin;
bool m_GameSkinLoaded;
bool m_GameSkinLoaded = false;
struct SClientParticlesSkin
{
@ -668,7 +667,7 @@ public:
};
SClientParticlesSkin m_ParticlesSkin;
bool m_ParticlesSkinLoaded;
bool m_ParticlesSkinLoaded = false;
struct SClientEmoticonsSkin
{
@ -676,7 +675,7 @@ public:
};
SClientEmoticonsSkin m_EmoticonsSkin;
bool m_EmoticonsSkinLoaded;
bool m_EmoticonsSkinLoaded = false;
struct SClientHudSkin
{
@ -713,7 +712,7 @@ public:
};
SClientHudSkin m_HudSkin;
bool m_HudSkinLoaded;
bool m_HudSkinLoaded = false;
struct SClientExtrasSkin
{
@ -722,7 +721,7 @@ public:
};
SClientExtrasSkin m_ExtrasSkin;
bool m_ExtrasSkinLoaded;
bool m_ExtrasSkinLoaded = false;
const std::vector<CSnapEntities> &SnapEntities() { return m_vSnapEntities; }

View file

@ -118,6 +118,11 @@ public:
std::list<int> m_Ids; // reverse of the order in the gameworld, since entities will be inserted in reverse
CCharOrder()
{
Reset();
}
void Reset()
{
m_Ids.clear();
for(int i = 0; i < MAX_CLIENTS; i++)
m_Ids.push_back(i);
}