Skip to content

Entity Head Rotation not working for fake player NPCs on Minecraft 1.21.10 #1400

Description

@TheCalypso

Entity Head Rotation not working for fake player NPCs on Minecraft 1.21.10

Environment

  • Minecraft Version: Paper 1.21.10
  • PacketEvents Version: 2.10.1
  • Problem: Rotation packets are sent successfully but NPCs don't rotate visually

Description

I'm developing a Spigot/Paper plugin that creates fake player NPCs using PacketEvents. The NPCs spawn correctly with skins and are fully visible, but when I try to make them rotate their head to look at nearby players, the rotation packets are sent without errors but the NPCs don't rotate visually in-game.

What Works ✅

  • NPC spawning (using WrapperPlayServerPlayerInfoUpdate + WrapperPlayServerSpawnEntity)
  • Player skins display correctly
  • Entity metadata (skin layers, colors)
  • Distance detection and angle calculations
  • Packet creation and sending (no errors in logs)

What Doesn't Work ❌

  • Visual rotation of the NPC's head/body
  • All rotation-related packets seem to have no effect on the client

Implementation Details

How I spawn NPCs (this works perfectly)

public void sendSpawnPackets(Player player, int entityID, Location location, Pose entityPose) {
    // STEP 1: PlayerInfo with skin
    List<WrapperPlayServerPlayerInfoUpdate.PlayerInfo> playerInfoList = new ArrayList<>();
    UserProfile userProfile = new UserProfile(npcUUID, npcName);
    
    // Add skin texture
    userProfile.getTextureProperties().add(
        new TextureProperty("textures", skinValue, skinSignature)
    );
    
    WrapperPlayServerPlayerInfoUpdate.PlayerInfo playerInfo = 
        new WrapperPlayServerPlayerInfoUpdate.PlayerInfo(
            userProfile,
            false, // listed
            0,     // ping
            GameMode.SURVIVAL,
            null,  // displayName
            null   // chatSession
        );
    
    playerInfoList.add(playerInfo);
    
    // Send PlayerInfo
    WrapperPlayServerPlayerInfoUpdate playerInfoPacket = 
        new WrapperPlayServerPlayerInfoUpdate(
            WrapperPlayServerPlayerInfoUpdate.Action.ADD_PLAYER,
            playerInfoList
        );
    PacketEvents.getAPI().getPlayerManager().sendPacket(player, playerInfoPacket);
    
    // STEP 2: Spawn entity
    WrapperPlayServerSpawnEntity spawnPacket = new WrapperPlayServerSpawnEntity(
        entityID,
        Optional.of(npcUUID),
        EntityTypes.PLAYER,
        new Vector3d(location.getX(), location.getY(), location.getZ()),
        location.getPitch(),
        location.getYaw(),
        location.getYaw(), // headYaw
        0,                 // data
        Optional.of(new Vector3d(0, 0, 0)) // velocity
    );
    PacketEvents.getAPI().getPlayerManager().sendPacket(player, spawnPacket);
    
    // STEP 3: Metadata for skin layers
    EntityData skinLayers = new EntityData(
        16, // index for displayed skin parts
        EntityDataTypes.BYTE,
        (byte) 127 // all layers enabled
    );
    
    WrapperPlayServerEntityMetadata metadataPacket = 
        new WrapperPlayServerEntityMetadata(entityID, List.of(skinLayers));
    PacketEvents.getAPI().getPlayerManager().sendPacket(player, metadataPacket);
}

How I try to rotate the NPC (this doesn't work)

I run this every 2 ticks (synchronously on main thread) for NPCs within 6 blocks of players:

public static void sendHeadRotate(Player player, int entityID, Location npcLocation) {
    if (player == null || !player.isOnline() || npcLocation == null) return;
    
    // Calculate direction toward the player
    Location newLoc = npcLocation.clone()
        .setDirection(player.getLocation().clone().subtract(npcLocation).toVector());
    
    // Try TELEPORT with same position but new rotation
    WrapperPlayServerEntityTeleport teleportPacket = new WrapperPlayServerEntityTeleport(
        entityID,
        new Vector3d(npcLocation.getX(), npcLocation.getY(), npcLocation.getZ()),
        newLoc.getYaw(),
        newLoc.getPitch(),
        false // onGround
    );
    PacketEvents.getAPI().getPlayerManager().sendPacket(player, teleportPacket);
    
    // Also send head rotation
    WrapperPlayServerEntityHeadLook headLookPacket = 
        new WrapperPlayServerEntityHeadLook(entityID, newLoc.getYaw());
    PacketEvents.getAPI().getPlayerManager().sendPacket(player, headLookPacket);
}

Packet creation helpers:

public static WrapperPlayServerEntityTeleport createTeleport(int entityID, Location location) {
    return new WrapperPlayServerEntityTeleport(
        entityID,
        new Vector3d(location.getX(), location.getY(), location.getZ()),
        location.getYaw(),
        location.getPitch(),
        false // onGround
    );
}

public static WrapperPlayServerEntityHeadLook createHeadRotation(int entityID, float yaw) {
    // PacketEvents handles conversion automatically (degrees → protocol format)
    return new WrapperPlayServerEntityHeadLook(entityID, yaw);
}

public static WrapperPlayServerEntityRotation createRotation(int entityID, float yaw, float pitch) {
    return new WrapperPlayServerEntityRotation(entityID, yaw, pitch, true);
}

What I've Tried

  1. EntityRotation + EntityHeadLook packets → No visual effect
  2. EntityHeadLook only → No visual effect
  3. EntityTeleport with rotation + EntityHeadLook → No visual effect
  4. Async vs sync execution → No difference
  5. Different angle formats → PacketEvents documentation confirms float degrees are correct

Debug Results

With extensive logging, I confirmed:

  • ✅ Packets are created successfully
  • ✅ Packets are sent without exceptions
  • ✅ Yaw/pitch calculations are correct (e.g., yaw changes from 213° to 214°)
  • ✅ Distance detection works (only sending to players < 6 blocks away)
  • NPCs don't rotate visually at all

Questions

  1. Do rotation packets work for fake entities (client-side only) in Minecraft 1.21.10?

    • My NPCs are created purely client-side with spawn packets (no server-side entity)
    • Could this be why rotation packets have no effect?
  2. Is there a specific packet sequence required?

    • Should I send PlayerInfo again before rotation?
    • Do I need to update metadata along with rotation?
  3. Is this a known limitation in PacketEvents 2.10.1 or Minecraft 1.21.10?

    • Did the protocol change regarding fake entity rotation?
  4. Alternative approaches?

    • Should I despawn and respawn the NPC with new rotation instead?
    • Is there a metadata field for rotation I'm missing?

Expected Behavior

The NPC should visually rotate its head/body to face nearby players, similar to how real player entities look at each other.

Actual Behavior

The NPC remains frozen in its initial spawn rotation, even though rotation packets are being sent successfully every 2 ticks.


Any help or insights would be greatly appreciated! Is this a known issue, or am I missing something in my implementation?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions