Skip to content

Instantly share code, notes, and snippets.

@Owen1212055
Last active October 18, 2023 23:38
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Owen1212055/f5d59169d3a6a5c32f0c173d57eb199d to your computer and use it in GitHub Desktop.
Save Owen1212055/f5d59169d3a6a5c32f0c173d57eb199d to your computer and use it in GitHub Desktop.
Custom Player Entity Names

Custom Player Names

alt text

This is meant as a resource to mimic setting player names whilst ensuring that it is positioned in the same way as an actual nametag.

The current issue

Currently, player entities ignore the custom DATA_CUSTOM_NAME EntityDataAccessor, which sadly means that we cannot just easily change the name of the player entity. Hopefully in the future this functionality will be provided so we do not have to apply such hacky methods.

My implementation (simplified)

public void show(@NotNull Player entity) {
        Location location = entity.getLocation();

        Interaction interaction = entity.getWorld().spawn(location, Interaction.class);
        entity.addPassenger(interaction);

        interaction.customName(this.name);
        interaction.setCustomNameVisible(true);

        net.minecraft.world.entity.Entity nmsEntity = ((CraftEntity) entity).getHandle();
        double ridingOffset = nmsEntity.getPassengersRidingOffset();
        double nametagOffset = nmsEntity.getNameTagOffsetY();

        // First, negate the riding offset to get to the bounding of the entity's bounding box
        // Negate the natural nametag offset of interaction entities (0.5)
        // Add the actual offset of the nametag
        double effectiveHeight = -ridingOffset - 0.5 + nametagOffset;

        // Calculate height
        interaction.setInteractionHeight((float) effectiveHeight);
        interaction.setInteractionWidth(0);
        ((CraftInteraction) interaction).getHandle().setPose(Pose.SNIFFING); // Send the pose to the client, force recalculation of the bounding box.
    }

In this case, we use an Interaction entity that rides the entity who's name we want to change. We then utilize our ability to customize the hitbox of this entity by setting its height in order to allow the entity to sit flush on the entity. This means that the heighest point of the entity's bounding box will now match the height of the interaction box.

image As we can see in this photo, the height of the interaction entity (circled) matches the hitbox. It should be noted that in some cases, some entities have special exceptions for nametag offsets, which is why it may not always be flush.

This then allows us to add a custom name to the interaction entity, which will cause the location of the nametag to remain in the same place.

Why is the entity disappearing?

When moving away from the interaction entity, the entity will stop rendering. This is because it's very small, and the client thinks it's safe to stop rendering it. Unfortunently, it is not easy to change this.

However, here is an example on how you would bypass it.

        ClientboundSetEntityDataPacket initialCreatePacket = new ClientboundSetEntityDataPacket(entityIdName, List.of(
                ofData(Reflection.DATA_WIDTH_ID, 0f),
                ofData(Reflection.DATA_HEIGHT_ID, (float) effectiveHeight),
                ofData(Reflection.DATA_POSE, Pose.CROAKING),
                ofData(Reflection.DATA_CUSTOM_NAME_VISIBLE, true)
        ));
        Packet<ClientGamePacketListener> syncData = createSyncData(); // name... sneaking state... etc
        ClientboundSetEntityDataPacket afterCreateData = new ClientboundSetEntityDataPacket(entityIdName, List.of(
                ofData(Reflection.DATA_HEIGHT_ID, 99999999f)
        ));

        return new ClientboundBundlePacket(List.of(
                createPacket(), // Create entity
                initialCreatePacket,
                syncData,
                new ClientboundSetPassengersPacket(buf),
                afterCreateData
        ));

Essentially, you update the height which changes the dimensions but not the stored bounding box. This causes the client to still render the nametag at the previous position, but it will now also use dimensions. image

The dimensions are highlighted in white, and as we can see, it goes super high into the skybox. This means that the game will use that box for determining whether or not to stop rendering that entity. So, we essentially have a permenent name tag!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment