diff --git a/nms-patches/PlayerList.patch b/nms-patches/PlayerList.patch
index 24d35178b5..31a7126ffd 100644
--- a/nms-patches/PlayerList.patch
+++ b/nms-patches/PlayerList.patch
@@ -1,10 +1,13 @@
 --- a/net/minecraft/server/PlayerList.java
 +++ b/net/minecraft/server/PlayerList.java
-@@ -19,6 +19,26 @@
+@@ -19,6 +19,29 @@
  import org.apache.logging.log4j.LogManager;
  import org.apache.logging.log4j.Logger;
  
 +// CraftBukkit start
++import com.google.common.base.Predicate;
++import com.google.common.collect.Iterables;
++
 +import org.bukkit.craftbukkit.CraftServer;
 +import org.bukkit.craftbukkit.CraftWorld;
 +import org.bukkit.craftbukkit.chunkio.ChunkIOExecutor;
@@ -27,7 +30,7 @@
  public abstract class PlayerList {
  
      public static final File a = new File("banned-players.json");
-@@ -28,7 +48,7 @@
+@@ -28,7 +51,7 @@
      private static final Logger f = LogManager.getLogger();
      private static final SimpleDateFormat g = new SimpleDateFormat("yyyy-MM-dd \'at\' HH:mm:ss z");
      private final MinecraftServer server;
@@ -36,7 +39,7 @@
      private final Map<UUID, EntityPlayer> j = Maps.newHashMap();
      private final GameProfileBanList k;
      private final IpBanList l;
-@@ -43,7 +63,15 @@
+@@ -43,7 +66,15 @@
      private boolean t;
      private int u;
  
@@ -52,7 +55,7 @@
          this.k = new GameProfileBanList(PlayerList.a);
          this.l = new IpBanList(PlayerList.b);
          this.operators = new OpList(PlayerList.c);
-@@ -63,6 +91,12 @@
+@@ -63,6 +94,12 @@
  
          usercache.a(gameprofile);
          NBTTagCompound nbttagcompound = this.a(entityplayer);
@@ -65,7 +68,7 @@
  
          entityplayer.spawnIn(this.server.getWorldServer(entityplayer.dimension));
          entityplayer.playerInteractManager.a((WorldServer) entityplayer.world);
-@@ -72,7 +106,8 @@
+@@ -72,7 +109,8 @@
              s1 = networkmanager.getSocketAddress().toString();
          }
  
@@ -75,7 +78,7 @@
          WorldServer worldserver = this.server.getWorldServer(entityplayer.dimension);
          WorldData worlddata = worldserver.getWorldData();
  
-@@ -80,6 +115,7 @@
+@@ -80,6 +118,7 @@
          PlayerConnection playerconnection = new PlayerConnection(this.server, networkmanager, entityplayer);
  
          playerconnection.sendPacket(new PacketPlayOutLogin(entityplayer.getId(), entityplayer.playerInteractManager.getGameMode(), worlddata.isHardcore(), worldserver.worldProvider.getDimensionManager().getDimensionID(), worldserver.getDifficulty(), this.getMaxPlayers(), worlddata.getType(), worldserver.getGameRules().getBoolean("reducedDebugInfo")));
@@ -83,7 +86,7 @@
          playerconnection.sendPacket(new PacketPlayOutCustomPayload("MC|Brand", (new PacketDataSerializer(Unpooled.buffer())).a(this.getServer().getServerModName())));
          playerconnection.sendPacket(new PacketPlayOutServerDifficulty(worlddata.getDifficulty(), worlddata.isDifficultyLocked()));
          playerconnection.sendPacket(new PacketPlayOutAbilities(entityplayer.abilities));
-@@ -89,17 +125,23 @@
+@@ -89,17 +128,23 @@
          entityplayer.getStatisticManager().updateStatistics(entityplayer);
          this.sendScoreboard((ScoreboardServer) worldserver.getScoreboard(), entityplayer);
          this.server.aD();
@@ -113,7 +116,7 @@
          playerconnection.a(entityplayer.locX, entityplayer.locY, entityplayer.locZ, entityplayer.yaw, entityplayer.pitch);
          this.b(entityplayer, worldserver);
          if (!this.server.getResourcePack().isEmpty()) {
-@@ -159,6 +201,8 @@
+@@ -159,6 +204,8 @@
          }
  
          entityplayer.syncInventory();
@@ -122,7 +125,7 @@
      }
  
      public void sendScoreboard(ScoreboardServer scoreboardserver, EntityPlayer entityplayer) {
-@@ -191,26 +235,27 @@
+@@ -191,26 +238,27 @@
      }
  
      public void setPlayerFileData(WorldServer[] aworldserver) {
@@ -155,7 +158,7 @@
              }
  
              public void b(WorldBorder worldborder, double d0) {}
-@@ -235,7 +280,7 @@
+@@ -235,7 +283,7 @@
      }
  
      public NBTTagCompound a(EntityPlayer entityplayer) {
@@ -164,7 +167,7 @@
          NBTTagCompound nbttagcompound1;
  
          if (entityplayer.getName().equals(this.server.Q()) && nbttagcompound != null) {
-@@ -259,28 +304,72 @@
+@@ -259,28 +307,72 @@
  
      }
  
@@ -243,7 +246,7 @@
          this.savePlayerFile(entityplayer);
          if (entityplayer.isPassenger()) {
              Entity entity = entityplayer.getVehicle();
-@@ -312,13 +401,61 @@
+@@ -312,13 +404,61 @@
              this.o.remove(uuid);
          }
  
@@ -308,7 +311,7 @@
              GameProfileBanEntry gameprofilebanentry = (GameProfileBanEntry) this.k.get(gameprofile);
  
              s = "You are banned from this server!\nReason: " + gameprofilebanentry.getReason();
-@@ -326,10 +463,12 @@
+@@ -326,10 +466,12 @@
                  s = s + "\nYour ban will be removed on " + PlayerList.g.format(gameprofilebanentry.getExpires());
              }
  
@@ -324,7 +327,7 @@
              IpBanEntry ipbanentry = this.l.get(socketaddress);
  
              s = "Your IP address is banned from this server!\nReason: " + ipbanentry.getReason();
-@@ -337,13 +476,25 @@
+@@ -337,13 +479,25 @@
                  s = s + "\nYour ban will be removed on " + PlayerList.g.format(ipbanentry.getExpires());
              }
  
@@ -337,13 +340,13 @@
 +            if (this.players.size() >= this.maxPlayers && !this.f(gameprofile)) {
 +                event.disallow(PlayerLoginEvent.Result.KICK_FULL, "The server is full");
 +            }
-+        }
+         }
 +
 +        cserver.getPluginManager().callEvent(event);
 +        if (event.getResult() != PlayerLoginEvent.Result.ALLOWED) {
 +            loginlistener.disconnect(event.getKickMessage());
 +            return null;
-         }
++        }
 +        return entity;
      }
  
@@ -353,7 +356,7 @@
          UUID uuid = EntityHuman.a(gameprofile);
          ArrayList arraylist = Lists.newArrayList();
  
-@@ -378,17 +529,26 @@
+@@ -378,17 +532,26 @@
          }
  
          return new EntityPlayer(this.server, this.server.getWorldServer(0), gameprofile, (PlayerInteractManager) object);
@@ -381,7 +384,7 @@
          entityplayer.dimension = i;
          Object object;
  
-@@ -399,6 +559,11 @@
+@@ -399,6 +562,11 @@
          }
  
          EntityPlayer entityplayer1 = new EntityPlayer(this.server, this.server.getWorldServer(entityplayer.dimension), entityplayer.getProfile(), (PlayerInteractManager) object);
@@ -393,7 +396,7 @@
  
          entityplayer1.playerConnection = entityplayer.playerConnection;
          entityplayer1.copyTo(entityplayer, flag);
-@@ -413,43 +578,157 @@
+@@ -413,43 +581,157 @@
              entityplayer1.a(s);
          }
  
@@ -424,13 +427,13 @@
 +                    entityplayer1.setRespawnPosition(null, true);
 +                    entityplayer1.playerConnection.sendPacket(new PacketPlayOutGameStateChange(0, 0.0F));
 +                }
-+            }
+             }
 +
 +            if (location == null) {
 +                cworld = (CraftWorld) this.server.server.getWorlds().get(0);
 +                blockposition = cworld.getHandle().getSpawn();
 +                location = new Location(cworld, (double) ((float) blockposition.getX() + 0.5F), (double) ((float) blockposition.getY() + 0.1F), (double) ((float) blockposition.getZ() + 0.5F));
-             }
++            }
 +
 +            Player respawnPlayer = cserver.getPlayer(entityplayer1);
 +            PlayerRespawnEvent respawnEvent = new PlayerRespawnEvent(respawnPlayer, location, isBedSpawn);
@@ -568,7 +571,7 @@
      public void f(EntityPlayer entityplayer) {
          GameProfile gameprofile = entityplayer.getProfile();
          int i = this.isOp(gameprofile) ? this.operators.a(gameprofile) : 0;
-@@ -488,12 +767,111 @@
+@@ -488,12 +770,111 @@
      }
  
      public void changeWorld(Entity entity, int i, WorldServer worldserver, WorldServer worldserver1) {
@@ -680,7 +683,7 @@
          if (entity.dimension == -1) {
              d0 = MathHelper.a(d0 / 8.0D, worldserver1.getWorldBorder().b() + 16.0D, worldserver1.getWorldBorder().d() - 16.0D);
              d1 = MathHelper.a(d1 / 8.0D, worldserver1.getWorldBorder().c() + 16.0D, worldserver1.getWorldBorder().e() - 16.0D);
-@@ -512,6 +890,8 @@
+@@ -512,6 +893,8 @@
              BlockPosition blockposition;
  
              if (i == 1) {
@@ -689,7 +692,7 @@
                  blockposition = worldserver1.getSpawn();
              } else {
                  blockposition = worldserver1.getDimensionSpawn();
-@@ -525,16 +905,27 @@
+@@ -525,16 +908,27 @@
                  worldserver.entityJoinedWorld(entity, false);
              }
          }
@@ -720,7 +723,7 @@
                  worldserver1.entityJoinedWorld(entity, false);
              }
  
-@@ -542,6 +933,7 @@
+@@ -542,11 +936,23 @@
          }
  
          entity.spawnIn(worldserver1);
@@ -728,7 +731,24 @@
      }
  
      public void tick() {
-@@ -559,6 +951,25 @@
+         if (++this.u > 600) {
+-            this.sendAll(new PacketPlayOutPlayerInfo(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.UPDATE_LATENCY, this.players));
++            // CraftBukkit start
++            for (int i = 0; i < this.players.size(); ++i) {
++                final EntityPlayer target = (EntityPlayer) this.players.get(i);
++
++                target.playerConnection.sendPacket(new PacketPlayOutPlayerInfo(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.UPDATE_LATENCY, Iterables.filter(this.players, new Predicate<EntityPlayer>() {
++                    @Override
++                    public boolean apply(EntityPlayer input) {
++                        return target.getBukkitEntity().canSee(input.getBukkitEntity());
++                    }
++                })));
++            }
++            // CraftBukkit end
+             this.u = 0;
+         }
+ 
+@@ -559,6 +965,25 @@
  
      }
  
@@ -754,7 +774,7 @@
      public void a(Packet<?> packet, int i) {
          for (int j = 0; j < this.players.size(); ++j) {
              EntityPlayer entityplayer = (EntityPlayer) this.players.get(j);
-@@ -657,11 +1068,23 @@
+@@ -657,11 +1082,23 @@
  
          this.operators.add(new OpListEntry(gameprofile, this.server.q(), this.operators.b(gameprofile)));
          this.b(this.a(gameprofile.getId()), i);
@@ -778,7 +798,7 @@
      }
  
      private void b(EntityPlayer entityplayer, int i) {
-@@ -686,7 +1109,7 @@
+@@ -686,7 +1123,7 @@
      }
  
      public boolean isOp(GameProfile gameprofile) {
@@ -787,7 +807,7 @@
      }
  
      @Nullable
-@@ -710,6 +1133,12 @@
+@@ -710,6 +1147,12 @@
          for (int j = 0; j < this.players.size(); ++j) {
              EntityPlayer entityplayer = (EntityPlayer) this.players.get(j);
  
@@ -800,7 +820,7 @@
              if (entityplayer != entityhuman && entityplayer.dimension == i) {
                  double d4 = d0 - entityplayer.locX;
                  double d5 = d1 - entityplayer.locY;
-@@ -757,7 +1186,7 @@
+@@ -757,7 +1200,7 @@
      public void reloadWhitelist() {}
  
      public void b(EntityPlayer entityplayer, WorldServer worldserver) {
@@ -809,7 +829,7 @@
  
          entityplayer.playerConnection.sendPacket(new PacketPlayOutWorldBorder(worldborder, PacketPlayOutWorldBorder.EnumWorldBorderAction.INITIALIZE));
          entityplayer.playerConnection.sendPacket(new PacketPlayOutUpdateTime(worldserver.getTime(), worldserver.getDayTime(), worldserver.getGameRules().getBoolean("doDaylightCycle")));
-@@ -765,16 +1194,21 @@
+@@ -765,16 +1208,21 @@
  
          entityplayer.playerConnection.sendPacket(new PacketPlayOutSpawnPosition(blockposition));
          if (worldserver.W()) {
@@ -835,7 +855,7 @@
          entityplayer.playerConnection.sendPacket(new PacketPlayOutHeldItemSlot(entityplayer.inventory.itemInHandIndex));
      }
  
-@@ -787,7 +1221,7 @@
+@@ -787,7 +1235,7 @@
      }
  
      public String[] getSeenPlayers() {
@@ -844,7 +864,7 @@
      }
  
      public boolean getHasWhitelist() {
-@@ -836,17 +1270,29 @@
+@@ -836,17 +1284,29 @@
      }
  
      public void u() {
@@ -877,7 +897,7 @@
      }
  
      public void sendMessage(IChatBaseComponent ichatbasecomponent) {
-@@ -883,8 +1329,10 @@
+@@ -883,8 +1343,10 @@
              WorldServer[] aworldserver = this.server.worldServer;
              int j = aworldserver.length;
  
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
index ab638ba759..7c616c177a 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
@@ -911,7 +911,7 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
         if (hiddenPlayers.contains(player.getUniqueId())) return;
         hiddenPlayers.add(player.getUniqueId());
 
-        //remove this player from the hidden player's EntityTrackerEntry
+        // Remove this player from the hidden player's EntityTrackerEntry
         EntityTracker tracker = ((WorldServer) entity.world).tracker;
         EntityPlayer other = ((CraftPlayer) player).getHandle();
         EntityTrackerEntry entry = (EntityTrackerEntry) tracker.trackedEntities.get(other.getId());
@@ -919,8 +919,10 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
             entry.clear(getHandle());
         }
 
-        //remove the hidden player from this player user list
-        getHandle().playerConnection.sendPacket(new PacketPlayOutPlayerInfo(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.REMOVE_PLAYER, other));
+        // Remove the hidden player from this player user list
+        if (!other.joining) {
+            getHandle().playerConnection.sendPacket(new PacketPlayOutPlayerInfo(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.REMOVE_PLAYER, other));
+        }
     }
 
     @Override