diff options
-rw-r--r-- | src/Chunk.cpp | 39 | ||||
-rw-r--r-- | src/Chunk.h | 5 | ||||
-rw-r--r-- | src/Entities/Entity.cpp | 20 | ||||
-rw-r--r-- | src/Entities/Pawn.cpp | 88 | ||||
-rw-r--r-- | src/Entities/Pawn.h | 35 | ||||
-rw-r--r-- | src/Entities/Player.cpp | 26 | ||||
-rw-r--r-- | src/Mobs/AggressiveMonster.cpp | 12 | ||||
-rw-r--r-- | src/Mobs/Blaze.cpp | 2 | ||||
-rw-r--r-- | src/Mobs/CaveSpider.cpp | 6 | ||||
-rw-r--r-- | src/Mobs/Creeper.cpp | 2 | ||||
-rw-r--r-- | src/Mobs/Enderman.cpp | 2 | ||||
-rw-r--r-- | src/Mobs/Ghast.cpp | 2 | ||||
-rw-r--r-- | src/Mobs/Monster.cpp | 121 | ||||
-rw-r--r-- | src/Mobs/Monster.h | 21 | ||||
-rw-r--r-- | src/Mobs/PassiveAggressiveMonster.cpp | 4 | ||||
-rw-r--r-- | src/Mobs/Skeleton.cpp | 4 | ||||
-rw-r--r-- | src/Mobs/Wolf.cpp | 33 | ||||
-rw-r--r-- | src/Root.cpp | 2 | ||||
-rw-r--r-- | src/World.cpp | 20 |
19 files changed, 352 insertions, 92 deletions
diff --git a/src/Chunk.cpp b/src/Chunk.cpp index da13e3b10..f5d447c45 100644 --- a/src/Chunk.cpp +++ b/src/Chunk.cpp @@ -603,6 +603,7 @@ void cChunk::SpawnMobs(cMobSpawner & a_MobSpawner) void cChunk::Tick(std::chrono::milliseconds a_Dt) { + m_IsInTick = true; BroadcastPendingBlockChanges(); CheckBlocks(); @@ -637,7 +638,7 @@ void cChunk::Tick(std::chrono::milliseconds a_Dt) else if ((*itr)->IsWorldTravellingFrom(m_World)) { // Remove all entities that are travelling to another world - LOGD("Removing entity from [%d, %d] that's travelling between worlds.", m_PosX, m_PosZ); + LOGD("Removing entity from [%d, %d] that's travelling between worlds. (Scheduled)", m_PosX, m_PosZ); MarkDirty(); (*itr)->SetWorldTravellingFrom(nullptr); itr = m_Entities.erase(itr); @@ -659,6 +660,7 @@ void cChunk::Tick(std::chrono::milliseconds a_Dt) } // for itr - m_Entitites[] ApplyWeatherToTop(); + m_IsInTick = false; } @@ -1910,6 +1912,31 @@ void cChunk::RemoveEntity(cEntity * a_Entity) +void cChunk::SafeRemoveEntity(cEntity * a_Entity) +{ + if (!m_IsInTick) + { + LOGD("Removing entity from [%d, %d] that's travelling between worlds. (immediate)", m_PosX, m_PosZ); + // If we're not in a tick, just remove it. + m_Entities.remove(a_Entity); + } + else + { + // If we are in a tick, we don't want to invalidate the iterator, so we schedule the removal. Removal will be done in cChunk::tick() + a_Entity->SetWorldTravellingFrom(GetWorld()); + } + + // Mark as dirty if it was a server-generated entity: + if (!a_Entity->IsPlayer()) + { + MarkDirty(); + } +} + + + + + bool cChunk::HasEntity(UInt32 a_EntityID) { for (cEntityList::const_iterator itr = m_Entities.begin(), end = m_Entities.end(); itr != end; ++itr) @@ -1932,6 +1959,10 @@ bool cChunk::ForEachEntity(cEntityCallback & a_Callback) for (cEntityList::iterator itr = m_Entities.begin(), itr2 = itr; itr != m_Entities.end(); itr = itr2) { ++itr2; + if ((*itr)->IsDestroyed()) + { + continue; + } if (a_Callback.Item(*itr)) { return false; @@ -1950,6 +1981,10 @@ bool cChunk::ForEachEntityInBox(const cBoundingBox & a_Box, cEntityCallback & a_ for (cEntityList::iterator itr = m_Entities.begin(), itr2 = itr; itr != m_Entities.end(); itr = itr2) { ++itr2; + if ((*itr)->IsDestroyed()) + { + continue; + } cBoundingBox EntBox((*itr)->GetPosition(), (*itr)->GetWidth() / 2, (*itr)->GetHeight()); if (!EntBox.DoesIntersect(a_Box)) { @@ -1973,7 +2008,7 @@ bool cChunk::DoWithEntityByID(UInt32 a_EntityID, cEntityCallback & a_Callback, b // The entity list is locked by the parent chunkmap's CS for (cEntityList::iterator itr = m_Entities.begin(), end = m_Entities.end(); itr != end; ++itr) { - if ((*itr)->GetUniqueID() == a_EntityID) + if (((*itr)->GetUniqueID() == a_EntityID) && (!(*itr)->IsDestroyed())) { a_CallbackResult = a_Callback.Item(*itr); return true; diff --git a/src/Chunk.h b/src/Chunk.h index d944af10a..41bc79746 100644 --- a/src/Chunk.h +++ b/src/Chunk.h @@ -260,6 +260,9 @@ public: void AddEntity(cEntity * a_Entity); void RemoveEntity(cEntity * a_Entity); + /** RemoveEntity is dangerous if the chunk is inside the tick() method because it invalidates the iterator. + This will safely remove an entity. */ + void SafeRemoveEntity(cEntity * a_Entity); bool HasEntity(UInt32 a_EntityID); /** Calls the callback for each entity; returns true if all entities processed, false if the callback aborted by returning true */ @@ -502,7 +505,7 @@ private: /** If the chunk fails to load, should it be queued in the generator or reset back to invalid? */ bool m_ShouldGenerateIfLoadFailed; - + bool m_IsInTick; // True if the chunk is executing the tick() method. bool m_IsLightValid; // True if the blocklight and skylight are calculated bool m_IsDirty; // True if the chunk has changed since it was last saved bool m_IsSaving; // True if the chunk is being saved diff --git a/src/Entities/Entity.cpp b/src/Entities/Entity.cpp index 593bc6aca..b207e79c9 100644 --- a/src/Entities/Entity.cpp +++ b/src/Entities/Entity.cpp @@ -1499,12 +1499,28 @@ bool cEntity::DoMoveToWorld(cWorld * a_World, bool a_ShouldSendRespawn, Vector3d return false; } - // Remove all links to the old world - SetWorldTravellingFrom(GetWorld()); // cChunk::Tick() handles entity removal + // Remove entity from chunk + if (!GetWorld()->DoWithChunk(GetChunkX(), GetChunkZ(), [this](cChunk & a_Chunk) -> bool + { + a_Chunk.SafeRemoveEntity(this); + return true; + })) + { + LOGD("Entity Teleportation failed! Didn't find the source chunk!\n"); + return false; + } + GetWorld()->BroadcastDestroyEntity(*this); SetPosition(a_NewPosition); + if (this->IsMob()) + { + cMonster * Monster = static_cast<cMonster*>(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<int>(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<short>(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<cMonster*>::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<cEntityEffect::eType, cEntityEffect *> tEffectMap; tEffectMap m_EntityEffects; double m_LastGroundHeight; bool m_bTouchGround; + +private: + + /** A list of all monsters that are targeting this pawn. */ + std::vector<cMonster*> m_TargetingMe; } ; // tolua_export diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp index 767ee2061..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 @@ -1695,6 +1695,23 @@ bool cPlayer::DoMoveToWorld(cWorld * a_World, bool a_ShouldSendRespawn, Vector3d return false; } + // Remove player from chunk + if (!GetWorld()->DoWithChunk(GetChunkX(), GetChunkZ(), [this](cChunk & a_Chunk) -> bool + { + a_Chunk.SafeRemoveEntity(this); + return true; + })) + { + LOGD("Entity Teleportation failed! Didn't find the source chunk!\n"); + return false; + } + + // 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)) { @@ -1704,12 +1721,11 @@ bool cPlayer::DoMoveToWorld(cWorld * a_World, bool a_ShouldSendRespawn, Vector3d // Broadcast for other people that the player is gone. GetWorld()->BroadcastDestroyEntity(*this); - // Remove player from the old world - SetWorldTravellingFrom(GetWorld()); // cChunk handles entity removal - GetWorld()->RemovePlayer(this, false); - 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 diff --git a/src/Mobs/AggressiveMonster.cpp b/src/Mobs/AggressiveMonster.cpp index 512bfb4a1..c67f01b8f 100644 --- a/src/Mobs/AggressiveMonster.cpp +++ b/src/Mobs/AggressiveMonster.cpp @@ -26,9 +26,9 @@ void cAggressiveMonster::InStateChasing(std::chrono::milliseconds a_Dt, cChunk & { super::InStateChasing(a_Dt, a_Chunk); - if (m_Target != nullptr) + if (GetTarget() != nullptr) { - MoveToPosition(m_Target->GetPosition()); + MoveToPosition(GetTarget()->GetPosition()); } } @@ -62,14 +62,14 @@ void cAggressiveMonster::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) CheckEventSeePlayer(a_Chunk); } - if (m_Target == nullptr) + if (GetTarget() == nullptr) { return; } cTracer LineOfSight(GetWorld()); Vector3d MyHeadPosition = GetPosition() + Vector3d(0, GetHeight(), 0); - Vector3d AttackDirection(m_Target->GetPosition() + Vector3d(0, m_Target->GetHeight(), 0) - MyHeadPosition); + Vector3d AttackDirection(GetTarget()->GetPosition() + Vector3d(0, GetTarget()->GetHeight(), 0) - MyHeadPosition); if (TargetIsInRange() && !LineOfSight.Trace(MyHeadPosition, AttackDirection, static_cast<int>(AttackDirection.Length())) && (GetHealth() > 0.0)) @@ -85,14 +85,14 @@ void cAggressiveMonster::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) bool cAggressiveMonster::Attack(std::chrono::milliseconds a_Dt) { - if ((m_Target == nullptr) || (m_AttackCoolDownTicksLeft != 0)) + if ((GetTarget() == nullptr) || (m_AttackCoolDownTicksLeft != 0)) { return false; } // Setting this higher gives us more wiggle room for attackrate ResetAttackCooldown(); - m_Target->TakeDamage(dtMobAttack, this, m_AttackDamage, 0); + GetTarget()->TakeDamage(dtMobAttack, this, m_AttackDamage, 0); return true; } diff --git a/src/Mobs/Blaze.cpp b/src/Mobs/Blaze.cpp index bd3b3f776..d002e14e7 100644 --- a/src/Mobs/Blaze.cpp +++ b/src/Mobs/Blaze.cpp @@ -34,7 +34,7 @@ void cBlaze::GetDrops(cItems & a_Drops, cEntity * a_Killer) bool cBlaze::Attack(std::chrono::milliseconds a_Dt) { - if ((m_Target != nullptr) && (m_AttackCoolDownTicksLeft == 0)) + if ((GetTarget() != nullptr) && (m_AttackCoolDownTicksLeft == 0)) { // Setting this higher gives us more wiggle room for attackrate Vector3d Speed = GetLookVector() * 20; diff --git a/src/Mobs/CaveSpider.cpp b/src/Mobs/CaveSpider.cpp index ee3f4803c..2a4975126 100644 --- a/src/Mobs/CaveSpider.cpp +++ b/src/Mobs/CaveSpider.cpp @@ -33,11 +33,11 @@ bool cCaveSpider::Attack(std::chrono::milliseconds a_Dt) { return false; } - - if (m_Target->IsPawn()) + + if (GetTarget()->IsPawn()) { // TODO: Easy = no poison, Medium = 7 seconds, Hard = 15 seconds - static_cast<cPawn *>(m_Target)->AddEntityEffect(cEntityEffect::effPoison, 7 * 20, 0); + static_cast<cPawn *>(GetTarget())->AddEntityEffect(cEntityEffect::effPoison, 7 * 20, 0); } return true; } diff --git a/src/Mobs/Creeper.cpp b/src/Mobs/Creeper.cpp index d88c99953..47d294a30 100644 --- a/src/Mobs/Creeper.cpp +++ b/src/Mobs/Creeper.cpp @@ -27,7 +27,7 @@ void cCreeper::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { super::Tick(a_Dt, a_Chunk); - if ((m_Target == nullptr) || (!TargetIsInRange() && !m_BurnedWithFlintAndSteel)) + if ((GetTarget() == nullptr) || (!TargetIsInRange() && !m_BurnedWithFlintAndSteel)) { if (m_bIsBlowing) { diff --git a/src/Mobs/Enderman.cpp b/src/Mobs/Enderman.cpp index 4a30a0acd..ccfd44110 100644 --- a/src/Mobs/Enderman.cpp +++ b/src/Mobs/Enderman.cpp @@ -104,7 +104,7 @@ void cEnderman::GetDrops(cItems & a_Drops, cEntity * a_Killer) void cEnderman::CheckEventSeePlayer(cChunk & a_Chunk) { - if (m_Target != nullptr) + if (GetTarget() != nullptr) { return; } diff --git a/src/Mobs/Ghast.cpp b/src/Mobs/Ghast.cpp index 61813d0fe..0544255df 100644 --- a/src/Mobs/Ghast.cpp +++ b/src/Mobs/Ghast.cpp @@ -34,7 +34,7 @@ void cGhast::GetDrops(cItems & a_Drops, cEntity * a_Killer) bool cGhast::Attack(std::chrono::milliseconds a_Dt) { - if ((m_Target != nullptr) && (m_AttackCoolDownTicksLeft == 0)) + if ((GetTarget() != nullptr) && (m_AttackCoolDownTicksLeft == 0)) { // Setting this higher gives us more wiggle room for attackrate Vector3d Speed = GetLookVector() * 20; diff --git a/src/Mobs/Monster.cpp b/src/Mobs/Monster.cpp index 4a543e400..28cb10238 100644 --- a/src/Mobs/Monster.cpp +++ b/src/Mobs/Monster.cpp @@ -74,7 +74,6 @@ cMonster::cMonster(const AString & a_ConfigName, eMonsterType a_MobType, const A : super(etMonster, a_Width, a_Height) , m_EMState(IDLE) , m_EMPersonality(AGGRESSIVE) - , m_Target(nullptr) , m_PathFinder(a_Width, a_Height) , m_PathfinderActivated(false) , m_JumpCoolDown(0) @@ -101,6 +100,7 @@ cMonster::cMonster(const AString & a_ConfigName, eMonsterType a_MobType, const A , m_RelativeWalkSpeed(1) , m_Age(1) , m_AgingTimer(20 * 60 * 20) // about 20 minutes + , m_Target(nullptr) { if (!a_ConfigName.empty()) { @@ -112,6 +112,25 @@ cMonster::cMonster(const AString & a_ConfigName, eMonsterType a_MobType, const A +cMonster::~cMonster() +{ + ASSERT(GetTarget() == nullptr); +} + + + + + +void cMonster::Destroyed() +{ + SetTarget(nullptr); // Tell them we're no longer targeting them. + super::Destroyed(); +} + + + + + void cMonster::SpawnOn(cClientHandle & a_Client) { a_Client.SendSpawnMob(*this); @@ -214,6 +233,7 @@ void cMonster::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) super::Tick(a_Dt, a_Chunk); GET_AND_VERIFY_CURRENT_CHUNK(Chunk, POSX_TOINT, POSZ_TOINT); + ASSERT((GetTarget() == nullptr) || (GetTarget()->IsPawn() && (GetTarget()->GetWorld() == GetWorld()))); if (m_AttackCoolDownTicksLeft > 0) { m_AttackCoolDownTicksLeft -= 1; @@ -234,17 +254,15 @@ void cMonster::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { ++m_TicksSinceLastDamaged; } - if ((m_Target != nullptr)) + if ((GetTarget() != nullptr)) { - if (m_Target->IsDestroyed()) - { - m_Target = nullptr; - } - else if (m_Target->IsPlayer()) + ASSERT(!GetTarget()->IsDestroyed()); + + if (GetTarget()->IsPlayer()) { - if (static_cast<cPlayer *>(m_Target)->IsGameModeCreative()) + if (static_cast<cPlayer *>(GetTarget())->IsGameModeCreative()) { - m_Target = nullptr; + SetTarget(nullptr); m_EMState = IDLE; } } @@ -343,11 +361,10 @@ void cMonster::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) void cMonster::SetPitchAndYawFromDestination(bool a_IsFollowingPath) { - /* Todo Buggy */ Vector3d BodyDistance; - if (!a_IsFollowingPath && (m_Target != nullptr)) + if (!a_IsFollowingPath && (GetTarget() != nullptr)) { - BodyDistance = m_Target->GetPosition() - GetPosition(); + BodyDistance = GetTarget()->GetPosition() - GetPosition(); } else { @@ -359,17 +376,16 @@ void cMonster::SetPitchAndYawFromDestination(bool a_IsFollowingPath) SetYaw(BodyRotation); Vector3d HeadDistance; - if (m_Target != nullptr) + if (GetTarget() != nullptr) { - if (m_Target->IsPlayer()) // Look at a player + if (GetTarget()->IsPlayer()) // Look at a player { - HeadDistance = m_Target->GetPosition() - GetPosition(); - // HeadDistance.y = static_cast<cPlayer *>(m_Target)->GetStance() - 1; + HeadDistance = GetTarget()->GetPosition() - GetPosition(); } else // Look at some other entity { - HeadDistance = m_Target->GetPosition() - GetPosition(); - // HeadDistance.y = m_Target->GetPosY() + GetHeight(); + HeadDistance = GetTarget()->GetPosition() - GetPosition(); + // HeadDistance.y = GetTarget()->GetPosY() + GetHeight(); } } else // Look straight @@ -448,9 +464,9 @@ bool cMonster::DoTakeDamage(TakeDamageInfo & a_TDI) m_World->BroadcastSoundEffect(m_SoundHurt, GetPosX(), GetPosY(), GetPosZ(), 1.0f, 0.8f); } - if (a_TDI.Attacker != nullptr) + if ((a_TDI.Attacker != nullptr) && a_TDI.Attacker->IsPawn()) { - m_Target = a_TDI.Attacker; + SetTarget(static_cast<cPawn*>(a_TDI.Attacker)); m_TicksSinceLastDamaged = 0; } return true; @@ -577,9 +593,9 @@ void cMonster::CheckEventSeePlayer(cChunk & a_Chunk) void cMonster::CheckEventLostPlayer(void) { - if (m_Target != nullptr) + if (GetTarget() != nullptr) { - if ((m_Target->GetPosition() - GetPosition()).Length() > m_SightDistance) + if ((GetTarget()->GetPosition() - GetPosition()).Length() > m_SightDistance) { EventLosePlayer(); } @@ -598,7 +614,9 @@ void cMonster::CheckEventLostPlayer(void) // default to change state to chasing void cMonster::EventSeePlayer(cEntity * a_SeenPlayer, cChunk & a_Chunk) { - m_Target = a_SeenPlayer; + UNUSED(a_Chunk); + ASSERT(a_SeenPlayer->IsPlayer()); + SetTarget(static_cast<cPawn*>(a_SeenPlayer)); } @@ -607,7 +625,7 @@ void cMonster::EventSeePlayer(cEntity * a_SeenPlayer, cChunk & a_Chunk) void cMonster::EventLosePlayer(void) { - m_Target = nullptr; + SetTarget(nullptr); m_EMState = IDLE; } @@ -678,11 +696,11 @@ void cMonster::InStateEscaping(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { UNUSED(a_Dt); - if (m_Target != nullptr) + if (GetTarget() != nullptr) { Vector3d newloc = GetPosition(); - newloc.x = (m_Target->GetPosition().x < newloc.x)? (newloc.x + m_SightDistance): (newloc.x - m_SightDistance); - newloc.z = (m_Target->GetPosition().z < newloc.z)? (newloc.z + m_SightDistance): (newloc.z - m_SightDistance); + newloc.x = (GetTarget()->GetPosition().x < newloc.x)? (newloc.x + m_SightDistance): (newloc.x - m_SightDistance); + newloc.z = (GetTarget()->GetPosition().z < newloc.z)? (newloc.z + m_SightDistance): (newloc.z - m_SightDistance); MoveToPosition(newloc); } else @@ -890,6 +908,55 @@ int cMonster::GetSpawnDelay(cMonster::eFamily a_MobFamily) + +/** Sets the target. */ +void cMonster::SetTarget (cPawn * a_NewTarget) +{ + ASSERT((a_NewTarget == nullptr) || (!IsDestroyed())); + if (m_Target == a_NewTarget) + { + return; + } + cPawn * OldTarget = m_Target; + m_Target = a_NewTarget; + + if (OldTarget != nullptr) + { + // Notify the old target that we are no longer targeting it. + OldTarget->NoLongerTargetingMe(this); + } + + if (a_NewTarget != nullptr) + { + ASSERT(!a_NewTarget->IsDestroyed()); + // Notify the new target that we are now targeting it. + m_Target->TargetingMe(this); + } + +} + + + + + +void cMonster::UnsafeUnsetTarget() +{ + m_Target = nullptr; +} + + + + + +cPawn * cMonster::GetTarget () +{ + return m_Target; +} + + + + + cMonster * cMonster::NewMonsterFromType(eMonsterType a_MobType) { cFastRandom Random; diff --git a/src/Mobs/Monster.h b/src/Mobs/Monster.h index 7c4683942..2155a4a7c 100644 --- a/src/Mobs/Monster.h +++ b/src/Mobs/Monster.h @@ -44,6 +44,10 @@ public: */ cMonster(const AString & a_ConfigName, eMonsterType a_MobType, const AString & a_SoundHurt, const AString & a_SoundDeath, double a_Width, double a_Height); + ~cMonster(); + + virtual void Destroyed() override; + CLASS_PROTODEF(cMonster) virtual void SpawnOn(cClientHandle & a_ClientHandle) override; @@ -156,6 +160,16 @@ public: // tolua_end + /** Sets the target that this mob will chase. Pass a nullptr to unset. */ + void SetTarget (cPawn * a_NewTarget); + + /** Unset the target without notifying the target entity. Do not use this, use SetTarget(nullptr) instead. + This is only used by cPawn internally. */ + void UnsafeUnsetTarget(); + + /** Returns the current target. */ + cPawn * GetTarget (); + /** Creates a new object of the specified mob. a_MobType is the type of the mob to be created Asserts and returns null if mob type is not specified @@ -164,9 +178,6 @@ public: protected: - /** A pointer to the entity this mobile is aiming to reach */ - cEntity * m_Target; - /** The pathfinder instance handles pathfinding for this monster. */ cPathFinder m_PathFinder; @@ -255,4 +266,8 @@ protected: /** Adds weapon that is equipped with the chance saved in m_DropChance[...] (this will be greter than 1 if picked up or 0.085 + (0.01 per LootingLevel) if born with) to the drop */ void AddRandomWeaponDropItem(cItems & a_Drops, unsigned int a_LootingLevel); +private: + /** A pointer to the entity this mobile is aiming to reach */ + cPawn * m_Target; + } ; // tolua_export diff --git a/src/Mobs/PassiveAggressiveMonster.cpp b/src/Mobs/PassiveAggressiveMonster.cpp index 71ac7bd89..a1bb1138f 100644 --- a/src/Mobs/PassiveAggressiveMonster.cpp +++ b/src/Mobs/PassiveAggressiveMonster.cpp @@ -26,9 +26,9 @@ bool cPassiveAggressiveMonster::DoTakeDamage(TakeDamageInfo & a_TDI) return false; } - if ((m_Target != nullptr) && (m_Target->IsPlayer())) + if ((GetTarget() != nullptr) && (GetTarget()->IsPlayer())) { - if (!static_cast<cPlayer *>(m_Target)->IsGameModeCreative()) + if (!static_cast<cPlayer *>(GetTarget())->IsGameModeCreative()) { m_EMState = CHASING; } diff --git a/src/Mobs/Skeleton.cpp b/src/Mobs/Skeleton.cpp index adad543d2..7697f1279 100644 --- a/src/Mobs/Skeleton.cpp +++ b/src/Mobs/Skeleton.cpp @@ -52,10 +52,10 @@ bool cSkeleton::Attack(std::chrono::milliseconds a_Dt) { StopMovingToPosition(); // Todo handle this in a better way, the skeleton does some uneeded recalcs due to inStateChasing cFastRandom Random; - if ((m_Target != nullptr) && (m_AttackCoolDownTicksLeft == 0)) + if ((GetTarget() != nullptr) && (m_AttackCoolDownTicksLeft == 0)) { Vector3d Inaccuracy = Vector3d(Random.NextFloat(0.5) - 0.25, Random.NextFloat(0.5) - 0.25, Random.NextFloat(0.5) - 0.25); - Vector3d Speed = (m_Target->GetPosition() + Inaccuracy - GetPosition()) * 5; + Vector3d Speed = (GetTarget()->GetPosition() + Inaccuracy - GetPosition()) * 5; Speed.y = Speed.y - 1 + Random.NextInt(3); cArrowEntity * Arrow = new cArrowEntity(this, GetPosX(), GetPosY() + 1, GetPosZ(), Speed); if (Arrow == nullptr) diff --git a/src/Mobs/Wolf.cpp b/src/Mobs/Wolf.cpp index 3be14211b..d188d91eb 100644 --- a/src/Mobs/Wolf.cpp +++ b/src/Mobs/Wolf.cpp @@ -30,7 +30,7 @@ cWolf::cWolf(void) : bool cWolf::DoTakeDamage(TakeDamageInfo & a_TDI) { - cEntity * PreviousTarget = m_Target; + cPawn * PreviousTarget = GetTarget(); if (!super::DoTakeDamage(a_TDI)) { return false; @@ -38,14 +38,13 @@ bool cWolf::DoTakeDamage(TakeDamageInfo & a_TDI) if ((a_TDI.Attacker != nullptr) && a_TDI.Attacker->IsPawn()) { - cPawn * Pawn = static_cast<cPawn*>(m_Target); - if (Pawn->IsPlayer()) + if (GetTarget()->IsPlayer()) { if (m_IsTame) { - if ((static_cast<cPlayer*>(Pawn)->GetUUID() == m_OwnerUUID)) + if ((static_cast<cPlayer*>(GetTarget())->GetUUID() == m_OwnerUUID)) { - m_Target = PreviousTarget; // Do not attack owner + SetTarget(PreviousTarget); // Do not attack owner } else { @@ -100,16 +99,16 @@ bool cWolf::Attack(std::chrono::milliseconds a_Dt) { UNUSED(a_Dt); - if ((m_Target != nullptr) && (m_Target->IsPlayer())) + if ((GetTarget() != nullptr) && (GetTarget()->IsPlayer())) { - if (static_cast<cPlayer *>(m_Target)->GetUUID() == m_OwnerUUID) + if (static_cast<cPlayer *>(GetTarget())->GetUUID() == m_OwnerUUID) { - m_Target = nullptr; + SetTarget(nullptr); return false; } } - NotifyAlliesOfFight(static_cast<cPawn*>(m_Target)); + NotifyAlliesOfFight(static_cast<cPawn*>(GetTarget())); return super::Attack(a_Dt); } @@ -129,7 +128,7 @@ void cWolf::ReceiveNearbyFightInfo(AString a_PlayerID, cPawn * a_Opponent, bool } // If we already have a target - if (m_Target != nullptr) + if (GetTarget() != nullptr) { // If a wolf is asking for help and we already have a target, do nothing if (!a_IsPlayerInvolved) @@ -159,7 +158,7 @@ void cWolf::ReceiveNearbyFightInfo(AString a_PlayerID, cPawn * a_Opponent, bool } } - m_Target = a_Opponent; + SetTarget(a_Opponent); } @@ -264,7 +263,7 @@ void cWolf::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) super::Tick(a_Dt, a_Chunk); } - if (m_Target == nullptr) + if (GetTarget() == nullptr) { cPlayer * a_Closest_Player = m_World->FindClosestPlayer(GetPosition(), static_cast<float>(m_SightDistance)); if (a_Closest_Player != nullptr) @@ -311,11 +310,11 @@ void cWolf::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { if (IsSitting()) { - m_Target = nullptr; + SetTarget(nullptr); } else { - MoveToPosition(m_Target->GetPosition()); + MoveToPosition(GetTarget()->GetPosition()); if (TargetIsInRange()) { Attack(a_Dt); @@ -359,18 +358,18 @@ void cWolf::TickFollowPlayer() { Callback.OwnerPos.y = FindFirstNonAirBlockPosition(Callback.OwnerPos.x, Callback.OwnerPos.z); TeleportToCoords(Callback.OwnerPos.x, Callback.OwnerPos.y, Callback.OwnerPos.z); - m_Target = nullptr; + SetTarget(nullptr); } if (Distance < 2) { - if (m_Target == nullptr) + if (GetTarget() == nullptr) { StopMovingToPosition(); } } else { - if (m_Target == nullptr) + if (GetTarget() == nullptr) { MoveToPosition(Callback.OwnerPos); } diff --git a/src/Root.cpp b/src/Root.cpp index aa532f88c..737d350ff 100644 --- a/src/Root.cpp +++ b/src/Root.cpp @@ -358,7 +358,7 @@ void cRoot::LoadWorlds(cSettingsRepositoryInterface & a_Settings, bool a_IsNewIn // Fix servers that have default world configs created prior to #2815. See #2810. // This can probably be removed several years after 2016 // We start by inspecting the world linkage and determining if it's the default one - if (DefaultWorldName == "world") + if ((DefaultWorldName == "world") && (Worlds.size() == 1)) { auto DefaultWorldIniFile= cpp14::make_unique<cIniFile>(); if (DefaultWorldIniFile->ReadFile("world/world.ini")) diff --git a/src/World.cpp b/src/World.cpp index 6bb5b5940..5b6a215d8 100644 --- a/src/World.cpp +++ b/src/World.cpp @@ -2875,6 +2875,10 @@ bool cWorld::ForEachPlayer(cPlayerListCallback & a_Callback) for (cPlayerList::iterator itr = m_Players.begin(), itr2 = itr; itr != m_Players.end(); itr = itr2) { ++itr2; + if ((*itr)->IsDestroyed()) + { + continue; + } if (a_Callback.Item(*itr)) { return false; @@ -2893,6 +2897,10 @@ bool cWorld::DoWithPlayer(const AString & a_PlayerName, cPlayerListCallback & a_ cCSLock Lock(m_CSPlayers); for (cPlayerList::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) { + if ((*itr)->IsDestroyed()) + { + continue; + } if (NoCaseCompare((*itr)->GetName(), a_PlayerName) == 0) { a_Callback.Item(*itr); @@ -2915,6 +2923,10 @@ bool cWorld::FindAndDoWithPlayer(const AString & a_PlayerNameHint, cPlayerListCa cCSLock Lock(m_CSPlayers); for (cPlayerList::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) { + if ((*itr)->IsDestroyed()) + { + continue; + } size_t Rating = RateCompareString (a_PlayerNameHint, (*itr)->GetName()); if (Rating >= BestRating) { @@ -2943,6 +2955,10 @@ bool cWorld::DoWithPlayerByUUID(const AString & a_PlayerUUID, cPlayerListCallbac cCSLock Lock(m_CSPlayers); for (cPlayerList::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) { + if ((*itr)->IsDestroyed()) + { + continue; + } if ((*itr)->GetUUID() == a_PlayerUUID) { return a_Callback.Item(*itr); @@ -2966,6 +2982,10 @@ cPlayer * cWorld::FindClosestPlayer(const Vector3d & a_Pos, float a_SightLimit, cCSLock Lock(m_CSPlayers); for (cPlayerList::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) { + if ((*itr)->IsDestroyed()) + { + continue; + } Vector3f Pos = (*itr)->GetPosition(); double Distance = (Pos - a_Pos).Length(); |