From d70a9db30806a6aaf6f78311b2e1e29f8e11ecd1 Mon Sep 17 00:00:00 2001 From: Thinkofdeath Date: Sun, 20 Apr 2014 13:18:55 +0100 Subject: [PATCH] Convert player skulls async diff --git a/src/main/java/net/minecraft/server/ItemSkull.java b/src/main/java/net/minecraft/server/ItemSkull.java index 40338ae..2a8c5f0 100644 --- a/src/main/java/net/minecraft/server/ItemSkull.java +++ b/src/main/java/net/minecraft/server/ItemSkull.java @@ -108,13 +108,21 @@ public class ItemSkull extends Item { return super.a(itemstack); } - public boolean a(NBTTagCompound nbttagcompound) { + public boolean a(final NBTTagCompound nbttagcompound) { // Spigot - make final super.a(nbttagcompound); if (nbttagcompound.hasKeyOfType("SkullOwner", 8) && !nbttagcompound.getString("SkullOwner").isEmpty()) { GameProfile gameprofile = new GameProfile((UUID) null, nbttagcompound.getString("SkullOwner")); - gameprofile = TileEntitySkull.b(gameprofile); - nbttagcompound.set("SkullOwner", GameProfileSerializer.serialize(new NBTTagCompound(), gameprofile)); + // Spigot start + TileEntitySkull.b(gameprofile, new com.google.common.base.Predicate() { + + @Override + public boolean apply(GameProfile gameprofile) { + nbttagcompound.set("SkullOwner", GameProfileSerializer.serialize(new NBTTagCompound(), gameprofile)); + return false; + } + }); + // Spigot end return true; } else { return false; diff --git a/src/main/java/net/minecraft/server/TileEntitySkull.java b/src/main/java/net/minecraft/server/TileEntitySkull.java index 244a424..7eecc6c 100644 --- a/src/main/java/net/minecraft/server/TileEntitySkull.java +++ b/src/main/java/net/minecraft/server/TileEntitySkull.java @@ -6,6 +6,20 @@ import com.mojang.authlib.minecraft.MinecraftSessionService; import com.mojang.authlib.properties.Property; import java.util.UUID; +// Spigot start +import com.google.common.base.Predicate; +import com.google.common.cache.LoadingCache; +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +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 { private int a; @@ -15,6 +29,58 @@ public class TileEntitySkull extends TileEntity implements ITickable { 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 skinCache = CacheBuilder.newBuilder() + .maximumSize( 5000 ) + .expireAfterAccess( 60, TimeUnit.MINUTES ) + .build( new CacheLoader() + { + @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().ay().fillProfileProperties( profile, true ); + } + } + + + return profile; + } + } ); + // Spigot end public TileEntitySkull() {} @@ -93,35 +159,60 @@ public class TileEntitySkull extends TileEntity implements ITickable { } private void g() { - this.g = b(this.g); - this.update(); + // Spigot start + GameProfile profile = this.g; + setSkullType( 0 ); // Work around client bug + b(profile, new Predicate() { + + @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 } - public static GameProfile b(GameProfile gameprofile) { + // Spigot start - Support async lookups + public static void b(final GameProfile gameprofile, final Predicate callback) { if (gameprofile != null && !UtilColor.b(gameprofile.getName())) { if (gameprofile.isComplete() && gameprofile.getProperties().containsKey("textures")) { - return gameprofile; - } else if (TileEntitySkull.j != null && TileEntitySkull.k != null) { - GameProfile gameprofile1 = TileEntitySkull.j.getProfile(gameprofile.getName()); - - if (gameprofile1 == null) { - return gameprofile; + callback.apply(gameprofile); + } else if (MinecraftServer.getServer() == null) { + callback.apply(gameprofile); + } else { + GameProfile profile = skinCache.getIfPresent(gameprofile.getName()); + if (profile != null && Iterables.getFirst(profile.getProperties().get("textures"), (Object) null) != null) { + callback.apply(profile); } else { - Property property = (Property) Iterables.getFirst(gameprofile1.getProperties().get("textures"), (Object) null); - - if (property == null) { - gameprofile1 = TileEntitySkull.k.fillProfileProperties(gameprofile1, true); - } - - return gameprofile1; + executor.execute(new Runnable() { + @Override + public void run() { + final GameProfile profile = skinCache.getUnchecked(gameprofile.getName().toLowerCase()); + MinecraftServer.getServer().processQueue.add(new Runnable() { + @Override + public void run() { + if (profile == null) { + callback.apply(gameprofile); + } else { + callback.apply(profile); + } + } + }); + } + }); } - } else { - return gameprofile; } } else { - return gameprofile; + callback.apply(gameprofile); } } + // Spigot end public int getSkullType() { return this.a; diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaSkull.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaSkull.java index 8a11b87..2abca0c 100644 --- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaSkull.java +++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaSkull.java @@ -68,13 +68,27 @@ class CraftMetaSkull extends CraftMetaItem implements SkullMeta { } @Override - void applyToItem(NBTTagCompound tag) { + 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); + 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() { + + @Override + public boolean apply(GameProfile input) { + NBTTagCompound owner = new NBTTagCompound(); + GameProfileSerializer.serialize( owner, input ); + tag.set( SKULL_OWNER.NBT, owner ); + return false; + } + }); + // Spigot end } } -- 2.5.0