diff --git a/CraftBukkit-Patches/0129-Convert-player-heads-async.patch b/CraftBukkit-Patches/0129-Convert-player-heads-async.patch new file mode 100644 index 0000000000..ef2c373428 --- /dev/null +++ b/CraftBukkit-Patches/0129-Convert-player-heads-async.patch @@ -0,0 +1,261 @@ +From 127178fe81c99ba39c9bf3fb1a8d89c192bd1b41 Mon Sep 17 00:00:00 2001 +From: Thinkofdeath +Date: Wed, 9 Apr 2014 13:29:57 +0100 +Subject: [PATCH] Convert player heads async + + +diff --git a/src/main/java/net/minecraft/server/Chunk.java b/src/main/java/net/minecraft/server/Chunk.java +index 90f32ed..6c2e905 100644 +--- a/src/main/java/net/minecraft/server/Chunk.java ++++ b/src/main/java/net/minecraft/server/Chunk.java +@@ -513,6 +513,13 @@ public class Chunk { + if (tileentity != null) { + tileentity.u(); + } ++ ++ // Spigot start ++ if ( tileentity instanceof TileEntitySkull ) ++ { ++ org.spigotmc.HeadConverter.convertHead( (TileEntitySkull) tileentity ); ++ } ++ // Spigot end + } + + this.n = true; +diff --git a/src/main/java/net/minecraft/server/ChunkRegionLoader.java b/src/main/java/net/minecraft/server/ChunkRegionLoader.java +index fb262bc..4c086d3 100644 +--- a/src/main/java/net/minecraft/server/ChunkRegionLoader.java ++++ b/src/main/java/net/minecraft/server/ChunkRegionLoader.java +@@ -378,6 +378,12 @@ public class ChunkRegionLoader implements IChunkLoader, IAsyncChunkSaver { + TileEntity tileentity = TileEntity.c(nbttagcompound4); + + if (tileentity != null) { ++ // Spigot start ++ if ( tileentity instanceof TileEntitySkull ) ++ { ++ org.spigotmc.HeadConverter.convertHead( (TileEntitySkull) tileentity ); ++ } ++ // Spigot end + chunk.a(tileentity); + } + } +diff --git a/src/main/java/net/minecraft/server/ItemSkull.java b/src/main/java/net/minecraft/server/ItemSkull.java +index 6b2bf9b..5a74142 100644 +--- a/src/main/java/net/minecraft/server/ItemSkull.java ++++ b/src/main/java/net/minecraft/server/ItemSkull.java +@@ -68,6 +68,7 @@ public class ItemSkull extends Item { + ((TileEntitySkull) tileentity).setSkullType(itemstack.getData(), s); + ((TileEntitySkull) tileentity).setRotation(i1); + ((BlockSkull) Blocks.SKULL).a(world, i, j, k, (TileEntitySkull) tileentity); ++ org.spigotmc.HeadConverter.convertHead( (TileEntitySkull) tileentity ); // Spigot + } + + --itemstack.count; +diff --git a/src/main/java/net/minecraft/server/PacketPlayOutTileEntityData.java b/src/main/java/net/minecraft/server/PacketPlayOutTileEntityData.java +index 005f1fe..4780c53 100644 +--- a/src/main/java/net/minecraft/server/PacketPlayOutTileEntityData.java ++++ b/src/main/java/net/minecraft/server/PacketPlayOutTileEntityData.java +@@ -1,5 +1,7 @@ + package net.minecraft.server; + ++import net.minecraft.util.com.mojang.authlib.GameProfile; ++ + public class PacketPlayOutTileEntityData extends Packet { + + private int a; +@@ -41,12 +43,14 @@ public class PacketPlayOutTileEntityData extends Packet { + packetdataserializer.writeShort(this.b); + packetdataserializer.writeInt(this.c); + packetdataserializer.writeByte((byte) this.d); +- if ( this.e.hasKey( "ExtraType" ) ) ++ if ( this.e.hasKey( "ExtraType" ) && !this.e.hasKey( "Owner" ) ) + { + NBTTagCompound profile = new NBTTagCompound(); + profile.setString( "Name", this.e.getString( "ExtraType" ) ); + profile.setString( "Id", "" ); + this.e.set( "Owner", profile ); ++ } else { ++ this.e.remove( "ExtraType" ); + } + packetdataserializer.a(this.e); + } +diff --git a/src/main/java/net/minecraft/server/TileEntitySkull.java b/src/main/java/net/minecraft/server/TileEntitySkull.java +index b241cfe..925e017 100644 +--- a/src/main/java/net/minecraft/server/TileEntitySkull.java ++++ b/src/main/java/net/minecraft/server/TileEntitySkull.java +@@ -5,6 +5,7 @@ public class TileEntitySkull extends TileEntity { + private int a; + private int i; + private String j = ""; ++ private NBTTagCompound owner = null; + + public TileEntitySkull() {} + +@@ -13,6 +14,7 @@ public class TileEntitySkull extends TileEntity { + nbttagcompound.setByte("SkullType", (byte) (this.a & 255)); + nbttagcompound.setByte("Rot", (byte) (this.i & 255)); + nbttagcompound.setString("ExtraType", this.j); ++ if ( owner != null ) nbttagcompound.set( "Owner", owner ); + } + + public void a(NBTTagCompound nbttagcompound) { +@@ -22,6 +24,10 @@ public class TileEntitySkull extends TileEntity { + if (nbttagcompound.hasKeyOfType("ExtraType", 8)) { + this.j = nbttagcompound.getString("ExtraType"); + } ++ if ( nbttagcompound.hasKey( "Owner" ) ) ++ { ++ owner = nbttagcompound.getCompound( "Owner" ); ++ } + } + + public Packet getUpdatePacket() { +diff --git a/src/main/java/org/spigotmc/HeadConverter.java b/src/main/java/org/spigotmc/HeadConverter.java +new file mode 100644 +index 0000000..0f465f0 +--- /dev/null ++++ b/src/main/java/org/spigotmc/HeadConverter.java +@@ -0,0 +1,140 @@ ++package org.spigotmc; ++ ++import com.google.common.base.Charsets; ++import com.google.common.util.concurrent.ThreadFactoryBuilder; ++import com.google.gson.JsonArray; ++import com.google.gson.JsonElement; ++import com.google.gson.JsonObject; ++import com.google.gson.JsonParser; ++import net.minecraft.server.EntityHuman; ++import net.minecraft.server.MinecraftServer; ++import net.minecraft.server.NBTTagCompound; ++import net.minecraft.server.NBTTagList; ++import net.minecraft.server.PacketPlayOutTileEntityData; ++import net.minecraft.server.TileEntitySkull; ++import net.minecraft.util.com.mojang.authlib.GameProfile; ++import org.bukkit.Bukkit; ++ ++import java.io.IOException; ++import java.io.InputStreamReader; ++import java.io.OutputStream; ++import java.net.HttpURLConnection; ++import java.net.MalformedURLException; ++import java.net.URL; ++import java.util.concurrent.Executor; ++import java.util.concurrent.Executors; ++ ++public class HeadConverter ++{ ++ private static final Executor executor = Executors.newFixedThreadPool( 3, ++ new ThreadFactoryBuilder() ++ .setNameFormat( "Head Conversion Thread - %1$d" ) ++ .build() ++ ); ++ ++ public static void convertHead(final TileEntitySkull head) ++ { ++ if ( head.getSkullType() != 3 ) ++ { ++ return; ++ } ++ final int x = head.x; ++ final int y = head.y; ++ final int z = head.z; ++ final String name = head.getExtraType(); ++ final NBTTagCompound tag = new NBTTagCompound(); ++ head.b( tag ); ++ if ( tag.hasKey( "Owner" ) && tag.getCompound( "Owner" ).hasKey( "Properties" ) ) { ++ return; ++ } ++ ++ executor.execute( new Runnable() ++ { ++ @Override ++ public void run() ++ { ++ try ++ { ++ // Firstly convert name -> uuid ++ URL accountsAPI = new URL( "https://api.mojang.com/profiles/page/1" ); ++ ++ HttpURLConnection connection = (HttpURLConnection) accountsAPI.openConnection(); ++ connection.setRequestProperty( "Content-Type", "application/json" ); ++ connection.setRequestMethod( "POST" ); ++ connection.setDoInput( true ); ++ connection.setDoOutput( true ); ++ ++ OutputStream outputStream = connection.getOutputStream(); ++ outputStream.write( ( "[{\"name\":\"" + name + ++ "\", \"agent\":\"minecraft\"}]" ).getBytes( Charsets.UTF_8 ) ); ++ outputStream.flush(); ++ outputStream.close(); ++ ++ JsonObject response = new JsonParser().parse( new InputStreamReader( connection.getInputStream() ) ) ++ .getAsJsonObject(); ++ if ( response.get( "size" ).getAsInt() != 1 ) ++ { ++ return; ++ } ++ String uuid = response.getAsJsonArray( "profiles" ) ++ .get( 0 ).getAsJsonObject() ++ .get( "id" ).getAsString(); ++ ++ NBTTagCompound owner = new NBTTagCompound(); ++ GameProfile gameProfile = new GameProfile( uuid, name ); ++ owner.setString( "Name", name ); ++ owner.setString( "Id", EntityHuman.a( gameProfile ).toString() ); ++ ++ NBTTagCompound properties = new NBTTagCompound(); ++ ++ // Now to lookup the textures ++ ++ URL url = new URL( "https://sessionserver.mojang.com/session/minecraft/profile/" + uuid ); ++ connection = (HttpURLConnection) url.openConnection(); ++ connection.setDoOutput( true ); ++ response = new JsonParser().parse( new InputStreamReader( connection.getInputStream() ) ) ++ .getAsJsonObject(); ++ ++ if ( !response.has( "properties" ) ) ++ { ++ return; ++ } ++ JsonArray props = response.getAsJsonArray( "properties" ); ++ for ( JsonElement e : props ) ++ { ++ JsonObject element = e.getAsJsonObject(); ++ NBTTagCompound prop = new NBTTagCompound(); ++ prop.setString( "Signature", element.get( "signature" ).getAsString() ); ++ prop.setString( "Value", element.get( "value" ).getAsString() ); ++ NBTTagList propList = new NBTTagList(); ++ propList.add( prop ); ++ properties.set( element.get( "name" ).getAsString(), propList ); ++ } ++ owner.set( "Properties", properties ); ++ tag.set( "Owner", owner ); ++ ++ // Update the tile entity ++ MinecraftServer.getServer().processQueue.add( new Runnable() ++ { ++ @Override ++ public void run() ++ { ++ head.a( tag ); ++ // Send the updated version ++ MinecraftServer.getServer().getPlayerList().sendPacketNearby( ++ x, y, z, head.getWorld().spigotConfig.viewDistance * 16, head.getWorld().worldData.j(), ++ head.getUpdatePacket() ); ++ } ++ } ); ++ ++ } catch ( MalformedURLException e ) ++ { ++ e.printStackTrace(); ++ } catch ( IOException e ) ++ { ++ Bukkit.getLogger().warning( "Error connecting to Mojang servers, cannot convert player heads" ); ++ } ++ } ++ } ); ++ } ++} +-- +1.8.5.2.msysgit.0 +