diff options
Diffstat (limited to 'src/Entities/Entity.cpp')
-rw-r--r-- | src/Entities/Entity.cpp | 302 |
1 files changed, 124 insertions, 178 deletions
diff --git a/src/Entities/Entity.cpp b/src/Entities/Entity.cpp index 2adbc3142..8d74ee99a 100644 --- a/src/Entities/Entity.cpp +++ b/src/Entities/Entity.cpp @@ -41,7 +41,6 @@ cEntity::cEntity(eEntityType a_EntityType, double a_X, double a_Y, double a_Z, d m_LastPosition(a_X, a_Y, a_Z), m_EntityType(a_EntityType), m_World(nullptr), - m_IsWorldChangeScheduled(false), m_IsFireproof(false), m_TicksSinceLastBurnDamage(0), m_TicksSinceLastLavaDamage(0), @@ -52,6 +51,7 @@ cEntity::cEntity(eEntityType a_EntityType, double a_X, double a_Y, double a_Z, d m_IsSubmerged(false), m_AirLevel(0), m_AirTickTimer(0), + m_PortalCooldownData({0, false, true}), m_TicksAlive(0), m_IsTicking(false), m_ParentChunk(nullptr), @@ -77,9 +77,6 @@ cEntity::cEntity(eEntityType a_EntityType, double a_X, double a_Y, double a_Z, d cEntity::~cEntity() { - - // Before deleting, the entity needs to have been removed from the world, if ever added - ASSERT((m_World == nullptr) || !m_World->HasEntity(m_UniqueID)); ASSERT(!IsTicking()); /* @@ -201,7 +198,7 @@ void cEntity::SetParentChunk(cChunk * a_Chunk) -cChunk * cEntity::GetParentChunk() +cChunk * cEntity::GetParentChunk() const { return m_ParentChunk; } @@ -1369,36 +1366,13 @@ void cEntity::DetectCacti(void) -void cEntity::ScheduleMoveToWorld(cWorld * a_World, Vector3d a_NewPosition, bool a_SetPortalCooldown) -{ - m_NewWorld = a_World; - m_NewWorldPosition = a_NewPosition; - m_IsWorldChangeScheduled = true; - m_WorldChangeSetPortalCooldown = a_SetPortalCooldown; -} - - - - bool cEntity::DetectPortal() { - // If somebody scheduled a world change with ScheduleMoveToWorld, change worlds now. - if (m_IsWorldChangeScheduled) + if (!m_PortalCooldownData.m_PositionValid) { - m_IsWorldChangeScheduled = false; - - if (m_WorldChangeSetPortalCooldown) - { - // Delay the portal check. - m_PortalCooldownData.m_TicksDelayed = 0; - m_PortalCooldownData.m_ShouldPreventTeleportation = true; - } - - MoveToWorld(m_NewWorld, false, m_NewWorldPosition); - return true; + return false; } - - if (GetWorld()->GetDimension() == dimOverworld) + else if (GetWorld()->GetDimension() == dimOverworld) { if (GetWorld()->GetLinkedNetherWorldName().empty() && GetWorld()->GetLinkedEndWorldName().empty()) { @@ -1412,7 +1386,7 @@ bool cEntity::DetectPortal() return false; } - int X = POSX_TOINT, Y = POSY_TOINT, Z = POSZ_TOINT; + const int X = POSX_TOINT, Y = POSY_TOINT, Z = POSZ_TOINT; if ((Y > 0) && (Y < cChunkDef::Height)) { switch (GetWorld()->GetBlock(X, Y, Z)) @@ -1425,62 +1399,56 @@ bool cEntity::DetectPortal() return false; } - if (IsPlayer() && !(reinterpret_cast<cPlayer *>(this))->IsGameModeCreative() && (m_PortalCooldownData.m_TicksDelayed != 80)) + if ( + IsPlayer() && + // !reinterpret_cast<cPlayer *>(this)->IsGameModeCreative() && // TODO: fix portal travel prevention - client sends outdated position data throwing off checks + (m_PortalCooldownData.m_TicksDelayed != 80) + ) { // Delay teleportation for four seconds if the entity is a non-creative player m_PortalCooldownData.m_TicksDelayed++; return false; } - m_PortalCooldownData.m_TicksDelayed = 0; + + m_PortalCooldownData.m_ShouldPreventTeleportation = true; // Stop portals from working on respawn + m_PortalCooldownData.m_PositionValid = false; if (GetWorld()->GetDimension() == dimNether) { - if (GetWorld()->GetLinkedOverworldName().empty()) - { - return false; - } - - m_PortalCooldownData.m_ShouldPreventTeleportation = true; // Stop portals from working on respawn + cWorld * TargetWorld = cRoot::Get()->GetWorld(GetWorld()->GetLinkedOverworldName()); + ASSERT(TargetWorld != nullptr); // The linkage checker should have prevented this at startup. See cWorld::start() - if (IsPlayer()) + if (GetWorld()->GetLinkedOverworldName().empty() || !OnPreWorldTravel(*TargetWorld)) { - // Send a respawn packet before world is loaded / generated so the client isn't left in limbo - (reinterpret_cast<cPlayer *>(this))->GetClientHandle()->SendRespawn(dimOverworld); + return false; } Vector3d TargetPos = GetPosition(); TargetPos.x *= 8.0; TargetPos.z *= 8.0; - cWorld * TargetWorld = cRoot::Get()->GetWorld(GetWorld()->GetLinkedOverworldName()); - ASSERT(TargetWorld != nullptr); // The linkage checker should have prevented this at startup. See cWorld::start() LOGD("Jumping nether -> overworld"); - new cNetherPortalScanner(this, TargetWorld, TargetPos, 256); + new cNetherPortalScanner(GetUniqueID(), GetWorld()->GetDimension(), TargetWorld, TargetPos, 256); + return true; } else { - if (GetWorld()->GetLinkedNetherWorldName().empty()) - { - return false; - } - - m_PortalCooldownData.m_ShouldPreventTeleportation = true; + cWorld * TargetWorld = cRoot::Get()->GetWorld(GetWorld()->GetLinkedNetherWorldName()); + ASSERT(TargetWorld != nullptr); // The linkage checker should have prevented this at startup. See cWorld::start() - if (IsPlayer()) + if (GetWorld()->GetLinkedNetherWorldName().empty() || !OnPreWorldTravel(*TargetWorld)) { - reinterpret_cast<cPlayer *>(this)->AwardAchievement(achEnterPortal); - reinterpret_cast<cPlayer *>(this)->GetClientHandle()->SendRespawn(dimNether); + return false; } Vector3d TargetPos = GetPosition(); TargetPos.x /= 8.0; TargetPos.z /= 8.0; - cWorld * TargetWorld = cRoot::Get()->GetWorld(GetWorld()->GetLinkedNetherWorldName()); - ASSERT(TargetWorld != nullptr); // The linkage checker should have prevented this at startup. See cWorld::start() LOGD("Jumping overworld -> nether"); - new cNetherPortalScanner(this, TargetWorld, TargetPos, 128); + new cNetherPortalScanner(GetUniqueID(), GetWorld()->GetDimension(), TargetWorld, TargetPos, 128); + return true; } } @@ -1491,6 +1459,9 @@ bool cEntity::DetectPortal() return false; } + m_PortalCooldownData.m_ShouldPreventTeleportation = true; + m_PortalCooldownData.m_PositionValid = false; + if (GetWorld()->GetDimension() == dimEnd) { @@ -1499,18 +1470,10 @@ bool cEntity::DetectPortal() return false; } - m_PortalCooldownData.m_ShouldPreventTeleportation = true; - - if (IsPlayer()) - { - cPlayer * Player = reinterpret_cast<cPlayer *>(this); - Player->TeleportToCoords(Player->GetLastBedPos().x, Player->GetLastBedPos().y, Player->GetLastBedPos().z); - Player->GetClientHandle()->SendRespawn(dimOverworld); - } - cWorld * TargetWorld = cRoot::Get()->GetWorld(GetWorld()->GetLinkedOverworldName()); ASSERT(TargetWorld != nullptr); // The linkage checker should have prevented this at startup. See cWorld::start() - return MoveToWorld(TargetWorld, false); + + return MoveToWorld(*TargetWorld, Vector3d(TargetWorld->GetSpawnX(), TargetWorld->GetSpawnY(), TargetWorld->GetSpawnZ())); } else { @@ -1519,27 +1482,18 @@ bool cEntity::DetectPortal() return false; } - m_PortalCooldownData.m_ShouldPreventTeleportation = true; - - if (IsPlayer()) - { - reinterpret_cast<cPlayer *>(this)->AwardAchievement(achEnterTheEnd); - reinterpret_cast<cPlayer *>(this)->GetClientHandle()->SendRespawn(dimEnd); - } - cWorld * TargetWorld = cRoot::Get()->GetWorld(GetWorld()->GetLinkedEndWorldName()); ASSERT(TargetWorld != nullptr); // The linkage checker should have prevented this at startup. See cWorld::start() - return MoveToWorld(TargetWorld, false); - } + return MoveToWorld(*TargetWorld, GetPosition()); + } } default: break; } } // Allow portals to work again - m_PortalCooldownData.m_ShouldPreventTeleportation = false; - m_PortalCooldownData.m_TicksDelayed = 0; + m_PortalCooldownData = { 0, false, true }; return false; } @@ -1547,99 +1501,6 @@ bool cEntity::DetectPortal() -bool cEntity::DoMoveToWorld(cWorld * a_World, bool a_ShouldSendRespawn, Vector3d a_NewPosition) -{ - UNUSED(a_ShouldSendRespawn); - ASSERT(a_World != nullptr); - ASSERT(IsTicking()); - - if (GetWorld() == a_World) - { - // Don't move to same world - return false; - } - - // Ask the plugins if the entity is allowed to changing the world - if (cRoot::Get()->GetPluginManager()->CallHookEntityChangingWorld(*this, *a_World)) - { - // A Plugin doesn't allow the entity to changing the world - return false; - } - - // Stop ticking, in preperation for detaching from this world. - SetIsTicking(false); - - // Tell others we are gone - GetWorld()->BroadcastDestroyEntity(*this); - - // Set position to the new position - SetPosition(a_NewPosition); - - // Stop all mobs from targeting this entity - // Stop this entity from targeting other mobs - if (this->IsMob()) - { - cMonster * Monster = static_cast<cMonster*>(this); - Monster->SetTarget(nullptr); - Monster->StopEveryoneFromTargetingMe(); - } - - // Queue add to new world and removal from the old one - cWorld * OldWorld = GetWorld(); - cChunk * ParentChunk = GetParentChunk(); - SetWorld(a_World); // Chunks may be streamed before cWorld::AddPlayer() sets the world to the new value - OldWorld->QueueTask([this, ParentChunk, a_World](cWorld & a_OldWorld) - { - LOGD("Warping entity #%i (%s) from world \"%s\" to \"%s\". Source chunk: (%d, %d) ", - this->GetUniqueID(), this->GetClass(), - a_OldWorld.GetName().c_str(), a_World->GetName().c_str(), - ParentChunk->GetPosX(), ParentChunk->GetPosZ() - ); - ParentChunk->RemoveEntity(this); - a_World->AddEntity(this); - cRoot::Get()->GetPluginManager()->CallHookEntityChangedWorld(*this, a_OldWorld); - }); - return true; -} - - - - - -bool cEntity::MoveToWorld(cWorld * a_World, bool a_ShouldSendRespawn, Vector3d a_NewPosition) -{ - return DoMoveToWorld(a_World, a_ShouldSendRespawn, a_NewPosition); -} - - - - - -bool cEntity::MoveToWorld(cWorld * a_World, bool a_ShouldSendRespawn) -{ - return MoveToWorld(a_World, a_ShouldSendRespawn, Vector3d(a_World->GetSpawnX(), a_World->GetSpawnY(), a_World->GetSpawnZ())); -} - - - - - -bool cEntity::MoveToWorld(const AString & a_WorldName, bool a_ShouldSendRespawn) -{ - cWorld * World = cRoot::Get()->GetWorld(a_WorldName); - if (World == nullptr) - { - LOG("%s: Couldn't find world \"%s\".", __FUNCTION__, a_WorldName.c_str()); - return false; - } - - return DoMoveToWorld(World, a_ShouldSendRespawn, Vector3d(World->GetSpawnX(), World->GetSpawnY(), World->GetSpawnZ())); -} - - - - - void cEntity::SetSwimState(cChunk & a_Chunk) { int RelY = FloorC(GetPosY() + 0.1); @@ -1836,19 +1697,19 @@ void cEntity::StopBurning(void) void cEntity::TeleportToEntity(cEntity & a_Entity) { - TeleportToCoords(a_Entity.GetPosX(), a_Entity.GetPosY(), a_Entity.GetPosZ()); + TeleportToCoords(a_Entity.GetPosition()); } -void cEntity::TeleportToCoords(double a_PosX, double a_PosY, double a_PosZ) +void cEntity::TeleportToCoords(const Vector3d & a_Position) { - // ask the plugins to allow teleport to the new position. - if (!cRoot::Get()->GetPluginManager()->CallHookEntityTeleport(*this, m_LastPosition, Vector3d(a_PosX, a_PosY, a_PosZ))) + // Ask the plugins to allow teleport to the new position. + if (!cRoot::Get()->GetPluginManager()->CallHookEntityTeleport(*this, m_LastPosition, a_Position)) { - SetPosition(a_PosX, a_PosY, a_PosZ); + SetPosition(a_Position); m_World->BroadcastTeleportEntity(*this); } } @@ -1857,6 +1718,91 @@ void cEntity::TeleportToCoords(double a_PosX, double a_PosY, double a_PosZ) +bool cEntity::MoveToWorld(cWorld & a_NewWorld, const Vector3d & a_NewPosition) +{ + auto PreviousDimension = GetWorld()->GetDimension(); + if (OnPreWorldTravel(a_NewWorld)) + { + OnPostWorldTravel(PreviousDimension, a_NewPosition); + SetPosition(a_NewPosition); // Just in case :) + return true; + } + return false; +} + + + + + +bool cEntity::MoveToWorld(const AString & a_WorldName, const Vector3d & a_NewPosition) +{ + auto World = cRoot::Get()->GetWorld(a_WorldName); + if (World == nullptr) + { + LOG("%s: Couldn't find world \"%s\".", __FUNCTION__, a_WorldName.c_str()); + return false; + } + + return MoveToWorld(*World, a_NewPosition); +} + + + + + +bool cEntity::OnPreWorldTravel(cWorld & a_NewWorld) +{ + ASSERT(IsTicking()); + + if (GetWorld() == &a_NewWorld) + { + // Don't move to same world + return false; + } + + // Ask the plugins if the player is allowed to changing the world + if (cRoot::Get()->GetPluginManager()->CallHookEntityChangingWorld(*this, a_NewWorld)) + { + // A Plugin doesn't allow the player to changing the world + return false; + } + + // Prevent further ticking in this world + SetIsTicking(false); + + auto Entity = GetParentChunk()->AcquireAssociatedEntityPtr(*this); + + // Broadcast for other people that the player is gone. + GetWorld()->BroadcastDestroyEntity(*this); + + cpp14::move_on_copy_wrapper<decltype(Entity)> EntityPtr(std::move(Entity)); + a_NewWorld.QueueTask( + [EntityPtr](cWorld & a_DestinationWorld) mutable + { + // Entity changed world, call the hook + cRoot::Get()->GetPluginManager()->CallHookEntityChangedWorld(*EntityPtr.value, *EntityPtr.value->GetWorld()); + + EntityPtr.value->Initialize(std::move(EntityPtr.value), a_DestinationWorld); + } + ); + + return true; +} + + + + + +void cEntity::OnPostWorldTravel(eDimension a_PreviousDimension, const Vector3d & a_RecommendedPosition) +{ + SetPosition(a_RecommendedPosition); + m_PortalCooldownData.m_PositionValid = true; +} + + + + + void cEntity::BroadcastMovementUpdate(const cClientHandle * a_Exclude) { // Process packet sending every two ticks |