Defect: projectile.cpp line 872, std::find on std::vector<BASE_OBJECT*> psDamaged inside grid neighbor iteration loop. Every projectile tick, for each nearby object, does O(D) linear scan to check if already damaged. Penetrating weapons inherit and grow psDamaged across hits. Fix: replace std::vector with std::unordered_set for O(1) lookup. push_back becomes insert, std::find becomes count, remove_if becomes iterator-based erase loop. Severity: MEDIUM. Hot path (per projectile per tick), scales with battle density. D=200 damaged, G=100 grid neighbors: 9.5x speedup. MOAD 0002-0005 CLEAN: - 0002: global state is architectural (Eidos-era C game), not coupling defect - 0003: no thread_local usage found - 0004: no secrets logged (public keys and IPs only, standard for server logs) - 0005: no unsynchronized cache patterns (game logic is single-threaded)
85 lines
3.1 KiB
Diff
85 lines
3.1 KiB
Diff
--- a/src/projectiledef.h
|
|
+++ b/src/projectiledef.h
|
|
@@ -22,7 +22,7 @@
|
|
#ifndef __INCLUDED_PROJECTILEDEF_H__
|
|
#define __INCLUDED_PROJECTILEDEF_H__
|
|
|
|
-#include "basedef.h"
|
|
+#include "basedef.h" // SIMPLE_OBJECT, BASE_OBJECT, OBJ_PROJECTILE
|
|
#include "lib/gamelib/gtime.h"
|
|
|
|
-#include <vector>
|
|
+#include <unordered_set>
|
|
|
|
|
|
enum PROJ_STATE
|
|
@@ -49,7 +49,7 @@
|
|
WEAPON_STATS *psWStats; ///< firing weapon stats
|
|
BASE_OBJECT *psSource; ///< what fired the projectile
|
|
BASE_OBJECT *psDest; ///< target of this projectile
|
|
- std::vector<BASE_OBJECT *> psDamaged; ///< the targets that have already been dealt damage to (don't damage the same target twice)
|
|
+ std::unordered_set<BASE_OBJECT *> psDamaged; ///< the targets that have already been dealt damage to (O(1) lookup instead of O(N) linear scan)
|
|
|
|
Vector3i src = Vector3i(0, 0, 0); ///< Where projectile started
|
|
Vector3i dst = Vector3i(0, 0, 0); ///< The target coordinates
|
|
--- a/src/projectile.cpp
|
|
+++ b/src/projectile.cpp
|
|
@@ -376,7 +376,7 @@
|
|
psProj->rot.direction, psProj->rot.pitch, psProj->rot.roll,
|
|
psProj->state,
|
|
(int)psProj->expectedDamageCaused,
|
|
- (int)psProj->psDamaged.size(),
|
|
+ (int)psProj->psDamaged.size(), // unchanged: .size() works on unordered_set
|
|
};
|
|
_syncDebugIntList(function, "%c projectile = p%d;pos(%d,%d,%d),rot(%d,%d,%d),state%d,expectedDamageCaused%d,numberDamaged%u", list, ARRAY_SIZE(list));
|
|
}
|
|
@@ -869,7 +869,7 @@
|
|
BASE_OBJECT *psTempObj = *gi;
|
|
CHECK_OBJECT(psTempObj);
|
|
|
|
- if (std::find(psProj->psDamaged.begin(), psProj->psDamaged.end(), psTempObj) != psProj->psDamaged.end())
|
|
+ if (psProj->psDamaged.count(psTempObj) != 0)
|
|
{
|
|
// Dont damage one target twice
|
|
continue;
|
|
@@ -950,7 +950,7 @@
|
|
asWeap.nStat = psStats - asWeaponStats.data();
|
|
|
|
// Assume we damaged the chosen target
|
|
- psProj->psDamaged.push_back(closestCollisionObject);
|
|
+ psProj->psDamaged.insert(closestCollisionObject);
|
|
|
|
spawnedProjectile = proj_SendProjectileInternal(&asWeap, psProj, psProj->player, psProj->dst, nullptr, true, -1);
|
|
}
|
|
@@ -1290,7 +1290,7 @@
|
|
|
|
if (relativeDamage >= 0) // So long as the target wasn't killed
|
|
{
|
|
- psObj->psDamaged.push_back(psObj->psDest);
|
|
+ psObj->psDamaged.insert(psObj->psDest);
|
|
}
|
|
}
|
|
}
|
|
@@ -1396,7 +1396,10 @@
|
|
setProjectileDestination(psObj, nullptr);
|
|
}
|
|
// Remove dead objects from psDamaged.
|
|
- psDamaged.erase(std::remove_if(psDamaged.begin(), psDamaged.end(), [](const BASE_OBJECT *psObj) { return ::isDead(psObj); }), psDamaged.end());
|
|
+ for (auto it = psDamaged.begin(); it != psDamaged.end(); )
|
|
+ {
|
|
+ it = ::isDead(*it) ? psDamaged.erase(it) : std::next(it);
|
|
+ }
|
|
|
|
// This extra check fixes a crash in cam2, mission1
|
|
if (worldOnMap(psObj->pos.x, psObj->pos.y) == false)
|
|
@@ -1954,9 +1957,9 @@
|
|
checkObject(psProjectile->psSource, location_description, function, recurse - 1);
|
|
}
|
|
|
|
- for (unsigned n = 0; n != psProjectile->psDamaged.size(); ++n)
|
|
+ for (const auto *psObj : psProjectile->psDamaged)
|
|
{
|
|
- checkObject(psProjectile->psDamaged[n], location_description, function, recurse - 1);
|
|
+ checkObject(psObj, location_description, function, recurse - 1);
|
|
}
|
|
}
|