From 91f92dfcdf9c6d9f9de49a16ab9d0456ac680667 Mon Sep 17 00:00:00 2001
From: Aikar <aikar@aikar.co>
Date: Tue, 22 Nov 2016 00:40:42 -0500
Subject: [PATCH] Fix client rendering skulls from same user

See: https://github.com/PaperMC/Paper/issues/1304

Changes the UUID sent to client to be based on either
the texture payload, or random.

This allows the client to render multiple skull textures from the same user,
for when different skins were used when skull was made.

diff --git a/src/main/java/net/minecraft/server/ItemStack.java b/src/main/java/net/minecraft/server/ItemStack.java
index 2acd02f2f4..ca1bd02995 100644
--- a/src/main/java/net/minecraft/server/ItemStack.java
+++ b/src/main/java/net/minecraft/server/ItemStack.java
@@ -54,7 +54,7 @@ public final class ItemStack {
     // Paper end
     @Deprecated
     private Item item;
-    private NBTTagCompound tag;
+    NBTTagCompound tag; // Paper -> package private
     private boolean h;
     private EntityItemFrame i;
     private ShapeDetectorBlock j;
diff --git a/src/main/java/net/minecraft/server/PacketDataSerializer.java b/src/main/java/net/minecraft/server/PacketDataSerializer.java
index 0d67676f7d..fa2d3ce8cb 100644
--- a/src/main/java/net/minecraft/server/PacketDataSerializer.java
+++ b/src/main/java/net/minecraft/server/PacketDataSerializer.java
@@ -253,6 +253,15 @@ public class PacketDataSerializer extends ByteBuf {
                 CraftItemStack.setItemMeta(itemstack, CraftItemStack.getItemMeta(itemstack));
                 // Spigot end
                 nbttagcompound = itemstack.getTag();
+                // Paper start
+                if (nbttagcompound != null && nbttagcompound.hasKeyOfType("SkullOwner", 10)) {
+                    NBTTagCompound owner = nbttagcompound.getCompound("SkullOwner");
+                    if (owner.hasKey("Id")) {
+                        nbttagcompound.setString("SkullOwnerOrig", owner.getString("Id"));
+                        TileEntitySkull.sanitizeUUID(owner);
+                    }
+                }
+                // Paper end
             }
 
             this.a(nbttagcompound);
@@ -272,6 +281,16 @@ public class PacketDataSerializer extends ByteBuf {
             itemstack.setTag(this.l());
             // CraftBukkit start
             if (itemstack.getTag() != null) {
+                // Paper start - Fix skulls of same owner - restore orig ID since we changed it on send to client
+                if (itemstack.tag.hasKey("SkullOwnerOrig")) {
+                    NBTTagCompound owner = itemstack.tag.getCompound("SkullOwner");
+                    String ownerOrig = itemstack.tag.getString("SkullOwnerOrig");
+                    if (!owner.isEmpty() && !ownerOrig.isEmpty()) {
+                        owner.setString("Id", ownerOrig);
+                    }
+                    itemstack.tag.remove("SkullOwnerOrig");
+                }
+                // Paper end
                 CraftItemStack.setItemMeta(itemstack, CraftItemStack.getItemMeta(itemstack));
             }
             // CraftBukkit end
diff --git a/src/main/java/net/minecraft/server/PacketPlayOutMapChunk.java b/src/main/java/net/minecraft/server/PacketPlayOutMapChunk.java
index 363ab5da12..d19a30ad87 100644
--- a/src/main/java/net/minecraft/server/PacketPlayOutMapChunk.java
+++ b/src/main/java/net/minecraft/server/PacketPlayOutMapChunk.java
@@ -51,6 +51,7 @@ public class PacketPlayOutMapChunk implements Packet<PacketListenerPlayOut> {
 
             if (this.f() || (i & 1 << j) != 0) {
                 NBTTagCompound nbttagcompound = tileentity.b();
+                if (tileentity instanceof TileEntitySkull) { TileEntitySkull.sanitizeTileEntityUUID(nbttagcompound); } // Paper
 
                 this.f.add(nbttagcompound);
             }
diff --git a/src/main/java/net/minecraft/server/TileEntitySkull.java b/src/main/java/net/minecraft/server/TileEntitySkull.java
index 177cceb77f..0882d82cef 100644
--- a/src/main/java/net/minecraft/server/TileEntitySkull.java
+++ b/src/main/java/net/minecraft/server/TileEntitySkull.java
@@ -142,9 +142,37 @@ public class TileEntitySkull extends TileEntity /*implements ITickable*/ { // Pa
     @Nullable
     @Override
     public PacketPlayOutTileEntityData getUpdatePacket() {
-        return new PacketPlayOutTileEntityData(this.position, 4, this.b());
+        return new PacketPlayOutTileEntityData(this.position, 4, sanitizeTileEntityUUID(this.b())); // Paper
     }
 
+    // Paper start
+    static NBTTagCompound sanitizeTileEntityUUID(NBTTagCompound cmp) {
+        NBTTagCompound owner = cmp.getCompound("Owner");
+        if (!owner.isEmpty()) {
+            sanitizeUUID(owner);
+        }
+        return cmp;
+    }
+
+    static void sanitizeUUID(NBTTagCompound owner) {
+        NBTTagCompound properties = owner.getCompound("Properties");
+        NBTTagList list = null;
+        if (!properties.isEmpty()) {
+            list = properties.getList("textures", 10);
+        }
+
+        if (list != null && !list.isEmpty()) {
+            String textures = ((NBTTagCompound)list.get(0)).getString("Value");
+            if (textures != null && textures.length() > 3) {
+                String uuid = UUID.nameUUIDFromBytes(textures.getBytes()).toString();
+                owner.setString("Id", uuid);
+                return;
+            }
+        }
+        owner.setString("Id", UUID.randomUUID().toString());
+    }
+    // Paper end
+
     @Override
     public NBTTagCompound b() {
         return this.save(new NBTTagCompound());
-- 
2.21.0

