diff --git a/Client/mods/deathmatch/logic/CClientStreamElement.cpp b/Client/mods/deathmatch/logic/CClientStreamElement.cpp index b883eb0ec7..6082703cf9 100644 --- a/Client/mods/deathmatch/logic/CClientStreamElement.cpp +++ b/Client/mods/deathmatch/logic/CClientStreamElement.cpp @@ -20,6 +20,7 @@ CClientStreamElement::CClientStreamElement(CClientStreamer* pStreamer, ElementID m_fExpDistance = 0.0f; m_bStreamedIn = false; m_bAttemptingToStreamIn = false; + m_lastStreamOutTime = 0u; m_usStreamReferences = 0; m_usStreamReferencesScript = 0; m_pStreamer->AddElement(this); @@ -67,6 +68,7 @@ void CClientStreamElement::InternalStreamOut() { StreamOut(); m_bStreamedIn = false; + m_lastStreamOutTime = static_cast(CClientTime::GetTime()); // Stream out attached elements CClientObject* thisObject = DynamicCast(this); diff --git a/Client/mods/deathmatch/logic/CClientStreamElement.h b/Client/mods/deathmatch/logic/CClientStreamElement.h index 90f96b1e90..c32a5c7d6b 100644 --- a/Client/mods/deathmatch/logic/CClientStreamElement.h +++ b/Client/mods/deathmatch/logic/CClientStreamElement.h @@ -11,6 +11,7 @@ #pragma once #include "CClientEntity.h" +#include class CClientStreamer; class CClientStreamSector; class CClientStreamSectorRow; @@ -39,6 +40,7 @@ class CClientStreamElement : public CClientEntity void RemoveStreamReference(bool bScript = false); unsigned short GetStreamReferences(bool bScript = false); unsigned long GetTotalStreamReferences() { return m_usStreamReferences + m_usStreamReferencesScript; } + std::uint32_t GetLastStreamOutTime() const { return m_lastStreamOutTime; } void StreamOutForABit(); void SetDimension(unsigned short usDimension) override; float GetExpDistance() { return m_fExpDistance; } @@ -64,6 +66,7 @@ class CClientStreamElement : public CClientEntity CClientStreamer* m_pStreamer; bool m_bStreamedIn; bool m_bAttemptingToStreamIn; + std::uint32_t m_lastStreamOutTime; public: float m_fCachedRadius; diff --git a/Client/mods/deathmatch/logic/CClientStreamer.cpp b/Client/mods/deathmatch/logic/CClientStreamer.cpp index 9297b231cc..12460430ad 100644 --- a/Client/mods/deathmatch/logic/CClientStreamer.cpp +++ b/Client/mods/deathmatch/logic/CClientStreamer.cpp @@ -9,6 +9,7 @@ *****************************************************************************/ #include "StdInc.h" +#include using std::list; void* CClientStreamer::pAddingElement = NULL; @@ -375,6 +376,12 @@ bool CClientStreamer::IsActiveElement(CClientStreamElement* pElement) void CClientStreamer::Restream(bool bMovedFar) { + // Avoid swap ping-pong when two candidates are almost the same distance. + // Distances are squared, so compare against squared hysteresis too. + constexpr float swapHysteresisDistanceSq = 10.0f * 10.0f; + constexpr std::uint32_t minStreamInDelayAfterOutMs = 1200u; + const std::uint32_t currentTime = static_cast(CClientTime::GetTime()); + // Limit distance stream in/out rate // Vehicles might have to ignore this to reduce blocking loads elsewhere. int iMaxOut = 6; @@ -524,6 +531,10 @@ void CClientStreamer::Restream(bool bMovedFar) continue; } + // Prevent rapid in/out thrashing of the same element. + if (!bMovedFar && (currentTime - pElement->GetLastStreamOutTime()) < minStreamInDelayAfterOutMs) + continue; + // Not room to stream in more elements? if (bReachedLimit) { @@ -571,7 +582,7 @@ void CClientStreamer::Restream(bool bMovedFar) // See if ClosestStreamedOut is nearer than FurthestStreamedIn CClientStreamElement* pFurthestStreamedIn = FurthestStreamedInList[iFurthestStreamedInIndex]; CClientStreamElement* pClosestStreamedOut = ClosestStreamedOutList[uiClosestStreamedOutIndex]; - if (pClosestStreamedOut->GetExpDistance() >= pFurthestStreamedIn->GetExpDistance()) + if ((pClosestStreamedOut->GetExpDistance() + swapHysteresisDistanceSq) >= pFurthestStreamedIn->GetExpDistance()) break; // Stream out FurthestStreamedIn candidate if possible @@ -587,6 +598,9 @@ void CClientStreamer::Restream(bool bMovedFar) // Stream in ClosestStreamedOut candidate if possible if (!ReachedLimit()) { + if (!bMovedFar && (currentTime - pClosestStreamedOut->GetLastStreamOutTime()) < minStreamInDelayAfterOutMs) + continue; + // Stream in the new element. No need to do it instantly unless moved from far away. pClosestStreamedOut->InternalStreamIn(bMovedFar); iMaxIn--;