Nms head rotation in 1.13

Discussion in 'Spigot Plugin Development' started by cc007, Aug 11, 2018 at 3:25 AM.

  1. Hello. I was looking to find a way to set the rotation of a head using nms. I used this as part of a bigger code segment in the past and it worked between version 1.8.0 and 1.12.2, but since there were a lot of under the hood changes in 1.13 this seems to have changed (I used to use the nms TileEntitySkull.setRotation())

    Does anyone know how to do this with nms 1.13 classes?
     
  2. Are you able to post a larger code segment that you have used in the past?
    That may help give us more context on what methods are no longer working and such.
    To my knowledge, you cannot control the rotation of a tile entity as it is completely client side, but if you have been doing that through 1.8 and 1.12.2, then maybe I'm missing something...
    Posting more code will help (y)
     
  3. I made a method that uses 0-15 value and converts it to the TileEntitySkull rotation values.
    For multiple version support I had to use reflection, because nms has the version name in its package name.

    Here is the method (simplified):

    Code (Text):
    private static void placeHead(ItemStack item, int x, int y, int z, int rotation, World w) {
    try{
        //get package names
        Package obcPackage = Bukkit.getServer().getClass().getPackage();
        String obcPackageName = obcPackage.getName();
        String obcVersion = obcPackageName.substring(obcPackageName.lastIndexOf(".") + 1);
        String nmsPackageName = "net.minecraft.server." + obcVersion;

        //reflection setup
        Class<?> nmsBlockPositionClass = Class.forName(nmsPackageName + ".BlockPosition");
        Constructor nmsBlockPositionConstructor = nmsBlockPositionClass.getDeclaredConstructor(int.class, int.class, int.class);

        Class<?> nmsWorldClass = Class.forName(nmsPackageName + ".World");
        Method worldGetTileEntity = nmsWorldClass.getDeclaredMethod("getTileEntity", nmsBlockPositionClass);

        Class<?> nmsBlocksClass = Class.forName(nmsPackageName + ".Blocks");
        Field skullField = nmsBlocksClass.getDeclaredField("SKULL");

        Class<?> nmsTileEntitySkullClass = Class.forName(nmsPackageName + ".TileEntitySkull");
        Method tileEntitySkullSetRotation = nmsTileEntitySkullClass.getDeclaredMethod("setRotation", int.class);

       ...

        Object bp = nmsBlockPositionConstructor.newInstance(x, y, z);
        Object tileentityskull = worldGetTileEntity.invoke(world, bp);

        ...
     
        //calculate item rotation
        int itemRotation = ((int) Math.floor((double) ((22 * rotation) * 16.0F / 360.0F) + 0.5D)) & 15;

        //set item rotation and place in world
        tileEntitySkullSetRotation.invoke(tileentityskull, itemRotation);
    }catch(...){...}
    }
    This works in v1.8-v1.12.2, but not in v1.13, because the setRotation method (among others) doesnt exist anymore.
     
    #3 cc007, Aug 11, 2018 at 7:35 AM
    Last edited: Aug 11, 2018 at 7:40 AM
  4. I do know now that bukkit has the Rotatable subclass of BlockData. It uses the BlockFace enum. This would also work for me in a lot of cases, but I can not easily loop over it to get all the directions (for instance if I wanted to create a rotating head, using BlockFace would be a pain, compared to the rotation int) also, this is not nms, but bukkit.
     
    #4 cc007, Aug 11, 2018 at 7:37 AM
    Last edited: Aug 11, 2018 at 8:05 AM
  5. According to https://minecraft.gamepedia.com/index.php?title=Block_states#Mob_head
    The new block state is still an integer from 0 to 15.
    So you still should be able to set this numeric value somewhere in NMS.
    But you should consider not doing so.
    How about if you make a Map<Integer,BlockFace> where the integer is the animation step number and the block face is the corresponding direction.
    Like this you can save any rotation animation and loop throuth the map keys to play it.
     
  6. Oh I see. In my previous post, I thought you were referring to tile entities as the items that float on the floor. You're referring to the player/skull item that you can place on blocks.
    To set the rotational value you can with an int you can use something along the lines of the following:
    Code (Text):
    private static final BlockFace[] blockFaceValues = BlockFace.values();

    private void rotateSkull(Rotatable playerSkull, int rotationIndex) {
        playerSkull.setRotation(blockFaceValues[rotationIndex]);
    }
    Keep in mind that some rotational values of BlockFace may throw an IllegalArgumentException. So you should probably create your own BlockFace[] filled with BlockFace enums in the order that you want to rotate through and then use .values() to create an array of the enums which you can loop through with a number.
    Hopefully that helps!
     

Share This Page