package net.minecraft.server; import com.google.common.collect.Iterables; import com.mojang.authlib.GameProfile; import com.mojang.authlib.minecraft.MinecraftSessionService; import com.mojang.authlib.properties.Property; import java.util.UUID; import javax.annotation.Nullable; import org.torch.server.cache.Caches; import com.github.benmanes.caffeine.cache.CacheLoader; import com.github.benmanes.caffeine.cache.Caffeine; import com.github.benmanes.caffeine.cache.LoadingCache; // Spigot start import com.google.common.base.Predicate; import java.util.concurrent.Executor; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import com.google.common.util.concurrent.ThreadFactoryBuilder; import com.mojang.authlib.Agent; import com.mojang.authlib.ProfileLookupCallback; // Spigot end public class TileEntitySkull extends TileEntity /*implements ITickable*/ { // Paper - remove tickable private int a; public int rotation; private GameProfile g; private int h; private boolean i; private static UserCache j; private static MinecraftSessionService k; // Spigot start public static final Executor executor = Executors.newFixedThreadPool(3, new ThreadFactoryBuilder() .setNameFormat("Head Conversion Thread - %1$d") .build() ); public static final LoadingCache<String, GameProfile> skinCache = Caffeine.newBuilder() .maximumSize( 5000 ) .expireAfterAccess( 60, TimeUnit.MINUTES ) .build( new CacheLoader<String, GameProfile>() { @Override public GameProfile load(String key) throws Exception { final GameProfile[] profiles = new GameProfile[1]; ProfileLookupCallback gameProfileLookup = new ProfileLookupCallback() { @Override public void onProfileLookupSucceeded(GameProfile gp) { profiles[0] = gp; } @Override public void onProfileLookupFailed(GameProfile gp, Exception excptn) { profiles[0] = gp; } }; MinecraftServer.getServer().getGameProfileRepository().findProfilesByNames(new String[] { key }, Agent.MINECRAFT, gameProfileLookup); GameProfile profile = profiles[ 0 ]; if (profile == null) { UUID uuid = EntityHuman.a(new GameProfile(null, key)); profile = new GameProfile(uuid, key); gameProfileLookup.onProfileLookupSucceeded(profile); } else { Property property = Iterables.getFirst( profile.getProperties().get( "textures" ), null ); if ( property == null ) { profile = MinecraftServer.getServer().az().fillProfileProperties( profile, true ); } } return profile; } } ); // Spigot end public TileEntitySkull() {} public static void a(UserCache usercache) { TileEntitySkull.j = usercache; } public static void a(MinecraftSessionService minecraftsessionservice) { TileEntitySkull.k = minecraftsessionservice; } @Override public NBTTagCompound save(NBTTagCompound nbttagcompound) { super.save(nbttagcompound); nbttagcompound.setByte("SkullType", (byte) (this.a & 255)); nbttagcompound.setByte("Rot", (byte) (this.rotation & 255)); if (this.g != null) { NBTTagCompound nbttagcompound1 = new NBTTagCompound(); GameProfileSerializer.serialize(nbttagcompound1, this.g); nbttagcompound.set("Owner", nbttagcompound1); } return nbttagcompound; } @Override public void a(NBTTagCompound nbttagcompound) { super.a(nbttagcompound); this.a = nbttagcompound.getByte("SkullType"); this.rotation = nbttagcompound.getByte("Rot"); if (this.a == 3) { if (nbttagcompound.hasKeyOfType("Owner", 10)) { this.g = GameProfileSerializer.deserialize(nbttagcompound.getCompound("Owner")); } else if (nbttagcompound.hasKeyOfType("ExtraType", 8)) { String s = nbttagcompound.getString("ExtraType"); if (!UtilColor.b(s)) { this.g = new GameProfile((UUID) null, s); this.h(); } } } } public void F_() { if (this.a == 5) { if (this.world.isBlockIndirectlyPowered(this.position)) { this.i = true; ++this.h; } else { this.i = false; } } } @Nullable public GameProfile getGameProfile() { return this.g; } @Override @Nullable public PacketPlayOutTileEntityData getUpdatePacket() { return new PacketPlayOutTileEntityData(this.position, 4, this.d()); } @Override public NBTTagCompound d() { return this.save(new NBTTagCompound()); } public void setSkullType(int i) { this.a = i; this.g = null; } public void setGameProfile(@Nullable GameProfile gameprofile) { this.a = 3; this.g = gameprofile; this.h(); } private void h() { // Spigot start GameProfile profile = this.g; setSkullType( 0 ); // Work around client bug b(profile, new Predicate<GameProfile>() { @Override public boolean apply(GameProfile input) { setSkullType(3); // Work around client bug g = input; update(); if (world != null) { world.m(position); // PAIL: notify } return false; } }); // Spigot end } // Spigot start - Support async lookups public static void b(final GameProfile gameprofile, final Predicate<GameProfile> callback) { if (gameprofile != null && !UtilColor.b(gameprofile.getName())) { if (gameprofile.isComplete() && gameprofile.getProperties().containsKey("textures")) { callback.apply(gameprofile); } else if (MinecraftServer.getServer() == null) { callback.apply(gameprofile); } else { GameProfile profile = skinCache.getIfPresent(Caches.toLowerCase(gameprofile.getName())); // Paper if (profile != null && Iterables.getFirst(profile.getProperties().get("textures"), (Object) null) != null) { callback.apply(profile); } else { executor.execute(new Runnable() { @Override public void run() { final GameProfile profile = skinCache.get(Caches.toLowerCase(gameprofile.getName())); MinecraftServer.getServer().processQueue.add(new Runnable() { @Override public void run() { if (profile == null) { callback.apply(gameprofile); } else { callback.apply(profile); } } }); } }); } } } else { callback.apply(gameprofile); } } // Spigot end public int getSkullType() { return this.a; } public void setRotation(int i) { this.rotation = i; } @Override public void a(EnumBlockMirror enumblockmirror) { if (this.world != null && this.world.getType(this.getPosition()).get(BlockSkull.FACING) == EnumDirection.UP) { this.rotation = enumblockmirror.a(this.rotation, 16); } } @Override public void a(EnumBlockRotation enumblockrotation) { if (this.world != null && this.world.getType(this.getPosition()).get(BlockSkull.FACING) == EnumDirection.UP) { this.rotation = enumblockrotation.a(this.rotation, 16); } } }