From 7d4934534e9c58a111215859ba83c32a9bc0fa8a Mon Sep 17 00:00:00 2001 From: Mat Date: Thu, 5 Mar 2020 12:52:34 +0200 Subject: 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 --- src/Entities/Player.cpp | 120 ++++++++++++++++++++---------------------------- 1 file changed, 49 insertions(+), 71 deletions(-) (limited to 'src/Entities/Player.cpp') 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(ParentChunk->RemoveEntity(*this).release()); - a_World->AddPlayer(std::unique_ptr(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; } ); -- cgit v1.2.3