Various local prediction and input processing related fixes

This commit is contained in:
karlberg
2021-05-15 15:10:17 -07:00
parent 61c24ee265
commit c6ea0c0a46
13 changed files with 78 additions and 74 deletions
@@ -113,73 +113,57 @@ namespace Multiplayer
[[maybe_unused]] const AzNetworking::PacketEncodingBuffer& clientState
)
{
// After receiving the first input from the client, start the update event to check for slow hacking
if (!m_updateBankedTimeEvent.IsScheduled())
{
m_updateBankedTimeEvent.Enqueue(sv_InputUpdateTimeMs, true);
}
if (invokingConnection == nullptr)
{
// Discard any input messages that were locally dispatched or sent by disconnected clients
return;
}
const ClientInputId clientInputId = inputArray[0].GetClientInputId();
if (clientInputId <= m_lastClientInputId)
{
AZLOG(NET_Prediction, "Discarding old or out of order move input (current: %u, received %u)",
aznumeric_cast<uint32_t>(m_lastClientInputId), aznumeric_cast<uint32_t>(clientInputId));
return;
}
// After receiving the first input from the client, start the update event to check for slow hacking
if (!m_updateBankedTimeEvent.IsScheduled())
{
m_updateBankedTimeEvent.Enqueue(sv_InputUpdateTimeMs, true);
}
const AZ::TimeMs currentTimeMs = AZ::GetElapsedTimeMs();
const double clientInputRateSec = static_cast<double>(static_cast<AZ::TimeMs>(cl_InputRateMs)) / 1000.0;
m_lastInputReceivedTimeMs = currentTimeMs;
// Keep track of last inputs received, also allows us to update frame ids
m_lastInputReceived = inputArray;
// Figure out which index from the input array we want
// we start at the oldest input that has not been processed
int32_t inputArrayIndex = -1;
for (int32_t i = NetworkInputArray::MaxElements - 1; i >= 0; --i)
{
// Find an input that is newer than the last one we processed
if (m_lastInputReceived[i].GetClientInputId() > GetLastInputId())
{
inputArrayIndex = i;
break;
}
}
if (inputArrayIndex < 0)
{
AZLOG
(
NET_Prediction,
"Discarding old or out of order move input (current: %u, received %u)",
aznumeric_cast<uint32_t>(GetLastInputId()),
aznumeric_cast<uint32_t>(m_lastInputReceived[0].GetClientInputId())
);
return;
}
bool lostInput = false;
if (GetLastInputId() < inputArray.GetPreviousInputId())
{
// last move id processed is older than the previous input id, we missed some input packets
lostInput = true;
}
SetLastInputId(m_lastInputReceived[0].GetClientInputId()); // Set this variable in case of migration
while (inputArrayIndex >= 0)
while (m_lastClientInputId < clientInputId)
{
NetworkInput& input = m_lastInputReceived[inputArrayIndex];
++m_lastClientInputId;
// Figure out which index from the input array we want
// If we have skipped an id, check if it was sent to us in the array. If we have lost too many, just use the oldest one in the array
const uint32_t deltaFrameId = aznumeric_cast<uint32_t>(clientInputId - m_lastClientInputId); // always >= 0 because of while loop check
const uint32_t inputArrayIdx = AZStd::min(deltaFrameId, NetworkInputArray::MaxElements - 1);
const bool lostInput = deltaFrameId >= NetworkInputArray::MaxElements; // For logging only
NetworkInput &input = m_lastInputReceived[inputArrayIdx];
input.SetClientInputId(m_lastClientInputId);
// Anticheat, if we're receiving too many inputs, and fall outside our variable latency input window
// Discard move input events, client may be speed hacking
if (m_clientBankedTime < sv_MaxBankTimeWindowSec)
{
m_clientBankedTime = AZStd::min(m_clientBankedTime + clientInputRateSec, (double)sv_MaxBankTimeWindowSec); // clamp to boundary
{
ScopedAlterTime scopedTime(input.GetHostFrameId(), input.GetHostTimeMs(), invokingConnection->GetConnectionId());
GetNetBindComponent()->ProcessInput(input, static_cast<float>(clientInputRateSec));
}
if (lostInput)
{
AZLOG(NET_Prediction, "InputLost InputId=%u", aznumeric_cast<uint32_t>(input.GetClientInputId()));
@@ -193,7 +177,6 @@ namespace Multiplayer
{
AZLOG(NET_Prediction, "Dropped InputId=%u", aznumeric_cast<uint32_t>(input.GetClientInputId()));
}
--inputArrayIndex;
}
if (sv_EnableCorrections && (currentTimeMs - m_lastCorrectionSentTimeMs > sv_MinCorrectionTimeMs))
@@ -205,6 +188,14 @@ namespace Multiplayer
const AZ::HashValue32 localAuthorityHash = hashSerializer.GetHash();
AZLOG
(
NET_Prediction,
"Hash values for ProcessInput: client=%u, server=%u",
aznumeric_cast<uint32_t>(stateHash),
aznumeric_cast<uint32_t>(localAuthorityHash)
);
if (stateHash != localAuthorityHash)
{
// Produce correction for client
@@ -542,21 +533,15 @@ namespace Multiplayer
m_inputHistory.PopFront();
}
const size_t inputHistorySize = m_inputHistory.Size();
const int64_t inputHistorySize = aznumeric_cast<int64_t>(m_inputHistory.Size());
// Form the rest of the input array using the n most recent elements in the history buffer
// NOTE: inputArray[0] has already been initialized hence start at i = 1
for (uint32_t i = 1; i < NetworkInputArray::MaxElements; ++i)
for (int64_t i = 1; i < aznumeric_cast<int64_t>(NetworkInputArray::MaxElements); ++i)
{
if (i < inputHistorySize)
{
inputArray[i] = m_inputHistory[inputHistorySize - 1 - i];
}
else // History is too small?
{
// Plug in the most recent input
inputArray[i] = input;
}
// Clamp to oldest element if history is too small
const int64_t historyIndex = AZStd::max<int64_t>(inputHistorySize - 1 - i, 0);
inputArray[i] = m_inputHistory[historyIndex];
}
// Send the input to server (only when we are not migrating)