package org.bukkit.craftbukkit.inventory; import java.util.Map; import net.minecraft.server.GameProfileSerializer; import net.minecraft.server.NBTBase; import net.minecraft.server.NBTTagCompound; import net.minecraft.server.TileEntitySkull; import net.minecraft.server.*; import org.bukkit.Material; import org.bukkit.configuration.serialization.DelegateDeserialization; import org.bukkit.craftbukkit.inventory.CraftMetaItem.SerializableMeta; import org.bukkit.craftbukkit.util.CraftMagicNumbers; import org.bukkit.inventory.meta.SkullMeta; import com.google.common.collect.ImmutableMap.Builder; import com.mojang.authlib.GameProfile; @DelegateDeserialization(SerializableMeta.class) class CraftMetaSkull extends CraftMetaItem implements SkullMeta { @ItemMetaKey.Specific(ItemMetaKey.Specific.To.NBT) static final ItemMetaKey SKULL_PROFILE = new ItemMetaKey("SkullProfile"); static final ItemMetaKey SKULL_OWNER = new ItemMetaKey("SkullOwner", "skull-owner"); static final int MAX_OWNER_LENGTH = 16; private GameProfile profile; CraftMetaSkull(CraftMetaItem meta) { super(meta); if (!(meta instanceof CraftMetaSkull)) { return; } CraftMetaSkull skullMeta = (CraftMetaSkull) meta; this.profile = skullMeta.profile; } CraftMetaSkull(NBTTagCompound tag) { super(tag); if (tag.hasKeyOfType(SKULL_OWNER.NBT, CraftMagicNumbers.NBT.TAG_COMPOUND)) { profile = GameProfileSerializer.deserialize(tag.getCompound(SKULL_OWNER.NBT)); } else if (tag.hasKeyOfType(SKULL_OWNER.NBT, CraftMagicNumbers.NBT.TAG_STRING) && !tag.getString(SKULL_OWNER.NBT).isEmpty()) { profile = new GameProfile(null, tag.getString(SKULL_OWNER.NBT)); } } CraftMetaSkull(Map<String, Object> map) { super(map); if (profile == null) { setOwner(SerializableMeta.getString(map, SKULL_OWNER.BUKKIT, true)); } } @Override void deserializeInternal(NBTTagCompound tag) { if (tag.hasKeyOfType(SKULL_PROFILE.NBT, CraftMagicNumbers.NBT.TAG_COMPOUND)) { profile = GameProfileSerializer.deserialize(tag.getCompound(SKULL_PROFILE.NBT)); } } @Override void serializeInternal(final Map<String, NBTBase> internalTags) { if (profile != null) { NBTTagCompound nbtData = new NBTTagCompound(); GameProfileSerializer.serialize(nbtData, profile); internalTags.put(SKULL_PROFILE.NBT, nbtData); } } @Override void applyToItem(final NBTTagCompound tag) { // Spigot - make final super.applyToItem(tag); if (profile != null) { NBTTagCompound owner = new NBTTagCompound(); GameProfileSerializer.serialize(owner, profile); tag.set( SKULL_OWNER.NBT, owner ); // Spigot start - do an async lookup of the profile. // Unfortunately there is not way to refresh the holding // inventory, so that responsibility is left to the user. net.minecraft.server.TileEntitySkull.b(profile, new com.google.common.base.Predicate<GameProfile>() { @Override public boolean apply(GameProfile input) { NBTTagCompound owner = new NBTTagCompound(); GameProfileSerializer.serialize( owner, input ); tag.set( SKULL_OWNER.NBT, owner ); return false; } }); // Spigot end } } @Override boolean isEmpty() { return super.isEmpty() && isSkullEmpty(); } boolean isSkullEmpty() { return profile == null; } @Override boolean applicableTo(Material type) { switch(type) { case SKULL_ITEM: return true; default: return false; } } @Override public CraftMetaSkull clone() { return (CraftMetaSkull) super.clone(); } @Override public boolean hasOwner() { return profile != null && profile.getName() != null; } @Override public String getOwner() { return hasOwner() ? profile.getName() : null; } @Override public boolean setOwner(String name) { if (name != null && name.length() > MAX_OWNER_LENGTH) { return false; } if (name == null) { profile = null; } else { // Paper start - Use Online Players Skull EntityPlayer player = MinecraftServer.getServer().getPlayerList().getPlayer(name); if (profile == null && player != null) profile = player.getProfile(); // Paper end // Spigot start if (profile == null) profile = TileEntitySkull.skinCache.getIfPresent(name.toLowerCase(java.util.Locale.ROOT)); // Paper if (profile == null) profile = new GameProfile(null, name); // Spigot end } return true; } @Override int applyHash() { final int original; int hash = original = super.applyHash(); if (hasOwner()) { hash = 61 * hash + profile.hashCode(); } return original != hash ? CraftMetaSkull.class.hashCode() ^ hash : hash; } @Override boolean equalsCommon(CraftMetaItem meta) { if (!super.equalsCommon(meta)) { return false; } if (meta instanceof CraftMetaSkull) { CraftMetaSkull that = (CraftMetaSkull) meta; return (this.hasOwner() ? that.hasOwner() && this.profile.equals(that.profile) : !that.hasOwner()); } return true; } @Override boolean notUncommon(CraftMetaItem meta) { return super.notUncommon(meta) && (meta instanceof CraftMetaSkull || isSkullEmpty()); } @Override Builder<String, Object> serialize(Builder<String, Object> builder) { super.serialize(builder); if (hasOwner()) { return builder.put(SKULL_OWNER.BUKKIT, this.profile.getName()); } return builder; } }