Prevent iterator invalidation during actor traversal

This commit is contained in:
Evil Eye 2024-11-06 19:46:45 +01:00
parent 22232e8710
commit c098f2ccde
4 changed files with 53 additions and 14 deletions

View File

@ -80,6 +80,7 @@
Bug #4754: Stack of ammunition cannot be equipped partially Bug #4754: Stack of ammunition cannot be equipped partially
Bug #4816: GetWeaponDrawn returns 1 before weapon is attached Bug #4816: GetWeaponDrawn returns 1 before weapon is attached
Bug #4822: Non-weapon equipment and body parts can't inherit time from parent animation Bug #4822: Non-weapon equipment and body parts can't inherit time from parent animation
Bug #4885: Disable in dialogue result script causes a crash
Bug #4898: Odd/Incorrect lighting on meshes Bug #4898: Odd/Incorrect lighting on meshes
Bug #5057: Weapon swing sound plays at same pitch whether it hits or misses Bug #5057: Weapon swing sound plays at same pitch whether it hits or misses
Bug #5062: Root bone rotations for NPC animation don't work the same as for creature animation Bug #5062: Root bone rotations for NPC animation don't work the same as for creature animation

View File

@ -62,14 +62,18 @@ namespace MWMechanics
void setPositionAdjusted(bool adjusted) { mPositionAdjusted = adjusted; } void setPositionAdjusted(bool adjusted) { mPositionAdjusted = adjusted; }
bool getPositionAdjusted() const { return mPositionAdjusted; } bool getPositionAdjusted() const { return mPositionAdjusted; }
void invalidate() { mInvalid = true; }
bool isInvalid() const { return mInvalid; }
private: private:
CharacterController mCharacterController; CharacterController mCharacterController;
int mGreetingTimer{ 0 }; int mGreetingTimer{ 0 };
float mTargetAngleRadians{ 0.f }; float mTargetAngleRadians{ 0.f };
GreetingState mGreetingState{ Greet_None }; GreetingState mGreetingState{ Greet_None };
bool mIsTurningToPlayer{ false };
Misc::DeviatingPeriodicTimer mEngageCombat{ 1.0f, 0.25f, Misc::DeviatingPeriodicTimer mEngageCombat{ 1.0f, 0.25f,
Misc::Rng::deviate(0, 0.25f, MWBase::Environment::get().getWorld()->getPrng()) }; Misc::Rng::deviate(0, 0.25f, MWBase::Environment::get().getWorld()->getPrng()) };
bool mIsTurningToPlayer{ false };
bool mInvalid{ false };
bool mPositionAdjusted; bool mPositionAdjusted;
}; };

View File

@ -1247,7 +1247,7 @@ namespace MWMechanics
{ {
if (!keepActive) if (!keepActive)
removeTemporaryEffects(iter->second->getPtr()); removeTemporaryEffects(iter->second->getPtr());
mActors.erase(iter->second); iter->second->invalidate();
mIndex.erase(iter); mIndex.erase(iter);
} }
} }
@ -1299,16 +1299,15 @@ namespace MWMechanics
void Actors::dropActors(const MWWorld::CellStore* cellStore, const MWWorld::Ptr& ignore) void Actors::dropActors(const MWWorld::CellStore* cellStore, const MWWorld::Ptr& ignore)
{ {
for (auto iter = mActors.begin(); iter != mActors.end();) for (Actor& actor : mActors)
{ {
if ((iter->getPtr().isInCell() && iter->getPtr().getCell() == cellStore) && iter->getPtr() != ignore) if (!actor.isInvalid() && actor.getPtr().isInCell() && actor.getPtr().getCell() == cellStore
&& actor.getPtr() != ignore)
{ {
removeTemporaryEffects(iter->getPtr()); removeTemporaryEffects(actor.getPtr());
mIndex.erase(iter->getPtr().mRef); mIndex.erase(actor.getPtr().mRef);
iter = mActors.erase(iter); actor.invalidate();
} }
else
++iter;
} }
} }
@ -1327,6 +1326,8 @@ namespace MWMechanics
const MWBase::World* const world = MWBase::Environment::get().getWorld(); const MWBase::World* const world = MWBase::Environment::get().getWorld();
for (const Actor& actor : mActors) for (const Actor& actor : mActors)
{ {
if (actor.isInvalid())
continue;
const MWWorld::Ptr& ptr = actor.getPtr(); const MWWorld::Ptr& ptr = actor.getPtr();
if (ptr == player) if (ptr == player)
continue; // Don't interfere with player controls. continue; // Don't interfere with player controls.
@ -1391,6 +1392,8 @@ namespace MWMechanics
// Iterate through all other actors and predict collisions. // Iterate through all other actors and predict collisions.
for (const Actor& otherActor : mActors) for (const Actor& otherActor : mActors)
{ {
if (otherActor.isInvalid())
continue;
const MWWorld::Ptr& otherPtr = otherActor.getPtr(); const MWWorld::Ptr& otherPtr = otherActor.getPtr();
if (otherPtr == ptr || otherPtr == currentTarget) if (otherPtr == ptr || otherPtr == currentTarget)
continue; continue;
@ -1509,6 +1512,8 @@ namespace MWMechanics
// AI and magic effects update // AI and magic effects update
for (Actor& actor : mActors) for (Actor& actor : mActors)
{ {
if (actor.isInvalid())
continue;
const bool isPlayer = actor.getPtr() == player; const bool isPlayer = actor.getPtr() == player;
CharacterController& ctrl = actor.getCharacterController(); CharacterController& ctrl = actor.getCharacterController();
MWBase::LuaManager::ActorControls* luaControls MWBase::LuaManager::ActorControls* luaControls
@ -1570,6 +1575,8 @@ namespace MWMechanics
for (const Actor& otherActor : mActors) for (const Actor& otherActor : mActors)
{ {
if (otherActor.isInvalid())
continue;
if (otherActor.getPtr() == actor.getPtr() || isPlayer) // player is not AI-controlled if (otherActor.getPtr() == actor.getPtr() || isPlayer) // player is not AI-controlled
continue; continue;
engageCombat( engageCombat(
@ -1627,6 +1634,8 @@ namespace MWMechanics
CharacterController* playerCharacter = nullptr; CharacterController* playerCharacter = nullptr;
for (Actor& actor : mActors) for (Actor& actor : mActors)
{ {
if (actor.isInvalid())
continue;
const float dist = (playerPos - actor.getPtr().getRefData().getPosition().asVec3()).length(); const float dist = (playerPos - actor.getPtr().getRefData().getPosition().asVec3()).length();
const bool isPlayer = actor.getPtr() == player; const bool isPlayer = actor.getPtr() == player;
CreatureStats& stats = actor.getPtr().getClass().getCreatureStats(actor.getPtr()); CreatureStats& stats = actor.getPtr().getClass().getCreatureStats(actor.getPtr());
@ -1692,8 +1701,15 @@ namespace MWMechanics
luaControls->mJump = false; luaControls->mJump = false;
} }
for (const Actor& actor : mActors) for (auto it = mActors.begin(); it != mActors.end();)
{ {
if (it->isInvalid())
{
it = mActors.erase(it);
continue;
}
const Actor& actor = *it;
it++;
const MWWorld::Class& cls = actor.getPtr().getClass(); const MWWorld::Class& cls = actor.getPtr().getClass();
CreatureStats& stats = cls.getCreatureStats(actor.getPtr()); CreatureStats& stats = cls.getCreatureStats(actor.getPtr());
@ -1743,6 +1759,8 @@ namespace MWMechanics
{ {
for (Actor& actor : mActors) for (Actor& actor : mActors)
{ {
if (actor.isInvalid())
continue;
const MWWorld::Class& cls = actor.getPtr().getClass(); const MWWorld::Class& cls = actor.getPtr().getClass();
CreatureStats& stats = cls.getCreatureStats(actor.getPtr()); CreatureStats& stats = cls.getCreatureStats(actor.getPtr());
@ -1830,6 +1848,8 @@ namespace MWMechanics
{ {
for (const Actor& actor : mActors) for (const Actor& actor : mActors)
{ {
if (actor.isInvalid())
continue;
MWMechanics::ActiveSpells& spells MWMechanics::ActiveSpells& spells
= actor.getPtr().getClass().getCreatureStats(actor.getPtr()).getActiveSpells(); = actor.getPtr().getClass().getCreatureStats(actor.getPtr()).getActiveSpells();
spells.purge(actor.getPtr(), casterActorId); spells.purge(actor.getPtr(), casterActorId);
@ -1849,6 +1869,8 @@ namespace MWMechanics
for (const Actor& actor : mActors) for (const Actor& actor : mActors)
{ {
if (actor.isInvalid())
continue;
if (actor.getPtr().getClass().getCreatureStats(actor.getPtr()).isDead()) if (actor.getPtr().getClass().getCreatureStats(actor.getPtr()).isDead())
{ {
adjustMagicEffects(actor.getPtr(), duration); adjustMagicEffects(actor.getPtr(), duration);
@ -2046,7 +2068,10 @@ namespace MWMechanics
void Actors::persistAnimationStates() const void Actors::persistAnimationStates() const
{ {
for (const Actor& actor : mActors) for (const Actor& actor : mActors)
actor.getCharacterController().persistAnimationState(); {
if (!actor.isInvalid())
actor.getCharacterController().persistAnimationState();
}
} }
void Actors::clearAnimationQueue(const MWWorld::Ptr& ptr, bool clearScripted) void Actors::clearAnimationQueue(const MWWorld::Ptr& ptr, bool clearScripted)
@ -2060,6 +2085,8 @@ namespace MWMechanics
{ {
for (const Actor& actor : mActors) for (const Actor& actor : mActors)
{ {
if (actor.isInvalid())
continue;
if ((actor.getPtr().getRefData().getPosition().asVec3() - position).length2() <= radius * radius) if ((actor.getPtr().getRefData().getPosition().asVec3() - position).length2() <= radius * radius)
out.push_back(actor.getPtr()); out.push_back(actor.getPtr());
} }
@ -2069,6 +2096,8 @@ namespace MWMechanics
{ {
for (const Actor& actor : mActors) for (const Actor& actor : mActors)
{ {
if (actor.isInvalid())
continue;
if ((actor.getPtr().getRefData().getPosition().asVec3() - position).length2() <= radius * radius) if ((actor.getPtr().getRefData().getPosition().asVec3() - position).length2() <= radius * radius)
return true; return true;
} }
@ -2082,6 +2111,8 @@ namespace MWMechanics
list.push_back(actorPtr); list.push_back(actorPtr);
for (const Actor& actor : mActors) for (const Actor& actor : mActors)
{ {
if (actor.isInvalid())
continue;
const MWWorld::Ptr& iteratedActor = actor.getPtr(); const MWWorld::Ptr& iteratedActor = actor.getPtr();
if (iteratedActor == getPlayer()) if (iteratedActor == getPlayer())
continue; continue;
@ -2352,10 +2383,11 @@ namespace MWMechanics
if (!MWBase::Environment::get().getMechanicsManager()->isAIActive()) if (!MWBase::Environment::get().getMechanicsManager()->isAIActive())
return; return;
for (auto it = mActors.begin(); it != mActors.end();) for (const Actor& actor : mActors)
{ {
const MWWorld::Ptr ptr = it->getPtr(); if (actor.isInvalid())
++it; continue;
const MWWorld::Ptr ptr = actor.getPtr();
if (ptr == getPlayer() || !isConscious(ptr) || ptr.getClass().getCreatureStats(ptr).isParalyzed()) if (ptr == getPlayer() || !isConscious(ptr) || ptr.getClass().getCreatureStats(ptr).isParalyzed())
continue; continue;
MWMechanics::AiSequence& seq = ptr.getClass().getCreatureStats(ptr).getAiSequence(); MWMechanics::AiSequence& seq = ptr.getClass().getCreatureStats(ptr).getAiSequence();

View File

@ -1725,6 +1725,8 @@ namespace MWMechanics
.getActorId()); // Stops guard from ending combat if player is unreachable .getActorId()); // Stops guard from ending combat if player is unreachable
for (const Actor& actor : mActors) for (const Actor& actor : mActors)
{ {
if (actor.isInvalid())
continue;
if (actor.getPtr().getClass().isClass(actor.getPtr(), "Guard")) if (actor.getPtr().getClass().isClass(actor.getPtr(), "Guard"))
{ {
MWMechanics::AiSequence& aiSeq MWMechanics::AiSequence& aiSeq