Bukkit - Change player's name above head?

Kaelinator picture Kaelinator · Jul 16, 2016 · Viewed 7.3k times · Source

I'm back again.
Today I have a question that many people have asked before. The reason I'm asking again is because in all my ~90 minutes of searching, I couldn't find an updated answer. Many answers tell me to use iTag/TagAPI, but I ran into some problems trying to use that, therefore I would not like to use iTag/TagAPI. I'm trying to use packets, and I found one answer, but it too was outdated.

EntityPlayer entityP = ((CraftPlayer) p).getHandle();
entityP.displayName = args[0];


for (Player a: Bukkit.getOnlinePlayers()) {

  if (!p.getUniqueId().equals(a.getUniqueId()))
    ((CraftPlayer) a).getHandle().playerConnection.sendPacket(new PacketPlayOutNamedEntitySpawn(entityP));
}

Here's that thread I was going off of: https://bukkit.org/threads/change-player-name-above-head.162356/

Any help is appreciated!

Answer

Cnly picture Cnly · Jul 16, 2016

It is possible to achieve this in 1.8. For convenience, I used ProtocolLib and PacketWrapper.

Since the 1.8 update, the NamedEntitySpawn packet has been modified and changing player's name by modifying that has been no longer supported.(ref)

But this post gave a reference: we can use packet PlayerInfoData. I did some testing, and here's the result(tested against 1.9.2):

Here's the code:

Player theGuyToChangeNameFor = Bukkit.getPlayer("theguy");

PlayerInfoData pid = new PlayerInfoData(WrappedGameProfile.fromPlayer(theGuyToChangeNameFor), 1,
                                        EnumWrappers.NativeGameMode.SURVIVAL,
                                        WrappedChatComponent.fromText("whatever_string"));
WrapperPlayServerPlayerInfo wpspi = new WrapperPlayServerPlayerInfo();
wpspi.setAction(EnumWrappers.PlayerInfoAction.REMOVE_PLAYER);
wpspi.setData(Collections.singletonList(pid));
for(Player p : Bukkit.getOnlinePlayers())
{
    if(p.equals(theGuyToChangeNameFor))
    {
        continue;
    }
    p.hidePlayer(theGuyToChangeNameFor);
    wpspi.sendPacket(p);
}

ProtocolLibrary.getProtocolManager().addPacketListener(
        new PacketAdapter(this, PacketType.Play.Server.PLAYER_INFO)
        {

            @Override
            public void onPacketSending(PacketEvent event)
            {

                if(event.getPacket().getPlayerInfoAction().read(0) != EnumWrappers.PlayerInfoAction.ADD_PLAYER)
                {
                    return;
                }

                PlayerInfoData pid = event.getPacket().getPlayerInfoDataLists().read(0).get(0);

                if(!pid.getProfile().getName().toLowerCase().equals("theguy")) // Here you can do something to ensure you're changing the name of the correct guy
                {
                    return;
                }

                PlayerInfoData newPid = new PlayerInfoData(pid.getProfile().withName("HEAD_NAME"), pid.getPing(), pid.getGameMode(),
                                                           WrappedChatComponent.fromText("TAB_LIST_NAME"));
                event.getPacket().getPlayerInfoDataLists().write(0, Collections.singletonList(newPid));

            }
        }
);

for(Player p : Bukkit.getOnlinePlayers())
{
    if(p.equals(theGuyToChangeNameFor))
    {
        continue;
    }
    p.showPlayer(theGuyToChangeNameFor);
}

Explanation:

  • We use ProtocolLib to modify the PlayerInfoData packets from the server to change the player's display name. (You can see that name tag and tab list name can even be two different values!)
  • hidePlayer, showPlayer and REMOVE_PLAYER are used to refresh the player's name immediately(otherwise it will require logging out and in again). So far haven't found a better method. If you have one, say it:)