From df10dcf656d339c13b03763be99b5f5dc5dcc640 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Tue, 29 Jul 2014 14:59:47 -0500 Subject: [PATCH] Update from upstream SpigotMC Set the minimum max health attribute value to 0.1 SpigotMC/Spigot@d3b9fa0d1f24aeb5fa94e3b4056d54c99cd18893 Add an option for a global cache for any request on Mojang's api SpigotMC/Spigot@3452c620df79bff2530583b235d3957f3109ca4e Handle cases where the api fails to return a profile SpigotMC/Spigot@6a6eff33197004ec4296f93a9f2220da855dd4c3 --- .../0133-Convert-player-skulls-async.patch | 31 +- ...um-max-health-attribute-value-to-0.1.patch | 21 ++ ...r-a-global-cache-for-any-request-on-.patch | 287 ++++++++++++++++++ 3 files changed, 325 insertions(+), 14 deletions(-) create mode 100644 CraftBukkit-Patches/0168-Set-the-minimum-max-health-attribute-value-to-0.1.patch create mode 100644 CraftBukkit-Patches/0169-Add-an-option-for-a-global-cache-for-any-request-on-.patch diff --git a/CraftBukkit-Patches/0133-Convert-player-skulls-async.patch b/CraftBukkit-Patches/0133-Convert-player-skulls-async.patch index 9080f4cdd..f89f921c7 100644 --- a/CraftBukkit-Patches/0133-Convert-player-skulls-async.patch +++ b/CraftBukkit-Patches/0133-Convert-player-skulls-async.patch @@ -1,11 +1,11 @@ -From 6eaf9a0a9503de652b7a3dfad975c891c09d74ca Mon Sep 17 00:00:00 2001 +From a9c0273e82c8e1ea6a93e66482b23d7b74061c0d 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 +index 7d2d401..ee82aba 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 { @@ -79,10 +79,10 @@ index 2723ccb..26f9fae 100644 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 +index 2a50db9..1796a56 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; +@@ -6,11 +6,64 @@ 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; @@ -121,19 +121,22 @@ index 2a50db9..c95e4c8 100644 + GameProfileLookup gameProfileLookup = new GameProfileLookup(profiles); + + MinecraftServer.getServer().getGameProfileRepository().findProfilesByNames(new String[] { key }, Agent.MINECRAFT, gameProfileLookup); -+ if (!MinecraftServer.getServer().getOnlineMode() && profiles[0] == null) { ++ ++ GameProfile profile = profiles[ 0 ]; ++ if (profile == null) { + UUID uuid = EntityHuman.a(new GameProfile(null, key)); -+ GameProfile profile = new GameProfile(uuid, key); ++ profile = new GameProfile(uuid, key); + + gameProfileLookup.onProfileLookupSucceeded(profile); -+ } ++ } else ++ { + -+ GameProfile profile = profiles[0]; ++ Property property = Iterables.getFirst( profile.getProperties().get( "textures" ), null ); + -+ Property property = Iterables.getFirst(profile.getProperties().get("textures"), null); -+ -+ if (property == null) { -+ profile = MinecraftServer.getServer().av().fillProfileProperties(profile, true); ++ if ( property == null ) ++ { ++ profile = MinecraftServer.getServer().av().fillProfileProperties( profile, true ); ++ } + } + + @@ -144,7 +147,7 @@ index 2a50db9..c95e4c8 100644 public TileEntitySkull() {} -@@ -66,18 +116,38 @@ public class TileEntitySkull extends TileEntity { +@@ -66,18 +119,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")) { @@ -241,4 +244,4 @@ index d648d05..e32bcb1 100644 } -- -1.9.1 \ No newline at end of file +1.9.1 diff --git a/CraftBukkit-Patches/0168-Set-the-minimum-max-health-attribute-value-to-0.1.patch b/CraftBukkit-Patches/0168-Set-the-minimum-max-health-attribute-value-to-0.1.patch new file mode 100644 index 000000000..9e575689f --- /dev/null +++ b/CraftBukkit-Patches/0168-Set-the-minimum-max-health-attribute-value-to-0.1.patch @@ -0,0 +1,21 @@ +From fed56f23e31a6d9c3082faccebdb381f568475f9 Mon Sep 17 00:00:00 2001 +From: Thinkofdeath +Date: Mon, 28 Jul 2014 23:15:00 +0100 +Subject: [PATCH] Set the minimum max health attribute value to 0.1 + + +diff --git a/src/main/java/net/minecraft/server/GenericAttributes.java b/src/main/java/net/minecraft/server/GenericAttributes.java +index 70b60ac..7ad88f5 100644 +--- a/src/main/java/net/minecraft/server/GenericAttributes.java ++++ b/src/main/java/net/minecraft/server/GenericAttributes.java +@@ -11,7 +11,7 @@ public class GenericAttributes { + + private static final Logger f = LogManager.getLogger(); + // Spigot Start +- public static final IAttribute maxHealth = (new AttributeRanged("generic.maxHealth", 20.0D, 0.0D, org.spigotmc.SpigotConfig.maxHealth)).a("Max Health").a(true); ++ public static final IAttribute maxHealth = (new AttributeRanged("generic.maxHealth", 20.0D, 0.1D, org.spigotmc.SpigotConfig.maxHealth)).a("Max Health").a(true); // Spigot + public static final IAttribute b = (new AttributeRanged("generic.followRange", 32.0D, 0.0D, 2048.0D)).a("Follow Range"); + public static final IAttribute c = (new AttributeRanged("generic.knockbackResistance", 0.0D, 0.0D, 1.0D)).a("Knockback Resistance"); + public static final IAttribute d = (new AttributeRanged("generic.movementSpeed", 0.699999988079071D, 0.0D, org.spigotmc.SpigotConfig.movementSpeed)).a("Movement Speed").a(true); +-- +1.9.1 diff --git a/CraftBukkit-Patches/0169-Add-an-option-for-a-global-cache-for-any-request-on-.patch b/CraftBukkit-Patches/0169-Add-an-option-for-a-global-cache-for-any-request-on-.patch new file mode 100644 index 000000000..f58484930 --- /dev/null +++ b/CraftBukkit-Patches/0169-Add-an-option-for-a-global-cache-for-any-request-on-.patch @@ -0,0 +1,287 @@ +From 21dcc9886017354c9497cc378242d970c1733ad4 Mon Sep 17 00:00:00 2001 +From: Thinkofdeath +Date: Mon, 28 Jul 2014 11:42:11 +0100 +Subject: [PATCH] Add an option for a global cache for any request on Mojang's + api to handle plugins that don't use UserCache (or any cache) + + +diff --git a/src/main/java/org/spigotmc/CachedMojangAPIConnection.java b/src/main/java/org/spigotmc/CachedMojangAPIConnection.java +new file mode 100644 +index 0000000..9911438 +--- /dev/null ++++ b/src/main/java/org/spigotmc/CachedMojangAPIConnection.java +@@ -0,0 +1,124 @@ ++package org.spigotmc; ++ ++import com.google.common.base.Charsets; ++import com.google.gson.JsonArray; ++import com.google.gson.JsonElement; ++import com.google.gson.JsonObject; ++import com.google.gson.JsonParser; ++import net.minecraft.util.com.google.common.cache.Cache; ++import net.minecraft.util.com.google.common.cache.CacheBuilder; ++ ++import java.io.ByteArrayInputStream; ++import java.io.ByteArrayOutputStream; ++import java.io.IOException; ++import java.io.InputStream; ++import java.io.InputStreamReader; ++import java.io.OutputStream; ++import java.net.HttpURLConnection; ++import java.net.Proxy; ++import java.net.URL; ++import java.util.concurrent.TimeUnit; ++ ++public class CachedMojangAPIConnection extends HttpURLConnection ++{ ++ private final CachedStreamHandlerFactory.CachedStreamHandler cachedStreamHandler; ++ private final Proxy proxy; ++ private final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); ++ private ByteArrayInputStream inputStream; ++ private boolean outClosed = false; ++ ++ private static final Cache cache = CacheBuilder.newBuilder() ++ .maximumSize( 10000 ) ++ .expireAfterAccess( 1, TimeUnit.HOURS ) ++ .build(); ++ ++ public CachedMojangAPIConnection(CachedStreamHandlerFactory.CachedStreamHandler cachedStreamHandler, URL url, Proxy proxy) ++ { ++ super( url ); ++ this.cachedStreamHandler = cachedStreamHandler; ++ this.proxy = proxy; ++ } ++ ++ @Override ++ public void disconnect() ++ { ++ ++ } ++ ++ @Override ++ public boolean usingProxy() ++ { ++ return proxy != null; ++ } ++ ++ @Override ++ public void connect() throws IOException ++ { ++ ++ } ++ ++ @Override ++ public InputStream getInputStream() throws IOException ++ { ++ if ( inputStream == null ) ++ { ++ outClosed = true; ++ JsonArray users = new JsonParser().parse( new String( outputStream.toByteArray(), Charsets.UTF_8 ) ).getAsJsonArray(); ++ StringBuilder reply = new StringBuilder( "[" ); ++ StringBuilder missingUsers = new StringBuilder( "[" ); ++ for ( JsonElement user : users ) ++ { ++ String username = user.getAsString().toLowerCase(); ++ String info = cache.getIfPresent( username ); ++ if ( info != null ) ++ { ++ reply.append( info ).append( "," ); ++ } else ++ { ++ missingUsers ++ .append( "\"" ) ++ .append( username ) ++ .append( "\"" ) ++ .append( "," ); ++ } ++ } ++ missingUsers.deleteCharAt( missingUsers.length() - 1 ).append( "]" ); ++ HttpURLConnection connection; ++ if ( proxy == null ) ++ { ++ connection = (HttpURLConnection) cachedStreamHandler.getDefaultConnection( url ); ++ } else ++ { ++ connection = (HttpURLConnection) cachedStreamHandler.getDefaultConnection( url, proxy ); ++ } ++ connection.setRequestMethod( "POST" ); ++ connection.setRequestProperty( "Content-Type", "application/json" ); ++ connection.setDoInput( true ); ++ connection.setDoOutput( true ); ++ OutputStream out = connection.getOutputStream(); ++ out.write( missingUsers.toString().getBytes( Charsets.UTF_8 ) ); ++ out.flush(); ++ out.close(); ++ JsonArray newUsers = new JsonParser().parse( new InputStreamReader( connection.getInputStream(), Charsets.UTF_8 ) ).getAsJsonArray(); ++ for ( JsonElement user : newUsers ) ++ { ++ JsonObject u = user.getAsJsonObject(); ++ cache.put( u.get( "name" ).getAsString(), u.toString() ); ++ reply.append( u.toString() ).append( "," ); ++ } ++ reply.deleteCharAt( reply.length() - 1 ); ++ inputStream = new ByteArrayInputStream( reply.append( "]" ).toString().getBytes( Charsets.UTF_8 ) ); ++ } ++ return inputStream; ++ } ++ ++ @Override ++ public OutputStream getOutputStream() throws IOException ++ { ++ if ( outClosed ) ++ { ++ throw new RuntimeException( "Write after send" ); ++ } ++ return outputStream; ++ } ++} +diff --git a/src/main/java/org/spigotmc/CachedStreamHandlerFactory.java b/src/main/java/org/spigotmc/CachedStreamHandlerFactory.java +new file mode 100644 +index 0000000..02a5fe7 +--- /dev/null ++++ b/src/main/java/org/spigotmc/CachedStreamHandlerFactory.java +@@ -0,0 +1,115 @@ ++package org.spigotmc; ++ ++import java.io.IOException; ++import java.lang.reflect.InvocationTargetException; ++import java.lang.reflect.Method; ++import java.net.Proxy; ++import java.net.URL; ++import java.net.URLConnection; ++import java.net.URLStreamHandler; ++import java.net.URLStreamHandlerFactory; ++ ++public class CachedStreamHandlerFactory implements URLStreamHandlerFactory ++{ ++ @Override ++ public URLStreamHandler createURLStreamHandler(String protocol) ++ { ++ if ( protocol.equals( "http" ) || protocol.equals( "https" ) ) ++ { ++ return new CachedStreamHandler( protocol ); ++ } ++ return null; ++ } ++ ++ public class CachedStreamHandler extends URLStreamHandler ++ { ++ private final String protocol; ++ private final URLStreamHandler handler; ++ private final Method openCon; ++ private final Method openConProxy; ++ ++ public CachedStreamHandler(String protocol) ++ { ++ this.protocol = protocol; ++ if ( protocol.equals( "http" ) ) ++ { ++ handler = new sun.net.www.protocol.http.Handler(); ++ } else ++ { ++ handler = new sun.net.www.protocol.https.Handler(); ++ } ++ try ++ { ++ openCon = handler.getClass().getDeclaredMethod( "openConnection", URL.class ); ++ openCon.setAccessible( true ); ++ openConProxy = handler.getClass().getDeclaredMethod( "openConnection", URL.class, Proxy.class ); ++ openConProxy.setAccessible( true ); ++ } catch ( NoSuchMethodException e ) ++ { ++ throw new RuntimeException( e ); ++ } ++ } ++ ++ @Override ++ protected URLConnection openConnection(URL u) throws IOException ++ { ++ if ( u.getHost().equals( "api.mojang.com" ) ++ || u.getPath().startsWith( "/profiles/minecraft" ) ) ++ { ++ return cachedConnection( u ); ++ } ++ return getDefaultConnection( u ); ++ } ++ ++ @Override ++ protected URLConnection openConnection(URL u, Proxy p) throws IOException ++ { ++ if ( u.getHost().equals( "api.mojang.com" ) ++ || u.getPath().startsWith( "/profiles/minecraft" ) ) ++ { ++ return cachedConnection( u, p ); ++ } ++ return getDefaultConnection( u, p ); ++ } ++ ++ private URLConnection cachedConnection(URL u) ++ { ++ return cachedConnection( u, null ); ++ } ++ ++ private URLConnection cachedConnection(URL u, Proxy p) ++ { ++ return new CachedMojangAPIConnection( this, u, p ); ++ } ++ ++ public URLConnection getDefaultConnection(URL u) ++ { ++ try ++ { ++ return (URLConnection) openCon.invoke( handler, u ); ++ } catch ( IllegalAccessException e ) ++ { ++ e.printStackTrace(); ++ } catch ( InvocationTargetException e ) ++ { ++ e.printStackTrace(); ++ } ++ return null; ++ } ++ ++ public URLConnection getDefaultConnection(URL u, Proxy p) ++ { ++ try ++ { ++ return (URLConnection) openConProxy.invoke( handler, u, p ); ++ } catch ( IllegalAccessException e ) ++ { ++ e.printStackTrace(); ++ } catch ( InvocationTargetException e ) ++ { ++ e.printStackTrace(); ++ } ++ return null; ++ } ++ } ++} +diff --git a/src/main/java/org/spigotmc/SpigotConfig.java b/src/main/java/org/spigotmc/SpigotConfig.java +index 4ceeab6..9d2dfe9 100644 +--- a/src/main/java/org/spigotmc/SpigotConfig.java ++++ b/src/main/java/org/spigotmc/SpigotConfig.java +@@ -6,6 +6,7 @@ import java.io.IOException; + import java.lang.reflect.InvocationTargetException; + import java.lang.reflect.Method; + import java.lang.reflect.Modifier; ++import java.net.URL; + import java.util.Arrays; + import java.util.HashMap; + import java.util.HashSet; +@@ -360,4 +361,14 @@ public class SpigotConfig + attackDamage = getDouble( "settings.attribute.attackDamage.max", attackDamage ); + ( (AttributeRanged) GenericAttributes.e ).b = attackDamage; + } ++ ++ private static void globalAPICache() ++ { ++ if ( getBoolean( "settings.global-api-cache", false ) ) ++ { ++ Bukkit.getLogger().info( "Global API cache enabled - All requests to Mojang's API will be " + ++ "handled by Spigot" ); ++ URL.setURLStreamHandlerFactory(new CachedStreamHandlerFactory()); ++ } ++ } + } +-- +1.9.1