From 1394fc8eb5c8c0ac49bb64ce1871a80470f058c3 Mon Sep 17 00:00:00 2001 From: Tiger Wang Date: Tue, 6 Apr 2021 12:26:43 +0100 Subject: Streamline player abilities handling * Update player list gamemode on world change * Fix invisibility for spectators, use entity metadata * Populate m_World for cPlayers on load - Remove SendPlayerMaxSpeed, a duplicate of SendEntityProperties --- src/Entities/Entity.cpp | 2 +- src/Entities/Player.cpp | 158 ++++++++++++++++++++---------------------------- src/Entities/Player.h | 16 +++-- 3 files changed, 73 insertions(+), 103 deletions(-) (limited to 'src/Entities') diff --git a/src/Entities/Entity.cpp b/src/Entities/Entity.cpp index 9fc3f80cf..6c777aca4 100644 --- a/src/Entities/Entity.cpp +++ b/src/Entities/Entity.cpp @@ -122,7 +122,7 @@ bool cEntity::Initialize(OwnedEntity a_Self, cWorld & a_EntityWorld) */ - ASSERT(m_World == nullptr); + ASSERT(a_Self->IsPlayer() || (m_World == nullptr)); // Players' worlds are loaded from disk. ASSERT(GetParentChunk() == nullptr); SetWorld(&a_EntityWorld); a_EntityWorld.AddEntity(std::move(a_Self)); diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp index 91882ad6a..9725118ee 100644 --- a/src/Entities/Player.cpp +++ b/src/Entities/Player.cpp @@ -144,30 +144,10 @@ cPlayer::cPlayer(const std::shared_ptr & a_Client) : SetMaxHealth(MAX_HEALTH); m_Health = MAX_HEALTH; - cWorld * World = nullptr; - LoadFromDisk(World); + LoadFromDisk(); + UpdateCapabilities(); m_LastGroundHeight = static_cast(GetPosY()); - - if (m_GameMode == gmNotSet) - { - if (World->IsGameModeCreative()) - { - m_IsFlightCapable = true; - } - if (World->IsGameModeSpectator()) // Otherwise Player will fall out of the world on join - { - m_IsFlightCapable = true; - m_IsFlying = true; - } - } - - if (m_GameMode == gmSpectator) // If player is reconnecting to the server in spectator mode - { - m_IsFlightCapable = true; - m_IsFlying = true; - m_IsVisible = false; - } } @@ -614,10 +594,11 @@ double cPlayer::GetMaxSpeed(void) const void cPlayer::SetNormalMaxSpeed(double a_Speed) { m_NormalMaxSpeed = a_Speed; - if (!IsSprinting() && !IsFlying() && !IsFrozen()) + + if (!m_IsFrozen) { // If we are frozen, we do not send this yet. We send when unfreeze() is called - m_ClientHandle->SendPlayerMaxSpeed(); + m_World->BroadcastEntityProperties(*this); } } @@ -628,10 +609,11 @@ void cPlayer::SetNormalMaxSpeed(double a_Speed) void cPlayer::SetSprintingMaxSpeed(double a_Speed) { m_SprintingMaxSpeed = a_Speed; - if (IsSprinting() && !m_IsFlying && !m_IsFrozen) + + if (!m_IsFrozen) { // If we are frozen, we do not send this yet. We send when unfreeze() is called - m_ClientHandle->SendPlayerMaxSpeed(); + m_World->BroadcastEntityProperties(*this); } } @@ -643,7 +625,6 @@ void cPlayer::SetFlyingMaxSpeed(double a_Speed) { m_FlyingMaxSpeed = a_Speed; - // Update the flying speed, always: if (!m_IsFrozen) { // If we are frozen, we do not send this yet. We send when unfreeze() is called @@ -723,6 +704,7 @@ void cPlayer::SetSprint(const bool a_ShouldSprint) } m_World->BroadcastEntityMetadata(*this); + m_World->BroadcastEntityProperties(*this); } @@ -1325,49 +1307,38 @@ void cPlayer::SetGameMode(eGameMode a_GameMode) } m_GameMode = a_GameMode; - m_ClientHandle->SendGameMode(a_GameMode); - - SetCapabilities(); + UpdateCapabilities(); + m_ClientHandle->SendGameMode(a_GameMode); + m_ClientHandle->SendInventorySlot(-1, -1, m_DraggingItem); m_World->BroadcastPlayerListUpdateGameMode(*this); + m_World->BroadcastEntityMetadata(*this); } -void cPlayer::SetCapabilities() +void cPlayer::UpdateCapabilities() { - // Fly ability + // Fly ability: if (IsGameModeCreative() || IsGameModeSpectator()) { - SetCanFly(true); + m_IsFlightCapable = true; } else { - SetFlying(false); - SetCanFly(false); + m_IsFlying = false; + m_IsFlightCapable = false; } - // Visible - if (IsGameModeSpectator()) - { - SetVisible(false); - } - else - { - SetVisible(true); - } + // Visible: + m_IsVisible = !IsGameModeSpectator(); - // Set for spectator + // Clear the current dragging item of spectators: if (IsGameModeSpectator()) { - // Clear the current dragging item of the player - if (GetWindow() != nullptr) - { - m_DraggingItem.Empty(); - GetClientHandle()->SendInventorySlot(-1, -1, m_DraggingItem); - } + m_DraggingItem.Empty(); } } @@ -1449,8 +1420,8 @@ void cPlayer::Unfreeze() m_World->BroadcastEntityMetadata(*this); } - GetClientHandle()->SendPlayerAbilities(); - GetClientHandle()->SendPlayerMaxSpeed(); + m_ClientHandle->SendPlayerAbilities(); + m_World->BroadcastEntityProperties(*this); m_IsFrozen = false; BroadcastMovementUpdate(m_ClientHandle.get()); @@ -1521,21 +1492,7 @@ Vector3d cPlayer::GetThrowSpeed(double a_SpeedCoeff) const eGameMode cPlayer::GetEffectiveGameMode(void) const { - // Since entities' m_World aren't set until Initialize, but cClientHandle sends the player's gamemode early - // the below block deals with m_World being nullptr when called. - - auto World = m_World; - - if (World == nullptr) - { - World = cRoot::Get()->GetDefaultWorld(); - } - else if (IsWorldChangeScheduled()) - { - World = m_WorldChangeInfo.m_NewWorld; - } - - return (m_GameMode == gmNotSet) ? World->GetGameMode() : m_GameMode; + return (m_GameMode == gmNotSet) ? m_World->GetGameMode() : m_GameMode; } @@ -1553,17 +1510,16 @@ void cPlayer::ForceSetSpeed(const Vector3d & a_Speed) void cPlayer::SetVisible(bool a_bVisible) { - // Need to Check if the player or other players are in gamemode spectator, but will break compatibility - if (a_bVisible && !m_IsVisible) // Make visible + if (a_bVisible && !m_IsVisible) { m_IsVisible = true; - m_World->BroadcastSpawnEntity(*this); } if (!a_bVisible && m_IsVisible) { m_IsVisible = false; - m_World->BroadcastDestroyEntity(*this, m_ClientHandle.get()); // Destroy on all clients } + + m_World->BroadcastEntityMetadata(*this); } @@ -1806,25 +1762,25 @@ void cPlayer::TossPickup(const cItem & a_Item) -void cPlayer::LoadFromDisk(cWorldPtr & a_World) +void cPlayer::LoadFromDisk() { LoadRank(); const auto & UUID = GetUUID(); // Load from the UUID file: - if (LoadFromFile(GetUUIDFileName(UUID), a_World)) + if (LoadFromFile(GetUUIDFileName(UUID))) { return; } // Player not found: - a_World = cRoot::Get()->GetDefaultWorld(); + m_World = cRoot::Get()->GetDefaultWorld(); LOG("Player \"%s\" (%s) data not found, resetting to defaults", GetName().c_str(), UUID.ToShortString().c_str()); - const Vector3i WorldSpawn(static_cast(a_World->GetSpawnX()), static_cast(a_World->GetSpawnY()), static_cast(a_World->GetSpawnZ())); + const Vector3i WorldSpawn(static_cast(m_World->GetSpawnX()), static_cast(m_World->GetSpawnY()), static_cast(m_World->GetSpawnZ())); SetPosition(WorldSpawn); - SetBedPos(WorldSpawn, a_World); + SetBedPos(WorldSpawn, m_World); m_Inventory.Clear(); m_EnchantmentSeed = GetRandomProvider().RandInt(); // Use a random number to seed the enchantment generator @@ -1836,7 +1792,7 @@ void cPlayer::LoadFromDisk(cWorldPtr & a_World) -bool cPlayer::LoadFromFile(const AString & a_FileName, cWorldPtr & a_World) +bool cPlayer::LoadFromFile(const AString & a_FileName) { Json::Value Root; @@ -1925,16 +1881,15 @@ bool cPlayer::LoadFromFile(const AString & a_FileName, cWorldPtr & a_World) cEnderChestEntity::LoadFromJson(Root["enderchestinventory"], m_EnderChestContents); m_CurrentWorldName = Root.get("world", "world").asString(); - a_World = cRoot::Get()->GetWorld(GetLoadedWorldName()); - if (a_World == nullptr) + m_World = cRoot::Get()->GetWorld(m_CurrentWorldName); + if (m_World == nullptr) { - a_World = cRoot::Get()->GetDefaultWorld(); + m_World = cRoot::Get()->GetDefaultWorld(); } - - m_LastBedPos.x = Root.get("SpawnX", a_World->GetSpawnX()).asInt(); - m_LastBedPos.y = Root.get("SpawnY", a_World->GetSpawnY()).asInt(); - m_LastBedPos.z = Root.get("SpawnZ", a_World->GetSpawnZ()).asInt(); + m_LastBedPos.x = Root.get("SpawnX", m_World->GetSpawnX()).asInt(); + m_LastBedPos.y = Root.get("SpawnY", m_World->GetSpawnY()).asInt(); + m_LastBedPos.z = Root.get("SpawnZ", m_World->GetSpawnZ()).asInt(); m_SpawnWorldName = Root.get("SpawnWorld", cRoot::Get()->GetDefaultWorld()->GetName()).asString(); try @@ -1949,7 +1904,7 @@ bool cPlayer::LoadFromFile(const AString & a_FileName, cWorldPtr & a_World) } FLOGD("Player {0} was read from file \"{1}\", spawning at {2:.2f} in world \"{3}\"", - GetName(), a_FileName, GetPosition(), a_World->GetName() + GetName(), a_FileName, GetPosition(), m_World->GetName() ); return true; @@ -2689,10 +2644,10 @@ void cPlayer::FreezeInternal(const Vector3d & a_Location, bool a_ManuallyFrozen) m_IsFlying = true; // Send the client its fake speed and max speed of 0 - GetClientHandle()->SendPlayerMoveLook(); - GetClientHandle()->SendPlayerAbilities(); - GetClientHandle()->SendPlayerMaxSpeed(); - GetClientHandle()->SendEntityVelocity(*this); + m_ClientHandle->SendPlayerMoveLook(); + m_ClientHandle->SendPlayerAbilities(); + m_ClientHandle->SendEntityVelocity(*this); + m_World->BroadcastEntityProperties(*this); // Keep the server side speed variables as they were in the first place m_NormalMaxSpeed = NormalMaxSpeed; @@ -3050,6 +3005,15 @@ float cPlayer::GetEnchantmentBlastKnockbackReduction() +bool cPlayer::IsInvisible() const +{ + return !m_IsVisible || Super::IsInvisible(); +} + + + + + bool cPlayer::IsCrouched(void) const { return std::holds_alternative(m_BodyStance); @@ -3079,6 +3043,7 @@ bool cPlayer::IsSprinting(void) const void cPlayer::OnAddToWorld(cWorld & a_World) { + // Sends player spawn: Super::OnAddToWorld(a_World); // Update world name tracking: @@ -3087,8 +3052,10 @@ void cPlayer::OnAddToWorld(cWorld & a_World) // Fix to stop the player falling through the world, until we get serversided collision detection: FreezeInternal(GetPosition(), false); - // Set capabilities based on new world: - SetCapabilities(); + // UpdateCapabilities was called in the constructor, and in OnRemoveFromWorld, possibly changing our visibility. + // If world is in spectator mode, invisibility will need updating. If we just connected, we might be on fire from a previous game. + // Hence, tell the client by sending metadata: + m_ClientHandle->SendEntityMetadata(*this); // Send contents of the inventory window: m_ClientHandle->SendWholeInventory(*m_CurrentWindow); @@ -3168,8 +3135,13 @@ void cPlayer::OnRemoveFromWorld(cWorld & a_World) AwardAchievement(Statistic::AchPortal); } + // Set capabilities based on new world: + UpdateCapabilities(); + // Clientside warp start: m_ClientHandle->SendRespawn(DestinationDimension, false); + m_ClientHandle->SendPlayerListUpdateGameMode(*this); + m_World->BroadcastPlayerListUpdateGameMode(*this); // Clear sent chunk lists from the clienthandle: m_ClientHandle->RemoveFromWorld(); @@ -3184,7 +3156,7 @@ void cPlayer::OnRemoveFromWorld(cWorld & a_World) void cPlayer::SpawnOn(cClientHandle & a_Client) { - if (!m_IsVisible || (m_ClientHandle.get() == (&a_Client))) + if (m_ClientHandle.get() == &a_Client) { return; } diff --git a/src/Entities/Player.h b/src/Entities/Player.h index 40797fa34..fd4d22d84 100644 --- a/src/Entities/Player.h +++ b/src/Entities/Player.h @@ -163,8 +163,8 @@ public: virtual void TeleportToCoords(double a_PosX, double a_PosY, double a_PosZ) override; - // Updates player's capabilities - flying, visibility, etc. from their gamemode. - void SetCapabilities(); + /** Updates player's capabilities - flying, visibility, etc. from their gamemode. */ + void UpdateCapabilities(); // tolua_begin @@ -412,21 +412,18 @@ public: void Respawn(void); // tolua_export void SetVisible( bool a_bVisible); // tolua_export - bool IsVisible(void) const { return m_IsVisible; } // tolua_export /** Saves all player data, such as inventory, to JSON. */ void SaveToDisk(void); - typedef cWorld * cWorldPtr; - /** Loads the player data from the disk file. - Sets a_World to the world where the player will spawn, based on the stored world name or the default world by calling LoadFromFile(). */ - void LoadFromDisk(cWorldPtr & a_World); + Sets m_World to the world where the player will spawn, based on the stored world name or the default world by calling LoadFromFile(). */ + void LoadFromDisk(); /** Loads the player data from the specified file. - Sets a_World to the world where the player will spawn, based on the stored world name or the default world. + Sets m_World to the world where the player will spawn, based on the stored world name or the default world. Returns true on success, false if the player wasn't found, and excepts with base std::runtime_error if the data couldn't be read or parsed. */ - bool LoadFromFile(const AString & a_FileName, cWorldPtr & a_World); + bool LoadFromFile(const AString & a_FileName); const AString & GetLoadedWorldName() const { return m_CurrentWorldName; } @@ -805,6 +802,7 @@ private: virtual bool DoTakeDamage(TakeDamageInfo & TDI) override; virtual float GetEnchantmentBlastKnockbackReduction() override; virtual void HandlePhysics(std::chrono::milliseconds a_Dt, cChunk &) override { UNUSED(a_Dt); } + virtual bool IsInvisible() const override; virtual bool IsRclking(void) const override { return IsEating() || IsChargingBow(); } virtual void OnAddToWorld(cWorld & a_World) override; virtual void OnRemoveFromWorld(cWorld & a_World) override; -- cgit v1.2.3