broken-arrow

SkullCreator

Nov 4th, 2021
122
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Java 9.78 KB | None | 0 0
  1. package org.brokenarrow.storage.util;
  2.  
  3. import com.mojang.authlib.GameProfile;
  4. import com.mojang.authlib.properties.Property;
  5. import org.bukkit.Bukkit;
  6. import org.bukkit.Material;
  7. import org.bukkit.OfflinePlayer;
  8. import org.bukkit.SkullType;
  9. import org.bukkit.block.Block;
  10. import org.bukkit.block.Skull;
  11. import org.bukkit.inventory.ItemStack;
  12. import org.bukkit.inventory.meta.SkullMeta;
  13.  
  14. import java.lang.reflect.Field;
  15. import java.lang.reflect.InvocationTargetException;
  16. import java.lang.reflect.Method;
  17. import java.net.URI;
  18. import java.net.URISyntaxException;
  19. import java.util.Base64;
  20. import java.util.UUID;
  21.  
  22. /**
  23.  * A library for the Bukkit API to create player skulls
  24.  * from names, base64 strings, and texture URLs.
  25.  * <p>
  26.  * Does not use any NMS code, and should work across all versions.
  27.  *
  28.  * @author Dean B on 12/28/2016.
  29.  */
  30. public class SkullCreator {
  31.  
  32.     private SkullCreator() {
  33.     }
  34.  
  35.     private static boolean warningPosted = false;
  36.  
  37.     // some reflection stuff to be used when setting a skull's profile
  38.     private static Field blockProfileField;
  39.     private static Method metaSetProfileMethod;
  40.     private static Field metaProfileField;
  41.  
  42.     /**
  43.      * Creates a player skull, should work in both legacy and new Bukkit APIs.
  44.      */
  45.     public static ItemStack createSkull() {
  46.         try {
  47.             return new ItemStack(Material.valueOf("PLAYER_HEAD"));
  48.         } catch (IllegalArgumentException e) {
  49.             return new ItemStack(getMaterial("SKULL_ITEM"), 1, (byte) 3);
  50.         }
  51.     }
  52.  
  53.     /**
  54.      * Creates a player skull item with the skin based on a player's name.
  55.      *
  56.      * @param name The Player's name.
  57.      * @return The head of the Player.
  58.      * @deprecated names don't make for good identifiers.
  59.      */
  60.     public static ItemStack itemFromName(String name) {
  61.         return itemWithName(createSkull(), name);
  62.     }
  63.  
  64.     /**
  65.      * Creates a player skull item with the skin based on a player's UUID.
  66.      *
  67.      * @param id The Player's UUID.
  68.      * @return The head of the Player.
  69.      */
  70.     public static ItemStack itemFromUuid(UUID id) {
  71.         return itemWithUuid(createSkull(), id);
  72.     }
  73.  
  74.     /**
  75.      * Creates a player skull item with the skin at a Mojang URL.
  76.      *
  77.      * @param url The Mojang URL.
  78.      * @return The head of the Player.
  79.      */
  80.     public static ItemStack itemFromUrl(String url) {
  81.         return itemWithUrl(createSkull(), url);
  82.     }
  83.  
  84.     /**
  85.      * Creates a player skull item with the skin based on a base64 string.
  86.      *
  87.      * @param base64 The Mojang URL.
  88.      * @return The head of the Player.
  89.      */
  90.     public static ItemStack itemFromBase64(String base64) {
  91.         return itemWithBase64(createSkull(), base64);
  92.     }
  93.  
  94.     /**
  95.      * Modifies a skull to use the skin of the player with a given name.
  96.      *
  97.      * @param item The item to apply the name to. Must be a player skull.
  98.      * @param name The Player's name.
  99.      * @return The head of the Player.
  100.      * @deprecated names don't make for good identifiers.
  101.      */
  102.     @Deprecated
  103.     public static ItemStack itemWithName(ItemStack item, String name) {
  104.         notNull(item, "item");
  105.         notNull(name, "name");
  106.  
  107.         SkullMeta meta = (SkullMeta) item.getItemMeta();
  108.         meta.setOwner(name);
  109.         item.setItemMeta(meta);
  110.  
  111.         return item;
  112.     }
  113.  
  114.     /**
  115.      * Modifies a skull to use the skin of the player with a given UUID.
  116.      *
  117.      * @param item The item to apply the name to. Must be a player skull.
  118.      * @param id   The Player's UUID.
  119.      * @return The head of the Player.
  120.      */
  121.     public static ItemStack itemWithUuid(ItemStack item, UUID id) {
  122.         notNull(item, "item");
  123.         notNull(id, "id");
  124.  
  125.         SkullMeta meta = (SkullMeta) item.getItemMeta();
  126.         SetOwningPlayer(meta, Bukkit.getOfflinePlayer(id));
  127.         item.setItemMeta(meta);
  128.  
  129.         return item;
  130.     }
  131.  
  132.     /**
  133.      * Modifies a skull to use the skin at the given Mojang URL.
  134.      *
  135.      * @param item The item to apply the skin to. Must be a player skull.
  136.      * @param url  The URL of the Mojang skin.
  137.      * @return The head associated with the URL.
  138.      */
  139.     public static ItemStack itemWithUrl(ItemStack item, String url) {
  140.         notNull(item, "item");
  141.         notNull(url, "url");
  142.  
  143.         return itemWithBase64(item, urlToBase64(url));
  144.     }
  145.  
  146.     /**
  147.      * Modifies a skull to use the skin based on the given base64 string.
  148.      *
  149.      * @param item   The ItemStack to put the base64 onto. Must be a player skull.
  150.      * @param base64 The base64 string containing the texture.
  151.      * @return The head with a custom texture.
  152.      */
  153.     public static ItemStack itemWithBase64(ItemStack item, String base64) {
  154.         notNull(item, "item");
  155.         notNull(base64, "base64");
  156.  
  157.         if (!(item.getItemMeta() instanceof SkullMeta)) {
  158.             return null;
  159.         }
  160.         SkullMeta meta = (SkullMeta) item.getItemMeta();
  161.         mutateItemMeta(meta, base64);
  162.         item.setItemMeta(meta);
  163.  
  164.         return item;
  165.     }
  166.  
  167.     /**
  168.      * Sets the block to a skull with the given name.
  169.      *
  170.      * @param block The block to set.
  171.      * @param name  The player to set it to.
  172.      * @deprecated names don't make for good identifiers.
  173.      */
  174.     @Deprecated
  175.     public static void blockWithName(Block block, String name) {
  176.         notNull(block, "block");
  177.         notNull(name, "name");
  178.  
  179.         Skull state = (Skull) block.getState();
  180.         state.setOwningPlayer(Bukkit.getOfflinePlayer(name));
  181.         state.update(false, false);
  182.     }
  183.  
  184.     /**
  185.      * Sets the block to a skull with the given UUID.
  186.      *
  187.      * @param block The block to set.
  188.      * @param id    The player to set it to.
  189.      */
  190.     public static void blockWithUuid(Block block, UUID id) {
  191.         notNull(block, "block");
  192.         notNull(id, "id");
  193.  
  194.         setToSkull(block);
  195.         Skull state = (Skull) block.getState();
  196.         state.setOwningPlayer(Bukkit.getOfflinePlayer(id));
  197.         state.update(false, false);
  198.     }
  199.  
  200.     /**
  201.      * Sets the block to a skull with the skin found at the provided mojang URL.
  202.      *
  203.      * @param block The block to set.
  204.      * @param url   The mojang URL to set it to use.
  205.      */
  206.     public static void blockWithUrl(Block block, String url) {
  207.         notNull(block, "block");
  208.         notNull(url, "url");
  209.  
  210.         blockWithBase64(block, urlToBase64(url));
  211.     }
  212.  
  213.     /**
  214.      * Sets the block to a skull with the skin for the base64 string.
  215.      *
  216.      * @param block  The block to set.
  217.      * @param base64 The base64 to set it to use.
  218.      */
  219.     public static void blockWithBase64(Block block, String base64) {
  220.         notNull(block, "block");
  221.         notNull(base64, "base64");
  222.  
  223.         setToSkull(block);
  224.         Skull state = (Skull) block.getState();
  225.         mutateBlockState(state, base64);
  226.         state.update(false, false);
  227.     }
  228.  
  229.     private static void setToSkull(Block block) {
  230.         checkLegacy();
  231.  
  232.         try {
  233.             block.setType(Material.valueOf("PLAYER_HEAD"), false);
  234.         } catch (IllegalArgumentException e) {
  235.             block.setType(Material.valueOf("SKULL"), false);
  236.             Skull state = (Skull) block.getState();
  237.             state.setSkullType(SkullType.PLAYER);
  238.             state.update(false, false);
  239.         }
  240.     }
  241.  
  242.     private static void notNull(Object o, String name) {
  243.         if (o == null) {
  244.             throw new NullPointerException(name + " should not be null!");
  245.         }
  246.     }
  247.  
  248.     private static String urlToBase64(String url) {
  249.  
  250.         URI actualUrl;
  251.         try {
  252.             actualUrl = new URI(url);
  253.         } catch (URISyntaxException e) {
  254.             throw new RuntimeException(e);
  255.         }
  256.         String toEncode = "{\"textures\":{\"SKIN\":{\"url\":\"" + actualUrl.toString() + "\"}}}";
  257.         return Base64.getEncoder().encodeToString(toEncode.getBytes());
  258.     }
  259.  
  260.     private static GameProfile makeProfile(String b64) {
  261.         // random uuid based on the b64 string
  262.         UUID id = new UUID(
  263.                 b64.substring(b64.length() - 20).hashCode(),
  264.                 b64.substring(b64.length() - 10).hashCode()
  265.         );
  266.         GameProfile profile = new GameProfile(id, "aaaaa");
  267.         profile.getProperties().put("textures", new Property("textures", b64));
  268.         return profile;
  269.     }
  270.  
  271.     private static void mutateBlockState(Skull block, String b64) {
  272.         try {
  273.             if (blockProfileField == null) {
  274.                 blockProfileField = block.getClass().getDeclaredField("profile");
  275.                 blockProfileField.setAccessible(true);
  276.             }
  277.             blockProfileField.set(block, makeProfile(b64));
  278.         } catch (NoSuchFieldException | IllegalAccessException e) {
  279.             e.printStackTrace();
  280.         }
  281.     }
  282.  
  283.     private static void mutateItemMeta(SkullMeta meta, String b64) {
  284.         try {
  285.             if (metaSetProfileMethod == null) {
  286.                 metaSetProfileMethod = meta.getClass().getDeclaredMethod("setProfile", GameProfile.class);
  287.                 metaSetProfileMethod.setAccessible(true);
  288.             }
  289.             metaSetProfileMethod.invoke(meta, makeProfile(b64));
  290.         } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException ex) {
  291.             // if in an older API where there is no setProfile method,
  292.             // we set the profile field directly.
  293.             try {
  294.                 if (metaProfileField == null) {
  295.                     metaProfileField = meta.getClass().getDeclaredField("profile");
  296.                     metaProfileField.setAccessible(true);
  297.                 }
  298.                 metaProfileField.set(meta, makeProfile(b64));
  299.  
  300.             } catch (NoSuchFieldException | IllegalAccessException ex2) {
  301.                 ex2.printStackTrace();
  302.             }
  303.         }
  304.     }
  305.  
  306.     // suppress warning since PLAYER_HEAD doesn't exist in 1.12.2,
  307.     // but we expect this and catch the error at runtime.
  308.     private static void checkLegacy() {
  309.         try {
  310.             // if both of these succeed, then we are running
  311.             // in a legacy api, but on a modern (1.13+) server.
  312.             Material.class.getDeclaredField("PLAYER_HEAD");
  313.             Material.valueOf("SKULL");
  314.  
  315.             if (!warningPosted) {
  316.                 Bukkit.getLogger().warning("SKULLCREATOR API - Using the legacy bukkit API with 1.13+ bukkit versions is not supported!");
  317.                 warningPosted = true;
  318.             }
  319.         } catch (NoSuchFieldException | IllegalArgumentException ignored) {
  320.         }
  321.     }
  322.  
  323.     public static Material getMaterial(String name) {
  324.         try {
  325.             return Material.valueOf(name);
  326.         } catch (Exception e) {
  327.             if (name.equals("WORKBENCH"))
  328.                 return Material.valueOf("CRAFTING_TABLE");
  329.             try {
  330.                 return Material.matchMaterial("LEGACY_" + name);
  331.             } catch (Exception e2) {
  332.             }
  333.         }
  334.         return null;
  335.     }
  336.  
  337.     public static void SetOwningPlayer(SkullMeta meta, OfflinePlayer player) {
  338.         try {
  339.             meta.setOwningPlayer(player);
  340.         } catch (Exception e) {
  341.             meta.setOwner(player.getName());
  342.         }
  343.     }
  344.  
  345.  
  346. }
Add Comment
Please, Sign In to add comment