mirror of
https://github.com/ddnet/ddnet.git
synced 2024-09-20 09:34:19 +00:00
Merge #2502
2502: Improved dummy switching r=def- a=Fireball-Teeworlds - Fix a use-after-free when there are no new snapshots for the cl_dummy tee after the switch: #2499 . - When one of the tees is hooked, cycling through them will no longer show phantom hook for the other tee: https://youtu.be/mxVT4pdyGnU. - Dummy switch might happen a bit quicker since it doesn't depend on receiving new snapshots. - Simplified code: m_LastDummy2 is no more. Co-authored-by: Fireball <fireball.teeworlds@gmail.com>
This commit is contained in:
commit
69593a3191
|
@ -221,6 +221,7 @@ public:
|
||||||
virtual void OnRconType(bool UsernameReq) = 0;
|
virtual void OnRconType(bool UsernameReq) = 0;
|
||||||
virtual void OnRconLine(const char *pLine) = 0;
|
virtual void OnRconLine(const char *pLine) = 0;
|
||||||
virtual void OnInit() = 0;
|
virtual void OnInit() = 0;
|
||||||
|
virtual void InvalidateSnapshot() = 0;
|
||||||
virtual void OnNewSnapshot() = 0;
|
virtual void OnNewSnapshot() = 0;
|
||||||
virtual void OnEnterGame() = 0;
|
virtual void OnEnterGame() = 0;
|
||||||
virtual void OnShutdown() = 0;
|
virtual void OnShutdown() = 0;
|
||||||
|
|
|
@ -339,7 +339,6 @@ CClient::CClient() : m_DemoPlayer(&m_SnapshotDelta)
|
||||||
m_CurrentInput[0] = 0;
|
m_CurrentInput[0] = 0;
|
||||||
m_CurrentInput[1] = 0;
|
m_CurrentInput[1] = 0;
|
||||||
m_LastDummy = 0;
|
m_LastDummy = 0;
|
||||||
m_LastDummy2 = 0;
|
|
||||||
|
|
||||||
mem_zero(&m_aInputs, sizeof(m_aInputs));
|
mem_zero(&m_aInputs, sizeof(m_aInputs));
|
||||||
|
|
||||||
|
@ -505,12 +504,6 @@ void CClient::SendInput()
|
||||||
if(m_PredTick[g_Config.m_ClDummy] <= 0)
|
if(m_PredTick[g_Config.m_ClDummy] <= 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if(m_LastDummy != (bool)g_Config.m_ClDummy)
|
|
||||||
{
|
|
||||||
m_LastDummy = g_Config.m_ClDummy;
|
|
||||||
GameClient()->OnDummySwap();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Force = false;
|
bool Force = false;
|
||||||
// fetch input
|
// fetch input
|
||||||
for(int Dummy = 0; Dummy < 2; Dummy++)
|
for(int Dummy = 0; Dummy < 2; Dummy++)
|
||||||
|
@ -848,6 +841,9 @@ void CClient::DummyDisconnect(const char *pReason)
|
||||||
m_NetClient[CLIENT_DUMMY].Disconnect(pReason);
|
m_NetClient[CLIENT_DUMMY].Disconnect(pReason);
|
||||||
g_Config.m_ClDummy = 0;
|
g_Config.m_ClDummy = 0;
|
||||||
m_RconAuthed[1] = 0;
|
m_RconAuthed[1] = 0;
|
||||||
|
m_aSnapshots[1][SNAP_CURRENT] = 0;
|
||||||
|
m_aSnapshots[1][SNAP_PREV] = 0;
|
||||||
|
m_ReceivedSnapshots[1] = 0;
|
||||||
m_DummyConnected = false;
|
m_DummyConnected = false;
|
||||||
GameClient()->OnDummyDisconnect();
|
GameClient()->OnDummyDisconnect();
|
||||||
}
|
}
|
||||||
|
@ -2574,8 +2570,15 @@ void CClient::Update()
|
||||||
Disconnect();
|
Disconnect();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if(State() == IClient::STATE_ONLINE && m_ReceivedSnapshots[g_Config.m_ClDummy] >= 3)
|
else if(State() == IClient::STATE_ONLINE)
|
||||||
{
|
{
|
||||||
|
if(m_LastDummy != (bool)g_Config.m_ClDummy)
|
||||||
|
{
|
||||||
|
// Invalidate references to !m_ClDummy snapshots
|
||||||
|
GameClient()->InvalidateSnapshot();
|
||||||
|
GameClient()->OnDummySwap();
|
||||||
|
}
|
||||||
|
|
||||||
if(m_ReceivedSnapshots[!g_Config.m_ClDummy] >= 3)
|
if(m_ReceivedSnapshots[!g_Config.m_ClDummy] >= 3)
|
||||||
{
|
{
|
||||||
// switch dummy snapshot
|
// switch dummy snapshot
|
||||||
|
@ -2605,92 +2608,100 @@ void CClient::Update()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// switch snapshot
|
if(m_ReceivedSnapshots[g_Config.m_ClDummy] >= 3)
|
||||||
int Repredict = 0;
|
|
||||||
int64 Freq = time_freq();
|
|
||||||
int64 Now = m_GameTime[g_Config.m_ClDummy].Get(time_get());
|
|
||||||
int64 PredNow = m_PredictedTime.Get(time_get());
|
|
||||||
|
|
||||||
while(1)
|
|
||||||
{
|
{
|
||||||
CSnapshotStorage::CHolder *pCur = m_aSnapshots[g_Config.m_ClDummy][SNAP_CURRENT];
|
// switch snapshot
|
||||||
int64 TickStart = (pCur->m_Tick)*time_freq()/50;
|
int Repredict = 0;
|
||||||
|
int64 Freq = time_freq();
|
||||||
|
int64 Now = m_GameTime[g_Config.m_ClDummy].Get(time_get());
|
||||||
|
int64 PredNow = m_PredictedTime.Get(time_get());
|
||||||
|
|
||||||
if(TickStart < Now)
|
if(m_LastDummy != (bool)g_Config.m_ClDummy && m_aSnapshots[g_Config.m_ClDummy][SNAP_CURRENT] && m_aSnapshots[g_Config.m_ClDummy][SNAP_PREV])
|
||||||
{
|
{
|
||||||
CSnapshotStorage::CHolder *pNext = m_aSnapshots[g_Config.m_ClDummy][SNAP_CURRENT]->m_pNext;
|
// Load snapshot for m_ClDummy
|
||||||
if(pNext)
|
GameClient()->OnNewSnapshot();
|
||||||
|
Repredict = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
while(1)
|
||||||
|
{
|
||||||
|
CSnapshotStorage::CHolder *pCur = m_aSnapshots[g_Config.m_ClDummy][SNAP_CURRENT];
|
||||||
|
int64 TickStart = (pCur->m_Tick)*time_freq()/50;
|
||||||
|
|
||||||
|
if(TickStart < Now)
|
||||||
{
|
{
|
||||||
m_aSnapshots[g_Config.m_ClDummy][SNAP_PREV] = m_aSnapshots[g_Config.m_ClDummy][SNAP_CURRENT];
|
CSnapshotStorage::CHolder *pNext = m_aSnapshots[g_Config.m_ClDummy][SNAP_CURRENT]->m_pNext;
|
||||||
m_aSnapshots[g_Config.m_ClDummy][SNAP_CURRENT] = pNext;
|
if(pNext)
|
||||||
|
|
||||||
// set ticks
|
|
||||||
m_CurGameTick[g_Config.m_ClDummy] = m_aSnapshots[g_Config.m_ClDummy][SNAP_CURRENT]->m_Tick;
|
|
||||||
m_PrevGameTick[g_Config.m_ClDummy] = m_aSnapshots[g_Config.m_ClDummy][SNAP_PREV]->m_Tick;
|
|
||||||
|
|
||||||
if(m_LastDummy2 == (bool)g_Config.m_ClDummy && m_aSnapshots[g_Config.m_ClDummy][SNAP_CURRENT] && m_aSnapshots[g_Config.m_ClDummy][SNAP_PREV])
|
|
||||||
{
|
{
|
||||||
GameClient()->OnNewSnapshot();
|
m_aSnapshots[g_Config.m_ClDummy][SNAP_PREV] = m_aSnapshots[g_Config.m_ClDummy][SNAP_CURRENT];
|
||||||
Repredict = 1;
|
m_aSnapshots[g_Config.m_ClDummy][SNAP_CURRENT] = pNext;
|
||||||
|
|
||||||
|
// set ticks
|
||||||
|
m_CurGameTick[g_Config.m_ClDummy] = m_aSnapshots[g_Config.m_ClDummy][SNAP_CURRENT]->m_Tick;
|
||||||
|
m_PrevGameTick[g_Config.m_ClDummy] = m_aSnapshots[g_Config.m_ClDummy][SNAP_PREV]->m_Tick;
|
||||||
|
|
||||||
|
if(m_aSnapshots[g_Config.m_ClDummy][SNAP_CURRENT] && m_aSnapshots[g_Config.m_ClDummy][SNAP_PREV])
|
||||||
|
{
|
||||||
|
GameClient()->OnNewSnapshot();
|
||||||
|
Repredict = 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(m_LastDummy2 != (bool)g_Config.m_ClDummy)
|
|
||||||
{
|
|
||||||
m_LastDummy2 = g_Config.m_ClDummy;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(m_aSnapshots[g_Config.m_ClDummy][SNAP_CURRENT] && m_aSnapshots[g_Config.m_ClDummy][SNAP_PREV])
|
if(m_aSnapshots[g_Config.m_ClDummy][SNAP_CURRENT] && m_aSnapshots[g_Config.m_ClDummy][SNAP_PREV])
|
||||||
{
|
|
||||||
int64 CurtickStart = (m_aSnapshots[g_Config.m_ClDummy][SNAP_CURRENT]->m_Tick)*time_freq()/50;
|
|
||||||
int64 PrevtickStart = (m_aSnapshots[g_Config.m_ClDummy][SNAP_PREV]->m_Tick)*time_freq()/50;
|
|
||||||
int PrevPredTick = (int)(PredNow*50/time_freq());
|
|
||||||
int NewPredTick = PrevPredTick+1;
|
|
||||||
|
|
||||||
m_GameIntraTick[g_Config.m_ClDummy] = (Now - PrevtickStart) / (float)(CurtickStart-PrevtickStart);
|
|
||||||
m_GameTickTime[g_Config.m_ClDummy] = (Now - PrevtickStart) / (float)Freq; //(float)SERVER_TICK_SPEED);
|
|
||||||
|
|
||||||
CurtickStart = NewPredTick*time_freq()/50;
|
|
||||||
PrevtickStart = PrevPredTick*time_freq()/50;
|
|
||||||
m_PredIntraTick[g_Config.m_ClDummy] = (PredNow - PrevtickStart) / (float)(CurtickStart-PrevtickStart);
|
|
||||||
|
|
||||||
if(NewPredTick < m_aSnapshots[g_Config.m_ClDummy][SNAP_PREV]->m_Tick-SERVER_TICK_SPEED || NewPredTick > m_aSnapshots[g_Config.m_ClDummy][SNAP_PREV]->m_Tick+SERVER_TICK_SPEED)
|
|
||||||
{
|
{
|
||||||
m_pConsole->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "client", "prediction time reset!");
|
int64 CurtickStart = (m_aSnapshots[g_Config.m_ClDummy][SNAP_CURRENT]->m_Tick)*time_freq()/50;
|
||||||
m_PredictedTime.Init(m_aSnapshots[g_Config.m_ClDummy][SNAP_CURRENT]->m_Tick*time_freq()/50);
|
int64 PrevtickStart = (m_aSnapshots[g_Config.m_ClDummy][SNAP_PREV]->m_Tick)*time_freq()/50;
|
||||||
|
int PrevPredTick = (int)(PredNow*50/time_freq());
|
||||||
|
int NewPredTick = PrevPredTick+1;
|
||||||
|
|
||||||
|
m_GameIntraTick[g_Config.m_ClDummy] = (Now - PrevtickStart) / (float)(CurtickStart-PrevtickStart);
|
||||||
|
m_GameTickTime[g_Config.m_ClDummy] = (Now - PrevtickStart) / (float)Freq; //(float)SERVER_TICK_SPEED);
|
||||||
|
|
||||||
|
CurtickStart = NewPredTick*time_freq()/50;
|
||||||
|
PrevtickStart = PrevPredTick*time_freq()/50;
|
||||||
|
m_PredIntraTick[g_Config.m_ClDummy] = (PredNow - PrevtickStart) / (float)(CurtickStart-PrevtickStart);
|
||||||
|
|
||||||
|
if(NewPredTick < m_aSnapshots[g_Config.m_ClDummy][SNAP_PREV]->m_Tick-SERVER_TICK_SPEED || NewPredTick > m_aSnapshots[g_Config.m_ClDummy][SNAP_PREV]->m_Tick+SERVER_TICK_SPEED)
|
||||||
|
{
|
||||||
|
m_pConsole->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "client", "prediction time reset!");
|
||||||
|
m_PredictedTime.Init(m_aSnapshots[g_Config.m_ClDummy][SNAP_CURRENT]->m_Tick*time_freq()/50);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(NewPredTick > m_PredTick[g_Config.m_ClDummy])
|
||||||
|
{
|
||||||
|
m_PredTick[g_Config.m_ClDummy] = NewPredTick;
|
||||||
|
Repredict = 1;
|
||||||
|
|
||||||
|
// send input
|
||||||
|
SendInput();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(NewPredTick > m_PredTick[g_Config.m_ClDummy])
|
// only do sane predictions
|
||||||
|
if(Repredict)
|
||||||
{
|
{
|
||||||
m_PredTick[g_Config.m_ClDummy] = NewPredTick;
|
if(m_PredTick[g_Config.m_ClDummy] > m_CurGameTick[g_Config.m_ClDummy] && m_PredTick[g_Config.m_ClDummy] < m_CurGameTick[g_Config.m_ClDummy]+50)
|
||||||
Repredict = 1;
|
GameClient()->OnPredict();
|
||||||
|
}
|
||||||
|
|
||||||
// send input
|
// fetch server info if we don't have it
|
||||||
SendInput();
|
if(State() >= IClient::STATE_LOADING &&
|
||||||
|
m_CurrentServerInfoRequestTime >= 0 &&
|
||||||
|
time_get() > m_CurrentServerInfoRequestTime)
|
||||||
|
{
|
||||||
|
m_ServerBrowser.RequestCurrentServer(m_ServerAddress);
|
||||||
|
m_CurrentServerInfoRequestTime = time_get()+time_freq()*2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// only do sane predictions
|
m_LastDummy = (bool)g_Config.m_ClDummy;
|
||||||
if(Repredict)
|
|
||||||
{
|
|
||||||
if(m_PredTick[g_Config.m_ClDummy] > m_CurGameTick[g_Config.m_ClDummy] && m_PredTick[g_Config.m_ClDummy] < m_CurGameTick[g_Config.m_ClDummy]+50)
|
|
||||||
GameClient()->OnPredict();
|
|
||||||
}
|
|
||||||
|
|
||||||
// fetch server info if we don't have it
|
|
||||||
if(State() >= IClient::STATE_LOADING &&
|
|
||||||
m_CurrentServerInfoRequestTime >= 0 &&
|
|
||||||
time_get() > m_CurrentServerInfoRequestTime)
|
|
||||||
{
|
|
||||||
m_ServerBrowser.RequestCurrentServer(m_ServerAddress);
|
|
||||||
m_CurrentServerInfoRequestTime = time_get()+time_freq()*2;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// STRESS TEST: join the server again
|
// STRESS TEST: join the server again
|
||||||
|
|
|
@ -186,7 +186,6 @@ class CClient : public IClient, public CDemoPlayer::IListener
|
||||||
|
|
||||||
int m_CurrentInput[2];
|
int m_CurrentInput[2];
|
||||||
bool m_LastDummy;
|
bool m_LastDummy;
|
||||||
bool m_LastDummy2;
|
|
||||||
bool m_DummySendConnInfo;
|
bool m_DummySendConnInfo;
|
||||||
|
|
||||||
// graphs
|
// graphs
|
||||||
|
|
|
@ -511,10 +511,10 @@ void CGameClient::OnConnected()
|
||||||
|
|
||||||
void CGameClient::OnReset()
|
void CGameClient::OnReset()
|
||||||
{
|
{
|
||||||
// clear out the invalid pointers
|
|
||||||
m_LastNewPredictedTick[0] = -1;
|
m_LastNewPredictedTick[0] = -1;
|
||||||
m_LastNewPredictedTick[1] = -1;
|
m_LastNewPredictedTick[1] = -1;
|
||||||
mem_zero(&g_GameClient.m_Snap, sizeof(g_GameClient.m_Snap));
|
|
||||||
|
InvalidateSnapshot();
|
||||||
|
|
||||||
for(int i = 0; i < MAX_CLIENTS; i++)
|
for(int i = 0; i < MAX_CLIENTS; i++)
|
||||||
m_aClients[i].Reset();
|
m_aClients[i].Reset();
|
||||||
|
@ -1084,13 +1084,18 @@ static CGameInfo GetGameInfo(const CNetObj_GameInfoEx *pInfoEx, int InfoExSize,
|
||||||
return Info;
|
return Info;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CGameClient::OnNewSnapshot()
|
void CGameClient::InvalidateSnapshot()
|
||||||
{
|
{
|
||||||
m_NewTick = true;
|
// clear all pointers
|
||||||
|
|
||||||
// clear out the invalid pointers
|
|
||||||
mem_zero(&g_GameClient.m_Snap, sizeof(g_GameClient.m_Snap));
|
mem_zero(&g_GameClient.m_Snap, sizeof(g_GameClient.m_Snap));
|
||||||
m_Snap.m_LocalClientID = -1;
|
m_Snap.m_LocalClientID = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CGameClient::OnNewSnapshot()
|
||||||
|
{
|
||||||
|
InvalidateSnapshot();
|
||||||
|
|
||||||
|
m_NewTick = true;
|
||||||
|
|
||||||
// secure snapshot
|
// secure snapshot
|
||||||
{
|
{
|
||||||
|
|
|
@ -346,6 +346,7 @@ public:
|
||||||
virtual void OnConsoleInit();
|
virtual void OnConsoleInit();
|
||||||
virtual void OnStateChange(int NewState, int OldState);
|
virtual void OnStateChange(int NewState, int OldState);
|
||||||
virtual void OnMessage(int MsgId, CUnpacker *pUnpacker, bool IsDummy = 0);
|
virtual void OnMessage(int MsgId, CUnpacker *pUnpacker, bool IsDummy = 0);
|
||||||
|
virtual void InvalidateSnapshot();
|
||||||
virtual void OnNewSnapshot();
|
virtual void OnNewSnapshot();
|
||||||
virtual void OnPredict();
|
virtual void OnPredict();
|
||||||
virtual void OnActivateEditor();
|
virtual void OnActivateEditor();
|
||||||
|
|
Loading…
Reference in a new issue