From 4aade202e006738a0baf4c3190cff8ce6b91003c Mon Sep 17 00:00:00 2001 From: LogicParrot Date: Mon, 1 Feb 2016 22:49:34 +0200 Subject: cMonster::m_Target safety across worlds --- src/Entities/Entity.cpp | 7 ++++ src/Entities/Pawn.cpp | 88 +++++++++++++++++++++++++++++++++++++++++++------ src/Entities/Pawn.h | 35 ++++++++++++++++---- src/Entities/Player.cpp | 8 ++++- 4 files changed, 120 insertions(+), 18 deletions(-) (limited to 'src/Entities') diff --git a/src/Entities/Entity.cpp b/src/Entities/Entity.cpp index 0706a1676..b207e79c9 100644 --- a/src/Entities/Entity.cpp +++ b/src/Entities/Entity.cpp @@ -1514,6 +1514,13 @@ bool cEntity::DoMoveToWorld(cWorld * a_World, bool a_ShouldSendRespawn, Vector3d SetPosition(a_NewPosition); + if (this->IsMob()) + { + cMonster * Monster = static_cast(this); + Monster->SetTarget(nullptr); + Monster->StopEveryoneFromTargetingMe(); + } + // Queue add to new world a_World->AddEntity(this); cWorld * OldWorld = cRoot::Get()->GetWorld(GetWorld()->GetName()); // Required for the hook HOOK_ENTITY_CHANGED_WORLD diff --git a/src/Entities/Pawn.cpp b/src/Entities/Pawn.cpp index 2d86dfecf..c8780c326 100644 --- a/src/Entities/Pawn.cpp +++ b/src/Entities/Pawn.cpp @@ -8,7 +8,7 @@ #include "BoundingBox.h" #include "../Blocks/BlockHandler.h" #include "EffectID.h" - +#include "../Mobs/Monster.h" @@ -27,6 +27,25 @@ cPawn::cPawn(eEntityType a_EntityType, double a_Width, double a_Height) : +cPawn::~cPawn() +{ + ASSERT(m_TargetingMe.size() == 0); +} + + + + + +void cPawn::Destroyed() +{ + StopEveryoneFromTargetingMe(); + super::Destroyed(); +} + + + + + void cPawn::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { // Iterate through this entity's applied effects @@ -35,18 +54,18 @@ void cPawn::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) // Copies values to prevent pesky wrong accesses and erasures cEntityEffect::eType EffectType = iter->first; cEntityEffect * Effect = iter->second; - + Effect->OnTick(*this); - + // Iterates (must be called before any possible erasure) ++iter; - + // Remove effect if duration has elapsed if (Effect->GetDuration() - Effect->GetTicks() <= 0) { RemoveEntityEffect(EffectType); } - + // TODO: Check for discrepancies between client and server effect values } @@ -126,7 +145,7 @@ void cPawn::HandleAir(void) // Prevent the oxygen from decreasing return; } - + super::HandleAir(); } @@ -142,14 +161,14 @@ void cPawn::AddEntityEffect(cEntityEffect::eType a_EffectType, int a_Duration, s // A plugin disallows the addition, bail out. return; } - + // No need to add empty effects: if (a_EffectType == cEntityEffect::effNoEffect) { return; } a_Duration = static_cast(a_Duration * a_DistanceModifier); - + m_EntityEffects[a_EffectType] = cEntityEffect::CreateEntityEffect(a_EffectType, a_Duration, a_Intensity, a_DistanceModifier); m_World->BroadcastEntityEffect(*this, a_EffectType, a_Intensity, static_cast(a_Duration)); m_EntityEffects[a_EffectType]->OnActivate(*this); @@ -187,10 +206,10 @@ void cPawn::ClearEntityEffects() { // Copy values to prevent pesky wrong erasures cEntityEffect::eType EffectType = iter->first; - + // Iterates (must be called before any possible erasure) ++iter; - + // Remove effect RemoveEntityEffect(EffectType); } @@ -200,6 +219,38 @@ void cPawn::ClearEntityEffects() +void cPawn::NoLongerTargetingMe(cMonster * a_Monster) +{ + ASSERT(!IsDestroyed()); // Our destroy override is supposed to clear all targets before we're destroyed. + for (auto i = m_TargetingMe.begin(); i != m_TargetingMe.end(); ++i) + { + cMonster * Monster = *i; + if (Monster == a_Monster) + { + ASSERT(Monster->GetTarget() != this); // The monster is notifying us it is no longer targeting us, assert if that's a lie + m_TargetingMe.erase(i); + return; + } + } + ASSERT(false); // If this happens, something is wrong. Perhaps the monster never called TargetingMe() or called NoLongerTargetingMe() twice. +} + + + + + +void cPawn::TargetingMe(cMonster * a_Monster) +{ + ASSERT(!IsDestroyed()); + ASSERT(m_TargetingMe.size() < 10000); + ASSERT(a_Monster->GetTarget() == this); + m_TargetingMe.push_back(a_Monster); +} + + + + + void cPawn::HandleFalling(void) { /* Not pretty looking, and is more suited to wherever server-sided collision detection is implemented. @@ -369,3 +420,20 @@ void cPawn::HandleFalling(void) m_LastGroundHeight = GetPosY(); } } + + + + + +void cPawn::StopEveryoneFromTargetingMe() +{ + std::vector::iterator i = m_TargetingMe.begin(); + while (i != m_TargetingMe.end()) + { + cMonster * Monster = *i; + ASSERT(Monster->GetTarget() == this); + Monster->UnsafeUnsetTarget(); + i = m_TargetingMe.erase(i); + } + ASSERT(m_TargetingMe.size() == 0); +} diff --git a/src/Entities/Pawn.h b/src/Entities/Pawn.h index 0ceb1073e..05bc09e88 100644 --- a/src/Entities/Pawn.h +++ b/src/Entities/Pawn.h @@ -4,6 +4,9 @@ #include "Entity.h" #include "EntityEffect.h" +// fwd cMonster +class cMonster; + @@ -14,21 +17,28 @@ class cPawn : { // tolua_end typedef cEntity super; - + public: CLASS_PROTODEF(cPawn) cPawn(eEntityType a_EntityType, double a_Width, double a_Height); - + ~cPawn(); + virtual void Destroyed() override; + virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; virtual void KilledBy(TakeDamageInfo & a_TDI) override; - + virtual bool IsFireproof(void) const override; virtual void HandleAir(void) override; virtual void HandleFalling(void); + /** Tells all pawns which are targeting us to stop targeting us. */ + void StopEveryoneFromTargetingMe(); + + + // tolua_begin - + /** Applies an entity effect Checks with plugins if they allow the addition. @param a_EffectType The entity effect to apply @@ -37,28 +47,39 @@ public: @param a_DistanceModifier The scalar multiplied to the potion duration, only applies to splash potions) */ void AddEntityEffect(cEntityEffect::eType a_EffectType, int a_EffectDurationTicks, short a_EffectIntensity, double a_DistanceModifier = 1); - + /** Removes a currently applied entity effect @param a_EffectType The entity effect to remove */ void RemoveEntityEffect(cEntityEffect::eType a_EffectType); - + /** Returns true, if the entity effect is currently applied @param a_EffectType The entity effect to check */ bool HasEntityEffect(cEntityEffect::eType a_EffectType) const; - + /** Removes all currently applied entity effects (used when drinking milk) */ void ClearEntityEffects(void); // tolua_end + /** remove the monster from the list of monsters targeting this pawn. */ + void NoLongerTargetingMe(cMonster * a_Monster); + + /** Add the monster to the list of monsters targeting this pawn. (Does not check if already in list!) */ + void TargetingMe(cMonster * a_Monster); + protected: typedef std::map tEffectMap; tEffectMap m_EntityEffects; double m_LastGroundHeight; bool m_bTouchGround; + +private: + + /** A list of all monsters that are targeting this pawn. */ + std::vector m_TargetingMe; } ; // tolua_export diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp index 7ba6b2bf6..5606e9668 100644 --- a/src/Entities/Player.cpp +++ b/src/Entities/Player.cpp @@ -176,6 +176,7 @@ cPlayer::~cPlayer(void) void cPlayer::Destroyed() { CloseWindow(false); + super::Destroyed(); } @@ -1681,7 +1682,6 @@ void cPlayer::FreezeInternal(const Vector3d & a_Location, bool a_ManuallyFrozen) bool cPlayer::DoMoveToWorld(cWorld * a_World, bool a_ShouldSendRespawn, Vector3d a_NewPosition) { ASSERT(a_World != nullptr); - if (GetWorld() == a_World) { // Don't move to same world @@ -1708,6 +1708,9 @@ bool cPlayer::DoMoveToWorld(cWorld * a_World, bool a_ShouldSendRespawn, Vector3d // Remove player from world GetWorld()->RemovePlayer(this, false); + // Stop all mobs from targeting this player + + StopEveryoneFromTargetingMe(); // Send the respawn packet: if (a_ShouldSendRespawn && (m_ClientHandle != nullptr)) @@ -1720,6 +1723,9 @@ bool cPlayer::DoMoveToWorld(cWorld * a_World, bool a_ShouldSendRespawn, Vector3d SetPosition(a_NewPosition); + // Stop all mobs from targeting this player + StopEveryoneFromTargetingMe(); + // Queue adding player to the new world, including all the necessary adjustments to the object a_World->AddPlayer(this); cWorld * OldWorld = cRoot::Get()->GetWorld(GetWorld()->GetName()); // Required for the hook HOOK_ENTITY_CHANGED_WORLD -- cgit v1.2.3