summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/Chunk.cpp39
-rw-r--r--src/Chunk.h5
-rw-r--r--src/Entities/Entity.cpp20
-rw-r--r--src/Entities/Pawn.cpp88
-rw-r--r--src/Entities/Pawn.h35
-rw-r--r--src/Entities/Player.cpp26
-rw-r--r--src/Mobs/AggressiveMonster.cpp12
-rw-r--r--src/Mobs/Blaze.cpp2
-rw-r--r--src/Mobs/CaveSpider.cpp6
-rw-r--r--src/Mobs/Creeper.cpp2
-rw-r--r--src/Mobs/Enderman.cpp2
-rw-r--r--src/Mobs/Ghast.cpp2
-rw-r--r--src/Mobs/Monster.cpp121
-rw-r--r--src/Mobs/Monster.h21
-rw-r--r--src/Mobs/PassiveAggressiveMonster.cpp4
-rw-r--r--src/Mobs/Skeleton.cpp4
-rw-r--r--src/Mobs/Wolf.cpp33
-rw-r--r--src/Root.cpp2
-rw-r--r--src/World.cpp20
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();