NMS [Reflection] Creating Skulls with Non-Player Skins!

Discussion in 'Resources' started by dori99xd, Nov 12, 2014.

?

Useful?

  1. Yeah!

    16 vote(s)
    45.7%
  2. No!

    4 vote(s)
    11.4%
  3. Maybe...

    2 vote(s)
    5.7%
  4. Bacon.

    13 vote(s)
    37.1%
Thread Status:
Not open for further replies.
  1. Offline

    GermanCoding

    First, this is very cool!
    I have to say that I am not very experienced with those skin system, so I did not really understand what is going on there.

    I tried this a few minutes ago, first I could only the see default skins on the skulls when I use your code. I waited about 10 minutes and made multiple restarts of the server and the client. Suddenly (Maybe some cache was updated, I do not know) the skins on the skulls are shown correctly, even if I create new skulls now, they have immediatly the correct/changed skin.
    In the log of my local server, the following error occured when the skulls got the correct skin for the first time:
    Code:
    [15:01:51 WARN]: Couldn't look up profile properties for net.minecraft.util.com.
    mojang.authlib.GameProfile@71b43e5e[id=74b9d394-f585-4115-9264-721212a0373d,name
    =Nummer378,properties={},legacy=false]
    net.minecraft.util.com.mojang.authlib.exceptions.AuthenticationException: The cl
    ient has sent too many requests within a certain amount of time
    My server is in online mode (The login was successfull, even if there was an error) and I can also see my skin AND the skulls correctly. So I get that there is an authentication problem, but everything works though... Strange skin and authentication system :).
     
  2. Offline

    dori99xd

    Yep. That's an known "bug" that i have to fix. You can only send 1 Request per Profile per Minute to Mojang. Sorry! I hope i can fix this :O
     
  3. Offline

    crolemol

    GermanCoding
    When you're server is offline mode this problem doesn't appear so thats a quick solution but it is not good to be offline mode. dori99xd you could try to cache the skins with guava
     
  4. Offline

    xTrollxDudex

    It also helps to spell your thread titles correctly :)
     
  5. Offline

    dori99xd

    xTrollxDudex My english is not the best. Can you post/send me a correct title?
     
  6. Offline

    mrCookieSlime Retired Staff

    dori99xd
    You typed Refletion instead of Reflection.
    You can also use one of these fancy new Prefixes now instead of fake Prefixes.
     
  7. Offline

    dori99xd

    Typo :p
     
  8. Offline

    Algeseven

    But How to get an Item in Inventory like with /give command ?
     
  9. Offline

    dori99xd

    What you mean? Do you want a Skull in an Inventory with a Skin (1.8 Feature), or did you want that you have a Skull in your Inventory, and when you place it, it has a Custom Skin? For the first: That skulls have a skin in inventory's is a 1.8 feature. Spigot isn't supported here (Add me on Skype: steam-dori99xd). Second: Give an ItemStack a lore, and in the BlockPlaceEvent, you'll check if the PlacedItem has the lore, and update the skull with my method.
     
  10. @xTrollxDudex @dori99xd Having trouble with 1.8(not the protocol hack). Error:

    Code:
    [22:23:24] [Server thread/WARN]: java.lang.NoSuchMethodException: net.minecraft.server.v1_8_R1.WorldServer.getTileEntity(int, int, int)
    [22:23:24] [Server thread/WARN]:     at java.lang.Class.getMethod(Unknown Source)
    [22:23:24] [Server thread/WARN]:     at me.HeyAwesomePeople.fieldmedic.FieldMedic.onEnable(FieldMedic.java:42)
    [22:23:24] [Server thread/WARN]:     at org.bukkit.plugin.java.JavaPlugin.setEnabled(JavaPlugin.java:321)
    [22:23:24] [Server thread/WARN]:     at org.bukkit.plugin.java.JavaPluginLoader.enablePlugin(JavaPluginLoader.java:335)
    [22:23:24] [Server thread/WARN]:     at org.bukkit.plugin.SimplePluginManager.enablePlugin(SimplePluginManager.java:405)
    [22:23:24] [Server thread/WARN]:     at org.bukkit.craftbukkit.v1_8_R1.CraftServer.loadPlugin(CraftServer.java:355)
    [22:23:24] [Server thread/WARN]:     at org.bukkit.craftbukkit.v1_8_R1.CraftServer.enablePlugins(CraftServer.java:315)
    [22:23:24] [Server thread/WARN]:     at org.bukkit.craftbukkit.v1_8_R1.CraftServer.reload(CraftServer.java:744)
    [22:23:24] [Server thread/WARN]:     at org.bukkit.Bukkit.reload(Bukkit.java:534)
    [22:23:24] [Server thread/WARN]:     at org.bukkit.command.defaults.ReloadCommand.execute(ReloadCommand.java:23)
    [22:23:24] [Server thread/WARN]:     at org.bukkit.command.SimpleCommandMap.dispatch(SimpleCommandMap.java:141)
    [22:23:24] [Server thread/WARN]:     at org.bukkit.craftbukkit.v1_8_R1.CraftServer.dispatchCommand(CraftServer.java:645)
    [22:23:24] [Server thread/WARN]:     at org.bukkit.craftbukkit.v1_8_R1.CraftServer.dispatchServerCommand(CraftServer.java:631)
    [22:23:24] [Server thread/WARN]:     at net.minecraft.server.v1_8_R1.DedicatedServer.aM(DedicatedServer.java:353)
    [22:23:24] [Server thread/WARN]:     at net.minecraft.server.v1_8_R1.DedicatedServer.z(DedicatedServer.java:317)
    [22:23:24] [Server thread/WARN]:     at net.minecraft.server.v1_8_R1.MinecraftServer.y(MinecraftServer.java:623)
    [22:23:24] [Server thread/WARN]:     at net.minecraft.server.v1_8_R1.MinecraftServer.run(MinecraftServer.java:526)
    [22:23:24] [Server thread/WARN]:     at java.lang.Thread.run(Unknown Source)
    Happening on this line:
    Code:
    getWorldTileEntity = CustomHead.getMCClass("WorldServer").getMethod("getTileEntity", int.class, int.class, int.class);
    In 1.8, the only getTileEntity method requires a single BlockPosition. There doesn't seem to be a method I can replace it with.
     
  11. Offline

    GermanCoding

    @HeyAwesomePeople

    Yes, since 1.8 locations are represented in vanilla as "BlockPositions". In earlier versions you gave only the x, y and z parameters.

    In december last year, I updated the code for myself. This is my code:

    Code:
    public static void setSkullWithNonPlayerProfile(String skinURL, boolean randomName, Block skull) {
            if (skull.getType() != Material.SKULL)
                throw new IllegalArgumentException("Block must be a skull.");
            TileEntitySkull skullTile = (TileEntitySkull) ((CraftWorld) skull.getWorld()).getHandle().getTileEntity(new BlockPosition(skull.getX(), skull.getY(), skull.getZ()));
            skullTile.setGameProfile(getNonPlayerProfile(skinURL, randomName));
            skull.getWorld().refreshChunk(skull.getChunk().getX(), skull.getChunk().getZ());
        }
    As you can see, I used the NMS version without reflection. There isn't really a "big change" only the parameters in the getTileEntity() method changed. Now, you give a BlockPosition object wich contains the x,y and z coordinate of the block.

    In december when I tested this code, the refreshChunk() method was also a littlebit buggy (Skulls randomly disappeared when refreshing the chunk) but without refreshChunk you won't see the changed skin of the skull.
     
  12. Offline

    guitargun

    @dori99xd When I am home. I am going to implement this and try to use it with the citizens library. this can just make the difference between being lucky with a skin name. or just being awesome in every aspect.
     
  13. @GermanCoding Okay that seems to be fine but I need to change the skull inside an inventory.... Kinda hard to do that when you cannot call block position or locations for the item.
     
  14. Offline

    crolemol

    @HeyAwesomePeople
    not hard at all :p
    Code:
        public static ItemStack getSkull(String skinURL) {
            ItemStack head = new ItemStack(Material.SKULL_ITEM, 1, (short)3);
            if(skinURL.isEmpty())return head;
           
           
            ItemMeta headMeta = head.getItemMeta();
            GameProfile profile = new GameProfile(UUID.randomUUID(), null);
            byte[] encodedData = Base64.encodeBase64(String.format("{textures:{SKIN:{url:\"%s\"}}}", skinURL).getBytes());
            profile.getProperties().put("textures", new Property("textures", new String(encodedData)));
            Field profileField = null;
            try {
                profileField = headMeta.getClass().getDeclaredField("profile");
            } catch (NoSuchFieldException | SecurityException e) {
                e.printStackTrace();
            }
            profileField.setAccessible(true);
            try {
                profileField.set(headMeta, profile);
            } catch (IllegalArgumentException | IllegalAccessException e) {
                e.printStackTrace();
            }
            head.setItemMeta(headMeta);
            return head;
        }
    this uses reflection so it should be update safe :p
     
    stefvanschie likes this.
  15. Okay I see. So in this case I only have a value id. For example, I use this code in a command block to spawn the block I need:
    Code:
     /give @p minecraft:skull 1 3 {display:{Name:"Medicine Chest"},SkullOwner:{Id:"f32f4e87-9fb0-48b0-8cc4-56eebf3c6f82",Properties:{textures:[{Value:"eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZjdjN2RmNTJiNWU1MGJhZGI2MWZlZDcyMTJkOTc5ZTYzZmU5NGYxYmRlMDJiMjk2OGM2YjE1NmE3NzAxMjZjIn19fQ=="}]}}}
    Instead of using a URL, I just have a texture value. So I used this code to implement the value instead of the url.
    Code:
        public static ItemStack getSkull(String skinURL) {
            ItemStack head = new ItemStack(Material.SKULL_ITEM, 1, (short) 3);
            if (skinURL.isEmpty()) return head;
            ItemMeta headMeta = head.getItemMeta();
            GameProfile profile = new GameProfile(UUID.randomUUID(), null);
            byte[] encodedData = Base64.encodeBase64(String.format("{textures:[{Value:\"%s\"}]}", skinURL).getBytes());
            Bukkit.broadcastMessage(String.format("{textures:[{Value:\"%s\"}]}", skinURL));
            profile.getProperties().put("textures", new Property("textures", new String(encodedData)));
            Field profileField = null;
            try {
                profileField = headMeta.getClass().getDeclaredField("profile");
            } catch (NoSuchFieldException e) {
                e.printStackTrace();
            } catch (SecurityException e) {
                e.printStackTrace();
            }
            profileField.setAccessible(true);
            try {
                profileField.set(headMeta, profile);
            } catch (IllegalArgumentException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
            head.setItemMeta(headMeta);
            return head;
        }
    This just returns a steve head. Any thoughts?
     
  16. Offline

    crolemol

    @HeyAwesomePeople
    You have to use SKIN instead of value i tyink. Try looking some more at the way my string property is managed
     
  17. Offline

    stefvanschie

    Works perfectly, thanks.
     
Thread Status:
Not open for further replies.

Share This Page