java-topology/defects/0ad-0002/patch/0ad-0002.patch
russell@unturf.com 223ccb3fee 0ad: 4 defects, 5-MOAD scan across pathfinding/visibility/templates/lobby
0ad-0001 CCmpObstructionManager dirty shapes vector+std::find O(N*D) HIGH 4.1x
0ad-0002 CCmpRangeManager m_ModifiedEntities vector+std::find O(E*M) HIGH 25.8x
0ad-0003 CCmpTemplateManager FindUsedTemplates vector+std::find O(T^2) MEDIUM 5.9x
0ad-0004 XmppClient+NetServer lobby auth token logged verbatim CWE-312 MEDIUM

MOAD-0002 (Intertangle): g_ globals are deliberate single-thread game arch, CLEAN
MOAD-0003 (Leaked Context): thread_local properly scoped, CLEAN
MOAD-0005 (Thundering Herd): single-threaded sim, no cache stampede, CLEAN

4/4 unit tests PASS, UNDF 956-959
2026-03-31 11:58:37 -04:00

47 lines
2 KiB
Diff

# UNDF: UNDF-2026-000000957
--- a/source/simulation2/components/CCmpRangeManager.cpp
+++ b/source/simulation2/components/CCmpRangeManager.cpp
@@ -1,6 +1,8 @@
// CWE-407: CCmpRangeManager m_ModifiedEntities uses std::vector with std::find
// for dedup. RequestVisibilityUpdate is called per-entity, and GetLosVisibility
// scans the vector per-entity per-player. With E entities and M modified,
// this is O(E*M) per frame. Fix: use std::unordered_set for O(1) membership test.
+#include <unordered_set>
// --- Declaration change ---
@@ -409,7 +411,7 @@
- std::vector<entity_id_t> m_ModifiedEntities;
+ std::unordered_set<entity_id_t> m_ModifiedEntities;
// --- Serialization: convert set to vector for serialization ---
@@ -488,7 +490,10 @@
- Serializer(serialize, "modified entities", m_ModifiedEntities);
+ // Serialize as vector for backward compatibility
+ std::vector<entity_id_t> modifiedVec(m_ModifiedEntities.begin(), m_ModifiedEntities.end());
+ Serializer(serialize, "modified entities", modifiedVec);
+ // On deserialize, rebuild set from vector
+ if (!serialize)
+ m_ModifiedEntities = std::unordered_set<entity_id_t>(modifiedVec.begin(), modifiedVec.end());
// --- GetLosVisibility: O(1) lookup instead of O(M) ---
@@ -1777,7 +1782,7 @@
- if (std::find(m_ModifiedEntities.begin(), m_ModifiedEntities.end(), entId) != m_ModifiedEntities.end())
+ if (m_ModifiedEntities.count(entId))
return ComputeLosVisibility(ent, player);
// --- UpdateVisibilityData: iterate set, pop via iterator ---
@@ -1877,8 +1882,11 @@
while (!m_ModifiedEntities.empty())
{
- entity_id_t ent = m_ModifiedEntities.back();
- m_ModifiedEntities.pop_back();
+ auto it = m_ModifiedEntities.begin();
+ entity_id_t ent = *it;
+ m_ModifiedEntities.erase(it);
// --- RequestVisibilityUpdate: O(1) insert instead of O(M) find ---
@@ -1891,8 +1899,7 @@
- if (std::find(m_ModifiedEntities.begin(), m_ModifiedEntities.end(), ent) == m_ModifiedEntities.end())
- m_ModifiedEntities.push_back(ent);
+ m_ModifiedEntities.insert(ent);
}