From 6eaf9a0a9503de652b7a3dfad975c891c09d74ca 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/ItemStack.java b/src/main/java/net/minecraft/server/ItemStack.java index 2723ccb..26f9fae 100644 --- a/src/main/java/net/minecraft/server/ItemStack.java +++ b/src/main/java/net/minecraft/server/ItemStack.java @@ -216,9 +216,61 @@ public final class ItemStack { if (nbttagcompound.hasKeyOfType("tag", 10)) { // CraftBukkit - make defensive copy as this data may be coming from the save thread this.tag = (NBTTagCompound) nbttagcompound.getCompound("tag").clone(); + validateSkullSkin(); // Spigot } } + // Spigot start - make sure the tag is given the full gameprofile if it's a skull (async lookup) + public void validateSkullSkin() + { + if ( this.item == Items.SKULL && this.getData() == 3 ) + { + String owner; + if ( this.tag.hasKeyOfType( "SkullOwner", 8 ) ) + { + owner = this.tag.getString( "SkullOwner" ); + } else if ( this.tag.hasKeyOfType( "SkullOwner", 10 ) ) + { + net.minecraft.util.com.mojang.authlib.GameProfile profile = GameProfileSerializer.deserialize( this.tag.getCompound( "SkullOwner" ) ); + if ( profile == null || !profile.getProperties().isEmpty() ) + { + return; + } else + { + owner = profile.getName(); + } + } else + { + return; + } + + final String finalOwner = owner; + TileEntitySkull.executor.execute( new Runnable() + { + @Override + public void run() + { + + final net.minecraft.util.com.mojang.authlib.GameProfile profile = TileEntitySkull.skinCache.getUnchecked( finalOwner.toLowerCase() ); + if ( profile != null ) + { + MinecraftServer.getServer().processQueue.add( new Runnable() + { + @Override + public void run() + { + NBTTagCompound nbtProfile = new NBTTagCompound(); + GameProfileSerializer.serialize( nbtProfile, profile ); + ItemStack.this.tag.set( "SkullOwner", nbtProfile ); + } + } ); + } + } + } ); + } + } + // Spigot end + public int getMaxStackSize() { return this.getItem().getMaxStackSize(); } @@ -457,6 +509,7 @@ public final class ItemStack { public void setTag(NBTTagCompound nbttagcompound) { this.tag = nbttagcompound; + validateSkullSkin(); // Spigot } public String getName() { diff --git a/src/main/java/net/minecraft/server/TileEntitySkull.java b/src/main/java/net/minecraft/server/TileEntitySkull.java index 2a50db9..c95e4c8 100644 --- a/src/main/java/net/minecraft/server/TileEntitySkull.java +++ b/src/main/java/net/minecraft/server/TileEntitySkull.java @@ -6,11 +6,61 @@ import net.minecraft.util.com.google.common.collect.Iterables; import net.minecraft.util.com.mojang.authlib.GameProfile; import net.minecraft.util.com.mojang.authlib.properties.Property; +// Spigot start +import com.google.common.cache.Cache; +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 net.minecraft.util.com.mojang.authlib.Agent; +// Spigot end + public class TileEntitySkull extends TileEntity { private int a; private int i; private GameProfile j = null; + // Spigot start + public static final Executor executor = Executors.newFixedThreadPool(3, + new ThreadFactoryBuilder() + .setNameFormat("Head Conversion Thread - %1$d") + .build() + ); + public static final Cache skinCache = CacheBuilder.newBuilder() + .maximumSize( 5000 ) + .expireAfterAccess( 60, TimeUnit.MINUTES ) + .build( new CacheLoader() + { + @Override + public GameProfile load(String key) throws Exception + { + GameProfile[] profiles = new GameProfile[1]; + GameProfileLookup gameProfileLookup = new GameProfileLookup(profiles); + + MinecraftServer.getServer().getGameProfileRepository().findProfilesByNames(new String[] { key }, Agent.MINECRAFT, gameProfileLookup); + if (!MinecraftServer.getServer().getOnlineMode() && profiles[0] == null) { + UUID uuid = EntityHuman.a(new GameProfile(null, key)); + GameProfile profile = new GameProfile(uuid, key); + + gameProfileLookup.onProfileLookupSucceeded(profile); + } + + GameProfile profile = profiles[0]; + + Property property = Iterables.getFirst(profile.getProperties().get("textures"), null); + + if (property == null) { + profile = MinecraftServer.getServer().av().fillProfileProperties(profile, true); + } + + + return profile; + } + } ); + // Spigot end public TileEntitySkull() {} @@ -66,18 +116,38 @@ public class TileEntitySkull extends TileEntity { private void d() { if (this.j != null && !UtilColor.b(this.j.getName())) { if (!this.j.isComplete() || !this.j.getProperties().containsKey("textures")) { - GameProfile gameprofile = MinecraftServer.getServer().getUserCache().getProfile(this.j.getName()); - - if (gameprofile != null) { - Property property = (Property) Iterables.getFirst(gameprofile.getProperties().get("textures"), null); - - if (property == null) { - gameprofile = MinecraftServer.getServer().av().fillProfileProperties(gameprofile, true); + // Spigot start - Handle async + final String name = this.j.getName(); + setSkullType( 0 ); // Work around a client bug + executor.execute(new Runnable() { + @Override + public void run() { + + GameProfile profile = skinCache.getUnchecked( name.toLowerCase() ); + + if (profile != null) { + final GameProfile finalProfile = profile; + MinecraftServer.getServer().processQueue.add(new Runnable() { + @Override + public void run() { + a = 3; + j = finalProfile; + world.notify( x, y, z ); + } + }); + } else { + MinecraftServer.getServer().processQueue.add(new Runnable() { + @Override + public void run() { + a = 3; + j = new GameProfile( null, name ); + world.notify( x, y, z ); + } + }); + } } - - this.j = gameprofile; - this.update(); - } + }); + // Spigot end } } } diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaSkull.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaSkull.java index d648d05..e32bcb1 100644 --- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaSkull.java +++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaSkull.java @@ -46,13 +46,39 @@ class CraftMetaSkull extends CraftMetaItem implements SkullMeta { } @Override - void applyToItem(NBTTagCompound tag) { + void applyToItem(final NBTTagCompound tag) { // Spigot - make final super.applyToItem(tag); if (hasOwner()) { 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.executor.execute( new Runnable() + { + @Override + public void run() + { + + final GameProfile profile = net.minecraft.server.TileEntitySkull.skinCache.getUnchecked( CraftMetaSkull.this.profile.getName().toLowerCase() ); + if ( profile != null ) + { + MinecraftServer.getServer().processQueue.add( new Runnable() + { + @Override + public void run() + { + NBTTagCompound owner = new NBTTagCompound(); + GameProfileSerializer.serialize( owner, profile ); + tag.set( SKULL_OWNER.NBT, owner ); + } + } ); + } + } + } ); + // Spigot end } } -- 1.9.1