diff --git a/Server/Plugins/APIDump/APIDesc.lua b/Server/Plugins/APIDump/APIDesc.lua index 2bf9f83b0..94082cd94 100644 --- a/Server/Plugins/APIDump/APIDesc.lua +++ b/Server/Plugins/APIDump/APIDesc.lua @@ -11351,6 +11351,16 @@ a_Player:OpenWindow(Window); }, Notes = "Returns the player's current maximum speed, relative to the game default speed. Takes into account the sprinting / flying status.", }, + GetMainHand = + { + Returns = + { + { + Type = "eMainHand", + }, + }, + Notes = "Returns the player's main hand.", + }, GetName = { Returns = @@ -11411,6 +11421,16 @@ a_Player:OpenWindow(Window); }, Notes = "Returns the player's maximum sprinting speed, relative to the game default speed. Defaults to 1.3, but plugins may modify it for faster or slower sprinting.", }, + GetSkinParts = + { + Returns = + { + { + Type = "number", + }, + }, + Notes = "Returns the player's current set of skin part flags. This is a bitwise OR of various {{eSkinPart}} constants. Note that HasSkinPart may be easier to use in most situations.", + }, GetStance = { Returns = @@ -11535,6 +11555,23 @@ a_Player:OpenWindow(Window); }, Notes = "Returns true if the player has the specified permission", }, + HasSkinPart = + { + Params = + { + { + Name = "Part", + Type = "eSkinPart", + }, + }, + Returns = + { + { + Type = "boolean", + }, + }, + Notes = "Returns true if the player has the specified skin part enabled", + }, Heal = { Params = @@ -12063,6 +12100,17 @@ a_Player:OpenWindow(Window); }, Notes = "Sets the 'IsFishing' flag for the player. The floater entity ID is expected for the true variant, it can be omitted when IsFishing is false. FIXME: Undefined behavior when multiple fishing rods are used simultanously", }, + SetMainHand = + { + Params = + { + { + Name = "Hand", + Type = "eMainHand", + }, + }, + Notes = "Sets the main hand of the player.", + }, SetName = { Params = @@ -12096,6 +12144,17 @@ a_Player:OpenWindow(Window); }, Notes = "Sets whether the player is sprinting or not.", }, + SetSkinParts = + { + Params = + { + { + Name = "Parts", + Type = "number", + }, + }, + Notes = "Sets the skin part flags of the player. The value should be a bitwise OR of several {{eSkinPart}} constants.", + }, SetSprintingMaxSpeed = { Params = @@ -16642,6 +16701,54 @@ end { Notes = "Something concerning (i.e. reload) is about to happen", }, + hMain = + { + Notes = "The main hand", + }, + hOff = + { + Notes = "The off hand", + }, + mhLeft = + { + Notes = "The left hand is the main hand", + }, + mhRight = + { + Notes = "The right hand is the main hand", + }, + spCape = + { + Notes = "The cape skin part", + }, + spJacket = + { + Notes = "The jacket skin part", + }, + spLeftSleeve = + { + Notes = "The left sleeve skin part", + }, + spRightSleeve = + { + Notes = "The right sleeve skin part", + }, + spLeftPants = + { + Notes = "The left pants leg skin part", + }, + spRightPants = + { + Notes = "The right pants leg skin part", + }, + spHat = + { + Notes = "The hat/head skin part", + }, + spMask = + { + Notes = "A mask of all valid skin parts combined", + }, }, ConstantGroups = { @@ -16727,6 +16834,32 @@ end gmXXX constants, the eGameMode_ constants are deprecated and will be removed from the API. ]], }, + eHand = + { + Include = + { + "hMain", + "hOff", + }, + TextBefore = [[ + These constants represent the main and off hand. Currently, these constants are not used, but + are provided for future use when dual-wielding is functional. An action or item can be in the + main hand or the off hand. The main hand can be either the left or the right hand - use + {{cPlayer}}:GetMainHand() to determine which (see {{eMainHand}}). + ]], + }, + eMainHand = + { + Include = + { + "^mh.*", + }, + TextBefore = [[ + These constants identify which hand is the main hand. The main hand can either be the left hand + or the right hand. Note that this is only visual, as the client behaves the same regardless of the + main hand setting. See {{cPlayer}}:GetMainHand(). + ]], + }, EMCSBiome = { Include = "^bi.*", @@ -16819,6 +16952,16 @@ end The following constants define the block types that are propelled outwards after an explosion. ]], }, + eSkinPart = + { + Include = + { + "^sp.*", + }, + TextBefore = [[ + These constants represent various skin part flags. + ]], + }, eSpreadSource = { Include = "^ss.*", diff --git a/src/Defines.h b/src/Defines.h index f8af22ef2..a1de94897 100644 --- a/src/Defines.h +++ b/src/Defines.h @@ -203,6 +203,42 @@ enum eMobHeadRotation +enum eHand +{ + hMain = 0, + hOff = 1, +} ; + + + + + +enum eMainHand +{ + mhLeft = 0, + mhRight = 1, +} ; + + + + + +enum eSkinPart +{ + spCape = 0x01, + spJacket = 0x02, + spLeftSleeve = 0x04, + spRightSleeve = 0x08, + spLeftPants = 0x10, + spRightPants = 0x20, + spHat = 0x40, + spMask = 0x7F, +}; + + + + + inline const char * ClickActionToString(int a_ClickAction) { switch (a_ClickAction) diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp index f3ea45cf4..9fba0d015 100644 --- a/src/Entities/Player.cpp +++ b/src/Entities/Player.cpp @@ -90,7 +90,9 @@ cPlayer::cPlayer(cClientHandlePtr a_Client, const AString & a_PlayerName) : m_TicksUntilNextSave(PLAYER_INVENTORY_SAVE_INTERVAL), m_bIsTeleporting(false), m_UUID((a_Client != nullptr) ? a_Client->GetUUID() : ""), - m_CustomName("") + m_CustomName(""), + m_SkinParts(0), + m_MainHand(mhRight) { ASSERT(a_PlayerName.length() <= 16); // Otherwise this player could crash many clients... @@ -2632,6 +2634,26 @@ bool cPlayer::PlaceBlocks(const sSetBlockVector & a_Blocks) +void cPlayer::SetSkinParts(int a_Parts) +{ + m_SkinParts = a_Parts & spMask; + m_World->BroadcastEntityMetadata(*this, m_ClientHandle.get()); +} + + + + + +void cPlayer::SetMainHand(eMainHand a_Hand) +{ + m_MainHand = a_Hand; + m_World->BroadcastEntityMetadata(*this, m_ClientHandle.get()); +} + + + + + void cPlayer::AttachTo(cEntity * a_AttachTo) { // Different attach, if this is a spectator diff --git a/src/Entities/Player.h b/src/Entities/Player.h index dab814692..b592913bd 100644 --- a/src/Entities/Player.h +++ b/src/Entities/Player.h @@ -499,6 +499,13 @@ public: The blocks in range (a_BlockX - a_Range, a_BlockX + a_Range) are sent (NY-metric). */ void SendBlocksAround(int a_BlockX, int a_BlockY, int a_BlockZ, int a_Range = 1); + bool HasSkinPart(eSkinPart a_Part) const { return (m_SkinParts & a_Part) != 0; } + int GetSkinParts(void) const { return m_SkinParts; } + void SetSkinParts(int a_Parts); + + eMainHand GetMainHand(void) const { return m_MainHand; } + void SetMainHand(eMainHand a_Hand); + // tolua_end /** Calls the block placement hooks and places the blocks in the world. @@ -682,6 +689,12 @@ protected: AString m_CustomName; + /** Displayed skin part bit mask */ + int m_SkinParts; + + /** The main hand of the player */ + eMainHand m_MainHand; + /** Sets the speed and sends it to the client, so that they are forced to move so. */ virtual void DoSetSpeed(double a_SpeedX, double a_SpeedY, double a_SpeedZ) override; diff --git a/src/Protocol/Protocol_1_10.cpp b/src/Protocol/Protocol_1_10.cpp index c0d9d921f..1cd7153a7 100644 --- a/src/Protocol/Protocol_1_10.cpp +++ b/src/Protocol/Protocol_1_10.cpp @@ -400,6 +400,14 @@ void cProtocol_1_10_0::WriteEntityMetadata(cPacketizer & a_Pkt, const cEntity & a_Pkt.WriteBEUInt8(LIVING_HEALTH); a_Pkt.WriteBEUInt8(METADATA_TYPE_FLOAT); a_Pkt.WriteBEFloat(static_cast(Player.GetHealth())); + + a_Pkt.WriteBEUInt8(PLAYER_DISPLAYED_SKIN_PARTS); + a_Pkt.WriteBEUInt8(METADATA_TYPE_BYTE); + a_Pkt.WriteBEUInt8(static_cast(Player.GetSkinParts())); + + a_Pkt.WriteBEUInt8(PLAYER_MAIN_HAND); + a_Pkt.WriteBEUInt8(METADATA_TYPE_BYTE); + a_Pkt.WriteBEUInt8(static_cast(Player.GetMainHand())); break; } case cEntity::etPickup: diff --git a/src/Protocol/Protocol_1_8.cpp b/src/Protocol/Protocol_1_8.cpp index bbeb3ae8b..2e7902303 100644 --- a/src/Protocol/Protocol_1_8.cpp +++ b/src/Protocol/Protocol_1_8.cpp @@ -2311,11 +2311,12 @@ void cProtocol_1_8_0::HandlePacketClientSettings(cByteBuffer & a_ByteBuffer) HANDLE_READ(a_ByteBuffer, ReadBEUInt8, UInt8, ViewDistance); HANDLE_READ(a_ByteBuffer, ReadBEUInt8, UInt8, ChatFlags); HANDLE_READ(a_ByteBuffer, ReadBool, bool, ChatColors); - HANDLE_READ(a_ByteBuffer, ReadBEUInt8, UInt8, SkinFlags); + HANDLE_READ(a_ByteBuffer, ReadBEUInt8, UInt8, SkinParts); m_Client->SetLocale(Locale); m_Client->SetViewDistance(ViewDistance); - // TODO: Handle other values + m_Client->GetPlayer()->SetSkinParts(SkinParts); + // TODO: Handle chat flags and chat colors } @@ -3234,7 +3235,20 @@ void cProtocol_1_8_0::WriteEntityMetadata(cPacketizer & a_Pkt, const cEntity & a switch (a_Entity.GetEntityType()) { - case cEntity::etPlayer: break; // TODO? + case cEntity::etPlayer: + { + auto & Player = reinterpret_cast(a_Entity); + + // Player health (not handled since players aren't monsters) + a_Pkt.WriteBEUInt8(0x66); + a_Pkt.WriteBEFloat(static_cast(Player.GetHealth())); + + // Skin flags + a_Pkt.WriteBEUInt8(0x0A); + a_Pkt.WriteBEUInt8(static_cast(Player.GetSkinParts())); + + break; + } case cEntity::etPickup: { a_Pkt.WriteBEUInt8((5 << 5) | 10); // Slot(5) + index 10 diff --git a/src/Protocol/Protocol_1_9.cpp b/src/Protocol/Protocol_1_9.cpp index 680675ca3..69b8deb55 100644 --- a/src/Protocol/Protocol_1_9.cpp +++ b/src/Protocol/Protocol_1_9.cpp @@ -2355,12 +2355,14 @@ void cProtocol_1_9_0::HandlePacketClientSettings(cByteBuffer & a_ByteBuffer) HANDLE_READ(a_ByteBuffer, ReadBEUInt8, UInt8, ViewDistance); HANDLE_READ(a_ByteBuffer, ReadBEUInt8, UInt8, ChatFlags); HANDLE_READ(a_ByteBuffer, ReadBool, bool, ChatColors); - HANDLE_READ(a_ByteBuffer, ReadBEUInt8, UInt8, SkinFlags); + HANDLE_READ(a_ByteBuffer, ReadBEUInt8, UInt8, SkinParts); HANDLE_READ(a_ByteBuffer, ReadVarInt, UInt32, MainHand); m_Client->SetLocale(Locale); m_Client->SetViewDistance(ViewDistance); - // TODO: Handle other values + m_Client->GetPlayer()->SetSkinParts(SkinParts); + m_Client->GetPlayer()->SetMainHand(static_cast(MainHand)); + // TODO: Handle chat flags and chat colors } @@ -3552,9 +3554,17 @@ void cProtocol_1_9_0::WriteEntityMetadata(cPacketizer & a_Pkt, const cEntity & a a_Pkt.WriteBEUInt8(METADATA_TYPE_STRING); a_Pkt.WriteString(Player.GetName()); - a_Pkt.WriteBEUInt8(6); // Start metadata - Index 6: Health + a_Pkt.WriteBEUInt8(6); // Index 6: Health a_Pkt.WriteBEUInt8(METADATA_TYPE_FLOAT); a_Pkt.WriteBEFloat(static_cast(Player.GetHealth())); + + a_Pkt.WriteBEUInt8(12); + a_Pkt.WriteBEUInt8(METADATA_TYPE_BYTE); + a_Pkt.WriteBEUInt8(static_cast(Player.GetSkinParts())); + + a_Pkt.WriteBEUInt8(13); + a_Pkt.WriteBEUInt8(METADATA_TYPE_BYTE); + a_Pkt.WriteBEUInt8(static_cast(Player.GetMainHand())); break; } case cEntity::etPickup: