From 1394fc8eb5c8c0ac49bb64ce1871a80470f058c3 Mon Sep 17 00:00:00 2001 From: Tiger Wang Date: Tue, 6 Apr 2021 12:26:43 +0100 Subject: [PATCH] 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 --- Server/Plugins/APIDump/APIDesc.lua | 10 -- src/Broadcaster.cpp | 13 +++ src/ClientHandle.cpp | 18 ++-- src/ClientHandle.h | 2 +- src/Entities/Entity.cpp | 2 +- src/Entities/Player.cpp | 158 ++++++++++++----------------- src/Entities/Player.h | 16 ++- src/Protocol/Packetizer.cpp | 1 - src/Protocol/Protocol.h | 2 - src/Protocol/Protocol_1_12.cpp | 2 - src/Protocol/Protocol_1_13.cpp | 3 +- src/Protocol/Protocol_1_8.cpp | 84 +++++++-------- src/Protocol/Protocol_1_8.h | 1 - src/Protocol/Protocol_1_9.cpp | 54 ---------- src/Protocol/Protocol_1_9.h | 4 - src/World.h | 1 + 16 files changed, 134 insertions(+), 237 deletions(-) diff --git a/Server/Plugins/APIDump/APIDesc.lua b/Server/Plugins/APIDump/APIDesc.lua index bffc40928..bebcda6d6 100644 --- a/Server/Plugins/APIDump/APIDesc.lua +++ b/Server/Plugins/APIDump/APIDesc.lua @@ -10781,16 +10781,6 @@ a_Player:OpenWindow(Window); }, Notes = "Returns true if the player is satiated (cannot eat).", }, - IsVisible = - { - Returns = - { - { - Type = "boolean", - }, - }, - Notes = "Returns true if the player is visible to other players", - }, LoadRank = { Notes = "Reloads the player's rank, message visuals and permissions from the {{cRankManager}}, based on the player's current rank.", diff --git a/src/Broadcaster.cpp b/src/Broadcaster.cpp index 09b834471..770a3987c 100644 --- a/src/Broadcaster.cpp +++ b/src/Broadcaster.cpp @@ -345,6 +345,19 @@ void cWorld::BroadcastEntityPosition(const cEntity & a_Entity, const cClientHand +void cWorld::BroadcastEntityProperties(const cEntity & a_Entity) +{ + ForClientsWithEntity(a_Entity, *this, nullptr, [&](cClientHandle & a_Client) + { + a_Client.SendEntityProperties(a_Entity); + } + ); +} + + + + + void cWorld::BroadcastEntityStatus(const cEntity & a_Entity, Int8 a_Status, const cClientHandle * a_Exclude) { ForClientsWithEntity(a_Entity, *this, a_Exclude, [&](cClientHandle & a_Client) diff --git a/src/ClientHandle.cpp b/src/ClientHandle.cpp index 75eb94bb5..2050fd1ed 100644 --- a/src/ClientHandle.cpp +++ b/src/ClientHandle.cpp @@ -2528,6 +2528,15 @@ void cClientHandle::SendEntityPosition(const cEntity & a_Entity) +void cClientHandle::SendEntityProperties(const cEntity & a_Entity) +{ + m_Protocol->SendEntityProperties(a_Entity); +} + + + + + void cClientHandle::SendEntityStatus(const cEntity & a_Entity, char a_Status) { m_Protocol->SendEntityStatus(a_Entity, a_Status); @@ -2737,15 +2746,6 @@ void cClientHandle::SendPlayerListUpdatePing() -void cClientHandle::SendPlayerMaxSpeed(void) -{ - m_Protocol->SendPlayerMaxSpeed(); -} - - - - - void cClientHandle::SendPlayerMoveLook(void) { /* diff --git a/src/ClientHandle.h b/src/ClientHandle.h index 384963a2d..971edfb8a 100644 --- a/src/ClientHandle.h +++ b/src/ClientHandle.h @@ -169,6 +169,7 @@ public: // tolua_export void SendEntityLook (const cEntity & a_Entity); void SendEntityMetadata (const cEntity & a_Entity); void SendEntityPosition (const cEntity & a_Entity); + void SendEntityProperties (const cEntity & a_Entity); void SendEntityStatus (const cEntity & a_Entity, char a_Status); void SendEntityVelocity (const cEntity & a_Entity); void SendExperience (void); @@ -191,7 +192,6 @@ public: // tolua_export void SendPlayerListUpdateDisplayName(const cPlayer & a_Player, const AString & a_CustomName); void SendPlayerListUpdateGameMode (const cPlayer & a_Player); void SendPlayerListUpdatePing (); - void SendPlayerMaxSpeed (void); ///< Informs the client of the maximum player speed (1.6.1+) void SendPlayerMoveLook (void); void SendPlayerPosition (void); void SendPlayerSpawn (const cPlayer & a_Player); 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; + UpdateCapabilities(); + m_ClientHandle->SendGameMode(a_GameMode); - - SetCapabilities(); - + 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; diff --git a/src/Protocol/Packetizer.cpp b/src/Protocol/Packetizer.cpp index e71cba086..6c5e5cd63 100644 --- a/src/Protocol/Packetizer.cpp +++ b/src/Protocol/Packetizer.cpp @@ -99,7 +99,6 @@ AString cPacketizer::PacketTypeToStr(cProtocol::ePacketType a_PacketType) case cProtocol::pktPlayerAbilities: return "pktPlayerAbilities"; case cProtocol::pktPlayerList: return "pktPlayerList"; case cProtocol::pktPlayerListHeaderFooter: return "pktPlayerListHeaderFooter"; - case cProtocol::pktPlayerMaxSpeed: return "pktPlayerMaxSpeed"; case cProtocol::pktPlayerMoveLook: return "pktPlayerMoveLook"; case cProtocol::pktPluginMessage: return "pktPluginMessage"; case cProtocol::pktRemoveEntityEffect: return "pktRemoveEntityEffect"; diff --git a/src/Protocol/Protocol.h b/src/Protocol/Protocol.h index 26a411909..0f615f4dc 100644 --- a/src/Protocol/Protocol.h +++ b/src/Protocol/Protocol.h @@ -105,7 +105,6 @@ public: pktPlayerAbilities, pktPlayerList, pktPlayerListHeaderFooter, - pktPlayerMaxSpeed, pktPlayerMoveLook, pktPluginMessage, pktRemoveEntityEffect, @@ -412,7 +411,6 @@ public: virtual void SendPlayerListUpdateGameMode (const cPlayer & a_Player) = 0; virtual void SendPlayerListUpdatePing () = 0; virtual void SendPlayerListUpdateDisplayName(const cPlayer & a_Player, const AString & a_CustomName) = 0; - virtual void SendPlayerMaxSpeed (void) = 0; ///< Informs the client of the maximum player speed (1.6.1+) virtual void SendPlayerMoveLook (void) = 0; virtual void SendPlayerPosition (void) = 0; virtual void SendPlayerSpawn (const cPlayer & a_Player) = 0; diff --git a/src/Protocol/Protocol_1_12.cpp b/src/Protocol/Protocol_1_12.cpp index 06238e408..b340d87f5 100644 --- a/src/Protocol/Protocol_1_12.cpp +++ b/src/Protocol/Protocol_1_12.cpp @@ -1003,7 +1003,6 @@ UInt32 cProtocol_1_12::GetPacketID(cProtocol::ePacketType a_Packet) case pktExperience: return 0x3f; case pktHeldItemChange: return 0x39; case pktLeashEntity: return 0x3c; - case pktPlayerMaxSpeed: return 0x4d; case pktRemoveEntityEffect: return 0x32; case pktResourcePack: return 0x33; case pktRespawn: return 0x34; @@ -1184,7 +1183,6 @@ UInt32 cProtocol_1_12_1::GetPacketID(ePacketType a_Packet) case pktPlayerList: return 0x2e; case pktPlayerListHeaderFooter: return 0x4a; case pktPlayerAbilities: return 0x2c; - case pktPlayerMaxSpeed: return 0x4e; case pktPlayerMoveLook: return 0x2f; case pktRemoveEntityEffect: return 0x33; case pktResourcePack: return 0x34; diff --git a/src/Protocol/Protocol_1_13.cpp b/src/Protocol/Protocol_1_13.cpp index b892e8433..28b6c26f9 100644 --- a/src/Protocol/Protocol_1_13.cpp +++ b/src/Protocol/Protocol_1_13.cpp @@ -372,8 +372,7 @@ UInt32 cProtocol_1_13::GetPacketID(ePacketType a_PacketType) case pktParticleEffect: return 0x24; case pktPlayerAbilities: return 0x2e; case pktPlayerList: return 0x30; - case pktPlayerListHeaderFooter: return 0x4E; - case pktPlayerMaxSpeed: return 0x52; + case pktPlayerListHeaderFooter: return 0x4e; case pktPlayerMoveLook: return 0x32; case pktPluginMessage: return 0x19; case pktRemoveEntityEffect: return 0x36; diff --git a/src/Protocol/Protocol_1_8.cpp b/src/Protocol/Protocol_1_8.cpp index 3c377ce21..179e2b2ea 100644 --- a/src/Protocol/Protocol_1_8.cpp +++ b/src/Protocol/Protocol_1_8.cpp @@ -832,9 +832,6 @@ void cProtocol_1_8_0::SendLogin(const cPlayer & a_Player, const cWorld & a_World cPacketizer Pkt(*this, pktDifficulty); Pkt.WriteBEInt8(1); } - - // Send player abilities: - SendPlayerAbilities(); } @@ -1132,35 +1129,6 @@ void cProtocol_1_8_0::SendPlayerListUpdatePing() -void cProtocol_1_8_0::SendPlayerMaxSpeed(void) -{ - ASSERT(m_State == 3); // In game mode? - - cPacketizer Pkt(*this, pktEntityProperties); - cPlayer * Player = m_Client->GetPlayer(); - Pkt.WriteVarInt32(Player->GetUniqueID()); - Pkt.WriteBEInt32(1); // Count - Pkt.WriteString("generic.movementSpeed"); - // The default game speed is 0.1, multiply that value by the relative speed: - Pkt.WriteBEDouble(0.1 * Player->GetNormalMaxSpeed()); - if (Player->IsSprinting()) - { - Pkt.WriteVarInt32(1); // Modifier count - Pkt.WriteBEUInt64(0x662a6b8dda3e4c1c); - Pkt.WriteBEUInt64(0x881396ea6097278d); // UUID of the modifier - Pkt.WriteBEDouble(Player->GetSprintingMaxSpeed() - Player->GetNormalMaxSpeed()); - Pkt.WriteBEUInt8(2); - } - else - { - Pkt.WriteVarInt32(0); // Modifier count - } -} - - - - - void cProtocol_1_8_0::SendPlayerMoveLook(void) { ASSERT(m_State == 3); // In game mode? @@ -1201,12 +1169,8 @@ void cProtocol_1_8_0::SendPlayerSpawn(const cPlayer & a_Player) Pkt.WriteFPInt(LastSentPos.z); Pkt.WriteByteAngle(a_Player.GetYaw()); Pkt.WriteByteAngle(a_Player.GetPitch()); - short ItemType = a_Player.GetEquippedItem().IsEmpty() ? 0 : a_Player.GetEquippedItem().m_ItemType; - Pkt.WriteBEInt16(ItemType); - Pkt.WriteBEUInt8((3 << 5) | 6); // Metadata: float + index 6 - Pkt.WriteBEFloat(static_cast(a_Player.GetHealth())); - Pkt.WriteBEUInt8((4 << 5 | (2 & 0x1F)) & 0xFF); - Pkt.WriteString(a_Player.GetName()); + Pkt.WriteBEInt16(a_Player.GetEquippedItem().IsEmpty() ? 0 : a_Player.GetEquippedItem().m_ItemType); + WriteEntityMetadata(Pkt, a_Player); Pkt.WriteBEUInt8(0x7f); // Metadata: end } @@ -3377,11 +3341,15 @@ void cProtocol_1_8_0::WriteEntityMetadata(cPacketizer & a_Pkt, const cEntity & a { auto & Player = static_cast(a_Entity); - // Player health (not handled since players aren't monsters) + // Player name: + a_Pkt.WriteBEUInt8(0x82); + a_Pkt.WriteString(Player.GetName()); + + // Player health: a_Pkt.WriteBEUInt8(0x66); a_Pkt.WriteBEFloat(static_cast(Player.GetHealth())); - // Skin flags + // Skin flags: a_Pkt.WriteBEUInt8(0x0A); a_Pkt.WriteBEUInt8(static_cast(Player.GetSkinParts())); @@ -3815,18 +3783,38 @@ void cProtocol_1_8_0::WriteMobMetadata(cPacketizer & a_Pkt, const cMonster & a_M void cProtocol_1_8_0::WriteEntityProperties(cPacketizer & a_Pkt, const cEntity & a_Entity) { - if (!a_Entity.IsMob()) + if (a_Entity.IsPlayer()) { - // No properties for anything else than mobs - a_Pkt.WriteBEInt32(0); - return; + const auto & Player = static_cast(a_Entity); + + a_Pkt.WriteBEInt32(1); // Count. + a_Pkt.WriteString("generic.movementSpeed"); + a_Pkt.WriteBEDouble(0.1 * Player.GetNormalMaxSpeed()); // The default game speed is 0.1, multiply that value by the relative speed. + + // It seems the modifiers aren't conditionally activated; their effects are applied immediately! + // We have to keep on re-sending this packet when the client notifies us of sprint start and end, and so on. Strange. + + if (Player.IsSprinting()) + { + a_Pkt.WriteVarInt32(1); // Modifier count. + a_Pkt.WriteBEUInt64(0x662a6b8dda3e4c1c); + a_Pkt.WriteBEUInt64(0x881396ea6097278d); // UUID of the modifier (sprinting speed boost). + a_Pkt.WriteBEDouble(Player.GetSprintingMaxSpeed() - Player.GetNormalMaxSpeed()); + a_Pkt.WriteBEUInt8(2); + } + else + { + a_Pkt.WriteVarInt32(0); + } } + else + { + // const cMonster & Mob = (const cMonster &)a_Entity; - // const cMonster & Mob = (const cMonster &)a_Entity; + // TODO: Send properties and modifiers based on the mob type - // TODO: Send properties and modifiers based on the mob type - - a_Pkt.WriteBEInt32(0); // NumProperties + a_Pkt.WriteBEInt32(0); + } } diff --git a/src/Protocol/Protocol_1_8.h b/src/Protocol/Protocol_1_8.h index 26fc6e8a8..44168e499 100644 --- a/src/Protocol/Protocol_1_8.h +++ b/src/Protocol/Protocol_1_8.h @@ -95,7 +95,6 @@ public: virtual void SendPlayerListUpdateDisplayName(const cPlayer & a_Player, const AString & a_CustomName) override; virtual void SendPlayerListUpdateGameMode (const cPlayer & a_Player) override; virtual void SendPlayerListUpdatePing () override; - virtual void SendPlayerMaxSpeed (void) override; virtual void SendPlayerMoveLook (void) override; virtual void SendPlayerPosition (void) override; virtual void SendPlayerSpawn (const cPlayer & a_Player) override; diff --git a/src/Protocol/Protocol_1_9.cpp b/src/Protocol/Protocol_1_9.cpp index 0827b8f0b..0ae451117 100644 --- a/src/Protocol/Protocol_1_9.cpp +++ b/src/Protocol/Protocol_1_9.cpp @@ -490,35 +490,6 @@ void cProtocol_1_9_0::SendMapData(const cMap & a_Map, int a_DataStartX, int a_Da -void cProtocol_1_9_0::SendPlayerMaxSpeed(void) -{ - ASSERT(m_State == 3); // In game mode? - - cPacketizer Pkt(*this, pktPlayerMaxSpeed); - cPlayer * Player = m_Client->GetPlayer(); - Pkt.WriteVarInt32(Player->GetUniqueID()); - Pkt.WriteBEInt32(1); // Count - Pkt.WriteString("generic.movementSpeed"); - // The default game speed is 0.1, multiply that value by the relative speed: - Pkt.WriteBEDouble(0.1 * Player->GetNormalMaxSpeed()); - if (Player->IsSprinting()) - { - Pkt.WriteVarInt32(1); // Modifier count - Pkt.WriteBEUInt64(0x662a6b8dda3e4c1c); - Pkt.WriteBEUInt64(0x881396ea6097278d); // UUID of the modifier - Pkt.WriteBEDouble(Player->GetSprintingMaxSpeed() - Player->GetNormalMaxSpeed()); - Pkt.WriteBEUInt8(2); - } - else - { - Pkt.WriteVarInt32(0); // Modifier count - } -} - - - - - void cProtocol_1_9_0::SendPlayerMoveLook(void) { ASSERT(m_State == 3); // In game mode? @@ -690,7 +661,6 @@ UInt32 cProtocol_1_9_0::GetPacketID(cProtocol::ePacketType a_Packet) case pktPlayerAbilities: return 0x2b; case pktPlayerList: return 0x2d; case pktPlayerListHeaderFooter: return 0x48; - case pktPlayerMaxSpeed: return 0x4b; case pktPlayerMoveLook: return 0x2e; case pktPluginMessage: return 0x18; case pktRemoveEntityEffect: return 0x31; @@ -2271,26 +2241,6 @@ void cProtocol_1_9_0::WriteMobMetadata(cPacketizer & a_Pkt, const cMonster & a_M -void cProtocol_1_9_0::WriteEntityProperties(cPacketizer & a_Pkt, const cEntity & a_Entity) -{ - if (!a_Entity.IsMob()) - { - // No properties for anything else than mobs - a_Pkt.WriteBEInt32(0); - return; - } - - // const cMonster & Mob = (const cMonster &)a_Entity; - - // TODO: Send properties and modifiers based on the mob type - - a_Pkt.WriteBEInt32(0); // NumProperties -} - - - - - //////////////////////////////////////////////////////////////////////////////// // cProtocol_1_9_1: @@ -2320,9 +2270,6 @@ void cProtocol_1_9_1::SendLogin(const cPlayer & a_Player, const cWorld & a_World cPacketizer Pkt(*this, pktDifficulty); Pkt.WriteBEInt8(1); } - - // Send player abilities: - SendPlayerAbilities(); } @@ -2405,7 +2352,6 @@ UInt32 cProtocol_1_9_4::GetPacketID(cProtocol::ePacketType a_Packet) case pktCollectEntity: return 0x48; case pktEntityEffect: return 0x4b; case pktEntityProperties: return 0x4a; - case pktPlayerMaxSpeed: return 0x4a; case pktPlayerListHeaderFooter: return 0x47; case pktTeleportEntity: return 0x49; diff --git a/src/Protocol/Protocol_1_9.h b/src/Protocol/Protocol_1_9.h index be2af147c..190b20005 100644 --- a/src/Protocol/Protocol_1_9.h +++ b/src/Protocol/Protocol_1_9.h @@ -57,7 +57,6 @@ public: virtual void SendLeashEntity (const cEntity & a_Entity, const cEntity & a_EntityLeashedTo) override; virtual void SendMapData (const cMap & a_Map, int a_DataStartX, int a_DataStartY) override; virtual void SendPaintingSpawn (const cPainting & a_Painting) override; - virtual void SendPlayerMaxSpeed (void) override; virtual void SendPlayerMoveLook (void) override; virtual void SendPlayerSpawn (const cPlayer & a_Player) override; virtual void SendSoundEffect (const AString & a_SoundName, double a_X, double a_Y, double a_Z, float a_Volume, float a_Pitch) override; @@ -125,9 +124,6 @@ protected: /** Writes the mob-specific metadata for the specified mob */ virtual void WriteMobMetadata(cPacketizer & a_Pkt, const cMonster & a_Mob) override; - /** Writes the entity properties for the specified entity, including the Count field. */ - virtual void WriteEntityProperties(cPacketizer & a_Pkt, const cEntity & a_Entity) override; - /** Types used within metadata */ enum eMetadataType { diff --git a/src/World.h b/src/World.h index 9c488c645..82dd04b49 100644 --- a/src/World.h +++ b/src/World.h @@ -184,6 +184,7 @@ public: virtual void BroadcastEntityLook (const cEntity & a_Entity, const cClientHandle * a_Exclude = nullptr) override; virtual void BroadcastEntityMetadata (const cEntity & a_Entity, const cClientHandle * a_Exclude = nullptr) override; virtual void BroadcastEntityPosition (const cEntity & a_Entity, const cClientHandle * a_Exclude = nullptr) override; + void BroadcastEntityProperties (const cEntity & a_Entity); virtual void BroadcastEntityStatus (const cEntity & a_Entity, Int8 a_Status, const cClientHandle * a_Exclude = nullptr) override; virtual void BroadcastEntityVelocity (const cEntity & a_Entity, const cClientHandle * a_Exclude = nullptr) override; virtual void BroadcastEntityAnimation (const cEntity & a_Entity, Int8 a_Animation, const cClientHandle * a_Exclude = nullptr) override; // tolua_export