/******************************************************************************* * This file is part of ASkyBlock. * * ASkyBlock is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * ASkyBlock is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with ASkyBlock. If not, see <http://www.gnu.org/licenses/>. *******************************************************************************/ package com.wasteofplastic.askyblock.schematics; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.util.HashMap; import java.util.Map; import java.util.Random; import java.util.UUID; import org.bukkit.Material; import org.bukkit.SkullType; import org.bukkit.block.Block; import org.bukkit.block.BlockFace; import org.bukkit.block.Skull; import com.mojang.authlib.GameProfile; import com.mojang.authlib.properties.Property; import com.wasteofplastic.org.jnbt.ByteTag; import com.wasteofplastic.org.jnbt.CompoundTag; import com.wasteofplastic.org.jnbt.IntTag; import com.wasteofplastic.org.jnbt.ListTag; import com.wasteofplastic.org.jnbt.StringTag; import com.wasteofplastic.org.jnbt.Tag; /** * This class describes skulls and is used in schematic importing * * @author SpyL1nk * */ public class SkullBlock { private static final Random random = new Random(); private static final String chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; private SkullType skullType; private String skullOwnerName; private String skullOwnerUUID; private BlockFace skullRotation; private int skullStanding; private String skullTextureValue = null; private String skullTextureSignature = null; private static HashMap<Integer, SkullType> skullTypeList; private static HashMap<Integer, BlockFace> skullRotationList; static { skullTypeList = new HashMap<Integer, SkullType>(); skullTypeList.put(0, SkullType.SKELETON); skullTypeList.put(1, SkullType.WITHER); skullTypeList.put(2, SkullType.ZOMBIE); skullTypeList.put(3, SkullType.PLAYER); skullTypeList.put(4, SkullType.CREEPER); } static { skullRotationList = new HashMap<Integer, BlockFace>(); skullRotationList.put(0, BlockFace.NORTH); skullRotationList.put(1, BlockFace.NORTH_NORTH_EAST); skullRotationList.put(2, BlockFace.NORTH_EAST); skullRotationList.put(3, BlockFace.EAST_NORTH_EAST); skullRotationList.put(4, BlockFace.EAST); skullRotationList.put(5, BlockFace.EAST_SOUTH_EAST); skullRotationList.put(6, BlockFace.SOUTH_EAST); skullRotationList.put(7, BlockFace.SOUTH_SOUTH_EAST); skullRotationList.put(8, BlockFace.SOUTH); skullRotationList.put(9, BlockFace.SOUTH_SOUTH_WEST); skullRotationList.put(10, BlockFace.SOUTH_WEST); skullRotationList.put(11, BlockFace.WEST_SOUTH_WEST); skullRotationList.put(12, BlockFace.WEST); skullRotationList.put(13, BlockFace.WEST_NORTH_WEST); skullRotationList.put(14, BlockFace.NORTH_WEST); skullRotationList.put(15, BlockFace.NORTH_NORTH_WEST); } @SuppressWarnings("deprecation") public boolean set(Block block) { Skull skull = (Skull) block.getState(); if(skullOwnerName != null){ skull.setOwner(skullOwnerName); } skull.setSkullType(skullType); skull.setRotation(skullRotation); skull.setRawData((byte) skullStanding); // Texture update if(skullTextureValue != null){ setSkullWithNonPlayerProfile(skullTextureValue, skullTextureSignature, skullOwnerUUID, skullOwnerName, skull); } skull.update(); return true; } public boolean prep(Map<String, Tag> tileData, int dataValue) { try { // Take skull type if(tileData.containsKey("SkullType")){ int skullTypeId = (int) ((ByteTag) tileData.get("SkullType")).getValue(); //Bukkit.getLogger().info("DEBUG: skull type = " + skullTypeId); if(skullTypeList.containsKey(skullTypeId)){ skullType = skullTypeList.get(skullTypeId); } else { // Prevent hacks, set to default skull type skullType = skullTypeList.get(0); } } else{ // Prevent hacks, set to defaut skull type skullType = skullTypeList.get(0); } //Bukkit.getLogger().info("DEBUG: skull's data value = " + dataValue); // Data value 0 is actually unused for skulls, set to 2 to prevent hacks if(dataValue > 0 && dataValue < 6){ skullStanding = dataValue; if(tileData.containsKey("Rot")){ int skullRotId = (int) ((ByteTag) tileData.get("Rot")).getValue(); //Bukkit.getLogger().info("DEBUG: skull's rotation byte = " + skullRotId); // Useful for skulls on the floor to insert rotation data if(skullRotationList.containsKey(skullStanding)){ skullRotation = skullRotationList.get(skullRotId); } else{ // Prevents hacks skullRotation = skullRotationList.get(0); } } else{ skullRotation = skullRotationList.get(0); } } else{ skullStanding = 2; if(tileData.containsKey("Rot")){ int skullRotId = ((IntTag) tileData.get("Rot")).getValue(); //Bukkit.getLogger().info("DEBUG: skull's rotation byte = " + skullRotId); // Useful for skulls on the floor to insert rotation data if(skullRotationList.containsKey(skullStanding)){ skullRotation = skullRotationList.get(skullRotId); } // Prevents hacks else{ skullRotation = skullRotationList.get(0); } } else{ skullRotation = skullRotationList.get(0); } } // Check for Player Heads (skin, texture etc.) if(skullType == SkullType.PLAYER && tileData.containsKey("Owner")){ Map<String, Tag> skullOwner = ((CompoundTag) tileData.get("Owner")).getValue(); if(skullOwner.containsKey("Name")){ skullOwnerName = ((StringTag) skullOwner.get("Name")).getValue(); //Bukkit.getLogger().info("DEBUG: skull owner's name = " + skullOwnerName); } if(skullOwner.containsKey("Id")){ skullOwnerUUID = ((StringTag) skullOwner.get("Id")).getValue(); //Bukkit.getLogger().info("DEBUG: skull owner's UUID = " + skullOwnerUUID); } if(skullOwner.containsKey("Properties")){ Map<String, Tag> skullOwnerProperties = ((CompoundTag) skullOwner.get("Properties")).getValue(); if(skullOwnerProperties.containsKey("textures")){ ListTag listTagTextures = (ListTag) skullOwnerProperties.get("textures"); //Bukkit.getLogger().info("DEBUG: skull texture's list = " + listTagTextures); if(listTagTextures != null){ // Logicaly, textures should have only one entry ... Map<String, Tag> skullOwnerTextures = ((CompoundTag) listTagTextures.getValue().get(0)).getValue(); if(skullOwnerTextures.containsKey("Value")){ skullTextureValue = ((StringTag) skullOwnerTextures.get("Value")).getValue(); //Bukkit.getLogger().info("DEBUG: skull texture's value = " + skullTextureValue); } if(skullOwnerTextures.containsKey("Signature")){ skullTextureSignature = ((StringTag) skullOwnerTextures.get("Signature")).getValue(); //Bukkit.getLogger().info("DEBUG: skull's texture signature = " + skullTextureSignature); } } } } } } catch (Exception e) { e.printStackTrace(); } return true; } // Credits: GermanCoding @SuppressWarnings("deprecation") public static void setSkullWithNonPlayerProfile(String textureValue, String textureSignature, String ownerUUID, String ownerName, Skull skull) { if (skull.getType() != Material.SKULL) throw new IllegalArgumentException("Block must be a skull."); skull.getWorld().refreshChunk(skull.getChunk().getX(), skull.getChunk().getZ()); // Difference beetween NonPlayerSkin and PlayerSkin if(textureSignature != null){ try { setSkullProfile(skull, getPlayerProfile(textureValue, textureSignature, ownerUUID, ownerName)); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } catch (SecurityException e) { e.printStackTrace(); } } else { try { setSkullProfile(skull, getNonPlayerProfile(textureValue, ownerUUID, ownerName)); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } catch (SecurityException e) { e.printStackTrace(); } } //skull.getWorld().refreshChunk(skull.getChunk().getX(), skull.getChunk().getZ()); } // Credits: val59000 (THANK YOU VERY MUCH VAL59000 !) private static void setSkullProfile(Skull skull, GameProfile gameProfile) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException { Field profileField = null; try { profileField = skull.getClass().getDeclaredField("profile"); profileField.setAccessible(true); profileField.set(skull, gameProfile); } catch (NoSuchFieldException | IllegalArgumentException | IllegalAccessException e) { e.printStackTrace(); } } // Credits: dori99xd public static GameProfile getNonPlayerProfile(String textureValue, String ownerUUID, String ownerName) { // Create a new GameProfile with .schematic informations or with fake informations GameProfile newSkinProfile = new GameProfile(ownerUUID == null ? UUID.randomUUID() : UUID.fromString(ownerUUID), ownerName == null ? getRandomString(16) : null); // Insert textures properties newSkinProfile.getProperties().put("textures", new Property("textures", textureValue)); return newSkinProfile; } // Credits: dori99xd public static GameProfile getPlayerProfile(String textureValue, String textureSignature, String ownerUUID, String ownerName) { // Create a new GameProfile with .schematic informations or with fake informations GameProfile newSkinProfile = new GameProfile( ownerUUID == null ? UUID.randomUUID() : UUID.fromString(ownerUUID), ownerName == null ? getRandomString(16) : null); // Insert textures properties newSkinProfile.getProperties().put("textures", new Property("textures", textureValue, textureSignature)); return newSkinProfile; } // Credits: dori99xd public static String getRandomString(int length) { StringBuilder b = new StringBuilder(length); for(int j = 0; j < length; j++){ b.append(chars.charAt(random.nextInt(chars.length()))); } return b.toString(); } }