From c2cf1a32af2324a7bb94779d254c79805ca30504 Mon Sep 17 00:00:00 2001 From: Xenius97 Date: Sun, 16 Nov 2025 17:13:35 +0100 Subject: [PATCH 01/18] Initial commit --- .../deathmatch/logic/CClientStreamSector.h | 1 + .../deathmatch/logic/CClientStreamSectorRow.h | 1 + .../mods/deathmatch/logic/CClientStreamer.cpp | 476 ++++++++++-------- .../mods/deathmatch/logic/CClientStreamer.h | 5 + 4 files changed, 284 insertions(+), 199 deletions(-) diff --git a/Client/mods/deathmatch/logic/CClientStreamSector.h b/Client/mods/deathmatch/logic/CClientStreamSector.h index 10bd3db2f3a..bb20cd59379 100644 --- a/Client/mods/deathmatch/logic/CClientStreamSector.h +++ b/Client/mods/deathmatch/logic/CClientStreamSector.h @@ -37,6 +37,7 @@ class CClientStreamSector void Remove(CClientStreamElement* pElement) { m_Elements.remove(pElement); } std::list::iterator Begin() { return m_Elements.begin(); } std::list::iterator End() { return m_Elements.end(); } + std::list& GetElements() { return m_Elements; } void AddElements(std::list* pList); void RemoveElements(std::list* pList); diff --git a/Client/mods/deathmatch/logic/CClientStreamSectorRow.h b/Client/mods/deathmatch/logic/CClientStreamSectorRow.h index 0fa26750fa4..b61726ee757 100644 --- a/Client/mods/deathmatch/logic/CClientStreamSectorRow.h +++ b/Client/mods/deathmatch/logic/CClientStreamSectorRow.h @@ -28,6 +28,7 @@ class CClientStreamSectorRow std::list::iterator Begin() { return m_Sectors.begin(); } std::list::iterator End() { return m_Sectors.end(); } CClientStreamSector* Front() { return m_Sectors.front(); } + std::list& GetList() { return m_Sectors; } void Add(CClientStreamSector* pSector); void Remove(CClientStreamSector* pSector); unsigned int CountSectors() { return m_Sectors.size(); } diff --git a/Client/mods/deathmatch/logic/CClientStreamer.cpp b/Client/mods/deathmatch/logic/CClientStreamer.cpp index dacb77fa851..9627f764aa8 100644 --- a/Client/mods/deathmatch/logic/CClientStreamer.cpp +++ b/Client/mods/deathmatch/logic/CClientStreamer.cpp @@ -9,10 +9,32 @@ *****************************************************************************/ #include "StdInc.h" +#include "CClientGame.h" using std::list; +extern CClientGame* g_pClientGame; + void* CClientStreamer::pAddingElement = NULL; +enum class ElementCategory +{ + PLAYER, + VEHICLE, + OBJECT, + OTHER +}; + +inline ElementCategory GetElementCategory(CClientStreamElement* e) +{ + if (IS_PLAYER(e)) + return ElementCategory::PLAYER; + if (IS_VEHICLE(e)) + return ElementCategory::VEHICLE; + if (IS_OBJECT(e)) + return ElementCategory::OBJECT; + return ElementCategory::OTHER; +} + CClientStreamer::CClientStreamer(StreamerLimitReachedFunction* pLimitReachedFunc, float fMaxDistance, float fSectorSize, float fRowSize) : m_fSectorSize(fSectorSize), m_fRowSize(fRowSize) { @@ -120,8 +142,6 @@ void CClientStreamer::ConnectRow(CClientStreamSectorRow* pRow) pRow->m_pBottom->m_pTop = pRow; } -#include "..\deathmatch\logic\CClientGame.h" -extern CClientGame* g_pClientGame; void CClientStreamer::DoPulse(CVector& vecPosition) { /* Debug code @@ -196,27 +216,62 @@ void CClientStreamer::DoPulse(CVector& vecPosition) void CClientStreamer::SetDimension(unsigned short usDimension) { // Different dimension than before? - if (usDimension != m_usDimension) + if (usDimension == m_usDimension) + return; + + // Set the new dimension + m_usDimension = usDimension; + + const CClientStreamElement* lastOutsideElement = m_outsideCurrentDimensionElements.empty() ? nullptr : m_outsideCurrentDimensionElements.back(); + + auto filterElementInRows = [this](list& list) { - // Set the new dimension - m_usDimension = usDimension; - - // That means all of the currently streamed in elements will have to - // go. Unstream all elements that are streamed in. - CClientStreamElement* pElement = NULL; - list::iterator iter = m_ActiveElements.begin(); - for (; iter != m_ActiveElements.end(); iter++) + for (CClientStreamSectorRow* sectorRow : list) { - pElement = *iter; - if (pElement->IsStreamedIn()) + for (CClientStreamSector* sector : sectorRow->GetList()) { - if (!pElement->IsVisibleInAllDimensions()) + auto& elements = sector->GetElements(); + auto iter = elements.begin(); + while (iter != sector->End()) { - // Unstream it - m_ToStreamOut.push_back(pElement); + CClientStreamElement* element = *iter; + + if (IsElementShouldVisibleInCurrentDimesnion(element)) + iter++; + else + { + iter = elements.erase(iter); + m_outsideCurrentDimensionElements.push_back(element); + element->SetStreamSector(nullptr); + + if (element->IsStreamedIn()) + m_ToStreamOut.push_back(element); + } } } } + }; + + filterElementInRows(m_WorldRows); + filterElementInRows(m_ExtraRows); + + if (!lastOutsideElement) + return; + + auto iter = m_outsideCurrentDimensionElements.begin(); + + while (*iter != lastOutsideElement) + { + CClientStreamElement* element = *iter; + if (element->GetDimension() == usDimension) + { + iter = m_outsideCurrentDimensionElements.erase(iter); + AddElementInSectors(element); + } + else + { + iter++; + } } } @@ -294,6 +349,9 @@ CClientStreamSectorRow* CClientStreamer::FindRow(float fY) void CClientStreamer::OnUpdateStreamPosition(CClientStreamElement* pElement) { + if (!pElement->GetStreamSector()) + return; + CVector vecPosition = pElement->GetStreamPosition(); CClientStreamSectorRow* pRow = pElement->GetStreamRow(); CClientStreamSector* pSector = pElement->GetStreamSector(); @@ -318,7 +376,7 @@ void CClientStreamer::OnUpdateStreamPosition(CClientStreamElement* pElement) } } -void CClientStreamer::AddElement(CClientStreamElement* pElement) +void CClientStreamer::AddElementInSectors(CClientStreamElement* pElement) { assert(pAddingElement == NULL); pAddingElement = pElement; @@ -329,13 +387,33 @@ void CClientStreamer::AddElement(CClientStreamElement* pElement) pAddingElement = NULL; } -void CClientStreamer::RemoveElement(CClientStreamElement* pElement) +void CClientStreamer::RemoveElementFromSectors(CClientStreamElement* pElement) { - OnElementEnterSector(pElement, NULL); - m_ActiveElements.remove(pElement); + OnElementEnterSector(pElement, nullptr); m_ToStreamOut.remove(pElement); } +bool CClientStreamer::IsElementShouldVisibleInCurrentDimesnion(CClientStreamElement* pElement) +{ + return pElement->GetDimension() == m_usDimension || pElement->IsVisibleInAllDimensions(); +} + +void CClientStreamer::AddElement(CClientStreamElement* pElement) +{ + if (IsElementShouldVisibleInCurrentDimesnion(pElement)) + AddElementInSectors(pElement); + else + m_outsideCurrentDimensionElements.push_back(pElement); +} + +void CClientStreamer::RemoveElement(CClientStreamElement* pElement) +{ + if (pElement->GetStreamSector()) + RemoveElementFromSectors(pElement); + else + m_outsideCurrentDimensionElements.remove(pElement); +} + void CClientStreamer::SetExpDistances(list* pList) { // Run through our list setting distances to world center @@ -399,225 +477,208 @@ bool CClientStreamer::IsActiveElement(CClientStreamElement* pElement) void CClientStreamer::Restream(bool bMovedFar) { - // Limit distance stream in/out rate - // Vehicles might have to ignore this to reduce blocking loads elsewhere. - int iMaxOut = 6; - int iMaxIn = 6; - if (bMovedFar) - { - iMaxOut = 1000; - iMaxIn = 1000; - } + // Adjust stream-in/out limits (large movement allows massive updates) + int iMaxOut = bMovedFar ? 1000 : 6; + int iMaxIn = bMovedFar ? 1000 : 6; - // Do we have any elements waiting to be streamed out? - while (!m_ToStreamOut.empty()) + // Process pending stream-out elements + while (!m_ToStreamOut.empty() && iMaxOut > 0) { - CClientStreamElement* pElement = m_ToStreamOut.front(); - // Make sure we have no stream-references - if (pElement->GetTotalStreamReferences() == 0) + CClientStreamElement* e = m_ToStreamOut.front(); + m_ToStreamOut.pop_front(); + + // Only stream out if no references remain + if (e->GetTotalStreamReferences() == 0) { - // Stream out 1 of them per frame - pElement->InternalStreamOut(); - iMaxOut--; + e->InternalStreamOut(); + --iMaxOut; } - m_ToStreamOut.remove(pElement); - - if (iMaxOut <= 0) - break; } - static std::vector ClosestStreamedOutList; - static std::vector FurthestStreamedInList; - ClosestStreamedOutList.clear(); - FurthestStreamedInList.clear(); + // Prepare temporary lists + static std::vector closestOut; + static std::vector furthestIn; - bool bReachedLimit = ReachedLimit(); - // Loop through our active elements list (they should be ordered closest to furthest) - list::iterator iter = m_ActiveElements.begin(); - for (; iter != m_ActiveElements.end(); iter++) + closestOut.clear(); + furthestIn.clear(); + + bool reachedLimit = ReachedLimit(); + + // Elements are sorted: nearest -> furthest + for (CClientStreamElement* e : m_ActiveElements) { - CClientStreamElement* pElement = *iter; - float fElementDistanceExp = pElement->GetExpDistance(); + float dist = e->GetExpDistance(); + ElementCategory category = GetElementCategory(e); + bool isIn = e->IsStreamedIn(); - // Is this element streamed in? - if (pElement->IsStreamedIn()) + if (isIn) { - if (IS_VEHICLE(pElement)) + // Extra handling for vehicles and players + switch (category) { - CClientVehicle* pVehicle = DynamicCast(pElement); - if (pVehicle) + case ElementCategory::VEHICLE: { - if (pVehicle->GetOccupant() && IS_PLAYER(pVehicle->GetOccupant())) + auto* v = DynamicCast(e); + + // Skip if player in vehicle had a lightsync update + if (v && v->GetOccupant() && IS_PLAYER(v->GetOccupant())) { - CClientPlayer* pPlayer = DynamicCast(pVehicle->GetOccupant()); - if (pPlayer->GetLastPuresyncType() == PURESYNC_TYPE_LIGHTSYNC) - { - // if the last packet was ls he shouldn't be streamed in - m_ToStreamOut.push_back(pElement); - } + auto* p = DynamicCast(v->GetOccupant()); + if (p && p->GetLastPuresyncType() == PURESYNC_TYPE_LIGHTSYNC) + m_ToStreamOut.push_back(e); } - // Is this a trailer? - if (pVehicle->GetTowedByVehicle() != NULL) - { - // Don't stream it out (this is handled by the towing vehicle) + // Towed vehicles are handled by the tow vehicle + if (v && v->GetTowedByVehicle()) continue; - } } - } - if (IS_PLAYER(pElement)) - { - CClientPlayer* pPlayer = DynamicCast(pElement); - if (pPlayer->GetLastPuresyncType() == PURESYNC_TYPE_LIGHTSYNC) + break; + + case ElementCategory::PLAYER: { - // if the last packet was ls he isn't/shouldn't be streamed in - m_ToStreamOut.push_back(pElement); + auto* p = DynamicCast(e); + + // Lightsync players should not be loaded + if (p && p->GetLastPuresyncType() == PURESYNC_TYPE_LIGHTSYNC) + m_ToStreamOut.push_back(e); } + break; + + default: + break; } - // Too far away? Use the threshold so we won't flicker load it if it's on the border moving. - if (fElementDistanceExp > m_fMaxDistanceThreshold) + + // Too far -> stream out (use threshold to avoid flickering) + if (dist > m_fMaxDistanceThreshold) { - // Unstream it now? - if (iMaxOut > 0) + if (iMaxOut > 0 && e->GetTotalStreamReferences() == 0) { - // Make sure we have no stream-references - if (pElement->GetTotalStreamReferences() == 0) - { - // Stream out now - pElement->InternalStreamOut(); - iMaxOut--; - } - m_ToStreamOut.remove(pElement); + e->InternalStreamOut(); + --iMaxOut; } else - { - // or later - m_ToStreamOut.push_back(pElement); - } + m_ToStreamOut.push_back(e); } else { - FurthestStreamedInList.push_back(pElement); + // Still inside distance -> keep track for possible swapping + furthestIn.push_back(e); } + + continue; } - else + + // Too far -> ignore + if (dist > m_fMaxDistanceExp) + continue; + + // Special rules for vehicles / players + switch (category) { - // Same dimension as us? - if (pElement->GetDimension() == m_usDimension || pElement->IsVisibleInAllDimensions()) + case ElementCategory::VEHICLE: { - // Too far away? Stop here. - if (fElementDistanceExp > m_fMaxDistanceExp) - continue; + auto* v = DynamicCast(e); - if (IS_VEHICLE(pElement)) + // Lightsync driver -> do not stream in + if (v && v->GetOccupant() && IS_PLAYER(v->GetOccupant())) { - CClientVehicle* pVehicle = DynamicCast(pElement); - if (pVehicle && pVehicle->GetOccupant() && IS_PLAYER(pVehicle->GetOccupant())) - { - CClientPlayer* pPlayer = DynamicCast(pVehicle->GetOccupant()); - if (pPlayer->GetLastPuresyncType() == PURESYNC_TYPE_LIGHTSYNC) - { - // if the last packet was ls he isn't streaming in soon. - continue; - } - } - - if (pVehicle && pVehicle->GetTowedByVehicle()) - { - // Streaming in of towed vehicles is done in CClientVehicle::StreamIn by the towing vehicle + auto* p = DynamicCast(v->GetOccupant()); + if (p && p->GetLastPuresyncType() == PURESYNC_TYPE_LIGHTSYNC) continue; - } - } - if (IS_PLAYER(pElement)) - { - CClientPlayer* pPlayer = DynamicCast(pElement); - if (pPlayer->GetLastPuresyncType() == PURESYNC_TYPE_LIGHTSYNC) - { - // if the last packet was ls he isn't streaming in soon. - continue; - } - } - // If attached and attached-to is streamed out, don't consider for streaming in - CClientStreamElement* pAttachedTo = DynamicCast(pElement->GetAttachedTo()); - if (pAttachedTo && !pAttachedTo->IsStreamedIn()) - { - // ...unless attached to low LOD version - CClientObject* pAttachedToObject = DynamicCast(pAttachedTo); - CClientObject* pObject = DynamicCast(pElement); - if (!pObject || !pAttachedToObject || pObject->IsLowLod() == pAttachedToObject->IsLowLod()) - continue; - } - - // Not room to stream in more elements? - if (bReachedLimit) - { - // Add to the list that might be streamed in during the final phase - if ((int)ClosestStreamedOutList.size() < iMaxIn) // (only add if there is a chance it will be used) - ClosestStreamedOutList.push_back(pElement); } - else - { - // Stream in the new element. Don't do it instantly unless moved from far away. - pElement->InternalStreamIn(bMovedFar); - bReachedLimit = ReachedLimit(); - if (!bReachedLimit) - { - iMaxIn--; - if (iMaxIn <= 0) - break; - } - } + // Towed vehicles are handled elsewhere + if (v && v->GetTowedByVehicle()) + continue; } - } - } - - // Complex code of doom: - // ClosestStreamedOutList is {nearest to furthest} list of streamed out elements within streaming distance - // FurthestStreamedInList is {nearest to furthest} list of streamed in elements - if (bReachedLimit) - { - // Check 'furthest streamed in' against 'closest streamed out' to see if the state can be swapped - int iFurthestStreamedInIndex = FurthestStreamedInList.size() - 1; - uint uiClosestStreamedOutIndex = 0; - for (uint i = 0; i < 10; i++) - { - // Check limits for this frame - if (iMaxIn <= 0 || iMaxOut <= 0) - break; + break; - // Check indicies are valid - if (iFurthestStreamedInIndex < 0) - break; - if (uiClosestStreamedOutIndex >= ClosestStreamedOutList.size()) - break; + case ElementCategory::PLAYER: + { + auto* p = DynamicCast(e); + if (p && p->GetLastPuresyncType() == PURESYNC_TYPE_LIGHTSYNC) + continue; + } + break; - // See if ClosestStreamedOut is nearer than FurthestStreamedIn - CClientStreamElement* pFurthestStreamedIn = FurthestStreamedInList[iFurthestStreamedInIndex]; - CClientStreamElement* pClosestStreamedOut = ClosestStreamedOutList[uiClosestStreamedOutIndex]; - if (pClosestStreamedOut->GetExpDistance() >= pFurthestStreamedIn->GetExpDistance()) + default: break; + } - // Stream out FurthestStreamedIn candidate if possible - if (pFurthestStreamedIn->GetTotalStreamReferences() == 0) + // Attached element: only load if LOD does not match + if (auto* at = DynamicCast(e->GetAttachedTo())) + { + if (!at->IsStreamedIn()) { - // Stream out now - pFurthestStreamedIn->InternalStreamOut(); - iMaxOut--; + auto* o = DynamicCast(e); + auto* o_att = DynamicCast(at); + if (!o || !o_att || o->IsLowLod() == o_att->IsLowLod()) + continue; } - m_ToStreamOut.remove(pFurthestStreamedIn); - iFurthestStreamedInIndex--; // Always advance to the next candidate + } + + // Reached streaming limit -> mark as candidate + if (reachedLimit) + { + if ((int)closestOut.size() < iMaxIn) + closestOut.push_back(e); + } + else + { + // Stream in now (instant if moved far) + e->InternalStreamIn(bMovedFar); - // Stream in ClosestStreamedOut candidate if possible - if (!ReachedLimit()) + reachedLimit = ReachedLimit(); + if (!reachedLimit) { - // Stream in the new element. No need to do it instantly unless moved from far away. - pClosestStreamedOut->InternalStreamIn(bMovedFar); - iMaxIn--; - uiClosestStreamedOutIndex++; + --iMaxIn; + if (iMaxIn <= 0) + break; } } } + + // Swap logic when at streaming limit + if (!reachedLimit) + return; + + int fi = (int)furthestIn.size() - 1; + unsigned int co = 0; + + // Up to 10 swaps per frame + for (int i = 0; i < 10; i++) + { + if (iMaxIn <= 0 || iMaxOut <= 0) + break; + if (fi < 0) + break; + if (co >= closestOut.size()) + break; + + auto* fIn = furthestIn[fi]; + auto* cOut = closestOut[co]; + + // Only swap if the streamed-out item is closer + if (cOut->GetExpDistance() >= fIn->GetExpDistance()) + break; + + // Stream out the furthest streamed-in item + if (fIn->GetTotalStreamReferences() == 0) + { + fIn->InternalStreamOut(); + --iMaxOut; + } + m_ToStreamOut.remove(fIn); + --fi; + + // Stream in the closest streamed-out item + if (!ReachedLimit()) + { + cOut->InternalStreamIn(bMovedFar); + --iMaxIn; + ++co; + } + } } void CClientStreamer::OnEnterSector(CClientStreamSector* pSector) @@ -705,6 +766,10 @@ void CClientStreamer::OnElementEnterSector(CClientStreamElement* pElement, CClie } else { + // Should we deactivate the element? + if (pPreviousSector && pPreviousSector->IsActivated()) + m_ActiveElements.remove(pElement); + // Should we activate this sector? if (pSector->IsExtra() && (m_pSector->IsMySurroundingSector(pSector) || m_pSector == pSector)) { @@ -718,6 +783,12 @@ void CClientStreamer::OnElementEnterSector(CClientStreamElement* pElement, CClie } } } + else if (pPreviousSector && pPreviousSector->IsActivated()) + { + // The element was removed from sectors. + // Remove it from active elements too. + m_ActiveElements.remove(pElement); + } pElement->SetStreamSector(pSector); } @@ -738,16 +809,23 @@ void CClientStreamer::OnElementForceStreamOut(CClientStreamElement* pElement) void CClientStreamer::OnElementDimension(CClientStreamElement* pElement) { - // Grab its new dimenson - unsigned short usDimension = pElement->GetDimension(); - // Is it streamed in? - if (pElement->IsStreamedIn()) + if (IsElementShouldVisibleInCurrentDimesnion(pElement)) { - // Has it moved to a different dimension to us? - if (usDimension != m_usDimension) + if (!pElement->GetStreamSector()) { - // Stream it out - m_ToStreamOut.push_back(pElement); + AddElementInSectors(pElement); + m_outsideCurrentDimensionElements.remove(pElement); + } + } + else + { + if (pElement->GetStreamSector()) + { + m_outsideCurrentDimensionElements.push_back(pElement); + RemoveElementFromSectors(pElement); + + if (pElement->IsStreamedIn()) + m_ToStreamOut.push_back(pElement); } } } diff --git a/Client/mods/deathmatch/logic/CClientStreamer.h b/Client/mods/deathmatch/logic/CClientStreamer.h index 9e81fedbdfe..53951fa43bb 100644 --- a/Client/mods/deathmatch/logic/CClientStreamer.h +++ b/Client/mods/deathmatch/logic/CClientStreamer.h @@ -49,6 +49,10 @@ class CClientStreamer void AddElement(CClientStreamElement* pElement); void RemoveElement(CClientStreamElement* pElement); + void AddElementInSectors(CClientStreamElement* pElement); + void RemoveElementFromSectors(CClientStreamElement* pElement); + bool IsElementShouldVisibleInCurrentDimesnion(CClientStreamElement* pElement); + void SetExpDistances(std::list* pList); void AddToSortedList(std::list* pList, CClientStreamElement* pElement); @@ -74,6 +78,7 @@ class CClientStreamer unsigned short m_usDimension; std::list m_ActiveElements; std::list m_ToStreamOut; + std::list m_outsideCurrentDimensionElements; static void* pAddingElement; }; From aa84cb13815ab4cf7c2eacc62c63bfc8d51908b0 Mon Sep 17 00:00:00 2001 From: Xenius97 Date: Sun, 16 Nov 2025 18:26:05 +0100 Subject: [PATCH 02/18] Crash fix one --- Client/mods/deathmatch/logic/CClientStreamer.cpp | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/Client/mods/deathmatch/logic/CClientStreamer.cpp b/Client/mods/deathmatch/logic/CClientStreamer.cpp index 9627f764aa8..6f800b2a1ff 100644 --- a/Client/mods/deathmatch/logic/CClientStreamer.cpp +++ b/Client/mods/deathmatch/logic/CClientStreamer.cpp @@ -232,7 +232,8 @@ void CClientStreamer::SetDimension(unsigned short usDimension) { auto& elements = sector->GetElements(); auto iter = elements.begin(); - while (iter != sector->End()) + + while (iter != elements.end()) { CClientStreamElement* element = *iter; @@ -260,17 +261,18 @@ void CClientStreamer::SetDimension(unsigned short usDimension) auto iter = m_outsideCurrentDimensionElements.begin(); - while (*iter != lastOutsideElement) + while (iter != m_outsideCurrentDimensionElements.end() && *iter != lastOutsideElement) { CClientStreamElement* element = *iter; - if (element->GetDimension() == usDimension) + + if (element->GetDimension() == m_usDimension) { iter = m_outsideCurrentDimensionElements.erase(iter); AddElementInSectors(element); } else { - iter++; + ++iter; } } } @@ -496,8 +498,8 @@ void CClientStreamer::Restream(bool bMovedFar) } // Prepare temporary lists - static std::vector closestOut; - static std::vector furthestIn; + std::vector closestOut; + std::vector furthestIn; closestOut.clear(); furthestIn.clear(); From 53db7edb3bab50011eebffe4aa8d480315e744bd Mon Sep 17 00:00:00 2001 From: Xenius97 Date: Sun, 16 Nov 2025 18:27:03 +0100 Subject: [PATCH 03/18] Crash fix: RemoveElements() already handles stream-out internally --- .../mods/deathmatch/logic/CClientStreamer.cpp | 25 ++++++------------- 1 file changed, 8 insertions(+), 17 deletions(-) diff --git a/Client/mods/deathmatch/logic/CClientStreamer.cpp b/Client/mods/deathmatch/logic/CClientStreamer.cpp index 6f800b2a1ff..22ed1bfef77 100644 --- a/Client/mods/deathmatch/logic/CClientStreamer.cpp +++ b/Client/mods/deathmatch/logic/CClientStreamer.cpp @@ -698,24 +698,15 @@ void CClientStreamer::OnEnterSector(CClientStreamSector* pSector) for (; iter != uncommon.end(); iter++) { pTempSector = *iter; - // Make sure we dont unload our new sector - if (pTempSector != pSector) + + // Don't unload the sector we are entering + if (pTempSector == pSector) + continue; + + if (pTempSector->IsActivated()) { - if (pTempSector->IsActivated()) - { - list::iterator iter = pTempSector->Begin(); - for (; iter != pTempSector->End(); iter++) - { - pElement = *iter; - if (pElement->IsStreamedIn()) - { - // Add it to our streaming out list - m_ToStreamOut.push_back(pElement); - } - } - pTempSector->RemoveElements(&m_ActiveElements); - pTempSector->SetActivated(false); - } + pTempSector->RemoveElements(&m_ActiveElements); + pTempSector->SetActivated(false); } } From 3f1f99c6fe9c7dc8863482ed1660c466152b833f Mon Sep 17 00:00:00 2001 From: Xenius97 Date: Sun, 16 Nov 2025 18:35:07 +0100 Subject: [PATCH 04/18] Revert "Crash fix: RemoveElements() already handles stream-out internally" This reverts commit 53db7edb3bab50011eebffe4aa8d480315e744bd. --- .../mods/deathmatch/logic/CClientStreamer.cpp | 25 +++++++++++++------ 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/Client/mods/deathmatch/logic/CClientStreamer.cpp b/Client/mods/deathmatch/logic/CClientStreamer.cpp index 22ed1bfef77..6f800b2a1ff 100644 --- a/Client/mods/deathmatch/logic/CClientStreamer.cpp +++ b/Client/mods/deathmatch/logic/CClientStreamer.cpp @@ -698,15 +698,24 @@ void CClientStreamer::OnEnterSector(CClientStreamSector* pSector) for (; iter != uncommon.end(); iter++) { pTempSector = *iter; - - // Don't unload the sector we are entering - if (pTempSector == pSector) - continue; - - if (pTempSector->IsActivated()) + // Make sure we dont unload our new sector + if (pTempSector != pSector) { - pTempSector->RemoveElements(&m_ActiveElements); - pTempSector->SetActivated(false); + if (pTempSector->IsActivated()) + { + list::iterator iter = pTempSector->Begin(); + for (; iter != pTempSector->End(); iter++) + { + pElement = *iter; + if (pElement->IsStreamedIn()) + { + // Add it to our streaming out list + m_ToStreamOut.push_back(pElement); + } + } + pTempSector->RemoveElements(&m_ActiveElements); + pTempSector->SetActivated(false); + } } } From b78e26dbd39d0537b76620bf79893e608edb70e6 Mon Sep 17 00:00:00 2001 From: Xenius97 Date: Sun, 16 Nov 2025 18:42:40 +0100 Subject: [PATCH 05/18] Revert "Crash fix one" This reverts commit aa84cb13815ab4cf7c2eacc62c63bfc8d51908b0. --- Client/mods/deathmatch/logic/CClientStreamer.cpp | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/Client/mods/deathmatch/logic/CClientStreamer.cpp b/Client/mods/deathmatch/logic/CClientStreamer.cpp index 6f800b2a1ff..9627f764aa8 100644 --- a/Client/mods/deathmatch/logic/CClientStreamer.cpp +++ b/Client/mods/deathmatch/logic/CClientStreamer.cpp @@ -232,8 +232,7 @@ void CClientStreamer::SetDimension(unsigned short usDimension) { auto& elements = sector->GetElements(); auto iter = elements.begin(); - - while (iter != elements.end()) + while (iter != sector->End()) { CClientStreamElement* element = *iter; @@ -261,18 +260,17 @@ void CClientStreamer::SetDimension(unsigned short usDimension) auto iter = m_outsideCurrentDimensionElements.begin(); - while (iter != m_outsideCurrentDimensionElements.end() && *iter != lastOutsideElement) + while (*iter != lastOutsideElement) { CClientStreamElement* element = *iter; - - if (element->GetDimension() == m_usDimension) + if (element->GetDimension() == usDimension) { iter = m_outsideCurrentDimensionElements.erase(iter); AddElementInSectors(element); } else { - ++iter; + iter++; } } } @@ -498,8 +496,8 @@ void CClientStreamer::Restream(bool bMovedFar) } // Prepare temporary lists - std::vector closestOut; - std::vector furthestIn; + static std::vector closestOut; + static std::vector furthestIn; closestOut.clear(); furthestIn.clear(); From 75b54f0890953822aaefaa0f4773ae3b01b6a441 Mon Sep 17 00:00:00 2001 From: Xenius97 Date: Sun, 16 Nov 2025 18:45:04 +0100 Subject: [PATCH 06/18] Fix --- Client/mods/deathmatch/logic/CClientStreamer.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Client/mods/deathmatch/logic/CClientStreamer.cpp b/Client/mods/deathmatch/logic/CClientStreamer.cpp index 9627f764aa8..c5efa5a4d62 100644 --- a/Client/mods/deathmatch/logic/CClientStreamer.cpp +++ b/Client/mods/deathmatch/logic/CClientStreamer.cpp @@ -496,8 +496,8 @@ void CClientStreamer::Restream(bool bMovedFar) } // Prepare temporary lists - static std::vector closestOut; - static std::vector furthestIn; + std::vector closestOut; + std::vector furthestIn; closestOut.clear(); furthestIn.clear(); From 0a824053b11689e99660a6473f14602e365f322d Mon Sep 17 00:00:00 2001 From: Xenius97 Date: Sun, 16 Nov 2025 20:09:13 +0100 Subject: [PATCH 07/18] Crash & typo fixes test --- .../mods/deathmatch/logic/CClientStreamer.cpp | 468 ++++++++++++------ .../mods/deathmatch/logic/CClientStreamer.h | 2 +- 2 files changed, 328 insertions(+), 142 deletions(-) diff --git a/Client/mods/deathmatch/logic/CClientStreamer.cpp b/Client/mods/deathmatch/logic/CClientStreamer.cpp index c5efa5a4d62..a78de817914 100644 --- a/Client/mods/deathmatch/logic/CClientStreamer.cpp +++ b/Client/mods/deathmatch/logic/CClientStreamer.cpp @@ -10,6 +10,8 @@ #include "StdInc.h" #include "CClientGame.h" +#include +#include using std::list; extern CClientGame* g_pClientGame; @@ -36,7 +38,7 @@ inline ElementCategory GetElementCategory(CClientStreamElement* e) } CClientStreamer::CClientStreamer(StreamerLimitReachedFunction* pLimitReachedFunc, float fMaxDistance, float fSectorSize, float fRowSize) - : m_fSectorSize(fSectorSize), m_fRowSize(fRowSize) + : m_fSectorSize(fSectorSize), m_fRowSize(fRowSize), m_pRow(NULL), m_pSector(NULL) { // Setup our distance variables m_fMaxDistanceExp = fMaxDistance * fMaxDistance; @@ -47,6 +49,9 @@ CClientStreamer::CClientStreamer(StreamerLimitReachedFunction* pLimitReachedFunc assert(pLimitReachedFunc); m_pLimitReachedFunc = pLimitReachedFunc; + // Initialize position to valid default (0,0,0) + m_vecPosition = CVector(0.0f, 0.0f, 0.0f); + // Create our main world sectors covering the mainland CVector2D size(m_fSectorSize, m_fRowSize); CVector2D bottomLeft(-WORLD_SIZE, -WORLD_SIZE); @@ -55,8 +60,14 @@ CClientStreamer::CClientStreamer(StreamerLimitReachedFunction* pLimitReachedFunc // Find our row and sector m_pRow = FindOrCreateRow(m_vecPosition); - m_pSector = NULL; - OnEnterSector(m_pRow->FindOrCreateSector(m_vecPosition)); + if (m_pRow) + { + CClientStreamSector* pInitialSector = m_pRow->FindOrCreateSector(m_vecPosition); + if (pInitialSector) + { + OnEnterSector(pInitialSector); + } + } } CClientStreamer::~CClientStreamer() @@ -80,6 +91,9 @@ CClientStreamer::~CClientStreamer() void CClientStreamer::CreateSectors(std::list* pList, CVector2D& vecSize, CVector2D& vecBottomLeft, CVector2D& vecTopRight) { + if (!pList) + return; + // Creates our sectors within rows, filling up our rectangle, connecting each sector and row CClientStreamSector * pCurrent = NULL, *pPrevious = NULL, *pPreviousRowSector = NULL; CClientStreamSectorRow *pCurrentRow = NULL, *pPreviousRow = NULL; @@ -88,6 +102,9 @@ void CClientStreamer::CreateSectors(std::list* pList, C while (fY < vecTopRight.fY) { pCurrentRow = new CClientStreamSectorRow(fY, fY + vecSize.fY, m_fSectorSize, m_fRowSize); + if (!pCurrentRow) + break; + pCurrentRow->m_pBottom = pPreviousRow; pList->push_back(pCurrentRow); @@ -101,6 +118,9 @@ void CClientStreamer::CreateSectors(std::list* pList, C pPrevious = pCurrent; pCurrent = new CClientStreamSector(pCurrentRow, bottomLeft, topRight); + if (!pCurrent) + break; + pCurrentRow->Add(pCurrent); pCurrent->m_pLeft = pPrevious; @@ -128,6 +148,9 @@ void CClientStreamer::CreateSectors(std::list* pList, C void CClientStreamer::ConnectRow(CClientStreamSectorRow* pRow) { + if (!pRow) + return; + float fTop, fBottom; pRow->GetPosition(fTop, fBottom); @@ -142,67 +165,59 @@ void CClientStreamer::ConnectRow(CClientStreamSectorRow* pRow) pRow->m_pBottom->m_pTop = pRow; } -void CClientStreamer::DoPulse(CVector& vecPosition) +void CClientStreamer::DoPulse(CVector& vecPosition) { - /* Debug code - CClientStreamSector * pSector; - list < CClientStreamSector * > ::iterator iterSector; - list < CClientStreamSectorRow * > ::iterator iterRow = m_WorldRows.begin (); - for ( ; iterRow != m_WorldRows.end () ; iterRow++ ) - { - iterSector = (*iterRow)->Begin (); - for ( ; iterSector != (*iterRow)->End () ; iterSector++ ) - { - pSector = *iterSector; - if ( !pSector->m_pArea ) - { - pSector->m_pArea = new CClientRadarArea ( g_pClientGame->GetManager (), INVALID_ELEMENT_ID ); - pSector->m_pArea->SetPosition ( pSector->m_vecBottomLeft ); - CVector2D vecSize ( pSector->m_vecTopRight.fX - pSector->m_vecBottomLeft.fX, pSector->m_vecTopRight.fY - pSector->m_vecBottomLeft.fY ); - pSector->m_pArea->SetSize ( vecSize ); - pSector->m_pArea->SetColor ( 255, 0, 0, 50 ); - } - pSector->m_pArea->SetColor ( 255, 0, 0, 50 ); - } - } - iterRow = m_ExtraRows.begin (); - for ( ; iterRow != m_ExtraRows.end () ; iterRow++ ) + // Validate position + if (!std::isfinite(vecPosition.fX) || !std::isfinite(vecPosition.fY) || !std::isfinite(vecPosition.fZ)) { - iterSector = (*iterRow)->Begin (); - for ( ; iterSector != (*iterRow)->End () ; iterSector++ ) - { - pSector = *iterSector; - if ( !pSector->m_pArea ) - { - pSector->m_pArea = new CClientRadarArea ( g_pClientGame->GetManager (), INVALID_ELEMENT_ID ); - pSector->m_pArea->SetPosition ( pSector->m_vecBottomLeft ); - CVector2D vecSize ( pSector->m_vecTopRight.fX - pSector->m_vecBottomLeft.fX, pSector->m_vecTopRight.fY - pSector->m_vecBottomLeft.fY ); - pSector->m_pArea->SetSize ( vecSize ); - pSector->m_pArea->SetColor ( 255, 0, 0, 50 ); - } - pSector->m_pArea->SetColor ( 255, 0, 0, 50 ); - } + // Invalid position, use last valid position + vecPosition = m_vecPosition; + return; } - */ bool bMovedFar = false; + bool bPositionChanged = false; + // Has our position changed? if (vecPosition != m_vecPosition) { - bMovedFar = ((m_vecPosition - vecPosition).LengthSquared() > (50 * 50)); + bPositionChanged = true; + float fDistSquared = (m_vecPosition - vecPosition).LengthSquared(); + bMovedFar = (fDistSquared > (50.0f * 50.0f)); m_vecPosition = vecPosition; + // Ensure m_pRow exists + if (!m_pRow) + { + m_pRow = FindOrCreateRow(vecPosition); + if (m_pRow) + { + CClientStreamSector* pSector = m_pRow->FindOrCreateSector(vecPosition); + if (pSector) + OnEnterSector(pSector); + } + return; + } + // Have we changed row? if (!m_pRow->DoesContain(vecPosition)) { - m_pRow = FindOrCreateRow(vecPosition, m_pRow); - OnEnterSector(m_pRow->FindOrCreateSector(vecPosition)); + CClientStreamSectorRow* pNewRow = FindOrCreateRow(vecPosition, m_pRow); + if (pNewRow) + { + m_pRow = pNewRow; + CClientStreamSector* pSector = m_pRow->FindOrCreateSector(vecPosition); + if (pSector) + OnEnterSector(pSector); + } } // Have we changed sector? - else if (!m_pSector->DoesContain(vecPosition)) + else if (m_pSector && !m_pSector->DoesContain(vecPosition)) { // Grab our new sector - OnEnterSector(m_pRow->FindOrCreateSector(vecPosition, m_pSector)); + CClientStreamSector* pNewSector = m_pRow->FindOrCreateSector(vecPosition, m_pSector); + if (pNewSector) + OnEnterSector(pNewSector); } } @@ -222,61 +237,96 @@ void CClientStreamer::SetDimension(unsigned short usDimension) // Set the new dimension m_usDimension = usDimension; - const CClientStreamElement* lastOutsideElement = m_outsideCurrentDimensionElements.empty() ? nullptr : m_outsideCurrentDimensionElements.back(); + // Reserve space for vectors + std::vector elementsToHide; + std::vector elementsToShow; - auto filterElementInRows = [this](list& list) + elementsToHide.reserve(100); + elementsToShow.reserve(100); + + // Collect elements from sectors that need to be hidden + auto collectFromRows = [this, &elementsToHide](list& rowList) { - for (CClientStreamSectorRow* sectorRow : list) + for (CClientStreamSectorRow* sectorRow : rowList) { + if (!sectorRow) + continue; + for (CClientStreamSector* sector : sectorRow->GetList()) { - auto& elements = sector->GetElements(); - auto iter = elements.begin(); - while (iter != sector->End()) + if (!sector) + continue; + + // Process directly without making a full copy + for (auto iter = sector->Begin(); iter != sector->End(); ++iter) { CClientStreamElement* element = *iter; + if (!element) + continue; - if (IsElementShouldVisibleInCurrentDimesnion(element)) - iter++; - else + // Should this element be hidden in the new dimension? + if (!IsElementShouldVisibleInCurrentDimension(element)) { - iter = elements.erase(iter); - m_outsideCurrentDimensionElements.push_back(element); - element->SetStreamSector(nullptr); - - if (element->IsStreamedIn()) - m_ToStreamOut.push_back(element); + elementsToHide.push_back(element); } } } } }; - filterElementInRows(m_WorldRows); - filterElementInRows(m_ExtraRows); + collectFromRows(m_WorldRows); + collectFromRows(m_ExtraRows); - if (!lastOutsideElement) - return; - - auto iter = m_outsideCurrentDimensionElements.begin(); - - while (*iter != lastOutsideElement) + // Collect elements that should be shown in new dimension + for (CClientStreamElement* element : m_outsideCurrentDimensionElements) { - CClientStreamElement* element = *iter; - if (element->GetDimension() == usDimension) + if (!element) + continue; + + // Should this element be visible in new dimension? + if (element->GetDimension() == usDimension || element->IsVisibleInAllDimensions()) { - iter = m_outsideCurrentDimensionElements.erase(iter); - AddElementInSectors(element); + elementsToShow.push_back(element); } - else + } + + // Process elements to hide + for (CClientStreamElement* element : elementsToHide) + { + if (!element) + continue; + + CClientStreamSector* sector = element->GetStreamSector(); + if (sector) { - iter++; + sector->Remove(element); + element->SetStreamSector(nullptr); + m_ActiveElements.remove(element); + m_outsideCurrentDimensionElements.push_back(element); + + if (element->IsStreamedIn()) + { + m_ToStreamOut.push_back(element); + } } } + + // Process elements to show + for (CClientStreamElement* element : elementsToShow) + { + if (!element) + continue; + m_outsideCurrentDimensionElements.remove(element); + AddElementInSectors(element); + } } CClientStreamSectorRow* CClientStreamer::FindOrCreateRow(CVector& vecPosition, CClientStreamSectorRow* pSurrounding) { + // Validate position + if (!std::isfinite(vecPosition.fY)) + return NULL; + // Do we have a row to check around first? if (pSurrounding) { @@ -293,7 +343,7 @@ CClientStreamSectorRow* CClientStreamer::FindOrCreateRow(CVector& vecPosition, C for (; iter != m_WorldRows.end(); iter++) { pRow = *iter; - if (pRow->DoesContain(vecPosition)) + if (pRow && pRow->DoesContain(vecPosition)) { return pRow; } @@ -304,7 +354,7 @@ CClientStreamSectorRow* CClientStreamer::FindOrCreateRow(CVector& vecPosition, C for (; iter != m_ExtraRows.end(); iter++) { pRow = *iter; - if (pRow->DoesContain(vecPosition)) + if (pRow && pRow->DoesContain(vecPosition)) { return pRow; } @@ -314,6 +364,9 @@ CClientStreamSectorRow* CClientStreamer::FindOrCreateRow(CVector& vecPosition, C if (vecPosition.fY < 0.0f) fBottom -= m_fRowSize; pRow = new CClientStreamSectorRow(fBottom, fBottom + m_fRowSize, m_fSectorSize, m_fRowSize); + if (!pRow) + return NULL; + ConnectRow(pRow); pRow->SetExtra(true); m_ExtraRows.push_back(pRow); @@ -328,7 +381,7 @@ CClientStreamSectorRow* CClientStreamer::FindRow(float fY) for (; iter != m_WorldRows.end(); iter++) { pRow = *iter; - if (pRow->DoesContain(fY)) + if (pRow && pRow->DoesContain(fY)) { return pRow; } @@ -339,7 +392,7 @@ CClientStreamSectorRow* CClientStreamer::FindRow(float fY) for (; iter != m_ExtraRows.end(); iter++) { pRow = *iter; - if (pRow->DoesContain(fY)) + if (pRow && pRow->DoesContain(fY)) { return pRow; } @@ -349,25 +402,40 @@ CClientStreamSectorRow* CClientStreamer::FindRow(float fY) void CClientStreamer::OnUpdateStreamPosition(CClientStreamElement* pElement) { - if (!pElement->GetStreamSector()) + if (!pElement || !pElement->GetStreamSector()) + return; + + CVector vecPosition = pElement->GetStreamPosition(); + + // Validate element position + if (!std::isfinite(vecPosition.fX) || !std::isfinite(vecPosition.fY) || !std::isfinite(vecPosition.fZ)) return; - CVector vecPosition = pElement->GetStreamPosition(); CClientStreamSectorRow* pRow = pElement->GetStreamRow(); CClientStreamSector* pSector = pElement->GetStreamSector(); + if (!pRow || !pSector) + return; + // Have we changed row? if (!pRow->DoesContain(vecPosition)) { pRow = FindOrCreateRow(vecPosition); + if (!pRow) + return; + pElement->SetStreamRow(pRow); - OnElementEnterSector(pElement, pRow->FindOrCreateSector(vecPosition)); + CClientStreamSector* pNewSector = pRow->FindOrCreateSector(vecPosition); + if (pNewSector) + OnElementEnterSector(pElement, pNewSector); } // Have we changed sector? else if (!pSector->DoesContain(vecPosition)) { // Grab our new sector - OnElementEnterSector(pElement, pRow->FindOrCreateSector(vecPosition, pSector)); + CClientStreamSector* pNewSector = pRow->FindOrCreateSector(vecPosition, pSector); + if (pNewSector) + OnElementEnterSector(pElement, pNewSector); } else { @@ -378,29 +446,62 @@ void CClientStreamer::OnUpdateStreamPosition(CClientStreamElement* pElement) void CClientStreamer::AddElementInSectors(CClientStreamElement* pElement) { + if (!pElement) + return; + + // Check if already in a sector + if (pElement->GetStreamSector()) + { + // Element already in a sector, update its position instead + OnUpdateStreamPosition(pElement); + return; + } + assert(pAddingElement == NULL); pAddingElement = pElement; - CVector vecPosition = pElement->GetStreamPosition(); + + CVector vecPosition = pElement->GetStreamPosition(); + + // Validate position + if (!std::isfinite(vecPosition.fX) || !std::isfinite(vecPosition.fY) || !std::isfinite(vecPosition.fZ)) + { + pAddingElement = NULL; + return; + } + CClientStreamSectorRow* pRow = FindOrCreateRow(vecPosition); - pElement->SetStreamRow(pRow); - OnElementEnterSector(pElement, pRow->FindOrCreateSector(vecPosition)); + if (pRow) + { + pElement->SetStreamRow(pRow); + CClientStreamSector* pSector = pRow->FindOrCreateSector(vecPosition); + if (pSector) + OnElementEnterSector(pElement, pSector); + } pAddingElement = NULL; } void CClientStreamer::RemoveElementFromSectors(CClientStreamElement* pElement) { + if (!pElement) + return; + OnElementEnterSector(pElement, nullptr); m_ToStreamOut.remove(pElement); } -bool CClientStreamer::IsElementShouldVisibleInCurrentDimesnion(CClientStreamElement* pElement) +bool CClientStreamer::IsElementShouldVisibleInCurrentDimension(CClientStreamElement* pElement) { + if (!pElement) + return false; return pElement->GetDimension() == m_usDimension || pElement->IsVisibleInAllDimensions(); } void CClientStreamer::AddElement(CClientStreamElement* pElement) { - if (IsElementShouldVisibleInCurrentDimesnion(pElement)) + if (!pElement) + return; + + if (IsElementShouldVisibleInCurrentDimension(pElement)) AddElementInSectors(pElement); else m_outsideCurrentDimensionElements.push_back(pElement); @@ -408,27 +509,41 @@ void CClientStreamer::AddElement(CClientStreamElement* pElement) void CClientStreamer::RemoveElement(CClientStreamElement* pElement) { + if (!pElement) + return; + if (pElement->GetStreamSector()) - RemoveElementFromSectors(pElement); - else - m_outsideCurrentDimensionElements.remove(pElement); + { + CClientStreamSector* sector = pElement->GetStreamSector(); + sector->Remove(pElement); + pElement->SetStreamSector(nullptr); + } + + m_ActiveElements.remove(pElement); + m_ToStreamOut.remove(pElement); + m_outsideCurrentDimensionElements.remove(pElement); } void CClientStreamer::SetExpDistances(list* pList) { - // Run through our list setting distances to world center - CClientStreamElement* pElement = NULL; - list::iterator iter = pList->begin(); - for (; iter != pList->end(); iter++) + if (!pList) + return; + + for (auto iter = pList->begin(); iter != pList->end(); ++iter) { - pElement = *iter; - // Set its distance ^ 2 + CClientStreamElement* pElement = *iter; + if (!pElement) + continue; + pElement->SetExpDistance(pElement->GetDistanceToBoundingBoxSquared(m_vecPosition)); } } void CClientStreamer::AddToSortedList(list* pList, CClientStreamElement* pElement) { + if (!pList || !pElement) + return; + // Make sure it's exp distance is updated float fDistance = pElement->GetDistanceToBoundingBoxSquared(m_vecPosition); pElement->SetExpDistance(fDistance); @@ -438,11 +553,12 @@ void CClientStreamer::AddToSortedList(list* pList, CClien return; // Search through our list. Add it behind the first item further away than this - CClientStreamElement* pTemp = NULL; list::iterator iter = pList->begin(); for (; iter != pList->end(); iter++) { - pTemp = *iter; + CClientStreamElement* pTemp = *iter; + if (!pTemp) + continue; // Is it further than the one we add? if (pTemp->GetDistanceToBoundingBoxSquared(m_vecPosition) > fDistance) @@ -459,13 +575,21 @@ void CClientStreamer::AddToSortedList(list* pList, CClien bool CClientStreamer::CompareExpDistance(CClientStreamElement* p1, CClientStreamElement* p2) { + if (!p1 && !p2) + return false; + if (!p1) + return false; + if (!p2) + return true; return p1->GetExpDistance() < p2->GetExpDistance(); } bool CClientStreamer::IsActiveElement(CClientStreamElement* pElement) { - list::iterator iter = m_ActiveElements.begin(); - for (; iter != m_ActiveElements.end(); iter++) + if (!pElement) + return false; + + for (auto iter = m_ActiveElements.begin(); iter != m_ActiveElements.end(); ++iter) { if (*iter == pElement) { @@ -482,16 +606,29 @@ void CClientStreamer::Restream(bool bMovedFar) int iMaxIn = bMovedFar ? 1000 : 6; // Process pending stream-out elements - while (!m_ToStreamOut.empty() && iMaxOut > 0) + int processedOut = 0; + auto iter = m_ToStreamOut.begin(); + + while (iter != m_ToStreamOut.end() && processedOut < iMaxOut) { - CClientStreamElement* e = m_ToStreamOut.front(); - m_ToStreamOut.pop_front(); + CClientStreamElement* e = *iter; + + if (!e) + { + iter = m_ToStreamOut.erase(iter); + continue; + } // Only stream out if no references remain if (e->GetTotalStreamReferences() == 0) { e->InternalStreamOut(); - --iMaxOut; + iter = m_ToStreamOut.erase(iter); + processedOut++; + } + else + { + ++iter; } } @@ -499,14 +636,23 @@ void CClientStreamer::Restream(bool bMovedFar) std::vector closestOut; std::vector furthestIn; - closestOut.clear(); - furthestIn.clear(); + // Reserve space + closestOut.reserve(iMaxIn); + furthestIn.reserve(50); bool reachedLimit = ReachedLimit(); + int processedIn = 0; // Elements are sorted: nearest -> furthest for (CClientStreamElement* e : m_ActiveElements) { + if (!e) + continue; + + // Early exit if we've processed enough + if (processedIn >= iMaxIn && processedOut >= iMaxOut && !bMovedFar) + break; + float dist = e->GetExpDistance(); ElementCategory category = GetElementCategory(e); bool isIn = e->IsStreamedIn(); @@ -525,7 +671,10 @@ void CClientStreamer::Restream(bool bMovedFar) { auto* p = DynamicCast(v->GetOccupant()); if (p && p->GetLastPuresyncType() == PURESYNC_TYPE_LIGHTSYNC) + { m_ToStreamOut.push_back(e); + continue; + } } // Towed vehicles are handled by the tow vehicle @@ -540,7 +689,10 @@ void CClientStreamer::Restream(bool bMovedFar) // Lightsync players should not be loaded if (p && p->GetLastPuresyncType() == PURESYNC_TYPE_LIGHTSYNC) + { m_ToStreamOut.push_back(e); + continue; + } } break; @@ -551,18 +703,21 @@ void CClientStreamer::Restream(bool bMovedFar) // Too far -> stream out (use threshold to avoid flickering) if (dist > m_fMaxDistanceThreshold) { - if (iMaxOut > 0 && e->GetTotalStreamReferences() == 0) + if (processedOut < iMaxOut && e->GetTotalStreamReferences() == 0) { e->InternalStreamOut(); - --iMaxOut; + processedOut++; } else + { m_ToStreamOut.push_back(e); + } } else { // Still inside distance -> keep track for possible swapping - furthestIn.push_back(e); + if (furthestIn.size() < 50) // Limit size + furthestIn.push_back(e); } continue; @@ -626,20 +781,18 @@ void CClientStreamer::Restream(bool bMovedFar) else { // Stream in now (instant if moved far) - e->InternalStreamIn(bMovedFar); - - reachedLimit = ReachedLimit(); - if (!reachedLimit) + if (processedIn < iMaxIn) { - --iMaxIn; - if (iMaxIn <= 0) - break; + e->InternalStreamIn(bMovedFar); + processedIn++; + + reachedLimit = ReachedLimit(); } } } // Swap logic when at streaming limit - if (!reachedLimit) + if (!reachedLimit || closestOut.empty() || furthestIn.empty()) return; int fi = (int)furthestIn.size() - 1; @@ -648,7 +801,7 @@ void CClientStreamer::Restream(bool bMovedFar) // Up to 10 swaps per frame for (int i = 0; i < 10; i++) { - if (iMaxIn <= 0 || iMaxOut <= 0) + if (processedIn >= iMaxIn || processedOut >= iMaxOut) break; if (fi < 0) break; @@ -658,6 +811,9 @@ void CClientStreamer::Restream(bool bMovedFar) auto* fIn = furthestIn[fi]; auto* cOut = closestOut[co]; + if (!fIn || !cOut) + break; + // Only swap if the streamed-out item is closer if (cOut->GetExpDistance() >= fIn->GetExpDistance()) break; @@ -666,7 +822,7 @@ void CClientStreamer::Restream(bool bMovedFar) if (fIn->GetTotalStreamReferences() == 0) { fIn->InternalStreamOut(); - --iMaxOut; + processedOut++; } m_ToStreamOut.remove(fIn); --fi; @@ -675,7 +831,7 @@ void CClientStreamer::Restream(bool bMovedFar) if (!ReachedLimit()) { cOut->InternalStreamIn(bMovedFar); - --iMaxIn; + processedIn++; ++co; } } @@ -683,6 +839,9 @@ void CClientStreamer::Restream(bool bMovedFar) void CClientStreamer::OnEnterSector(CClientStreamSector* pSector) { + if (!pSector) + return; + CClientStreamElement* pElement = NULL; if (m_pSector) { @@ -696,16 +855,18 @@ void CClientStreamer::OnEnterSector(CClientStreamSector* pSector) for (; iter != uncommon.end(); iter++) { pTempSector = *iter; + if (!pTempSector) + continue; + // Make sure we dont unload our new sector if (pTempSector != pSector) { if (pTempSector->IsActivated()) { - list::iterator iter = pTempSector->Begin(); - for (; iter != pTempSector->End(); iter++) + for (auto elemIter = pTempSector->Begin(); elemIter != pTempSector->End(); elemIter++) { - pElement = *iter; - if (pElement->IsStreamedIn()) + pElement = *elemIter; + if (pElement && pElement->IsStreamedIn()) { // Add it to our streaming out list m_ToStreamOut.push_back(pElement); @@ -720,11 +881,14 @@ void CClientStreamer::OnEnterSector(CClientStreamSector* pSector) // Grab the wanted sectors m_pSector->CompareSurroundings(pSector, &common, &uncommon, true); - // Activate the unwanted sectors + // Activate the wanted sectors iter = uncommon.begin(); for (; iter != uncommon.end(); iter++) { pTempSector = *iter; + if (!pTempSector) + continue; + if (!pTempSector->IsActivated()) { pTempSector->AddElements(&m_ActiveElements); @@ -739,11 +903,14 @@ void CClientStreamer::OnEnterSector(CClientStreamSector* pSector) void CClientStreamer::OnElementEnterSector(CClientStreamElement* pElement, CClientStreamSector* pSector) { + if (!pElement) + return; + CClientStreamSector* pPreviousSector = pElement->GetStreamSector(); if (pPreviousSector) { // Skip if disconnecting - if (g_pClientGame->IsBeingDeleted()) + if (g_pClientGame && g_pClientGame->IsBeingDeleted()) return; // Remove the element from its old sector @@ -771,7 +938,7 @@ void CClientStreamer::OnElementEnterSector(CClientStreamElement* pElement, CClie m_ActiveElements.remove(pElement); // Should we activate this sector? - if (pSector->IsExtra() && (m_pSector->IsMySurroundingSector(pSector) || m_pSector == pSector)) + if (m_pSector && pSector->IsExtra() && (m_pSector->IsMySurroundingSector(pSector) || m_pSector == pSector)) { pSector->AddElements(&m_ActiveElements); pSector->SetActivated(true); @@ -794,12 +961,18 @@ void CClientStreamer::OnElementEnterSector(CClientStreamElement* pElement, CClie void CClientStreamer::OnElementForceStreamIn(CClientStreamElement* pElement) { + if (!pElement) + return; + // Make sure we're streamed in pElement->InternalStreamIn(true); } void CClientStreamer::OnElementForceStreamOut(CClientStreamElement* pElement) { + if (!pElement) + return; + // Make sure we're streamed out if need be if (!IsActiveElement(pElement)) { @@ -809,23 +982,36 @@ void CClientStreamer::OnElementForceStreamOut(CClientStreamElement* pElement) void CClientStreamer::OnElementDimension(CClientStreamElement* pElement) { - if (IsElementShouldVisibleInCurrentDimesnion(pElement)) + if (!pElement) + return; + + bool shouldBeVisible = IsElementShouldVisibleInCurrentDimension(pElement); + bool currentlyInSector = (pElement->GetStreamSector() != nullptr); + + // Should be visible but not in sector -> Add it + if (shouldBeVisible && !currentlyInSector) { - if (!pElement->GetStreamSector()) - { - AddElementInSectors(pElement); - m_outsideCurrentDimensionElements.remove(pElement); - } + m_outsideCurrentDimensionElements.remove(pElement); + AddElementInSectors(pElement); } - else + // Should NOT be visible but IS in sector -> Remove it + else if (!shouldBeVisible && currentlyInSector) { - if (pElement->GetStreamSector()) + // Remove from sector + CClientStreamSector* sector = pElement->GetStreamSector(); + if (sector) { - m_outsideCurrentDimensionElements.push_back(pElement); - RemoveElementFromSectors(pElement); + sector->Remove(pElement); + } - if (pElement->IsStreamedIn()) - m_ToStreamOut.push_back(pElement); + pElement->SetStreamSector(nullptr); + m_ActiveElements.remove(pElement); + m_outsideCurrentDimensionElements.push_back(pElement); + + // Stream out if needed + if (pElement->IsStreamedIn()) + { + m_ToStreamOut.push_back(pElement); } } } diff --git a/Client/mods/deathmatch/logic/CClientStreamer.h b/Client/mods/deathmatch/logic/CClientStreamer.h index 53951fa43bb..1014bbf057d 100644 --- a/Client/mods/deathmatch/logic/CClientStreamer.h +++ b/Client/mods/deathmatch/logic/CClientStreamer.h @@ -51,7 +51,7 @@ class CClientStreamer void AddElementInSectors(CClientStreamElement* pElement); void RemoveElementFromSectors(CClientStreamElement* pElement); - bool IsElementShouldVisibleInCurrentDimesnion(CClientStreamElement* pElement); + bool IsElementShouldVisibleInCurrentDimension(CClientStreamElement* pElement); void SetExpDistances(std::list* pList); void AddToSortedList(std::list* pList, CClientStreamElement* pElement); From f94cb7d43039de63c0f576dc7cc460bfeff5f1f2 Mon Sep 17 00:00:00 2001 From: Xenius97 Date: Mon, 17 Nov 2025 08:45:14 +0100 Subject: [PATCH 08/18] refactor & new limit management --- .../mods/deathmatch/logic/CClientStreamer.cpp | 616 ++++++++++-------- .../mods/deathmatch/logic/CClientStreamer.h | 14 + 2 files changed, 349 insertions(+), 281 deletions(-) diff --git a/Client/mods/deathmatch/logic/CClientStreamer.cpp b/Client/mods/deathmatch/logic/CClientStreamer.cpp index a78de817914..0b2d6d4004e 100644 --- a/Client/mods/deathmatch/logic/CClientStreamer.cpp +++ b/Client/mods/deathmatch/logic/CClientStreamer.cpp @@ -12,12 +12,21 @@ #include "CClientGame.h" #include #include -using std::list; extern CClientGame* g_pClientGame; -void* CClientStreamer::pAddingElement = NULL; +void* CClientStreamer::pAddingElement = nullptr; +// Constants +#define STREAMER_MOVEMENT_THRESHOLD 50.0f +#define STREAMER_MAX_OUT_DEFAULT 6 +#define STREAMER_MAX_IN_DEFAULT 6 +#define STREAMER_MAX_OUT_FAR 1000 +#define STREAMER_MAX_IN_FAR 1000 +#define STREAMER_MAX_SWAPS 10 +#define STREAMER_FURTHEST_IN_LIMIT 50 + +// Element categories enum class ElementCategory { PLAYER, @@ -26,31 +35,37 @@ enum class ElementCategory OTHER }; -inline ElementCategory GetElementCategory(CClientStreamElement* e) +inline ElementCategory GetElementCategory(CClientStreamElement* pElement) { - if (IS_PLAYER(e)) + if (IS_PLAYER(pElement)) return ElementCategory::PLAYER; - if (IS_VEHICLE(e)) + + if (IS_VEHICLE(pElement)) return ElementCategory::VEHICLE; - if (IS_OBJECT(e)) + + if (IS_OBJECT(pElement)) return ElementCategory::OBJECT; + return ElementCategory::OTHER; } CClientStreamer::CClientStreamer(StreamerLimitReachedFunction* pLimitReachedFunc, float fMaxDistance, float fSectorSize, float fRowSize) - : m_fSectorSize(fSectorSize), m_fRowSize(fRowSize), m_pRow(NULL), m_pSector(NULL) + : m_fSectorSize(fSectorSize), + m_fRowSize(fRowSize), + m_pRow(nullptr), + m_pSector(nullptr), + m_fMaxDistanceExp(fMaxDistance * fMaxDistance), + m_fMaxDistanceThreshold((fMaxDistance + 50.0f) * (fMaxDistance + 50.0f)), + m_usDimension(0), + m_vecPosition(0.0f, 0.0f, 0.0f), + m_pLimitReachedFunc(pLimitReachedFunc), + m_iMaxInDefault(STREAMER_MAX_IN_DEFAULT), + m_iMaxOutDefault(STREAMER_MAX_OUT_DEFAULT), + m_iMaxInFar(STREAMER_MAX_IN_FAR), + m_iMaxOutFar(STREAMER_MAX_OUT_FAR) { - // Setup our distance variables - m_fMaxDistanceExp = fMaxDistance * fMaxDistance; - m_fMaxDistanceThreshold = (fMaxDistance + 50.0f) * (fMaxDistance + 50.0f); - m_usDimension = 0; - // We need the limit reached func assert(pLimitReachedFunc); - m_pLimitReachedFunc = pLimitReachedFunc; - - // Initialize position to valid default (0,0,0) - m_vecPosition = CVector(0.0f, 0.0f, 0.0f); // Create our main world sectors covering the mainland CVector2D size(m_fSectorSize, m_fRowSize); @@ -60,32 +75,26 @@ CClientStreamer::CClientStreamer(StreamerLimitReachedFunction* pLimitReachedFunc // Find our row and sector m_pRow = FindOrCreateRow(m_vecPosition); - if (m_pRow) - { - CClientStreamSector* pInitialSector = m_pRow->FindOrCreateSector(m_vecPosition); - if (pInitialSector) - { - OnEnterSector(pInitialSector); - } - } + if (!m_pRow) + return; + + CClientStreamSector* pInitialSector = m_pRow->FindOrCreateSector(m_vecPosition); + if (pInitialSector) + OnEnterSector(pInitialSector); } CClientStreamer::~CClientStreamer() { // Clear our mainland rows - list::iterator iter = m_WorldRows.begin(); - for (; iter != m_WorldRows.end(); iter++) - { - delete *iter; - } + for (CClientStreamSectorRow* pRow : m_WorldRows) + delete pRow; + m_WorldRows.clear(); // Clear our extra rows - iter = m_ExtraRows.begin(); - for (; iter != m_ExtraRows.end(); iter++) - { - delete *iter; - } + for (CClientStreamSectorRow* pRow : m_ExtraRows) + delete pRow; + m_ExtraRows.clear(); } @@ -95,9 +104,13 @@ void CClientStreamer::CreateSectors(std::list* pList, C return; // Creates our sectors within rows, filling up our rectangle, connecting each sector and row - CClientStreamSector * pCurrent = NULL, *pPrevious = NULL, *pPreviousRowSector = NULL; - CClientStreamSectorRow *pCurrentRow = NULL, *pPreviousRow = NULL; - float fX = vecBottomLeft.fX, fY = vecBottomLeft.fY; + CClientStreamSector* pCurrent = nullptr; + CClientStreamSector* pPrevious = nullptr; + CClientStreamSector* pPreviousRowSector = nullptr; + CClientStreamSectorRow* pCurrentRow = nullptr; + CClientStreamSectorRow* pPreviousRow = nullptr; + float fX = vecBottomLeft.fX; + float fY = vecBottomLeft.fY; while (fY < vecTopRight.fY) { @@ -137,8 +150,8 @@ void CClientStreamer::CreateSectors(std::list* pList, C fX += vecSize.fX; } - pPrevious = NULL; - pCurrent = NULL; + pPrevious = nullptr; + pCurrent = nullptr; pPreviousRow = pCurrentRow; pPreviousRowSector = pPreviousRow->Front(); fX = vecBottomLeft.fX; @@ -161,6 +174,7 @@ void CClientStreamer::ConnectRow(CClientStreamSectorRow* pRow) // Connect the other rows to us if (pRow->m_pTop) pRow->m_pTop->m_pBottom = pRow; + if (pRow->m_pBottom) pRow->m_pBottom->m_pTop = pRow; } @@ -175,27 +189,28 @@ void CClientStreamer::DoPulse(CVector& vecPosition) return; } - bool bMovedFar = false; bool bPositionChanged = false; + bool bMovedFar = false; // Has our position changed? if (vecPosition != m_vecPosition) { bPositionChanged = true; - float fDistSquared = (m_vecPosition - vecPosition).LengthSquared(); - bMovedFar = (fDistSquared > (50.0f * 50.0f)); + const float fDistSquared = (m_vecPosition - vecPosition).LengthSquared(); + bMovedFar = (fDistSquared > (STREAMER_MOVEMENT_THRESHOLD * STREAMER_MOVEMENT_THRESHOLD)); m_vecPosition = vecPosition; // Ensure m_pRow exists if (!m_pRow) { m_pRow = FindOrCreateRow(vecPosition); - if (m_pRow) - { - CClientStreamSector* pSector = m_pRow->FindOrCreateSector(vecPosition); - if (pSector) - OnEnterSector(pSector); - } + if (!m_pRow) + return; + + CClientStreamSector* pSector = m_pRow->FindOrCreateSector(vecPosition); + if (pSector) + OnEnterSector(pSector); + return; } @@ -203,13 +218,13 @@ void CClientStreamer::DoPulse(CVector& vecPosition) if (!m_pRow->DoesContain(vecPosition)) { CClientStreamSectorRow* pNewRow = FindOrCreateRow(vecPosition, m_pRow); - if (pNewRow) - { - m_pRow = pNewRow; - CClientStreamSector* pSector = m_pRow->FindOrCreateSector(vecPosition); - if (pSector) - OnEnterSector(pSector); - } + if (!pNewRow) + return; + + m_pRow = pNewRow; + CClientStreamSector* pSector = m_pRow->FindOrCreateSector(vecPosition); + if (pSector) + OnEnterSector(pSector); } // Have we changed sector? else if (m_pSector && !m_pSector->DoesContain(vecPosition)) @@ -245,30 +260,28 @@ void CClientStreamer::SetDimension(unsigned short usDimension) elementsToShow.reserve(100); // Collect elements from sectors that need to be hidden - auto collectFromRows = [this, &elementsToHide](list& rowList) + auto collectFromRows = [this, &elementsToHide](std::list& rowList) { - for (CClientStreamSectorRow* sectorRow : rowList) + for (CClientStreamSectorRow* pSectorRow : rowList) { - if (!sectorRow) + if (!pSectorRow) continue; - for (CClientStreamSector* sector : sectorRow->GetList()) + for (CClientStreamSector* pSector : pSectorRow->GetList()) { - if (!sector) + if (!pSector) continue; // Process directly without making a full copy - for (auto iter = sector->Begin(); iter != sector->End(); ++iter) + for (auto iter = pSector->Begin(); iter != pSector->End(); ++iter) { - CClientStreamElement* element = *iter; - if (!element) + CClientStreamElement* pElement = *iter; + if (!pElement) continue; // Should this element be hidden in the new dimension? - if (!IsElementShouldVisibleInCurrentDimension(element)) - { - elementsToHide.push_back(element); - } + if (!IsElementShouldVisibleInCurrentDimension(pElement)) + elementsToHide.push_back(pElement); } } } @@ -278,46 +291,43 @@ void CClientStreamer::SetDimension(unsigned short usDimension) collectFromRows(m_ExtraRows); // Collect elements that should be shown in new dimension - for (CClientStreamElement* element : m_outsideCurrentDimensionElements) + for (CClientStreamElement* pElement : m_outsideCurrentDimensionElements) { - if (!element) + if (!pElement) continue; // Should this element be visible in new dimension? - if (element->GetDimension() == usDimension || element->IsVisibleInAllDimensions()) - { - elementsToShow.push_back(element); - } + if (pElement->GetDimension() == usDimension || pElement->IsVisibleInAllDimensions()) + elementsToShow.push_back(pElement); } // Process elements to hide - for (CClientStreamElement* element : elementsToHide) + for (CClientStreamElement* pElement : elementsToHide) { - if (!element) + if (!pElement) continue; - CClientStreamSector* sector = element->GetStreamSector(); - if (sector) - { - sector->Remove(element); - element->SetStreamSector(nullptr); - m_ActiveElements.remove(element); - m_outsideCurrentDimensionElements.push_back(element); + CClientStreamSector* pSector = pElement->GetStreamSector(); + if (!pSector) + continue; - if (element->IsStreamedIn()) - { - m_ToStreamOut.push_back(element); - } - } + pSector->Remove(pElement); + pElement->SetStreamSector(nullptr); + m_ActiveElements.remove(pElement); + m_outsideCurrentDimensionElements.push_back(pElement); + + if (pElement->IsStreamedIn()) + m_ToStreamOut.push_back(pElement); } // Process elements to show - for (CClientStreamElement* element : elementsToShow) + for (CClientStreamElement* pElement : elementsToShow) { - if (!element) + if (!pElement) continue; - m_outsideCurrentDimensionElements.remove(element); - AddElementInSectors(element); + + m_outsideCurrentDimensionElements.remove(pElement); + AddElementInSectors(pElement); } } @@ -325,7 +335,7 @@ CClientStreamSectorRow* CClientStreamer::FindOrCreateRow(CVector& vecPosition, C { // Validate position if (!std::isfinite(vecPosition.fY)) - return NULL; + return nullptr; // Do we have a row to check around first? if (pSurrounding) @@ -333,71 +343,58 @@ CClientStreamSectorRow* CClientStreamer::FindOrCreateRow(CVector& vecPosition, C // Check the above and below rows if (pSurrounding->m_pTop && pSurrounding->m_pTop->DoesContain(vecPosition)) return pSurrounding->m_pTop; + if (pSurrounding->m_pBottom && pSurrounding->m_pBottom->DoesContain(vecPosition)) return pSurrounding->m_pBottom; } // Search through our main world rows - CClientStreamSectorRow* pRow = NULL; - list::iterator iter = m_WorldRows.begin(); - for (; iter != m_WorldRows.end(); iter++) + for (CClientStreamSectorRow* pRow : m_WorldRows) { - pRow = *iter; if (pRow && pRow->DoesContain(vecPosition)) - { return pRow; - } } // Search through our extra rows - iter = m_ExtraRows.begin(); - for (; iter != m_ExtraRows.end(); iter++) + for (CClientStreamSectorRow* pRow : m_ExtraRows) { - pRow = *iter; if (pRow && pRow->DoesContain(vecPosition)) - { return pRow; - } } + // We need a new row, align it with the others - float fBottom = float((int)(vecPosition.fY / m_fRowSize)) * m_fRowSize; + float fBottom = static_cast(static_cast(vecPosition.fY / m_fRowSize)) * m_fRowSize; if (vecPosition.fY < 0.0f) fBottom -= m_fRowSize; - pRow = new CClientStreamSectorRow(fBottom, fBottom + m_fRowSize, m_fSectorSize, m_fRowSize); + + CClientStreamSectorRow* pRow = new CClientStreamSectorRow(fBottom, fBottom + m_fRowSize, m_fSectorSize, m_fRowSize); if (!pRow) - return NULL; + return nullptr; ConnectRow(pRow); pRow->SetExtra(true); m_ExtraRows.push_back(pRow); + return pRow; } CClientStreamSectorRow* CClientStreamer::FindRow(float fY) { // Search through our main world rows - CClientStreamSectorRow* pRow = NULL; - list::iterator iter = m_WorldRows.begin(); - for (; iter != m_WorldRows.end(); iter++) + for (CClientStreamSectorRow* pRow : m_WorldRows) { - pRow = *iter; if (pRow && pRow->DoesContain(fY)) - { return pRow; - } } // Search through our extra rows - iter = m_ExtraRows.begin(); - for (; iter != m_ExtraRows.end(); iter++) + for (CClientStreamSectorRow* pRow : m_ExtraRows) { - pRow = *iter; if (pRow && pRow->DoesContain(fY)) - { return pRow; - } } - return NULL; + + return nullptr; } void CClientStreamer::OnUpdateStreamPosition(CClientStreamElement* pElement) @@ -457,7 +454,7 @@ void CClientStreamer::AddElementInSectors(CClientStreamElement* pElement) return; } - assert(pAddingElement == NULL); + assert(pAddingElement == nullptr); pAddingElement = pElement; CVector vecPosition = pElement->GetStreamPosition(); @@ -465,19 +462,23 @@ void CClientStreamer::AddElementInSectors(CClientStreamElement* pElement) // Validate position if (!std::isfinite(vecPosition.fX) || !std::isfinite(vecPosition.fY) || !std::isfinite(vecPosition.fZ)) { - pAddingElement = NULL; + pAddingElement = nullptr; return; } CClientStreamSectorRow* pRow = FindOrCreateRow(vecPosition); - if (pRow) + if (!pRow) { - pElement->SetStreamRow(pRow); - CClientStreamSector* pSector = pRow->FindOrCreateSector(vecPosition); - if (pSector) - OnElementEnterSector(pElement, pSector); + pAddingElement = nullptr; + return; } - pAddingElement = NULL; + + pElement->SetStreamRow(pRow); + CClientStreamSector* pSector = pRow->FindOrCreateSector(vecPosition); + if (pSector) + OnElementEnterSector(pElement, pSector); + + pAddingElement = nullptr; } void CClientStreamer::RemoveElementFromSectors(CClientStreamElement* pElement) @@ -493,6 +494,7 @@ bool CClientStreamer::IsElementShouldVisibleInCurrentDimension(CClientStreamElem { if (!pElement) return false; + return pElement->GetDimension() == m_usDimension || pElement->IsVisibleInAllDimensions(); } @@ -514,8 +516,8 @@ void CClientStreamer::RemoveElement(CClientStreamElement* pElement) if (pElement->GetStreamSector()) { - CClientStreamSector* sector = pElement->GetStreamSector(); - sector->Remove(pElement); + CClientStreamSector* pSector = pElement->GetStreamSector(); + pSector->Remove(pElement); pElement->SetStreamSector(nullptr); } @@ -524,14 +526,13 @@ void CClientStreamer::RemoveElement(CClientStreamElement* pElement) m_outsideCurrentDimensionElements.remove(pElement); } -void CClientStreamer::SetExpDistances(list* pList) +void CClientStreamer::SetExpDistances(std::list* pList) { if (!pList) return; - for (auto iter = pList->begin(); iter != pList->end(); ++iter) + for (CClientStreamElement* pElement : *pList) { - CClientStreamElement* pElement = *iter; if (!pElement) continue; @@ -539,13 +540,13 @@ void CClientStreamer::SetExpDistances(list* pList) } } -void CClientStreamer::AddToSortedList(list* pList, CClientStreamElement* pElement) +void CClientStreamer::AddToSortedList(std::list* pList, CClientStreamElement* pElement) { if (!pList || !pElement) return; - // Make sure it's exp distance is updated - float fDistance = pElement->GetDistanceToBoundingBoxSquared(m_vecPosition); + // Make sure its exp distance is updated + const float fDistance = pElement->GetDistanceToBoundingBoxSquared(m_vecPosition); pElement->SetExpDistance(fDistance); // Don't add if already in the list @@ -553,8 +554,7 @@ void CClientStreamer::AddToSortedList(list* pList, CClien return; // Search through our list. Add it behind the first item further away than this - list::iterator iter = pList->begin(); - for (; iter != pList->end(); iter++) + for (auto iter = pList->begin(); iter != pList->end(); ++iter) { CClientStreamElement* pTemp = *iter; if (!pTemp) @@ -575,12 +575,9 @@ void CClientStreamer::AddToSortedList(list* pList, CClien bool CClientStreamer::CompareExpDistance(CClientStreamElement* p1, CClientStreamElement* p2) { - if (!p1 && !p2) + if (!p1 || !p2) return false; - if (!p1) - return false; - if (!p2) - return true; + return p1->GetExpDistance() < p2->GetExpDistance(); } @@ -589,42 +586,41 @@ bool CClientStreamer::IsActiveElement(CClientStreamElement* pElement) if (!pElement) return false; - for (auto iter = m_ActiveElements.begin(); iter != m_ActiveElements.end(); ++iter) + for (CClientStreamElement* pActive : m_ActiveElements) { - if (*iter == pElement) - { + if (pActive == pElement) return true; - } } + return false; } void CClientStreamer::Restream(bool bMovedFar) { // Adjust stream-in/out limits (large movement allows massive updates) - int iMaxOut = bMovedFar ? 1000 : 6; - int iMaxIn = bMovedFar ? 1000 : 6; + const int iMaxOut = bMovedFar ? m_iMaxOutFar : m_iMaxOutDefault; + const int iMaxIn = bMovedFar ? m_iMaxInFar : m_iMaxInDefault; // Process pending stream-out elements - int processedOut = 0; + int iProcessedOut = 0; auto iter = m_ToStreamOut.begin(); - while (iter != m_ToStreamOut.end() && processedOut < iMaxOut) + while (iter != m_ToStreamOut.end() && iProcessedOut < iMaxOut) { - CClientStreamElement* e = *iter; + CClientStreamElement* pElement = *iter; - if (!e) + if (!pElement) { iter = m_ToStreamOut.erase(iter); continue; } // Only stream out if no references remain - if (e->GetTotalStreamReferences() == 0) + if (pElement->GetTotalStreamReferences() == 0) { - e->InternalStreamOut(); + pElement->InternalStreamOut(); iter = m_ToStreamOut.erase(iter); - processedOut++; + iProcessedOut++; } else { @@ -638,59 +634,61 @@ void CClientStreamer::Restream(bool bMovedFar) // Reserve space closestOut.reserve(iMaxIn); - furthestIn.reserve(50); + furthestIn.reserve(STREAMER_FURTHEST_IN_LIMIT); - bool reachedLimit = ReachedLimit(); - int processedIn = 0; + bool bReachedLimit = ReachedLimit(); + int iProcessedIn = 0; // Elements are sorted: nearest -> furthest - for (CClientStreamElement* e : m_ActiveElements) + for (CClientStreamElement* pElement : m_ActiveElements) { - if (!e) + if (!pElement) continue; // Early exit if we've processed enough - if (processedIn >= iMaxIn && processedOut >= iMaxOut && !bMovedFar) + if (iProcessedIn >= iMaxIn && iProcessedOut >= iMaxOut && !bMovedFar) break; - float dist = e->GetExpDistance(); - ElementCategory category = GetElementCategory(e); - bool isIn = e->IsStreamedIn(); + const float fDist = pElement->GetExpDistance(); + ElementCategory category = GetElementCategory(pElement); + const bool bIsIn = pElement->IsStreamedIn(); - if (isIn) + if (bIsIn) { // Extra handling for vehicles and players switch (category) { case ElementCategory::VEHICLE: { - auto* v = DynamicCast(e); + auto* pVehicle = DynamicCast(pElement); + if (!pVehicle) + break; // Skip if player in vehicle had a lightsync update - if (v && v->GetOccupant() && IS_PLAYER(v->GetOccupant())) + if (pVehicle->GetOccupant() && IS_PLAYER(pVehicle->GetOccupant())) { - auto* p = DynamicCast(v->GetOccupant()); - if (p && p->GetLastPuresyncType() == PURESYNC_TYPE_LIGHTSYNC) + auto* pPlayer = DynamicCast(pVehicle->GetOccupant()); + if (pPlayer && pPlayer->GetLastPuresyncType() == PURESYNC_TYPE_LIGHTSYNC) { - m_ToStreamOut.push_back(e); + m_ToStreamOut.push_back(pElement); continue; } } // Towed vehicles are handled by the tow vehicle - if (v && v->GetTowedByVehicle()) + if (pVehicle->GetTowedByVehicle()) continue; } break; case ElementCategory::PLAYER: { - auto* p = DynamicCast(e); + auto* pPlayer = DynamicCast(pElement); // Lightsync players should not be loaded - if (p && p->GetLastPuresyncType() == PURESYNC_TYPE_LIGHTSYNC) + if (pPlayer && pPlayer->GetLastPuresyncType() == PURESYNC_TYPE_LIGHTSYNC) { - m_ToStreamOut.push_back(e); + m_ToStreamOut.push_back(pElement); continue; } } @@ -701,30 +699,30 @@ void CClientStreamer::Restream(bool bMovedFar) } // Too far -> stream out (use threshold to avoid flickering) - if (dist > m_fMaxDistanceThreshold) + if (fDist > m_fMaxDistanceThreshold) { - if (processedOut < iMaxOut && e->GetTotalStreamReferences() == 0) + if (iProcessedOut < iMaxOut && pElement->GetTotalStreamReferences() == 0) { - e->InternalStreamOut(); - processedOut++; + pElement->InternalStreamOut(); + iProcessedOut++; } else { - m_ToStreamOut.push_back(e); + m_ToStreamOut.push_back(pElement); } } else { // Still inside distance -> keep track for possible swapping - if (furthestIn.size() < 50) // Limit size - furthestIn.push_back(e); + if (furthestIn.size() < STREAMER_FURTHEST_IN_LIMIT) + furthestIn.push_back(pElement); } continue; } // Too far -> ignore - if (dist > m_fMaxDistanceExp) + if (fDist > m_fMaxDistanceExp) continue; // Special rules for vehicles / players @@ -732,26 +730,28 @@ void CClientStreamer::Restream(bool bMovedFar) { case ElementCategory::VEHICLE: { - auto* v = DynamicCast(e); + auto* pVehicle = DynamicCast(pElement); + if (!pVehicle) + break; // Lightsync driver -> do not stream in - if (v && v->GetOccupant() && IS_PLAYER(v->GetOccupant())) + if (pVehicle->GetOccupant() && IS_PLAYER(pVehicle->GetOccupant())) { - auto* p = DynamicCast(v->GetOccupant()); - if (p && p->GetLastPuresyncType() == PURESYNC_TYPE_LIGHTSYNC) + auto* pPlayer = DynamicCast(pVehicle->GetOccupant()); + if (pPlayer && pPlayer->GetLastPuresyncType() == PURESYNC_TYPE_LIGHTSYNC) continue; } // Towed vehicles are handled elsewhere - if (v && v->GetTowedByVehicle()) + if (pVehicle->GetTowedByVehicle()) continue; } break; case ElementCategory::PLAYER: { - auto* p = DynamicCast(e); - if (p && p->GetLastPuresyncType() == PURESYNC_TYPE_LIGHTSYNC) + auto* pPlayer = DynamicCast(pElement); + if (pPlayer && pPlayer->GetLastPuresyncType() == PURESYNC_TYPE_LIGHTSYNC) continue; } break; @@ -761,78 +761,81 @@ void CClientStreamer::Restream(bool bMovedFar) } // Attached element: only load if LOD does not match - if (auto* at = DynamicCast(e->GetAttachedTo())) + if (auto* pAttached = DynamicCast(pElement->GetAttachedTo())) { - if (!at->IsStreamedIn()) + if (!pAttached->IsStreamedIn()) { - auto* o = DynamicCast(e); - auto* o_att = DynamicCast(at); - if (!o || !o_att || o->IsLowLod() == o_att->IsLowLod()) + auto* pObject = DynamicCast(pElement); + auto* pObjectAtt = DynamicCast(pAttached); + if (!pObject || !pObjectAtt || pObject->IsLowLod() == pObjectAtt->IsLowLod()) continue; } } // Reached streaming limit -> mark as candidate - if (reachedLimit) + if (bReachedLimit) { - if ((int)closestOut.size() < iMaxIn) - closestOut.push_back(e); + if (static_cast(closestOut.size()) < iMaxIn) + closestOut.push_back(pElement); } else { // Stream in now (instant if moved far) - if (processedIn < iMaxIn) + if (iProcessedIn < iMaxIn) { - e->InternalStreamIn(bMovedFar); - processedIn++; + pElement->InternalStreamIn(bMovedFar); + iProcessedIn++; - reachedLimit = ReachedLimit(); + bReachedLimit = ReachedLimit(); } } } // Swap logic when at streaming limit - if (!reachedLimit || closestOut.empty() || furthestIn.empty()) + if (!bReachedLimit || closestOut.empty() || furthestIn.empty()) return; - int fi = (int)furthestIn.size() - 1; - unsigned int co = 0; + int iFurthestIndex = static_cast(furthestIn.size()) - 1; + unsigned int uiClosestIndex = 0; - // Up to 10 swaps per frame - for (int i = 0; i < 10; i++) + // Up to STREAMER_MAX_SWAPS swaps per frame + for (int i = 0; i < STREAMER_MAX_SWAPS; i++) { - if (processedIn >= iMaxIn || processedOut >= iMaxOut) + if (iProcessedIn >= iMaxIn || iProcessedOut >= iMaxOut) break; - if (fi < 0) + + if (iFurthestIndex < 0) break; - if (co >= closestOut.size()) + + if (uiClosestIndex >= closestOut.size()) break; - auto* fIn = furthestIn[fi]; - auto* cOut = closestOut[co]; + auto* pFurthestIn = furthestIn[iFurthestIndex]; + auto* pClosestOut = closestOut[uiClosestIndex]; - if (!fIn || !cOut) + if (!pFurthestIn || !pClosestOut) break; // Only swap if the streamed-out item is closer - if (cOut->GetExpDistance() >= fIn->GetExpDistance()) + if (pClosestOut->GetExpDistance() >= pFurthestIn->GetExpDistance()) break; // Stream out the furthest streamed-in item - if (fIn->GetTotalStreamReferences() == 0) + if (pFurthestIn->GetTotalStreamReferences() == 0) { - fIn->InternalStreamOut(); - processedOut++; + pFurthestIn->InternalStreamOut(); + iProcessedOut++; } - m_ToStreamOut.remove(fIn); - --fi; + + m_ToStreamOut.remove(pFurthestIn); + --iFurthestIndex; // Stream in the closest streamed-out item if (!ReachedLimit()) { - cOut->InternalStreamIn(bMovedFar); - processedIn++; - ++co; + pClosestOut->InternalStreamIn(bMovedFar); + iProcessedIn++; + ++uiClosestIndex; } } } @@ -842,60 +845,59 @@ void CClientStreamer::OnEnterSector(CClientStreamSector* pSector) if (!pSector) return; - CClientStreamElement* pElement = NULL; - if (m_pSector) + if (!m_pSector) { - // Grab the unwanted sectors - list common, uncommon; - pSector->CompareSurroundings(m_pSector, &common, &uncommon, true); + m_pSector = pSector; + SetExpDistances(&m_ActiveElements); + m_ActiveElements.sort(CompareExpDistance); + return; + } - // Deactivate the unwanted sectors - CClientStreamSector* pTempSector = NULL; - list::iterator iter = uncommon.begin(); - for (; iter != uncommon.end(); iter++) - { - pTempSector = *iter; - if (!pTempSector) - continue; + // Grab the unwanted sectors + std::list common; + std::list uncommon; + pSector->CompareSurroundings(m_pSector, &common, &uncommon, true); - // Make sure we dont unload our new sector - if (pTempSector != pSector) - { - if (pTempSector->IsActivated()) - { - for (auto elemIter = pTempSector->Begin(); elemIter != pTempSector->End(); elemIter++) - { - pElement = *elemIter; - if (pElement && pElement->IsStreamedIn()) - { - // Add it to our streaming out list - m_ToStreamOut.push_back(pElement); - } - } - pTempSector->RemoveElements(&m_ActiveElements); - pTempSector->SetActivated(false); - } - } - } + // Deactivate the unwanted sectors + for (CClientStreamSector* pTempSector : uncommon) + { + if (!pTempSector) + continue; + + // Make sure we dont unload our new sector + if (pTempSector == pSector) + continue; - // Grab the wanted sectors - m_pSector->CompareSurroundings(pSector, &common, &uncommon, true); + if (!pTempSector->IsActivated()) + continue; - // Activate the wanted sectors - iter = uncommon.begin(); - for (; iter != uncommon.end(); iter++) + for (auto elemIter = pTempSector->Begin(); elemIter != pTempSector->End(); ++elemIter) { - pTempSector = *iter; - if (!pTempSector) - continue; + CClientStreamElement* pElement = *elemIter; + if (pElement && pElement->IsStreamedIn()) + m_ToStreamOut.push_back(pElement); + } - if (!pTempSector->IsActivated()) - { - pTempSector->AddElements(&m_ActiveElements); - pTempSector->SetActivated(true); - } + pTempSector->RemoveElements(&m_ActiveElements); + pTempSector->SetActivated(false); + } + + // Grab the wanted sectors + m_pSector->CompareSurroundings(pSector, &common, &uncommon, true); + + // Activate the wanted sectors + for (CClientStreamSector* pTempSector : uncommon) + { + if (!pTempSector) + continue; + + if (!pTempSector->IsActivated()) + { + pTempSector->AddElements(&m_ActiveElements); + pTempSector->SetActivated(true); } } + m_pSector = pSector; SetExpDistances(&m_ActiveElements); m_ActiveElements.sort(CompareExpDistance); @@ -916,6 +918,7 @@ void CClientStreamer::OnElementEnterSector(CClientStreamElement* pElement, CClie // Remove the element from its old sector pPreviousSector->Remove(pElement); } + if (pSector) { // Add the element to its new sector @@ -926,10 +929,7 @@ void CClientStreamer::OnElementEnterSector(CClientStreamElement* pElement, CClie { // Was the previous sector not active? if (!pPreviousSector || !pPreviousSector->IsActivated()) - { - // Add this element to our active-elements list AddToSortedList(&m_ActiveElements, pElement); - } } else { @@ -956,6 +956,7 @@ void CClientStreamer::OnElementEnterSector(CClientStreamElement* pElement, CClie // Remove it from active elements too. m_ActiveElements.remove(pElement); } + pElement->SetStreamSector(pSector); } @@ -975,9 +976,7 @@ void CClientStreamer::OnElementForceStreamOut(CClientStreamElement* pElement) // Make sure we're streamed out if need be if (!IsActiveElement(pElement)) - { m_ToStreamOut.push_back(pElement); - } } void CClientStreamer::OnElementDimension(CClientStreamElement* pElement) @@ -985,24 +984,24 @@ void CClientStreamer::OnElementDimension(CClientStreamElement* pElement) if (!pElement) return; - bool shouldBeVisible = IsElementShouldVisibleInCurrentDimension(pElement); - bool currentlyInSector = (pElement->GetStreamSector() != nullptr); + const bool bShouldBeVisible = IsElementShouldVisibleInCurrentDimension(pElement); + const bool bCurrentlyInSector = (pElement->GetStreamSector() != nullptr); // Should be visible but not in sector -> Add it - if (shouldBeVisible && !currentlyInSector) + if (bShouldBeVisible && !bCurrentlyInSector) { m_outsideCurrentDimensionElements.remove(pElement); AddElementInSectors(pElement); + return; } + // Should NOT be visible but IS in sector -> Remove it - else if (!shouldBeVisible && currentlyInSector) + if (!bShouldBeVisible && bCurrentlyInSector) { // Remove from sector - CClientStreamSector* sector = pElement->GetStreamSector(); - if (sector) - { - sector->Remove(pElement); - } + CClientStreamSector* pSector = pElement->GetStreamSector(); + if (pSector) + pSector->Remove(pElement); pElement->SetStreamSector(nullptr); m_ActiveElements.remove(pElement); @@ -1010,8 +1009,63 @@ void CClientStreamer::OnElementDimension(CClientStreamElement* pElement) // Stream out if needed if (pElement->IsStreamedIn()) - { m_ToStreamOut.push_back(pElement); - } } } + +void CClientStreamer::SetStreamerLimits(int normalIn, int normalOut, int farIn, int farOut) +{ + if (normalIn <= 0 || normalOut <= 0 || farIn <= 0 || farOut <= 0) + return; + + m_iMaxInDefault = normalIn; + m_iMaxOutDefault = normalOut; + m_iMaxInFar = farIn; + m_iMaxOutFar = farOut; +} + +void CClientStreamer::ResetStreamerLimits() +{ + m_iMaxInDefault = STREAMER_MAX_IN_DEFAULT; + m_iMaxOutDefault = STREAMER_MAX_OUT_DEFAULT; + m_iMaxInFar = STREAMER_MAX_IN_FAR; + m_iMaxOutFar = STREAMER_MAX_OUT_FAR; + m_iMaxSwaps = STREAMER_MAX_SWAPS; + m_iFurthestInLimit = STREAMER_FURTHEST_IN_LIMIT; +} + +void CClientStreamer::GetStreamingLimits(int& normalIn, int& normalOut, int& farIn, int& farOut, int& maxSwaps, int& furthestInLimit) const noexcept +{ + normalIn = m_iMaxInDefault; + normalOut = m_iMaxOutDefault; + farIn = m_iMaxInFar; + farOut = m_iMaxOutFar; + maxSwaps = m_iMaxSwaps; + furthestInLimit = m_iFurthestInLimit; +} + +void CClientStreamer::SetStreamerMaxSwaps(int maxSwaps) +{ + if (maxSwaps <= 0) + return; + + m_iMaxSwaps = maxSwaps; +} + +void CClientStreamer::ResetStreamerMaxSwaps() +{ + m_iMaxSwaps = STREAMER_MAX_SWAPS; +} + +void CClientStreamer::SetStreamerFurthestInLimit(int limit) +{ + if (limit <= 0) + return; + + m_iFurthestInLimit = limit; +} + +void CClientStreamer::ResetStreamerFurthestInLimit() +{ + m_iFurthestInLimit = STREAMER_FURTHEST_IN_LIMIT; +} diff --git a/Client/mods/deathmatch/logic/CClientStreamer.h b/Client/mods/deathmatch/logic/CClientStreamer.h index 1014bbf057d..7c8911a8f69 100644 --- a/Client/mods/deathmatch/logic/CClientStreamer.h +++ b/Client/mods/deathmatch/logic/CClientStreamer.h @@ -30,6 +30,14 @@ class CClientStreamer static bool CompareExpDistance(CClientStreamElement* p1, CClientStreamElement* p2); + void SetStreamerLimits(int normalIn, int normalOut, int farIn, int farOut); + void ResetStreamerLimits(); + void GetStreamingLimits(int& normalIn, int& normalOut, int& farIn, int& farOut, int& maxSwaps, int& furthestInLimit) const noexcept; + void SetStreamerMaxSwaps(int maxSwaps); + void ResetStreamerMaxSwaps(); + void SetStreamerFurthestInLimit(int limit); + void ResetStreamerFurthestInLimit(); + unsigned int CountActiveElements() { return (unsigned int)m_ActiveElements.size(); } bool IsActiveElement(CClientStreamElement* pElement); std::list::iterator ActiveElementsBegin() { return m_ActiveElements.begin(); } @@ -79,6 +87,12 @@ class CClientStreamer std::list m_ActiveElements; std::list m_ToStreamOut; std::list m_outsideCurrentDimensionElements; + int m_iMaxInDefault; + int m_iMaxOutDefault; + int m_iMaxInFar; + int m_iMaxOutFar; + int m_iMaxSwaps; + int m_iFurthestInLimit; static void* pAddingElement; }; From 83e45f18cf4c4f1c7dcef6d601014b6d9c6b981c Mon Sep 17 00:00:00 2001 From: Xenius97 Date: Mon, 17 Nov 2025 09:07:45 +0100 Subject: [PATCH 09/18] crash fix --- Client/mods/deathmatch/logic/CClientGame.cpp | 5 ++ .../mods/deathmatch/logic/CClientStreamer.cpp | 16 +++- .../logic/luadefs/CLuaEngineDefs.cpp | 79 +++++++++++++++++++ .../deathmatch/logic/luadefs/CLuaEngineDefs.h | 8 ++ 4 files changed, 105 insertions(+), 3 deletions(-) diff --git a/Client/mods/deathmatch/logic/CClientGame.cpp b/Client/mods/deathmatch/logic/CClientGame.cpp index 7632881badd..2db78153a64 100644 --- a/Client/mods/deathmatch/logic/CClientGame.cpp +++ b/Client/mods/deathmatch/logic/CClientGame.cpp @@ -426,6 +426,11 @@ CClientGame::~CClientGame() // ...and restore the buffer size too g_pGame->GetStreaming()->SetStreamingBufferSize(g_pClientGame->GetManager()->GetIMGManager()->GetLargestFileSizeBlocks()); + // Reset streamer limits + g_pClientGame->GetManager()->GetObjectStreamer()->ResetStreamerLimits(); + g_pClientGame->GetManager()->GetObjectStreamer()->ResetStreamerMaxSwaps(); + g_pClientGame->GetManager()->GetObjectStreamer()->ResetStreamerFurthestInLimit(); + // Reset camera shaking g_pGame->GetCamera()->SetShakeForce(0.0f); diff --git a/Client/mods/deathmatch/logic/CClientStreamer.cpp b/Client/mods/deathmatch/logic/CClientStreamer.cpp index 0b2d6d4004e..7dbaa44d898 100644 --- a/Client/mods/deathmatch/logic/CClientStreamer.cpp +++ b/Client/mods/deathmatch/logic/CClientStreamer.cpp @@ -256,9 +256,6 @@ void CClientStreamer::SetDimension(unsigned short usDimension) std::vector elementsToHide; std::vector elementsToShow; - elementsToHide.reserve(100); - elementsToShow.reserve(100); - // Collect elements from sectors that need to be hidden auto collectFromRows = [this, &elementsToHide](std::list& rowList) { @@ -487,6 +484,11 @@ void CClientStreamer::RemoveElementFromSectors(CClientStreamElement* pElement) return; OnElementEnterSector(pElement, nullptr); + + // Clear row and sector + pElement->SetStreamRow(nullptr); + + // Remove from active elements m_ToStreamOut.remove(pElement); } @@ -957,7 +959,12 @@ void CClientStreamer::OnElementEnterSector(CClientStreamElement* pElement, CClie m_ActiveElements.remove(pElement); } + // Update the element's sector pointer pElement->SetStreamSector(pSector); + + // If the element no longer has a sector, clear the row pointer too + if (!pSector) + pElement->SetStreamRow(nullptr); } void CClientStreamer::OnElementForceStreamIn(CClientStreamElement* pElement) @@ -1010,6 +1017,9 @@ void CClientStreamer::OnElementDimension(CClientStreamElement* pElement) // Stream out if needed if (pElement->IsStreamedIn()) m_ToStreamOut.push_back(pElement); + + // Clear row pointer + pElement->SetStreamRow(nullptr); } } diff --git a/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.cpp b/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.cpp index 04a361d8a9f..ca1d8b5f45a 100644 --- a/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.cpp +++ b/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.cpp @@ -152,6 +152,13 @@ void CLuaEngineDefs::LoadFunctions() {"enginePreloadWorldArea", ArgumentParser}, {"engineRestreamModel", ArgumentParser}, {"engineRestream", ArgumentParser}, + {"engineStreamingSetLimits", ArgumentParser}, + {"engineStreamingGetLimits", ArgumentParser}, + {"engineStreamingResetLimits", ArgumentParser}, + {"engineStreamingSetMaxSwaps", ArgumentParser}, + {"engineStreamingResetMaxSwaps", ArgumentParser}, + {"engineStreamingSetFurthestInLimit", ArgumentParser}, + {"engineStreamingResetFurthestInLimit", ArgumentParser}, // CLuaCFunctions::AddFunction ( "engineReplaceMatchingAtomics", EngineReplaceMatchingAtomics ); @@ -216,6 +223,13 @@ void CLuaEngineDefs::AddClass(lua_State* luaVM) lua_classfunction(luaVM, "getBufferSize", "engineStreamingGetBufferSize"); lua_classfunction(luaVM, "setBufferSize", "engineStreamingSetBufferSize"); lua_classfunction(luaVM, "restoreBufferSize", "engineStreamingRestoreBufferSize"); + lua_classfunction(luaVM, "setLimits", "engineStreamingSetLimits"); + lua_classfunction(luaVM, "getLimits", "engineStreamingGetLimits"); + lua_classfunction(luaVM, "resetLimits", "engineStreamingResetLimits"); + lua_classfunction(luaVM, "setMaxSwaps", "engineStreamingSetMaxSwaps"); + lua_classfunction(luaVM, "resetMaxSwaps", "engineStreamingResetMaxSwaps"); + lua_classfunction(luaVM, "setFurthestInLimit", "engineStreamingSetFurthestInLimit"); + lua_classfunction(luaVM, "resetFurthestInLimit", "engineStreamingResetFurthestInLimit"); lua_classvariable(luaVM, "memorySize", "engineStreamingSetMemorySize", "engineStreamingGetMemorySize"); lua_classvariable(luaVM, "bufferSize", "engineStreamingSetBufferSize", "engineStreamingGetBufferSize"); @@ -2615,3 +2629,68 @@ void CLuaEngineDefs::EngineRestream(std::optional option) { g_pClientGame->Restream(option); } + +void CLuaEngineDefs::EngineStreamingSetLimits(int normalIn, int normalOut, int farIn, int farOut) +{ + g_pClientGame->GetManager()->GetObjectStreamer()->SetStreamerLimits(normalIn, normalOut, farIn, farOut); +} + +int CLuaEngineDefs::EngineStreamingGetLimits(lua_State* luaVM) +{ + auto* pStreamer = g_pClientGame->GetManager()->GetObjectStreamer(); + int normalIn, normalOut, farIn, farOut, maxSwaps, furthestInLimit; + pStreamer->GetStreamingLimits(normalIn, normalOut, farIn, farOut, maxSwaps, furthestInLimit); + + lua_newtable(luaVM); + + lua_pushstring(luaVM, "normalIn"); + lua_pushnumber(luaVM, normalIn); + lua_settable(luaVM, -3); + + lua_pushstring(luaVM, "normalOut"); + lua_pushnumber(luaVM, normalOut); + lua_settable(luaVM, -3); + + lua_pushstring(luaVM, "farIn"); + lua_pushnumber(luaVM, farIn); + lua_settable(luaVM, -3); + + lua_pushstring(luaVM, "farOut"); + lua_pushnumber(luaVM, farOut); + lua_settable(luaVM, -3); + + lua_pushstring(luaVM, "maxSwaps"); + lua_pushnumber(luaVM, maxSwaps); + lua_settable(luaVM, -3); + + lua_pushstring(luaVM, "furthestInLimit"); + lua_pushnumber(luaVM, furthestInLimit); + lua_settable(luaVM, -3); + + return 1; +} + +void CLuaEngineDefs::EngineStreamingResetLimits() +{ + g_pClientGame->GetManager()->GetObjectStreamer()->ResetStreamerLimits(); +} + +void CLuaEngineDefs::EngineStreamingSetMaxSwaps(int maxSwaps) +{ + g_pClientGame->GetManager()->GetObjectStreamer()->SetStreamerMaxSwaps(maxSwaps); +} + +void CLuaEngineDefs::EngineStreamingResetMaxSwaps() +{ + g_pClientGame->GetManager()->GetObjectStreamer()->ResetStreamerMaxSwaps(); +} + +void CLuaEngineDefs::EngineStreamingSetFurthestInLimit(int limit) +{ + g_pClientGame->GetManager()->GetObjectStreamer()->SetStreamerFurthestInLimit(limit); +} + +void CLuaEngineDefs::EngineStreamingResetFurthestInLimit() +{ + g_pClientGame->GetManager()->GetObjectStreamer()->ResetStreamerFurthestInLimit(); +} diff --git a/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.h b/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.h index 6a107c0ded0..8be1fa3eeed 100644 --- a/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.h +++ b/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.h @@ -99,6 +99,14 @@ class CLuaEngineDefs : public CLuaDefs static bool EngineRestreamModel(std::uint16_t modelId); static void EngineRestream(std::optional option); + static void EngineStreamingSetLimits(int normalIn, int normalOut, int farIn, int farOut); + static int EngineStreamingGetLimits(lua_State* luaVM); + static void EngineStreamingResetLimits(); + static void EngineStreamingSetMaxSwaps(int maxSwaps); + static void EngineStreamingResetMaxSwaps(); + static void EngineStreamingSetFurthestInLimit(int limit); + static void EngineStreamingResetFurthestInLimit(); + private: static void AddEngineColClass(lua_State* luaVM); static void AddEngineTxdClass(lua_State* luaVM); From e1d32e6237784a4e3311bd8739b8945dbf41801f Mon Sep 17 00:00:00 2001 From: Xenius97 Date: Mon, 17 Nov 2025 09:12:36 +0100 Subject: [PATCH 10/18] typo --- Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.cpp b/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.cpp index ca1d8b5f45a..194e46bf015 100644 --- a/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.cpp +++ b/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.cpp @@ -2641,7 +2641,7 @@ int CLuaEngineDefs::EngineStreamingGetLimits(lua_State* luaVM) int normalIn, normalOut, farIn, farOut, maxSwaps, furthestInLimit; pStreamer->GetStreamingLimits(normalIn, normalOut, farIn, farOut, maxSwaps, furthestInLimit); - lua_newtable(luaVM); + lua_createtable(luaVM, 0, 6); lua_pushstring(luaVM, "normalIn"); lua_pushnumber(luaVM, normalIn); From f6d77c008458f63d5937c9d2cdf09ed71952cb6a Mon Sep 17 00:00:00 2001 From: Xenius97 Date: Mon, 17 Nov 2025 09:23:52 +0100 Subject: [PATCH 11/18] Finish object streamer limit manager & fixes --- .../mods/deathmatch/logic/CClientStreamer.cpp | 19 +++++++++++-------- .../logic/luadefs/CLuaEngineDefs.cpp | 2 +- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/Client/mods/deathmatch/logic/CClientStreamer.cpp b/Client/mods/deathmatch/logic/CClientStreamer.cpp index 7dbaa44d898..9a7fa634f79 100644 --- a/Client/mods/deathmatch/logic/CClientStreamer.cpp +++ b/Client/mods/deathmatch/logic/CClientStreamer.cpp @@ -1025,8 +1025,11 @@ void CClientStreamer::OnElementDimension(CClientStreamElement* pElement) void CClientStreamer::SetStreamerLimits(int normalIn, int normalOut, int farIn, int farOut) { - if (normalIn <= 0 || normalOut <= 0 || farIn <= 0 || farOut <= 0) - return; + if ((normalIn) < (STREAMER_MAX_IN_DEFAULT / 2) || (normalOut) < (STREAMER_MAX_OUT_DEFAULT / 2) || (farIn) < (STREAMER_MAX_IN_FAR / 2) || + (farOut) < (STREAMER_MAX_OUT_FAR / 2)) // Block too low limits to avoid issues + { + throw std::invalid_argument("Invalid streamer limits"); + } m_iMaxInDefault = normalIn; m_iMaxOutDefault = normalOut; @@ -1056,8 +1059,8 @@ void CClientStreamer::GetStreamingLimits(int& normalIn, int& normalOut, int& far void CClientStreamer::SetStreamerMaxSwaps(int maxSwaps) { - if (maxSwaps <= 0) - return; + if (maxSwaps < (STREAMER_MAX_SWAPS / 2)) + throw std::invalid_argument("Invalid streamer limits"); m_iMaxSwaps = maxSwaps; } @@ -1067,12 +1070,12 @@ void CClientStreamer::ResetStreamerMaxSwaps() m_iMaxSwaps = STREAMER_MAX_SWAPS; } -void CClientStreamer::SetStreamerFurthestInLimit(int limit) +void CClientStreamer::SetStreamerFurthestInLimit(int furthestInLimit) { - if (limit <= 0) - return; + if (furthestInLimit < (STREAMER_FURTHEST_IN_LIMIT / 2)) + throw std::invalid_argument("Invalid streamer limits"); - m_iFurthestInLimit = limit; + m_iFurthestInLimit = furthestInLimit; } void CClientStreamer::ResetStreamerFurthestInLimit() diff --git a/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.cpp b/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.cpp index 194e46bf015..8f41e21c23a 100644 --- a/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.cpp +++ b/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.cpp @@ -153,7 +153,7 @@ void CLuaEngineDefs::LoadFunctions() {"engineRestreamModel", ArgumentParser}, {"engineRestream", ArgumentParser}, {"engineStreamingSetLimits", ArgumentParser}, - {"engineStreamingGetLimits", ArgumentParser}, + {"engineStreamingGetLimits", CLuaEngineDefs::EngineStreamingGetLimits}, {"engineStreamingResetLimits", ArgumentParser}, {"engineStreamingSetMaxSwaps", ArgumentParser}, {"engineStreamingResetMaxSwaps", ArgumentParser}, From 52b19c3fe6aaef772f067bcdc89710ce0b7e7300 Mon Sep 17 00:00:00 2001 From: Xenius97 Date: Mon, 17 Nov 2025 10:06:07 +0100 Subject: [PATCH 12/18] fix initialization now should be good --- Client/mods/deathmatch/logic/CClientStreamer.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Client/mods/deathmatch/logic/CClientStreamer.cpp b/Client/mods/deathmatch/logic/CClientStreamer.cpp index 9a7fa634f79..bf9a5ec2067 100644 --- a/Client/mods/deathmatch/logic/CClientStreamer.cpp +++ b/Client/mods/deathmatch/logic/CClientStreamer.cpp @@ -62,7 +62,9 @@ CClientStreamer::CClientStreamer(StreamerLimitReachedFunction* pLimitReachedFunc m_iMaxInDefault(STREAMER_MAX_IN_DEFAULT), m_iMaxOutDefault(STREAMER_MAX_OUT_DEFAULT), m_iMaxInFar(STREAMER_MAX_IN_FAR), - m_iMaxOutFar(STREAMER_MAX_OUT_FAR) + m_iMaxOutFar(STREAMER_MAX_OUT_FAR), + m_iMaxSwaps(STREAMER_MAX_SWAPS), + m_iFurthestInLimit(STREAMER_FURTHEST_IN_LIMIT) { // We need the limit reached func assert(pLimitReachedFunc); From 6a5086484407ad24de86c6cf3b34d1b66a8b19ca Mon Sep 17 00:00:00 2001 From: Xenius97 Date: Mon, 17 Nov 2025 10:58:09 +0100 Subject: [PATCH 13/18] fix --- Client/mods/deathmatch/logic/CClientStreamer.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/Client/mods/deathmatch/logic/CClientStreamer.cpp b/Client/mods/deathmatch/logic/CClientStreamer.cpp index bf9a5ec2067..6580a5852b1 100644 --- a/Client/mods/deathmatch/logic/CClientStreamer.cpp +++ b/Client/mods/deathmatch/logic/CClientStreamer.cpp @@ -1045,8 +1045,6 @@ void CClientStreamer::ResetStreamerLimits() m_iMaxOutDefault = STREAMER_MAX_OUT_DEFAULT; m_iMaxInFar = STREAMER_MAX_IN_FAR; m_iMaxOutFar = STREAMER_MAX_OUT_FAR; - m_iMaxSwaps = STREAMER_MAX_SWAPS; - m_iFurthestInLimit = STREAMER_FURTHEST_IN_LIMIT; } void CClientStreamer::GetStreamingLimits(int& normalIn, int& normalOut, int& farIn, int& farOut, int& maxSwaps, int& furthestInLimit) const noexcept From 31dc99dc4a35366513b9920436c4dc63a3757d1c Mon Sep 17 00:00:00 2001 From: Xenius97 Date: Mon, 17 Nov 2025 11:21:50 +0100 Subject: [PATCH 14/18] Use new argument parser for engineStreamingGetLimits --- .../logic/luadefs/CLuaEngineDefs.cpp | 34 +++---------------- .../deathmatch/logic/luadefs/CLuaEngineDefs.h | 2 +- 2 files changed, 5 insertions(+), 31 deletions(-) diff --git a/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.cpp b/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.cpp index 8f41e21c23a..59b426e989e 100644 --- a/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.cpp +++ b/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.cpp @@ -153,7 +153,7 @@ void CLuaEngineDefs::LoadFunctions() {"engineRestreamModel", ArgumentParser}, {"engineRestream", ArgumentParser}, {"engineStreamingSetLimits", ArgumentParser}, - {"engineStreamingGetLimits", CLuaEngineDefs::EngineStreamingGetLimits}, + {"engineStreamingGetLimits", ArgumentParser}, {"engineStreamingResetLimits", ArgumentParser}, {"engineStreamingSetMaxSwaps", ArgumentParser}, {"engineStreamingResetMaxSwaps", ArgumentParser}, @@ -2635,39 +2635,13 @@ void CLuaEngineDefs::EngineStreamingSetLimits(int normalIn, int normalOut, int f g_pClientGame->GetManager()->GetObjectStreamer()->SetStreamerLimits(normalIn, normalOut, farIn, farOut); } -int CLuaEngineDefs::EngineStreamingGetLimits(lua_State* luaVM) +CLuaMultiReturn CLuaEngineDefs::EngineStreamingGetLimits() { auto* pStreamer = g_pClientGame->GetManager()->GetObjectStreamer(); int normalIn, normalOut, farIn, farOut, maxSwaps, furthestInLimit; pStreamer->GetStreamingLimits(normalIn, normalOut, farIn, farOut, maxSwaps, furthestInLimit); - - lua_createtable(luaVM, 0, 6); - - lua_pushstring(luaVM, "normalIn"); - lua_pushnumber(luaVM, normalIn); - lua_settable(luaVM, -3); - - lua_pushstring(luaVM, "normalOut"); - lua_pushnumber(luaVM, normalOut); - lua_settable(luaVM, -3); - - lua_pushstring(luaVM, "farIn"); - lua_pushnumber(luaVM, farIn); - lua_settable(luaVM, -3); - - lua_pushstring(luaVM, "farOut"); - lua_pushnumber(luaVM, farOut); - lua_settable(luaVM, -3); - - lua_pushstring(luaVM, "maxSwaps"); - lua_pushnumber(luaVM, maxSwaps); - lua_settable(luaVM, -3); - - lua_pushstring(luaVM, "furthestInLimit"); - lua_pushnumber(luaVM, furthestInLimit); - lua_settable(luaVM, -3); - - return 1; + + return { normalIn, normalOut, farIn, farOut, maxSwaps, furthestInLimit }; } void CLuaEngineDefs::EngineStreamingResetLimits() diff --git a/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.h b/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.h index 8be1fa3eeed..60a639ef7a8 100644 --- a/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.h +++ b/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.h @@ -100,7 +100,7 @@ class CLuaEngineDefs : public CLuaDefs static void EngineRestream(std::optional option); static void EngineStreamingSetLimits(int normalIn, int normalOut, int farIn, int farOut); - static int EngineStreamingGetLimits(lua_State* luaVM); + static CLuaMultiReturn EngineStreamingGetLimits(); static void EngineStreamingResetLimits(); static void EngineStreamingSetMaxSwaps(int maxSwaps); static void EngineStreamingResetMaxSwaps(); From c7342602fea89dc23d7fc5d743c2b26a8db676c9 Mon Sep 17 00:00:00 2001 From: Xenius97 Date: Mon, 17 Nov 2025 11:34:59 +0100 Subject: [PATCH 15/18] typo --- Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.cpp b/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.cpp index 59b426e989e..a2635046df0 100644 --- a/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.cpp +++ b/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.cpp @@ -2641,7 +2641,7 @@ CLuaMultiReturn CLuaEngineDefs::EngineStreamingGet int normalIn, normalOut, farIn, farOut, maxSwaps, furthestInLimit; pStreamer->GetStreamingLimits(normalIn, normalOut, farIn, farOut, maxSwaps, furthestInLimit); - return { normalIn, normalOut, farIn, farOut, maxSwaps, furthestInLimit }; + return std::tuple(normalIn, normalOut, farIn, farOut, maxSwaps, furthestInLimit); } void CLuaEngineDefs::EngineStreamingResetLimits() From c66e79009211f282629f06761cb5bb17bee5c4f3 Mon Sep 17 00:00:00 2001 From: Xenius97 Date: Mon, 17 Nov 2025 12:06:04 +0100 Subject: [PATCH 16/18] Fix some logic --- Client/mods/deathmatch/logic/CClientStreamer.cpp | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/Client/mods/deathmatch/logic/CClientStreamer.cpp b/Client/mods/deathmatch/logic/CClientStreamer.cpp index 6580a5852b1..8dd958be7e9 100644 --- a/Client/mods/deathmatch/logic/CClientStreamer.cpp +++ b/Client/mods/deathmatch/logic/CClientStreamer.cpp @@ -636,10 +636,6 @@ void CClientStreamer::Restream(bool bMovedFar) std::vector closestOut; std::vector furthestIn; - // Reserve space - closestOut.reserve(iMaxIn); - furthestIn.reserve(STREAMER_FURTHEST_IN_LIMIT); - bool bReachedLimit = ReachedLimit(); int iProcessedIn = 0; @@ -718,7 +714,7 @@ void CClientStreamer::Restream(bool bMovedFar) else { // Still inside distance -> keep track for possible swapping - if (furthestIn.size() < STREAMER_FURTHEST_IN_LIMIT) + if (furthestIn.size() < static_cast(m_iFurthestInLimit)) furthestIn.push_back(pElement); } @@ -802,8 +798,8 @@ void CClientStreamer::Restream(bool bMovedFar) int iFurthestIndex = static_cast(furthestIn.size()) - 1; unsigned int uiClosestIndex = 0; - // Up to STREAMER_MAX_SWAPS swaps per frame - for (int i = 0; i < STREAMER_MAX_SWAPS; i++) + // Up to max swaps per frame + for (int i = 0; i < m_iMaxSwaps; i++) { if (iProcessedIn >= iMaxIn || iProcessedOut >= iMaxOut) break; From 5470ec58deda21803814c46b0540314042763130 Mon Sep 17 00:00:00 2001 From: Xenius97 Date: Mon, 17 Nov 2025 13:08:11 +0100 Subject: [PATCH 17/18] Fix some streaming issues --- Client/mods/deathmatch/logic/CClientStreamer.cpp | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/Client/mods/deathmatch/logic/CClientStreamer.cpp b/Client/mods/deathmatch/logic/CClientStreamer.cpp index 8dd958be7e9..e6edda9fff3 100644 --- a/Client/mods/deathmatch/logic/CClientStreamer.cpp +++ b/Client/mods/deathmatch/logic/CClientStreamer.cpp @@ -271,14 +271,17 @@ void CClientStreamer::SetDimension(unsigned short usDimension) if (!pSector) continue; - // Process directly without making a full copy + std::vector tempElements; + for (auto iter = pSector->Begin(); iter != pSector->End(); ++iter) { CClientStreamElement* pElement = *iter; - if (!pElement) - continue; + if (pElement) + tempElements.push_back(pElement); + } - // Should this element be hidden in the new dimension? + for (CClientStreamElement* pElement : tempElements) + { if (!IsElementShouldVisibleInCurrentDimension(pElement)) elementsToHide.push_back(pElement); } @@ -565,7 +568,7 @@ void CClientStreamer::AddToSortedList(std::list* pList, C continue; // Is it further than the one we add? - if (pTemp->GetDistanceToBoundingBoxSquared(m_vecPosition) > fDistance) + if (pTemp->GetExpDistance() > fDistance) { // Add it before here pList->insert(iter, pElement); @@ -1024,7 +1027,7 @@ void CClientStreamer::OnElementDimension(CClientStreamElement* pElement) void CClientStreamer::SetStreamerLimits(int normalIn, int normalOut, int farIn, int farOut) { if ((normalIn) < (STREAMER_MAX_IN_DEFAULT / 2) || (normalOut) < (STREAMER_MAX_OUT_DEFAULT / 2) || (farIn) < (STREAMER_MAX_IN_FAR / 2) || - (farOut) < (STREAMER_MAX_OUT_FAR / 2)) // Block too low limits to avoid issues + (farOut) < (STREAMER_MAX_OUT_FAR / 2)) { throw std::invalid_argument("Invalid streamer limits"); } From ed3047ef877c894158a34e128529d1f8cd1c1fe1 Mon Sep 17 00:00:00 2001 From: Xenius97 Date: Mon, 17 Nov 2025 14:20:10 +0100 Subject: [PATCH 18/18] add new object streamer functions more tools for developers --- Client/mods/deathmatch/logic/CClientGame.cpp | 5 ++++ .../deathmatch/logic/CClientObjectManager.cpp | 27 ++++++++++++++++- .../deathmatch/logic/CClientObjectManager.h | 4 +++ .../logic/luadefs/CLuaEngineDefs.cpp | 29 +++++++++++++++++++ .../deathmatch/logic/luadefs/CLuaEngineDefs.h | 4 +++ 5 files changed, 68 insertions(+), 1 deletion(-) diff --git a/Client/mods/deathmatch/logic/CClientGame.cpp b/Client/mods/deathmatch/logic/CClientGame.cpp index 2db78153a64..2269fd85a20 100644 --- a/Client/mods/deathmatch/logic/CClientGame.cpp +++ b/Client/mods/deathmatch/logic/CClientGame.cpp @@ -431,6 +431,11 @@ CClientGame::~CClientGame() g_pClientGame->GetManager()->GetObjectStreamer()->ResetStreamerMaxSwaps(); g_pClientGame->GetManager()->GetObjectStreamer()->ResetStreamerFurthestInLimit(); + // Reset streamer limits + CClientObjectManager* pObjectManager = g_pClientGame->GetObjectManager(); + if (pObjectManager) + pObjectManager->ResetMaxObjectStreamCount(); + // Reset camera shaking g_pGame->GetCamera()->SetShakeForce(0.0f); diff --git a/Client/mods/deathmatch/logic/CClientObjectManager.cpp b/Client/mods/deathmatch/logic/CClientObjectManager.cpp index c59483b4db2..4af2a80b1ee 100644 --- a/Client/mods/deathmatch/logic/CClientObjectManager.cpp +++ b/Client/mods/deathmatch/logic/CClientObjectManager.cpp @@ -245,8 +245,12 @@ bool CClientObjectManager::StaticIsLowLodObjectLimitReached() bool CClientObjectManager::IsObjectLimitReached() { - if (IsHardObjectLimitReached() || m_uiStreamedInCount >= m_uiMaxStreamedInCount) + if (IsHardObjectLimitReached() || (m_uiCustomMaxStreamedInCount > 0 && m_uiStreamedInCount >= m_uiCustomMaxStreamedInCount) || + m_uiStreamedInCount >= m_uiMaxStreamedInCount) + { return true; + } + return false; } @@ -333,3 +337,24 @@ bool CClientObjectManager::Exists(CClientObject* pObject) { return ListContains(m_Objects, pObject); } + +void CClientObjectManager::SetMaxObjectStreamCount(int cValue) +{ + if (cValue < m_uiMaxStreamedInCount) + throw std::invalid_argument("Custom limit must be greater than or equal to default limit"); + + m_uiCustomMaxStreamedInCount = cValue; +} + +void CClientObjectManager::ResetMaxObjectStreamCount() +{ + m_uiCustomMaxStreamedInCount = 0; +} + +int CClientObjectManager::GetMaxObjectStreamCount() +{ + if (m_uiCustomMaxStreamedInCount > 0) + return m_uiCustomMaxStreamedInCount; + + return m_uiMaxStreamedInCount; +} diff --git a/Client/mods/deathmatch/logic/CClientObjectManager.h b/Client/mods/deathmatch/logic/CClientObjectManager.h index 7be7aa70ab2..8148117b6c8 100644 --- a/Client/mods/deathmatch/logic/CClientObjectManager.h +++ b/Client/mods/deathmatch/logic/CClientObjectManager.h @@ -42,6 +42,9 @@ class CClientObjectManager bool IsObjectLimitReached(); bool IsLowLodObjectLimitReached(); bool IsHardObjectLimitReached(); + int GetMaxObjectStreamCount(); + void SetMaxObjectStreamCount(int customCount); + void ResetMaxObjectStreamCount(); void RestreamObjects(unsigned short usModel); void RestreamAllObjects(); @@ -58,6 +61,7 @@ class CClientObjectManager int m_iEntryInfoNodeEntries; int m_iPointerNodeDoubleLinkEntries; uint m_uiMaxStreamedInCount; + uint m_uiCustomMaxStreamedInCount; uint m_uiMaxLowLodStreamedInCount; uint m_uiStreamedInCount; uint m_uiLowLodStreamedInCount; diff --git a/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.cpp b/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.cpp index a2635046df0..7e9ed8b0d98 100644 --- a/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.cpp +++ b/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.cpp @@ -159,6 +159,9 @@ void CLuaEngineDefs::LoadFunctions() {"engineStreamingResetMaxSwaps", ArgumentParser}, {"engineStreamingSetFurthestInLimit", ArgumentParser}, {"engineStreamingResetFurthestInLimit", ArgumentParser}, + {"engineSetMaxObjectStreamCount", ArgumentParser}, + {"engineResetMaxObjectStreamCount", ArgumentParser}, + {"engineGetMaxObjectStreamCount", ArgumentParser}, // CLuaCFunctions::AddFunction ( "engineReplaceMatchingAtomics", EngineReplaceMatchingAtomics ); @@ -230,6 +233,9 @@ void CLuaEngineDefs::AddClass(lua_State* luaVM) lua_classfunction(luaVM, "resetMaxSwaps", "engineStreamingResetMaxSwaps"); lua_classfunction(luaVM, "setFurthestInLimit", "engineStreamingSetFurthestInLimit"); lua_classfunction(luaVM, "resetFurthestInLimit", "engineStreamingResetFurthestInLimit"); + lua_classfunction(luaVM, "setMaxObjectStreamCount", "engineSetMaxObjectStreamCount"); + lua_classfunction(luaVM, "getMaxObjectStreamCount", "engineGetMaxObjectStreamCount"); + lua_classfunction(luaVM, "resetMaxObjectStreamCount", "engineResetMaxObjectStreamCount"); lua_classvariable(luaVM, "memorySize", "engineStreamingSetMemorySize", "engineStreamingGetMemorySize"); lua_classvariable(luaVM, "bufferSize", "engineStreamingSetBufferSize", "engineStreamingGetBufferSize"); @@ -2668,3 +2674,26 @@ void CLuaEngineDefs::EngineStreamingResetFurthestInLimit() { g_pClientGame->GetManager()->GetObjectStreamer()->ResetStreamerFurthestInLimit(); } + +void CLuaEngineDefs::EngineSetMaxObjectStreamCount(int limit) +{ + CClientObjectManager* pObjectManager = g_pClientGame->GetObjectManager(); + if (pObjectManager) + pObjectManager->SetMaxObjectStreamCount(limit); +} + +void CLuaEngineDefs::EngineResetMaxObjectStreamCount() +{ + CClientObjectManager* pObjectManager = g_pClientGame->GetObjectManager(); + if (pObjectManager) + pObjectManager->ResetMaxObjectStreamCount(); +} + +int CLuaEngineDefs::EngineGetMaxObjectStreamCount() +{ + CClientObjectManager* pObjectManager = g_pClientGame->GetObjectManager(); + if (pObjectManager) + return pObjectManager->GetMaxObjectStreamCount(); + + return 0; +} diff --git a/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.h b/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.h index 60a639ef7a8..f904b337c4f 100644 --- a/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.h +++ b/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.h @@ -107,6 +107,10 @@ class CLuaEngineDefs : public CLuaDefs static void EngineStreamingSetFurthestInLimit(int limit); static void EngineStreamingResetFurthestInLimit(); + static void EngineSetMaxObjectStreamCount(int limit); + static void EngineResetMaxObjectStreamCount(); + static int EngineGetMaxObjectStreamCount(); + private: static void AddEngineColClass(lua_State* luaVM); static void AddEngineTxdClass(lua_State* luaVM);