Skip to content

Commit

Permalink
Allow client to re send past inputs to the server
Browse files Browse the repository at this point in the history
  • Loading branch information
sjrc6 committed Jun 3, 2024
1 parent 64fec2c commit 8e5c8c4
Show file tree
Hide file tree
Showing 6 changed files with 83 additions and 2 deletions.
58 changes: 58 additions & 0 deletions src/engine/client/client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,13 @@ void CClient::SendInput()
Msg.AddInt(m_aPredTick[g_Config.m_ClDummy]);
Msg.AddInt(Size);

// copy to sent inputs
int SendIndex = m_aPredTick[g_Config.m_ClDummy] % 200;
m_aSentInputs[i][SendIndex].m_AckGameTick = m_aAckGameTick[i];
m_aSentInputs[i][SendIndex].m_PredTick = m_aPredTick[g_Config.m_ClDummy];
m_aSentInputs[i][SendIndex].Size = Size;
mem_copy(m_aSentInputs[i][SendIndex].m_aData, m_aInputs[i][m_aCurrentInput[i]].m_aData, sizeof(m_aSentInputs[i][SendIndex].m_aData));

m_aInputs[i][m_aCurrentInput[i]].m_Tick = m_aPredTick[g_Config.m_ClDummy];
m_aInputs[i][m_aCurrentInput[i]].m_PredictedTime = m_PredictedTime.Get(Now);
m_aInputs[i][m_aCurrentInput[i]].m_PredictionMargin = PredictionMargin() * time_freq() / 1000;
Expand All @@ -295,6 +302,53 @@ void CClient::SendInput()
Force = true;
}
}
if(g_Config.m_ClResendInputs && m_ServerCapabilities.m_SortInputs)
{
// for each 20ms of prediction margin (1 tick duration) we can usefully send 1 extra input to the server
// incase the previous attempt was dropped
int ResendTicks = PredictionMargin() / (1000 / GameTickSpeed());
for(int Dummy = 0; Dummy < NUM_DUMMIES; Dummy++)
{
if(!m_DummyConnected && Dummy != 0)
{
break;
}
int i = g_Config.m_ClDummy ^ Dummy;

int FlushTick = 0;

// figure out which tick will be the last one so we can flush the network
for(int Tick = 0; Tick < ResendTicks; Tick++)
{
int TargetTick = m_aPredTick[g_Config.m_ClDummy] - Tick;
int SendIndex = TargetTick % 200;
if(m_aSentInputs[i][SendIndex].m_PredTick == TargetTick)
FlushTick = Tick;
}

for(int Tick = 0; Tick < ResendTicks; Tick++)
{
int TargetTick = m_aPredTick[g_Config.m_ClDummy] - Tick;
int SendIndex = TargetTick % 200;

// we only want to send inputs that we already sent the server normally
if(m_aSentInputs[i][SendIndex].m_PredTick == TargetTick)
{
CMsgPacker Msg(NETMSG_INPUT, true);
Msg.AddInt(m_aSentInputs[i][SendIndex].m_AckGameTick);
Msg.AddInt(m_aSentInputs[i][SendIndex].m_PredTick);
Msg.AddInt(m_aSentInputs[i][SendIndex].Size);
for(int k = 0; k < m_aSentInputs[i][SendIndex].Size / 4; k++)
Msg.AddInt(m_aSentInputs[i][SendIndex].m_aData[k]);

if(Tick == FlushTick || Tick == ResendTicks - 1)
SendMsg(i, &Msg, MSGFLAG_FLUSH);
else
SendMsg(i, &Msg, 0);
}
}
}
}
}

const char *CClient::LatestVersion() const
Expand Down Expand Up @@ -1311,6 +1365,10 @@ static CServerCapabilities GetServerCapabilities(int Version, int Flags)
{
Result.m_SyncWeaponInput = Flags & SERVERCAPFLAG_SYNCWEAPONINPUT;
}
if(Version >= 6)
{
Result.m_SortInputs = Flags & SERVERCAPFLAG_SORTINPUTS;
}
return Result;
}

Expand Down
10 changes: 10 additions & 0 deletions src/engine/client/client.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ class CServerCapabilities
bool m_PingEx = false;
bool m_AllowDummy = false;
bool m_SyncWeaponInput = false;
bool m_SortInputs = false;
};

class CClient : public IClient, public CDemoPlayer::IListener
Expand Down Expand Up @@ -175,6 +176,15 @@ class CClient : public IClient, public CDemoPlayer::IListener
int64_t m_Time;
} m_aInputs[NUM_DUMMIES][200];

struct CSentInput
{
int m_AckGameTick;
int m_PredTick;
int Size;
int m_aData[MAX_INPUT_SIZE];
};
CSentInput m_aSentInputs[NUM_DUMMIES][200];

int m_aCurrentInput[NUM_DUMMIES] = {0, 0};
bool m_LastDummy = false;
bool m_DummySendConnInfo = false;
Expand Down
2 changes: 1 addition & 1 deletion src/engine/server/server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1199,7 +1199,7 @@ void CServer::SendCapabilities(int ClientId)
{
CMsgPacker Msg(NETMSG_CAPABILITIES, true);
Msg.AddInt(SERVERCAP_CURVERSION); // version
Msg.AddInt(SERVERCAPFLAG_DDNET | SERVERCAPFLAG_CHATTIMEOUTCODE | SERVERCAPFLAG_ANYPLAYERFLAG | SERVERCAPFLAG_PINGEX | SERVERCAPFLAG_ALLOWDUMMY | SERVERCAPFLAG_SYNCWEAPONINPUT); // flags
Msg.AddInt(SERVERCAPFLAG_DDNET | SERVERCAPFLAG_CHATTIMEOUTCODE | SERVERCAPFLAG_ANYPLAYERFLAG | SERVERCAPFLAG_PINGEX | SERVERCAPFLAG_ALLOWDUMMY | SERVERCAPFLAG_SYNCWEAPONINPUT | SERVERCAPFLAG_SORTINPUTS); // flags
SendMsg(&Msg, MSGFLAG_VITAL, ClientId);
}

Expand Down
1 change: 1 addition & 0 deletions src/engine/shared/config_variables.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ MACRO_CONFIG_INT(ClAntiPingSmooth, cl_antiping_smooth, 0, 0, 1, CFGFLAG_CLIENT |
MACRO_CONFIG_INT(ClAntiPingGunfire, cl_antiping_gunfire, 1, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Predict gunfire and show predicted weapon physics (with cl_antiping_grenade 1 and cl_antiping_weapons 1)")
MACRO_CONFIG_INT(ClPredictionMargin, cl_prediction_margin, 10, 1, 300, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Prediction margin in ms (adds latency, can reduce lag from ping jumps)")
MACRO_CONFIG_INT(ClSubTickAiming, cl_sub_tick_aiming, 0, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Send aiming data at sub-tick accuracy")
MACRO_CONFIG_INT(ClResendInputs, cl_resend_inputs, 1, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Send Inputs to server multiple times when prediction margin is high enough")

MACRO_CONFIG_INT(ClNameplates, cl_nameplates, 1, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Show name plates")
MACRO_CONFIG_INT(ClAfkEmote, cl_afk_emote, 1, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Show zzz emote next to afk players")
Expand Down
3 changes: 2 additions & 1 deletion src/engine/shared/protocol_ex.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,14 @@ enum
UNPACKMESSAGE_OK,
UNPACKMESSAGE_ANSWER,

SERVERCAP_CURVERSION = 5,
SERVERCAP_CURVERSION = 6,
SERVERCAPFLAG_DDNET = 1 << 0,
SERVERCAPFLAG_CHATTIMEOUTCODE = 1 << 1,
SERVERCAPFLAG_ANYPLAYERFLAG = 1 << 2,
SERVERCAPFLAG_PINGEX = 1 << 3,
SERVERCAPFLAG_ALLOWDUMMY = 1 << 4,
SERVERCAPFLAG_SYNCWEAPONINPUT = 1 << 5,
SERVERCAPFLAG_SORTINPUTS = 1 << 6,
};

void RegisterUuids(CUuidManager *pManager);
Expand Down
11 changes: 11 additions & 0 deletions src/game/client/components/controls.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,8 @@ void CControls::OnMessage(int Msg, void *pRawMsg)
int CControls::SnapInput(int *pData)
{
static int64_t LastSendTime = 0;
static int HotSendCount = 0;

bool Send = false;

// update player state
Expand Down Expand Up @@ -339,10 +341,19 @@ int CControls::SnapInput(int *pData)
else if(m_aInputData[g_Config.m_ClDummy].m_PrevWeapon != m_aLastData[g_Config.m_ClDummy].m_PrevWeapon)
Send = true;

HotSendCount = std::max(0, HotSendCount - 1);

if(Send)
HotSendCount = 5;

// send at at least 10hz
if(time_get() > LastSendTime + time_freq() / 25)
Send = true;

// always send for 5 ticks in a row when a new input happens
if(HotSendCount > 0)
Send = true;

if(m_pClient->m_Snap.m_pLocalCharacter && m_pClient->m_Snap.m_pLocalCharacter->m_Weapon == WEAPON_NINJA && (m_aInputData[g_Config.m_ClDummy].m_Direction || m_aInputData[g_Config.m_ClDummy].m_Jump || m_aInputData[g_Config.m_ClDummy].m_Hook))
Send = true;
}
Expand Down

0 comments on commit 8e5c8c4

Please sign in to comment.