From a431b45641d119a91a74cd88768389eebeb54a68 Mon Sep 17 00:00:00 2001 From: LogicParrot Date: Tue, 5 Apr 2016 11:45:09 +0300 Subject: Improved player freeze code --- src/Entities/Player.cpp | 105 ++++++++++++++++++++++++++++-------------------- src/Entities/Player.h | 11 +---- 2 files changed, 62 insertions(+), 54 deletions(-) (limited to 'src/Entities') diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp index f4e7eee44..83f134bf5 100644 --- a/src/Entities/Player.cpp +++ b/src/Entities/Player.cpp @@ -65,7 +65,6 @@ cPlayer::cPlayer(cClientHandlePtr a_Client, const AString & a_PlayerName) : m_GameMode(eGameMode_NotSet), m_IP(""), m_ClientHandle(a_Client), - m_FreezeCounter(-1), m_NormalMaxSpeed(1.0), m_SprintingMaxSpeed(1.3), m_FlyingMaxSpeed(1.0), @@ -119,7 +118,7 @@ cPlayer::cPlayer(cClientHandlePtr a_Client, const AString & a_PlayerName) : m_LastGroundHeight = static_cast(GetPosY()); m_Stance = GetPosY() + 1.62; - FreezeInternal(GetPosition(), false); // Freeze. Will be unfrozen once the chunk is loaded + if (m_GameMode == gmNotSet) { @@ -246,13 +245,16 @@ void cPlayer::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) ASSERT(!"Player ticked whilst in the process of destruction!"); } + m_Stats.AddValue(statMinutesPlayed, 1); // Handle a frozen player + TickFreezeCode(); if (m_IsFrozen) { return; } + ASSERT((GetParentChunk() != nullptr) && (GetParentChunk()->IsValid())); ASSERT(a_Chunk.IsValid()); @@ -314,18 +316,13 @@ void cPlayer::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) -void cPlayer::TickFreezeCode(bool a_MyChunkIsSent) +void cPlayer::TickFreezeCode() { - // This function is ticked by the player's client handle. This ensures it always ticks, even if the player - // is standing in an unloaded chunk, unlike cPlayer::Tick. We need this because the freeze handling code must - // also tick in unloaded chunks. if (m_IsFrozen) { - m_FreezeCounter += 1; - if ((!m_IsManuallyFrozen) && (a_MyChunkIsSent)) + if ((!m_IsManuallyFrozen) && (GetClientHandle()->IsPlayerChunkSent())) { - cWorld::cLock Lock(*GetWorld()); - // If the player was automatically frozen, unfreeze if the chunk the player is inside is loaded + // If the player was automatically frozen, unfreeze if the chunk the player is inside is loaded and sent Unfreeze(); // Pull the player out of any solids that might have loaded on them. @@ -357,26 +354,16 @@ void cPlayer::TickFreezeCode(bool a_MyChunkIsSent) } } } - else + else if (GetWorld()->GetWorldAge() % 4096 == 0) { - // If the player was externally / manually frozen (plugin, etc.) or if the chunk isn't loaded yet: - // 1. Set the location to m_FrozenPosition every tick. - // 2. Zero out the speed every tick. - // 3. Send location updates every 60 ticks. - - if ((m_FreezeCounter % 60 == 0) || ((m_FrozenPosition - GetPosition()).SqrLength() > 2 * 2)) - { - SetPosition(m_FrozenPosition); - SetSpeed(0, 0, 0); - BroadcastMovementUpdate(m_ClientHandle.get()); - m_ClientHandle->SendPlayerPosition(); - } - return; + // Despite the client side freeze, the player may be able to move a little by + // Jumping or canceling flight. Re-freeze every now and then + FreezeInternal(GetPosition(), m_IsManuallyFrozen); } } else { - if (!a_MyChunkIsSent) + if (!GetClientHandle()->IsPlayerChunkSent()) { FreezeInternal(GetPosition(), false); } @@ -772,8 +759,9 @@ double cPlayer::GetMaxSpeed(void) const void cPlayer::SetNormalMaxSpeed(double a_Speed) { m_NormalMaxSpeed = a_Speed; - if (!m_IsSprinting && !m_IsFlying) + if (!m_IsSprinting && !m_IsFlying && !m_IsFrozen) { + // If we are frozen, we do not send this yet. We send when unfreeze() is called m_ClientHandle->SendPlayerMaxSpeed(); } } @@ -785,8 +773,9 @@ void cPlayer::SetNormalMaxSpeed(double a_Speed) void cPlayer::SetSprintingMaxSpeed(double a_Speed) { m_SprintingMaxSpeed = a_Speed; - if (m_IsSprinting && !m_IsFlying) + if (m_IsSprinting && !m_IsFlying && !m_IsFrozen) { + // If we are frozen, we do not send this yet. We send when unfreeze() is called m_ClientHandle->SendPlayerMaxSpeed(); } } @@ -800,7 +789,11 @@ void cPlayer::SetFlyingMaxSpeed(double a_Speed) m_FlyingMaxSpeed = a_Speed; // Update the flying speed, always: - m_ClientHandle->SendPlayerAbilities(); + if (!m_IsFrozen) + { + // If we are frozen, we do not send this yet. We send when unfreeze() is called + m_ClientHandle->SendPlayerAbilities(); + } } @@ -907,7 +900,11 @@ void cPlayer::SetFlying(bool a_IsFlying) } m_IsFlying = a_IsFlying; - m_ClientHandle->SendPlayerAbilities(); + if (!m_IsFrozen) + { + // If we are frozen, we do not send this yet. We send when unfreeze() is called + m_ClientHandle->SendPlayerAbilities(); + } } @@ -1471,22 +1468,14 @@ bool cPlayer::IsFrozen() -int cPlayer::GetFrozenDuration() -{ - return m_FreezeCounter; -} - - - - - void cPlayer::Unfreeze() { - m_FreezeCounter = -1; + GetClientHandle()->SendPlayerAbilities(); + GetClientHandle()->SendPlayerMaxSpeed(); + m_IsFrozen = false; - SetPosition(m_FrozenPosition); - BroadcastMovementUpdate(m_ClientHandle.get()); - m_ClientHandle->SendPlayerPosition(); + BroadcastMovementUpdate(GetClientHandle()); + GetClientHandle()->SendPlayerPosition(); } @@ -1545,8 +1534,12 @@ void cPlayer::ForceSetSpeed(const Vector3d & a_Speed) void cPlayer::DoSetSpeed(double a_SpeedX, double a_SpeedY, double a_SpeedZ) { + if (m_IsFrozen) + { + // Do not set speed to a frozen client + return; + } super::DoSetSpeed(a_SpeedX, a_SpeedY, a_SpeedZ); - // Send the speed to the client so he actualy moves m_ClientHandle->SendEntityVelocity(*this); } @@ -1768,9 +1761,33 @@ void cPlayer::TossItems(const cItems & a_Items) void cPlayer::FreezeInternal(const Vector3d & a_Location, bool a_ManuallyFrozen) { + SetSpeed(0, 0, 0); + SetPosition(a_Location); m_IsFrozen = true; - m_FrozenPosition = a_Location; m_IsManuallyFrozen = a_ManuallyFrozen; + + double NormalMaxSpeed = GetNormalMaxSpeed(); + double SprintMaxSpeed = GetSprintingMaxSpeed(); + double FlyingMaxpeed = GetFlyingMaxSpeed(); + bool IsFlying = m_IsFlying; + + // Set the client-side speed to 0 + m_NormalMaxSpeed = 0; + m_SprintingMaxSpeed = 0; + m_FlyingMaxSpeed = 0; + m_IsFlying = true; + + // Send the client its fake speed and max speed of 0 + GetClientHandle()->SendPlayerMoveLook(); + GetClientHandle()->SendPlayerAbilities(); + GetClientHandle()->SendPlayerMaxSpeed(); + GetClientHandle()->SendEntityVelocity(*this); + + // Keep the server side speed variables as they were in the first place + m_NormalMaxSpeed = NormalMaxSpeed; + m_SprintingMaxSpeed = SprintMaxSpeed; + m_FlyingMaxSpeed = FlyingMaxpeed; + m_IsFlying = IsFlying; } diff --git a/src/Entities/Player.h b/src/Entities/Player.h index 777a533c9..f04bcdd39 100644 --- a/src/Entities/Player.h +++ b/src/Entities/Player.h @@ -50,7 +50,7 @@ public: virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; - void TickFreezeCode(bool a_MyChunkIsSent); + void TickFreezeCode(); virtual void HandlePhysics(std::chrono::milliseconds a_Dt, cChunk &) override { UNUSED(a_Dt); } @@ -148,9 +148,6 @@ public: /** Is the player frozen? */ bool IsFrozen(); - /** How long has the player been frozen? */ - int GetFrozenDuration(); - /** Cancels Freeze(...) and allows the player to move naturally. */ void Unfreeze(); @@ -605,15 +602,9 @@ protected: cSlotNums m_InventoryPaintSlots; - /** if m_IsFrozen is true, we lock m_Location to this position. */ - Vector3d m_FrozenPosition; - /** If true, we are locking m_Position to m_FrozenPosition. */ bool m_IsFrozen; - /** */ - int m_FreezeCounter; - /** Was the player frozen manually by a plugin or automatically by the server? */ bool m_IsManuallyFrozen; -- cgit v1.2.3