Will This Method Work For Changing A Player's Skin?

Discussion in 'Spigot Plugin Development' started by ReadySetPawn, Jul 1, 2015.

  1. I don't have access to Minecraft and I won't have access until mid August. So if anybody is kind enough to test out the method I'm about to paste and post rather it works or not, it'll be highly appreciated.

    Code (Text):
    public static void changeSkin(Player player, UUID skin){

            EntityPlayer nmsPlayer = ((CraftPlayer) player).getHandle();
            GameProfile gp = nmsPlayer.getProfile();
            MinecraftServer nmsServer = ((CraftServer) player.getServer()).getServer();

            //Remove player from server
            PacketPlayOutPlayerInfo removePacket = new PacketPlayOutPlayerInfo(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.REMOVE_PLAYER, nmsPlayer);

            for (Player online : Bukkit.getOnlinePlayers()){
                ((CraftPlayer) online).getHandle().playerConnection.sendPacket(removePacket);
            }

            //Edit the player's GameProfile to have the skin we want
            GameProfile skinProfile = new GameProfile(skin, null);
            skinProfile = nmsServer.aC().fillProfileProperties(skinProfile, true);

            Property textures = skinProfile.getProperties().get("textures").iterator().next();
            gp.getProperties().put("textures", textures);

            //Set the player's GameProfile to the updated one
            Field field = null;
            Field modifiersField = null;

            try {
                field = EntityHuman.class.getField("bH");
                modifiersField = Field.class.getField("modifiers");
            } catch (NoSuchFieldException e){
                e.printStackTrace();
            }

            field.setAccessible(true);
            modifiersField.setAccessible(true);

            try {
                modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
                field.set(nmsPlayer, gp);
            } catch (IllegalAccessException e){
                e.printStackTrace();
            }

            //Re-add the player back to the server
            PacketPlayOutPlayerInfo addPacket = new PacketPlayOutPlayerInfo(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.ADD_PLAYER, nmsPlayer);

            for (Player online : Bukkit.getOnlinePlayers()){
                ((CraftPlayer) online).getHandle().playerConnection.sendPacket(addPacket);
            }

            //Destroy player entity
            PacketPlayOutEntityDestroy destroyPacket = new PacketPlayOutEntityDestroy(nmsPlayer.getId());

            for (Player online : Bukkit.getOnlinePlayers()){
                if (!online.equals(player)){
                    ((CraftPlayer) online).getHandle().playerConnection.sendPacket(destroyPacket);
                }
            }

            //Respawn player with new skin
            PacketPlayOutNamedEntitySpawn spawnPacket = new PacketPlayOutNamedEntitySpawn(nmsPlayer);

            for (Player online : Bukkit.getOnlinePlayers()){
                if (!online.equals(player)){
                    ((CraftPlayer) online).getHandle().playerConnection.sendPacket(spawnPacket);
                }
            }

        }
    Also, since I posted this code publicly now, I guess it's free to use :D
     
  2. Serializator

    Supporter

    I used 1.8.7-R0.1-SNAPSHOT

    It didn't work, but I had to change some parts from the code you've provided because I got a NoSuchFieldException and a method name changed. I will list the parts I've changed below and after that the updated code from that part.

    Code (Text):
    skinProfile = nmsServer.aC().fillProfileProperties(skinProfile, true);
    Code (Text):
    try {
        field = EntityHuman.class.getField("bH");
        modifiersField = Field.class.getField("modifiers");
    } catch (NoSuchFieldException e){
        e.printStackTrace();
    }

    Became:
    Code (Text):
    skinProfile = nmsServer.aD().fillProfileProperties(skinProfile, true);
    Code (Text):
    try {
        field = EntityHuman.class.getDeclaredField("bH");
        modifiersField = Field.class.getDeclaredField("modifiers");
    } catch (NoSuchFieldException e){
        e.printStackTrace();
    }
     
  3. You can't change skins?
     
    • Funny Funny x 3
  4. @Serializator You didn't have to change getField to getDeclaredField, since getField will pull declared + inherited fields, it's a pointless change.

    Please read the JavaDocs regarding the two, and you will see what I mean.
     
    • Agree Agree x 1
  5. Serializator

    Supporter

    I've changed it because it was causing a NoSuchFieldException.
     
  6. Class#getField() only returns a field if it's defined in that class. Class#getDeclaredField() returns a public field in that class or in any super-classes.
    So if the field is public, but in a super class one has to use Class#getDeclaredField() instead of Class#getField().
     
    • Agree Agree x 1
  7. You have it backwards. Just check the JavaDocs..

    Class#getField will grab all public members from the class in question and all super interfaces.
    Class#getDeclaredField will grab all members, even public, only from the current class and no super interfaces.

    My original assumption was wrong, but you're confusing this even further.
     
  8. Serializator

    Supporter

    Thats why I couldn't use the Class#getField because the "bH" field is private.
     
  9. Did it work after you applied the changes?
     
  10. Serializator

    Supporter

    No, the skin didn't change.
     
  11. Did you enter a valid uuid for the second argument?
     
  12. Serializator

    Supporter

    I did ;-)
     
  13. Hmm strange. Idk I think other players would see your skin change not sure though.
     
  14. It does not work because player skin data has to be signed by mojang.

    Secondly to have your own skin changed client-side you need a dimension switch, for other player's you just need to respawn them (use bukkit's hidePlayer/showPlayer method) after you've changed the skin data which is in the tablist packet.

    Maybe you want to look at my plugin CustomSkins (however it does not support switch dimensions), but shows how you can edit a player's skin with ProtocolLib.
     
    • Informative Informative x 1
  15. Thanks :) I'll check it out.
     
  16. Just as a notice that this plugin is based on this thread, showing further information how that can be done.