From de2cce9b79ab60427daa86dcd1da99db7d7ea161 Mon Sep 17 00:00:00 2001 From: Tiger Wang Date: Tue, 21 Jul 2015 21:25:37 +0100 Subject: [PATCH] Fixed food drain bugs --- src/ClientHandle.cpp | 40 ++---- src/ClientHandle.h | 9 +- src/Color.h | 2 +- src/Entities/Entity.cpp | 108 ++------------- src/Entities/Entity.h | 44 +++--- src/Entities/Player.cpp | 256 +++++++++++++---------------------- src/Entities/Player.h | 15 +- src/Protocol/Protocol18x.cpp | 5 +- 8 files changed, 162 insertions(+), 317 deletions(-) diff --git a/src/ClientHandle.cpp b/src/ClientHandle.cpp index a0fd6cc10..679dd6a46 100644 --- a/src/ClientHandle.cpp +++ b/src/ClientHandle.cpp @@ -20,7 +20,6 @@ #include "ChatColor.h" #include "Items/ItemHandler.h" #include "Blocks/BlockHandler.h" -#include "Blocks/BlockSlab.h" #include "Blocks/BlockBed.h" #include "Blocks/ChunkInterface.h" #include "BlockInServerPluginInterface.h" @@ -760,40 +759,31 @@ void cClientHandle::HandlePlayerPos(double a_PosX, double a_PosY, double a_PosZ, return; } - /* - // TODO: Invalid stance check - if ((a_PosY >= a_Stance) || (a_Stance > a_PosY + 1.65)) - { - LOGD("Invalid stance"); - SendPlayerMoveLook(); - return; - } - */ + Vector3d NewPosition(a_PosX, a_PosY, a_PosZ); + Vector3d OldPosition = GetPlayer()->GetPosition(); + auto PreviousIsOnGround = GetPlayer()->IsOnGround(); // If the player has moved too far, "repair" them: - Vector3d Pos(a_PosX, a_PosY, a_PosZ); - if ((m_Player->GetPosition() - Pos).SqrLength() > 100 * 100) + if ((OldPosition - NewPosition).SqrLength() > 100 * 100) { - LOGD("Too far away (%0.2f), \"repairing\" the client", (m_Player->GetPosition() - Pos).Length()); + LOGD("Too far away (%0.2f), \"repairing\" the client", (OldPosition - NewPosition).Length()); SendPlayerMoveLook(); return; } - - // If a jump just started, process food exhaustion: - if ((a_PosY > m_Player->GetPosY()) && !a_IsOnGround && m_Player->IsOnGround()) - { - // we only add this exhaustion if the player is not swimming - otherwise we end up with both jump + swim exhaustion - if (!m_Player->IsSwimming()) - { - m_Player->GetStatManager().AddValue(statJumps, 1); - m_Player->AddFoodExhaustion(m_Player->IsSprinting() ? 0.8 : 0.2); - } + if (cRoot::Get()->GetPluginManager()->CallHookPlayerMoving(*m_Player, OldPosition, NewPosition)) + { + SendPlayerMoveLook(); + return; } + + // TODO: should do some checks to see if player is not moving through terrain + // TODO: Official server refuses position packets too far away from each other, kicking "hacked" clients; we should, too - m_Player->MoveTo(Pos); + m_Player->SetPosition(NewPosition); m_Player->SetStance(a_Stance); m_Player->SetTouchGround(a_IsOnGround); + m_Player->UpdateMovementStats(NewPosition - OldPosition, PreviousIsOnGround); } @@ -1501,8 +1491,8 @@ void cClientHandle::HandlePlayerLook(float a_Rotation, float a_Pitch, bool a_IsO void cClientHandle::HandlePlayerMoveLook(double a_PosX, double a_PosY, double a_PosZ, double a_Stance, float a_Rotation, float a_Pitch, bool a_IsOnGround) { - HandlePlayerLook(a_Rotation, a_Pitch, a_IsOnGround); HandlePlayerPos(a_PosX, a_PosY, a_PosZ, a_Stance, a_IsOnGround); + HandlePlayerLook(a_Rotation, a_Pitch, a_IsOnGround); } diff --git a/src/ClientHandle.h b/src/ClientHandle.h index a347732cd..b305648cb 100644 --- a/src/ClientHandle.h +++ b/src/ClientHandle.h @@ -321,7 +321,14 @@ public: // tolua_export void HandlePlayerAbilities (bool a_CanFly, bool a_IsFlying, float FlyingSpeed, float WalkingSpeed); void HandlePlayerLook (float a_Rotation, float a_Pitch, bool a_IsOnGround); void HandlePlayerMoveLook (double a_PosX, double a_PosY, double a_PosZ, double a_Stance, float a_Rotation, float a_Pitch, bool a_IsOnGround); // While m_bPositionConfirmed (normal gameplay) - void HandlePlayerPos (double a_PosX, double a_PosY, double a_PosZ, double a_Stance, bool a_IsOnGround); + + /** Verifies and sets player position, performing relevant checks + Calls relevant methods to process movement related statistics + Requires state of previous position and on-ground status, so must be called when these are still intact + */ + void HandlePlayerPos(double a_PosX, double a_PosY, double a_PosZ, double a_Stance, bool a_IsOnGround); + + void HandlePluginMessage (const AString & a_Channel, const AString & a_Message); void HandleRespawn (void); void HandleRightClick (int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, const cItem & a_HeldItem); diff --git a/src/Color.h b/src/Color.h index 69e6a3f60..57775b0dd 100644 --- a/src/Color.h +++ b/src/Color.h @@ -15,7 +15,7 @@ class cColor { public: - enum + enum : unsigned int { COLOR_MIN = 0, COLOR_MAX = 255, diff --git a/src/Entities/Entity.cpp b/src/Entities/Entity.cpp index 4bad6b94c..a70ac6d1a 100644 --- a/src/Entities/Entity.cpp +++ b/src/Entities/Entity.cpp @@ -38,7 +38,7 @@ cEntity::cEntity(eEntityType a_EntityType, double a_X, double a_Y, double a_Z, d m_bOnGround(false), m_Gravity(-9.81f), m_AirDrag(0.02f), - m_LastPos(a_X, a_Y, a_Z), + m_LastPosition(a_X, a_Y, a_Z), m_IsInitialized(false), m_WorldTravellingFrom(nullptr), m_EntityType(a_EntityType), @@ -57,7 +57,8 @@ cEntity::cEntity(eEntityType a_EntityType, double a_X, double a_Y, double a_Z, d m_TicksAlive(0), m_HeadYaw(0.0), m_Rot(0.0, 0.0, 0.0), - m_Pos(a_X, a_Y, a_Z), + m_Position(a_X, a_Y, a_Z), + m_LastSentPosition(a_X, a_Y, a_Z), m_WaterSpeed(0, 0, 0), m_Mass (0.001), // Default 1g m_Width(a_Width), @@ -793,17 +794,7 @@ void cEntity::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) if (m_AttachedTo != nullptr) { - Vector3d DeltaPos = m_Pos - m_AttachedTo->GetPosition(); - if (DeltaPos.Length() > 0.5) - { - SetPosition(m_AttachedTo->GetPosition()); - - if (IsPlayer()) - { - cPlayer * Player = reinterpret_cast(this); - Player->UpdateMovementStats(DeltaPos); - } - } + SetPosition(m_AttachedTo->GetPosition()); } else { @@ -1692,7 +1683,7 @@ void cEntity::TeleportToEntity(cEntity & a_Entity) void cEntity::TeleportToCoords(double a_PosX, double a_PosY, double a_PosZ) { // ask the plugins to allow teleport to the new position. - if (!cRoot::Get()->GetPluginManager()->CallHookEntityTeleport(*this, m_LastPos, Vector3d(a_PosX, a_PosY, a_PosZ))) + if (!cRoot::Get()->GetPluginManager()->CallHookEntityTeleport(*this, m_LastPosition, Vector3d(a_PosX, a_PosY, a_PosZ))) { SetPosition(a_PosX, a_PosY, a_PosZ); m_World->BroadcastTeleportEntity(*this); @@ -1727,9 +1718,9 @@ void cEntity::BroadcastMovementUpdate(const cClientHandle * a_Exclude) } // TODO: Pickups move disgracefully if relative move packets are sent as opposed to just velocity. Have a system to send relmove only when SetPosXXX() is called with a large difference in position - int DiffX = FloorC(GetPosX() * 32.0) - FloorC(m_LastPos.x * 32.0); - int DiffY = FloorC(GetPosY() * 32.0) - FloorC(m_LastPos.y * 32.0); - int DiffZ = FloorC(GetPosZ() * 32.0) - FloorC(m_LastPos.z * 32.0); + int DiffX = FloorC(GetPosX() * 32.0) - FloorC(m_LastSentPosition.x * 32.0); + int DiffY = FloorC(GetPosY() * 32.0) - FloorC(m_LastSentPosition.y * 32.0); + int DiffZ = FloorC(GetPosZ() * 32.0) - FloorC(m_LastSentPosition.z * 32.0); if ((DiffX != 0) || (DiffY != 0) || (DiffZ != 0)) // Have we moved? { @@ -1746,14 +1737,14 @@ void cEntity::BroadcastMovementUpdate(const cClientHandle * a_Exclude) m_World->BroadcastEntityRelMove(*this, static_cast(DiffX), static_cast(DiffY), static_cast(DiffZ), a_Exclude); } // Clients seem to store two positions, one for the velocity packet and one for the teleport / relmove packet - // The latter is only changed with a relmove / teleport, and m_LastPos stores this position - m_LastPos = GetPosition(); + // The latter is only changed with a relmove / teleport, and m_LastSentPosition stores this position + m_LastSentPosition = GetPosition(); } else { // Too big a movement, do a teleport m_World->BroadcastTeleportEntity(*this, a_Exclude); - m_LastPos = GetPosition(); // See above + m_LastSentPosition = GetPosition(); // See above m_bDirtyOrientation = false; } } @@ -1824,16 +1815,6 @@ bool cEntity::IsA(const char * a_ClassName) const -void cEntity::SetRot(const Vector3f & a_Rot) -{ - m_Rot = a_Rot; - m_bDirtyOrientation = true; -} - - - - - void cEntity::SetHeadYaw(double a_HeadYaw) { m_HeadYaw = a_HeadYaw; @@ -1939,41 +1920,6 @@ void cEntity::SetWidth(double a_Width) - -void cEntity::AddPosX(double a_AddPosX) -{ - m_Pos.x += a_AddPosX; -} - - - - -void cEntity::AddPosY(double a_AddPosY) -{ - m_Pos.y += a_AddPosY; -} - - - - -void cEntity::AddPosZ(double a_AddPosZ) -{ - m_Pos.z += a_AddPosZ; -} - - - - -void cEntity::AddPosition(double a_AddPosX, double a_AddPosY, double a_AddPosZ) -{ - m_Pos.x += a_AddPosX; - m_Pos.y += a_AddPosY; - m_Pos.z += a_AddPosZ; -} - - - - void cEntity::AddSpeed(double a_AddSpeedX, double a_AddSpeedY, double a_AddSpeedZ) { DoSetSpeed(m_Speed.x + a_AddSpeedX, m_Speed.y + a_AddSpeedY, m_Speed.z + a_AddSpeedZ); @@ -2055,36 +2001,10 @@ Vector3d cEntity::GetLookVector(void) const //////////////////////////////////////////////////////////////////////////////// // Set position -void cEntity::SetPosition(double a_PosX, double a_PosY, double a_PosZ) +void cEntity::SetPosition(const Vector3d & a_Position) { - m_Pos.Set(a_PosX, a_PosY, a_PosZ); -} - - - - - -void cEntity::SetPosX(double a_PosX) -{ - m_Pos.x = a_PosX; -} - - - - - -void cEntity::SetPosY(double a_PosY) -{ - m_Pos.y = a_PosY; -} - - - - - -void cEntity::SetPosZ(double a_PosZ) -{ - m_Pos.z = a_PosZ; + m_LastPosition = m_Position; + m_Position = a_Position; } diff --git a/src/Entities/Entity.h b/src/Entities/Entity.h index 6024a5de5..0161db387 100644 --- a/src/Entities/Entity.h +++ b/src/Entities/Entity.h @@ -30,7 +30,7 @@ #define POSX_TOINT FloorC(GetPosX()) #define POSY_TOINT FloorC(GetPosY()) #define POSZ_TOINT FloorC(GetPosZ()) -#define POS_TOINT Vector3i(POSXTOINT, POSYTOINT, POSZTOINT) +#define POS_TOINT GetPosition().Floor() #define GET_AND_VERIFY_CURRENT_CHUNK(ChunkVarName, X, Z) cChunk * ChunkVarName = a_Chunk.GetNeighborChunk(X, Z); if ((ChunkVarName == nullptr) || !ChunkVarName->IsValid()) { return; } @@ -190,11 +190,10 @@ public: double GetHeadYaw (void) const { return m_HeadYaw; } // In degrees double GetHeight (void) const { return m_Height; } double GetMass (void) const { return m_Mass; } - const Vector3d & GetPosition (void) const { return m_Pos; } - double GetPosX (void) const { return m_Pos.x; } - double GetPosY (void) const { return m_Pos.y; } - double GetPosZ (void) const { return m_Pos.z; } - const Vector3d & GetRot (void) const { return m_Rot; } // OBSOLETE, use individual GetYaw(), GetPitch, GetRoll() components + const Vector3d & GetPosition (void) const { return m_Position; } + double GetPosX (void) const { return m_Position.x; } + double GetPosY (void) const { return m_Position.y; } + double GetPosZ (void) const { return m_Position.z; } double GetYaw (void) const { return m_Rot.x; } // In degrees, [-180, +180) double GetPitch (void) const { return m_Rot.y; } // In degrees, [-180, +180), but normal client clips to [-90, +90] double GetRoll (void) const { return m_Rot.z; } // In degrees, unused in current client @@ -205,18 +204,17 @@ public: double GetSpeedZ (void) const { return m_Speed.z; } double GetWidth (void) const { return m_Width; } - int GetChunkX(void) const {return static_cast(floor(m_Pos.x / cChunkDef::Width)); } - int GetChunkZ(void) const {return static_cast(floor(m_Pos.z / cChunkDef::Width)); } + int GetChunkX(void) const { return FloorC(m_Position.x / cChunkDef::Width); } + int GetChunkZ(void) const { return FloorC(m_Position.z / cChunkDef::Width); } void SetHeadYaw (double a_HeadYaw); void SetHeight (double a_Height); void SetMass (double a_Mass); - void SetPosX (double a_PosX); - void SetPosY (double a_PosY); - void SetPosZ (double a_PosZ); - void SetPosition(double a_PosX, double a_PosY, double a_PosZ); - void SetPosition(const Vector3d & a_Pos) { SetPosition(a_Pos.x, a_Pos.y, a_Pos.z); } - void SetRot (const Vector3f & a_Rot); // OBSOLETE, use individual SetYaw(), SetPitch(), SetRoll() components + void SetPosX (double a_PosX) { SetPosition({a_PosX, m_Position.y, m_Position.z}); } + void SetPosY (double a_PosY) { SetPosition({m_Position.x, a_PosY, m_Position.z}); } + void SetPosZ (double a_PosZ) { SetPosition({m_Position.x, m_Position.y, a_PosZ}); } + void SetPosition(double a_PosX, double a_PosY, double a_PosZ) { SetPosition({a_PosX, a_PosY, a_PosZ}); } + void SetPosition(const Vector3d & a_Position); void SetYaw (double a_Yaw); // In degrees, normalizes to [-180, +180) void SetPitch (double a_Pitch); // In degrees, normalizes to [-180, +180) void SetRoll (double a_Roll); // In degrees, normalizes to [-180, +180) @@ -238,10 +236,10 @@ public: void SetWidth (double a_Width); - void AddPosX (double a_AddPosX); - void AddPosY (double a_AddPosY); - void AddPosZ (double a_AddPosZ); - void AddPosition(double a_AddPosX, double a_AddPosY, double a_AddPosZ); + void AddPosX (double a_AddPosX) { AddPosition(a_AddPosX, 0, 0); } + void AddPosY (double a_AddPosY) { AddPosition(0, a_AddPosY, 0); } + void AddPosZ (double a_AddPosZ) { AddPosition(0, 0, a_AddPosZ); } + void AddPosition(double a_AddPosX, double a_AddPosY, double a_AddPosZ) { SetPosition(m_Position + Vector3d(a_AddPosX, a_AddPosY, a_AddPosZ)); } void AddPosition(const Vector3d & a_AddPos) { AddPosition(a_AddPos.x, a_AddPos.y, a_AddPos.z); } void AddSpeed (double a_AddSpeedX, double a_AddSpeedY, double a_AddSpeedZ); void AddSpeed (const Vector3d & a_AddSpeed) { AddSpeed(a_AddSpeed.x, a_AddSpeed.y, a_AddSpeed.z); } @@ -531,9 +529,7 @@ protected: Data: http://minecraft.gamepedia.com/Entity#Motion_of_entities */ float m_AirDrag; - /** Last position sent to client via the Relative Move or Teleport packets (not Velocity) - Only updated if cEntity::BroadcastMovementUpdate() is called! */ - Vector3d m_LastPos; + Vector3d m_LastPosition; /** True when entity is initialised (Initialize()) and false when destroyed pending deletion (Destroy()) */ bool m_IsInitialized; @@ -610,7 +606,11 @@ private: Vector3d m_Rot; /** Position of the entity's XZ center and Y bottom */ - Vector3d m_Pos; + Vector3d m_Position; + + /** Last position sent to client via the Relative Move or Teleport packets (not Velocity) + Only updated if cEntity::BroadcastMovementUpdate() is called! */ + Vector3d m_LastSentPosition; /** Measured in meter / second */ Vector3d m_WaterSpeed; diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp index 40754f6d9..b8673420d 100644 --- a/src/Entities/Player.cpp +++ b/src/Entities/Player.cpp @@ -55,7 +55,6 @@ cPlayer::cPlayer(cClientHandlePtr a_Client, const AString & a_PlayerName) : m_FoodSaturationLevel(5.0), m_FoodTickTimer(0), m_FoodExhaustionLevel(0.0), - m_LastJumpHeight(0), m_LastGroundHeight(0), m_bTouchGround(false), m_Stance(0.0), @@ -113,7 +112,6 @@ cPlayer::cPlayer(cClientHandlePtr a_Client, const AString & a_PlayerName) : ); } - m_LastJumpHeight = static_cast(GetPosY()); m_LastGroundHeight = static_cast(GetPosY()); m_Stance = GetPosY() + 1.62; @@ -244,23 +242,7 @@ void cPlayer::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) SendExperience(); } - bool CanMove = true; - if (!GetPosition().EqualsEps(m_LastPos, 0.02)) // Non negligible change in position from last tick? 0.02 tp prevent continous calling while floating sometimes. - { - // Apply food exhaustion from movement: - ApplyFoodExhaustionFromMovement(); - - if (cRoot::Get()->GetPluginManager()->CallHookPlayerMoving(*this, m_LastPos, GetPosition())) - { - CanMove = false; - TeleportToCoords(m_LastPos.x, m_LastPos.y, m_LastPos.z); - } - } - - if (CanMove) - { - BroadcastMovementUpdate(m_ClientHandle.get()); - } + BroadcastMovementUpdate(m_ClientHandle.get()); if (m_Health > 0) // make sure player is alive { @@ -289,11 +271,6 @@ void cPlayer::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) m_LastPlayerListTime = std::chrono::steady_clock::now(); } - if (IsFlying()) - { - m_LastGroundHeight = static_cast(GetPosY()); - } - if (m_TicksUntilNextSave == 0) { SaveToDisk(); @@ -474,61 +451,88 @@ void cPlayer::SetTouchGround(bool a_bTouchGround) return; } - m_bTouchGround = a_bTouchGround; + /* Not pretty looking, and is more suited to wherever server-sided collision detection is implemented. + The following condition sets on-ground-ness if + The player isn't swimming or flying (client hardcoded conditions) and + they're on a block (Y is exact) - ensure any they could be standing on (including on the edges) is solid or + they're on a slab (Y significand is 0.5) - ditto with slab check + they're on a snow layer (Y divisible by 0.125) - ditto with snow layer check + */ - if (!m_bTouchGround) - { - if (GetPosY() > m_LastJumpHeight) - { - m_LastJumpHeight = static_cast(GetPosY()); - } - cWorld * World = GetWorld(); - if ((GetPosY() >= 0) && (GetPosY() < cChunkDef::Height)) - { - BLOCKTYPE BlockType = World->GetBlock(POSX_TOINT, POSY_TOINT, POSZ_TOINT); - if (BlockType != E_BLOCK_AIR) - { - m_bTouchGround = true; - } - if ( - (BlockType == E_BLOCK_WATER) || - (BlockType == E_BLOCK_STATIONARY_WATER) || - (BlockType == E_BLOCK_LADDER) || - (BlockType == E_BLOCK_VINES) + static const auto HalfWidth = GetWidth() / 2; + static const auto EPS = 0.0001; + if ( + !IsSwimming() && !IsFlying() && + ( + ( + ((GetPosY() >= 1) && ((GetPosY() - POSY_TOINT) <= EPS)) && + ( + cBlockInfo::IsSolid(GetWorld()->GetBlock((GetPosition() + Vector3d(0, -1, 0)).Floor())) || + cBlockInfo::IsSolid(GetWorld()->GetBlock((GetPosition() + Vector3d(HalfWidth, -1, 0)).Floor())) || + cBlockInfo::IsSolid(GetWorld()->GetBlock((GetPosition() + Vector3d(-HalfWidth, -1, 0)).Floor())) || + cBlockInfo::IsSolid(GetWorld()->GetBlock((GetPosition() + Vector3d(0, -1, HalfWidth)).Floor())) || + cBlockInfo::IsSolid(GetWorld()->GetBlock((GetPosition() + Vector3d(0, -1, -HalfWidth)).Floor())) + ) + ) || + ( + ((GetPosY() >= POSY_TOINT) && ((GetPosY() - (POSY_TOINT + 0.5)) <= EPS)) && + ( + cBlockSlabHandler::IsAnySlabType(GetWorld()->GetBlock((GetPosition() + Vector3d(0, 0, 0)).Floor())) || + cBlockSlabHandler::IsAnySlabType(GetWorld()->GetBlock((GetPosition() + Vector3d(HalfWidth, 0, 0)).Floor())) || + cBlockSlabHandler::IsAnySlabType(GetWorld()->GetBlock((GetPosition() + Vector3d(-HalfWidth, 0, 0)).Floor())) || + cBlockSlabHandler::IsAnySlabType(GetWorld()->GetBlock((GetPosition() + Vector3d(0, 0, HalfWidth)).Floor())) || + cBlockSlabHandler::IsAnySlabType(GetWorld()->GetBlock((GetPosition() + Vector3d(0, 0, -HalfWidth)).Floor())) + ) + ) || + ( + (fmod(GetPosY(), 0.125) <= EPS) && + ( + (GetWorld()->GetBlock((GetPosition() + Vector3d(0, 0, 0)).Floor()) == E_BLOCK_SNOW) || + (GetWorld()->GetBlock((GetPosition() + Vector3d(HalfWidth, 0, 0)).Floor()) == E_BLOCK_SNOW) || + (GetWorld()->GetBlock((GetPosition() + Vector3d(-HalfWidth, 0, 0)).Floor()) == E_BLOCK_SNOW) || + (GetWorld()->GetBlock((GetPosition() + Vector3d(0, 0, HalfWidth)).Floor()) == E_BLOCK_SNOW) || + (GetWorld()->GetBlock((GetPosition() + Vector3d(0, 0, -HalfWidth)).Floor()) == E_BLOCK_SNOW) + ) ) - { - m_LastGroundHeight = static_cast(GetPosY()); - } - } - } - else + ) + ) { - float Dist = static_cast(m_LastGroundHeight - floor(GetPosY())); - - if (Dist >= 2.0) // At least two blocks - TODO: Use m_LastJumpHeight instead of m_LastGroundHeight above - { - // Increment statistic - m_Stats.AddValue(statDistFallen, FloorC(Dist * 100 + 0.5)); - } - - int Damage = static_cast(Dist - 3.f); - if (m_LastJumpHeight > m_LastGroundHeight) - { - Damage++; - } - m_LastJumpHeight = static_cast(GetPosY()); - + auto Damage = static_cast(m_LastGroundHeight - GetPosY() - 3.0); if (Damage > 0) { // cPlayer makes sure damage isn't applied in creative, no need to check here TakeDamage(dtFalling, nullptr, Damage, Damage, 0); // Fall particles - GetWorld()->BroadcastSoundParticleEffect(2006, POSX_TOINT, static_cast(GetPosY()) - 1, POSZ_TOINT, Damage /* Used as particle effect speed modifier */); + Damage = std::min(15, Damage); + GetClientHandle()->SendParticleEffect( + "blockdust", + GetPosition(), + { 0, 0, 0 }, + (Damage - 1.f) * ((0.3f - 0.1f) / (15.f - 1.f)) + 0.1f, // Map damage (1 - 15) to particle speed (0.1 - 0.3) + static_cast((Damage - 1.f) * ((50.f - 20.f) / (15.f - 1.f)) + 20.f), // Map damage (1 - 15) to particle quantity (20 - 50) + { { GetWorld()->GetBlock(POS_TOINT - Vector3i(0, 1, 0)), 0 } } + ); } - m_LastGroundHeight = static_cast(GetPosY()); + m_bTouchGround = true; + m_LastGroundHeight = GetPosY(); } + else + { + m_bTouchGround = false; + } + + if (IsFlying() || IsSwimming() || IsClimbing()) + { + m_LastGroundHeight = GetPosY(); + } + + UNUSED(a_bTouchGround); + /* Unfortunately whatever the reason, there are still desyncs in on-ground status between the client and server. For example: + 1. Walking off a ledge (whatever height) + 2. Initial login + Thus, it is too risky to compare their value against ours and kick them for hacking */ } @@ -1322,11 +1326,10 @@ unsigned int cPlayer::AwardAchievement(const eStatistic a_Ach) void cPlayer::TeleportToCoords(double a_PosX, double a_PosY, double a_PosZ) { // ask plugins to allow teleport to the new position. - if (!cRoot::Get()->GetPluginManager()->CallHookEntityTeleport(*this, m_LastPos, Vector3d(a_PosX, a_PosY, a_PosZ))) + if (!cRoot::Get()->GetPluginManager()->CallHookEntityTeleport(*this, m_LastPosition, Vector3d(a_PosX, a_PosY, a_PosZ))) { SetPosition(a_PosX, a_PosY, a_PosZ); m_LastGroundHeight = static_cast(a_PosY); - m_LastJumpHeight = static_cast(a_PosY); m_bIsTeleporting = true; m_World->BroadcastTeleportEntity(*this, GetClientHandle()); @@ -1400,37 +1403,6 @@ void cPlayer::DoSetSpeed(double a_SpeedX, double a_SpeedY, double a_SpeedZ) -void cPlayer::MoveTo( const Vector3d & a_NewPos) -{ - if ((a_NewPos.y < -990) && (GetPosY() > -100)) - { - // When attached to an entity, the client sends position packets with weird coords: - // Y = -999 and X, Z = attempting to create speed, usually up to 0.03 - // We cannot test m_AttachedTo, because when deattaching, the server thinks the client is already deattached while - // the client may still send more of these nonsensical packets. - if (m_AttachedTo != nullptr) - { - Vector3d AddSpeed(a_NewPos); - AddSpeed.y = 0; - m_AttachedTo->AddSpeed(AddSpeed); - } - return; - } - - // TODO: should do some checks to see if player is not moving through terrain - // TODO: Official server refuses position packets too far away from each other, kicking "hacked" clients; we should, too - - Vector3d DeltaPos = a_NewPos - GetPosition(); - UpdateMovementStats(DeltaPos); - - SetPosition( a_NewPos); - SetStance(a_NewPos.y + 1.62); -} - - - - - void cPlayer::SetVisible(bool a_bVisible) { // Need to Check if the player or other players are in gamemode spectator, but will break compatibility @@ -1784,7 +1756,7 @@ bool cPlayer::LoadFromFile(const AString & a_FileName, cWorldPtr & a_World) SetPosX(JSON_PlayerPosition[0].asDouble()); SetPosY(JSON_PlayerPosition[1].asDouble()); SetPosZ(JSON_PlayerPosition[2].asDouble()); - m_LastPos = GetPosition(); + m_LastPosition = GetPosition(); } Json::Value & JSON_PlayerRotation = root["rotation"]; @@ -2102,12 +2074,23 @@ bool cPlayer::IsClimbing(void) const -void cPlayer::UpdateMovementStats(const Vector3d & a_DeltaPos) +void cPlayer::UpdateMovementStats(const Vector3d & a_DeltaPos, bool a_PreviousIsOnGround) { - StatValue Value = FloorC(a_DeltaPos.Length() * 100 + 0.5); + if (m_bIsTeleporting) + { + m_bIsTeleporting = false; + return; + } + StatValue Value = FloorC(a_DeltaPos.Length() * 100 + 0.5); if (m_AttachedTo == nullptr) { + if (IsFlying()) + { + m_Stats.AddValue(statDistFlown, Value); + // May be flying and doing any of the following: + } + if (IsClimbing()) { if (a_DeltaPos.y > 0.0) // Going up @@ -2128,14 +2111,22 @@ void cPlayer::UpdateMovementStats(const Vector3d & a_DeltaPos) else if (IsOnGround()) { m_Stats.AddValue(statDistWalked, Value); - AddFoodExhaustion((m_IsSprinting ? 0.001 : 0.0001) * static_cast(Value)); + AddFoodExhaustion((IsSprinting() ? 0.001 : 0.0001) * static_cast(Value)); } else { - if (Value >= 25) // Ignore small / slow movement + // If a jump just started, process food exhaustion: + if ((a_DeltaPos.y > 0.0) && a_PreviousIsOnGround) { - m_Stats.AddValue(statDistFlown, Value); + m_Stats.AddValue(statJumps, 1); + AddFoodExhaustion((IsSprinting() ? 0.008 : 0.002) * static_cast(Value)); } + else if (a_DeltaPos.y < 0.0) + { + // Increment statistic + m_Stats.AddValue(statDistFallen, (StatValue)(abs(a_DeltaPos.y) * 100 + 0.5)); + } + // TODO: good opportunity to detect illegal flight (check for falling tho) } } else @@ -2164,59 +2155,6 @@ void cPlayer::UpdateMovementStats(const Vector3d & a_DeltaPos) -void cPlayer::ApplyFoodExhaustionFromMovement() -{ - if (IsGameModeCreative() || IsGameModeSpectator()) - { - return; - } - - // If we have just teleported, apply no exhaustion - if (m_bIsTeleporting) - { - m_bIsTeleporting = false; - return; - } - - // If riding anything, apply no food exhaustion - if (m_AttachedTo != nullptr) - { - return; - } - - // Calculate the distance travelled, update the last pos: - double SpeedX = m_Speed.x; - double SpeedZ = m_Speed.z; - double BaseExhaustion(sqrt((SpeedX * SpeedX) + (SpeedZ * SpeedZ))); - - // Apply the exhaustion based on distance travelled: - if (IsFlying() || IsClimbing()) - { - // Apply no exhaustion when flying or climbing. - BaseExhaustion = 0; - } - else if (IsSprinting()) - { - // 0.1 pt per meter sprinted - BaseExhaustion = BaseExhaustion * 0.1; - } - else if (IsSwimming()) - { - // 0.015 pt per meter swum - BaseExhaustion = BaseExhaustion * 0.015; - } - else - { - // 0.01 pt per meter walked / sneaked - BaseExhaustion = BaseExhaustion * 0.01; - } - m_FoodExhaustionLevel += BaseExhaustion; -} - - - - - void cPlayer::LoadRank(void) { // Load the values from cRankManager: diff --git a/src/Entities/Player.h b/src/Entities/Player.h index 0fee6ef8a..8656f7336 100644 --- a/src/Entities/Player.h +++ b/src/Entities/Player.h @@ -117,8 +117,8 @@ public: /** Returns true if the player is currently charging the bow */ bool IsChargingBow(void) const { return m_IsChargingBow; } - void SetTouchGround( bool a_bTouchGround); - inline void SetStance( const double a_Stance) { m_Stance = a_Stance; } + void SetTouchGround(bool a_bTouchGround); + inline void SetStance(const double a_Stance) { m_Stance = a_Stance; } double GetEyeHeight(void) const; // tolua_export Vector3d GetEyePosition(void) const; // tolua_export virtual bool IsOnGround(void) const override { return m_bTouchGround; } @@ -208,9 +208,6 @@ public: @deprecated Use SetSpeed instead. */ void ForceSetSpeed(const Vector3d & a_Speed); // tolua_export - /** Tries to move to a new position, with attachment-related checks (y == -999) */ - void MoveTo(const Vector3d & a_NewPos); // tolua_export - cWindow * GetWindow(void) { return m_CurrentWindow; } // tolua_export const cWindow * GetWindow(void) const { return m_CurrentWindow; } @@ -454,7 +451,7 @@ public: // tolua_end /** Update movement-related statistics. */ - void UpdateMovementStats(const Vector3d & a_DeltaPos); + void UpdateMovementStats(const Vector3d & a_DeltaPos, bool a_PreviousIsOnGround); // tolua_begin @@ -553,8 +550,7 @@ protected: /** A "buffer" which adds up hunger before it is substracted from m_FoodSaturationLevel or m_FoodLevel. Each action adds a little */ double m_FoodExhaustionLevel; - float m_LastJumpHeight; - float m_LastGroundHeight; + double m_LastGroundHeight; bool m_bTouchGround; double m_Stance; @@ -667,9 +663,6 @@ protected: /** Tosses a list of items. */ void TossItems(const cItems & a_Items); - /** Adds food exhaustion based on the difference between Pos and LastPos, sprinting status and swimming (in water block) */ - void ApplyFoodExhaustionFromMovement(); - /** Returns the filename for the player data based on the UUID given. This can be used both for online and offline UUIDs. */ AString GetUUIDFileName(const AString & a_UUID); diff --git a/src/Protocol/Protocol18x.cpp b/src/Protocol/Protocol18x.cpp index da5c451db..58cc839f0 100644 --- a/src/Protocol/Protocol18x.cpp +++ b/src/Protocol/Protocol18x.cpp @@ -1028,10 +1028,7 @@ void cProtocol180::SendPlayerMoveLook(void) cPacketizer Pkt(*this, 0x08); // Player Position And Look packet cPlayer * Player = m_Client->GetPlayer(); Pkt.WriteBEDouble(Player->GetPosX()); - - // The "+ 0.001" is there because otherwise the player falls through the block they were standing on. - Pkt.WriteBEDouble(Player->GetPosY() + 0.001); - + Pkt.WriteBEDouble(Player->GetPosY()); Pkt.WriteBEDouble(Player->GetPosZ()); Pkt.WriteBEFloat(static_cast(Player->GetYaw())); Pkt.WriteBEFloat(static_cast(Player->GetPitch()));