dmulloy2 / ProtocolLib

Provides read and write access to the Minecraft protocol with Bukkit.
GNU General Public License v2.0
1.04k stars 257 forks source link

Trouble modifying entity's custom name #3045

Open Itzdlg opened 5 months ago

Itzdlg commented 5 months ago

Describe the question A clear and concise description of what your question is.

I'm spawning modelled mobs with MythicMobs and ModelEngine. I want to modify the custom name of the entity based on the player viewing, so I'm intercepting the Entity Metadata packet as described here and here. The name of the entities do not change, so I'm curious if anyone has insight to whether I'm writing it incorrectly. The super invocation is as follows: super(JavaPlugin, ListenerPriority.HIGH, PacketType.Play.Server.ENTITY_METADATA);

API method(s) used PacketContainer#getDataValueCollectionModifier() StructureModifier#read(int); WrappedDataValue#setRawValue(Object); StructureModifier#write(int, Object);

Expected behavior The name of the entity should be recolored.

Code If applicable, add relevant code from your project

    @Override
    public void onPacketSending(PacketEvent event) {
        if (!plugin.config().nametagsEnabled())
            return;

        if (event.getPacketType() != PacketType.Play.Server.ENTITY_METADATA)
            return;

        System.out.println("[ OUTBOUND PACKET ]");
        System.out.println("To: " + event.getPlayer().getName());
        System.out.println("Type: " + event.getPacketType().name());
        System.out.println("Entity Id: " + event.getPacket().getIntegers().read(0));

        PacketContainer packet = event.getPacket();

        List<WrappedDataValue> read = packet.getDataValueCollectionModifier().read(0);
        for (WrappedDataValue value : read) {
            if (value.getIndex() != 2)
                continue;

            System.out.println("2");

            WrappedChatComponent newNametag = WrappedChatComponent.fromHandle(((WrappedChatComponent) ((Optional) value.getValue()).get()).getHandle());
            System.out.println("3: " + newNametag.getJson());
            System.out.println("3: " + newNametag.getHandle());

            Entity entity = packet.getEntityModifier(event).read(0);

            WrappedChatComponent nametag = plugin.calculate(newNametag, plugin.ofPlayer(event.getPlayer().getUniqueId()), 1);
            if (nametag != null)
                newNametag = nametag;

            System.out.println("4: " + newNametag.getJson());
            System.out.println("4: " + newNametag.getHandle());
            value.setRawValue(Optional.of(newNametag.getHandle()));
        }

        packet.getDataValueCollectionModifier().write(0, read);
    }

Additional context System Output:

[07:14:05] [Server thread/INFO]: [ OUTBOUND PACKET ]
[07:14:05] [Server thread/INFO]: To: SchoolTests
[07:14:05] [Server thread/INFO]: Type: ENTITY_METADATA
[07:14:05] [Server thread/INFO]: Entity Id: 374
[07:14:05] [Server thread/INFO]: 2
[07:14:05] [Server thread/INFO]: 3: {"text":"[LvL100]","extra":[{"text":" a giant rat","color":"dark_green"}],"italic":false,"color":"white"}
[07:14:05] [Server thread/INFO]: 3: literal{[LvL100]}[style={color=white,!italic}, siblings=[literal{ a giant rat}[style={color=dark_green}]]]
[07:14:05] [Server thread/INFO]: 4: {"text":"[LvL100]","extra":[{"text":" a giant rat","color":"green"}],"italic":false,"color":"green"}
[07:14:05] [Server thread/INFO]: 4: literal{[LvL100]}[style={color=green,!italic}, siblings=[literal{ a giant rat}[style={color=green}]]]

While the text component described in the output marked 4 is correct, the entity's nametag does not change, remaining as the text component described in the output marked 3.

Itzdlg commented 5 months ago

Server Version: 4090-Spigot-b754dcc-38b1f49 (MC: 1.20.4) ProtocolLib Version: 5.2.0-SNAPSHOT-679

[x] No duplicate issue found [x] No /reload

Ingrim4 commented 5 months ago

I'm not personally experienced with MythicMobs and ModelEngine, but it seems like you are looking to modify an entity without changing its custom name. Normally, an entity's custom name in Minecraft is visible to players only when they are nearby and directly looking at the mob. From what I understand, I would guess that both plugins use TextDisplay entities for rendering text that remains visible at all times, irrespective of the player's cameras rotation.

To achieve what you're asking for, you would likely need to delve into the plugins' source code to understand how they handle entity names and text displays. Alternatively, reaching out directly to the plugin authors could provide a more straightforward answer and guidance on how to manipulate these settings without affecting the visible custom names.

Itzdlg commented 5 months ago

@Ingrim4 Thanks for the guidance. I did in fact check MythicMobs to see how names are set, and in my first version it did change the names for non-modelled MythicMob entities. That leads me to believe that ModelEngine mobs are either 1) using TextDisplay or 2) using an additional entity for the nametag. Option 2 doesn't seem to make sense because the code provided should change the display name regardless of which entity sets it. In the case of option 1, this code should show that index 23 is being set and change it as well, but it doesn't seem to show any value for index 23 (i.e. "2: 23" is not printed after spawning a modelled mob.).

    @Override
    public void onPacketSending(PacketEvent event) {
        if (!plugin.config().nametagsEnabled())
            return;

        if (event.getPacketType() != PacketType.Play.Server.ENTITY_METADATA)
            return;

        System.out.println("[ OUTBOUND PACKET ]");
        System.out.println("To: " + event.getPlayer().getName());
        System.out.println("Type: " + event.getPacketType().name());
        System.out.println("Entity Id: " + event.getPacket().getIntegers().read(0));

        PacketContainer packet = event.getPacket();

        List<WrappedDataValue> read = packet.getDataValueCollectionModifier().read(0);
        for (WrappedDataValue value : read) {
            if (value.getIndex() != 2 && value.getIndex() != 23)
                continue;

            boolean isOptionalWrapped = value.getIndex() == 2;

            System.out.println("2: " + value.getIndex());
            WrappedChatComponent newNametag;

            if (isOptionalWrapped)
                newNametag = WrappedChatComponent.fromHandle(((WrappedChatComponent) ((Optional) value.getValue()).get()).getHandle());
            else newNametag = WrappedChatComponent.fromHandle(((WrappedChatComponent) value.getValue()).getHandle());

            System.out.println("3: " + newNametag.getJson());
            System.out.println("3: " + newNametag.getHandle());
            Entity entity = packet.getEntityModifier(event).read(0);
            WrappedChatComponent nametag;

            if (entity != null)
                nametag = plugin.calculate(newNametag, plugin.ofPlayer(event.getPlayer().getUniqueId()), 1);
            else nametag = plugin.calculate(newNametag, plugin.ofPlayer(event.getPlayer().getUniqueId()), 1);

            if (nametag != null)
                newNametag = nametag;

            System.out.println("4: " + newNametag.getJson());
            System.out.println("4: " + newNametag.getHandle());

            if (isOptionalWrapped)
                value.setRawValue(Optional.of(newNametag.getHandle()));
            else value.setRawValue(newNametag.getHandle());
        }

        packet.getDataValueCollectionModifier().write(0, read);
    }
Ingrim4 commented 5 months ago

I would be careful with your code because you simply assume that id 23 is always a ChatComponent but for other entities that doesn't have to be the case because the ids are only unique for a single entity type not across all of them. For example a BlockDisplay entity would contain/expect a BlockState for id 23.

As for id 23 not containing a value: that could be because the entity is not a TextDisplay or the id in the wiki isn't correct anymore as they tend to change often.