diff options
Diffstat (limited to 'src/Entities')
-rw-r--r-- | src/Entities/ArrowEntity.cpp | 4 | ||||
-rw-r--r-- | src/Entities/Boat.cpp | 4 | ||||
-rw-r--r-- | src/Entities/Entity.cpp | 39 | ||||
-rw-r--r-- | src/Entities/Entity.h | 14 | ||||
-rw-r--r-- | src/Entities/EntityEffect.cpp | 4 | ||||
-rw-r--r-- | src/Entities/FallingBlock.cpp | 2 | ||||
-rw-r--r-- | src/Entities/FireChargeEntity.cpp | 1 | ||||
-rw-r--r-- | src/Entities/FireworkEntity.cpp | 2 | ||||
-rw-r--r-- | src/Entities/Floater.cpp | 6 | ||||
-rw-r--r-- | src/Entities/GhastFireballEntity.cpp | 1 | ||||
-rw-r--r-- | src/Entities/HangingEntity.h | 7 | ||||
-rw-r--r-- | src/Entities/Minecart.cpp | 8 | ||||
-rw-r--r-- | src/Entities/Minecart.h | 7 | ||||
-rw-r--r-- | src/Entities/Pawn.cpp | 2 | ||||
-rw-r--r-- | src/Entities/Pickup.cpp | 3 | ||||
-rw-r--r-- | src/Entities/Player.cpp | 192 | ||||
-rw-r--r-- | src/Entities/Player.h | 194 | ||||
-rw-r--r-- | src/Entities/ProjectileEntity.cpp | 14 | ||||
-rw-r--r-- | src/Entities/ProjectileEntity.h | 2 | ||||
-rw-r--r-- | src/Entities/TNTEntity.cpp | 4 | ||||
-rw-r--r-- | src/Entities/WitherSkullEntity.cpp | 2 |
21 files changed, 317 insertions, 195 deletions
diff --git a/src/Entities/ArrowEntity.cpp b/src/Entities/ArrowEntity.cpp index 3c1fabb1b..32952100c 100644 --- a/src/Entities/ArrowEntity.cpp +++ b/src/Entities/ArrowEntity.cpp @@ -21,6 +21,8 @@ cArrowEntity::cArrowEntity(cEntity * a_Creator, double a_X, double a_Y, double a { SetSpeed(a_Speed); SetMass(0.1); + SetGravity(-20.0f); + SetAirDrag(0.2f); SetYawFromSpeed(); SetPitchFromSpeed(); LOGD("Created arrow %d with speed {%.02f, %.02f, %.02f} and rot {%.02f, %.02f}", @@ -48,6 +50,8 @@ cArrowEntity::cArrowEntity(cPlayer & a_Player, double a_Force) : { m_PickupState = psInCreative; } + SetGravity(-20.0f); + SetAirDrag(0.01f); } diff --git a/src/Entities/Boat.cpp b/src/Entities/Boat.cpp index 6177eb32f..4ad418be4 100644 --- a/src/Entities/Boat.cpp +++ b/src/Entities/Boat.cpp @@ -16,7 +16,9 @@ cBoat::cBoat(double a_X, double a_Y, double a_Z) : super(etBoat, a_X, a_Y, a_Z, 0.98, 0.7) { - SetMass(20.f); + SetMass(20.0f); + SetGravity(-16.0f); + SetAirDrag(0.05f); SetMaxHealth(6); SetHealth(6); } diff --git a/src/Entities/Entity.cpp b/src/Entities/Entity.cpp index c8df6b4b1..dca44488b 100644 --- a/src/Entities/Entity.cpp +++ b/src/Entities/Entity.cpp @@ -36,6 +36,7 @@ cEntity::cEntity(eEntityType a_EntityType, double a_X, double a_Y, double a_Z, d m_bHasSentNoSpeed(true), m_bOnGround(false), m_Gravity(-9.81f), + m_AirDrag(0.02f), m_LastPos(a_X, a_Y, a_Z), m_IsInitialized(false), m_WorldTravellingFrom(nullptr), @@ -246,7 +247,6 @@ void cEntity::TakeDamage(eDamageType a_DamageType, cEntity * a_Attacker, int a_R if (a_Attacker != nullptr) { Heading = a_Attacker->GetLookVector() * (a_Attacker->IsSprinting() ? 16 : 11); - Heading.y = 1.6; } TDI.Knockback = Heading * a_KnockbackAmount; @@ -943,6 +943,7 @@ void cEntity::HandlePhysics(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { // Normal gravity fallspeed = m_Gravity * DtSec.count(); + NextSpeed -= NextSpeed * (m_AirDrag * 20.0f) * DtSec.count(); } NextSpeed.y += static_cast<float>(fallspeed); } @@ -999,7 +1000,7 @@ void cEntity::HandlePhysics(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) NextSpeed += m_WaterSpeed; - if (NextSpeed.SqrLength() > 0.f) + if (NextSpeed.SqrLength() > 0.0f) { cTracer Tracer(GetWorld()); // Distance traced is an integer, so we round up from the distance we should go (Speed * Delta), else we will encounter collision detection failurse @@ -1015,20 +1016,20 @@ void cEntity::HandlePhysics(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) // Block hit was within our projected path // Begin by stopping movement in the direction that we hit something. The Normal is the line perpendicular to a 2D face and in this case, stores what block face was hit through either -1 or 1. // For example: HitNormal.y = -1 : BLOCK_FACE_YM; HitNormal.y = 1 : BLOCK_FACE_YP - if (Tracer.HitNormal.x != 0.f) + if (Tracer.HitNormal.x != 0.0f) { - NextSpeed.x = 0.f; + NextSpeed.x = 0.0f; } - if (Tracer.HitNormal.y != 0.f) + if (Tracer.HitNormal.y != 0.0f) { - NextSpeed.y = 0.f; + NextSpeed.y = 0.0f; } - if (Tracer.HitNormal.z != 0.f) + if (Tracer.HitNormal.z != 0.0f) { - NextSpeed.z = 0.f; + NextSpeed.z = 0.0f; } - if (Tracer.HitNormal.y == 1.f) // Hit BLOCK_FACE_YP, we are on the ground + if (Tracer.HitNormal.y == 1.0f) // Hit BLOCK_FACE_YP, we are on the ground { m_bOnGround = true; } @@ -1308,7 +1309,8 @@ bool cEntity::DetectPortal() if (IsPlayer()) { - ((cPlayer *)this)->GetClientHandle()->SendRespawn(dimOverworld); // Send a respawn packet before world is loaded/generated so the client isn't left in limbo + // Send a respawn packet before world is loaded / generated so the client isn't left in limbo + ((cPlayer *)this)->GetClientHandle()->SendRespawn(dimOverworld); } return MoveToWorld(cRoot::Get()->CreateAndInitializeWorld(GetWorld()->GetLinkedOverworldName()), false); @@ -1401,14 +1403,25 @@ bool cEntity::DoMoveToWorld(cWorld * a_World, bool a_ShouldSendRespawn) return false; } + // Ask the plugins if the entity is allowed to changing the world + if (cRoot::Get()->GetPluginManager()->CallHookEntityChangingWorld(*this, *a_World)) + { + // A Plugin doesn't allow the entity to changing the world + return false; + } + // Remove all links to the old world SetWorldTravellingFrom(GetWorld()); // cChunk::Tick() handles entity removal GetWorld()->BroadcastDestroyEntity(*this); // Queue add to new world a_World->AddEntity(this); + cWorld * OldWorld = cRoot::Get()->GetWorld(GetWorld()->GetName()); // Required for the hook HOOK_ENTITY_CHANGED_WORLD SetWorld(a_World); + // Entity changed the world, call the hook + cRoot::Get()->GetPluginManager()->CallHookEntityChangedWorld(*this, *OldWorld); + return true; } @@ -1687,8 +1700,8 @@ void cEntity::BroadcastMovementUpdate(const cClientHandle * a_Exclude) { m_World->BroadcastEntityRelMove(*this, (char)DiffX, (char)DiffY, (char)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 + // 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(); } else @@ -1971,7 +1984,7 @@ void cEntity::SteerVehicle(float a_Forward, float a_Sideways) { return; } - if ((a_Forward != 0.f) || (a_Sideways != 0.f)) + if ((a_Forward != 0.0f) || (a_Sideways != 0.0f)) { m_AttachedTo->HandleSpeedFromAttachee(a_Forward, a_Sideways); } diff --git a/src/Entities/Entity.h b/src/Entities/Entity.h index 9bb1837f1..fecbb9bf5 100644 --- a/src/Entities/Entity.h +++ b/src/Entities/Entity.h @@ -270,6 +270,10 @@ public: float GetGravity(void) const { return m_Gravity; } void SetGravity(float a_Gravity) { m_Gravity = a_Gravity; } + + float GetAirDrag(void) const { return m_AirDrag; } + + void SetAirDrag(float a_AirDrag) { m_AirDrag = a_AirDrag; } /// Sets the rotation to match the speed vector (entity goes "face-forward") void SetYawFromSpeed(void); @@ -470,7 +474,7 @@ protected: static cCriticalSection m_CSCount; static UInt32 m_EntityCount; - /** Measured in meter/second (m/s) */ + /** Measured in meters / second (m / s) */ Vector3d m_Speed; /** The ID of the entity that is guaranteed to be unique within a single run of the server. @@ -490,7 +494,7 @@ protected: /** Stores whether head yaw has been set manually */ bool m_bDirtyHead; - /** Stores whether our yaw/pitch/roll (body orientation) has been set manually */ + /** Stores whether our yaw / pitch / roll (body orientation) has been set manually */ bool m_bDirtyOrientation; /** Stores whether we have sent a Velocity packet with a speed of zero (no speed) to the client @@ -504,6 +508,12 @@ protected: For realistic effects, this should be negative. For spaaaaaaace, this can be zero or even positive */ float m_Gravity; + /** Stores the air drag that is applied to the entity every tick, measured in speed ratio per tick + Acts as air friction and slows down flight + Will be interpolated if the server tick rate varies + 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; diff --git a/src/Entities/EntityEffect.cpp b/src/Entities/EntityEffect.cpp index bae686b77..c8be414d4 100644 --- a/src/Entities/EntityEffect.cpp +++ b/src/Entities/EntityEffect.cpp @@ -102,11 +102,11 @@ int cEntityEffect::GetPotionEffectDuration(short a_ItemDamage) // If potion is level II, half the duration. If not, stays the same TierCoeff = (GetPotionEffectIntensity(a_ItemDamage) > 0) ? 0.5 : 1; - // If potion is extended, multiply duration by 8/3. If not, stays the same + // If potion is extended, multiply duration by 8 / 3. If not, stays the same // Extended potion if sixth lowest bit is set ExtCoeff = (a_ItemDamage & 0x40) ? (8.0 / 3.0) : 1; - // If potion is splash potion, multiply duration by 3/4. If not, stays the same + // If potion is splash potion, multiply duration by 3 / 4. If not, stays the same SplashCoeff = IsPotionDrinkable(a_ItemDamage) ? 1 : 0.75; // Ref.: diff --git a/src/Entities/FallingBlock.cpp b/src/Entities/FallingBlock.cpp index 7301a3c9d..4a165909a 100644 --- a/src/Entities/FallingBlock.cpp +++ b/src/Entities/FallingBlock.cpp @@ -16,6 +16,8 @@ cFallingBlock::cFallingBlock(const Vector3i & a_BlockPosition, BLOCKTYPE a_Block m_BlockMeta(a_BlockMeta), m_OriginalPosition(a_BlockPosition) { + SetGravity(-16.0f); + SetAirDrag(0.02f); } diff --git a/src/Entities/FireChargeEntity.cpp b/src/Entities/FireChargeEntity.cpp index aba32602f..f6c665156 100644 --- a/src/Entities/FireChargeEntity.cpp +++ b/src/Entities/FireChargeEntity.cpp @@ -12,6 +12,7 @@ cFireChargeEntity::cFireChargeEntity(cEntity * a_Creator, double a_X, double a_Y { SetSpeed(a_Speed); SetGravity(0); + SetAirDrag(0); } diff --git a/src/Entities/FireworkEntity.cpp b/src/Entities/FireworkEntity.cpp index 32eaf669a..89f69f113 100644 --- a/src/Entities/FireworkEntity.cpp +++ b/src/Entities/FireworkEntity.cpp @@ -13,6 +13,8 @@ cFireworkEntity::cFireworkEntity(cEntity * a_Creator, double a_X, double a_Y, do m_TicksToExplosion(a_Item.m_FireworkItem.m_FlightTimeInTicks), m_FireworkItem(a_Item) { + SetGravity(0); + SetAirDrag(0); } diff --git a/src/Entities/Floater.cpp b/src/Entities/Floater.cpp index cf8dd6c6f..0c868270d 100644 --- a/src/Entities/Floater.cpp +++ b/src/Entities/Floater.cpp @@ -6,7 +6,7 @@ #include "Floater.h" #include "Player.h" #include "../ClientHandle.h" - +#include "Broadcaster.h" @@ -145,12 +145,12 @@ void cFloater::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { LOGD("Started producing particles for floater %i", GetUniqueID()); m_ParticlePos.Set(GetPosX() + (-4 + m_World->GetTickRandomNumber(8)), GetPosY(), GetPosZ() + (-4 + m_World->GetTickRandomNumber(8))); - m_World->BroadcastParticleEffect("splash", (float) m_ParticlePos.x, (float) m_ParticlePos.y, (float) m_ParticlePos.z, 0, 0, 0, 0, 15); + m_World->GetBroadcaster().BroadcastParticleEffect("splash", static_cast<Vector3f>(m_ParticlePos), Vector3f{}, 0, 15); } else if (m_CountDownTime < 20) { m_ParticlePos = (m_ParticlePos + (GetPosition() - m_ParticlePos) / 6); - m_World->BroadcastParticleEffect("splash", (float) m_ParticlePos.x, (float) m_ParticlePos.y, (float) m_ParticlePos.z, 0, 0, 0, 0, 15); + m_World->GetBroadcaster().BroadcastParticleEffect("splash", static_cast<Vector3f>(m_ParticlePos), Vector3f{}, 0, 15); } m_CountDownTime--; diff --git a/src/Entities/GhastFireballEntity.cpp b/src/Entities/GhastFireballEntity.cpp index 9e4cb387e..c64fb2a17 100644 --- a/src/Entities/GhastFireballEntity.cpp +++ b/src/Entities/GhastFireballEntity.cpp @@ -12,6 +12,7 @@ cGhastFireballEntity::cGhastFireballEntity(cEntity * a_Creator, double a_X, doub { SetSpeed(a_Speed); SetGravity(0); + SetAirDrag(0); } diff --git a/src/Entities/HangingEntity.h b/src/Entities/HangingEntity.h index 507502ac6..9d783006c 100644 --- a/src/Entities/HangingEntity.h +++ b/src/Entities/HangingEntity.h @@ -27,7 +27,10 @@ public: eBlockFace GetFacing() const { return cHangingEntity::ProtocolFaceToBlockFace(m_Facing); } /** Set the direction in which the entity is facing. */ - void SetFacing(eBlockFace a_Facing) { m_Facing = cHangingEntity::BlockFaceToProtocolFace(a_Facing); } + void SetFacing(eBlockFace a_Facing) + { + m_Facing = cHangingEntity::BlockFaceToProtocolFace(a_Facing); + } // tolua_end @@ -37,7 +40,7 @@ public: /** Set the direction in which the entity is facing. */ void SetProtocolFacing(Byte a_Facing) { - ASSERT((a_Facing <= 3) && (a_Facing >= 0)); + ASSERT(a_Facing <= 3); m_Facing = a_Facing; } diff --git a/src/Entities/Minecart.cpp b/src/Entities/Minecart.cpp index ee10cf6b3..b80759d24 100644 --- a/src/Entities/Minecart.cpp +++ b/src/Entities/Minecart.cpp @@ -92,7 +92,9 @@ cMinecart::cMinecart(ePayload a_Payload, double a_X, double a_Y, double a_Z) : m_DetectorRailPosition(0, 0, 0), m_bIsOnDetectorRail(false) { - SetMass(20.f); + SetMass(20.0f); + SetGravity(-16.0f); + SetAirDrag(0.05f); SetMaxHealth(6); SetHealth(6); SetWidth(1); @@ -903,7 +905,7 @@ bool cMinecart::TestEntityCollision(NIBBLETYPE a_RailMeta) } /* Check to which side the minecart is to be pushed. - Let's consider a z-x-coordinate system where the minecart is the center (0/0). + Let's consider a z-x-coordinate system where the minecart is the center (0, 0). The minecart moves along the line x = -z, the perpendicular line to this is x = z. In order to decide to which side the minecart is to be pushed, it must be checked on what side of the perpendicular line the pushing entity is located. */ if ( @@ -952,7 +954,7 @@ bool cMinecart::TestEntityCollision(NIBBLETYPE a_RailMeta) } /* Check to which side the minecart is to be pushed. - Let's consider a z-x-coordinate system where the minecart is the center (0/0). + Let's consider a z-x-coordinate system where the minecart is the center (0, 0). The minecart moves along the line x = z, the perpendicular line to this is x = -z. In order to decide to which side the minecart is to be pushed, it must be checked on what side of the perpendicular line the pushing entity is located. */ if ( diff --git a/src/Entities/Minecart.h b/src/Entities/Minecart.h index 898776e71..05eaf16e9 100644 --- a/src/Entities/Minecart.h +++ b/src/Entities/Minecart.h @@ -29,7 +29,7 @@ public: enum ePayload { mpNone = 0, // Empty minecart, ridable by player or mobs - mpChest = 1, // Minecart-with-chest, can store a grid of 3*8 items + mpChest = 1, // Minecart-with-chest, can store a grid of 3 * 8 items mpFurnace = 2, // Minecart-with-furnace, can be powered mpTNT = 3, // Minecart-with-TNT, can be blown up with activator rail mpHopper = 5, // Minecart-with-hopper, can be hopper @@ -54,8 +54,7 @@ protected: cMinecart(ePayload a_Payload, double a_X, double a_Y, double a_Z); /** Handles physics on normal rails - For each tick, slow down on flat rails, speed up or slow down on ascending/descending rails (depending on direction), and turn on curved rails - */ + For each tick, slow down on flat rails, speed up or slow down on ascending / descending rails (depending on direction), and turn on curved rails. */ void HandleRailPhysics(NIBBLETYPE a_RailMeta, std::chrono::milliseconds a_Dt); /** Handles powered rail physics @@ -136,7 +135,7 @@ protected: virtual void Destroyed() override; // cItemGrid::cListener overrides: - virtual void OnSlotChanged(cItemGrid * a_Grid, int a_SlotNum) + virtual void OnSlotChanged(cItemGrid * a_Grid, int a_SlotNum) override { UNUSED(a_SlotNum); ASSERT(a_Grid == &m_Contents); diff --git a/src/Entities/Pawn.cpp b/src/Entities/Pawn.cpp index baf8a2f3b..fcb686e28 100644 --- a/src/Entities/Pawn.cpp +++ b/src/Entities/Pawn.cpp @@ -13,6 +13,8 @@ cPawn::cPawn(eEntityType a_EntityType, double a_Width, double a_Height) : super(a_EntityType, 0, 0, 0, a_Width, a_Height) , m_EntityEffects(tEffectMap()) { + SetGravity(-32.0f); + SetAirDrag(0.02f); } diff --git a/src/Entities/Pickup.cpp b/src/Entities/Pickup.cpp index 9f2609894..f2f76dbf9 100644 --- a/src/Entities/Pickup.cpp +++ b/src/Entities/Pickup.cpp @@ -91,7 +91,8 @@ cPickup::cPickup(double a_PosX, double a_PosY, double a_PosZ, const cItem & a_It , m_bCollected(false) , m_bIsPlayerCreated(IsPlayerCreated) { - SetGravity(-10.5f); + SetGravity(-16.0f); + SetAirDrag(0.02f); SetMaxHealth(5); SetHealth(5); SetSpeed(a_SpeedX, a_SpeedY, a_SpeedZ); diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp index c89e7b87c..e3e3fac4f 100644 --- a/src/Entities/Player.cpp +++ b/src/Entities/Player.cpp @@ -94,7 +94,7 @@ cPlayer::cPlayer(cClientHandlePtr a_Client, const AString & a_PlayerName) : SetMaxHealth(MAX_HEALTH); m_Health = MAX_HEALTH; - + m_LastPlayerListTime = std::chrono::steady_clock::now(); m_PlayerName = a_PlayerName; @@ -106,7 +106,7 @@ cPlayer::cPlayer(cClientHandlePtr a_Client, const AString & a_PlayerName) : SetPosY(World->GetSpawnY()); SetPosZ(World->GetSpawnZ()); SetBedPos(Vector3i(static_cast<int>(World->GetSpawnX()), static_cast<int>(World->GetSpawnY()), static_cast<int>(World->GetSpawnZ()))); - + LOGD("Player \"%s\" is connecting for the first time, spawning at default world spawn {%.2f, %.2f, %.2f}", a_PlayerName.c_str(), GetPosX(), GetPosY(), GetPosZ() ); @@ -128,7 +128,14 @@ cPlayer::cPlayer(cClientHandlePtr a_Client, const AString & a_PlayerName) : m_IsFlying = true; } } - + + if (m_GameMode == gmSpectator) // If player is reconnecting to the server in spectator mode + { + m_CanFly = true; + m_IsFlying = true; + m_bVisible = false; + } + cRoot::Get()->GetServer()->PlayerCreated(this); } @@ -145,17 +152,17 @@ cPlayer::~cPlayer(void) } LOGD("Deleting cPlayer \"%s\" at %p, ID %d", GetName().c_str(), this, GetUniqueID()); - + // Notify the server that the player is being destroyed cRoot::Get()->GetServer()->PlayerDestroying(this); SaveToDisk(); m_ClientHandle = nullptr; - + delete m_InventoryWindow; m_InventoryWindow = nullptr; - + LOGD("Player %p deleted", this); } @@ -201,7 +208,7 @@ void cPlayer::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) m_ClientHandle = nullptr; return; } - + if (!m_ClientHandle->IsPlaying()) { // We're not yet in the game, ignore everything @@ -210,21 +217,21 @@ void cPlayer::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) } m_Stats.AddValue(statMinutesPlayed, 1); - + if (!a_Chunk.IsValid()) { // This may happen if the cPlayer is created before the chunks have the chance of being loaded / generated (#83) return; } - + super::Tick(a_Dt, a_Chunk); - + // Handle charging the bow: if (m_IsChargingBow) { m_BowCharge += 1; } - + // Handle updating experience if (m_bDirtyExperience) { @@ -236,7 +243,7 @@ void cPlayer::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { // Apply food exhaustion from movement: ApplyFoodExhaustionFromMovement(); - + if (cRoot::Get()->GetPluginManager()->CallHookPlayerMoving(*this, m_LastPos, GetPosition())) { CanMove = false; @@ -257,10 +264,10 @@ void cPlayer::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { FinishEating(); } - + HandleFood(); } - + if (m_IsFishing) { HandleFloater(); @@ -355,7 +362,7 @@ float cPlayer::GetXpPercentage() int currentLevel_XpBase = XpForLevel(currentLevel); return static_cast<float>(m_CurrentXp - currentLevel_XpBase) / - static_cast<float>(XpForLevel(1+currentLevel) - currentLevel_XpBase); + static_cast<float>(XpForLevel(1 + currentLevel) - currentLevel_XpBase); } @@ -364,7 +371,7 @@ float cPlayer::GetXpPercentage() bool cPlayer::SetCurrentExperience(int a_CurrentXp) { - if (!(a_CurrentXp >= 0) || (a_CurrentXp > (std::numeric_limits<int>().max() - m_LifetimeTotalXp))) + if (!(a_CurrentXp >= 0) || (a_CurrentXp > (std::numeric_limits<int>::max() - m_LifetimeTotalXp))) { LOGWARNING("Tried to update experiece with an invalid Xp value: %d", a_CurrentXp); return false; // oops, they gave us a dodgey number @@ -403,7 +410,7 @@ int cPlayer::DeltaExperience(int a_Xp_delta) m_LifetimeTotalXp += a_Xp_delta; } - LOGD("Player \"%s\" gained/lost %d experience, total is now: %d", GetName().c_str(), a_Xp_delta, m_CurrentXp); + LOGD("Player \"%s\" gained / lost %d experience, total is now: %d", GetName().c_str(), a_Xp_delta, m_CurrentXp); // Set experience to be updated m_bDirtyExperience = true; @@ -460,7 +467,7 @@ void cPlayer::SetTouchGround(bool a_bTouchGround) { return; } - + m_bTouchGround = a_bTouchGround; if (!m_bTouchGround) @@ -509,7 +516,7 @@ void cPlayer::SetTouchGround(bool a_bTouchGround) { // 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<int>(GetPosY()) - 1, POSZ_TOINT, Damage /* Used as particle effect speed modifier */); } @@ -541,7 +548,7 @@ void cPlayer::SetFoodLevel(int a_FoodLevel) m_FoodSaturationLevel = 5.0; return; } - + m_FoodLevel = FoodLevel; SendHealth(); } @@ -609,7 +616,7 @@ void cPlayer::StartEating(void) { // Set the timer: m_EatingFinishTick = m_World->GetWorldAge() + EATING_TICKS; - + // Send the packets: m_World->BroadcastEntityAnimation(*this, 3); m_World->BroadcastEntityMetadata(*this); @@ -623,7 +630,7 @@ void cPlayer::FinishEating(void) { // Reset the timer: m_EatingFinishTick = -1; - + // Send the packets: m_ClientHandle->SendEntityStatus(*this, esPlayerEatingAccepted); m_World->BroadcastEntityMetadata(*this); @@ -757,7 +764,7 @@ void cPlayer::SetSprintingMaxSpeed(double a_Speed) void cPlayer::SetFlyingMaxSpeed(double a_Speed) { m_FlyingMaxSpeed = a_Speed; - + // Update the flying speed, always: m_ClientHandle->SendPlayerAbilities(); } @@ -769,7 +776,7 @@ void cPlayer::SetFlyingMaxSpeed(double a_Speed) void cPlayer::SetCrouch(bool a_IsCrouched) { // Set the crouch status, broadcast to all visible players - + if (a_IsCrouched == m_IsCrouched) { // No change @@ -790,7 +797,7 @@ void cPlayer::SetSprint(bool a_IsSprinting) // No change return; } - + m_IsSprinting = a_IsSprinting; m_ClientHandle->SendPlayerMaxSpeed(); } @@ -876,7 +883,7 @@ bool cPlayer::DoTakeDamage(TakeDamageInfo & a_TDI) } } } - + if (super::DoTakeDamage(a_TDI)) { // Any kind of damage adds food exhaustion @@ -1005,7 +1012,7 @@ void cPlayer::Respawn(void) m_Health = GetMaxHealth(); SetInvulnerableTicks(20); - + // Reset food level: m_FoodLevel = MAX_FOOD_LEVEL; m_FoodSaturationLevel = 5.0; @@ -1017,7 +1024,7 @@ void cPlayer::Respawn(void) // ToDo: send score to client? How? m_ClientHandle->SendRespawn(GetWorld()->GetDimension(), true); - + // Extinguish the fire: StopBurning(); @@ -1151,7 +1158,7 @@ void cPlayer::CloseWindow(bool a_CanRefuse) m_CurrentWindow = m_InventoryWindow; return; } - + if (m_CurrentWindow->ClosedByPlayer(*this, a_CanRefuse) || !a_CanRefuse) { // Close accepted, go back to inventory window (the default): @@ -1189,21 +1196,17 @@ void cPlayer::SetGameMode(eGameMode a_GameMode) LOGWARNING("%s: Setting invalid gamemode: %d", GetName().c_str(), a_GameMode); return; } - + if (m_GameMode == a_GameMode) { // Gamemode already set return; } - + m_GameMode = a_GameMode; m_ClientHandle->SendGameMode(a_GameMode); - if (!(IsGameModeCreative() || IsGameModeSpectator())) - { - SetFlying(false); - SetCanFly(false); - } + SetCapabilities(); m_World->BroadcastPlayerListUpdateGameMode(*this); } @@ -1215,6 +1218,30 @@ void cPlayer::SetGameMode(eGameMode a_GameMode) void cPlayer::LoginSetGameMode( eGameMode a_GameMode) { m_GameMode = a_GameMode; + + SetCapabilities(); +} + + + + + +void cPlayer::SetCapabilities() +{ + if (!IsGameModeCreative() || IsGameModeSpectator()) + { + SetFlying(false); + SetCanFly(false); + } + + if (IsGameModeSpectator()) + { + SetVisible(false); + } + else + { + SetVisible(true); + } } @@ -1306,12 +1333,12 @@ void cPlayer::SendRotation(double a_YawDegrees, double a_PitchDegrees) Vector3d cPlayer::GetThrowStartPos(void) const { Vector3d res = GetEyePosition(); - + // Adjust the position to be just outside the player's bounding box: res.x += 0.16 * cos(GetPitch()); res.y += -0.1; res.z += 0.16 * sin(GetPitch()); - + return res; } @@ -1323,9 +1350,9 @@ Vector3d cPlayer::GetThrowSpeed(double a_SpeedCoeff) const { Vector3d res = GetLookVector(); res.Normalize(); - + // TODO: Add a slight random change (+-0.0075 in each direction) - + return res * a_SpeedCoeff; } @@ -1370,13 +1397,13 @@ void cPlayer::MoveTo( const Vector3d & a_NewPos) } 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); } @@ -1411,17 +1438,26 @@ bool cPlayer::HasPermission(const AString & a_Permission) // Empty permission request is always granted return true; } - + AStringVector Split = StringSplit(a_Permission, "."); + // Iterate over all restrictions; if any matches, then return failure: + for (auto & Restriction: m_SplitRestrictions) + { + if (PermissionMatches(Split, Restriction)) + { + return false; + } + } // for Restriction - m_SplitRestrictions[] + // Iterate over all granted permissions; if any matches, then return success: - for (AStringVectorVector::const_iterator itr = m_SplitPermissions.begin(), end = m_SplitPermissions.end(); itr != end; ++itr) + for (auto & Permission: m_SplitPermissions) { - if (PermissionMatches(Split, *itr)) + if (PermissionMatches(Split, Permission)) { return true; } - } // for itr - m_SplitPermissions[] + } // for Permission - m_SplitPermissions[] // No granted permission matches return false; @@ -1574,7 +1610,7 @@ void cPlayer::TossItems(const cItems & a_Items) { return; } - + m_Stats.AddValue(statItemsDropped, (StatValue)a_Items.Size()); double vX = 0, vY = 0, vZ = 0; @@ -1596,19 +1632,30 @@ bool cPlayer::DoMoveToWorld(cWorld * a_World, bool a_ShouldSendRespawn) // Don't move to same world return false; } - + + // Ask the plugins if the player is allowed to changing the world + if (cRoot::Get()->GetPluginManager()->CallHookEntityChangingWorld(*this, *a_World)) + { + // A Plugin doesn't allow the player to changing the world + return false; + } + // Send the respawn packet: if (a_ShouldSendRespawn && (m_ClientHandle != nullptr)) { m_ClientHandle->SendRespawn(a_World->GetDimension()); } + // Broadcast for other people that the player is gone. + GetWorld()->BroadcastDestroyEntity(*this); + // Remove player from the old world SetWorldTravellingFrom(GetWorld()); // cChunk handles entity removal GetWorld()->RemovePlayer(this, false); // Queue adding player to the new world, including all the necessary adjustments to the object a_World->AddPlayer(this); + cWorld * OldWorld = cRoot::Get()->GetWorld(GetWorld()->GetName()); // Required for the hook HOOK_ENTITY_CHANGED_WORLD SetWorld(a_World); // Chunks may be streamed before cWorld::AddPlayer() sets the world to the new value // Update the view distance. @@ -1619,7 +1666,13 @@ bool cPlayer::DoMoveToWorld(cWorld * a_World, bool a_ShouldSendRespawn) { m_ClientHandle->SendWeather(a_World->GetWeather()); } - + + // Broadcast the player into the new world. + a_World->BroadcastSpawnEntity(*this); + + // Player changed the world, call the hook + cRoot::Get()->GetPluginManager()->CallHookEntityChangedWorld(*this, *OldWorld); + return true; } @@ -1636,7 +1689,7 @@ bool cPlayer::LoadFromDisk(cWorldPtr & a_World) { return true; } - + // Load from the offline UUID file, if allowed: AString OfflineUUID = cClientHandle::GenerateOfflineUUID(GetName()); const char * OfflineUsage = " (unused)"; @@ -1648,7 +1701,7 @@ bool cPlayer::LoadFromDisk(cWorldPtr & a_World) return true; } } - + // Load from the old-style name-based file, if allowed: if (cRoot::Get()->GetServer()->ShouldLoadNamedPlayerData()) { @@ -1663,7 +1716,7 @@ bool cPlayer::LoadFromDisk(cWorldPtr & a_World) return true; } } - + // None of the files loaded successfully LOG("Player data file not found for %s (%s, offline %s%s), will be reset to defaults.", GetName().c_str(), m_UUID.c_str(), OfflineUUID.c_str(), OfflineUsage @@ -1740,7 +1793,7 @@ bool cPlayer::LoadFromFile(const AString & a_FileName, cWorldPtr & a_World) { m_CanFly = true; } - + m_Inventory.LoadFromJson(root["inventory"]); cEnderChestEntity::LoadFromJson(root["enderchestinventory"], m_EnderChestContents); @@ -1756,14 +1809,14 @@ bool cPlayer::LoadFromFile(const AString & a_FileName, cWorldPtr & a_World) m_LastBedPos.z = root.get("SpawnZ", a_World->GetSpawnZ()).asInt(); // Load the player stats. - // We use the default world name (like bukkit) because stats are shared between dimensions/worlds. + // We use the default world name (like bukkit) because stats are shared between dimensions / worlds. cStatSerializer StatSerializer(cRoot::Get()->GetDefaultWorld()->GetName(), GetName(), &m_Stats); StatSerializer.Load(); - + LOGD("Player %s was read from file \"%s\", spawning at {%.2f, %.2f, %.2f} in world \"%s\"", GetName().c_str(), a_FileName.c_str(), GetPosX(), GetPosY(), GetPosZ(), a_World->GetName().c_str() ); - + return true; } @@ -1853,7 +1906,7 @@ bool cPlayer::SaveToDisk() } // Save the player stats. - // We use the default world name (like bukkit) because stats are shared between dimensions/worlds. + // We use the default world name (like bukkit) because stats are shared between dimensions / worlds. cStatSerializer StatSerializer(cRoot::Get()->GetDefaultWorld()->GetName(), GetName(), &m_Stats); if (!StatSerializer.Save()) { @@ -2061,7 +2114,7 @@ void cPlayer::UpdateMovementStats(const Vector3d & a_DeltaPos) } else { - if (Value >= 25) // Ignore small/slow movement + if (Value >= 25) // Ignore small / slow movement { m_Stats.AddValue(statDistFlown, Value); } @@ -2119,7 +2172,7 @@ void cPlayer::ApplyFoodExhaustionFromMovement() { return; } - + // Calculate the distance travelled, update the last pos: Vector3d Movement(GetPosition() - m_LastPos); Movement.y = 0; // Only take XZ movement into account @@ -2163,15 +2216,24 @@ void cPlayer::LoadRank(void) RankMgr->UpdatePlayerName(m_UUID, m_PlayerName); } m_Permissions = RankMgr->GetPlayerPermissions(m_UUID); + m_Restrictions = RankMgr->GetPlayerRestrictions(m_UUID); RankMgr->GetRankVisuals(m_Rank, m_MsgPrefix, m_MsgSuffix, m_MsgNameColorCode); // Break up the individual permissions on each dot, into m_SplitPermissions: m_SplitPermissions.clear(); m_SplitPermissions.reserve(m_Permissions.size()); - for (AStringVector::const_iterator itr = m_Permissions.begin(), end = m_Permissions.end(); itr != end; ++itr) + for (auto & Permission: m_Permissions) + { + m_SplitPermissions.push_back(StringSplit(Permission, ".")); + } // for Permission - m_Permissions[] + + // Break up the individual restrictions on each dot, into m_SplitRestrictions: + m_SplitRestrictions.clear(); + m_SplitRestrictions.reserve(m_Restrictions.size()); + for (auto & Restriction: m_Restrictions) { - m_SplitPermissions.push_back(StringSplit(*itr, ".")); - } // for itr - m_Permissions[] + m_SplitRestrictions.push_back(StringSplit(Restriction, ".")); + } // for itr - m_Restrictions[] } @@ -2313,7 +2375,7 @@ AString cPlayer::GetUUIDFileName(const AString & a_UUID) { AString UUID = cMojangAPI::MakeUUIDDashed(a_UUID); ASSERT(UUID.length() == 36); - + AString res("players/"); res.append(UUID, 0, 2); res.push_back('/'); @@ -2321,7 +2383,3 @@ AString cPlayer::GetUUIDFileName(const AString & a_UUID) res.append(".json"); return res; } - - - - diff --git a/src/Entities/Player.h b/src/Entities/Player.h index 3dae58dc1..a84fdd0c7 100644 --- a/src/Entities/Player.h +++ b/src/Entities/Player.h @@ -26,42 +26,42 @@ class cPlayer : public cPawn { typedef cPawn super; - + public: static const int MAX_HEALTH; - + static const int MAX_FOOD_LEVEL; - + /** Number of ticks it takes to eat an item */ static const int EATING_TICKS; - + // tolua_end - + CLASS_PROTODEF(cPlayer) - + cPlayer(cClientHandlePtr a_Client, const AString & a_PlayerName); - + virtual ~cPlayer(); virtual void SpawnOn(cClientHandle & a_Client) override; - + virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; virtual void HandlePhysics(std::chrono::milliseconds a_Dt, cChunk &) override { UNUSED(a_Dt); } /** Returns the curently equipped weapon; empty item if none */ virtual cItem GetEquippedWeapon(void) const override { return m_Inventory.GetEquippedItem(); } - + /** Returns the currently equipped helmet; empty item if none */ virtual cItem GetEquippedHelmet(void) const override { return m_Inventory.GetEquippedHelmet(); } - + /** Returns the currently equipped chestplate; empty item if none */ virtual cItem GetEquippedChestplate(void) const override { return m_Inventory.GetEquippedChestplate(); } /** Returns the currently equipped leggings; empty item if none */ virtual cItem GetEquippedLeggings(void) const override { return m_Inventory.GetEquippedLeggings(); } - + /** Returns the currently equipped boots; empty item if none */ virtual cItem GetEquippedBoots(void) const override { return m_Inventory.GetEquippedBoots(); } @@ -104,16 +104,16 @@ public: static int CalcLevelFromXp(int a_CurrentXp); // tolua_end - + /** Starts charging the equipped bow */ void StartChargingBow(void); - + /** Finishes charging the current bow. Returns the number of ticks for which the bow has been charged */ int FinishChargingBow(void); - + /** Cancels the current bow charging */ void CancelChargingBow(void); - + /** Returns true if the player is currently charging the bow */ bool IsChargingBow(void) const { return m_IsChargingBow; } @@ -122,13 +122,13 @@ public: double GetEyeHeight(void) const; // tolua_export Vector3d GetEyePosition(void) const; // tolua_export virtual bool IsOnGround(void) const override { return m_bTouchGround; } - inline double GetStance(void) const { return GetPosY() + 1.62; } // tolua_export // TODO: Proper stance when crouching etc. + inline double GetStance(void) const { return m_Stance; } // tolua_export inline cInventory & GetInventory(void) { return m_Inventory; } // tolua_export inline const cInventory & GetInventory(void) const { return m_Inventory; } /** Gets the contents of the player's associated enderchest */ cItemGrid & GetEnderChestContents(void) { return m_EnderChestContents; } - + inline const cItem & GetEquippedItem(void) const { return GetInventory().GetEquippedItem(); } // tolua_export /** Returns whether the player is climbing (ladders, vines etc.) */ @@ -137,43 +137,49 @@ public: virtual void TeleportToCoords(double a_PosX, double a_PosY, double a_PosZ) override; // tolua_begin - + /** Sends the "look" packet to the player, forcing them to set their rotation to the specified values. a_YawDegrees is clipped to range [-180, +180), a_PitchDegrees is clipped to range [-180, +180) but the client only uses [-90, +90] */ void SendRotation(double a_YawDegrees, double a_PitchDegrees); - + /** Returns the position where projectiles thrown by this player should start, player eye position + adjustment */ Vector3d GetThrowStartPos(void) const; - + /** Returns the initial speed vector of a throw, with a 3D length of a_SpeedCoeff. */ Vector3d GetThrowSpeed(double a_SpeedCoeff) const; - + /** Returns the current gamemode. Partly OBSOLETE, you should use IsGameModeXXX() functions wherever applicable */ eGameMode GetGameMode(void) const { return m_GameMode; } - + /** Returns the current effective gamemode (inherited gamemode is resolved before returning) */ eGameMode GetEffectiveGameMode(void) const { return (m_GameMode == gmNotSet) ? m_World->GetGameMode() : m_GameMode; } - + /** Sets the gamemode for the player. The gamemode may be gmNotSet, in that case the player inherits the world's gamemode. Updates the gamemode on the client (sends the packet) */ void SetGameMode(eGameMode a_GameMode); + // Sets the current gamemode, doesn't check validity, doesn't send update packets to client + void LoginSetGameMode(eGameMode a_GameMode); + + // Updates player's capabilities - flying, visibility, etc. from their gamemode. + void SetCapabilities(); + /** Returns true if the player is in Creative mode, either explicitly, or by inheriting from current world */ bool IsGameModeCreative(void) const; - + /** Returns true if the player is in Survival mode, either explicitly, or by inheriting from current world */ bool IsGameModeSurvival(void) const; - + /** Returns true if the player is in Adventure mode, either explicitly, or by inheriting from current world */ bool IsGameModeAdventure(void) const; - + /** Returns true if the player is in Spectator mode, either explicitly, or by inheriting from current world */ bool IsGameModeSpectator(void) const; - + AString GetIP(void) const { return m_IP; } // tolua_export /** Returns the associated team, nullptr if none */ @@ -195,11 +201,8 @@ public: If the achievement has been already awarded to the player, this method will just increment the stat counter. Returns the _new_ stat value. (0 = Could not award achievement) */ unsigned int AwardAchievement(const eStatistic a_Ach); - + void SetIP(const AString & a_IP); - - // Sets the current gamemode, doesn't check validity, doesn't send update packets to client - void LoginSetGameMode(eGameMode a_GameMode); /** Forces the player to move in the given direction. @deprecated Use SetSpeed instead. */ @@ -210,15 +213,15 @@ public: cWindow * GetWindow(void) { return m_CurrentWindow; } // tolua_export const cWindow * GetWindow(void) const { return m_CurrentWindow; } - + /** Opens the specified window; closes the current one first using CloseWindow() */ void OpenWindow(cWindow * a_Window); // Exported in ManualBindings.cpp - + // tolua_begin - + /** Closes the current window, resets current window to m_InventoryWindow. A plugin may refuse the closing if a_CanRefuse is true */ void CloseWindow(bool a_CanRefuse = true); - + /** Closes the current window if it matches the specified ID, resets current window to m_InventoryWindow */ void CloseWindowIfID(char a_WindowID, bool a_CanRefuse = true); @@ -243,7 +246,7 @@ public: const AString & GetName(void) const { return m_PlayerName; } void SetName(const AString & a_Name) { m_PlayerName = a_Name; } - + // tolua_end bool HasPermission(const AString & a_Permission); // tolua_export @@ -254,10 +257,13 @@ public: static bool PermissionMatches(const AStringVector & a_Permission, const AStringVector & a_Template); // Exported in ManualBindings with AString params /** Returns all the permissions that the player has assigned to them. */ - const AStringVector & GetPermissions(void) { return m_Permissions; } // Exported in ManualBindings.cpp + const AStringVector & GetPermissions(void) const { return m_Permissions; } // Exported in ManualBindings.cpp + + /** Returns all the restrictions that the player has assigned to them. */ + const AStringVector & GetRestrictions(void) const { return m_Restrictions; } // Exported in ManualBindings.cpp // tolua_begin - + /** Returns the full color code to use for this player, based on their rank. The returned value either is empty, or includes the cChatColor::Delimiter. */ AString GetColor(void) const; @@ -276,15 +282,15 @@ public: /** Heals the player by the specified amount of HPs (positive only); sends health update */ virtual void Heal(int a_Health) override; - + int GetFoodLevel (void) const { return m_FoodLevel; } double GetFoodSaturationLevel (void) const { return m_FoodSaturationLevel; } int GetFoodTickTimer (void) const { return m_FoodTickTimer; } double GetFoodExhaustionLevel (void) const { return m_FoodExhaustionLevel; } - + /** Returns true if the player is satiated, i. e. their foodlevel is at the max and they cannot eat anymore */ bool IsSatiated(void) const { return (m_FoodLevel >= MAX_FOOD_LEVEL); } - + void SetFoodLevel (int a_FoodLevel); void SetFoodSaturationLevel (double a_FoodSaturationLevel); void SetFoodTickTimer (int a_FoodTickTimer); @@ -295,10 +301,10 @@ public: /** Adds the specified exhaustion to m_FoodExhaustion. Expects only positive values. */ void AddFoodExhaustion(double a_Exhaustion); - + /** Returns true if the player is currently in the process of eating the currently equipped item */ bool IsEating(void) const { return (m_EatingFinishTick >= 0); } - + /** Returns true if the player is currently flying. */ bool IsFlying(void) const { return m_IsFlying; } @@ -326,16 +332,16 @@ public: GetWorld()->BroadcastEntityAnimation(*this, 2); } } - + /** Starts eating the currently equipped item. Resets the eating timer and sends the proper animation packet */ void StartEating(void); - + /** Finishes eating the currently equipped item. Consumes the item, updates health and broadcasts the packets */ void FinishEating(void); - + /** Aborts the current eating operation */ void AbortEating(void); - + virtual void KilledBy(TakeDamageInfo & a_TDI) override; virtual void Killed(cEntity * a_Victim) override; @@ -353,69 +359,69 @@ public: bool 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() Returns true on success, false on failure */ bool LoadFromDisk(cWorldPtr & a_World); - + /** 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 Returns true on success, false on failure */ bool LoadFromFile(const AString & a_FileName, cWorldPtr & a_World); - + const AString & GetLoadedWorldName() { return m_LoadedWorldName; } void UseEquippedItem(int a_Amount = 1); - + void SendHealth(void); void SendExperience(void); - + /** In UI windows, get the item that the player is dragging */ cItem & GetDraggingItem(void) {return m_DraggingItem; } - + // In UI windows, when inventory-painting: /** Clears the list of slots that are being inventory-painted. To be used by cWindow only */ void ClearInventoryPaintSlots(void); - + /** Adds a slot to the list for inventory painting. To be used by cWindow only */ void AddInventoryPaintSlot(int a_SlotNum); - + /** Returns the list of slots currently stored for inventory painting. To be used by cWindow only */ const cSlotNums & GetInventoryPaintSlots(void) const; - + // tolua_begin - + /** Returns the current relative maximum speed (takes current sprinting / flying state into account) */ double GetMaxSpeed(void) const; - + /** Gets the normal relative maximum speed */ double GetNormalMaxSpeed(void) const { return m_NormalMaxSpeed; } - + /** Gets the sprinting relative maximum speed */ double GetSprintingMaxSpeed(void) const { return m_SprintingMaxSpeed; } - + /** Gets the flying relative maximum speed */ double GetFlyingMaxSpeed(void) const { return m_FlyingMaxSpeed; } - + /** Sets the normal relative maximum speed. Sends the update to player, if needed. */ void SetNormalMaxSpeed(double a_Speed); - + /** Sets the sprinting relative maximum speed. Sends the update to player, if needed. */ void SetSprintingMaxSpeed(double a_Speed); - + /** Sets the flying relative maximum speed. Sends the update to player, if needed. */ void SetFlyingMaxSpeed(double a_Speed); - + /** Sets the crouch status, broadcasts to all visible players */ void SetCrouch(bool a_IsCrouched); - + /** Starts or stops sprinting, sends the max speed update to the client, if needed */ void SetSprint(bool a_IsSprinting); - + /** Flags the player as flying */ void SetFlying(bool a_IsFlying); @@ -439,17 +445,17 @@ public: /** Sets the player's bed (home) position */ void SetBedPos(const Vector3i & a_Pos) { m_LastBedPos = a_Pos; } - + // tolua_end /** Update movement-related statistics. */ void UpdateMovementStats(const Vector3d & a_DeltaPos); - + // tolua_begin /** Returns wheter the player can fly or not. */ virtual bool CanFly(void) const { return m_CanFly; } - + /** Returns the UUID (short format) that has been read from the client, or empty string if not available. */ const AString & GetUUID(void) const { return m_UUID; } @@ -480,16 +486,16 @@ public: bool PlaceBlocks(const sSetBlockVector & a_Blocks); // cEntity overrides: - virtual bool IsCrouched (void) const { return m_IsCrouched; } - virtual bool IsSprinting(void) const { return m_IsSprinting; } - virtual bool IsRclking (void) const { return IsEating() || IsChargingBow(); } + virtual bool IsCrouched (void) const override { return m_IsCrouched; } + virtual bool IsSprinting(void) const override { return m_IsSprinting; } + virtual bool IsRclking (void) const override { return IsEating() || IsChargingBow(); } - virtual void Detach(void); + virtual void Detach(void) override; /** Called by cClientHandle when the client is being destroyed. The player removes its m_ClientHandle ownership so that the ClientHandle gets deleted. */ void RemoveClientHandle(void); - + protected: typedef std::vector<std::vector<AString> > AStringVectorVector; @@ -500,10 +506,18 @@ protected: /** All the permissions that this player has, based on their rank. */ AStringVector m_Permissions; + /** All the restrictions that this player has, based on their rank. */ + AStringVector m_Restrictions; + /** All the permissions that this player has, based on their rank, split into individual dot-delimited parts. This is used mainly by the HasPermission() function to optimize the lookup. */ AStringVectorVector m_SplitPermissions; + /** All the restrictions that this player has, based on their rank, split into individual dot-delimited parts. + This is used mainly by the HasPermission() function to optimize the lookup. */ + AStringVectorVector m_SplitRestrictions; + + // Message visuals: AString m_MsgPrefix, m_MsgSuffix; AString m_MsgNameColorCode; @@ -524,16 +538,16 @@ protected: // Food-related variables: /** Represents the food bar, one point equals half a "drumstick" */ int m_FoodLevel; - + /** "Overcharge" for the m_FoodLevel; is depleted before m_FoodLevel */ double m_FoodSaturationLevel; - + /** Count-up to the healing or damaging action, based on m_FoodLevel */ int m_FoodTickTimer; - + /** 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; bool m_bTouchGround; @@ -553,31 +567,31 @@ protected: eGameMode m_GameMode; AString m_IP; - + /** The item being dragged by the cursor while in a UI window */ cItem m_DraggingItem; std::chrono::steady_clock::time_point m_LastPlayerListTime; cClientHandlePtr m_ClientHandle; - + cSlotNums m_InventoryPaintSlots; - + /** Max speed, relative to the game default. 1 means regular speed, 2 means twice as fast, 0.5 means half-speed. Default value is 1. */ double m_NormalMaxSpeed; - + /** Max speed, relative to the game default max speed, when sprinting. 1 means regular speed, 2 means twice as fast, 0.5 means half-speed. Default value is 1.3. */ double m_SprintingMaxSpeed; - + /** Max speed, relative to the game default flying max speed, when flying. 1 means regular speed, 2 means twice as fast, 0.5 means half-speed. Default value is 1. */ double m_FlyingMaxSpeed; - + bool m_IsCrouched; bool m_IsSprinting; bool m_IsFlying; @@ -618,7 +632,7 @@ protected: Will not apply food penalties if found to be true; will set to false after processing */ bool m_bIsTeleporting; - + /** The short UUID (no dashes) of the player, as read from the ClientHandle. If no ClientHandle is given, the UUID is initialized to empty. */ AString m_UUID; @@ -631,14 +645,14 @@ protected: void ResolvePermissions(void); void ResolveGroups(void); - virtual void Destroyed(void); + virtual void Destroyed(void) override; - /** Filters out damage for creative mode/friendly fire */ + /** Filters out damage for creative mode / friendly fire */ virtual bool DoTakeDamage(TakeDamageInfo & TDI) override; /** Stops players from burning in creative mode */ virtual void TickBurning(cChunk & a_Chunk) override; - + /** Called in each tick to handle food-related processing */ void HandleFood(void); @@ -655,7 +669,3 @@ protected: This can be used both for online and offline UUIDs. */ AString GetUUIDFileName(const AString & a_UUID); } ; // tolua_export - - - - diff --git a/src/Entities/ProjectileEntity.cpp b/src/Entities/ProjectileEntity.cpp index 4684e3e09..05b7669cd 100644 --- a/src/Entities/ProjectileEntity.cpp +++ b/src/Entities/ProjectileEntity.cpp @@ -227,6 +227,8 @@ cProjectileEntity::cProjectileEntity(eKind a_Kind, cEntity * a_Creator, double a ), m_IsInGround(false) { + SetGravity(-12.0f); + SetAirDrag(0.01f); } @@ -242,6 +244,8 @@ cProjectileEntity::cProjectileEntity(eKind a_Kind, cEntity * a_Creator, const Ve SetSpeed(a_Speed); SetYawFromSpeed(); SetPitchFromSpeed(); + SetGravity(-12.0f); + SetAirDrag(0.01f); } @@ -349,9 +353,11 @@ void cProjectileEntity::HandlePhysics(std::chrono::milliseconds a_Dt, cChunk & a return; } - const Vector3d PerTickSpeed = GetSpeed() / 20; + auto DtSec = std::chrono::duration_cast<std::chrono::duration<double>>(a_Dt); + + const Vector3d DeltaSpeed = GetSpeed() * DtSec.count(); const Vector3d Pos = GetPosition(); - const Vector3d NextPos = Pos + PerTickSpeed; + const Vector3d NextPos = Pos + DeltaSpeed; // Test for entity collisions: cProjectileEntityCollisionCallback EntityCollisionCallback(this, Pos, NextPos); @@ -388,8 +394,8 @@ void cProjectileEntity::HandlePhysics(std::chrono::milliseconds a_Dt, cChunk & a // Add slowdown and gravity effect to the speed: Vector3d NewSpeed(GetSpeed()); - NewSpeed.y += m_Gravity / 20; - NewSpeed *= TracerCallback.GetSlowdownCoeff(); + NewSpeed.y += m_Gravity * DtSec.count(); + NewSpeed -= NewSpeed * (m_AirDrag * 20.0f) * DtSec.count(); SetSpeed(NewSpeed); SetYawFromSpeed(); SetPitchFromSpeed(); diff --git a/src/Entities/ProjectileEntity.h b/src/Entities/ProjectileEntity.h index de460a8ad..7ee87f93a 100644 --- a/src/Entities/ProjectileEntity.h +++ b/src/Entities/ProjectileEntity.h @@ -109,7 +109,7 @@ protected: eKind m_ProjectileKind; /** The structure for containing the entity ID and name who has created this projectile - The ID and/or name may be nullptr (e.g. for dispensers/mobs). */ + The ID and / or name may be nullptr (e.g. for dispensers / mobs). */ CreatorData m_CreatorData; /** True if the projectile has hit the ground and is stuck there */ diff --git a/src/Entities/TNTEntity.cpp b/src/Entities/TNTEntity.cpp index a89d2f300..d849bd4c9 100644 --- a/src/Entities/TNTEntity.cpp +++ b/src/Entities/TNTEntity.cpp @@ -12,6 +12,8 @@ cTNTEntity::cTNTEntity(double a_X, double a_Y, double a_Z, int a_FuseTicks) : super(etTNT, a_X, a_Y, a_Z, 0.98, 0.98), m_FuseTicks(a_FuseTicks) { + SetGravity(-16.0f); + SetAirDrag(0.02f); } @@ -22,6 +24,8 @@ cTNTEntity::cTNTEntity(const Vector3d & a_Pos, int a_FuseTicks) : super(etTNT, a_Pos.x, a_Pos.y, a_Pos.z, 0.98, 0.98), m_FuseTicks(a_FuseTicks) { + SetGravity(-16.0f); + SetAirDrag(0.4f); } diff --git a/src/Entities/WitherSkullEntity.cpp b/src/Entities/WitherSkullEntity.cpp index a7e774bba..dc95e3edd 100644 --- a/src/Entities/WitherSkullEntity.cpp +++ b/src/Entities/WitherSkullEntity.cpp @@ -16,6 +16,8 @@ cWitherSkullEntity::cWitherSkullEntity(cEntity * a_Creator, double a_X, double a super(pkWitherSkull, a_Creator, a_X, a_Y, a_Z, 0.25, 0.25) { SetSpeed(a_Speed); + SetGravity(0); + SetAirDrag(0); } |