From a03d3471b1741d354da03ff76313f057e5a0b51f 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..ad0454a --- /dev/null +++ b/src/main/java/org/spigotmc/HeadConverter.java @@ -0,0 +1,195 @@ +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.NBTBase; +import net.minecraft.server.NBTTagCompound; +import net.minecraft.server.NBTTagList; +import net.minecraft.server.TileEntitySkull; +import net.minecraft.util.com.mojang.authlib.GameProfile; +import org.bukkit.Bukkit; +import org.spigotmc.authlib.properties.Property; + +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.Set; +import java.util.UUID; +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() + ); + private static boolean hasWarned = false; + + 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" ) ) + { + // Validate the head + org.spigotmc.authlib.GameProfile profile = getProfile( tag.getCompound( "Owner" ) ); + if ( MinecraftServer.getServer().newSessionService.getTextures( profile, false ).size() == 0 ) { + tag.remove( "Owner" ); + head.a( tag ); + } else + { + return; + } + } + + executor.execute( new Runnable() + { + @Override + public void run() + { + HttpURLConnection connection = null; + try + { + // Firstly convert name -> uuid + URL accountsAPI = new URL( "https://api.mojang.com/profiles/page/1" ); + + 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(); + + InputStreamReader inputStreamReader = new InputStreamReader( connection.getInputStream() ); + JsonObject response; + try + { + response = new JsonParser().parse( inputStreamReader ) + .getAsJsonObject(); + } finally + { + inputStreamReader.close(); + } + if ( response.get( "size" ).getAsInt() != 1 || + response.getAsJsonArray( "profiles" ).size() != 1 ) + { + return; + } + String uuid = response.getAsJsonArray( "profiles" ) + .get( 0 ).getAsJsonObject() + .get( "id" ).getAsString(); + String correctedName = response.getAsJsonArray( "profiles" ) + .get( 0 ).getAsJsonObject() + .get( "name" ).getAsString(); + + NBTTagCompound owner = new NBTTagCompound(); + GameProfile gameProfile = new GameProfile( uuid, correctedName ); + owner.setString( "Name", correctedName ); + owner.setString( "Id", EntityHuman.a( gameProfile ).toString() ); + + NBTTagCompound properties = new NBTTagCompound(); + + // Now to lookup the textures + org.spigotmc.authlib.GameProfile newStyleProfile = new org.spigotmc.authlib.GameProfile( + EntityHuman.a( gameProfile ), + gameProfile.getName() ); + MinecraftServer.getServer().newSessionService.fillProfileProperties( newStyleProfile ); + + if ( newStyleProfile.getProperties().size() < 1) + { + return; + } + + + for ( String key : newStyleProfile.getProperties().keys() ) + { + NBTTagList propList = new NBTTagList(); + for ( Property prop : newStyleProfile.getProperties().get( key ) ) + { + NBTTagCompound nprop = new NBTTagCompound(); + nprop.setString( "Signature", prop.getSignature() ); + nprop.setString( "Value", prop.getValue() ); + propList.add( nprop ); + } + properties.set( key, 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 ) + { + if (!hasWarned) + { + hasWarned = true; + Bukkit.getLogger().warning( "Error connecting to Mojang servers, cannot convert player heads" ); + } + } finally + { + if ( connection != null ) + { + connection.disconnect(); + } + } + } + } ); + } + + private static org.spigotmc.authlib.GameProfile getProfile(NBTTagCompound owner) + { + org.spigotmc.authlib.GameProfile profile = new org.spigotmc.authlib.GameProfile( + UUID.fromString( owner.getString( "Id" ) ), owner.getString( "Name" ) ); + + NBTTagCompound properties = owner.getCompound( "Properties" ); + for (String key : (Set) properties.c()) + { + NBTTagList props = properties.getList( key, 10 ); + for (int i = 0; i < props.size(); i++) { + NBTTagCompound prop = props.get( i ); + profile.getProperties().put( key, new Property( key, prop.getString( "Value" ), prop.getString( "Signature" ) ) ); + } + } + return profile; + } +} -- 1.8.5.2.msysgit.0