diff options
28 files changed, 335 insertions, 239 deletions
@@ -38,7 +38,7 @@ PROJECT_NUMBER = # for a project that appears at the top of each page and should give viewer # a quick idea about the purpose of the project. Keep the description short. -PROJECT_BRIEF = "C++ Minecraft Server" +PROJECT_BRIEF = "A custom Minecraft compatible game server written in C++" # With the PROJECT_LOGO tag one can specify an logo or icon that is # included in the documentation. The maximum height of the logo should not @@ -1,7 +1,7 @@ -Cuberite: A performant C++ Minecraft Server +Cuberite: A custom Minecraft compatible game server written in C++ www: https://github.com/cuberite/cuberite -Copyright 2011-2015 Cuberite Team +Copyright 2011-2016 Cuberite Team ------ diff --git a/Server/BACKERS b/Server/BACKERS new file mode 100644 index 000000000..f2a1cfa8c --- /dev/null +++ b/Server/BACKERS @@ -0,0 +1,11 @@ +Thanks to the following people for supporting the Cuberite project with a donation: + + - Anonymous + - chrobione + - DrMasik + - PureTryOut + - SphinxC0re + - VaiN474 + +If you enjoy Cuberite, feel free to donate to the project on Bountysource: +https://salt.bountysource.com/teams/cuberite diff --git a/Server/webadmin/files/background.gif b/Server/webadmin/files/background.gif Binary files differdeleted file mode 100644 index cab9bed56..000000000 --- a/Server/webadmin/files/background.gif +++ /dev/null diff --git a/Server/webadmin/files/loading.gif b/Server/webadmin/files/loading.gif Binary files differdeleted file mode 100644 index b8d06f669..000000000 --- a/Server/webadmin/files/loading.gif +++ /dev/null diff --git a/Server/webadmin/files/logo.png b/Server/webadmin/files/logo.png Binary files differdeleted file mode 100644 index 50733e824..000000000 --- a/Server/webadmin/files/logo.png +++ /dev/null diff --git a/Server/webadmin/files/mc-logo.png b/Server/webadmin/files/mc-logo.png Binary files differdeleted file mode 100644 index 9a77a490f..000000000 --- a/Server/webadmin/files/mc-logo.png +++ /dev/null diff --git a/Server/webadmin/template.html b/Server/webadmin/template.html deleted file mode 100644 index 305c566cd..000000000 --- a/Server/webadmin/template.html +++ /dev/null @@ -1,140 +0,0 @@ -<!DOCTYPE html> -<html> - <head> - <title>{TITLE} | {PLUGIN_NAME}</title> - <style type="text/css"> - body { - line-height: 1; - background: #B8B8B8; - } - - #maincontent { - padding: 0px 25px 10px 25px; - } - - #wrapper { - min-width: 850px; - width: 75%; - margin: 10px auto; - background-color: white; - border: 4px #888888 solid; - border-radius: 10px; - font-family: Calibri, Trebuchet MS; - } - - header { - text-align:center; - padding: 10px; 0px; - } - - span { - text-align: right; - float: right; - border-left: 2px #C8C8C8 solid; - border-bottom: 2px #C8C8C8 solid; - padding: 2px 10px; - } - - footer { - font-family: helvetica; - font-size: 10px; - text-align: center; - border-top: 1px #000 dotted; - padding: 1px 0px 1px 0px; - } - - table { - border-collapse: collapse; - border-spacing: 10; - } - - table { - border-top: 1px solid #ddd; - width: 700px; - } - - table tr th { - text-align: left; - background: #f6f6f6; - padding: 0px 20px; - height: 25px; - line-height: 25px; - border: 1px solid #ddd; - border-radius: 3px; - } - - table tr td { - background: #f6f6f6; - padding: 0px 20px; - height: 29px; - line-height: 29px; - border: 1px solid #ddd; - border-radius: 3px; - } - - #main table tr.odd td { - background: #fbfbfb; - } - - table tr:hover td { - background: #fdfcf6; - } - - table .action { - text-align: right; - padding: 0 20px 0 10px; - } - - table tr .action a { - color: #9b9b9b; - } - - #cssmenu{ height:10px; display:table; padding:0; margin: 0 auto; border:1px #707070 solid; border-radius:5px; } - #cssmenu > ul {list-style:inside none; padding:0; margin:0;} - #cssmenu > ul > li {list-style:inside none; padding:0; margin:0; float:left; display:block; position:relative;} - #cssmenu > ul > li > a{ outline:none; display:block; position:relative; color:#E8E8E8; padding:10px 10px; font:bold 13px/100% Arial, Helvetica, sans-serif; text-align:center; text-decoration:none; text-shadow:1px 1px 0 rgba(0,0,0, 0.4); } - #cssmenu > ul > li:first-child > a{border-radius:5px 0 0 5px;} - /* #cssmenu > ul > li > a:after{ content:''; position:absolute; border-right:1px solid #FFFFFF; top:-1px; bottom:-1px; right:-2px; z-index:99; } */ - #cssmenu ul li.has-sub:hover > a:after{top:0; bottom:0;} - #cssmenu > ul > li.has-sub > a:before{ content:''; position:absolute; top:18px; right:6px; border:5px solid transparent; border-top:5px solid #707070; } - #cssmenu > ul > li.has-sub:hover > a:before{top:19px;} - #cssmenu ul li.has-sub:hover > a{ background:#3f3f3f; border-color:#707070; padding-bottom:13px; padding-top:13px; top:-1px; z-index:999; } - #cssmenu ul li.has-sub:hover > ul, #cssmenu ul li.has-sub:hover > div{display:block;} - #cssmenu ul li.has-sub > a:hover{background:#3f3f3f; border-color:#3f3f3f;} - #cssmenu ul li > ul, #cssmenu ul li > div{ display:none; width:auto; position:absolute; top:38px; padding:10px 0; background:#3f3f3f; border-radius:0 0 5px 5px; z-index:999; } - #cssmenu ul li > ul{width:200px;} - #cssmenu ul li > ul li{display:block; list-style:inside none; padding:0; margin:0; position:relative;} - #cssmenu ul li > ul li a{ outline:none; display:block; position:relative; margin:0; padding:8px 20px; font:10pt Arial, Helvetica, sans-serif; color:#fff; text-decoration:none; text-shadow:1px 1px 0 rgba(0,0,0, 0.5); } - #cssmenu, #cssmenu > ul > li > ul > li a:hover{ background:#C8C8C8;} - #cssmenu > ul > li > a { border-right:1px solid #707070; color:#FFFFFF; } - #cssmenu > ul > li > a:after { border-color:#707070; } - #cssmenu > ul > li > a:hover { background:#B8B8B8; } - </style> - <meta name="msapplication-tooltip" content="Cuberite WebAdmin"/> - <meta name="msapplication-navbutton-color" content="#B8B8B8" /> - <link rel="shortcut icon" href="http://cuberite.org/favicon.ico" /> - </head> - <body> - <div id="wrapper"> - <span> - <b>Login: {USERNAME}</b> - </span> - <br /> - <header> - <img alt="" src="files/logo.png" /> - </header> - <nav id="cssmenu"> - <ul> - {MENU} - </ul> - </nav> - <div id="maincontent"> - {CONTENT} - </div> - <footer> - <p>Cuberite is using {MEM}MB of memory, with {NUMCHUNKS} chunks loaded.</p> - <p>Web Design by <a href="https://github.com/WebFreak001"@WebFreak001</a></p> - </footer> - </div> - </body> -</html> diff --git a/src/Chunk.cpp b/src/Chunk.cpp index 1e3e749fd..f5d447c45 100644 --- a/src/Chunk.cpp +++ b/src/Chunk.cpp @@ -1959,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; @@ -1977,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)) { @@ -2000,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/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<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 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 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/PassiveMonster.cpp b/src/Mobs/PassiveMonster.cpp index 30b46500d..53288a54c 100644 --- a/src/Mobs/PassiveMonster.cpp +++ b/src/Mobs/PassiveMonster.cpp @@ -157,19 +157,33 @@ void cPassiveMonster::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) virtual bool Item(cEntity * a_Entity) override { - // if we're the same species as someone around and they don't have a partner, start mating with them - if ((a_Entity->GetEntityType() == m_Me->GetEntityType()) && (a_Entity != m_Me)) + // If the entity is not a monster, don't breed with it + // Also, do not self-breed + if ((a_Entity->GetEntityType() != etMonster) || (a_Entity == m_Me)) { - cPassiveMonster * Me = static_cast<cPassiveMonster*>(m_Me); - cPassiveMonster * Partner = static_cast<cPassiveMonster*>(a_Entity); - if (Partner->IsInLove() && (Partner->GetPartner() == nullptr)) - { - Partner->EngageLoveMode(Me); - Me->EngageLoveMode(Partner); - return true; - } + return false; } - return false; + + cPassiveMonster * Me = static_cast<cPassiveMonster*>(m_Me); + cPassiveMonster * PotentialPartner = static_cast<cPassiveMonster*>(a_Entity); + + // If the potential partner is not of the same species, don't breed with it + if (PotentialPartner->GetMobType() != Me->GetMobType()) + { + return false; + } + + // If the potential partner is not in love + // Or they already have a mate, do not breed with them + if ((!PotentialPartner->IsInLove()) || (PotentialPartner->GetPartner() != nullptr)) + { + return false; + } + + // All conditions met, let's breed! + PotentialPartner->EngageLoveMode(Me); + Me->EngageLoveMode(PotentialPartner); + return true; } } Callback(this); diff --git a/src/Mobs/PathFinder.h b/src/Mobs/PathFinder.h index 1bdc13a32..213530b11 100644 --- a/src/Mobs/PathFinder.h +++ b/src/Mobs/PathFinder.h @@ -14,7 +14,7 @@ class cPathFinder public: /** Creates a cPathFinder instance. Each mob should have one cPathFinder throughout its lifetime. @param a_MobWidth The mob width. - @param a_MobWidth The mob height. + @param a_MobHeight The mob height. */ cPathFinder(double a_MobWidth, double a_MobHeight); 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(); |