PaperMC/CraftBukkit-Patches/0110-Convert-player-skulls-async.patch

233 lines
9.6 KiB
Diff

From 0204f55807902b26e73cbeacf9cc7b35e663feac Mon Sep 17 00:00:00 2001
From: Thinkofdeath <thethinkofdeath@gmail.com>
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 a46cfef..e710073 100644
--- a/src/main/java/net/minecraft/server/ItemSkull.java
+++ b/src/main/java/net/minecraft/server/ItemSkull.java
@@ -109,13 +109,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").length() > 0) {
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<GameProfile>() {
+
+ @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 0048b65..58014c5 100644
--- a/src/main/java/net/minecraft/server/TileEntitySkull.java
+++ b/src/main/java/net/minecraft/server/TileEntitySkull.java
@@ -5,11 +5,78 @@ import com.mojang.authlib.GameProfile;
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 {
private int a;
private int rotation;
private GameProfile g = null;
+ // 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 = CacheBuilder.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().aD().fillProfileProperties( profile, true );
+ }
+ }
+
+
+ return profile;
+ }
+ } );
+
+ // Spigot end
public TileEntitySkull() {}
@@ -68,35 +135,60 @@ public class TileEntitySkull extends TileEntity {
}
private void e() {
- this.g = b(this.g);
- this.update();
+ // 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.notify(position);
+ }
+ return false;
+ }
+ });
+ // Spigot end
}
- public static GameProfile b(GameProfile gameprofile) {
+ // 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")) {
- return gameprofile;
+ callback.apply(gameprofile);
} else if (MinecraftServer.getServer() == null) {
- return gameprofile;
+ callback.apply(gameprofile);
} else {
- GameProfile gameprofile1 = MinecraftServer.getServer().getUserCache().getProfile(gameprofile.getName());
-
- if (gameprofile1 == null) {
- return gameprofile;
+ 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 = MinecraftServer.getServer().aD().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;
+ 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 ead8a98..ce5425f 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 (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.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
}
}
--
2.1.4