Resource How to force change player skin, and update it instantly!

Discussion in 'Spigot Plugin Development' started by FlameFOxYT, Jul 6, 2021.

  1. This resource was tested in 1.16.5, in 1.8 the packets sent are the same, the difference are the constructors.


    lets start by creating a class called SkinUtil and add some player data,
    important note is that other player data like inventory and more are not important to save since you can update it in a different way:
    Code (Text):
    public class SkinUtil {
     
        private Player player;
        private Collection<PotionEffect> effects;
        private Location location;
        private int slot;
     
        public SkinUtil(Player player) {
            this.player = player;
           
        }
    }

    after that is done, lets add a method for changing the gameprofile of the player:
    Code (Text):
    public void changeSkin(String data, String signature) {
            EntityPlayer ePlayer = ((CraftPlayer) player).getHandle();
            GameProfile profile = ePlayer.getProfile();
            PropertyMap pMap = profile.getProperties();
            Property property = pMap.get("textures").iterator().next();
            pMap.remove("textures", property);
            pMap.put("textures", new Property("textures", data, signature));
    }

    now all thats left is to update the skin
    lets start by creating a getter for player gamemode(will be important very soon):
    Code (Text):
    public EnumGamemode getGamemode() {
            switch (player.getGameMode()) {
            case SURVIVAL:
                return EnumGamemode.SURVIVAL;
            case CREATIVE:
                return EnumGamemode.CREATIVE;
            case SPECTATOR:
                return EnumGamemode.SPECTATOR;
            default:
                return EnumGamemode.ADVENTURE;
            }
    }

    now lets make an update method
    we will start by saving the player information, effects, location, and the slot they are holding
    (other information can be updated in a different way, well get to that)
    Code (Text):
    public void updateSkin() {
       effects = player.getActivePotionEffects();
       location = player.getLocation();
       slot = player.getInventory().getHeldItemSlot();
    }[/code]


    after that we send 3 different packets to the player, 1 packet will remove player information, the second one will add information, and the third will respawn the player, important note is that the player information is not getting removed, we are sending a packet of removing the information, meaning the player inventory will look cleared but its not, you can later update it with opening and closing an inventory.
    Code (Text):
    CraftWorld world = (CraftWorld) location.getWorld();
    CraftPlayer craftPlayer = ((CraftPlayer) player);
    EntityPlayer entityPlayer = craftPlayer.getHandle();
    entityPlayer.playerConnection.sendPacket(new PacketPlayOutPlayerInfo(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.REMOVE_PLAYER, craftPlayer.getHandle()));
    entityPlayer.playerConnection.sendPacket(new PacketPlayOutPlayerInfo(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.ADD_PLAYER, craftPlayer.getHandle()));
    entityPlayer.playerConnection.sendPacket(new PacketPlayOutRespawn(world.getHandle().getDimensionManager(), world.getHandle().getDimensionKey(), world.getEnvironment().getId(), getGamemode(), getGamemode(), false, false, false));

    after the packets got sent the player will teleport to his spawnpoint, because of the respawn packet,
    we prevent seeing that by immediately teleporting the player to his previous location:
    Code (Text):
    player.teleport(location);

    then we hide and show the player to every player online (this is how to update to other people):
    Code (Text):
    for (Player player : Bukkit.getOnlinePlayers()) {
                player.hidePlayer(this.player);
                player.showPlayer(this.player);
    }

    finally we update after 2 ticks, the player slot, the potion effects, the exp, the health, and the inventory,
    the slot and potion effects are updated by variables we saved, the exp on the other hand is updated by just changing the player exp to his own, and the health and saturation automatically update when you set health to the player health minus 0.0001, the inventory is updated by opening and closing any inventory,
    here I used ender chest for simplicity:
    Code (Text):
    Bukkit.getScheduler().runTaskLater(Main.main, new Runnable() {public void run() {
                player.getInventory().setHeldItemSlot(slot);
                player.addPotionEffects(effects);
                player.setExp(player.getExp());
                player.setHealth(player.getHealth()-0.0001);
                player.openInventory(player.getEnderChest());
                player.closeInventory();
            }}, 2);
    we are now done, it is that easy and simple
    full class
    Code (Text):

    import java.util.Collection;

    import org.bukkit.Bukkit;
    import org.bukkit.Location;
    import org.bukkit.craftbukkit.v1_16_R3.CraftWorld;
    import org.bukkit.craftbukkit.v1_16_R3.entity.CraftPlayer;
    import org.bukkit.entity.Player;
    import org.bukkit.potion.PotionEffect;

    import com.mojang.authlib.GameProfile;
    import com.mojang.authlib.properties.Property;
    import com.mojang.authlib.properties.PropertyMap;

    import net.minecraft.server.v1_16_R3.EntityPlayer;
    import net.minecraft.server.v1_16_R3.EnumGamemode;
    import net.minecraft.server.v1_16_R3.PacketPlayOutPlayerInfo;
    import net.minecraft.server.v1_16_R3.PacketPlayOutRespawn;

    public class SkinUtil {
     
        private Player player;
        private Collection<PotionEffect> effects;
        private Location location;
        private int slot;
     
        public SkinUtil(Player player) {
            this.player = player;
           
        }
       
     
     
        public void changeSkin(String data, String signature) {
            EntityPlayer ePlayer = ((CraftPlayer) player).getHandle();
            GameProfile profile = ePlayer.getProfile();
            PropertyMap pMap = profile.getProperties();
            Property property = pMap.get("textures").iterator().next();
            pMap.remove("textures", property);
            pMap.put("textures", new Property("textures", data, signature));
            updateSkin();
        }

     
     
        public void updateSkin() {
            effects = player.getActivePotionEffects();
            location = player.getLocation();
            slot = player.getInventory().getHeldItemSlot();
         
            CraftWorld world = (CraftWorld) location.getWorld();
            CraftPlayer craftPlayer = ((CraftPlayer) player);
            EntityPlayer entityPlayer = craftPlayer.getHandle();
            entityPlayer.playerConnection.sendPacket(new PacketPlayOutPlayerInfo(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.REMOVE_PLAYER, craftPlayer.getHandle()));
            entityPlayer.playerConnection.sendPacket(new PacketPlayOutPlayerInfo(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.ADD_PLAYER, craftPlayer.getHandle()));
            entityPlayer.playerConnection.sendPacket(new PacketPlayOutRespawn(world.getHandle().getDimensionManager(), world.getHandle().getDimensionKey(), world.getEnvironment().getId(), getGamemode(), getGamemode(), false, false, false));
         
            player.teleport(location);
            for (Player player : Bukkit.getOnlinePlayers()) {
                player.hidePlayer(this.player);
                player.showPlayer(this.player);
            }
         
            Bukkit.getScheduler().runTaskLater(Main.main, new Runnable() {public void run() {
                player.getInventory().setHeldItemSlot(slot);
                player.addPotionEffects(effects);
                player.setExp(player.getExp());
                player.setHealth(player.getHealth()-0.0001);
                player.openInventory(player.getEnderChest());
                player.closeInventory();
            }}, 2);
        }
     
       
     
        public EnumGamemode getGamemode() {
            switch (player.getGameMode()) {
            case SURVIVAL:
                return EnumGamemode.SURVIVAL;
            case CREATIVE:
                return EnumGamemode.CREATIVE;
            case SPECTATOR:
                return EnumGamemode.SPECTATOR;
            default:
                return EnumGamemode.ADVENTURE;
            }
        }
     
     

     
    }
     
     
    • Useful Useful x 1
  2. Do you know if this works on 1.17?
     
  3. the code i wrote will not work for 1.17, youll have to change some variables,
    the way packets are sent changed
     
    #3 FlameFOxYT, Jul 6, 2021
    Last edited: Jul 6, 2021
    • Like Like x 1