diff options
-rw-r--r-- | MCServer/Plugins/APIDump/APIDesc.lua | 9 | ||||
-rw-r--r-- | MCServer/Plugins/Debuggers/Debuggers.lua | 16 | ||||
-rw-r--r-- | src/ClientHandle.cpp | 11 | ||||
-rw-r--r-- | src/Entities/Boat.cpp | 2 | ||||
-rw-r--r-- | src/Entities/Entity.h | 2 | ||||
-rw-r--r-- | src/Entities/ItemFrame.cpp | 2 | ||||
-rw-r--r-- | src/Entities/Minecart.cpp | 5 | ||||
-rw-r--r-- | src/Entities/Pawn.cpp | 9 | ||||
-rw-r--r-- | src/Entities/Player.cpp | 47 | ||||
-rw-r--r-- | src/Entities/Player.h | 15 | ||||
-rw-r--r-- | src/Mobs/Monster.cpp | 54 | ||||
-rw-r--r-- | src/Mobs/Monster.h | 23 | ||||
-rw-r--r-- | src/Mobs/Sheep.cpp | 2 | ||||
-rw-r--r-- | src/Protocol/Protocol125.cpp | 15 | ||||
-rw-r--r-- | src/Protocol/Protocol132.cpp | 9 | ||||
-rw-r--r-- | src/Protocol/Protocol17x.cpp | 35 | ||||
-rw-r--r-- | src/Protocol/Protocol18x.cpp | 19 | ||||
-rw-r--r-- | src/World.cpp | 8 | ||||
-rw-r--r-- | src/WorldStorage/NBTChunkSerializer.cpp | 2 | ||||
-rw-r--r-- | src/WorldStorage/WSSAnvil.cpp | 13 |
20 files changed, 267 insertions, 31 deletions
diff --git a/MCServer/Plugins/APIDump/APIDesc.lua b/MCServer/Plugins/APIDump/APIDesc.lua index 2f791293d..e6ee4ca10 100644 --- a/MCServer/Plugins/APIDump/APIDesc.lua +++ b/MCServer/Plugins/APIDump/APIDesc.lua @@ -1634,6 +1634,11 @@ a_Player:OpenWindow(Window); ]], Functions = { + HasCustomName = { Params = "", Return = "bool", Notes = "Returns true if the monster has a custom name." }, + GetCustomName = { Params = "", Return = "string", Notes = "Gets the custom name of the monster. If no custom name is set, the function returns an empty string." }, + SetCustomName = { Params = "string", Return = "", Notes = "Sets the custom name of the monster. You see the name over the monster. If you want to disable the custom name, simply set an empty string." }, + IsCustomNameAlwaysVisible = { Params = "", Return = "bool", Notes = "Is the custom name of this monster always visible? If not, you only see the name when you sight the mob." }, + SetCustomNameAlwaysVisible = { Params = "bool", Return = "", Notes = "Sets the custom name visiblity of this monster. If it's false, you only see the name when you sight the mob. If it's true, you always see the custom name." }, FamilyFromType = { Params = "{{cMonster#MobType|MobType}}", Return = "{{cMonster#MobFamily|MobFamily}}", Notes = "(STATIC) Returns the mob family ({{cMonster#MobFamily|mfXXX}} constants) based on the mob type ({{cMonster#MobType|mtXXX}} constants)" }, GetMobFamily = { Params = "", Return = "{{cMonster#MobFamily|MobFamily}}", Notes = "Returns this mob's family ({{cMonster#MobFamily|mfXXX}} constant)" }, GetMobType = { Params = "", Return = "{{cMonster#MobType|MobType}}", Notes = "Returns the type of this mob ({{cMonster#MobType|mtXXX}} constant)" }, @@ -1759,6 +1764,7 @@ a_Player:OpenWindow(Window); ForceSetSpeed = { Params = "{{Vector3d|Direction}}", Notes = "Forces the player to move to the given direction." }, GetClientHandle = { Params = "", Return = "{{cClientHandle}}", Notes = "Returns the client handle representing the player's connection. May be nil (AI players)." }, GetColor = { Return = "string", Notes = "Returns the full color code to be used for this player's messages (based on their rank). Prefix player messages with this code." }, + GetPlayerListName = { Return = "string", Notes = "Returns the name that is used in the playerlist." }, GetCurrentXp = { Params = "", Return = "number", Notes = "Returns the current amount of XP" }, GetEffectiveGameMode = { Params = "", Return = "{{Globals#GameMode|GameMode}}", Notes = "(OBSOLETE) Returns the current resolved game mode of the player. If the player is set to inherit the world's gamemode, returns that instead. See also GetGameMode() and IsGameModeXXX() functions. Note that this function is the same as GetGameMode(), use that function instead." }, GetEquippedItem = { Params = "", Return = "{{cItem}}", Notes = "Returns the item that the player is currently holding; empty item if holding nothing." }, @@ -1809,6 +1815,9 @@ a_Player:OpenWindow(Window); SendMessagePrivateMsg = { Params = "Message, SenderName", Return = "", Notes = "Prepends Light Blue [MSG: *SenderName*] / prepends SenderName and colours entire text (depending on ShouldUseChatPrefixes()) and sends message to player. For private messaging." }, SendMessageSuccess = { Params = "Message", Return = "", Notes = "Prepends Green [INFO] / colours entire text (depending on ShouldUseChatPrefixes()) and sends message to player. Success notification." }, SendMessageWarning = { Params = "Message, Sender", Return = "", Notes = "Prepends Rose [WARN] / colours entire text (depending on ShouldUseChatPrefixes()) and sends message to player. Denotes that something concerning, such as plugin reload, is about to happen." }, + HasCustomName = { Params = "", Return = "bool", Notes = "Returns true if the player has a custom name." }, + GetCustomName = { Params = "", Return = "string", Notes = "Returns the custom name of this player. If the player hasn't a custom name, it will return an empty string." }, + SetCustomName = { Params = "string", Return = "", Notes = "Sets the custom name of this player. If you want to disable the custom name, simply set an empty string. The custom name will be used in the tab-list, in the player nametag and in the tab-completion." }, SetCanFly = { Params = "CanFly", Notes = "Sets if the player can fly or not." }, SetCrouch = { Params = "IsCrouched", Return = "", Notes = "Sets the crouch state, broadcasts the change to other players." }, SetCurrentExperience = { Params = "XPAmount", Return = "", Notes = "Sets the current amount of experience (and indirectly, the XP level)." }, diff --git a/MCServer/Plugins/Debuggers/Debuggers.lua b/MCServer/Plugins/Debuggers/Debuggers.lua index 66e06cb72..f66ac76a0 100644 --- a/MCServer/Plugins/Debuggers/Debuggers.lua +++ b/MCServer/Plugins/Debuggers/Debuggers.lua @@ -38,6 +38,7 @@ function Initialize(Plugin) -- _X: Disabled so that the normal operation doesn't interfere with anything -- PM:AddHook(cPluginManager.HOOK_CHUNK_GENERATED, OnChunkGenerated); + PM:BindCommand("/nick", "debuggers", HandleNickCmd, "- Gives you a custom name"); PM:BindCommand("/le", "debuggers", HandleListEntitiesCmd, "- Shows a list of all the loaded entities"); PM:BindCommand("/ke", "debuggers", HandleKillEntitiesCmd, "- Kills all the loaded entities"); PM:BindCommand("/wool", "debuggers", HandleWoolCmd, "- Sets all your armor to blue wool"); @@ -770,6 +771,21 @@ end +function HandleNickCmd(Split, Player) + if (Split[2] == nil) then + Player:SendMessage("Usage: /nick [CustomName]"); + return true; + end + + Player:SetCustomName(Split[2]); + Player:SendMessageSuccess("Custom name setted to " .. Player:GetCustomName() .. "!") + return true +end + + + + + function HandleListEntitiesCmd(Split, Player) local NumEntities = 0; diff --git a/src/ClientHandle.cpp b/src/ClientHandle.cpp index 43a4161e4..1e2f4ffad 100644 --- a/src/ClientHandle.cpp +++ b/src/ClientHandle.cpp @@ -124,13 +124,14 @@ cClientHandle::~cClientHandle() if (m_Player != NULL) { cWorld * World = m_Player->GetWorld(); - if (!m_Username.empty() && (World != NULL)) - { - // Send the Offline PlayerList packet: - World->BroadcastPlayerListRemovePlayer(*m_Player, this); - } if (World != NULL) { + if (!m_Username.empty()) + { + // Send the Offline PlayerList packet: + World->BroadcastPlayerListRemovePlayer(*m_Player, this); + } + World->RemovePlayer(m_Player, true); // Must be called before cPlayer::Destroy() as otherwise cChunk tries to delete the player, and then we do it again m_Player->Destroy(); } diff --git a/src/Entities/Boat.cpp b/src/Entities/Boat.cpp index 8ff8866a1..328a70846 100644 --- a/src/Entities/Boat.cpp +++ b/src/Entities/Boat.cpp @@ -62,6 +62,8 @@ bool cBoat::DoTakeDamage(TakeDamageInfo & TDI) void cBoat::OnRightClicked(cPlayer & a_Player) { + super::OnRightClicked(a_Player); + if (m_Attachee != NULL) { if (m_Attachee->GetUniqueID() == a_Player.GetUniqueID()) diff --git a/src/Entities/Entity.h b/src/Entities/Entity.h index 6bc070dcc..3fa7e80c1 100644 --- a/src/Entities/Entity.h +++ b/src/Entities/Entity.h @@ -447,7 +447,7 @@ public: // tolua_end /// Called when the specified player right-clicks this entity - virtual void OnRightClicked(cPlayer &) {} + virtual void OnRightClicked(cPlayer & a_Player) {} /// Returns the list of drops for this pawn when it is killed. May check a_Killer for special handling (sword of looting etc.). Called from KilledBy(). virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) diff --git a/src/Entities/ItemFrame.cpp b/src/Entities/ItemFrame.cpp index 7f923ef61..f512324eb 100644 --- a/src/Entities/ItemFrame.cpp +++ b/src/Entities/ItemFrame.cpp @@ -22,6 +22,8 @@ cItemFrame::cItemFrame(eBlockFace a_BlockFace, double a_X, double a_Y, double a_ void cItemFrame::OnRightClicked(cPlayer & a_Player) { + super::OnRightClicked(a_Player); + if (!m_Item.IsEmpty()) { // Item not empty, rotate, clipping values to zero to three inclusive diff --git a/src/Entities/Minecart.cpp b/src/Entities/Minecart.cpp index 5833cb4ae..2df64d5c1 100644 --- a/src/Entities/Minecart.cpp +++ b/src/Entities/Minecart.cpp @@ -1073,6 +1073,8 @@ cRideableMinecart::cRideableMinecart(double a_X, double a_Y, double a_Z, const c void cRideableMinecart::OnRightClicked(cPlayer & a_Player) { + super::OnRightClicked(a_Player); + if (m_Attachee != NULL) { if (m_Attachee->GetUniqueID() == a_Player.GetUniqueID()) @@ -1125,8 +1127,7 @@ void cMinecartWithChest::SetSlot(size_t a_Idx, const cItem & a_Item) void cMinecartWithChest::OnRightClicked(cPlayer & a_Player) { - // Show the chest UI window to the player - // TODO + // TODO: Show the chest UI window to the player } diff --git a/src/Entities/Pawn.cpp b/src/Entities/Pawn.cpp index fe6c24a7a..fc8ca3d47 100644 --- a/src/Entities/Pawn.cpp +++ b/src/Entities/Pawn.cpp @@ -9,9 +9,9 @@ -cPawn::cPawn(eEntityType a_EntityType, double a_Width, double a_Height): - super(a_EntityType, 0, 0, 0, a_Width, a_Height), - m_EntityEffects(tEffectMap()) +cPawn::cPawn(eEntityType a_EntityType, double a_Width, double a_Height) : + super(a_EntityType, 0, 0, 0, a_Width, a_Height) + , m_EntityEffects(tEffectMap()) { } @@ -111,3 +111,6 @@ void cPawn::ClearEntityEffects() RemoveEntityEffect(EffectType); } } + + + diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp index 871311e86..1bdb752c5 100644 --- a/src/Entities/Player.cpp +++ b/src/Entities/Player.cpp @@ -81,7 +81,8 @@ cPlayer::cPlayer(cClientHandle* a_Client, const AString & a_PlayerName) : m_Team(NULL), m_TicksUntilNextSave(PLAYER_INVENTORY_SAVE_INTERVAL), m_bIsTeleporting(false), - m_UUID((a_Client != NULL) ? a_Client->GetUUID() : "") + m_UUID((a_Client != NULL) ? a_Client->GetUUID() : ""), + m_CustomName("") { m_InventoryWindow = new cInventoryWindow(*this); m_CurrentWindow = m_InventoryWindow; @@ -809,6 +810,28 @@ void cPlayer::SetCanFly(bool a_CanFly) +void cPlayer::SetCustomName(const AString & a_CustomName) +{ + if (m_CustomName == a_CustomName) + { + return; + } + AString OldCustomName = m_CustomName; + + m_CustomName = a_CustomName; + if (m_CustomName.length() > 16) + { + m_CustomName = m_CustomName.substr(0, 16); + } + + m_World->BroadcastPlayerListUpdateDisplayName(*this, m_CustomName); + m_World->BroadcastSpawnEntity(*this, m_ClientHandle); +} + + + + + void cPlayer::SetFlying(bool a_IsFlying) { if (a_IsFlying == m_IsFlying) @@ -1450,6 +1473,28 @@ AString cPlayer::GetColor(void) const +AString cPlayer::GetPlayerListName(void) const +{ + const AString & Color = GetColor(); + + if (HasCustomName()) + { + return m_CustomName; + } + else if ((GetName().length() <= 14) && !Color.empty()) + { + return Printf("%s%s", Color.c_str(), GetName().c_str()); + } + else + { + return GetName(); + } +} + + + + + void cPlayer::TossEquippedItem(char a_Amount) { cItems Drops; diff --git a/src/Entities/Player.h b/src/Entities/Player.h index d64dd6b99..ffd0b7e03 100644 --- a/src/Entities/Player.h +++ b/src/Entities/Player.h @@ -254,6 +254,9 @@ public: The returned value either is empty, or includes the cChatColor::Delimiter. */ AString GetColor(void) const; + /** Returns the name that is used in the playerlist. */ + AString GetPlayerListName(void) const; + /** tosses the item in the selected hotbar slot */ void TossEquippedItem(char a_Amount = 1); @@ -401,6 +404,16 @@ public: /** If true the player can fly even when he's not in creative. */ void SetCanFly(bool a_CanFly); + /** Returns true if the player has a custom name. */ + bool HasCustomName(void) const { return !m_CustomName.empty(); } + + /** Returns the custom name of this player. If the player hasn't a custom name, it will return an empty string. */ + const AString & GetCustomName(void) const { return m_CustomName; } + + /** Sets the custom name of this player. If you want to disable the custom name, simply set an empty string. + The custom name will be used in the tab-list, in the player nametag and in the tab-completion. */ + void SetCustomName(const AString & a_CustomName); + /** Gets the last position that the player slept in This is initialised to the world spawn point if the player has not slept in a bed as of yet */ @@ -565,6 +578,8 @@ protected: If no ClientHandle is given, the UUID is initialized to empty. */ AString m_UUID; + AString m_CustomName; + /** 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/Mobs/Monster.cpp b/src/Mobs/Monster.cpp index 81acf1a93..339367f47 100644 --- a/src/Mobs/Monster.cpp +++ b/src/Mobs/Monster.cpp @@ -75,6 +75,8 @@ cMonster::cMonster(const AString & a_ConfigName, eType a_MobType, const AString , m_IdleInterval(0) , m_DestroyTimer(0) , m_MobType(a_MobType) + , m_CustomName("") + , m_CustomNameAlwaysVisible(false) , m_SoundHurt(a_SoundHurt) , m_SoundDeath(a_SoundDeath) , m_AttackRate(3) @@ -555,6 +557,25 @@ void cMonster::KilledBy(TakeDamageInfo & a_TDI) +void cMonster::OnRightClicked(cPlayer & a_Player) +{ + super::OnRightClicked(a_Player); + + const cItem & EquippedItem = a_Player.GetEquippedItem(); + if ((EquippedItem.m_ItemType == E_ITEM_NAME_TAG) && !EquippedItem.m_CustomName.empty()) + { + SetCustomName(EquippedItem.m_CustomName); + if (!a_Player.IsGameModeCreative()) + { + a_Player.GetInventory().RemoveOneEquippedItem(); + } + } +} + + + + + // Checks to see if EventSeePlayer should be fired // monster sez: Do I see the player void cMonster::CheckEventSeePlayer(void) @@ -683,6 +704,39 @@ void cMonster::InStateEscaping(float a_Dt) +void cMonster::SetCustomName(const AString & a_CustomName) +{ + m_CustomName = a_CustomName; + + // The maximal length is 64 + if (a_CustomName.length() > 64) + { + m_CustomName = a_CustomName.substr(0, 64); + } + + if (m_World != NULL) + { + m_World->BroadcastEntityMetadata(*this); + } +} + + + + + +void cMonster::SetCustomNameAlwaysVisible(bool a_CustomNameAlwaysVisible) +{ + m_CustomNameAlwaysVisible = a_CustomNameAlwaysVisible; + if (m_World != NULL) + { + m_World->BroadcastEntityMetadata(*this); + } +} + + + + + void cMonster::GetMonsterConfig(const AString & a_Name) { cRoot::Get()->GetMonsterConfig()->AssignAttributes(this, a_Name); diff --git a/src/Mobs/Monster.h b/src/Mobs/Monster.h index ca3c04c23..4865cb99e 100644 --- a/src/Mobs/Monster.h +++ b/src/Mobs/Monster.h @@ -92,6 +92,8 @@ public: virtual void KilledBy(TakeDamageInfo & a_TDI) override; + virtual void OnRightClicked(cPlayer & a_Player) override; + virtual void MoveToPosition(const Vector3d & a_Position); // tolua_export virtual bool ReachedDestination(void); @@ -147,7 +149,24 @@ public: virtual bool IsSitting (void) const { return false; } // tolua_begin - + + /** Returns true if the monster has a custom name. */ + bool HasCustomName(void) const { return !m_CustomName.empty(); } + + /** Gets the custom name of the monster. If no custom name is set, the function returns an empty string. */ + const AString & GetCustomName(void) const { return m_CustomName; } + + /** Sets the custom name of the monster. You see the name over the monster. + If you want to disable the custom name, simply set an empty string. */ + void SetCustomName(const AString & a_CustomName); + + /** Is the custom name of this monster always visible? If not, you only see the name when you sight the mob. */ + bool IsCustomNameAlwaysVisible(void) const { return m_CustomNameAlwaysVisible; } + + /** Sets the custom name visiblity of this monster. + If it's false, you only see the name when you sight the mob. If it's true, you always see the custom name. */ + void SetCustomNameAlwaysVisible(bool a_CustomNameAlwaysVisible); + /// Translates MobType enum to a string, empty string if unknown static AString MobTypeToString(eType a_MobType); @@ -231,6 +250,8 @@ protected: float m_DestroyTimer; eType m_MobType; + AString m_CustomName; + bool m_CustomNameAlwaysVisible; AString m_SoundHurt; AString m_SoundDeath; diff --git a/src/Mobs/Sheep.cpp b/src/Mobs/Sheep.cpp index 1a82115d2..c12e8d786 100644 --- a/src/Mobs/Sheep.cpp +++ b/src/Mobs/Sheep.cpp @@ -54,6 +54,8 @@ void cSheep::GetDrops(cItems & a_Drops, cEntity * a_Killer) void cSheep::OnRightClicked(cPlayer & a_Player) { + super::OnRightClicked(a_Player); + const cItem & EquippedItem = a_Player.GetEquippedItem(); if ((EquippedItem.m_ItemType == E_ITEM_SHEARS) && !IsSheared() && !IsBaby()) { diff --git a/src/Protocol/Protocol125.cpp b/src/Protocol/Protocol125.cpp index 4378ef466..ba4cfa7ef 100644 --- a/src/Protocol/Protocol125.cpp +++ b/src/Protocol/Protocol125.cpp @@ -723,7 +723,7 @@ void cProtocol125::SendPlayerListAddPlayer(const cPlayer & a_Player) { cCSLock Lock(m_CSPacket); WriteByte (PACKET_PLAYER_LIST_ITEM); - WriteString(a_Player.GetName()); + WriteString(a_Player.GetPlayerListName()); WriteBool (true); WriteShort (a_Player.GetClientHandle()->GetPing()); Flush(); @@ -737,7 +737,7 @@ void cProtocol125::SendPlayerListRemovePlayer(const cPlayer & a_Player) { cCSLock Lock(m_CSPacket); WriteByte (PACKET_PLAYER_LIST_ITEM); - WriteString(a_Player.GetName()); + WriteString(a_Player.GetPlayerListName()); WriteBool (false); WriteShort (0); Flush(); @@ -769,7 +769,7 @@ void cProtocol125::SendPlayerListUpdatePing(const cPlayer & a_Player) void cProtocol125::SendPlayerListUpdateDisplayName(const cPlayer & a_Player, const AString & a_OldListName) { - if (a_OldListName == a_Player.GetName()) + if (a_OldListName == a_Player.GetPlayerListName()) { return; } @@ -843,7 +843,14 @@ void cProtocol125::SendPlayerSpawn(const cPlayer & a_Player) cCSLock Lock(m_CSPacket); WriteByte (PACKET_PLAYER_SPAWN); WriteInt (a_Player.GetUniqueID()); - WriteString(a_Player.GetName()); + if (a_Player.HasCustomName()) + { + WriteString(a_Player.GetCustomName()); + } + else + { + WriteString(a_Player.GetName()); + } WriteInt ((int)(a_Player.GetPosX() * 32)); WriteInt ((int)(a_Player.GetPosY() * 32)); WriteInt ((int)(a_Player.GetPosZ() * 32)); diff --git a/src/Protocol/Protocol132.cpp b/src/Protocol/Protocol132.cpp index 5fd2655b8..63a15c082 100644 --- a/src/Protocol/Protocol132.cpp +++ b/src/Protocol/Protocol132.cpp @@ -260,7 +260,14 @@ void cProtocol132::SendPlayerSpawn(const cPlayer & a_Player) cCSLock Lock(m_CSPacket); WriteByte (PACKET_PLAYER_SPAWN); WriteInt (a_Player.GetUniqueID()); - WriteString(a_Player.GetName()); + if (a_Player.HasCustomName()) + { + WriteString(a_Player.GetCustomName()); + } + else + { + WriteString(a_Player.GetName()); + } WriteInt ((int)(a_Player.GetPosX() * 32)); WriteInt ((int)(a_Player.GetPosY() * 32)); WriteInt ((int)(a_Player.GetPosZ() * 32)); diff --git a/src/Protocol/Protocol17x.cpp b/src/Protocol/Protocol17x.cpp index 2fbc58541..67a4c47a7 100644 --- a/src/Protocol/Protocol17x.cpp +++ b/src/Protocol/Protocol17x.cpp @@ -795,7 +795,7 @@ void cProtocol172::SendPlayerListAddPlayer(const cPlayer & a_Player) ASSERT(m_State == 3); // In game mode? cPacketizer Pkt(*this, 0x38); // Playerlist Item packet - Pkt.WriteString(a_Player.GetName()); + Pkt.WriteString(a_Player.GetPlayerListName()); Pkt.WriteBool(true); Pkt.WriteShort(a_Player.GetClientHandle()->GetPing()); } @@ -809,7 +809,7 @@ void cProtocol172::SendPlayerListRemovePlayer(const cPlayer & a_Player) ASSERT(m_State == 3); // In game mode? cPacketizer Pkt(*this, 0x38); - Pkt.WriteString(a_Player.GetName()); + Pkt.WriteString(a_Player.GetPlayerListName()); Pkt.WriteBool(false); Pkt.WriteShort(0); } @@ -841,7 +841,7 @@ void cProtocol172::SendPlayerListUpdatePing(const cPlayer & a_Player) void cProtocol172::SendPlayerListUpdateDisplayName(const cPlayer & a_Player, const AString & a_OldListName) { ASSERT(m_State == 3); // In game mode? - if (a_OldListName == a_Player.GetName()) + if (a_OldListName == a_Player.GetPlayerListName()) { return; } @@ -928,9 +928,16 @@ void cProtocol172::SendPlayerSpawn(const cPlayer & a_Player) // Called to spawn another player for the client cPacketizer Pkt(*this, 0x0c); // Spawn Player packet - Pkt.WriteVarInt(a_Player.GetUniqueID()); + Pkt.WriteVarInt((UInt32) a_Player.GetUniqueID()); Pkt.WriteString(cMojangAPI::MakeUUIDDashed(a_Player.GetClientHandle()->GetUUID())); - Pkt.WriteString(a_Player.GetName()); + if (a_Player.HasCustomName()) + { + Pkt.WriteString(a_Player.GetCustomName()); + } + else + { + Pkt.WriteString(a_Player.GetName()); + } Pkt.WriteFPInt(a_Player.GetPosX()); Pkt.WriteFPInt(a_Player.GetPosY()); Pkt.WriteFPInt(a_Player.GetPosZ()); @@ -2928,6 +2935,15 @@ void cProtocol172::cPacketizer::WriteMobMetadata(const cMonster & a_Mob) break; } } // switch (a_Mob.GetType()) + + // Custom name: + if (a_Mob.HasCustomName()) + { + WriteByte(0x8a); + WriteString(a_Mob.GetCustomName()); + WriteByte(0x0b); + WriteByte(a_Mob.IsCustomNameAlwaysVisible() ? 1 : 0); + } } @@ -2972,7 +2988,14 @@ void cProtocol176::SendPlayerSpawn(const cPlayer & a_Player) cPacketizer Pkt(*this, 0x0c); // Spawn Player packet Pkt.WriteVarInt(a_Player.GetUniqueID()); Pkt.WriteString(cMojangAPI::MakeUUIDDashed(a_Player.GetClientHandle()->GetUUID())); - Pkt.WriteString(a_Player.GetName()); + if (a_Player.HasCustomName()) + { + Pkt.WriteString(a_Player.GetCustomName()); + } + else + { + Pkt.WriteString(a_Player.GetName()); + } const Json::Value & Properties = a_Player.GetClientHandle()->GetProperties(); Pkt.WriteVarInt(Properties.size()); diff --git a/src/Protocol/Protocol18x.cpp b/src/Protocol/Protocol18x.cpp index a7ab3abb1..e2ea6701a 100644 --- a/src/Protocol/Protocol18x.cpp +++ b/src/Protocol/Protocol18x.cpp @@ -824,7 +824,17 @@ void cProtocol180::SendPlayerListAddPlayer(const cPlayer & a_Player) Pkt.WriteVarInt((UInt32)a_Player.GetGameMode()); Pkt.WriteVarInt((UInt32)a_Player.GetClientHandle()->GetPing()); - Pkt.WriteBool(false); + + AString CustomName = a_Player.GetPlayerListName(); + if (CustomName != a_Player.GetName()) + { + Pkt.WriteBool(true); + Pkt.WriteString(Printf("{\"text\":\"%s\"}", CustomName.c_str())); + } + else + { + Pkt.WriteBool(false); + } } @@ -877,7 +887,6 @@ void cProtocol180::SendPlayerListUpdatePing(const cPlayer & a_Player) void cProtocol180::SendPlayerListUpdateDisplayName(const cPlayer & a_Player, const AString & a_OldListName) { - UNUSED(a_OldListName); ASSERT(m_State == 3); // In game mode? cPacketizer Pkt(*this, 0x38); // Playerlist Item packet @@ -885,15 +894,15 @@ void cProtocol180::SendPlayerListUpdateDisplayName(const cPlayer & a_Player, con Pkt.WriteVarInt(1); Pkt.WriteUUID(a_Player.GetUUID()); - // TODO: Replace this with GetPlayerListName() (It's already done in other pull request) - if (a_Player.GetName().empty()) + AString CustomName = a_Player.GetPlayerListName(); + if (CustomName == a_Player.GetName()) { Pkt.WriteBool(false); } else { Pkt.WriteBool(true); - Pkt.WriteString(Printf("{\"text\":\"%s\"}", a_Player.GetName().c_str())); + Pkt.WriteString(Printf("{\"text\":\"%s\"}", CustomName.c_str())); } } diff --git a/src/World.cpp b/src/World.cpp index 71ce7e680..8664bbec6 100644 --- a/src/World.cpp +++ b/src/World.cpp @@ -3288,13 +3288,17 @@ void cWorld::TabCompleteUserName(const AString & a_Text, AStringVector & a_Resul for (cPlayerList::iterator itr = m_Players.begin(), end = m_Players.end(); itr != end; ++itr) { AString PlayerName ((*itr)->GetName()); + if ((*itr)->HasCustomName()) + { + PlayerName = (*itr)->GetCustomName(); + } + AString::size_type Found = PlayerName.find(LastWord); // Try to find last word in playername - if (Found == AString::npos) { continue; // No match } - + UsernamesByWeight.push_back(std::make_pair(Found, PlayerName)); // Match! Store it with the position of the match as a weight } Lock.Unlock(); diff --git a/src/WorldStorage/NBTChunkSerializer.cpp b/src/WorldStorage/NBTChunkSerializer.cpp index 68e541eba..1962d42ff 100644 --- a/src/WorldStorage/NBTChunkSerializer.cpp +++ b/src/WorldStorage/NBTChunkSerializer.cpp @@ -504,6 +504,8 @@ void cNBTChunkSerializer::AddMonsterEntity(cMonster * a_Monster) m_Writer.AddFloat("", a_Monster->GetDropChanceBoots()); m_Writer.EndList(); m_Writer.AddByte("CanPickUpLoot", (char)a_Monster->CanPickUpLoot()); + m_Writer.AddString("CustomName", a_Monster->GetCustomName()); + m_Writer.AddByte("CustomNameVisible", (char)a_Monster->IsCustomNameAlwaysVisible()); switch (a_Monster->GetMobType()) { case cMonster::mtBat: diff --git a/src/WorldStorage/WSSAnvil.cpp b/src/WorldStorage/WSSAnvil.cpp index fe309ce4e..10cc39083 100644 --- a/src/WorldStorage/WSSAnvil.cpp +++ b/src/WorldStorage/WSSAnvil.cpp @@ -2652,6 +2652,19 @@ bool cWSSAnvil::LoadMonsterBaseFromNBT(cMonster & a_Monster, const cParsedNBT & a_Monster.SetCanPickUpLoot(CanPickUpLoot); } + int CustomNameTag = a_NBT.FindChildByName(a_TagIdx, "CustomName"); + if ((CustomNameTag > 0) && (a_NBT.GetType(CustomNameTag) == TAG_String)) + { + a_Monster.SetCustomName(a_NBT.GetString(CustomNameTag)); + } + + int CustomNameVisibleTag = a_NBT.FindChildByName(a_TagIdx, "CustomNameVisible"); + if ((CustomNameVisibleTag > 0) && (a_NBT.GetType(CustomNameVisibleTag) == TAG_Byte)) + { + bool CustomNameVisible = (a_NBT.GetByte(CustomNameVisibleTag) == 1); + a_Monster.SetCustomNameAlwaysVisible(CustomNameVisible); + } + return true; } |