summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Bindings/CMakeLists.txt3
-rw-r--r--src/ClientHandle.cpp2
-rw-r--r--src/Entities/Boat.cpp2
-rw-r--r--src/Entities/Entity.cpp193
-rw-r--r--src/Entities/Entity.h73
-rw-r--r--src/Entities/ExpOrb.cpp6
-rw-r--r--src/Entities/FallingBlock.cpp8
-rw-r--r--src/Entities/Floater.cpp2
-rw-r--r--src/Entities/Pickup.cpp8
-rw-r--r--src/Entities/Player.cpp120
-rw-r--r--src/Entities/Player.h6
-rw-r--r--src/Entities/TNTEntity.cpp2
-rw-r--r--src/Entities/WitherSkullEntity.cpp2
-rw-r--r--src/Items/ItemFishingRod.h2
-rw-r--r--src/Mobs/Monster.cpp22
-rw-r--r--src/Mobs/Monster.h4
-rw-r--r--src/NetherPortalScanner.cpp2
-rw-r--r--src/OSSupport/AtomicUniquePtr.h81
-rw-r--r--src/OSSupport/CMakeLists.txt1
-rw-r--r--src/World.cpp36
-rw-r--r--src/World.h3
21 files changed, 366 insertions, 212 deletions
diff --git a/src/Bindings/CMakeLists.txt b/src/Bindings/CMakeLists.txt
index b1a19beca..17f2bdf39 100644
--- a/src/Bindings/CMakeLists.txt
+++ b/src/Bindings/CMakeLists.txt
@@ -164,7 +164,8 @@ set_source_files_properties(${BINDING_OUTPUTS} PROPERTIES GENERATED TRUE)
set_source_files_properties(${CMAKE_SOURCE_DIR}/src/Bindings/Bindings.cpp PROPERTIES COMPILE_FLAGS -Wno-error)
if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
- set_source_files_properties(Bindings.cpp PROPERTIES COMPILE_FLAGS "-Wno-old-style-cast -Wno-missing-prototypes")
+ set_source_files_properties(Bindings.cpp PROPERTIES COMPILE_FLAGS
+ "-Wno-old-style-cast -Wno-missing-prototypes -Wno-deprecated-declarations")
endif()
if(NOT MSVC)
diff --git a/src/ClientHandle.cpp b/src/ClientHandle.cpp
index 49a882c77..ce6d4c981 100644
--- a/src/ClientHandle.cpp
+++ b/src/ClientHandle.cpp
@@ -189,7 +189,7 @@ void cClientHandle::Destroy(void)
// If ownership was transferred, our own smart pointer should be unset
ASSERT(!m_PlayerPtr);
- m_PlayerPtr = world->RemovePlayer(*player, true);
+ m_PlayerPtr = world->RemovePlayer(*player);
// And RemovePlayer should have returned a valid smart pointer
ASSERT(m_PlayerPtr);
diff --git a/src/Entities/Boat.cpp b/src/Entities/Boat.cpp
index c05e67275..c88df4952 100644
--- a/src/Entities/Boat.cpp
+++ b/src/Entities/Boat.cpp
@@ -60,7 +60,7 @@ bool cBoat::DoTakeDamage(TakeDamageInfo & TDI)
m_World->SpawnItemPickups(Pickups, GetPosX(), GetPosY(), GetPosZ(), 0, 0, 0, true);
}
}
- Destroy(true);
+ Destroy();
}
return true;
}
diff --git a/src/Entities/Entity.cpp b/src/Entities/Entity.cpp
index 57a4680bd..44808e2a5 100644
--- a/src/Entities/Entity.cpp
+++ b/src/Entities/Entity.cpp
@@ -47,7 +47,6 @@ cEntity::cEntity(eEntityType a_EntityType, Vector3d a_Pos, double a_Width, doubl
m_LastPosition(a_Pos),
m_EntityType(a_EntityType),
m_World(nullptr),
- m_IsWorldChangeScheduled(false),
m_IsFireproof(false),
m_TicksSinceLastBurnDamage(0),
m_TicksSinceLastLavaDamage(0),
@@ -158,19 +157,29 @@ bool cEntity::Initialize(OwnedEntity a_Self, cWorld & a_EntityWorld)
cPluginManager::Get()->CallHookSpawnedEntity(a_EntityWorld, *this);
+ return true;
+}
+
+
+
+
+
+void cEntity::OnAddToWorld(cWorld & a_World)
+{
// Spawn the entity on the clients:
- a_EntityWorld.BroadcastSpawnEntity(*this);
+ m_LastSentPosition = GetPosition();
+ a_World.BroadcastSpawnEntity(*this);
+ BroadcastLeashedMobs();
+}
- // If has any mob leashed broadcast every leashed entity to this
- if (HasAnyMobLeashed())
- {
- for (auto LeashedMob : m_LeashedMobs)
- {
- m_World->BroadcastLeashEntity(*LeashedMob, *this);
- }
- }
- return true;
+
+
+
+void cEntity::OnRemoveFromWorld(cWorld & a_World)
+{
+ RemoveAllLeashedMobs();
+ a_World.BroadcastDestroyEntity(*this);
}
@@ -216,7 +225,7 @@ void cEntity::SetParentChunk(cChunk * a_Chunk)
-void cEntity::Destroy(bool a_ShouldBroadcast)
+void cEntity::Destroy()
{
SetIsTicking(false);
@@ -226,11 +235,6 @@ void cEntity::Destroy(bool a_ShouldBroadcast)
m_LeashedMobs.front()->Unleash(true, true);
}
- if (a_ShouldBroadcast)
- {
- m_World->BroadcastDestroyEntity(*this);
- }
-
auto ParentChunkCoords = cChunkDef::BlockToChunk(GetPosition());
m_World->QueueTask([this, ParentChunkCoords](cWorld & a_World)
{
@@ -1166,7 +1170,7 @@ void cEntity::ApplyFriction(Vector3d & a_Speed, double a_SlowdownMultiplier, flo
void cEntity::TickBurning(cChunk & a_Chunk)
{
// If we're about to change worlds, then we can't accurately determine whether we're in lava (#3939)
- if (m_IsWorldChangeScheduled)
+ if (IsWorldChangeScheduled())
{
return;
}
@@ -1310,34 +1314,11 @@ void cEntity::DetectCacti(void)
-void cEntity::ScheduleMoveToWorld(cWorld * a_World, Vector3d a_NewPosition, bool a_SetPortalCooldown, bool a_ShouldSendRespawn)
-{
- m_NewWorld = a_World;
- m_NewWorldPosition = a_NewPosition;
- m_IsWorldChangeScheduled = true;
- m_WorldChangeSetPortalCooldown = a_SetPortalCooldown;
- m_WorldChangeSendRespawn = a_ShouldSendRespawn;
-}
-
-
-
-
-
bool cEntity::DetectPortal()
{
- // If somebody scheduled a world change with ScheduleMoveToWorld, change worlds now.
- if (m_IsWorldChangeScheduled)
+ // If somebody scheduled a world change, do nothing.
+ if (IsWorldChangeScheduled())
{
- m_IsWorldChangeScheduled = false;
-
- if (m_WorldChangeSetPortalCooldown)
- {
- // Delay the portal check.
- m_PortalCooldownData.m_TicksDelayed = 0;
- m_PortalCooldownData.m_ShouldPreventTeleportation = true;
- }
-
- MoveToWorld(m_NewWorld, m_WorldChangeSendRespawn, m_NewWorldPosition);
return true;
}
@@ -1519,69 +1500,81 @@ bool cEntity::DetectPortal()
-bool cEntity::DoMoveToWorld(cWorld * a_World, bool a_ShouldSendRespawn, Vector3d a_NewPosition)
+void cEntity::DoMoveToWorld(const sWorldChangeInfo & a_WorldChangeInfo)
{
- UNUSED(a_ShouldSendRespawn);
- ASSERT(a_World != nullptr);
+ ASSERT(a_WorldChangeInfo.m_NewWorld != nullptr);
- if (GetWorld() == a_World)
+ if (a_WorldChangeInfo.m_SetPortalCooldown)
{
- // Don't move to same world
- return false;
+ m_PortalCooldownData.m_TicksDelayed = 0;
+ m_PortalCooldownData.m_ShouldPreventTeleportation = true;
}
- // Ask the plugins if the entity is allowed to changing the world
- if (cRoot::Get()->GetPluginManager()->CallHookEntityChangingWorld(*this, *a_World))
+ if (GetWorld() == a_WorldChangeInfo.m_NewWorld)
{
- // A Plugin doesn't allow the entity to changing the world
- return false;
+ // Moving to same world, don't need to remove from world
+ SetPosition(a_WorldChangeInfo.m_NewPosition);
+ return;
}
+ LOGD("Warping entity #%i (%s) from world \"%s\" to \"%s\". Source chunk: (%d, %d) ",
+ GetUniqueID(), GetClass(),
+ m_World->GetName(), a_WorldChangeInfo.m_NewWorld->GetName(),
+ GetChunkX(), GetChunkZ()
+ );
+
// Stop ticking, in preperation for detaching from this world.
SetIsTicking(false);
- // Tell others we are gone
- GetWorld()->BroadcastDestroyEntity(*this);
+ // Remove from the old world
+ auto Self = m_World->RemoveEntity(*this);
- // Take note of old chunk coords
- auto OldChunkCoords = cChunkDef::BlockToChunk(GetPosition());
+ // Update entity before calling hook
+ ResetPosition(a_WorldChangeInfo.m_NewPosition);
+ SetWorld(a_WorldChangeInfo.m_NewWorld);
- // Set position to the new position
- ResetPosition(a_NewPosition);
+ cRoot::Get()->GetPluginManager()->CallHookEntityChangedWorld(*this, *m_World);
- // 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();
- SetWorld(a_World); // Chunks may be streamed before cWorld::AddPlayer() sets the world to the new value
- OldWorld->QueueTask([this, OldChunkCoords, 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(),
- OldChunkCoords.m_ChunkX, OldChunkCoords.m_ChunkZ
- );
- UNUSED(OldChunkCoords); // Non Debug mode only
- a_World->AddEntity(a_OldWorld.RemoveEntity(*this));
- cRoot::Get()->GetPluginManager()->CallHookEntityChangedWorld(*this, a_OldWorld);
- });
- return true;
+ // Don't do anything after adding as the old world's CS no longer protects us
+ a_WorldChangeInfo.m_NewWorld->AddEntity(std::move(Self));
}
-bool cEntity::MoveToWorld(cWorld * a_World, bool a_ShouldSendRespawn, Vector3d a_NewPosition)
+bool cEntity::MoveToWorld(cWorld * a_World, Vector3d a_NewPosition, bool a_SetPortalCooldown, bool a_ShouldSendRespawn)
{
- return DoMoveToWorld(a_World, a_ShouldSendRespawn, a_NewPosition);
+ ASSERT(a_World != nullptr);
+
+ // Ask the plugins if the entity is allowed to change world
+ if (cRoot::Get()->GetPluginManager()->CallHookEntityChangingWorld(*this, *a_World))
+ {
+ // A Plugin isn't allowing the entity to change world
+ return false;
+ }
+
+ // Create new world change info
+ auto NewWCI = cpp14::make_unique<sWorldChangeInfo>();
+ *NewWCI = { a_World, a_NewPosition, a_SetPortalCooldown, a_ShouldSendRespawn };
+
+ // Publish atomically
+ auto OldWCI = m_WorldChangeInfo.exchange(std::move(NewWCI));
+
+ if (OldWCI == nullptr)
+ {
+ // Schedule a new world change.
+ GetWorld()->QueueTask(
+ [this](cWorld & a_CurWorld)
+ {
+ auto WCI = m_WorldChangeInfo.exchange(nullptr);
+ cWorld::cLock Lock(a_CurWorld);
+ DoMoveToWorld(*WCI);
+ }
+ );
+ }
+
+ return true;
}
@@ -1606,7 +1599,7 @@ bool cEntity::MoveToWorld(const AString & a_WorldName, bool a_ShouldSendRespawn)
return false;
}
- return DoMoveToWorld(World, a_ShouldSendRespawn, Vector3d(World->GetSpawnX(), World->GetSpawnY(), World->GetSpawnZ()));
+ return MoveToWorld(World, Vector3d(World->GetSpawnX(), World->GetSpawnY(), World->GetSpawnZ()), false, a_ShouldSendRespawn);
}
@@ -2253,6 +2246,34 @@ void cEntity::RemoveLeashedMob(cMonster * a_Monster)
+void cEntity::RemoveAllLeashedMobs()
+{
+ while (!m_LeashedMobs.empty())
+ {
+ m_LeashedMobs.front()->Unleash(false, true);
+ }
+}
+
+
+
+
+
+void cEntity::BroadcastLeashedMobs()
+{
+ // If has any mob leashed broadcast every leashed entity to this
+ if (HasAnyMobLeashed())
+ {
+ for (auto LeashedMob : m_LeashedMobs)
+ {
+ m_World->BroadcastLeashEntity(*LeashedMob, *this);
+ }
+ }
+}
+
+
+
+
+
float cEntity::GetExplosionExposureRate(Vector3d a_ExplosionPosition, float a_ExlosionPower)
{
double EntitySize = m_Width * m_Width * m_Height;
diff --git a/src/Entities/Entity.h b/src/Entities/Entity.h
index 2805ee9e0..b151f745d 100644
--- a/src/Entities/Entity.h
+++ b/src/Entities/Entity.h
@@ -2,6 +2,7 @@
#pragma once
#include "../Item.h"
+#include "../OSSupport/AtomicUniquePtr.h"
@@ -72,6 +73,16 @@ struct TakeDamageInfo
// tolua_begin
class cEntity
{
+protected:
+ /** State variables for MoveToWorld. */
+ struct sWorldChangeInfo
+ {
+ cWorld * m_NewWorld;
+ Vector3d m_NewPosition;
+ bool m_SetPortalCooldown;
+ bool m_SendRespawn;
+ };
+
public:
enum eEntityType
@@ -163,6 +174,16 @@ public:
Adds the entity to the world. */
virtual bool Initialize(OwnedEntity a_Self, cWorld & a_EntityWorld);
+ /** Called when the entity is added to a world.
+ e.g after first spawning or after successfuly moving between worlds.
+ \param a_World The world being added to. */
+ virtual void OnAddToWorld(cWorld & a_World);
+
+ /** Called when the entity is removed from a world.
+ e.g. When the entity is destroyed or moved to a different world.
+ \param a_World The world being removed from. */
+ virtual void OnRemoveFromWorld(cWorld & a_World);
+
// tolua_begin
eEntityType GetEntityType(void) const { return m_EntityType; }
@@ -268,8 +289,14 @@ public:
If this returns false, you must stop using the cEntity pointer you have. */
bool IsTicking(void) const;
- /** Destroys the entity and schedules it for memory freeing; if a_ShouldBroadcast is set to true, broadcasts the DestroyEntity packet */
- virtual void Destroy(bool a_ShouldBroadcast = true);
+ /** Destroys the entity, schedules it for memory freeing and broadcasts the DestroyEntity packet */
+ virtual void Destroy();
+
+ OBSOLETE void Destroy(bool a_ShouldBroadcast)
+ {
+ LOGWARNING("cEntity:Destory(bool) is deprecated, use cEntity:Destroy() instead.");
+ Destroy();
+ }
/** Makes this pawn take damage from an attack by a_Attacker. Damage values are calculated automatically and DoTakeDamage() called */
void TakeDamage(cEntity & a_Attacker);
@@ -442,9 +469,18 @@ public:
virtual void TeleportToCoords(double a_PosX, double a_PosY, double a_PosZ);
/** Schedules a MoveToWorld call to occur on the next Tick of the entity */
- void ScheduleMoveToWorld(cWorld * a_World, Vector3d a_NewPosition, bool a_ShouldSetPortalCooldown = false, bool a_ShouldSendRespawn = false);
+ OBSOLETE void ScheduleMoveToWorld(cWorld * a_World, Vector3d a_NewPosition, bool a_ShouldSetPortalCooldown = false, bool a_ShouldSendRespawn = false)
+ {
+ LOGWARNING("ScheduleMoveToWorld is deprecated, use MoveToWorld instead");
+ MoveToWorld(a_World, a_NewPosition, a_ShouldSetPortalCooldown, a_ShouldSendRespawn);
+ }
+
+ bool MoveToWorld(cWorld * a_World, Vector3d a_NewPosition, bool a_ShouldSetPortalCooldown = false, bool a_ShouldSendRespawn = false);
- bool MoveToWorld(cWorld * a_World, bool a_ShouldSendRespawn, Vector3d a_NewPosition);
+ bool MoveToWorld(cWorld * a_World, bool a_ShouldSendRespawn, Vector3d a_NewPosition)
+ {
+ return MoveToWorld(a_World, a_NewPosition, false, a_ShouldSendRespawn);
+ }
/** Moves entity to specified world, taking a world pointer */
bool MoveToWorld(cWorld * a_World, bool a_ShouldSendRespawn = true);
@@ -454,7 +490,11 @@ public:
// tolua_end
- virtual bool DoMoveToWorld(cWorld * a_World, bool a_ShouldSendRespawn, Vector3d a_NewPosition);
+ /** Returns true if a world change is scheduled to happen. */
+ bool IsWorldChangeScheduled() const
+ {
+ return (m_WorldChangeInfo.load() != nullptr);
+ }
/** Updates clients of changes in the entity. */
virtual void BroadcastMovementUpdate(const cClientHandle * a_Exclude = nullptr);
@@ -543,13 +583,16 @@ public:
/** Set the entity's status to either ticking or not ticking. */
void SetIsTicking(bool a_IsTicking);
- /** Adds a mob to the leashed list of mobs */
+ /** Adds a mob to the leashed list of mobs. */
void AddLeashedMob(cMonster * a_Monster);
- /** Removes a mob from the leashed list of mobs */
+ /** Removes a mob from the leashed list of mobs. */
void RemoveLeashedMob(cMonster * a_Monster);
- /** Returs whether the entity has any mob leashed to */
+ /** Removes all mobs from the leashed list of mobs. */
+ void RemoveAllLeashedMobs();
+
+ /** Returs whether the entity has any mob leashed to it. */
bool HasAnyMobLeashed() const { return m_LeashedMobs.size() > 0; }
/** a lightweight calculation approach to get explosion exposure rate
@@ -619,12 +662,8 @@ protected:
cWorld * m_World;
- /** State variables for ScheduleMoveToWorld. */
- bool m_IsWorldChangeScheduled;
- bool m_WorldChangeSetPortalCooldown;
- bool m_WorldChangeSendRespawn;
- cWorld * m_NewWorld;
- Vector3d m_NewWorldPosition;
+ /** If not nullptr, a world change is scheduled and a task is queued in the current world. */
+ cAtomicUniquePtr<sWorldChangeInfo> m_WorldChangeInfo;
/** Whether the entity is capable of taking fire or lava damage. */
bool m_IsFireproof;
@@ -671,6 +710,10 @@ protected:
overrides can provide further processing, such as forcing players to move at the given speed. */
virtual void DoSetSpeed(double a_SpeedX, double a_SpeedY, double a_SpeedZ);
+ /** Handles the moving of this entity between worlds.
+ Should handle degenerate cases such as moving to the same world. */
+ virtual void DoMoveToWorld(const sWorldChangeInfo & a_WorldChangeInfo);
+
virtual void Destroyed(void) {} // Called after the entity has been destroyed
/** Applies friction to an entity
@@ -689,6 +732,8 @@ protected:
Only to be used when the caller will broadcast a teleport or equivalent to clients. */
virtual void ResetPosition(Vector3d a_NewPos);
+ /** If has any mobs are leashed, broadcasts every leashed entity to this. */
+ void BroadcastLeashedMobs();
private:
diff --git a/src/Entities/ExpOrb.cpp b/src/Entities/ExpOrb.cpp
index ad6f6e97d..3d0c9e2b8 100644
--- a/src/Entities/ExpOrb.cpp
+++ b/src/Entities/ExpOrb.cpp
@@ -49,7 +49,7 @@ void cExpOrb::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
a_Player.DeltaExperience(m_Reward);
m_World->BroadcastSoundEffect("entity.experience_orb.pickup", GetPosition(), 0.5f, (0.75f + (static_cast<float>((GetUniqueID() * 23) % 32)) / 64));
- Destroy(true);
+ Destroy();
return true;
}
@@ -84,7 +84,7 @@ void cExpOrb::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
m_Timer += a_Dt;
if (m_Timer >= std::chrono::minutes(5))
{
- Destroy(true);
+ Destroy();
}
}
@@ -96,7 +96,7 @@ bool cExpOrb::DoTakeDamage(TakeDamageInfo & a_TDI)
{
if (a_TDI.DamageType == dtCactusContact)
{
- Destroy(true);
+ Destroy();
return true;
}
diff --git a/src/Entities/FallingBlock.cpp b/src/Entities/FallingBlock.cpp
index 55b9e81e1..132acd09a 100644
--- a/src/Entities/FallingBlock.cpp
+++ b/src/Entities/FallingBlock.cpp
@@ -46,7 +46,7 @@ void cFallingBlock::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
// Fallen out of this world, just continue falling until out of sight, then destroy:
if (BlockY < VOID_BOUNDARY)
{
- Destroy(true);
+ Destroy();
}
return;
}
@@ -64,7 +64,7 @@ void cFallingBlock::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
// Fallen onto a block that breaks this into pickups (e. g. half-slab)
// Must finish the fall with coords one below the block:
cSandSimulator::FinishFalling(m_World, BlockX, BlockY, BlockZ, m_BlockType, m_BlockMeta);
- Destroy(true);
+ Destroy();
return;
}
else if (!cSandSimulator::CanContinueFallThrough(BlockBelow))
@@ -83,14 +83,14 @@ void cFallingBlock::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
{
cSandSimulator::FinishFalling(m_World, BlockX, BlockY + 1, BlockZ, m_BlockType, m_BlockMeta);
}
- Destroy(true);
+ Destroy();
return;
}
else if ((m_BlockType == E_BLOCK_CONCRETE_POWDER) && IsBlockWater(BlockBelow))
{
// Concrete powder falling into water solidifies on the first water it touches
cSandSimulator::FinishFalling(m_World, BlockX, BlockY, BlockZ, E_BLOCK_CONCRETE, m_BlockMeta);
- Destroy(true);
+ Destroy();
return;
}
diff --git a/src/Entities/Floater.cpp b/src/Entities/Floater.cpp
index c9cc526a7..7b0314ae5 100644
--- a/src/Entities/Floater.cpp
+++ b/src/Entities/Floater.cpp
@@ -179,7 +179,7 @@ void cFloater::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
if (!m_World->DoWithEntityByID(m_PlayerID, [](cEntity &) { return true; })) // The owner doesn't exist anymore. Destroy the floater entity.
{
- Destroy(true);
+ Destroy();
}
if (m_AttachedMobID != cEntity::INVALID_ID)
diff --git a/src/Entities/Pickup.cpp b/src/Entities/Pickup.cpp
index be64e9e2f..679e9d5ff 100644
--- a/src/Entities/Pickup.cpp
+++ b/src/Entities/Pickup.cpp
@@ -156,7 +156,7 @@ void cPickup::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
m_Timer += a_Dt; // In case we have to destroy the pickup in the same tick.
if (m_Timer > std::chrono::milliseconds(500))
{
- Destroy(true);
+ Destroy();
return;
}
}
@@ -180,14 +180,14 @@ void cPickup::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
{
if (m_Timer > std::chrono::milliseconds(500)) // 0.5 second
{
- Destroy(true);
+ Destroy();
return;
}
}
if (m_Timer > m_Lifetime)
{
- Destroy(true);
+ Destroy();
return;
}
}
@@ -200,7 +200,7 @@ bool cPickup::DoTakeDamage(TakeDamageInfo & a_TDI)
{
if (a_TDI.DamageType == dtCactusContact)
{
- Destroy(true);
+ Destroy();
return true;
}
diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp
index e7b6ade15..421eddbd5 100644
--- a/src/Entities/Player.cpp
+++ b/src/Entities/Player.cpp
@@ -193,9 +193,6 @@ bool cPlayer::Initialize(OwnedEntity a_Self, cWorld & a_World)
cPluginManager::Get()->CallHookSpawnedEntity(*GetWorld(), *this);
- // Spawn the entity on the clients:
- GetWorld()->BroadcastSpawnEntity(*this);
-
return true;
}
@@ -243,6 +240,9 @@ void cPlayer::SpawnOn(cClientHandle & a_Client)
{
return;
}
+
+ LOGD("Spawing %s on %s", GetName().c_str(), a_Client.GetUsername().c_str());
+
a_Client.SendPlayerSpawn(*this);
a_Client.SendEntityHeadLook(*this);
a_Client.SendEntityEquipment(*this, 0, m_Inventory.GetEquippedItem());
@@ -1225,7 +1225,7 @@ void cPlayer::Respawn(void)
if (GetWorld() != m_SpawnWorld)
{
- ScheduleMoveToWorld(m_SpawnWorld, GetLastBedPos(), false);
+ MoveToWorld(m_SpawnWorld, GetLastBedPos(), false);
}
else
{
@@ -2003,92 +2003,70 @@ void cPlayer::TossItems(const cItems & a_Items)
-bool cPlayer::DoMoveToWorld(cWorld * a_World, bool a_ShouldSendRespawn, Vector3d a_NewPosition)
+void cPlayer::DoMoveToWorld(const cEntity::sWorldChangeInfo & a_WorldChangeInfo)
{
- ASSERT(a_World != nullptr);
- ASSERT(IsTicking());
+ ASSERT(a_WorldChangeInfo.m_NewWorld != nullptr);
- if (GetWorld() == a_World)
+ // Reset portal cooldown
+ if (a_WorldChangeInfo.m_SetPortalCooldown)
{
- // Don't move to same world
- return false;
+ m_PortalCooldownData.m_TicksDelayed = 0;
+ m_PortalCooldownData.m_ShouldPreventTeleportation = true;
}
- // Ask the plugins if the player is allowed to change the world
- if (cRoot::Get()->GetPluginManager()->CallHookEntityChangingWorld(*this, *a_World))
+ if (m_World == a_WorldChangeInfo.m_NewWorld)
{
- // A Plugin doesn't allow the player to change the world
- return false;
+ // Moving to same world, don't need to remove from world
+ SetPosition(a_WorldChangeInfo.m_NewPosition);
+ return;
}
- GetWorld()->QueueTask([this, a_World, a_ShouldSendRespawn, a_NewPosition](cWorld & a_OldWorld)
- {
- // The clienthandle caches the coords of the chunk we're standing at. Invalidate this.
- GetClientHandle()->InvalidateCachedSentChunk();
-
- // Prevent further ticking in this world
- SetIsTicking(false);
+ LOGD("Warping player \"%s\" from world \"%s\" to \"%s\". Source chunk: (%d, %d) ",
+ GetName(), GetWorld()->GetName(), a_WorldChangeInfo.m_NewWorld->GetName(),
+ GetChunkX(), GetChunkZ()
+ );
- // Tell others we are gone
- GetWorld()->BroadcastDestroyEntity(*this);
+ // Stop all mobs from targeting this player
+ StopEveryoneFromTargetingMe();
- // Remove player from world
- // Make sure that RemovePlayer didn't return a valid smart pointer, due to the second parameter being false
- // We remain valid and not destructed after this call
- VERIFY(!GetWorld()->RemovePlayer(*this, false));
+ // Prevent further ticking in this world
+ SetIsTicking(false);
- // Set position to the new position
- ResetPosition(a_NewPosition);
- FreezeInternal(a_NewPosition, false);
+ // Remove from the old world
+ auto & OldWorld = *GetWorld();
+ auto Self = OldWorld.RemovePlayer(*this);
- // Stop all mobs from targeting this player
- StopEveryoneFromTargetingMe();
+ ResetPosition(a_WorldChangeInfo.m_NewPosition);
+ FreezeInternal(a_WorldChangeInfo.m_NewPosition, false);
+ SetWorld(a_WorldChangeInfo.m_NewWorld); // Chunks may be streamed before cWorld::AddPlayer() sets the world to the new value
- // Deal with new world
- SetWorld(a_World);
+ // Set capabilities based on new world
+ SetCapabilities();
- // Set capabilities based on new world
- SetCapabilities();
+ cClientHandle * ch = GetClientHandle();
+ if (ch != nullptr)
+ {
+ // The clienthandle caches the coords of the chunk we're standing at. Invalidate this.
+ ch->InvalidateCachedSentChunk();
- cClientHandle * ch = this->GetClientHandle();
- if (ch != nullptr)
+ // Send the respawn packet:
+ if (a_WorldChangeInfo.m_SendRespawn)
{
- // Send the respawn packet:
- if (a_ShouldSendRespawn)
- {
- m_ClientHandle->SendRespawn(a_World->GetDimension());
- }
-
- // Update the view distance.
- ch->SetViewDistance(m_ClientHandle->GetRequestedViewDistance());
-
- // Send current weather of target world to player
- if (a_World->GetDimension() == dimOverworld)
- {
- ch->SendWeather(a_World->GetWeather());
- }
+ ch->SendRespawn(a_WorldChangeInfo.m_NewWorld->GetDimension());
}
- // Broadcast the player into the new world.
- a_World->BroadcastSpawnEntity(*this);
-
- // Queue add to new world and removal from the old one
+ // Update the view distance.
+ ch->SetViewDistance(ch->GetRequestedViewDistance());
- // Chunks may be streamed before cWorld::AddPlayer() sets the world to the new value
- cChunk * ParentChunk = this->GetParentChunk();
-
- LOGD("Warping player \"%s\" from world \"%s\" to \"%s\". Source chunk: (%d, %d) ",
- this->GetName().c_str(),
- a_OldWorld.GetName().c_str(), a_World->GetName().c_str(),
- ParentChunk->GetPosX(), ParentChunk->GetPosZ()
- );
-
- // New world will take over and announce client at its next tick
- auto PlayerPtr = static_cast<cPlayer *>(ParentChunk->RemoveEntity(*this).release());
- a_World->AddPlayer(std::unique_ptr<cPlayer>(PlayerPtr), &a_OldWorld);
- });
+ // Send current weather of target world to player
+ if (a_WorldChangeInfo.m_NewWorld->GetDimension() == dimOverworld)
+ {
+ ch->SendWeather(a_WorldChangeInfo.m_NewWorld->GetWeather());
+ }
+ }
- return true;
+ // New world will take over and announce client at its next tick
+ a_WorldChangeInfo.m_NewWorld->AddPlayer(std::move(Self), &OldWorld);
}
@@ -2515,7 +2493,7 @@ void cPlayer::HandleFloater()
}
m_World->DoWithEntityByID(m_FloaterID, [](cEntity & a_Entity)
{
- a_Entity.Destroy(true);
+ a_Entity.Destroy();
return true;
}
);
diff --git a/src/Entities/Player.h b/src/Entities/Player.h
index c9249b2f1..1ce2c5d9d 100644
--- a/src/Entities/Player.h
+++ b/src/Entities/Player.h
@@ -387,10 +387,6 @@ public:
void SetVisible( bool a_bVisible); // tolua_export
bool IsVisible(void) const { return m_bVisible; } // tolua_export
- /** Moves the player to the specified world.
- Returns true if successful, false on failure (world not found). */
- virtual bool DoMoveToWorld(cWorld * a_World, bool a_ShouldSendRespawn, Vector3d a_NewPosition) override;
-
/** Saves all player data, such as inventory, to JSON */
bool SaveToDisk(void);
@@ -735,6 +731,8 @@ protected:
/** The main hand of the player */
eMainHand m_MainHand;
+ virtual void DoMoveToWorld(const cEntity::sWorldChangeInfo & a_WorldChangeInfo) override;
+
/** Sets the speed and sends it to the client, so that they are forced to move so. */
virtual void DoSetSpeed(double a_SpeedX, double a_SpeedY, double a_SpeedZ) override;
diff --git a/src/Entities/TNTEntity.cpp b/src/Entities/TNTEntity.cpp
index c76b09119..fa17296cf 100644
--- a/src/Entities/TNTEntity.cpp
+++ b/src/Entities/TNTEntity.cpp
@@ -34,7 +34,7 @@ void cTNTEntity::SpawnOn(cClientHandle & a_ClientHandle)
void cTNTEntity::Explode(void)
{
m_FuseTicks = 0;
- Destroy(true);
+ Destroy();
FLOGD("BOOM at {0}", GetPosition());
m_World->DoExplosionAt(4.0, GetPosX() + 0.49, GetPosY() + 0.49, GetPosZ() + 0.49, true, esPrimedTNT, this);
}
diff --git a/src/Entities/WitherSkullEntity.cpp b/src/Entities/WitherSkullEntity.cpp
index 65e055e59..55f1ff32f 100644
--- a/src/Entities/WitherSkullEntity.cpp
+++ b/src/Entities/WitherSkullEntity.cpp
@@ -43,7 +43,7 @@ void cWitherSkullEntity::OnHitEntity(cEntity & a_EntityHit, Vector3d a_HitPos)
// TODO: Explode
// TODO: Apply wither effect to entity and others nearby
- Destroy(true);
+ Destroy();
}
diff --git a/src/Items/ItemFishingRod.h b/src/Items/ItemFishingRod.h
index 60e0617d6..2d42411e3 100644
--- a/src/Items/ItemFishingRod.h
+++ b/src/Items/ItemFishingRod.h
@@ -28,7 +28,7 @@ public:
m_Pos = Floater.GetPosition();
m_BitePos = Floater.GetBitePos();
m_AttachedMobID = Floater.GetAttachedMobID();
- Floater.Destroy(true);
+ Floater.Destroy();
return true;
}
diff --git a/src/Mobs/Monster.cpp b/src/Mobs/Monster.cpp
index dfcd0dd6a..09f937564 100644
--- a/src/Mobs/Monster.cpp
+++ b/src/Mobs/Monster.cpp
@@ -132,12 +132,12 @@ cMonster::~cMonster()
-void cMonster::Destroy(bool a_ShouldBroadcast)
+void cMonster::OnRemoveFromWorld(cWorld & a_World)
{
if (IsLeashed())
{
cEntity * LeashedTo = GetLeashedTo();
- Unleash(false, a_ShouldBroadcast);
+ Unleash(false, true);
// Remove leash knot if there are no more mobs leashed to
if (!LeashedTo->HasAnyMobLeashed() && LeashedTo->IsLeashKnot())
@@ -146,7 +146,7 @@ void cMonster::Destroy(bool a_ShouldBroadcast)
}
}
- super::Destroy(a_ShouldBroadcast);
+ super::OnRemoveFromWorld(a_World);
}
@@ -282,7 +282,7 @@ void cMonster::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
m_DestroyTimer += a_Dt;
if (m_DestroyTimer > std::chrono::seconds(1))
{
- Destroy(true);
+ Destroy();
}
return;
}
@@ -590,6 +590,20 @@ bool cMonster::DoTakeDamage(TakeDamageInfo & a_TDI)
+void cMonster::DoMoveToWorld(const cEntity::sWorldChangeInfo & a_WorldChangeInfo)
+{
+ // Stop all mobs from targeting this entity
+ // Stop this entity from targeting other mobs
+ SetTarget(nullptr);
+ StopEveryoneFromTargetingMe();
+
+ super::DoMoveToWorld(a_WorldChangeInfo);
+}
+
+
+
+
+
void cMonster::KilledBy(TakeDamageInfo & a_TDI)
{
super::KilledBy(a_TDI);
diff --git a/src/Mobs/Monster.h b/src/Mobs/Monster.h
index 11d49b82e..676e8ebe5 100644
--- a/src/Mobs/Monster.h
+++ b/src/Mobs/Monster.h
@@ -43,7 +43,7 @@ public:
virtual ~cMonster() override;
- virtual void Destroy(bool a_ShouldBroadcast = true) override;
+ virtual void OnRemoveFromWorld(cWorld & a_World) override;
virtual void Destroyed() override;
@@ -319,6 +319,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);
+ virtual void DoMoveToWorld(const cEntity::sWorldChangeInfo & a_WorldChangeInfo) override;
+
private:
/** A pointer to the entity this mobile is aiming to reach.
The validity of this pointer SHALL be guaranteed by the pointee;
diff --git a/src/NetherPortalScanner.cpp b/src/NetherPortalScanner.cpp
index 9625ed7cf..78cdb2f02 100644
--- a/src/NetherPortalScanner.cpp
+++ b/src/NetherPortalScanner.cpp
@@ -296,7 +296,7 @@ void cNetherPortalScanner::OnDisabled(void)
}
FLOGD("Placing player at {0}", Position);
- m_Entity->ScheduleMoveToWorld(m_World, Position, true);
+ m_Entity->MoveToWorld(m_World, Position, true);
delete this;
}
diff --git a/src/OSSupport/AtomicUniquePtr.h b/src/OSSupport/AtomicUniquePtr.h
new file mode 100644
index 000000000..5b18763d3
--- /dev/null
+++ b/src/OSSupport/AtomicUniquePtr.h
@@ -0,0 +1,81 @@
+
+
+#pragma once
+
+
+/** An RAII wrapper for std::atomic<T*>. */
+template <typename T>
+class cAtomicUniquePtr
+{
+public:
+ static_assert(!std::is_array<T>::value, "cAtomicUniquePtr does not support arrays");
+ DISALLOW_COPY_AND_ASSIGN(cAtomicUniquePtr);
+
+ cAtomicUniquePtr() NOEXCEPT:
+ m_Ptr(nullptr)
+ {
+ }
+
+
+ cAtomicUniquePtr(std::unique_ptr<T> a_Ptr) NOEXCEPT:
+ m_Ptr(a_Ptr.release())
+ {
+ }
+
+ cAtomicUniquePtr & operator = (std::unique_ptr<T> a_Ptr) NOEXCEPT
+ {
+ store(std::move(a_Ptr));
+ return *this;
+ }
+
+ ~cAtomicUniquePtr() NOEXCEPT
+ {
+ delete load();
+ }
+
+ operator T * () const NOEXCEPT
+ {
+ return load();
+ }
+
+ bool compare_exchange_weak(T *& a_Expected, std::unique_ptr<T> && a_Desired, std::memory_order a_Order = std::memory_order_seq_cst) NOEXCEPT
+ {
+ bool DidExchange = m_Ptr.compare_exchange_weak(a_Expected, a_Desired.get(), a_Order);
+ if (DidExchange)
+ {
+ // Only release ownership from the caller if the exchange occurred
+ a_Desired.release();
+ }
+ return DidExchange;
+ }
+
+ bool compare_exchange_strong(T *& a_Expected, std::unique_ptr<T> && a_Desired, std::memory_order a_Order = std::memory_order_seq_cst) NOEXCEPT
+ {
+ bool DidExchange = m_Ptr.compare_exchange_strong(a_Expected, a_Desired.get(), a_Order);
+ if (DidExchange)
+ {
+ // Only release ownership from the caller if the exchange occurred
+ a_Desired.release();
+ }
+ return DidExchange;
+ }
+
+ std::unique_ptr<T> exchange(std::unique_ptr<T> a_Ptr, std::memory_order a_Order = std::memory_order_seq_cst) NOEXCEPT
+ {
+ return std::unique_ptr<T>{ m_Ptr.exchange(a_Ptr.release(), a_Order) };
+ }
+
+ T * load(std::memory_order a_Order = std::memory_order_seq_cst) const NOEXCEPT
+ {
+ return m_Ptr.load(a_Order);
+ }
+
+ void store(std::unique_ptr<T> a_Ptr, std::memory_order a_Order = std::memory_order_seq_cst) NOEXCEPT
+ {
+ // Store new value and delete old value
+ delete m_Ptr.exchange(a_Ptr.release(), a_Order);
+ }
+
+private:
+ std::atomic<T*> m_Ptr;
+};
diff --git a/src/OSSupport/CMakeLists.txt b/src/OSSupport/CMakeLists.txt
index 332b880ed..9f3fcb8a0 100644
--- a/src/OSSupport/CMakeLists.txt
+++ b/src/OSSupport/CMakeLists.txt
@@ -19,6 +19,7 @@ SET (SRCS
)
SET (HDRS
+ AtomicUniquePtr.h
CriticalSection.h
Errors.h
Event.h
diff --git a/src/World.cpp b/src/World.cpp
index 312f5967a..a80cffbcc 100644
--- a/src/World.cpp
+++ b/src/World.cpp
@@ -1024,6 +1024,7 @@ void cWorld::Tick(std::chrono::milliseconds a_Dt, std::chrono::milliseconds a_La
Entity->SetWorld(this);
auto EntityPtr = Entity.get();
m_ChunkMap->AddEntity(std::move(Entity));
+ EntityPtr->OnAddToWorld(*this);
ASSERT(!EntityPtr->IsTicking());
EntityPtr->SetIsTicking(true);
}
@@ -1167,14 +1168,14 @@ void cWorld::TickMobs(std::chrono::milliseconds a_Dt)
{
if (Monster.GetMobType() != eMonsterType::mtWolf)
{
- Monster.Destroy(true);
+ Monster.Destroy();
}
else
{
auto & Wolf = static_cast<cWolf &>(Monster);
if (!Wolf.IsAngry() && !Wolf.IsTame())
{
- Monster.Destroy(true);
+ Monster.Destroy();
}
}
}
@@ -2454,23 +2455,34 @@ void cWorld::AddPlayer(std::unique_ptr<cPlayer> a_Player, cWorld * a_OldWorld)
-std::unique_ptr<cPlayer> cWorld::RemovePlayer(cPlayer & a_Player, bool a_RemoveFromChunk)
+std::unique_ptr<cPlayer> cWorld::RemovePlayer(cPlayer & a_Player)
{
- std::unique_ptr<cPlayer> PlayerPtr;
+ // Check the chunkmap
+ std::unique_ptr<cPlayer> PlayerPtr(static_cast<cPlayer *>(m_ChunkMap->RemoveEntity(a_Player).release()));
- if (a_RemoveFromChunk)
+ if (PlayerPtr != nullptr)
{
- // To prevent iterator invalidations when an entity goes through a portal and calls this function whilst being ticked by cChunk
- // we should not change cChunk's entity list if asked not to
- PlayerPtr = std::unique_ptr<cPlayer>(static_cast<cPlayer *>(m_ChunkMap->RemoveEntity(a_Player).release()));
+ // Player found in the world, tell it it's being removed
+ PlayerPtr->OnRemoveFromWorld(*this);
}
+ else // Check the awaiting players list
{
cCSLock Lock(m_CSPlayersToAdd);
- m_PlayersToAdd.remove_if([&](const decltype(m_PlayersToAdd)::value_type & value) -> bool
+ auto itr = std::find_if(m_PlayersToAdd.begin(), m_PlayersToAdd.end(),
+ [&](const decltype(m_PlayersToAdd)::value_type & value)
+ {
+ return (value.first.get() == &a_Player);
+ }
+ );
+
+ if (itr != m_PlayersToAdd.end())
{
- return (value.first.get() == &a_Player);
- });
+ PlayerPtr = std::move(itr->first);
+ m_PlayersToAdd.erase(itr);
+ }
}
+
+ // Remove from the player list
{
cCSLock Lock(m_CSPlayers);
LOGD("Removing player %s from world \"%s\"", a_Player.GetName().c_str(), m_WorldName.c_str());
@@ -3076,6 +3088,7 @@ OwnedEntity cWorld::RemoveEntity(cEntity & a_Entity)
auto Entity = m_ChunkMap->RemoveEntity(a_Entity);
if (Entity != nullptr)
{
+ Entity->OnRemoveFromWorld(*this);
return Entity;
}
@@ -3475,6 +3488,7 @@ void cWorld::AddQueuedPlayers(void)
// Add to chunkmap, if not already there (Spawn vs MoveToWorld):
auto PlayerPtr = Player.get();
m_ChunkMap->AddEntityIfNotPresent(std::move(Player));
+ PlayerPtr->OnAddToWorld(*this);
ASSERT(!PlayerPtr->IsTicking());
PlayerPtr->SetIsTicking(true);
AddedPlayerPtrs.emplace_back(PlayerPtr, AwaitingPlayer.second);
diff --git a/src/World.h b/src/World.h
index 159fa7d93..4363804c6 100644
--- a/src/World.h
+++ b/src/World.h
@@ -263,9 +263,8 @@ public:
/** Removes the player from the world.
Removes the player from the addition queue, too, if appropriate.
If the player has a ClientHandle, the ClientHandle is removed from all chunks in the world and will not be ticked by this world anymore.
- @param a_RemoveFromChunk determines if the entity should be removed from its chunk as well. Should be false when ticking from cChunk.
@return An owning reference to the given player. */
- std::unique_ptr<cPlayer> RemovePlayer(cPlayer & a_Player, bool a_RemoveFromChunk);
+ std::unique_ptr<cPlayer> RemovePlayer(cPlayer & a_Player);
#ifdef _DEBUG
bool IsPlayerReferencedInWorldOrChunk(cPlayer & a_Player);