Stabilise MoveToWorld (#4004)
* Stabilise MoveToWorld * Fix comments and deprecate ScheduleMoveToWorld * Enhanced thread safety for m_WorldChangeInfo * Return unique_ptr from cAtomicUniquePtr::exchange * cWorld now calls entity cEntity::OnAddToWorld and cEntity::OnRemoveFromWorld. Allows broadcasting entities added to the world from the world's tick thread. This also factors out some common code from cEntity::DoMoveToWorld and cEntity::Initialize. As a consequence, cEntity::Destroy(false) (i.e. Destroying the entity without broadcasting) is impossible. This isn't used anywhere in Cuberite so it's now deprecated. * Update entity position after removing it from the world. Fixes broadcasts being sent to the wrong chunk. * Fix style * cEntity: Update LastSentPosition when sending spawn packet * Add Wno-deprecated-declarations to the lua bindings * Kill uses of ScheduleMoveToWorld
This commit is contained in:
@@ -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)
|
||||
LOGD("Warping player \"%s\" from world \"%s\" to \"%s\". Source chunk: (%d, %d) ",
|
||||
GetName(), GetWorld()->GetName(), a_WorldChangeInfo.m_NewWorld->GetName(),
|
||||
GetChunkX(), GetChunkZ()
|
||||
);
|
||||
|
||||
// Stop all mobs from targeting this player
|
||||
StopEveryoneFromTargetingMe();
|
||||
|
||||
// Prevent further ticking in this world
|
||||
SetIsTicking(false);
|
||||
|
||||
// Remove from the old world
|
||||
auto & OldWorld = *GetWorld();
|
||||
auto Self = OldWorld.RemovePlayer(*this);
|
||||
|
||||
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
|
||||
|
||||
// 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.
|
||||
GetClientHandle()->InvalidateCachedSentChunk();
|
||||
ch->InvalidateCachedSentChunk();
|
||||
|
||||
// Prevent further ticking in this world
|
||||
SetIsTicking(false);
|
||||
|
||||
// Tell others we are gone
|
||||
GetWorld()->BroadcastDestroyEntity(*this);
|
||||
|
||||
// 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));
|
||||
|
||||
// Set position to the new position
|
||||
ResetPosition(a_NewPosition);
|
||||
FreezeInternal(a_NewPosition, false);
|
||||
|
||||
// Stop all mobs from targeting this player
|
||||
StopEveryoneFromTargetingMe();
|
||||
|
||||
// Deal with new world
|
||||
SetWorld(a_World);
|
||||
|
||||
// Set capabilities based on new world
|
||||
SetCapabilities();
|
||||
|
||||
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);
|
||||
// Update the view distance.
|
||||
ch->SetViewDistance(ch->GetRequestedViewDistance());
|
||||
|
||||
// Queue add to new world and removal from the old one
|
||||
// Send current weather of target world to player
|
||||
if (a_WorldChangeInfo.m_NewWorld->GetDimension() == dimOverworld)
|
||||
{
|
||||
ch->SendWeather(a_WorldChangeInfo.m_NewWorld->GetWeather());
|
||||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
});
|
||||
|
||||
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;
|
||||
}
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user