More more more more more work
This commit is contained in:
		
					parent
					
						
							
								d7cdc72bdf
							
						
					
				
			
			
				commit
				
					
						6f3591fd6d
					
				
			
		
					 90 changed files with 203 additions and 211 deletions
				
			
		| 
						 | 
				
			
			@ -1,46 +0,0 @@
 | 
			
		|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
 | 
			
		||||
From: BillyGalbreath <Blake.Galbreath@GMail.com>
 | 
			
		||||
Date: Sun, 7 May 2017 06:26:09 -0500
 | 
			
		||||
Subject: [PATCH] PlayerPickupItemEvent#setFlyAtPlayer
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
diff --git a/src/main/java/net/minecraft/world/entity/item/ItemEntity.java b/src/main/java/net/minecraft/world/entity/item/ItemEntity.java
 | 
			
		||||
index 9611388a6aeebb86b19d89c526f53dfed4d3ed27..d17af2ec8f72bf0cbe5928e7a83c06ba5ad4503d 100644
 | 
			
		||||
--- a/src/main/java/net/minecraft/world/entity/item/ItemEntity.java
 | 
			
		||||
+++ b/src/main/java/net/minecraft/world/entity/item/ItemEntity.java
 | 
			
		||||
@@ -379,6 +379,7 @@ public class ItemEntity extends Entity {
 | 
			
		||||
             // CraftBukkit start - fire PlayerPickupItemEvent
 | 
			
		||||
             int canHold = player.getInventory().canHold(itemstack);
 | 
			
		||||
             int remaining = i - canHold;
 | 
			
		||||
+            boolean flyAtPlayer = false; // Paper
 | 
			
		||||
 
 | 
			
		||||
             if (this.pickupDelay <= 0 && canHold > 0) {
 | 
			
		||||
                 itemstack.setCount(canHold);
 | 
			
		||||
@@ -386,8 +387,14 @@ public class ItemEntity extends Entity {
 | 
			
		||||
                 PlayerPickupItemEvent playerEvent = new PlayerPickupItemEvent((org.bukkit.entity.Player) player.getBukkitEntity(), (org.bukkit.entity.Item) this.getBukkitEntity(), remaining);
 | 
			
		||||
                 playerEvent.setCancelled(!playerEvent.getPlayer().getCanPickupItems());
 | 
			
		||||
                 this.level.getCraftServer().getPluginManager().callEvent(playerEvent);
 | 
			
		||||
+                flyAtPlayer = playerEvent.getFlyAtPlayer(); // Paper
 | 
			
		||||
                 if (playerEvent.isCancelled()) {
 | 
			
		||||
                     itemstack.setCount(i); // SPIGOT-5294 - restore count
 | 
			
		||||
+                    // Paper Start
 | 
			
		||||
+                    if (flyAtPlayer) {
 | 
			
		||||
+                        player.take(this, i);
 | 
			
		||||
+                    }
 | 
			
		||||
+                    // Paper End
 | 
			
		||||
                     return;
 | 
			
		||||
                 }
 | 
			
		||||
 
 | 
			
		||||
@@ -417,7 +424,11 @@ public class ItemEntity extends Entity {
 | 
			
		||||
             // CraftBukkit end
 | 
			
		||||
 
 | 
			
		||||
             if (this.pickupDelay == 0 && (this.owner == null || this.owner.equals(player.getUUID())) && player.getInventory().add(itemstack)) {
 | 
			
		||||
-                player.take(this, i);
 | 
			
		||||
+                // Paper Start
 | 
			
		||||
+                if (flyAtPlayer) {
 | 
			
		||||
+                    player.take(this, i);
 | 
			
		||||
+                }
 | 
			
		||||
+                // Paper End
 | 
			
		||||
                 if (itemstack.isEmpty()) {
 | 
			
		||||
                     this.discard();
 | 
			
		||||
                     itemstack.setCount(i);
 | 
			
		||||
| 
						 | 
				
			
			@ -1,41 +0,0 @@
 | 
			
		|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
 | 
			
		||||
From: BillyGalbreath <Blake.Galbreath@GMail.com>
 | 
			
		||||
Date: Sun, 11 Jun 2017 16:30:30 -0500
 | 
			
		||||
Subject: [PATCH] PlayerAttemptPickupItemEvent
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
diff --git a/src/main/java/net/minecraft/world/entity/item/ItemEntity.java b/src/main/java/net/minecraft/world/entity/item/ItemEntity.java
 | 
			
		||||
index d17af2ec8f72bf0cbe5928e7a83c06ba5ad4503d..54025e401eb02fceb47afb182f0ede620ca23a8d 100644
 | 
			
		||||
--- a/src/main/java/net/minecraft/world/entity/item/ItemEntity.java
 | 
			
		||||
+++ b/src/main/java/net/minecraft/world/entity/item/ItemEntity.java
 | 
			
		||||
@@ -37,6 +37,7 @@ import net.minecraft.stats.Stats;
 | 
			
		||||
 import org.bukkit.event.entity.EntityPickupItemEvent;
 | 
			
		||||
 import org.bukkit.event.player.PlayerPickupItemEvent;
 | 
			
		||||
 // CraftBukkit end
 | 
			
		||||
+import org.bukkit.event.player.PlayerAttemptPickupItemEvent; // Paper
 | 
			
		||||
 
 | 
			
		||||
 public class ItemEntity extends Entity {
 | 
			
		||||
 
 | 
			
		||||
@@ -381,6 +382,22 @@ public class ItemEntity extends Entity {
 | 
			
		||||
             int remaining = i - canHold;
 | 
			
		||||
             boolean flyAtPlayer = false; // Paper
 | 
			
		||||
 
 | 
			
		||||
+            // Paper start
 | 
			
		||||
+            if (this.pickupDelay <= 0) {
 | 
			
		||||
+                PlayerAttemptPickupItemEvent attemptEvent = new PlayerAttemptPickupItemEvent((org.bukkit.entity.Player) player.getBukkitEntity(), (org.bukkit.entity.Item) this.getBukkitEntity(), remaining);
 | 
			
		||||
+                this.level.getCraftServer().getPluginManager().callEvent(attemptEvent);
 | 
			
		||||
+
 | 
			
		||||
+                flyAtPlayer = attemptEvent.getFlyAtPlayer();
 | 
			
		||||
+                if (attemptEvent.isCancelled()) {
 | 
			
		||||
+                    if (flyAtPlayer) {
 | 
			
		||||
+                        player.take(this, i);
 | 
			
		||||
+                    }
 | 
			
		||||
+
 | 
			
		||||
+                    return;
 | 
			
		||||
+                }
 | 
			
		||||
+            }
 | 
			
		||||
+            // Paper end
 | 
			
		||||
+
 | 
			
		||||
             if (this.pickupDelay <= 0 && canHold > 0) {
 | 
			
		||||
                 itemstack.setCount(canHold);
 | 
			
		||||
                 // Call legacy event
 | 
			
		||||
| 
						 | 
				
			
			@ -1,25 +0,0 @@
 | 
			
		|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
 | 
			
		||||
From: Sweepyoface <github@sweepy.pw>
 | 
			
		||||
Date: Sat, 17 Jun 2017 18:48:21 -0400
 | 
			
		||||
Subject: [PATCH] Add UnknownCommandEvent
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
 | 
			
		||||
index 696e2495ee8046c78ed53126db0e6c696c77c00d..e01f800130f183bf10a383e298b7da3d5f11e3a2 100644
 | 
			
		||||
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
 | 
			
		||||
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
 | 
			
		||||
@@ -817,7 +817,13 @@ public final class CraftServer implements Server {
 | 
			
		||||
 
 | 
			
		||||
         // Spigot start
 | 
			
		||||
         if (!org.spigotmc.SpigotConfig.unknownCommandMessage.isEmpty()) {
 | 
			
		||||
-            sender.sendMessage(org.spigotmc.SpigotConfig.unknownCommandMessage);
 | 
			
		||||
+            // Paper start
 | 
			
		||||
+            org.bukkit.event.command.UnknownCommandEvent event = new org.bukkit.event.command.UnknownCommandEvent(sender, commandLine, org.spigotmc.SpigotConfig.unknownCommandMessage);
 | 
			
		||||
+            Bukkit.getServer().getPluginManager().callEvent(event);
 | 
			
		||||
+            if (event.message() != null) {
 | 
			
		||||
+                sender.sendMessage(event.message());
 | 
			
		||||
+            }
 | 
			
		||||
+            // Paper end
 | 
			
		||||
         }
 | 
			
		||||
         // Spigot end
 | 
			
		||||
 
 | 
			
		||||
| 
						 | 
				
			
			@ -1,549 +0,0 @@
 | 
			
		|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
 | 
			
		||||
From: Aikar <aikar@aikar.co>
 | 
			
		||||
Date: Mon, 15 Jan 2018 22:11:48 -0500
 | 
			
		||||
Subject: [PATCH] Basic PlayerProfile API
 | 
			
		||||
 | 
			
		||||
Establishes base extension of profile systems for future edits too
 | 
			
		||||
 | 
			
		||||
diff --git a/src/main/java/com/destroystokyo/paper/profile/CraftPlayerProfile.java b/src/main/java/com/destroystokyo/paper/profile/CraftPlayerProfile.java
 | 
			
		||||
new file mode 100644
 | 
			
		||||
index 0000000000000000000000000000000000000000..84551164b76bc8f064a3a0c030c3a1b47f567b6f
 | 
			
		||||
--- /dev/null
 | 
			
		||||
+++ b/src/main/java/com/destroystokyo/paper/profile/CraftPlayerProfile.java
 | 
			
		||||
@@ -0,0 +1,302 @@
 | 
			
		||||
+package com.destroystokyo.paper.profile;
 | 
			
		||||
+
 | 
			
		||||
+import com.destroystokyo.paper.PaperConfig;
 | 
			
		||||
+import com.google.common.base.Charsets;
 | 
			
		||||
+import com.mojang.authlib.GameProfile;
 | 
			
		||||
+import com.mojang.authlib.properties.Property;
 | 
			
		||||
+import com.mojang.authlib.properties.PropertyMap;
 | 
			
		||||
+import net.minecraft.server.MinecraftServer;
 | 
			
		||||
+import net.minecraft.server.players.GameProfileCache;
 | 
			
		||||
+import org.apache.commons.lang3.Validate;
 | 
			
		||||
+import org.bukkit.craftbukkit.entity.CraftPlayer;
 | 
			
		||||
+
 | 
			
		||||
+import javax.annotation.Nonnull;
 | 
			
		||||
+import javax.annotation.Nullable;
 | 
			
		||||
+import java.util.AbstractSet;
 | 
			
		||||
+import java.util.Collection;
 | 
			
		||||
+import java.util.Iterator;
 | 
			
		||||
+import java.util.Objects;
 | 
			
		||||
+import java.util.Optional;
 | 
			
		||||
+import java.util.Set;
 | 
			
		||||
+import java.util.UUID;
 | 
			
		||||
+
 | 
			
		||||
+public class CraftPlayerProfile implements PlayerProfile {
 | 
			
		||||
+
 | 
			
		||||
+    private GameProfile profile;
 | 
			
		||||
+    private final PropertySet properties = new PropertySet();
 | 
			
		||||
+
 | 
			
		||||
+    public CraftPlayerProfile(CraftPlayer player) {
 | 
			
		||||
+        this.profile = player.getHandle().getGameProfile();
 | 
			
		||||
+    }
 | 
			
		||||
+
 | 
			
		||||
+    public CraftPlayerProfile(UUID id, String name) {
 | 
			
		||||
+        this.profile = new GameProfile(id, name);
 | 
			
		||||
+    }
 | 
			
		||||
+
 | 
			
		||||
+    public CraftPlayerProfile(GameProfile profile) {
 | 
			
		||||
+        Validate.notNull(profile, "GameProfile cannot be null!");
 | 
			
		||||
+        this.profile = profile;
 | 
			
		||||
+    }
 | 
			
		||||
+
 | 
			
		||||
+    @Override
 | 
			
		||||
+    public boolean hasProperty(String property) {
 | 
			
		||||
+        return profile.getProperties().containsKey(property);
 | 
			
		||||
+    }
 | 
			
		||||
+
 | 
			
		||||
+    @Override
 | 
			
		||||
+    public void setProperty(ProfileProperty property) {
 | 
			
		||||
+        String name = property.getName();
 | 
			
		||||
+        PropertyMap properties = profile.getProperties();
 | 
			
		||||
+        properties.removeAll(name);
 | 
			
		||||
+        properties.put(name, new Property(name, property.getValue(), property.getSignature()));
 | 
			
		||||
+    }
 | 
			
		||||
+
 | 
			
		||||
+    public GameProfile getGameProfile() {
 | 
			
		||||
+        return profile;
 | 
			
		||||
+    }
 | 
			
		||||
+
 | 
			
		||||
+    @Nullable
 | 
			
		||||
+    @Override
 | 
			
		||||
+    public UUID getId() {
 | 
			
		||||
+        return profile.getId();
 | 
			
		||||
+    }
 | 
			
		||||
+
 | 
			
		||||
+    @Override
 | 
			
		||||
+    public UUID setId(@Nullable UUID uuid) {
 | 
			
		||||
+        GameProfile prev = this.profile;
 | 
			
		||||
+        this.profile = new GameProfile(uuid, prev.getName());
 | 
			
		||||
+        copyProfileProperties(prev, this.profile);
 | 
			
		||||
+        return prev.getId();
 | 
			
		||||
+    }
 | 
			
		||||
+
 | 
			
		||||
+    @Nullable
 | 
			
		||||
+    @Override
 | 
			
		||||
+    public String getName() {
 | 
			
		||||
+        return profile.getName();
 | 
			
		||||
+    }
 | 
			
		||||
+
 | 
			
		||||
+    @Override
 | 
			
		||||
+    public String setName(@Nullable String name) {
 | 
			
		||||
+        GameProfile prev = this.profile;
 | 
			
		||||
+        this.profile = new GameProfile(prev.getId(), name);
 | 
			
		||||
+        copyProfileProperties(prev, this.profile);
 | 
			
		||||
+        return prev.getName();
 | 
			
		||||
+    }
 | 
			
		||||
+
 | 
			
		||||
+    @Nonnull
 | 
			
		||||
+    @Override
 | 
			
		||||
+    public Set<ProfileProperty> getProperties() {
 | 
			
		||||
+        return properties;
 | 
			
		||||
+    }
 | 
			
		||||
+
 | 
			
		||||
+    @Override
 | 
			
		||||
+    public void setProperties(Collection<ProfileProperty> properties) {
 | 
			
		||||
+        properties.forEach(this::setProperty);
 | 
			
		||||
+    }
 | 
			
		||||
+
 | 
			
		||||
+    @Override
 | 
			
		||||
+    public void clearProperties() {
 | 
			
		||||
+        profile.getProperties().clear();
 | 
			
		||||
+    }
 | 
			
		||||
+
 | 
			
		||||
+    @Override
 | 
			
		||||
+    public boolean removeProperty(String property) {
 | 
			
		||||
+        return !profile.getProperties().removeAll(property).isEmpty();
 | 
			
		||||
+    }
 | 
			
		||||
+
 | 
			
		||||
+    @Override
 | 
			
		||||
+    public boolean equals(Object o) {
 | 
			
		||||
+        if (this == o) return true;
 | 
			
		||||
+        if (o == null || getClass() != o.getClass()) return false;
 | 
			
		||||
+        CraftPlayerProfile that = (CraftPlayerProfile) o;
 | 
			
		||||
+        return Objects.equals(profile, that.profile);
 | 
			
		||||
+    }
 | 
			
		||||
+
 | 
			
		||||
+    @Override
 | 
			
		||||
+    public int hashCode() {
 | 
			
		||||
+        return profile.hashCode();
 | 
			
		||||
+    }
 | 
			
		||||
+
 | 
			
		||||
+    @Override
 | 
			
		||||
+    public String toString() {
 | 
			
		||||
+        return profile.toString();
 | 
			
		||||
+    }
 | 
			
		||||
+
 | 
			
		||||
+    @Override
 | 
			
		||||
+    public CraftPlayerProfile clone() {
 | 
			
		||||
+        CraftPlayerProfile clone = new CraftPlayerProfile(this.getId(), this.getName());
 | 
			
		||||
+        clone.setProperties(getProperties());
 | 
			
		||||
+        return clone;
 | 
			
		||||
+    }
 | 
			
		||||
+
 | 
			
		||||
+    @Override
 | 
			
		||||
+    public boolean isComplete() {
 | 
			
		||||
+        return profile.isComplete();
 | 
			
		||||
+    }
 | 
			
		||||
+
 | 
			
		||||
+    @Override
 | 
			
		||||
+    public boolean completeFromCache() {
 | 
			
		||||
+        MinecraftServer server = MinecraftServer.getServer();
 | 
			
		||||
+        return completeFromCache(false, PaperConfig.isProxyOnlineMode());
 | 
			
		||||
+    }
 | 
			
		||||
+
 | 
			
		||||
+    public boolean completeFromCache(boolean onlineMode) {
 | 
			
		||||
+        return completeFromCache(false, onlineMode);
 | 
			
		||||
+    }
 | 
			
		||||
+
 | 
			
		||||
+    public boolean completeFromCache(boolean lookupUUID, boolean onlineMode) {
 | 
			
		||||
+        MinecraftServer server = MinecraftServer.getServer();
 | 
			
		||||
+        String name = profile.getName();
 | 
			
		||||
+        GameProfileCache userCache = server.getProfileCache();
 | 
			
		||||
+        if (profile.getId() == null) {
 | 
			
		||||
+            final GameProfile profile;
 | 
			
		||||
+            if (onlineMode) {
 | 
			
		||||
+                profile = lookupUUID ? userCache.get(name).orElse(null) : userCache.getProfileIfCached(name);
 | 
			
		||||
+            } else {
 | 
			
		||||
+                // Make an OfflinePlayer using an offline mode UUID since the name has no profile
 | 
			
		||||
+                profile = new GameProfile(UUID.nameUUIDFromBytes(("OfflinePlayer:" + name).getBytes(Charsets.UTF_8)), name);
 | 
			
		||||
+            }
 | 
			
		||||
+            if (profile != null) {
 | 
			
		||||
+                // if old has it, assume its newer, so overwrite, else use cached if it was set and ours wasn't
 | 
			
		||||
+                copyProfileProperties(this.profile, profile);
 | 
			
		||||
+                this.profile = profile;
 | 
			
		||||
+            }
 | 
			
		||||
+        }
 | 
			
		||||
+
 | 
			
		||||
+        if ((profile.getName() == null || !hasTextures()) && profile.getId() != null) {
 | 
			
		||||
+            Optional<GameProfile> optProfile = userCache.get(this.profile.getId());
 | 
			
		||||
+            if (optProfile.isPresent()) {
 | 
			
		||||
+                GameProfile profile = optProfile.get();
 | 
			
		||||
+                if (this.profile.getName() == null) {
 | 
			
		||||
+                    // if old has it, assume its newer, so overwrite, else use cached if it was set and ours wasn't
 | 
			
		||||
+                    copyProfileProperties(this.profile, profile);
 | 
			
		||||
+                    this.profile = profile;
 | 
			
		||||
+                } else {
 | 
			
		||||
+                    copyProfileProperties(profile, this.profile);
 | 
			
		||||
+                }
 | 
			
		||||
+            }
 | 
			
		||||
+        }
 | 
			
		||||
+        return this.profile.isComplete();
 | 
			
		||||
+    }
 | 
			
		||||
+
 | 
			
		||||
+    public boolean complete(boolean textures) {
 | 
			
		||||
+        MinecraftServer server = MinecraftServer.getServer();
 | 
			
		||||
+        return complete(textures, PaperConfig.isProxyOnlineMode());
 | 
			
		||||
+    }
 | 
			
		||||
+    public boolean complete(boolean textures, boolean onlineMode) {
 | 
			
		||||
+        MinecraftServer server = MinecraftServer.getServer();
 | 
			
		||||
+
 | 
			
		||||
+        boolean isCompleteFromCache = this.completeFromCache(true, onlineMode);
 | 
			
		||||
+        if (onlineMode && (!isCompleteFromCache || textures && !hasTextures())) {
 | 
			
		||||
+            GameProfile result = server.getSessionService().fillProfileProperties(profile, true);
 | 
			
		||||
+            if (result != null) {
 | 
			
		||||
+                copyProfileProperties(result, this.profile, true);
 | 
			
		||||
+            }
 | 
			
		||||
+            if (this.profile.isComplete()) {
 | 
			
		||||
+                server.getProfileCache().add(this.profile);
 | 
			
		||||
+            }
 | 
			
		||||
+        }
 | 
			
		||||
+        return profile.isComplete() && (!onlineMode || !textures || hasTextures());
 | 
			
		||||
+    }
 | 
			
		||||
+
 | 
			
		||||
+    private static void copyProfileProperties(GameProfile source, GameProfile target) {
 | 
			
		||||
+        copyProfileProperties(source, target, false);
 | 
			
		||||
+    }
 | 
			
		||||
+
 | 
			
		||||
+    private static void copyProfileProperties(GameProfile source, GameProfile target, boolean clearTarget) {
 | 
			
		||||
+        PropertyMap sourceProperties = source.getProperties();
 | 
			
		||||
+        PropertyMap targetProperties = target.getProperties();
 | 
			
		||||
+        if (clearTarget) targetProperties.clear();
 | 
			
		||||
+        if (sourceProperties.isEmpty()) {
 | 
			
		||||
+            return;
 | 
			
		||||
+        }
 | 
			
		||||
+
 | 
			
		||||
+        for (Property property : sourceProperties.values()) {
 | 
			
		||||
+            targetProperties.removeAll(property.getName());
 | 
			
		||||
+            targetProperties.put(property.getName(), property);
 | 
			
		||||
+        }
 | 
			
		||||
+    }
 | 
			
		||||
+
 | 
			
		||||
+    private static ProfileProperty toBukkit(Property property) {
 | 
			
		||||
+        return new ProfileProperty(property.getName(), property.getValue(), property.getSignature());
 | 
			
		||||
+    }
 | 
			
		||||
+
 | 
			
		||||
+    public static PlayerProfile asBukkitCopy(GameProfile gameProfile) {
 | 
			
		||||
+        CraftPlayerProfile profile = new CraftPlayerProfile(gameProfile.getId(), gameProfile.getName());
 | 
			
		||||
+        copyProfileProperties(gameProfile, profile.profile);
 | 
			
		||||
+        return profile;
 | 
			
		||||
+    }
 | 
			
		||||
+
 | 
			
		||||
+    public static PlayerProfile asBukkitMirror(GameProfile profile) {
 | 
			
		||||
+        return new CraftPlayerProfile(profile);
 | 
			
		||||
+    }
 | 
			
		||||
+
 | 
			
		||||
+    public static Property asAuthlib(ProfileProperty property) {
 | 
			
		||||
+        return new Property(property.getName(), property.getValue(), property.getSignature());
 | 
			
		||||
+    }
 | 
			
		||||
+
 | 
			
		||||
+    public static GameProfile asAuthlibCopy(PlayerProfile profile) {
 | 
			
		||||
+        CraftPlayerProfile craft = ((CraftPlayerProfile) profile);
 | 
			
		||||
+        return asAuthlib(craft.clone());
 | 
			
		||||
+    }
 | 
			
		||||
+
 | 
			
		||||
+    public static GameProfile asAuthlib(PlayerProfile profile) {
 | 
			
		||||
+        CraftPlayerProfile craft = ((CraftPlayerProfile) profile);
 | 
			
		||||
+        return craft.getGameProfile();
 | 
			
		||||
+    }
 | 
			
		||||
+
 | 
			
		||||
+    private class PropertySet extends AbstractSet<ProfileProperty> {
 | 
			
		||||
+
 | 
			
		||||
+        @Override
 | 
			
		||||
+        @Nonnull
 | 
			
		||||
+        public Iterator<ProfileProperty> iterator() {
 | 
			
		||||
+            return new ProfilePropertyIterator(profile.getProperties().values().iterator());
 | 
			
		||||
+        }
 | 
			
		||||
+
 | 
			
		||||
+        @Override
 | 
			
		||||
+        public int size() {
 | 
			
		||||
+            return profile.getProperties().size();
 | 
			
		||||
+        }
 | 
			
		||||
+
 | 
			
		||||
+        @Override
 | 
			
		||||
+        public boolean add(ProfileProperty property) {
 | 
			
		||||
+            setProperty(property);
 | 
			
		||||
+            return true;
 | 
			
		||||
+        }
 | 
			
		||||
+
 | 
			
		||||
+        @Override
 | 
			
		||||
+        public boolean addAll(Collection<? extends ProfileProperty> c) {
 | 
			
		||||
+            //noinspection unchecked
 | 
			
		||||
+            setProperties((Collection<ProfileProperty>) c);
 | 
			
		||||
+            return true;
 | 
			
		||||
+        }
 | 
			
		||||
+
 | 
			
		||||
+        @Override
 | 
			
		||||
+        public boolean contains(Object o) {
 | 
			
		||||
+            return o instanceof ProfileProperty && profile.getProperties().containsKey(((ProfileProperty) o).getName());
 | 
			
		||||
+        }
 | 
			
		||||
+
 | 
			
		||||
+        private class ProfilePropertyIterator implements Iterator<ProfileProperty> {
 | 
			
		||||
+            private final Iterator<Property> iterator;
 | 
			
		||||
+
 | 
			
		||||
+            ProfilePropertyIterator(Iterator<Property> iterator) {
 | 
			
		||||
+                this.iterator = iterator;
 | 
			
		||||
+            }
 | 
			
		||||
+
 | 
			
		||||
+            @Override
 | 
			
		||||
+            public boolean hasNext() {
 | 
			
		||||
+                return iterator.hasNext();
 | 
			
		||||
+            }
 | 
			
		||||
+
 | 
			
		||||
+            @Override
 | 
			
		||||
+            public ProfileProperty next() {
 | 
			
		||||
+                return toBukkit(iterator.next());
 | 
			
		||||
+            }
 | 
			
		||||
+
 | 
			
		||||
+            @Override
 | 
			
		||||
+            public void remove() {
 | 
			
		||||
+                iterator.remove();
 | 
			
		||||
+            }
 | 
			
		||||
+        }
 | 
			
		||||
+    }
 | 
			
		||||
+}
 | 
			
		||||
diff --git a/src/main/java/com/destroystokyo/paper/profile/PaperAuthenticationService.java b/src/main/java/com/destroystokyo/paper/profile/PaperAuthenticationService.java
 | 
			
		||||
new file mode 100644
 | 
			
		||||
index 0000000000000000000000000000000000000000..d64d45eb01c65864fca1077982d89bc05e0f811b
 | 
			
		||||
--- /dev/null
 | 
			
		||||
+++ b/src/main/java/com/destroystokyo/paper/profile/PaperAuthenticationService.java
 | 
			
		||||
@@ -0,0 +1,31 @@
 | 
			
		||||
+package com.destroystokyo.paper.profile;
 | 
			
		||||
+
 | 
			
		||||
+import com.mojang.authlib.*;
 | 
			
		||||
+import com.mojang.authlib.minecraft.MinecraftSessionService;
 | 
			
		||||
+import com.mojang.authlib.yggdrasil.YggdrasilAuthenticationService;
 | 
			
		||||
+import com.mojang.authlib.yggdrasil.YggdrasilEnvironment;
 | 
			
		||||
+
 | 
			
		||||
+import java.net.Proxy;
 | 
			
		||||
+
 | 
			
		||||
+public class PaperAuthenticationService extends YggdrasilAuthenticationService {
 | 
			
		||||
+    private final Environment environment;
 | 
			
		||||
+    public PaperAuthenticationService(Proxy proxy) {
 | 
			
		||||
+        super(proxy);
 | 
			
		||||
+        this.environment = (Environment)EnvironmentParser.getEnvironmentFromProperties().orElse(YggdrasilEnvironment.PROD);;
 | 
			
		||||
+    }
 | 
			
		||||
+
 | 
			
		||||
+    @Override
 | 
			
		||||
+    public UserAuthentication createUserAuthentication(Agent agent) {
 | 
			
		||||
+        return new PaperUserAuthentication(this, agent);
 | 
			
		||||
+    }
 | 
			
		||||
+
 | 
			
		||||
+    @Override
 | 
			
		||||
+    public MinecraftSessionService createMinecraftSessionService() {
 | 
			
		||||
+        return new PaperMinecraftSessionService(this, this.environment);
 | 
			
		||||
+    }
 | 
			
		||||
+
 | 
			
		||||
+    @Override
 | 
			
		||||
+    public GameProfileRepository createProfileRepository() {
 | 
			
		||||
+        return new PaperGameProfileRepository(this, this.environment);
 | 
			
		||||
+    }
 | 
			
		||||
+}
 | 
			
		||||
diff --git a/src/main/java/com/destroystokyo/paper/profile/PaperGameProfileRepository.java b/src/main/java/com/destroystokyo/paper/profile/PaperGameProfileRepository.java
 | 
			
		||||
new file mode 100644
 | 
			
		||||
index 0000000000000000000000000000000000000000..582c169c85ac66f1f9430f79042e4655f776c157
 | 
			
		||||
--- /dev/null
 | 
			
		||||
+++ b/src/main/java/com/destroystokyo/paper/profile/PaperGameProfileRepository.java
 | 
			
		||||
@@ -0,0 +1,18 @@
 | 
			
		||||
+package com.destroystokyo.paper.profile;
 | 
			
		||||
+
 | 
			
		||||
+import com.mojang.authlib.Agent;
 | 
			
		||||
+import com.mojang.authlib.Environment;
 | 
			
		||||
+import com.mojang.authlib.ProfileLookupCallback;
 | 
			
		||||
+import com.mojang.authlib.yggdrasil.YggdrasilAuthenticationService;
 | 
			
		||||
+import com.mojang.authlib.yggdrasil.YggdrasilGameProfileRepository;
 | 
			
		||||
+
 | 
			
		||||
+public class PaperGameProfileRepository extends YggdrasilGameProfileRepository {
 | 
			
		||||
+    public PaperGameProfileRepository(YggdrasilAuthenticationService authenticationService, Environment environment) {
 | 
			
		||||
+        super(authenticationService, environment);
 | 
			
		||||
+    }
 | 
			
		||||
+
 | 
			
		||||
+    @Override
 | 
			
		||||
+    public void findProfilesByNames(String[] names, Agent agent, ProfileLookupCallback callback) {
 | 
			
		||||
+        super.findProfilesByNames(names, agent, callback);
 | 
			
		||||
+    }
 | 
			
		||||
+}
 | 
			
		||||
diff --git a/src/main/java/com/destroystokyo/paper/profile/PaperMinecraftSessionService.java b/src/main/java/com/destroystokyo/paper/profile/PaperMinecraftSessionService.java
 | 
			
		||||
new file mode 100644
 | 
			
		||||
index 0000000000000000000000000000000000000000..93d73c27340645c7502acafdc0b2cfbc1a759dd8
 | 
			
		||||
--- /dev/null
 | 
			
		||||
+++ b/src/main/java/com/destroystokyo/paper/profile/PaperMinecraftSessionService.java
 | 
			
		||||
@@ -0,0 +1,30 @@
 | 
			
		||||
+package com.destroystokyo.paper.profile;
 | 
			
		||||
+
 | 
			
		||||
+import com.mojang.authlib.Environment;
 | 
			
		||||
+import com.mojang.authlib.GameProfile;
 | 
			
		||||
+import com.mojang.authlib.minecraft.MinecraftProfileTexture;
 | 
			
		||||
+import com.mojang.authlib.yggdrasil.YggdrasilAuthenticationService;
 | 
			
		||||
+import com.mojang.authlib.yggdrasil.YggdrasilMinecraftSessionService;
 | 
			
		||||
+
 | 
			
		||||
+import java.util.Map;
 | 
			
		||||
+
 | 
			
		||||
+public class PaperMinecraftSessionService extends YggdrasilMinecraftSessionService {
 | 
			
		||||
+    protected PaperMinecraftSessionService(YggdrasilAuthenticationService authenticationService, Environment environment) {
 | 
			
		||||
+        super(authenticationService, environment);
 | 
			
		||||
+    }
 | 
			
		||||
+
 | 
			
		||||
+    @Override
 | 
			
		||||
+    public Map<MinecraftProfileTexture.Type, MinecraftProfileTexture> getTextures(GameProfile profile, boolean requireSecure) {
 | 
			
		||||
+        return super.getTextures(profile, requireSecure);
 | 
			
		||||
+    }
 | 
			
		||||
+
 | 
			
		||||
+    @Override
 | 
			
		||||
+    public GameProfile fillProfileProperties(GameProfile profile, boolean requireSecure) {
 | 
			
		||||
+        return super.fillProfileProperties(profile, requireSecure);
 | 
			
		||||
+    }
 | 
			
		||||
+
 | 
			
		||||
+    @Override
 | 
			
		||||
+    protected GameProfile fillGameProfile(GameProfile profile, boolean requireSecure) {
 | 
			
		||||
+        return super.fillGameProfile(profile, requireSecure);
 | 
			
		||||
+    }
 | 
			
		||||
+}
 | 
			
		||||
diff --git a/src/main/java/com/destroystokyo/paper/profile/PaperUserAuthentication.java b/src/main/java/com/destroystokyo/paper/profile/PaperUserAuthentication.java
 | 
			
		||||
new file mode 100644
 | 
			
		||||
index 0000000000000000000000000000000000000000..3cdd06d3af7ff94f1fe1a11b9a9275e17c695a38
 | 
			
		||||
--- /dev/null
 | 
			
		||||
+++ b/src/main/java/com/destroystokyo/paper/profile/PaperUserAuthentication.java
 | 
			
		||||
@@ -0,0 +1,12 @@
 | 
			
		||||
+package com.destroystokyo.paper.profile;
 | 
			
		||||
+
 | 
			
		||||
+import com.mojang.authlib.Agent;
 | 
			
		||||
+import com.mojang.authlib.yggdrasil.YggdrasilAuthenticationService;
 | 
			
		||||
+import com.mojang.authlib.yggdrasil.YggdrasilUserAuthentication;
 | 
			
		||||
+import java.util.UUID;
 | 
			
		||||
+
 | 
			
		||||
+public class PaperUserAuthentication extends YggdrasilUserAuthentication {
 | 
			
		||||
+    public PaperUserAuthentication(YggdrasilAuthenticationService authenticationService, Agent agent) {
 | 
			
		||||
+        super(authenticationService, UUID.randomUUID().toString(), agent);
 | 
			
		||||
+    }
 | 
			
		||||
+}
 | 
			
		||||
diff --git a/src/main/java/net/minecraft/server/MCUtil.java b/src/main/java/net/minecraft/server/MCUtil.java
 | 
			
		||||
index f0fed1d2c297d8d2a6903d2caa219b458c5e43c2..aa96017819712f42e16c7eac57222301600b66a5 100644
 | 
			
		||||
--- a/src/main/java/net/minecraft/server/MCUtil.java
 | 
			
		||||
+++ b/src/main/java/net/minecraft/server/MCUtil.java
 | 
			
		||||
@@ -1,5 +1,7 @@
 | 
			
		||||
 package net.minecraft.server;
 | 
			
		||||
 
 | 
			
		||||
+import com.destroystokyo.paper.profile.CraftPlayerProfile;
 | 
			
		||||
+import com.destroystokyo.paper.profile.PlayerProfile;
 | 
			
		||||
 import com.google.common.util.concurrent.ThreadFactoryBuilder;
 | 
			
		||||
 import it.unimi.dsi.fastutil.objects.ObjectRBTreeSet;
 | 
			
		||||
 import java.lang.ref.Cleaner;
 | 
			
		||||
@@ -11,6 +13,7 @@ import net.minecraft.world.level.ChunkPos;
 | 
			
		||||
 import net.minecraft.world.level.ClipContext;
 | 
			
		||||
 import net.minecraft.world.level.Level;
 | 
			
		||||
 import org.apache.commons.lang.exception.ExceptionUtils;
 | 
			
		||||
+import com.mojang.authlib.GameProfile;
 | 
			
		||||
 import org.bukkit.Location;
 | 
			
		||||
 import org.bukkit.block.BlockFace;
 | 
			
		||||
 import org.bukkit.craftbukkit.CraftWorld;
 | 
			
		||||
@@ -349,6 +352,10 @@ public final class MCUtil {
 | 
			
		||||
         return run.get();
 | 
			
		||||
     }
 | 
			
		||||
 
 | 
			
		||||
+    public static PlayerProfile toBukkit(GameProfile profile) {
 | 
			
		||||
+        return CraftPlayerProfile.asBukkitMirror(profile);
 | 
			
		||||
+    }
 | 
			
		||||
+
 | 
			
		||||
     /**
 | 
			
		||||
      * Calculates distance between 2 entities
 | 
			
		||||
      * @param e1
 | 
			
		||||
diff --git a/src/main/java/net/minecraft/server/Main.java b/src/main/java/net/minecraft/server/Main.java
 | 
			
		||||
index c8385460701395cb5c65fba41335469ffb2d9b9a..fb0b3c5770f66cc3590f5ac4e690a33cb6179be3 100644
 | 
			
		||||
--- a/src/main/java/net/minecraft/server/Main.java
 | 
			
		||||
+++ b/src/main/java/net/minecraft/server/Main.java
 | 
			
		||||
@@ -130,7 +130,7 @@ public class Main {
 | 
			
		||||
             }
 | 
			
		||||
 
 | 
			
		||||
             File file = (File) optionset.valueOf("universe"); // CraftBukkit
 | 
			
		||||
-            YggdrasilAuthenticationService yggdrasilauthenticationservice = new YggdrasilAuthenticationService(Proxy.NO_PROXY);
 | 
			
		||||
+            YggdrasilAuthenticationService yggdrasilauthenticationservice = new com.destroystokyo.paper.profile.PaperAuthenticationService(Proxy.NO_PROXY); // Paper
 | 
			
		||||
             MinecraftSessionService minecraftsessionservice = yggdrasilauthenticationservice.createMinecraftSessionService();
 | 
			
		||||
             GameProfileRepository gameprofilerepository = yggdrasilauthenticationservice.createProfileRepository();
 | 
			
		||||
             GameProfileCache usercache = new GameProfileCache(gameprofilerepository, new File(file, MinecraftServer.USERID_CACHE_FILE.getName()));
 | 
			
		||||
diff --git a/src/main/java/net/minecraft/server/players/GameProfileCache.java b/src/main/java/net/minecraft/server/players/GameProfileCache.java
 | 
			
		||||
index 6e1b7d5b20e9f6ed1b650eb9d6ac9f8c4867b4b7..61405c2b53e03a4b83e2c70c6e4d3739ca9676cb 100644
 | 
			
		||||
--- a/src/main/java/net/minecraft/server/players/GameProfileCache.java
 | 
			
		||||
+++ b/src/main/java/net/minecraft/server/players/GameProfileCache.java
 | 
			
		||||
@@ -135,6 +135,13 @@ public class GameProfileCache {
 | 
			
		||||
         return this.operationCount.incrementAndGet();
 | 
			
		||||
     }
 | 
			
		||||
 
 | 
			
		||||
+    // Paper start
 | 
			
		||||
+    @Nullable public GameProfile getProfileIfCached(String name) {
 | 
			
		||||
+        GameProfileCache.GameProfileInfo entry = this.profilesByName.get(name.toLowerCase(Locale.ROOT));
 | 
			
		||||
+        return entry == null ? null : entry.getProfile();
 | 
			
		||||
+    }
 | 
			
		||||
+    // Paper end
 | 
			
		||||
+
 | 
			
		||||
     public synchronized Optional<GameProfile> get(String name) { // Paper - synchronize
 | 
			
		||||
         String s1 = name.toLowerCase(Locale.ROOT);
 | 
			
		||||
         GameProfileCache.GameProfileInfo usercache_usercacheentry = (GameProfileCache.GameProfileInfo) this.profilesByName.get(s1);
 | 
			
		||||
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
 | 
			
		||||
index e01f800130f183bf10a383e298b7da3d5f11e3a2..a337967295f61f4892a2ae7dd65aeaba75a06172 100644
 | 
			
		||||
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
 | 
			
		||||
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
 | 
			
		||||
@@ -246,6 +246,9 @@ import org.yaml.snakeyaml.error.MarkedYAMLException;
 | 
			
		||||
 
 | 
			
		||||
 import net.md_5.bungee.api.chat.BaseComponent; // Spigot
 | 
			
		||||
 
 | 
			
		||||
+import javax.annotation.Nullable; // Paper
 | 
			
		||||
+import javax.annotation.Nonnull; // Paper
 | 
			
		||||
+
 | 
			
		||||
 public final class CraftServer implements Server {
 | 
			
		||||
     private final String serverName = "Paper"; // Paper
 | 
			
		||||
     private final String serverVersion;
 | 
			
		||||
@@ -2494,5 +2497,24 @@ public final class CraftServer implements Server {
 | 
			
		||||
     public boolean suggestPlayerNamesWhenNullTabCompletions() {
 | 
			
		||||
         return com.destroystokyo.paper.PaperConfig.suggestPlayersWhenNullTabCompletions;
 | 
			
		||||
     }
 | 
			
		||||
+
 | 
			
		||||
+    @Override
 | 
			
		||||
+    public com.destroystokyo.paper.profile.PlayerProfile createProfile(@Nonnull UUID uuid) {
 | 
			
		||||
+        return createProfile(uuid, null);
 | 
			
		||||
+    }
 | 
			
		||||
+
 | 
			
		||||
+    @Override
 | 
			
		||||
+    public com.destroystokyo.paper.profile.PlayerProfile createProfile(@Nonnull String name) {
 | 
			
		||||
+        return createProfile(null, name);
 | 
			
		||||
+    }
 | 
			
		||||
+
 | 
			
		||||
+    @Override
 | 
			
		||||
+    public com.destroystokyo.paper.profile.PlayerProfile createProfile(@Nullable UUID uuid, @Nullable String name) {
 | 
			
		||||
+        Player player = uuid != null ? Bukkit.getPlayer(uuid) : (name != null ? Bukkit.getPlayerExact(name) : null);
 | 
			
		||||
+        if (player != null) {
 | 
			
		||||
+            return new com.destroystokyo.paper.profile.CraftPlayerProfile((CraftPlayer)player);
 | 
			
		||||
+        }
 | 
			
		||||
+        return new com.destroystokyo.paper.profile.CraftPlayerProfile(uuid, name);
 | 
			
		||||
+    }
 | 
			
		||||
     // Paper end
 | 
			
		||||
 }
 | 
			
		||||
diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaSkull.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaSkull.java
 | 
			
		||||
index 9405812b8c308d70de1e26ba55500301b24ecc3c..490df0dcfd0e1e0ab05943410493522f86444ef8 100644
 | 
			
		||||
--- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaSkull.java
 | 
			
		||||
+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaSkull.java
 | 
			
		||||
@@ -80,6 +80,13 @@ class CraftMetaSkull extends CraftMetaItem implements SkullMeta {
 | 
			
		||||
     }
 | 
			
		||||
 
 | 
			
		||||
     private void setProfile(GameProfile profile) {
 | 
			
		||||
+        // Paper start
 | 
			
		||||
+        if (profile != null) {
 | 
			
		||||
+            com.destroystokyo.paper.profile.CraftPlayerProfile paperProfile = new com.destroystokyo.paper.profile.CraftPlayerProfile(profile);
 | 
			
		||||
+            paperProfile.completeFromCache(false, true);
 | 
			
		||||
+            profile = paperProfile.getGameProfile();
 | 
			
		||||
+        }
 | 
			
		||||
+        // Paper end
 | 
			
		||||
         this.profile = profile;
 | 
			
		||||
         this.serializedProfile = (profile == null) ? null : NbtUtils.writeGameProfile(new CompoundTag(), profile);
 | 
			
		||||
     }
 | 
			
		||||
| 
						 | 
				
			
			@ -1,96 +0,0 @@
 | 
			
		|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
 | 
			
		||||
From: Aikar <aikar@aikar.co>
 | 
			
		||||
Date: Sat, 17 Jun 2017 15:18:30 -0400
 | 
			
		||||
Subject: [PATCH] Shoulder Entities Release API
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
diff --git a/src/main/java/net/minecraft/world/entity/player/Player.java b/src/main/java/net/minecraft/world/entity/player/Player.java
 | 
			
		||||
index a4eb5de429f6934cf6f2b771d62db51e328f8987..193d73c53c7c3cc31246dd19c6d7ff4cb39cac1b 100644
 | 
			
		||||
--- a/src/main/java/net/minecraft/world/entity/player/Player.java
 | 
			
		||||
+++ b/src/main/java/net/minecraft/world/entity/player/Player.java
 | 
			
		||||
@@ -1951,20 +1951,44 @@ public abstract class Player extends LivingEntity {
 | 
			
		||||
 
 | 
			
		||||
     }
 | 
			
		||||
 
 | 
			
		||||
+    // Paper start
 | 
			
		||||
+    public Entity releaseLeftShoulderEntity() {
 | 
			
		||||
+        Entity entity = this.spawnEntityFromShoulder0(this.getShoulderEntityLeft());
 | 
			
		||||
+        if (entity != null) {
 | 
			
		||||
+            this.setShoulderEntityLeft(new CompoundTag());
 | 
			
		||||
+        }
 | 
			
		||||
+        return entity;
 | 
			
		||||
+    }
 | 
			
		||||
+
 | 
			
		||||
+    public Entity releaseRightShoulderEntity() {
 | 
			
		||||
+        Entity entity = this.spawnEntityFromShoulder0(this.getShoulderEntityRight());
 | 
			
		||||
+        if (entity != null) {
 | 
			
		||||
+            this.setShoulderEntityRight(new CompoundTag());
 | 
			
		||||
+        }
 | 
			
		||||
+        return entity;
 | 
			
		||||
+    }
 | 
			
		||||
+    // Paper - maintain old signature
 | 
			
		||||
     private boolean spawnEntityFromShoulder(CompoundTag nbttagcompound) { // CraftBukkit void->boolean
 | 
			
		||||
-        if (!this.level.isClientSide && !nbttagcompound.isEmpty()) {
 | 
			
		||||
+        return spawnEntityFromShoulder0(nbttagcompound) != null;
 | 
			
		||||
+    }
 | 
			
		||||
+
 | 
			
		||||
+    // Paper - return entity
 | 
			
		||||
+    private Entity spawnEntityFromShoulder0(@Nullable CompoundTag nbttagcompound) {
 | 
			
		||||
+        if (!this.level.isClientSide && nbttagcompound != null && !nbttagcompound.isEmpty()) {
 | 
			
		||||
             return EntityType.create(nbttagcompound, this.level).map((entity) -> { // CraftBukkit
 | 
			
		||||
                 if (entity instanceof TamableAnimal) {
 | 
			
		||||
                     ((TamableAnimal) entity).setOwnerUUID(this.uuid);
 | 
			
		||||
                 }
 | 
			
		||||
 
 | 
			
		||||
                 entity.setPos(this.getX(), this.getY() + 0.699999988079071D, this.getZ());
 | 
			
		||||
-                return ((ServerLevel) this.level).addEntitySerialized(entity, CreatureSpawnEvent.SpawnReason.SHOULDER_ENTITY); // CraftBukkit
 | 
			
		||||
-            }).orElse(true); // CraftBukkit
 | 
			
		||||
+                boolean addedToWorld = ((ServerLevel) this.level).addEntitySerialized(entity, CreatureSpawnEvent.SpawnReason.SHOULDER_ENTITY); // CraftBukkit
 | 
			
		||||
+                return addedToWorld ? entity : null;
 | 
			
		||||
+            }).orElse(null); // CraftBukkit // Paper - false -> null
 | 
			
		||||
         }
 | 
			
		||||
 
 | 
			
		||||
-        return true; // CraftBukkit
 | 
			
		||||
+        return null; // Paper - return null
 | 
			
		||||
     }
 | 
			
		||||
+    // Paper end
 | 
			
		||||
 
 | 
			
		||||
     @Override
 | 
			
		||||
     public abstract boolean isSpectator();
 | 
			
		||||
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftHumanEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftHumanEntity.java
 | 
			
		||||
index 841dbf4a86b19d7c8ea41930ecb1f88c660fa117..54947f02f29dd3dc546ee0d0f4600630242f003d 100644
 | 
			
		||||
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftHumanEntity.java
 | 
			
		||||
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftHumanEntity.java
 | 
			
		||||
@@ -500,6 +500,32 @@ public class CraftHumanEntity extends CraftLivingEntity implements HumanEntity {
 | 
			
		||||
         this.getHandle().getCooldowns().addCooldown(CraftMagicNumbers.getItem(material), ticks);
 | 
			
		||||
     }
 | 
			
		||||
 
 | 
			
		||||
+    // Paper start
 | 
			
		||||
+    @Override
 | 
			
		||||
+    public org.bukkit.entity.Entity releaseLeftShoulderEntity() {
 | 
			
		||||
+        if (!getHandle().getShoulderEntityLeft().isEmpty()) {
 | 
			
		||||
+            Entity entity = getHandle().releaseLeftShoulderEntity();
 | 
			
		||||
+            if (entity != null) {
 | 
			
		||||
+                return entity.getBukkitEntity();
 | 
			
		||||
+            }
 | 
			
		||||
+        }
 | 
			
		||||
+
 | 
			
		||||
+        return null;
 | 
			
		||||
+    }
 | 
			
		||||
+
 | 
			
		||||
+    @Override
 | 
			
		||||
+    public org.bukkit.entity.Entity releaseRightShoulderEntity() {
 | 
			
		||||
+        if (!getHandle().getShoulderEntityRight().isEmpty()) {
 | 
			
		||||
+            Entity entity = getHandle().releaseRightShoulderEntity();
 | 
			
		||||
+            if (entity != null) {
 | 
			
		||||
+                return entity.getBukkitEntity();
 | 
			
		||||
+            }
 | 
			
		||||
+        }
 | 
			
		||||
+
 | 
			
		||||
+        return null;
 | 
			
		||||
+    }
 | 
			
		||||
+    // Paper end
 | 
			
		||||
+
 | 
			
		||||
     @Override
 | 
			
		||||
     public boolean discoverRecipe(NamespacedKey recipe) {
 | 
			
		||||
         return this.discoverRecipes(Arrays.asList(recipe)) != 0;
 | 
			
		||||
| 
						 | 
				
			
			@ -1,81 +0,0 @@
 | 
			
		|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
 | 
			
		||||
From: Aikar <aikar@aikar.co>
 | 
			
		||||
Date: Sat, 17 Jun 2017 17:00:32 -0400
 | 
			
		||||
Subject: [PATCH] Profile Lookup Events
 | 
			
		||||
 | 
			
		||||
Adds a Pre Lookup Event and a Post Lookup Event so that plugins may prefill in profile data, and cache the responses from
 | 
			
		||||
profiles that had to be looked up.
 | 
			
		||||
 | 
			
		||||
diff --git a/src/main/java/com/destroystokyo/paper/profile/PaperGameProfileRepository.java b/src/main/java/com/destroystokyo/paper/profile/PaperGameProfileRepository.java
 | 
			
		||||
index 582c169c85ac66f1f9430f79042e4655f776c157..08fdb681a68e8be6e4062af0630957ce3e524806 100644
 | 
			
		||||
--- a/src/main/java/com/destroystokyo/paper/profile/PaperGameProfileRepository.java
 | 
			
		||||
+++ b/src/main/java/com/destroystokyo/paper/profile/PaperGameProfileRepository.java
 | 
			
		||||
@@ -1,11 +1,16 @@
 | 
			
		||||
 package com.destroystokyo.paper.profile;
 | 
			
		||||
 
 | 
			
		||||
+import com.destroystokyo.paper.event.profile.LookupProfileEvent;
 | 
			
		||||
+import com.destroystokyo.paper.event.profile.PreLookupProfileEvent;
 | 
			
		||||
+import com.google.common.collect.Sets;
 | 
			
		||||
 import com.mojang.authlib.Agent;
 | 
			
		||||
 import com.mojang.authlib.Environment;
 | 
			
		||||
+import com.mojang.authlib.GameProfile;
 | 
			
		||||
 import com.mojang.authlib.ProfileLookupCallback;
 | 
			
		||||
 import com.mojang.authlib.yggdrasil.YggdrasilAuthenticationService;
 | 
			
		||||
 import com.mojang.authlib.yggdrasil.YggdrasilGameProfileRepository;
 | 
			
		||||
 
 | 
			
		||||
+import java.util.Set;
 | 
			
		||||
 public class PaperGameProfileRepository extends YggdrasilGameProfileRepository {
 | 
			
		||||
     public PaperGameProfileRepository(YggdrasilAuthenticationService authenticationService, Environment environment) {
 | 
			
		||||
         super(authenticationService, environment);
 | 
			
		||||
@@ -13,6 +18,50 @@ public class PaperGameProfileRepository extends YggdrasilGameProfileRepository {
 | 
			
		||||
 
 | 
			
		||||
     @Override
 | 
			
		||||
     public void findProfilesByNames(String[] names, Agent agent, ProfileLookupCallback callback) {
 | 
			
		||||
-        super.findProfilesByNames(names, agent, callback);
 | 
			
		||||
+        Set<String> unfoundNames = Sets.newHashSet();
 | 
			
		||||
+        for (String name : names) {
 | 
			
		||||
+            PreLookupProfileEvent event = new PreLookupProfileEvent(name);
 | 
			
		||||
+            event.callEvent();
 | 
			
		||||
+            if (event.getUUID() != null) {
 | 
			
		||||
+                // Plugin provided UUI, we can skip network call.
 | 
			
		||||
+                GameProfile gameprofile = new GameProfile(event.getUUID(), name);
 | 
			
		||||
+                // We might even have properties!
 | 
			
		||||
+                Set<ProfileProperty> profileProperties = event.getProfileProperties();
 | 
			
		||||
+                if (!profileProperties.isEmpty()) {
 | 
			
		||||
+                    for (ProfileProperty property : profileProperties) {
 | 
			
		||||
+                        gameprofile.getProperties().put(property.getName(), CraftPlayerProfile.asAuthlib(property));
 | 
			
		||||
+                    }
 | 
			
		||||
+                }
 | 
			
		||||
+                callback.onProfileLookupSucceeded(gameprofile);
 | 
			
		||||
+            } else {
 | 
			
		||||
+                unfoundNames.add(name);
 | 
			
		||||
+            }
 | 
			
		||||
+        }
 | 
			
		||||
+
 | 
			
		||||
+        // Some things were not found.... Proceed to look up.
 | 
			
		||||
+        if (!unfoundNames.isEmpty()) {
 | 
			
		||||
+            String[] namesArr = unfoundNames.toArray(new String[unfoundNames.size()]);
 | 
			
		||||
+            super.findProfilesByNames(namesArr, agent, new PreProfileLookupCallback(callback));
 | 
			
		||||
+        }
 | 
			
		||||
+    }
 | 
			
		||||
+
 | 
			
		||||
+    private static class PreProfileLookupCallback implements ProfileLookupCallback {
 | 
			
		||||
+        private final ProfileLookupCallback callback;
 | 
			
		||||
+
 | 
			
		||||
+        PreProfileLookupCallback(ProfileLookupCallback callback) {
 | 
			
		||||
+            this.callback = callback;
 | 
			
		||||
+        }
 | 
			
		||||
+
 | 
			
		||||
+        @Override
 | 
			
		||||
+        public void onProfileLookupSucceeded(GameProfile gameProfile) {
 | 
			
		||||
+            PlayerProfile from = CraftPlayerProfile.asBukkitMirror(gameProfile);
 | 
			
		||||
+            new LookupProfileEvent(from).callEvent();
 | 
			
		||||
+            callback.onProfileLookupSucceeded(gameProfile);
 | 
			
		||||
+        }
 | 
			
		||||
+
 | 
			
		||||
+        @Override
 | 
			
		||||
+        public void onProfileLookupFailed(GameProfile gameProfile, Exception e) {
 | 
			
		||||
+            callback.onProfileLookupFailed(gameProfile, e);
 | 
			
		||||
+        }
 | 
			
		||||
     }
 | 
			
		||||
 }
 | 
			
		||||
| 
						 | 
				
			
			@ -1,23 +0,0 @@
 | 
			
		|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
 | 
			
		||||
From: Zach Brown <zach.brown@destroystokyo.com>
 | 
			
		||||
Date: Sun, 2 Jul 2017 21:35:56 -0500
 | 
			
		||||
Subject: [PATCH] Block player logins during server shutdown
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
diff --git a/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java
 | 
			
		||||
index a60b40d8cc802456374625af216c27998f8348b3..ab3409dd3a7671b46cba210cfa326311d10a7ef4 100644
 | 
			
		||||
--- a/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java
 | 
			
		||||
+++ b/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java
 | 
			
		||||
@@ -69,6 +69,12 @@ public class ServerLoginPacketListenerImpl implements ServerLoginPacketListener
 | 
			
		||||
     }
 | 
			
		||||
 
 | 
			
		||||
     public void tick() {
 | 
			
		||||
+        // Paper start - Do not allow logins while the server is shutting down
 | 
			
		||||
+        if (!MinecraftServer.getServer().isRunning()) {
 | 
			
		||||
+            this.disconnect(org.bukkit.craftbukkit.util.CraftChatMessage.fromString(org.spigotmc.SpigotConfig.restartMessage)[0]);
 | 
			
		||||
+            return;
 | 
			
		||||
+        }
 | 
			
		||||
+        // Paper end
 | 
			
		||||
         if (this.state == ServerLoginPacketListenerImpl.State.READY_TO_ACCEPT) {
 | 
			
		||||
             this.handleAcceptedLogin();
 | 
			
		||||
         } else if (this.state == ServerLoginPacketListenerImpl.State.DELAY_ACCEPT) {
 | 
			
		||||
| 
						 | 
				
			
			@ -1,65 +0,0 @@
 | 
			
		|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
 | 
			
		||||
From: BillyGalbreath <Blake.Galbreath@GMail.com>
 | 
			
		||||
Date: Sun, 18 Jun 2017 18:17:05 -0500
 | 
			
		||||
Subject: [PATCH] Entity#fromMobSpawner()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
 | 
			
		||||
index bf52f5f9b13e8b4eb3c7ee19b0e57bb872d2af1d..3b2334e9ba44205a4e0ec12045eab4fad91bb15a 100644
 | 
			
		||||
--- a/src/main/java/net/minecraft/world/entity/Entity.java
 | 
			
		||||
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
 | 
			
		||||
@@ -322,6 +322,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, n
 | 
			
		||||
     public final org.spigotmc.ActivationRange.ActivationType activationType = org.spigotmc.ActivationRange.initializeEntityActivationType(this);
 | 
			
		||||
     public final boolean defaultActivationState;
 | 
			
		||||
     public long activatedTick = Integer.MIN_VALUE;
 | 
			
		||||
+    public boolean spawnedViaMobSpawner; // Paper - Yes this name is similar to above, upstream took the better one
 | 
			
		||||
     protected int numCollisions = 0; // Paper
 | 
			
		||||
     public void inactiveTick() { }
 | 
			
		||||
     // Spigot end
 | 
			
		||||
@@ -1868,6 +1869,10 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, n
 | 
			
		||||
                 }
 | 
			
		||||
                 nbt.put("Paper.Origin", this.newDoubleList(origin.getX(), origin.getY(), origin.getZ()));
 | 
			
		||||
             }
 | 
			
		||||
+            // Save entity's from mob spawner status
 | 
			
		||||
+            if (spawnedViaMobSpawner) {
 | 
			
		||||
+                nbt.putBoolean("Paper.FromMobSpawner", true);
 | 
			
		||||
+            }
 | 
			
		||||
             // Paper end
 | 
			
		||||
             return nbt;
 | 
			
		||||
         } catch (Throwable throwable) {
 | 
			
		||||
@@ -2007,6 +2012,8 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, n
 | 
			
		||||
                 this.originWorld = originWorld;
 | 
			
		||||
                 origin = new org.bukkit.util.Vector(originTag.getDouble(0), originTag.getDouble(1), originTag.getDouble(2));
 | 
			
		||||
             }
 | 
			
		||||
+
 | 
			
		||||
+            spawnedViaMobSpawner = nbt.getBoolean("Paper.FromMobSpawner"); // Restore entity's from mob spawner status
 | 
			
		||||
             // Paper end
 | 
			
		||||
 
 | 
			
		||||
         } catch (Throwable throwable) {
 | 
			
		||||
diff --git a/src/main/java/net/minecraft/world/level/BaseSpawner.java b/src/main/java/net/minecraft/world/level/BaseSpawner.java
 | 
			
		||||
index 65744b55f06c225745e3e145e5f5e60ebefd304c..e720c751518af3f38fba0c1b22e4c01a46561b00 100644
 | 
			
		||||
--- a/src/main/java/net/minecraft/world/level/BaseSpawner.java
 | 
			
		||||
+++ b/src/main/java/net/minecraft/world/level/BaseSpawner.java
 | 
			
		||||
@@ -167,6 +167,7 @@ public abstract class BaseSpawner {
 | 
			
		||||
                             }
 | 
			
		||||
                             // Spigot End
 | 
			
		||||
                         }
 | 
			
		||||
+                        entity.spawnedViaMobSpawner = true; // Paper
 | 
			
		||||
                         // Spigot Start
 | 
			
		||||
                         if (org.bukkit.craftbukkit.event.CraftEventFactory.callSpawnerSpawnEvent(entity, pos).isCancelled()) {
 | 
			
		||||
                             Entity vehicle = entity.getVehicle();
 | 
			
		||||
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java
 | 
			
		||||
index 78b3be2ea6351cb6375b77340218615aa75b87f5..a83d15178f71b1c4c6d8d49f7621eddd66577251 100644
 | 
			
		||||
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java
 | 
			
		||||
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java
 | 
			
		||||
@@ -1192,5 +1192,10 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity {
 | 
			
		||||
         //noinspection ConstantConditions
 | 
			
		||||
         return originVector.toLocation(world);
 | 
			
		||||
     }
 | 
			
		||||
+
 | 
			
		||||
+    @Override
 | 
			
		||||
+    public boolean fromMobSpawner() {
 | 
			
		||||
+        return getHandle().spawnedViaMobSpawner;
 | 
			
		||||
+    }
 | 
			
		||||
     // Paper end
 | 
			
		||||
 }
 | 
			
		||||
| 
						 | 
				
			
			@ -1,59 +0,0 @@
 | 
			
		|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
 | 
			
		||||
From: Aikar <aikar@aikar.co>
 | 
			
		||||
Date: Sat, 10 Dec 2016 16:24:06 -0500
 | 
			
		||||
Subject: [PATCH] Improve the Saddle API for Horses
 | 
			
		||||
 | 
			
		||||
Not all horses with Saddles have armor. This lets us break up the horses with saddles
 | 
			
		||||
and access their saddle state separately from an interface shared with Armor.
 | 
			
		||||
 | 
			
		||||
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftAbstractHorse.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftAbstractHorse.java
 | 
			
		||||
index 6f473dbf949552afd288382b36223ea036eaa857..27a1ca43792644fc239af81dea5510f25d3328e9 100644
 | 
			
		||||
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftAbstractHorse.java
 | 
			
		||||
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftAbstractHorse.java
 | 
			
		||||
@@ -5,6 +5,7 @@ import net.minecraft.world.entity.ai.attributes.Attributes;
 | 
			
		||||
 import org.apache.commons.lang.Validate;
 | 
			
		||||
 import org.bukkit.craftbukkit.CraftServer;
 | 
			
		||||
 import org.bukkit.craftbukkit.inventory.CraftInventoryAbstractHorse;
 | 
			
		||||
+import org.bukkit.craftbukkit.inventory.CraftSaddledInventory;
 | 
			
		||||
 import org.bukkit.entity.AbstractHorse;
 | 
			
		||||
 import org.bukkit.entity.AnimalTamer;
 | 
			
		||||
 import org.bukkit.entity.Horse;
 | 
			
		||||
@@ -98,6 +99,6 @@ public abstract class CraftAbstractHorse extends CraftAnimals implements Abstrac
 | 
			
		||||
 
 | 
			
		||||
     @Override
 | 
			
		||||
     public AbstractHorseInventory getInventory() {
 | 
			
		||||
-        return new CraftInventoryAbstractHorse(this.getHandle().inventory);
 | 
			
		||||
+        return new CraftSaddledInventory(getHandle().inventory);
 | 
			
		||||
     }
 | 
			
		||||
 }
 | 
			
		||||
diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryHorse.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryHorse.java
 | 
			
		||||
index 7013059856c2471dc34112a1a2068b96b809dd96..b72b4260fc1c0e9928d70f97589d8db00849b9e8 100644
 | 
			
		||||
--- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryHorse.java
 | 
			
		||||
+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryHorse.java
 | 
			
		||||
@@ -4,7 +4,7 @@ import net.minecraft.world.Container;
 | 
			
		||||
 import org.bukkit.inventory.HorseInventory;
 | 
			
		||||
 import org.bukkit.inventory.ItemStack;
 | 
			
		||||
 
 | 
			
		||||
-public class CraftInventoryHorse extends CraftInventoryAbstractHorse implements HorseInventory {
 | 
			
		||||
+public class CraftInventoryHorse extends CraftSaddledInventory implements HorseInventory {
 | 
			
		||||
 
 | 
			
		||||
     public CraftInventoryHorse(Container inventory) {
 | 
			
		||||
         super(inventory);
 | 
			
		||||
diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftSaddledInventory.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftSaddledInventory.java
 | 
			
		||||
new file mode 100644
 | 
			
		||||
index 0000000000000000000000000000000000000000..3a617c07d445bacf5a13e0e3ff6481823cfc8477
 | 
			
		||||
--- /dev/null
 | 
			
		||||
+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftSaddledInventory.java
 | 
			
		||||
@@ -0,0 +1,12 @@
 | 
			
		||||
+package org.bukkit.craftbukkit.inventory;
 | 
			
		||||
+
 | 
			
		||||
+import net.minecraft.world.Container;
 | 
			
		||||
+import org.bukkit.inventory.SaddledHorseInventory;
 | 
			
		||||
+
 | 
			
		||||
+public class CraftSaddledInventory extends CraftInventoryAbstractHorse implements SaddledHorseInventory {
 | 
			
		||||
+
 | 
			
		||||
+    public CraftSaddledInventory(Container inventory) {
 | 
			
		||||
+        super(inventory);
 | 
			
		||||
+    }
 | 
			
		||||
+
 | 
			
		||||
+}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,24 +0,0 @@
 | 
			
		|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
 | 
			
		||||
From: Aikar <aikar@aikar.co>
 | 
			
		||||
Date: Wed, 4 May 2016 22:43:12 -0400
 | 
			
		||||
Subject: [PATCH] Implement ensureServerConversions API
 | 
			
		||||
 | 
			
		||||
This will take a Bukkit ItemStack and run it through any conversions a server process would perform on it,
 | 
			
		||||
to ensure it meets latest minecraft expectations.
 | 
			
		||||
 | 
			
		||||
diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemFactory.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemFactory.java
 | 
			
		||||
index 69af359e0160480b77886ca35d8f8f3135f06455..73da393b26a7afd766b23716da454b0b68c26d5b 100644
 | 
			
		||||
--- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemFactory.java
 | 
			
		||||
+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemFactory.java
 | 
			
		||||
@@ -356,5 +356,11 @@ public final class CraftItemFactory implements ItemFactory {
 | 
			
		||||
     public net.kyori.adventure.text.@org.jetbrains.annotations.NotNull Component displayName(@org.jetbrains.annotations.NotNull ItemStack itemStack) {
 | 
			
		||||
         return io.papermc.paper.adventure.PaperAdventure.asAdventure(CraftItemStack.asNMSCopy(itemStack).getDisplayName());
 | 
			
		||||
     }
 | 
			
		||||
+
 | 
			
		||||
+    // Paper start
 | 
			
		||||
+    @Override
 | 
			
		||||
+    public ItemStack ensureServerConversions(ItemStack item) {
 | 
			
		||||
+        return CraftItemStack.asCraftMirror(CraftItemStack.asNMSCopy(item));
 | 
			
		||||
+    }
 | 
			
		||||
     // Paper end
 | 
			
		||||
 }
 | 
			
		||||
| 
						 | 
				
			
			@ -1,32 +0,0 @@
 | 
			
		|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
 | 
			
		||||
From: Aikar <aikar@aikar.co>
 | 
			
		||||
Date: Wed, 4 May 2016 23:59:38 -0400
 | 
			
		||||
Subject: [PATCH] Implement getI18NDisplayName
 | 
			
		||||
 | 
			
		||||
Gets the Display name as seen in the Client.
 | 
			
		||||
Currently the server only supports the English language. To override this,
 | 
			
		||||
You must replace the language file embedded in the server jar.
 | 
			
		||||
 | 
			
		||||
diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemFactory.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemFactory.java
 | 
			
		||||
index 73da393b26a7afd766b23716da454b0b68c26d5b..c50d3cee3dfebb62ad30f8d4efe23b5c7c9f2b57 100644
 | 
			
		||||
--- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemFactory.java
 | 
			
		||||
+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemFactory.java
 | 
			
		||||
@@ -362,5 +362,18 @@ public final class CraftItemFactory implements ItemFactory {
 | 
			
		||||
     public ItemStack ensureServerConversions(ItemStack item) {
 | 
			
		||||
         return CraftItemStack.asCraftMirror(CraftItemStack.asNMSCopy(item));
 | 
			
		||||
     }
 | 
			
		||||
+
 | 
			
		||||
+    @Override
 | 
			
		||||
+    public String getI18NDisplayName(ItemStack item) {
 | 
			
		||||
+        net.minecraft.world.item.ItemStack nms = null;
 | 
			
		||||
+        if (item instanceof CraftItemStack) {
 | 
			
		||||
+            nms = ((CraftItemStack) item).handle;
 | 
			
		||||
+        }
 | 
			
		||||
+        if (nms == null) {
 | 
			
		||||
+            nms = CraftItemStack.asNMSCopy(item);
 | 
			
		||||
+        }
 | 
			
		||||
+
 | 
			
		||||
+        return nms != null ? net.minecraft.locale.Language.getInstance().getOrDefault(nms.getItem().getDescriptionId()) : null;
 | 
			
		||||
+    }
 | 
			
		||||
     // Paper end
 | 
			
		||||
 }
 | 
			
		||||
| 
						 | 
				
			
			@ -1,50 +0,0 @@
 | 
			
		|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
 | 
			
		||||
From: Aikar <aikar@aikar.co>
 | 
			
		||||
Date: Mon, 3 Jul 2017 18:11:10 -0500
 | 
			
		||||
Subject: [PATCH] ProfileWhitelistVerifyEvent
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java
 | 
			
		||||
index 0eea43c994e76b466fdda8ecd145d0b1c9273cea..17f56157d60d33695c4eac0e4fc94120a2101214 100644
 | 
			
		||||
--- a/src/main/java/net/minecraft/server/players/PlayerList.java
 | 
			
		||||
+++ b/src/main/java/net/minecraft/server/players/PlayerList.java
 | 
			
		||||
@@ -615,9 +615,9 @@ public abstract class PlayerList {
 | 
			
		||||
 
 | 
			
		||||
             // return chatmessage;
 | 
			
		||||
             event.disallow(PlayerLoginEvent.Result.KICK_BANNED, PaperAdventure.asAdventure(chatmessage)); // Paper - Adventure
 | 
			
		||||
-        } else if (!this.isWhiteListed(gameprofile)) {
 | 
			
		||||
-            chatmessage = new TranslatableComponent("multiplayer.disconnect.not_whitelisted");
 | 
			
		||||
-            event.disallow(PlayerLoginEvent.Result.KICK_WHITELIST, PaperAdventure.LEGACY_SECTION_UXRC.deserialize(org.spigotmc.SpigotConfig.whitelistMessage)); // Spigot // Paper - Adventure
 | 
			
		||||
+        } else if (!this.isWhitelisted(gameprofile, event)) { // Paper
 | 
			
		||||
+            //chatmessage = new ChatMessage("multiplayer.disconnect.not_whitelisted"); // Paper
 | 
			
		||||
+            //event.disallow(PlayerLoginEvent.Result.KICK_WHITELIST, org.spigotmc.SpigotConfig.whitelistMessage); // Spigot // Paper - moved to isWhitelisted
 | 
			
		||||
         } else if (this.getIpBans().isBanned(socketaddress) && !this.getIpBans().get(socketaddress).hasExpired()) {
 | 
			
		||||
             IpBanListEntry ipbanentry = this.ipBans.get(socketaddress);
 | 
			
		||||
 
 | 
			
		||||
@@ -998,9 +998,25 @@ public abstract class PlayerList {
 | 
			
		||||
         this.server.getCommands().sendCommands(player);
 | 
			
		||||
     }
 | 
			
		||||
 
 | 
			
		||||
+    // Paper start
 | 
			
		||||
     public boolean isWhiteListed(GameProfile profile) {
 | 
			
		||||
-        return !this.doWhiteList || this.ops.contains(profile) || this.whitelist.contains(profile);
 | 
			
		||||
+        return isWhitelisted(profile, null);
 | 
			
		||||
     }
 | 
			
		||||
+    public boolean isWhitelisted(GameProfile gameprofile, org.bukkit.event.player.PlayerLoginEvent loginEvent) {
 | 
			
		||||
+        boolean isOp = this.ops.contains(gameprofile);
 | 
			
		||||
+        boolean isWhitelisted = !this.doWhiteList || isOp || this.whitelist.contains(gameprofile);
 | 
			
		||||
+        final com.destroystokyo.paper.event.profile.ProfileWhitelistVerifyEvent event;
 | 
			
		||||
+        event = new com.destroystokyo.paper.event.profile.ProfileWhitelistVerifyEvent(net.minecraft.server.MCUtil.toBukkit(gameprofile), this.doWhiteList, isWhitelisted, isOp, org.spigotmc.SpigotConfig.whitelistMessage);
 | 
			
		||||
+        event.callEvent();
 | 
			
		||||
+        if (!event.isWhitelisted()) {
 | 
			
		||||
+            if (loginEvent != null) {
 | 
			
		||||
+                loginEvent.disallow(PlayerLoginEvent.Result.KICK_WHITELIST, PaperAdventure.LEGACY_SECTION_UXRC.deserialize(event.getKickMessage() == null ? org.spigotmc.SpigotConfig.whitelistMessage : event.getKickMessage()));
 | 
			
		||||
+            }
 | 
			
		||||
+            return false;
 | 
			
		||||
+        }
 | 
			
		||||
+        return true;
 | 
			
		||||
+    }
 | 
			
		||||
+    // Paper end
 | 
			
		||||
 
 | 
			
		||||
     public boolean isOp(GameProfile profile) {
 | 
			
		||||
         return this.ops.contains(profile) || this.server.isSingleplayerOwner(profile) && this.server.getWorldData().getAllowCommands() || this.allowCheatsForAllPlayers;
 | 
			
		||||
| 
						 | 
				
			
			@ -1,52 +0,0 @@
 | 
			
		|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
 | 
			
		||||
From: Kyle Wood <kyle@denwav.dev>
 | 
			
		||||
Date: Sun, 6 Aug 2017 17:17:53 -0500
 | 
			
		||||
Subject: [PATCH] Fix this stupid bullshit
 | 
			
		||||
 | 
			
		||||
Disable the 15 second sleep when the server jar hasn't been rebuilt within a period of time.
 | 
			
		||||
 | 
			
		||||
modified in order to prevent merge conflicts when Spigot changes/disables the warning,
 | 
			
		||||
and to provide some level of hint without being disruptive.
 | 
			
		||||
 | 
			
		||||
diff --git a/src/main/java/net/minecraft/server/Bootstrap.java b/src/main/java/net/minecraft/server/Bootstrap.java
 | 
			
		||||
index c7d5018cb6acef12e6da90626c75543ac279a101..64576ddd8363be55755fa50d1c16d95e4e02402d 100644
 | 
			
		||||
--- a/src/main/java/net/minecraft/server/Bootstrap.java
 | 
			
		||||
+++ b/src/main/java/net/minecraft/server/Bootstrap.java
 | 
			
		||||
@@ -42,7 +42,7 @@ public class Bootstrap {
 | 
			
		||||
     public static void bootStrap() {
 | 
			
		||||
         if (!Bootstrap.isBootstrapped) {
 | 
			
		||||
             // CraftBukkit start
 | 
			
		||||
-            String name = Bootstrap.class.getSimpleName();
 | 
			
		||||
+            /*String name = Bootstrap.class.getSimpleName(); // Paper - actually, I don't think this class should ever have been called DispenserRegistry, that's a stupid name, bootstrap is waaay better
 | 
			
		||||
             switch (name) {
 | 
			
		||||
                 case "DispenserRegistry":
 | 
			
		||||
                     break;
 | 
			
		||||
@@ -56,7 +56,7 @@ public class Bootstrap {
 | 
			
		||||
                     System.err.println("*** WARNING: This server jar is unsupported, use at your own risk. ***");
 | 
			
		||||
                     System.err.println("**********************************************************************");
 | 
			
		||||
                     break;
 | 
			
		||||
-            }
 | 
			
		||||
+            }*/ // Paper
 | 
			
		||||
             // CraftBukkit end
 | 
			
		||||
             Bootstrap.isBootstrapped = true;
 | 
			
		||||
             if (Registry.REGISTRY.keySet().isEmpty()) {
 | 
			
		||||
diff --git a/src/main/java/org/bukkit/craftbukkit/Main.java b/src/main/java/org/bukkit/craftbukkit/Main.java
 | 
			
		||||
index bbf552bf586de94d8dfc5fb1c18e0af6f75aebe1..1da136f365664d4f8ace3d2d135b19eb97e55304 100644
 | 
			
		||||
--- a/src/main/java/org/bukkit/craftbukkit/Main.java
 | 
			
		||||
+++ b/src/main/java/org/bukkit/craftbukkit/Main.java
 | 
			
		||||
@@ -228,10 +228,12 @@ public class Main {
 | 
			
		||||
                     Calendar deadline = Calendar.getInstance();
 | 
			
		||||
                     deadline.add(Calendar.DAY_OF_YEAR, -28);
 | 
			
		||||
                     if (buildDate.before(deadline.getTime())) {
 | 
			
		||||
-                        System.err.println("*** Error, this build is outdated ***");
 | 
			
		||||
+                        // Paper start - This is some stupid bullshit
 | 
			
		||||
+                        System.err.println("*** Warning, you've not updated in a while! ***");
 | 
			
		||||
                         System.err.println("*** Please download a new build as per instructions from https://papermc.io/downloads ***"); // Paper
 | 
			
		||||
-                        System.err.println("*** Server will start in 20 seconds ***");
 | 
			
		||||
-                        Thread.sleep(TimeUnit.SECONDS.toMillis(20));
 | 
			
		||||
+                        //System.err.println("*** Server will start in 20 seconds ***");
 | 
			
		||||
+                        //Thread.sleep(TimeUnit.SECONDS.toMillis(20));
 | 
			
		||||
+                        // Paper End
 | 
			
		||||
                     }
 | 
			
		||||
                 }
 | 
			
		||||
 
 | 
			
		||||
| 
						 | 
				
			
			@ -1,35 +0,0 @@
 | 
			
		|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
 | 
			
		||||
From: BillyGalbreath <Blake.Galbreath@GMail.com>
 | 
			
		||||
Date: Mon, 31 Jul 2017 01:49:48 -0500
 | 
			
		||||
Subject: [PATCH] LivingEntity#setKiller
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java
 | 
			
		||||
index e955126fc82dfcdadb824c8d2d15e8b1f33bc67f..90b70935242757b5c302bac7777eb1428d69619e 100644
 | 
			
		||||
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java
 | 
			
		||||
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java
 | 
			
		||||
@@ -8,6 +8,7 @@ import java.util.Iterator;
 | 
			
		||||
 import java.util.List;
 | 
			
		||||
 import java.util.Set;
 | 
			
		||||
 import java.util.UUID;
 | 
			
		||||
+import net.minecraft.server.level.ServerPlayer;
 | 
			
		||||
 import net.minecraft.world.InteractionHand;
 | 
			
		||||
 import net.minecraft.world.damagesource.DamageSource;
 | 
			
		||||
 import net.minecraft.world.effect.MobEffect;
 | 
			
		||||
@@ -344,6 +345,16 @@ public class CraftLivingEntity extends CraftEntity implements LivingEntity {
 | 
			
		||||
         return this.getHandle().lastHurtByPlayer == null ? null : (Player) this.getHandle().lastHurtByPlayer.getBukkitEntity();
 | 
			
		||||
     }
 | 
			
		||||
 
 | 
			
		||||
+    // Paper start
 | 
			
		||||
+    @Override
 | 
			
		||||
+    public void setKiller(Player killer) {
 | 
			
		||||
+        ServerPlayer entityPlayer = killer == null ? null : ((CraftPlayer) killer).getHandle();
 | 
			
		||||
+        getHandle().lastHurtByPlayer = entityPlayer;
 | 
			
		||||
+        getHandle().lastHurtByMob = entityPlayer;
 | 
			
		||||
+        getHandle().lastHurtByPlayerTime = entityPlayer == null ? 0 : 100; // 100 value taken from EntityLiving#damageEntity
 | 
			
		||||
+    }
 | 
			
		||||
+    // Paper end
 | 
			
		||||
+
 | 
			
		||||
     @Override
 | 
			
		||||
     public boolean addPotionEffect(PotionEffect effect) {
 | 
			
		||||
         return this.addPotionEffect(effect, false);
 | 
			
		||||
| 
						 | 
				
			
			@ -1,19 +0,0 @@
 | 
			
		|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
 | 
			
		||||
From: BillyGalbreath <Blake.Galbreath@GMail.com>
 | 
			
		||||
Date: Mon, 31 Jul 2017 01:54:40 -0500
 | 
			
		||||
Subject: [PATCH] Ocelot despawns should honor nametags and leash
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
diff --git a/src/main/java/net/minecraft/world/entity/animal/Ocelot.java b/src/main/java/net/minecraft/world/entity/animal/Ocelot.java
 | 
			
		||||
index b223b54c80bdc2c9b620e5c9e44cab0c9abdd44c..49122e7baa5c0cd3691bcb48176fdefbdb79026b 100644
 | 
			
		||||
--- a/src/main/java/net/minecraft/world/entity/animal/Ocelot.java
 | 
			
		||||
+++ b/src/main/java/net/minecraft/world/entity/animal/Ocelot.java
 | 
			
		||||
@@ -133,7 +133,7 @@ public class Ocelot extends Animal {
 | 
			
		||||
 
 | 
			
		||||
     @Override
 | 
			
		||||
     public boolean removeWhenFarAway(double distanceSquared) {
 | 
			
		||||
-        return !this.isTrusting() /*&& this.ticksLived > 2400*/; // CraftBukkit
 | 
			
		||||
+        return !this.isTrusting() && !this.hasCustomName() && !this.isLeashed() /*&& this.ticksLived > 2400*/; // CraftBukkit // Paper - honor name and leash
 | 
			
		||||
     }
 | 
			
		||||
 
 | 
			
		||||
     public static AttributeSupplier.Builder createAttributes() {
 | 
			
		||||
| 
						 | 
				
			
			@ -1,27 +0,0 @@
 | 
			
		|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
 | 
			
		||||
From: BillyGalbreath <Blake.Galbreath@GMail.com>
 | 
			
		||||
Date: Mon, 31 Jul 2017 01:45:19 -0500
 | 
			
		||||
Subject: [PATCH] Reset spawner timer when spawner event is cancelled
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
diff --git a/src/main/java/net/minecraft/world/level/BaseSpawner.java b/src/main/java/net/minecraft/world/level/BaseSpawner.java
 | 
			
		||||
index e720c751518af3f38fba0c1b22e4c01a46561b00..14188ac6f158b36755abe23c0a967763cf7367d8 100644
 | 
			
		||||
--- a/src/main/java/net/minecraft/world/level/BaseSpawner.java
 | 
			
		||||
+++ b/src/main/java/net/minecraft/world/level/BaseSpawner.java
 | 
			
		||||
@@ -168,6 +168,7 @@ public abstract class BaseSpawner {
 | 
			
		||||
                             // Spigot End
 | 
			
		||||
                         }
 | 
			
		||||
                         entity.spawnedViaMobSpawner = true; // Paper
 | 
			
		||||
+                        flag = true; // Paper
 | 
			
		||||
                         // Spigot Start
 | 
			
		||||
                         if (org.bukkit.craftbukkit.event.CraftEventFactory.callSpawnerSpawnEvent(entity, pos).isCancelled()) {
 | 
			
		||||
                             Entity vehicle = entity.getVehicle();
 | 
			
		||||
@@ -191,7 +192,7 @@ public abstract class BaseSpawner {
 | 
			
		||||
                             ((Mob) entity).spawnAnim();
 | 
			
		||||
                         }
 | 
			
		||||
 
 | 
			
		||||
-                        flag = true;
 | 
			
		||||
+                        //flag = true; // Paper - moved up above cancellable event
 | 
			
		||||
                     }
 | 
			
		||||
                 }
 | 
			
		||||
 
 | 
			
		||||
| 
						 | 
				
			
			@ -1,43 +0,0 @@
 | 
			
		|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
 | 
			
		||||
From: kashike <kashike@vq.lc>
 | 
			
		||||
Date: Thu, 17 Aug 2017 16:08:20 -0700
 | 
			
		||||
Subject: [PATCH] Allow specifying a custom "authentication servers down" kick
 | 
			
		||||
 message
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
diff --git a/src/main/java/com/destroystokyo/paper/PaperConfig.java b/src/main/java/com/destroystokyo/paper/PaperConfig.java
 | 
			
		||||
index 7a69f9d9bb9c05474d8fbab22d626529a41a66a1..f4735cc330822183e098a67f2c0f00f21db9e137 100644
 | 
			
		||||
--- a/src/main/java/com/destroystokyo/paper/PaperConfig.java
 | 
			
		||||
+++ b/src/main/java/com/destroystokyo/paper/PaperConfig.java
 | 
			
		||||
@@ -1,5 +1,6 @@
 | 
			
		||||
 package com.destroystokyo.paper;
 | 
			
		||||
 
 | 
			
		||||
+import com.google.common.base.Strings;
 | 
			
		||||
 import com.google.common.base.Throwables;
 | 
			
		||||
 
 | 
			
		||||
 import java.io.File;
 | 
			
		||||
@@ -285,4 +286,9 @@ public class PaperConfig {
 | 
			
		||||
     private static void suggestPlayersWhenNull() {
 | 
			
		||||
         suggestPlayersWhenNullTabCompletions = getBoolean("settings.suggest-player-names-when-null-tab-completions", suggestPlayersWhenNullTabCompletions);
 | 
			
		||||
     }
 | 
			
		||||
+
 | 
			
		||||
+    public static String authenticationServersDownKickMessage = ""; // empty = use translatable message
 | 
			
		||||
+    private static void authenticationServersDownKickMessage() {
 | 
			
		||||
+        authenticationServersDownKickMessage = Strings.emptyToNull(getString("messages.kick.authentication-servers-down", authenticationServersDownKickMessage));
 | 
			
		||||
+    }
 | 
			
		||||
 }
 | 
			
		||||
diff --git a/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java
 | 
			
		||||
index ab3409dd3a7671b46cba210cfa326311d10a7ef4..82d0979e3239dddf3951df4a8b65ae7319d3d5b5 100644
 | 
			
		||||
--- a/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java
 | 
			
		||||
+++ b/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java
 | 
			
		||||
@@ -297,6 +297,10 @@ public class ServerLoginPacketListenerImpl implements ServerLoginPacketListener
 | 
			
		||||
                         ServerLoginPacketListenerImpl.this.gameProfile = ServerLoginPacketListenerImpl.this.createFakeProfile(gameprofile);
 | 
			
		||||
                         ServerLoginPacketListenerImpl.this.state = ServerLoginPacketListenerImpl.State.READY_TO_ACCEPT;
 | 
			
		||||
                     } else {
 | 
			
		||||
+                            // Paper start
 | 
			
		||||
+                            if (com.destroystokyo.paper.PaperConfig.authenticationServersDownKickMessage != null) {
 | 
			
		||||
+                                ServerLoginPacketListenerImpl.this.disconnect(new TextComponent(com.destroystokyo.paper.PaperConfig.authenticationServersDownKickMessage));
 | 
			
		||||
+                            } else // Paper end
 | 
			
		||||
                         ServerLoginPacketListenerImpl.this.disconnect(new TranslatableComponent("multiplayer.disconnect.authservers_down"));
 | 
			
		||||
                         ServerLoginPacketListenerImpl.LOGGER.error("Couldn't verify username because servers are unavailable");
 | 
			
		||||
                     }
 | 
			
		||||
| 
						 | 
				
			
			@ -1,71 +0,0 @@
 | 
			
		|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
 | 
			
		||||
From: Minecrell <minecrell@minecrell.net>
 | 
			
		||||
Date: Thu, 21 Sep 2017 16:14:55 +0200
 | 
			
		||||
Subject: [PATCH] Handle plugin prefixes using Log4J configuration
 | 
			
		||||
 | 
			
		||||
Display logger name in the console for all loggers except the
 | 
			
		||||
root logger, Bukkit's logger ("Minecraft") and Minecraft loggers.
 | 
			
		||||
Since plugins now use the plugin name as logger name this will
 | 
			
		||||
restore the plugin prefixes without having to prepend them manually
 | 
			
		||||
to the log messages.
 | 
			
		||||
 | 
			
		||||
Logger prefixes are shown by default for all loggers except for
 | 
			
		||||
the root logger, the Minecraft/Mojang loggers and the Bukkit loggers.
 | 
			
		||||
This may cause additional prefixes to be disabled for plugins bypassing
 | 
			
		||||
the plugin logger.
 | 
			
		||||
 | 
			
		||||
diff --git a/build.gradle.kts b/build.gradle.kts
 | 
			
		||||
index 9b70376813531718c02082633e9f8105f4879a63..fc8ffeea3e808eb1381f85972adffc614937ef6d 100644
 | 
			
		||||
--- a/build.gradle.kts
 | 
			
		||||
+++ b/build.gradle.kts
 | 
			
		||||
@@ -25,7 +25,7 @@ dependencies {
 | 
			
		||||
           all its classes to check if they are plugins.
 | 
			
		||||
           Scanning takes about 1-2 seconds so adding this speeds up the server start.
 | 
			
		||||
      */
 | 
			
		||||
-    runtimeOnly("org.apache.logging.log4j:log4j-core:2.14.1")
 | 
			
		||||
+    implementation("org.apache.logging.log4j:log4j-core:2.14.1") // Paper - implementation
 | 
			
		||||
     // Paper end
 | 
			
		||||
     implementation("org.apache.logging.log4j:log4j-iostreams:2.14.1") // Paper
 | 
			
		||||
     implementation("org.apache.logging.log4j:log4j-api:2.14.1") // Paper
 | 
			
		||||
diff --git a/src/main/java/org/spigotmc/SpigotConfig.java b/src/main/java/org/spigotmc/SpigotConfig.java
 | 
			
		||||
index f8a9d6a394f796634e4663ef4078a4c98447e13c..d73dfe72a54b621c0f944c90904df3e3bc709445 100644
 | 
			
		||||
--- a/src/main/java/org/spigotmc/SpigotConfig.java
 | 
			
		||||
+++ b/src/main/java/org/spigotmc/SpigotConfig.java
 | 
			
		||||
@@ -290,7 +290,7 @@ public class SpigotConfig
 | 
			
		||||
     private static void playerSample()
 | 
			
		||||
     {
 | 
			
		||||
         SpigotConfig.playerSample = SpigotConfig.getInt( "settings.sample-count", 12 );
 | 
			
		||||
-        System.out.println( "Server Ping Player Sample Count: " + SpigotConfig.playerSample );
 | 
			
		||||
+        Bukkit.getLogger().log( Level.INFO, "Server Ping Player Sample Count: {0}", playerSample ); // Paper - Use logger
 | 
			
		||||
     }
 | 
			
		||||
 
 | 
			
		||||
     public static int playerShuffle;
 | 
			
		||||
diff --git a/src/main/resources/log4j2.xml b/src/main/resources/log4j2.xml
 | 
			
		||||
index 620b9490e5f159080e50289d127404a1b56adbef..a8bdaaeaa1a9316848416f0533739b9b083ca151 100644
 | 
			
		||||
--- a/src/main/resources/log4j2.xml
 | 
			
		||||
+++ b/src/main/resources/log4j2.xml
 | 
			
		||||
@@ -5,10 +5,22 @@
 | 
			
		||||
             <PatternLayout pattern="[%d{HH:mm:ss} %level]: %msg%n" />
 | 
			
		||||
         </Queue>
 | 
			
		||||
         <TerminalConsole name="TerminalConsole">
 | 
			
		||||
-            <PatternLayout pattern="%highlightError{[%d{HH:mm:ss} %level]: %minecraftFormatting{%msg}%n%xEx}" />
 | 
			
		||||
+            <PatternLayout>
 | 
			
		||||
+                <LoggerNamePatternSelector defaultPattern="%highlightError{[%d{HH:mm:ss} %level]: [%logger] %minecraftFormatting{%msg}%n%xEx}">
 | 
			
		||||
+                    <!-- Log root, Minecraft, Mojang and Bukkit loggers without prefix -->
 | 
			
		||||
+                    <PatternMatch key=",net.minecraft.,Minecraft,com.mojang."
 | 
			
		||||
+                                  pattern="%highlightError{[%d{HH:mm:ss} %level]: %minecraftFormatting{%msg}%n%xEx}" />
 | 
			
		||||
+                </LoggerNamePatternSelector>
 | 
			
		||||
+            </PatternLayout>
 | 
			
		||||
         </TerminalConsole>
 | 
			
		||||
         <RollingRandomAccessFile name="File" fileName="logs/latest.log" filePattern="logs/%d{yyyy-MM-dd}-%i.log.gz">
 | 
			
		||||
-            <PatternLayout pattern="[%d{HH:mm:ss}] [%t/%level]: %minecraftFormatting{%msg}{strip}%n" />
 | 
			
		||||
+            <PatternLayout>
 | 
			
		||||
+                <LoggerNamePatternSelector defaultPattern="[%d{HH:mm:ss}] [%t/%level]: [%logger] %minecraftFormatting{%msg}{strip}%n">
 | 
			
		||||
+                    <!-- Log root, Minecraft, Mojang and Bukkit loggers without prefix -->
 | 
			
		||||
+                    <PatternMatch key=",net.minecraft.,Minecraft,com.mojang."
 | 
			
		||||
+                                  pattern="[%d{HH:mm:ss}] [%t/%level]: %minecraftFormatting{%msg}{strip}%n" />
 | 
			
		||||
+                </LoggerNamePatternSelector>
 | 
			
		||||
+            </PatternLayout>
 | 
			
		||||
             <Policies>
 | 
			
		||||
                 <TimeBasedTriggeringPolicy />
 | 
			
		||||
                 <OnStartupTriggeringPolicy />
 | 
			
		||||
| 
						 | 
				
			
			@ -1,47 +0,0 @@
 | 
			
		|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
 | 
			
		||||
From: Minecrell <minecrell@minecrell.net>
 | 
			
		||||
Date: Sat, 23 Sep 2017 21:07:20 +0200
 | 
			
		||||
Subject: [PATCH] Improve Log4J Configuration / Plugin Loggers
 | 
			
		||||
 | 
			
		||||
Add full exceptions to log4j to not truncate stack traces
 | 
			
		||||
 | 
			
		||||
Disable logger prefix for various plugins bypassing the plugin logger
 | 
			
		||||
 | 
			
		||||
Some plugins bypass the plugin logger and add the plugin prefix
 | 
			
		||||
manually to the log message. Since they use other logger names
 | 
			
		||||
(e.g. qualified class names) these would now also appear in the
 | 
			
		||||
log. Disable the logger prefix for these plugins so the messages
 | 
			
		||||
show up correctly.
 | 
			
		||||
 | 
			
		||||
diff --git a/src/main/resources/log4j2.xml b/src/main/resources/log4j2.xml
 | 
			
		||||
index a8bdaaeaa1a9316848416f0533739b9b083ca151..476f4a5cbe664ddd05474cb88553018bd334a5b8 100644
 | 
			
		||||
--- a/src/main/resources/log4j2.xml
 | 
			
		||||
+++ b/src/main/resources/log4j2.xml
 | 
			
		||||
@@ -6,19 +6,21 @@
 | 
			
		||||
         </Queue>
 | 
			
		||||
         <TerminalConsole name="TerminalConsole">
 | 
			
		||||
             <PatternLayout>
 | 
			
		||||
-                <LoggerNamePatternSelector defaultPattern="%highlightError{[%d{HH:mm:ss} %level]: [%logger] %minecraftFormatting{%msg}%n%xEx}">
 | 
			
		||||
+                <LoggerNamePatternSelector defaultPattern="%highlightError{[%d{HH:mm:ss} %level]: [%logger] %minecraftFormatting{%msg}%n%xEx{full}}">
 | 
			
		||||
                     <!-- Log root, Minecraft, Mojang and Bukkit loggers without prefix -->
 | 
			
		||||
-                    <PatternMatch key=",net.minecraft.,Minecraft,com.mojang."
 | 
			
		||||
-                                  pattern="%highlightError{[%d{HH:mm:ss} %level]: %minecraftFormatting{%msg}%n%xEx}" />
 | 
			
		||||
+                    <!-- Disable prefix for various plugins that bypass the plugin logger -->
 | 
			
		||||
+                    <PatternMatch key=",net.minecraft.,Minecraft,com.mojang.,com.sk89q.,ru.tehkode.,Minecraft.AWE"
 | 
			
		||||
+                                  pattern="%highlightError{[%d{HH:mm:ss} %level]: %minecraftFormatting{%msg}%n%xEx{full}}" />
 | 
			
		||||
                 </LoggerNamePatternSelector>
 | 
			
		||||
             </PatternLayout>
 | 
			
		||||
         </TerminalConsole>
 | 
			
		||||
         <RollingRandomAccessFile name="File" fileName="logs/latest.log" filePattern="logs/%d{yyyy-MM-dd}-%i.log.gz">
 | 
			
		||||
             <PatternLayout>
 | 
			
		||||
-                <LoggerNamePatternSelector defaultPattern="[%d{HH:mm:ss}] [%t/%level]: [%logger] %minecraftFormatting{%msg}{strip}%n">
 | 
			
		||||
+                <LoggerNamePatternSelector defaultPattern="[%d{HH:mm:ss}] [%t/%level]: [%logger] %minecraftFormatting{%msg}{strip}%n%xEx{full}">
 | 
			
		||||
                     <!-- Log root, Minecraft, Mojang and Bukkit loggers without prefix -->
 | 
			
		||||
-                    <PatternMatch key=",net.minecraft.,Minecraft,com.mojang."
 | 
			
		||||
-                                  pattern="[%d{HH:mm:ss}] [%t/%level]: %minecraftFormatting{%msg}{strip}%n" />
 | 
			
		||||
+                    <!-- Disable prefix for various plugins that bypass the plugin logger -->
 | 
			
		||||
+                    <PatternMatch key=",net.minecraft.,Minecraft,com.mojang.,com.sk89q.,ru.tehkode.,Minecraft.AWE"
 | 
			
		||||
+                                  pattern="[%d{HH:mm:ss}] [%t/%level]: %minecraftFormatting{%msg}{strip}%n%xEx{full}" />
 | 
			
		||||
                 </LoggerNamePatternSelector>
 | 
			
		||||
             </PatternLayout>
 | 
			
		||||
             <Policies>
 | 
			
		||||
| 
						 | 
				
			
			@ -1,46 +0,0 @@
 | 
			
		|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
 | 
			
		||||
From: Zach Brown <zach.brown@destroystokyo.com>
 | 
			
		||||
Date: Thu, 28 Sep 2017 17:21:44 -0400
 | 
			
		||||
Subject: [PATCH] Add PlayerJumpEvent
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
 | 
			
		||||
index 41f1b355a8a90216964e89432244a7d6929c9152..7759bf2afb9edeaca24726aace9358a8d5eafc64 100644
 | 
			
		||||
--- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
 | 
			
		||||
+++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
 | 
			
		||||
@@ -1174,7 +1174,34 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Ser
 | 
			
		||||
                             boolean flag = d8 > 0.0D;
 | 
			
		||||
 
 | 
			
		||||
                             if (this.player.isOnGround() && !packet.isOnGround() && flag) {
 | 
			
		||||
-                                this.player.jumpFromGround();
 | 
			
		||||
+                                // Paper start - Add player jump event
 | 
			
		||||
+                                Player player = this.getCraftPlayer();
 | 
			
		||||
+                                Location from = new Location(player.getWorld(), lastPosX, lastPosY, lastPosZ, lastYaw, lastPitch); // Get the Players previous Event location.
 | 
			
		||||
+                                Location to = player.getLocation().clone(); // Start off the To location as the Players current location.
 | 
			
		||||
+
 | 
			
		||||
+                                // If the packet contains movement information then we update the To location with the correct XYZ.
 | 
			
		||||
+                                if (packet.hasPos) {
 | 
			
		||||
+                                    to.setX(packet.x);
 | 
			
		||||
+                                    to.setY(packet.y);
 | 
			
		||||
+                                    to.setZ(packet.z);
 | 
			
		||||
+                                }
 | 
			
		||||
+
 | 
			
		||||
+                                // If the packet contains look information then we update the To location with the correct Yaw & Pitch.
 | 
			
		||||
+                                if (packet.hasRot) {
 | 
			
		||||
+                                    to.setYaw(packet.yRot);
 | 
			
		||||
+                                    to.setPitch(packet.xRot);
 | 
			
		||||
+                                }
 | 
			
		||||
+
 | 
			
		||||
+                                com.destroystokyo.paper.event.player.PlayerJumpEvent event = new com.destroystokyo.paper.event.player.PlayerJumpEvent(player, from, to);
 | 
			
		||||
+
 | 
			
		||||
+                                if (event.callEvent()) {
 | 
			
		||||
+                                    this.player.jumpFromGround();
 | 
			
		||||
+                                } else {
 | 
			
		||||
+                                    from = event.getFrom();
 | 
			
		||||
+                                    this.internalTeleport(from.getX(), from.getY(), from.getZ(), from.getYaw(), from.getPitch(), Collections.emptySet(), false);
 | 
			
		||||
+                                    return;
 | 
			
		||||
+                                }
 | 
			
		||||
+                                // Paper end
 | 
			
		||||
                             }
 | 
			
		||||
 
 | 
			
		||||
                             this.player.move(MoverType.PLAYER, new Vec3(d7, d8, d9));
 | 
			
		||||
| 
						 | 
				
			
			@ -1,40 +0,0 @@
 | 
			
		|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
 | 
			
		||||
From: Shane Freeder <theboyetronic@gmail.com>
 | 
			
		||||
Date: Thu, 5 Oct 2017 01:54:07 +0100
 | 
			
		||||
Subject: [PATCH] handle PacketPlayInKeepAlive async
 | 
			
		||||
 | 
			
		||||
In 1.12.2, Mojang moved the processing of PacketPlayInKeepAlive off the main
 | 
			
		||||
thread, while entirely correct for the server, this causes issues with
 | 
			
		||||
plugins which are expecting the PlayerQuitEvent on the main thread.
 | 
			
		||||
 | 
			
		||||
In order to counteract some bad behavior, we will post handling of the
 | 
			
		||||
disconnection to the main thread, but leave the actual processing of the packet
 | 
			
		||||
off the main thread.
 | 
			
		||||
 | 
			
		||||
also adding some additional logging in order to help work out what is causing
 | 
			
		||||
random disconnections for clients.
 | 
			
		||||
 | 
			
		||||
diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
 | 
			
		||||
index 89588d6478ebd7d4892dceb03026dff89e1844db..7b6e6e646511bc47d2215c512b4d839b3f3a1c55 100644
 | 
			
		||||
--- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
 | 
			
		||||
+++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
 | 
			
		||||
@@ -2771,14 +2771,18 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Ser
 | 
			
		||||
 
 | 
			
		||||
     @Override
 | 
			
		||||
     public void handleKeepAlive(ServerboundKeepAlivePacket packet) {
 | 
			
		||||
-        PacketUtils.ensureRunningOnSameThread(packet, this, this.player.getLevel()); // CraftBukkit
 | 
			
		||||
+        //PlayerConnectionUtils.ensureMainThread(packetplayinkeepalive, this, this.player.getWorldServer()); // CraftBukkit // Paper - This shouldn't be on the main thread
 | 
			
		||||
         if (this.keepAlivePending && packet.getId() == this.keepAliveChallenge) {
 | 
			
		||||
             int i = (int) (Util.getMillis() - this.keepAliveTime);
 | 
			
		||||
 
 | 
			
		||||
             this.player.latency = (this.player.latency * 3 + i) / 4;
 | 
			
		||||
             this.keepAlivePending = false;
 | 
			
		||||
         } else if (!this.isSingleplayerOwner()) {
 | 
			
		||||
+            // Paper start - This needs to be handled on the main thread for plugins
 | 
			
		||||
+            server.submit(() -> {
 | 
			
		||||
             this.disconnect(new TranslatableComponent("disconnect.timeout"));
 | 
			
		||||
+            });
 | 
			
		||||
+            // Paper end
 | 
			
		||||
         }
 | 
			
		||||
 
 | 
			
		||||
     }
 | 
			
		||||
| 
						 | 
				
			
			@ -1,116 +0,0 @@
 | 
			
		|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
 | 
			
		||||
From: Minecrell <minecrell@minecrell.net>
 | 
			
		||||
Date: Tue, 10 Oct 2017 18:45:20 +0200
 | 
			
		||||
Subject: [PATCH] Expose client protocol version and virtual host
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
diff --git a/src/main/java/com/destroystokyo/paper/network/PaperNetworkClient.java b/src/main/java/com/destroystokyo/paper/network/PaperNetworkClient.java
 | 
			
		||||
new file mode 100644
 | 
			
		||||
index 0000000000000000000000000000000000000000..a5a7624f1f372a26b982836cd31cff15e2589e9b
 | 
			
		||||
--- /dev/null
 | 
			
		||||
+++ b/src/main/java/com/destroystokyo/paper/network/PaperNetworkClient.java
 | 
			
		||||
@@ -0,0 +1,49 @@
 | 
			
		||||
+package com.destroystokyo.paper.network;
 | 
			
		||||
+
 | 
			
		||||
+import java.net.InetSocketAddress;
 | 
			
		||||
+
 | 
			
		||||
+import javax.annotation.Nullable;
 | 
			
		||||
+import net.minecraft.network.Connection;
 | 
			
		||||
+
 | 
			
		||||
+public class PaperNetworkClient implements NetworkClient {
 | 
			
		||||
+
 | 
			
		||||
+    private final Connection networkManager;
 | 
			
		||||
+
 | 
			
		||||
+    PaperNetworkClient(Connection networkManager) {
 | 
			
		||||
+        this.networkManager = networkManager;
 | 
			
		||||
+    }
 | 
			
		||||
+
 | 
			
		||||
+    @Override
 | 
			
		||||
+    public InetSocketAddress getAddress() {
 | 
			
		||||
+        return (InetSocketAddress) this.networkManager.getRemoteAddress();
 | 
			
		||||
+    }
 | 
			
		||||
+
 | 
			
		||||
+    @Override
 | 
			
		||||
+    public int getProtocolVersion() {
 | 
			
		||||
+        return this.networkManager.protocolVersion;
 | 
			
		||||
+    }
 | 
			
		||||
+
 | 
			
		||||
+    @Nullable
 | 
			
		||||
+    @Override
 | 
			
		||||
+    public InetSocketAddress getVirtualHost() {
 | 
			
		||||
+        return this.networkManager.virtualHost;
 | 
			
		||||
+    }
 | 
			
		||||
+
 | 
			
		||||
+    public static InetSocketAddress prepareVirtualHost(String host, int port) {
 | 
			
		||||
+        int len = host.length();
 | 
			
		||||
+
 | 
			
		||||
+        // FML appends a marker to the host to recognize FML clients (\0FML\0)
 | 
			
		||||
+        int pos = host.indexOf('\0');
 | 
			
		||||
+        if (pos >= 0) {
 | 
			
		||||
+            len = pos;
 | 
			
		||||
+        }
 | 
			
		||||
+
 | 
			
		||||
+        // When clients connect with a SRV record, their host contains a trailing '.'
 | 
			
		||||
+        if (len > 0 && host.charAt(len -  1) == '.') {
 | 
			
		||||
+            len--;
 | 
			
		||||
+        }
 | 
			
		||||
+
 | 
			
		||||
+        return InetSocketAddress.createUnresolved(host.substring(0, len), port);
 | 
			
		||||
+    }
 | 
			
		||||
+
 | 
			
		||||
+}
 | 
			
		||||
diff --git a/src/main/java/net/minecraft/network/Connection.java b/src/main/java/net/minecraft/network/Connection.java
 | 
			
		||||
index b62e373445406ae84b37ec0570ebb1da02cff0b7..06e965996a5c50bce617847e594ae0dd83403484 100644
 | 
			
		||||
--- a/src/main/java/net/minecraft/network/Connection.java
 | 
			
		||||
+++ b/src/main/java/net/minecraft/network/Connection.java
 | 
			
		||||
@@ -83,6 +83,10 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
 | 
			
		||||
     private float averageSentPackets;
 | 
			
		||||
     private int tickCount;
 | 
			
		||||
     private boolean handlingFault;
 | 
			
		||||
+    // Paper start - NetworkClient implementation
 | 
			
		||||
+    public int protocolVersion;
 | 
			
		||||
+    public java.net.InetSocketAddress virtualHost;
 | 
			
		||||
+    // Paper end
 | 
			
		||||
 
 | 
			
		||||
     public Connection(PacketFlow side) {
 | 
			
		||||
         this.receiving = side;
 | 
			
		||||
diff --git a/src/main/java/net/minecraft/server/network/ServerHandshakePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerHandshakePacketListenerImpl.java
 | 
			
		||||
index c4ba069f5124ec151e05813beddf293fddc3b804..484221e5a9c246aa91e0eacef3911b0e9ecff401 100644
 | 
			
		||||
--- a/src/main/java/net/minecraft/server/network/ServerHandshakePacketListenerImpl.java
 | 
			
		||||
+++ b/src/main/java/net/minecraft/server/network/ServerHandshakePacketListenerImpl.java
 | 
			
		||||
@@ -150,6 +150,10 @@ public class ServerHandshakePacketListenerImpl implements ServerHandshakePacketL
 | 
			
		||||
                 throw new UnsupportedOperationException("Invalid intention " + packet.getIntention());
 | 
			
		||||
         }
 | 
			
		||||
 
 | 
			
		||||
+        // Paper start - NetworkClient implementation
 | 
			
		||||
+        this.connection.protocolVersion = packet.getProtocolVersion();
 | 
			
		||||
+        this.connection.virtualHost = com.destroystokyo.paper.network.PaperNetworkClient.prepareVirtualHost(packet.hostName, packet.port);
 | 
			
		||||
+        // Paper end
 | 
			
		||||
     }
 | 
			
		||||
 
 | 
			
		||||
     @Override
 | 
			
		||||
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
 | 
			
		||||
index 4233f5ffa673801c57e3f929cd9ae919d6c7169f..e5e12d1672588138e4f56007fcdd14a1bb8ec1ca 100644
 | 
			
		||||
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
 | 
			
		||||
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
 | 
			
		||||
@@ -193,6 +193,20 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
 | 
			
		||||
         }
 | 
			
		||||
     }
 | 
			
		||||
 
 | 
			
		||||
+    // Paper start - Implement NetworkClient
 | 
			
		||||
+    @Override
 | 
			
		||||
+    public int getProtocolVersion() {
 | 
			
		||||
+        if (getHandle().connection == null) return -1;
 | 
			
		||||
+        return getHandle().connection.connection.protocolVersion;
 | 
			
		||||
+    }
 | 
			
		||||
+
 | 
			
		||||
+    @Override
 | 
			
		||||
+    public InetSocketAddress getVirtualHost() {
 | 
			
		||||
+        if (getHandle().connection == null) return null;
 | 
			
		||||
+        return getHandle().connection.connection.virtualHost;
 | 
			
		||||
+    }
 | 
			
		||||
+    // Paper end
 | 
			
		||||
+
 | 
			
		||||
     @Override
 | 
			
		||||
     public double getEyeHeight(boolean ignorePose) {
 | 
			
		||||
         if (ignorePose) {
 | 
			
		||||
| 
						 | 
				
			
			@ -1,77 +0,0 @@
 | 
			
		|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
 | 
			
		||||
From: Shane Freeder <theboyetronic@gmail.com>
 | 
			
		||||
Date: Sun, 15 Oct 2017 00:29:07 +0100
 | 
			
		||||
Subject: [PATCH] revert serverside behavior of keepalives
 | 
			
		||||
 | 
			
		||||
This patch intends to bump up the time that a client has to reply to the
 | 
			
		||||
server back to 30 seconds as per pre 1.12.2, which allowed clients
 | 
			
		||||
more than enough time to reply potentially allowing them to be less
 | 
			
		||||
tempermental due to lag spikes on the network thread, e.g. that caused
 | 
			
		||||
by plugins that are interacting with netty.
 | 
			
		||||
 | 
			
		||||
We also add a system property to allow people to tweak how long the server
 | 
			
		||||
will wait for a reply. There is a compromise here between lower and higher
 | 
			
		||||
values, lower values will mean that dead connections can be closed sooner,
 | 
			
		||||
whereas higher values will make this less sensitive to issues such as spikes
 | 
			
		||||
from networking or during connections flood of chunk packets on slower clients,
 | 
			
		||||
 at the cost of dead connections being kept open for longer.
 | 
			
		||||
 | 
			
		||||
diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
 | 
			
		||||
index 652703b1582e63148658a3a9d2604afa55674c23..cf42d59254f2786bfe8785249ad270d35996417a 100644
 | 
			
		||||
--- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
 | 
			
		||||
+++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
 | 
			
		||||
@@ -221,9 +221,9 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Ser
 | 
			
		||||
     private final MinecraftServer server;
 | 
			
		||||
     public ServerPlayer player;
 | 
			
		||||
     private int tickCount;
 | 
			
		||||
-    private long keepAliveTime; @Deprecated private void setLastPing(long lastPing) { this.keepAliveTime = lastPing;}; @Deprecated private long getLastPing() { return this.keepAliveTime;}; // Paper - OBFHELPER
 | 
			
		||||
-    private boolean keepAlivePending; @Deprecated private void setPendingPing(boolean isPending) { this.keepAlivePending = isPending;}; @Deprecated private boolean isPendingPing() { return this.keepAlivePending;}; // Paper - OBFHELPER
 | 
			
		||||
-    private long keepAliveChallenge; @Deprecated private void setKeepAliveID(long keepAliveID) { this.keepAliveChallenge = keepAliveID;}; @Deprecated private long getKeepAliveID() {return this.keepAliveChallenge; };  // Paper - OBFHELPER
 | 
			
		||||
+    private long keepAliveTime = Util.getMillis();
 | 
			
		||||
+    private boolean keepAlivePending;
 | 
			
		||||
+    private long keepAliveChallenge;
 | 
			
		||||
     // CraftBukkit start - multithreaded fields
 | 
			
		||||
     private AtomicInteger chatSpamTickCount = new AtomicInteger();
 | 
			
		||||
     // CraftBukkit end
 | 
			
		||||
@@ -252,6 +252,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Ser
 | 
			
		||||
     private int aboveGroundVehicleTickCount;
 | 
			
		||||
     private int receivedMovePacketCount;
 | 
			
		||||
     private int knownMovePacketCount;
 | 
			
		||||
+    private static final long KEEPALIVE_LIMIT = Long.getLong("paper.playerconnection.keepalive", 30) * 1000; // Paper - provide property to set keepalive limit
 | 
			
		||||
 
 | 
			
		||||
     public ServerGamePacketListenerImpl(MinecraftServer server, Connection connection, ServerPlayer player) {
 | 
			
		||||
         this.server = server;
 | 
			
		||||
@@ -334,18 +335,25 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Ser
 | 
			
		||||
         }
 | 
			
		||||
 
 | 
			
		||||
         this.server.getProfiler().push("keepAlive");
 | 
			
		||||
-        long i = Util.getMillis();
 | 
			
		||||
-
 | 
			
		||||
-        if (i - this.keepAliveTime >= 25000L) { // CraftBukkit
 | 
			
		||||
-            if (this.keepAlivePending) {
 | 
			
		||||
-                this.disconnect(new TranslatableComponent("disconnect.timeout"));
 | 
			
		||||
-            } else {
 | 
			
		||||
+        // Paper Start - give clients a longer time to respond to pings as per pre 1.12.2 timings
 | 
			
		||||
+        // This should effectively place the keepalive handling back to "as it was" before 1.12.2
 | 
			
		||||
+        long currentTime = Util.getMillis();
 | 
			
		||||
+        long elapsedTime = currentTime - this.keepAliveTime;
 | 
			
		||||
+
 | 
			
		||||
+        if (this.keepAlivePending) {
 | 
			
		||||
+            if (!this.processedDisconnect && elapsedTime >= KEEPALIVE_LIMIT) { // check keepalive limit, don't fire if already disconnected
 | 
			
		||||
+                ServerGamePacketListenerImpl.LOGGER.warn("{} was kicked due to keepalive timeout!", this.player.getScoreboardName()); // more info
 | 
			
		||||
+                this.disconnect(new TranslatableComponent("disconnect.timeout", new Object[0]));
 | 
			
		||||
+            }
 | 
			
		||||
+        } else {
 | 
			
		||||
+            if (elapsedTime >= 15000L) { // 15 seconds
 | 
			
		||||
                 this.keepAlivePending = true;
 | 
			
		||||
-                this.keepAliveTime = i;
 | 
			
		||||
-                this.keepAliveChallenge = i;
 | 
			
		||||
+                this.keepAliveTime = currentTime;
 | 
			
		||||
+                this.keepAliveChallenge = currentTime;
 | 
			
		||||
                 this.send(new ClientboundKeepAlivePacket(this.keepAliveChallenge));
 | 
			
		||||
             }
 | 
			
		||||
         }
 | 
			
		||||
+        // Paper end
 | 
			
		||||
 
 | 
			
		||||
         this.server.getProfiler().pop();
 | 
			
		||||
         // CraftBukkit start
 | 
			
		||||
| 
						 | 
				
			
			@ -1,80 +0,0 @@
 | 
			
		|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
 | 
			
		||||
From: Brokkonaut <hannos17@gmx.de>
 | 
			
		||||
Date: Tue, 31 Oct 2017 03:26:18 +0100
 | 
			
		||||
Subject: [PATCH] Send attack SoundEffects only to players who can see the
 | 
			
		||||
 attacker
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
diff --git a/src/main/java/net/minecraft/world/entity/player/Player.java b/src/main/java/net/minecraft/world/entity/player/Player.java
 | 
			
		||||
index 193d73c53c7c3cc31246dd19c6d7ff4cb39cac1b..8c20af91c9298cb36fdb2700d042b1e2fccf5f54 100644
 | 
			
		||||
--- a/src/main/java/net/minecraft/world/entity/player/Player.java
 | 
			
		||||
+++ b/src/main/java/net/minecraft/world/entity/player/Player.java
 | 
			
		||||
@@ -30,6 +30,7 @@ import net.minecraft.network.chat.MutableComponent;
 | 
			
		||||
 import net.minecraft.network.chat.TextComponent;
 | 
			
		||||
 import net.minecraft.network.chat.TranslatableComponent;
 | 
			
		||||
 import net.minecraft.network.protocol.game.ClientboundSetEntityMotionPacket;
 | 
			
		||||
+import net.minecraft.network.protocol.game.ClientboundSoundPacket;
 | 
			
		||||
 import net.minecraft.network.syncher.EntityDataAccessor;
 | 
			
		||||
 import net.minecraft.network.syncher.EntityDataSerializers;
 | 
			
		||||
 import net.minecraft.network.syncher.SynchedEntityData;
 | 
			
		||||
@@ -1181,7 +1182,7 @@ public abstract class Player extends LivingEntity {
 | 
			
		||||
                     int i = b0 + EnchantmentHelper.getKnockbackBonus((LivingEntity) this);
 | 
			
		||||
 
 | 
			
		||||
                     if (this.isSprinting() && flag) {
 | 
			
		||||
-                        this.level.playSound((Player) null, this.getX(), this.getY(), this.getZ(), SoundEvents.PLAYER_ATTACK_KNOCKBACK, this.getSoundSource(), 1.0F, 1.0F);
 | 
			
		||||
+                        sendSoundEffect(this, this.getX(), this.getY(), this.getZ(), SoundEvents.PLAYER_ATTACK_KNOCKBACK, this.getSoundSource(), 1.0F, 1.0F); // Paper - send while respecting visibility
 | 
			
		||||
                         ++i;
 | 
			
		||||
                         flag1 = true;
 | 
			
		||||
                     }
 | 
			
		||||
@@ -1256,7 +1257,7 @@ public abstract class Player extends LivingEntity {
 | 
			
		||||
                                 }
 | 
			
		||||
                             }
 | 
			
		||||
 
 | 
			
		||||
-                            this.level.playSound((Player) null, this.getX(), this.getY(), this.getZ(), SoundEvents.PLAYER_ATTACK_SWEEP, this.getSoundSource(), 1.0F, 1.0F);
 | 
			
		||||
+                            sendSoundEffect(this, this.getX(), this.getY(), this.getZ(), SoundEvents.PLAYER_ATTACK_SWEEP, this.getSoundSource(), 1.0F, 1.0F); // Paper - send while respecting visibility
 | 
			
		||||
                             this.sweepAttack();
 | 
			
		||||
                         }
 | 
			
		||||
 
 | 
			
		||||
@@ -1284,15 +1285,15 @@ public abstract class Player extends LivingEntity {
 | 
			
		||||
                         }
 | 
			
		||||
 
 | 
			
		||||
                         if (flag2) {
 | 
			
		||||
-                            this.level.playSound((Player) null, this.getX(), this.getY(), this.getZ(), SoundEvents.PLAYER_ATTACK_CRIT, this.getSoundSource(), 1.0F, 1.0F);
 | 
			
		||||
+                            sendSoundEffect(this, this.getX(), this.getY(), this.getZ(), SoundEvents.PLAYER_ATTACK_CRIT, this.getSoundSource(), 1.0F, 1.0F); // Paper - send while respecting visibility
 | 
			
		||||
                             this.crit(target);
 | 
			
		||||
                         }
 | 
			
		||||
 
 | 
			
		||||
                         if (!flag2 && !flag3) {
 | 
			
		||||
                             if (flag) {
 | 
			
		||||
-                                this.level.playSound((Player) null, this.getX(), this.getY(), this.getZ(), SoundEvents.PLAYER_ATTACK_STRONG, this.getSoundSource(), 1.0F, 1.0F);
 | 
			
		||||
+                                sendSoundEffect(this, this.getX(), this.getY(), this.getZ(), SoundEvents.PLAYER_ATTACK_STRONG, this.getSoundSource(), 1.0F, 1.0F); // Paper - send while respecting visibility
 | 
			
		||||
                             } else {
 | 
			
		||||
-                                this.level.playSound((Player) null, this.getX(), this.getY(), this.getZ(), SoundEvents.PLAYER_ATTACK_WEAK, this.getSoundSource(), 1.0F, 1.0F);
 | 
			
		||||
+                                sendSoundEffect(this, this.getX(), this.getY(), this.getZ(), SoundEvents.PLAYER_ATTACK_WEAK, this.getSoundSource(), 1.0F, 1.0F); // Paper - send while respecting visibility
 | 
			
		||||
                             }
 | 
			
		||||
                         }
 | 
			
		||||
 
 | 
			
		||||
@@ -1344,7 +1345,7 @@ public abstract class Player extends LivingEntity {
 | 
			
		||||
 
 | 
			
		||||
                         this.applyExhaustion(level.spigotConfig.combatExhaustion, EntityExhaustionEvent.ExhaustionReason.ATTACK); // CraftBukkit - EntityExhaustionEvent // Spigot - Change to use configurable value
 | 
			
		||||
                     } else {
 | 
			
		||||
-                        this.level.playSound((Player) null, this.getX(), this.getY(), this.getZ(), SoundEvents.PLAYER_ATTACK_NODAMAGE, this.getSoundSource(), 1.0F, 1.0F);
 | 
			
		||||
+                        sendSoundEffect(this, this.getX(), this.getY(), this.getZ(), SoundEvents.PLAYER_ATTACK_NODAMAGE, this.getSoundSource(), 1.0F, 1.0F); // Paper - send while respecting visibility
 | 
			
		||||
                         if (flag4) {
 | 
			
		||||
                             target.clearFire();
 | 
			
		||||
                         }
 | 
			
		||||
@@ -1791,6 +1792,14 @@ public abstract class Player extends LivingEntity {
 | 
			
		||||
     public int getXpNeededForNextLevel() {
 | 
			
		||||
         return this.experienceLevel >= 30 ? 112 + (this.experienceLevel - 30) * 9 : (this.experienceLevel >= 15 ? 37 + (this.experienceLevel - 15) * 5 : 7 + this.experienceLevel * 2);
 | 
			
		||||
     }
 | 
			
		||||
+    // Paper start - send SoundEffect to everyone who can see fromEntity
 | 
			
		||||
+    private static void sendSoundEffect(Player fromEntity, double x, double y, double z, SoundEvent soundEffect, SoundSource soundCategory, float volume, float pitch) {
 | 
			
		||||
+        fromEntity.level.playSound(fromEntity, x, y, z, soundEffect, soundCategory, volume, pitch); // This will not send the effect to the entity himself
 | 
			
		||||
+        if (fromEntity instanceof ServerPlayer) {
 | 
			
		||||
+            ((ServerPlayer) fromEntity).connection.send(new ClientboundSoundPacket(soundEffect, soundCategory, x, y, z, volume, pitch));
 | 
			
		||||
+        }
 | 
			
		||||
+    }
 | 
			
		||||
+    // Paper end
 | 
			
		||||
 
 | 
			
		||||
     // CraftBukkit start
 | 
			
		||||
     public void causeFoodExhaustion(float exhaustion) {
 | 
			
		||||
| 
						 | 
				
			
			@ -1,31 +0,0 @@
 | 
			
		|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
 | 
			
		||||
From: pkt77 <parkerkt77@gmail.com>
 | 
			
		||||
Date: Fri, 10 Nov 2017 23:46:34 -0500
 | 
			
		||||
Subject: [PATCH] Add PlayerArmorChangeEvent
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java
 | 
			
		||||
index 5f24963bd2681cab15d20221154a9a758be53b03..375959ed616596e604dc07d3b30d708b08d7ce57 100644
 | 
			
		||||
--- a/src/main/java/net/minecraft/world/entity/LivingEntity.java
 | 
			
		||||
+++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java
 | 
			
		||||
@@ -1,5 +1,6 @@
 | 
			
		||||
 package net.minecraft.world.entity;
 | 
			
		||||
 
 | 
			
		||||
+import com.destroystokyo.paper.event.player.PlayerArmorChangeEvent; // Paper
 | 
			
		||||
 import com.google.common.base.Objects;
 | 
			
		||||
 import com.google.common.collect.ImmutableList;
 | 
			
		||||
 import com.google.common.collect.ImmutableMap;
 | 
			
		||||
@@ -2941,6 +2942,13 @@ public abstract class LivingEntity extends Entity {
 | 
			
		||||
             ItemStack itemstack1 = this.getItemBySlot(enumitemslot);
 | 
			
		||||
 
 | 
			
		||||
             if (!ItemStack.matches(itemstack1, itemstack)) {
 | 
			
		||||
+                // Paper start - PlayerArmorChangeEvent
 | 
			
		||||
+                if (this instanceof ServerPlayer && enumitemslot.getType() == EquipmentSlot.Type.ARMOR) {
 | 
			
		||||
+                    final org.bukkit.inventory.ItemStack oldItem = CraftItemStack.asBukkitCopy(itemstack);
 | 
			
		||||
+                    final org.bukkit.inventory.ItemStack newItem = CraftItemStack.asBukkitCopy(itemstack1);
 | 
			
		||||
+                    new PlayerArmorChangeEvent((Player) this.getBukkitEntity(), PlayerArmorChangeEvent.SlotType.valueOf(enumitemslot.name()), oldItem, newItem).callEvent();
 | 
			
		||||
+                }
 | 
			
		||||
+                // Paper end
 | 
			
		||||
                 if (map == null) {
 | 
			
		||||
                     map = Maps.newEnumMap(EquipmentSlot.class);
 | 
			
		||||
                 }
 | 
			
		||||
| 
						 | 
				
			
			@ -1,24 +0,0 @@
 | 
			
		|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
 | 
			
		||||
From: killme <killme-git@ibts.me>
 | 
			
		||||
Date: Sun, 12 Nov 2017 19:40:01 +0100
 | 
			
		||||
Subject: [PATCH] Prevent logins from being processed when the player has
 | 
			
		||||
 disconnected
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
diff --git a/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java
 | 
			
		||||
index 82d0979e3239dddf3951df4a8b65ae7319d3d5b5..109d7f1bf37e442ff80f7f63d50e27e6e30e0b5e 100644
 | 
			
		||||
--- a/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java
 | 
			
		||||
+++ b/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java
 | 
			
		||||
@@ -76,7 +76,11 @@ public class ServerLoginPacketListenerImpl implements ServerLoginPacketListener
 | 
			
		||||
         }
 | 
			
		||||
         // Paper end
 | 
			
		||||
         if (this.state == ServerLoginPacketListenerImpl.State.READY_TO_ACCEPT) {
 | 
			
		||||
-            this.handleAcceptedLogin();
 | 
			
		||||
+            // Paper start - prevent logins to be processed even though disconnect was called
 | 
			
		||||
+            if (connection.isConnected()) {
 | 
			
		||||
+                this.handleAcceptedLogin();
 | 
			
		||||
+            }
 | 
			
		||||
+            // Paper end
 | 
			
		||||
         } else if (this.state == ServerLoginPacketListenerImpl.State.DELAY_ACCEPT) {
 | 
			
		||||
             ServerPlayer entityplayer = this.server.getPlayerList().getPlayer(this.gameProfile.getId());
 | 
			
		||||
 
 | 
			
		||||
| 
						 | 
				
			
			@ -1,42 +0,0 @@
 | 
			
		|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
 | 
			
		||||
From: mezz <tehgeek@gmail.com>
 | 
			
		||||
Date: Wed, 9 Aug 2017 17:51:22 -0500
 | 
			
		||||
Subject: [PATCH] Fix MC-117075: TE Unload Lag Spike
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java
 | 
			
		||||
index acfa844c87e5814c033cad4318813800f57885fd..b327e10a5708d523b9d4a6c77c1359521f2927d5 100644
 | 
			
		||||
--- a/src/main/java/net/minecraft/world/level/Level.java
 | 
			
		||||
+++ b/src/main/java/net/minecraft/world/level/Level.java
 | 
			
		||||
@@ -730,6 +730,8 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
 | 
			
		||||
         // Spigot start
 | 
			
		||||
         // Iterator iterator = this.blockEntityTickers.iterator();
 | 
			
		||||
         int tilesThisCycle = 0;
 | 
			
		||||
+        var toRemove = new it.unimi.dsi.fastutil.objects.ObjectOpenCustomHashSet<TickingBlockEntity>(net.minecraft.Util.identityStrategy()); // Paper - use removeAll
 | 
			
		||||
+        toRemove.add(null);
 | 
			
		||||
         for (tileTickPosition = 0; tileTickPosition < this.blockEntityTickers.size(); tileTickPosition++) { // Paper - Disable tick limiters
 | 
			
		||||
             this.tileTickPosition = (this.tileTickPosition < this.blockEntityTickers.size()) ? this.tileTickPosition : 0;
 | 
			
		||||
             TickingBlockEntity tickingblockentity = (TickingBlockEntity) this.blockEntityTickers.get(tileTickPosition);
 | 
			
		||||
@@ -737,7 +739,6 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
 | 
			
		||||
             if (tickingblockentity == null) {
 | 
			
		||||
                 this.getCraftServer().getLogger().severe("Spigot has detected a null entity and has removed it, preventing a crash");
 | 
			
		||||
                 tilesThisCycle--;
 | 
			
		||||
-                this.blockEntityTickers.remove(this.tileTickPosition--);
 | 
			
		||||
                 continue;
 | 
			
		||||
             }
 | 
			
		||||
             // Spigot end
 | 
			
		||||
@@ -745,12 +746,13 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
 | 
			
		||||
             if (tickingblockentity.isRemoved()) {
 | 
			
		||||
                 // Spigot start
 | 
			
		||||
                 tilesThisCycle--;
 | 
			
		||||
-                this.blockEntityTickers.remove(this.tileTickPosition--);
 | 
			
		||||
+                toRemove.add(tickingblockentity); // Paper - use removeAll
 | 
			
		||||
                 // Spigot end
 | 
			
		||||
             } else {
 | 
			
		||||
                 tickingblockentity.tick();
 | 
			
		||||
             }
 | 
			
		||||
         }
 | 
			
		||||
+        this.blockEntityTickers.removeAll(toRemove);
 | 
			
		||||
 
 | 
			
		||||
         timings.tileEntityTick.stopTiming(); // Spigot
 | 
			
		||||
         this.tickingBlockEntities = false;
 | 
			
		||||
| 
						 | 
				
			
			@ -1,60 +0,0 @@
 | 
			
		|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
 | 
			
		||||
From: Shane Freeder <theboyetronic@gmail.com>
 | 
			
		||||
Date: Thu, 16 Nov 2017 12:12:41 +0000
 | 
			
		||||
Subject: [PATCH] use CB BlockState implementations for captured blocks
 | 
			
		||||
 | 
			
		||||
When modifying the world, CB will store a copy of the affected
 | 
			
		||||
blocks in order to restore their state in the case that the event
 | 
			
		||||
is cancelled. This change only modifies the collection of blocks
 | 
			
		||||
in the world by normal means, e.g. not during tree population,
 | 
			
		||||
as the potentially marginal overheads would serve no advantage.
 | 
			
		||||
 | 
			
		||||
CB was using a CraftBlockState for all blocks, which causes issues
 | 
			
		||||
should any block that uses information beyond a data ID would suffer
 | 
			
		||||
from missing information, e.g. Skulls.
 | 
			
		||||
 | 
			
		||||
By using CBs CraftBlock#getState(), we will maintain a proper copy of
 | 
			
		||||
the blockstate that will be valid for restoration, as opposed to dropping
 | 
			
		||||
information on restoration when the event is cancelled.
 | 
			
		||||
 | 
			
		||||
diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java
 | 
			
		||||
index b327e10a5708d523b9d4a6c77c1359521f2927d5..233a8fcfae4ac568ad1f73cc61d8ae5b66a01ac2 100644
 | 
			
		||||
--- a/src/main/java/net/minecraft/world/level/Level.java
 | 
			
		||||
+++ b/src/main/java/net/minecraft/world/level/Level.java
 | 
			
		||||
@@ -141,7 +141,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
 | 
			
		||||
     public boolean preventPoiUpdated = false; // CraftBukkit - SPIGOT-5710
 | 
			
		||||
     public boolean captureBlockStates = false;
 | 
			
		||||
     public boolean captureTreeGeneration = false;
 | 
			
		||||
-    public Map<BlockPos, CapturedBlockState> capturedBlockStates = new java.util.LinkedHashMap<>();
 | 
			
		||||
+    public Map<BlockPos, org.bukkit.craftbukkit.block.CraftBlockState> capturedBlockStates = new java.util.LinkedHashMap<>(); // Paper
 | 
			
		||||
     public Map<BlockPos, BlockEntity> capturedTileEntities = new HashMap<>();
 | 
			
		||||
     public List<ItemEntity> captureDrops;
 | 
			
		||||
     public long ticksPerAnimalSpawns;
 | 
			
		||||
@@ -361,7 +361,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
 | 
			
		||||
     public boolean setBlock(BlockPos pos, BlockState state, int flags, int maxUpdateDepth) {
 | 
			
		||||
         // CraftBukkit start - tree generation
 | 
			
		||||
         if (this.captureTreeGeneration) {
 | 
			
		||||
-            CapturedBlockState blockstate = this.capturedBlockStates.get(pos);
 | 
			
		||||
+            CraftBlockState blockstate = this.capturedBlockStates.get(pos);
 | 
			
		||||
             if (blockstate == null) {
 | 
			
		||||
                 blockstate = CapturedBlockState.getTreeBlockState(this, pos, flags);
 | 
			
		||||
                 this.capturedBlockStates.put(pos.immutable(), blockstate);
 | 
			
		||||
@@ -381,7 +381,8 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
 | 
			
		||||
             // CraftBukkit start - capture blockstates
 | 
			
		||||
             boolean captured = false;
 | 
			
		||||
             if (this.captureBlockStates && !this.capturedBlockStates.containsKey(pos)) {
 | 
			
		||||
-                CapturedBlockState blockstate = CapturedBlockState.getBlockState(this, pos, flags);
 | 
			
		||||
+                CraftBlockState blockstate = (CraftBlockState) world.getBlockAt(pos.getX(), pos.getY(), pos.getZ()).getState(); // Paper - use CB getState to get a suitable snapshot
 | 
			
		||||
+                blockstate.setFlag(flags); // Paper - set flag
 | 
			
		||||
                 this.capturedBlockStates.put(pos.immutable(), blockstate);
 | 
			
		||||
                 captured = true;
 | 
			
		||||
             }
 | 
			
		||||
@@ -650,7 +651,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
 | 
			
		||||
     public BlockState getBlockState(BlockPos pos) {
 | 
			
		||||
         // CraftBukkit start - tree generation
 | 
			
		||||
         if (this.captureTreeGeneration) {
 | 
			
		||||
-            CapturedBlockState previous = this.capturedBlockStates.get(pos);
 | 
			
		||||
+            CraftBlockState previous = this.capturedBlockStates.get(pos); // Paper
 | 
			
		||||
             if (previous != null) {
 | 
			
		||||
                 return previous.getHandle();
 | 
			
		||||
             }
 | 
			
		||||
| 
						 | 
				
			
			@ -1,125 +0,0 @@
 | 
			
		|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
 | 
			
		||||
From: Aikar <aikar@aikar.co>
 | 
			
		||||
Date: Mon, 6 Nov 2017 21:08:22 -0500
 | 
			
		||||
Subject: [PATCH] API to get a BlockState without a snapshot
 | 
			
		||||
 | 
			
		||||
This allows you to get a BlockState without creating a snapshot, operating
 | 
			
		||||
on the real tile entity.
 | 
			
		||||
 | 
			
		||||
This is useful for where performance is needed
 | 
			
		||||
 | 
			
		||||
also Avoid NPE during CraftBlockEntityState load if could not get TE
 | 
			
		||||
 | 
			
		||||
If Tile Entity was null, correct Sign to return empty lines instead of null
 | 
			
		||||
 | 
			
		||||
diff --git a/src/main/java/net/minecraft/world/level/block/entity/BlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/BlockEntity.java
 | 
			
		||||
index 27895fbe1cd7ee6ee025ed3e320671e3e971764d..1d1764766d2b4a0b7bf4078ce428bb1474f709df 100644
 | 
			
		||||
--- a/src/main/java/net/minecraft/world/level/block/entity/BlockEntity.java
 | 
			
		||||
+++ b/src/main/java/net/minecraft/world/level/block/entity/BlockEntity.java
 | 
			
		||||
@@ -42,6 +42,7 @@ public abstract class BlockEntity implements net.minecraft.server.KeyedObject {
 | 
			
		||||
         this.type = type;
 | 
			
		||||
         this.worldPosition = pos.immutable();
 | 
			
		||||
         this.blockState = state;
 | 
			
		||||
+        this.persistentDataContainer = new CraftPersistentDataContainer(DATA_TYPE_REGISTRY); // Paper - always init
 | 
			
		||||
     }
 | 
			
		||||
 
 | 
			
		||||
     // Paper start
 | 
			
		||||
@@ -79,7 +80,7 @@ public abstract class BlockEntity implements net.minecraft.server.KeyedObject {
 | 
			
		||||
 
 | 
			
		||||
     // CraftBukkit start - read container
 | 
			
		||||
     public void load(CompoundTag nbt) {
 | 
			
		||||
-        this.persistentDataContainer = new CraftPersistentDataContainer(BlockEntity.DATA_TYPE_REGISTRY);
 | 
			
		||||
+        this.persistentDataContainer.clear(); // Paper - clear instead of init
 | 
			
		||||
 
 | 
			
		||||
         net.minecraft.nbt.Tag persistentDataTag = nbt.get("PublicBukkitValues");
 | 
			
		||||
         if (persistentDataTag instanceof CompoundTag) {
 | 
			
		||||
@@ -222,10 +223,15 @@ public abstract class BlockEntity implements net.minecraft.server.KeyedObject {
 | 
			
		||||
 
 | 
			
		||||
     // CraftBukkit start - add method
 | 
			
		||||
     public InventoryHolder getOwner() {
 | 
			
		||||
+        // Paper start
 | 
			
		||||
+        return getOwner(true);
 | 
			
		||||
+    }
 | 
			
		||||
+    public InventoryHolder getOwner(boolean useSnapshot) {
 | 
			
		||||
+        // Paper end
 | 
			
		||||
         if (this.level == null) return null;
 | 
			
		||||
         org.bukkit.block.Block block = this.level.getWorld().getBlockAt(this.worldPosition.getX(), this.worldPosition.getY(), this.worldPosition.getZ());
 | 
			
		||||
         if (block.getType() == org.bukkit.Material.AIR) return null;
 | 
			
		||||
-        org.bukkit.block.BlockState state = block.getState();
 | 
			
		||||
+        org.bukkit.block.BlockState state = block.getState(useSnapshot); // Paper
 | 
			
		||||
         if (state instanceof InventoryHolder) return (InventoryHolder) state;
 | 
			
		||||
         return null;
 | 
			
		||||
     }
 | 
			
		||||
diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftBlock.java b/src/main/java/org/bukkit/craftbukkit/block/CraftBlock.java
 | 
			
		||||
index e6b8dd52cd503f45ca9bb868891ae4c8b29b3fcb..f1c4c3a3392c2d4d836fa10d7a38558d08084d9d 100644
 | 
			
		||||
--- a/src/main/java/org/bukkit/craftbukkit/block/CraftBlock.java
 | 
			
		||||
+++ b/src/main/java/org/bukkit/craftbukkit/block/CraftBlock.java
 | 
			
		||||
@@ -314,7 +314,20 @@ public class CraftBlock implements Block {
 | 
			
		||||
 
 | 
			
		||||
     @Override
 | 
			
		||||
     public BlockState getState() {
 | 
			
		||||
+        // Paper start
 | 
			
		||||
+        return this.getState(true);
 | 
			
		||||
+    }
 | 
			
		||||
+
 | 
			
		||||
+    @Override
 | 
			
		||||
+    public BlockState getState(boolean useSnapshot) {
 | 
			
		||||
+        boolean prev = CraftBlockEntityState.DISABLE_SNAPSHOT;
 | 
			
		||||
+        CraftBlockEntityState.DISABLE_SNAPSHOT = !useSnapshot;
 | 
			
		||||
+        try {
 | 
			
		||||
         return CraftBlockStates.getBlockState(this);
 | 
			
		||||
+        } finally {
 | 
			
		||||
+            CraftBlockEntityState.DISABLE_SNAPSHOT = prev;
 | 
			
		||||
+        }
 | 
			
		||||
+        // Paper end
 | 
			
		||||
     }
 | 
			
		||||
 
 | 
			
		||||
     @Override
 | 
			
		||||
diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftBlockEntityState.java b/src/main/java/org/bukkit/craftbukkit/block/CraftBlockEntityState.java
 | 
			
		||||
index 2fb445e6edc43eb8e3e169cca3fc3b46ced94202..059a122ef7038f7c4e269b476eb6e013b3eb4531 100644
 | 
			
		||||
--- a/src/main/java/org/bukkit/craftbukkit/block/CraftBlockEntityState.java
 | 
			
		||||
+++ b/src/main/java/org/bukkit/craftbukkit/block/CraftBlockEntityState.java
 | 
			
		||||
@@ -10,15 +10,26 @@ public abstract class CraftBlockEntityState<T extends BlockEntity> extends Craft
 | 
			
		||||
 
 | 
			
		||||
     private final T tileEntity;
 | 
			
		||||
     private final T snapshot;
 | 
			
		||||
+    public final boolean snapshotDisabled; // Paper
 | 
			
		||||
+    public static boolean DISABLE_SNAPSHOT = false; // Paper
 | 
			
		||||
 
 | 
			
		||||
     public CraftBlockEntityState(World world, T tileEntity) {
 | 
			
		||||
         super(world, tileEntity.getBlockPos(), tileEntity.getBlockState());
 | 
			
		||||
 
 | 
			
		||||
         this.tileEntity = tileEntity;
 | 
			
		||||
 
 | 
			
		||||
+        // Paper start
 | 
			
		||||
+        this.snapshotDisabled = DISABLE_SNAPSHOT;
 | 
			
		||||
+        if (DISABLE_SNAPSHOT) {
 | 
			
		||||
+            this.snapshot = this.tileEntity;
 | 
			
		||||
+        } else {
 | 
			
		||||
+            this.snapshot = this.createSnapshot(tileEntity);
 | 
			
		||||
+        }
 | 
			
		||||
         // copy tile entity data:
 | 
			
		||||
-        this.snapshot = this.createSnapshot(tileEntity);
 | 
			
		||||
-        this.load(snapshot);
 | 
			
		||||
+        if (this.snapshot != null) {
 | 
			
		||||
+            this.load(this.snapshot);
 | 
			
		||||
+        }
 | 
			
		||||
+        // Paper end
 | 
			
		||||
     }
 | 
			
		||||
 
 | 
			
		||||
     public void refreshSnapshot() {
 | 
			
		||||
diff --git a/src/main/java/org/bukkit/craftbukkit/persistence/CraftPersistentDataContainer.java b/src/main/java/org/bukkit/craftbukkit/persistence/CraftPersistentDataContainer.java
 | 
			
		||||
index ddd7b63f0452042baa3fca04bb9fbdb42fcecbfd..b638351581fa09c488425a2318b782a5812140ce 100644
 | 
			
		||||
--- a/src/main/java/org/bukkit/craftbukkit/persistence/CraftPersistentDataContainer.java
 | 
			
		||||
+++ b/src/main/java/org/bukkit/craftbukkit/persistence/CraftPersistentDataContainer.java
 | 
			
		||||
@@ -155,4 +155,10 @@ public final class CraftPersistentDataContainer implements PersistentDataContain
 | 
			
		||||
     public Map<String, Object> serialize() {
 | 
			
		||||
         return (Map<String, Object>) CraftNBTTagConfigSerializer.serialize(this.toTagCompound());
 | 
			
		||||
     }
 | 
			
		||||
+
 | 
			
		||||
+    // Paper start
 | 
			
		||||
+    public void clear() {
 | 
			
		||||
+        this.customDataTags.clear();
 | 
			
		||||
+    }
 | 
			
		||||
+    // Paper end
 | 
			
		||||
 }
 | 
			
		||||
| 
						 | 
				
			
			@ -1,130 +0,0 @@
 | 
			
		|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
 | 
			
		||||
From: Aikar <aikar@aikar.co>
 | 
			
		||||
Date: Sun, 26 Nov 2017 13:19:58 -0500
 | 
			
		||||
Subject: [PATCH] AsyncTabCompleteEvent
 | 
			
		||||
 | 
			
		||||
Let plugins be able to control tab completion of commands and chat async.
 | 
			
		||||
 | 
			
		||||
This will be useful for frameworks like ACF so we can define async safe completion handlers,
 | 
			
		||||
and avoid going to main for tab completions.
 | 
			
		||||
 | 
			
		||||
Especially useful if you need to query a database in order to obtain the results for tab
 | 
			
		||||
completion, such as offline players.
 | 
			
		||||
 | 
			
		||||
Also adds isCommand and getLocation to the sync TabCompleteEvent
 | 
			
		||||
 | 
			
		||||
diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
 | 
			
		||||
index cf42d59254f2786bfe8785249ad270d35996417a..8c2242d7e443bee26741608c65d314d8902f5765 100644
 | 
			
		||||
--- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
 | 
			
		||||
+++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
 | 
			
		||||
@@ -702,10 +702,10 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Ser
 | 
			
		||||
 
 | 
			
		||||
     @Override
 | 
			
		||||
     public void handleCustomCommandSuggestions(ServerboundCommandSuggestionPacket packet) {
 | 
			
		||||
-        PacketUtils.ensureRunningOnSameThread(packet, this, this.player.getLevel());
 | 
			
		||||
+        // PlayerConnectionUtils.ensureMainThread(packetplayintabcomplete, this, this.player.getWorldServer()); // Paper - run this async
 | 
			
		||||
         // CraftBukkit start
 | 
			
		||||
         if (this.chatSpamTickCount.addAndGet(1) > 500 && !this.server.getPlayerList().isOp(this.player.getGameProfile())) {
 | 
			
		||||
-            this.disconnect(new TranslatableComponent("disconnect.spam", new Object[0]));
 | 
			
		||||
+            server.scheduleOnMain(() -> this.disconnect(new TranslatableComponent("disconnect.spam", new Object[0]))); // Paper
 | 
			
		||||
             return;
 | 
			
		||||
         }
 | 
			
		||||
         // CraftBukkit end
 | 
			
		||||
@@ -715,12 +715,35 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Ser
 | 
			
		||||
             stringreader.skip();
 | 
			
		||||
         }
 | 
			
		||||
 
 | 
			
		||||
-        ParseResults<CommandSourceStack> parseresults = this.server.getCommands().getDispatcher().parse(stringreader, this.player.createCommandSourceStack());
 | 
			
		||||
+        // Paper start - async tab completion
 | 
			
		||||
+        com.destroystokyo.paper.event.server.AsyncTabCompleteEvent event;
 | 
			
		||||
+        java.util.List<String> completions = new java.util.ArrayList<>();
 | 
			
		||||
+        String buffer = packet.getCommand();
 | 
			
		||||
+        event = new com.destroystokyo.paper.event.server.AsyncTabCompleteEvent(this.getCraftPlayer(), completions,
 | 
			
		||||
+                buffer, true, null);
 | 
			
		||||
+        event.callEvent();
 | 
			
		||||
+        completions = event.isCancelled() ? com.google.common.collect.ImmutableList.of() : event.getCompletions();
 | 
			
		||||
+        // If the event isn't handled, we can assume that we have no completions, and so we'll ask the server
 | 
			
		||||
+        if (!event.isHandled()) {
 | 
			
		||||
+            if (!event.isCancelled()) {
 | 
			
		||||
+
 | 
			
		||||
+                this.server.scheduleOnMain(() -> { // Paper - This needs to be on main
 | 
			
		||||
+                    ParseResults<CommandSourceStack> parseresults = this.server.getCommands().getDispatcher().parse(stringreader, this.player.createCommandSourceStack());
 | 
			
		||||
 
 | 
			
		||||
-        this.server.getCommands().getDispatcher().getCompletionSuggestions(parseresults).thenAccept((suggestions) -> {
 | 
			
		||||
-            if (suggestions.isEmpty()) return; // CraftBukkit - don't send through empty suggestions - prevents [<args>] from showing for plugins with nothing more to offer
 | 
			
		||||
-            this.connection.send(new ClientboundCommandSuggestionsPacket(packet.getId(), suggestions));
 | 
			
		||||
-        });
 | 
			
		||||
+                    this.server.getCommands().getDispatcher().getCompletionSuggestions(parseresults).thenAccept((suggestions) -> {
 | 
			
		||||
+                        if (suggestions.isEmpty()) return; // CraftBukkit - don't send through empty suggestions - prevents [<args>] from showing for plugins with nothing more to offer
 | 
			
		||||
+                        this.connection.send(new ClientboundCommandSuggestionsPacket(packet.getId(), suggestions));
 | 
			
		||||
+                    });
 | 
			
		||||
+                });
 | 
			
		||||
+            }
 | 
			
		||||
+        } else if (!completions.isEmpty()) {
 | 
			
		||||
+            com.mojang.brigadier.suggestion.SuggestionsBuilder builder = new com.mojang.brigadier.suggestion.SuggestionsBuilder(packet.getCommand(), stringreader.getTotalLength());
 | 
			
		||||
+
 | 
			
		||||
+            builder = builder.createOffset(builder.getInput().lastIndexOf(' ') + 1);
 | 
			
		||||
+            completions.forEach(builder::suggest);
 | 
			
		||||
+            player.connection.send(new ClientboundCommandSuggestionsPacket(packet.getId(), builder.buildFuture().join()));
 | 
			
		||||
+        }
 | 
			
		||||
+        // Paper end - async tab completion
 | 
			
		||||
     }
 | 
			
		||||
 
 | 
			
		||||
     @Override
 | 
			
		||||
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
 | 
			
		||||
index a337967295f61f4892a2ae7dd65aeaba75a06172..435a68e6c99bffdc8d6ded9deda68d4e970a2d49 100644
 | 
			
		||||
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
 | 
			
		||||
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
 | 
			
		||||
@@ -2015,7 +2015,7 @@ public final class CraftServer implements Server {
 | 
			
		||||
             offers = this.tabCompleteChat(player, message);
 | 
			
		||||
         }
 | 
			
		||||
 
 | 
			
		||||
-        TabCompleteEvent tabEvent = new TabCompleteEvent(player, message, offers);
 | 
			
		||||
+        TabCompleteEvent tabEvent = new TabCompleteEvent(player, message, offers, message.startsWith("/") || forceCommand, pos != null ? net.minecraft.server.MCUtil.toLocation(((CraftWorld) player.getWorld()).getHandle(), new BlockPos(pos)) : null); // Paper
 | 
			
		||||
         this.getPluginManager().callEvent(tabEvent);
 | 
			
		||||
 
 | 
			
		||||
         return tabEvent.isCancelled() ? Collections.EMPTY_LIST : tabEvent.getCompletions();
 | 
			
		||||
diff --git a/src/main/java/org/bukkit/craftbukkit/command/ConsoleCommandCompleter.java b/src/main/java/org/bukkit/craftbukkit/command/ConsoleCommandCompleter.java
 | 
			
		||||
index b996fde481cebbbcce80a6c267591136db7cc0bc..e5af155d75f717d33c23e22ff8b96bb3ff87844d 100644
 | 
			
		||||
--- a/src/main/java/org/bukkit/craftbukkit/command/ConsoleCommandCompleter.java
 | 
			
		||||
+++ b/src/main/java/org/bukkit/craftbukkit/command/ConsoleCommandCompleter.java
 | 
			
		||||
@@ -28,6 +28,39 @@ public class ConsoleCommandCompleter implements Completer {
 | 
			
		||||
     public void complete(LineReader reader, ParsedLine line, List<Candidate> candidates) {
 | 
			
		||||
         final CraftServer server = this.server.server;
 | 
			
		||||
         final String buffer = line.line();
 | 
			
		||||
+        // Async Tab Complete
 | 
			
		||||
+        com.destroystokyo.paper.event.server.AsyncTabCompleteEvent event;
 | 
			
		||||
+        java.util.List<String> completions = new java.util.ArrayList<>();
 | 
			
		||||
+        event = new com.destroystokyo.paper.event.server.AsyncTabCompleteEvent(server.getConsoleSender(), completions,
 | 
			
		||||
+            buffer, true, null);
 | 
			
		||||
+        event.callEvent();
 | 
			
		||||
+        completions = event.isCancelled() ? com.google.common.collect.ImmutableList.of() : event.getCompletions();
 | 
			
		||||
+
 | 
			
		||||
+        if (event.isCancelled() || event.isHandled()) {
 | 
			
		||||
+            // Still fire sync event with the provided completions, if someone is listening
 | 
			
		||||
+            if (!event.isCancelled() && TabCompleteEvent.getHandlerList().getRegisteredListeners().length > 0) {
 | 
			
		||||
+                List<String> finalCompletions = completions;
 | 
			
		||||
+                Waitable<List<String>> syncCompletions = new Waitable<List<String>>() {
 | 
			
		||||
+                    @Override
 | 
			
		||||
+                    protected List<String> evaluate() {
 | 
			
		||||
+                        org.bukkit.event.server.TabCompleteEvent syncEvent = new org.bukkit.event.server.TabCompleteEvent(server.getConsoleSender(), buffer, finalCompletions);
 | 
			
		||||
+                        return syncEvent.callEvent() ? syncEvent.getCompletions() : com.google.common.collect.ImmutableList.of();
 | 
			
		||||
+                    }
 | 
			
		||||
+                };
 | 
			
		||||
+                server.getServer().processQueue.add(syncCompletions);
 | 
			
		||||
+                try {
 | 
			
		||||
+                    completions = syncCompletions.get();
 | 
			
		||||
+                } catch (InterruptedException | ExecutionException e1) {
 | 
			
		||||
+                    e1.printStackTrace();
 | 
			
		||||
+                }
 | 
			
		||||
+            }
 | 
			
		||||
+
 | 
			
		||||
+            if (!completions.isEmpty()) {
 | 
			
		||||
+                candidates.addAll(completions.stream().map(Candidate::new).collect(java.util.stream.Collectors.toList()));
 | 
			
		||||
+            }
 | 
			
		||||
+            return;
 | 
			
		||||
+        }
 | 
			
		||||
+
 | 
			
		||||
         // Paper end
 | 
			
		||||
         Waitable<List<String>> waitable = new Waitable<List<String>>() {
 | 
			
		||||
             @Override
 | 
			
		||||
| 
						 | 
				
			
			@ -1,20 +0,0 @@
 | 
			
		|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
 | 
			
		||||
From: Aikar <aikar@aikar.co>
 | 
			
		||||
Date: Tue, 19 Dec 2017 22:02:53 -0500
 | 
			
		||||
Subject: [PATCH] PlayerPickupExperienceEvent
 | 
			
		||||
 | 
			
		||||
Allows plugins to cancel a player picking up an experience orb
 | 
			
		||||
 | 
			
		||||
diff --git a/src/main/java/net/minecraft/world/entity/ExperienceOrb.java b/src/main/java/net/minecraft/world/entity/ExperienceOrb.java
 | 
			
		||||
index 5220e3ee9fab4c4cbc95e0cf1928392316a35e34..1fdfeaa7758497e93fc13b44996e11d74a812546 100644
 | 
			
		||||
--- a/src/main/java/net/minecraft/world/entity/ExperienceOrb.java
 | 
			
		||||
+++ b/src/main/java/net/minecraft/world/entity/ExperienceOrb.java
 | 
			
		||||
@@ -300,7 +300,7 @@ public class ExperienceOrb extends Entity {
 | 
			
		||||
     @Override
 | 
			
		||||
     public void playerTouch(Player player) {
 | 
			
		||||
         if (!this.level.isClientSide) {
 | 
			
		||||
-            if (player.takeXpDelay == 0) {
 | 
			
		||||
+            if (player.takeXpDelay == 0 && new com.destroystokyo.paper.event.player.PlayerPickupExperienceEvent(((net.minecraft.server.level.ServerPlayer) player).getBukkitEntity(), (org.bukkit.entity.ExperienceOrb) this.getBukkitEntity()).callEvent()) { // Paper
 | 
			
		||||
                 player.takeXpDelay = 2;
 | 
			
		||||
                 player.take(this, 1);
 | 
			
		||||
                 int i = this.repairPlayerItems(player, this.value);
 | 
			
		||||
| 
						 | 
				
			
			@ -1,87 +0,0 @@
 | 
			
		|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
 | 
			
		||||
From: Aikar <aikar@aikar.co>
 | 
			
		||||
Date: Wed, 20 Dec 2017 17:36:49 -0500
 | 
			
		||||
Subject: [PATCH] Ability to apply mending to XP API
 | 
			
		||||
 | 
			
		||||
This allows plugins that give players the ability to apply the experience
 | 
			
		||||
points to the Item Mending formula, which will repair an item instead
 | 
			
		||||
of giving the player experience points.
 | 
			
		||||
 | 
			
		||||
Both an API To standalone mend, and apply mending logic to .giveExp has been added.
 | 
			
		||||
 | 
			
		||||
diff --git a/src/main/java/net/minecraft/world/item/enchantment/EnchantmentHelper.java b/src/main/java/net/minecraft/world/item/enchantment/EnchantmentHelper.java
 | 
			
		||||
index 6f25e9f41d93a225acaa6575954967438a6cabbf..d439e8ce87bf7da03683a336941c7673b8b166e4 100644
 | 
			
		||||
--- a/src/main/java/net/minecraft/world/item/enchantment/EnchantmentHelper.java
 | 
			
		||||
+++ b/src/main/java/net/minecraft/world/item/enchantment/EnchantmentHelper.java
 | 
			
		||||
@@ -270,8 +270,11 @@ public class EnchantmentHelper {
 | 
			
		||||
         return getItemEnchantmentLevel(Enchantments.CHANNELING, stack) > 0;
 | 
			
		||||
     }
 | 
			
		||||
 
 | 
			
		||||
-    @Nullable
 | 
			
		||||
-    public static Entry<EquipmentSlot, ItemStack> getRandomItemWith(Enchantment enchantment, LivingEntity entity) {
 | 
			
		||||
+    @Deprecated public static @javax.annotation.Nonnull ItemStack getRandomEquippedItemWithEnchant(Enchantment enchantment, LivingEntity entityliving) {
 | 
			
		||||
+        Entry<EquipmentSlot, ItemStack> entry = getRandomItemWith(enchantment, entityliving);
 | 
			
		||||
+        return entry != null ? entry.getValue() : ItemStack.EMPTY;
 | 
			
		||||
+    } // Paper - OBFHELPER
 | 
			
		||||
+    @Nullable public static Entry<EquipmentSlot, ItemStack> getRandomItemWith(Enchantment enchantment, LivingEntity entity) {
 | 
			
		||||
         return getRandomItemWith(enchantment, entity, (stack) -> {
 | 
			
		||||
             return true;
 | 
			
		||||
         });
 | 
			
		||||
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
 | 
			
		||||
index 6b8ee829306ad22f52844ba29bf2a549558048bd..4c8617fbd2cd8e34c87634fe448d204ee7fb8a0f 100644
 | 
			
		||||
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
 | 
			
		||||
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
 | 
			
		||||
@@ -61,11 +61,14 @@ import net.minecraft.server.level.ServerPlayer;
 | 
			
		||||
 import net.minecraft.server.network.ServerGamePacketListenerImpl;
 | 
			
		||||
 import net.minecraft.server.players.UserWhiteListEntry;
 | 
			
		||||
 import net.minecraft.world.entity.Entity;
 | 
			
		||||
+import net.minecraft.world.entity.ExperienceOrb;
 | 
			
		||||
 import net.minecraft.world.entity.LivingEntity;
 | 
			
		||||
 import net.minecraft.world.entity.ai.attributes.AttributeInstance;
 | 
			
		||||
 import net.minecraft.world.entity.ai.attributes.AttributeMap;
 | 
			
		||||
 import net.minecraft.world.entity.ai.attributes.Attributes;
 | 
			
		||||
 import net.minecraft.world.inventory.AbstractContainerMenu;
 | 
			
		||||
+import net.minecraft.world.item.enchantment.EnchantmentHelper;
 | 
			
		||||
+import net.minecraft.world.item.enchantment.Enchantments;
 | 
			
		||||
 import net.minecraft.world.level.GameType;
 | 
			
		||||
 import net.minecraft.world.level.block.Blocks;
 | 
			
		||||
 import net.minecraft.world.level.block.entity.SignBlockEntity;
 | 
			
		||||
@@ -1216,8 +1219,37 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
 | 
			
		||||
         return GameMode.getByValue(this.getHandle().gameMode.getGameModeForPlayer().getId());
 | 
			
		||||
     }
 | 
			
		||||
 
 | 
			
		||||
+    // Paper start
 | 
			
		||||
+    @Override
 | 
			
		||||
+    public int applyMending(int amount) {
 | 
			
		||||
+        ServerPlayer handle = getHandle();
 | 
			
		||||
+        // Logic copied from EntityExperienceOrb and remapped to unobfuscated methods/properties
 | 
			
		||||
+        net.minecraft.world.item.ItemStack itemstack = EnchantmentHelper.getRandomEquippedItemWithEnchant(Enchantments.MENDING, handle);
 | 
			
		||||
+        if (!itemstack.isEmpty() && itemstack.getItem().canBeDepleted()) {
 | 
			
		||||
+
 | 
			
		||||
+            ExperienceOrb orb = net.minecraft.world.entity.EntityType.EXPERIENCE_ORB.create(handle.level);
 | 
			
		||||
+            orb.value = amount;
 | 
			
		||||
+            orb.spawnReason = org.bukkit.entity.ExperienceOrb.SpawnReason.CUSTOM;
 | 
			
		||||
+            orb.setPosRaw(handle.getX(), handle.getY(), handle.getZ());
 | 
			
		||||
+
 | 
			
		||||
+            int i = Math.min(orb.xpToDurability(amount), itemstack.getDamageValue());
 | 
			
		||||
+            org.bukkit.event.player.PlayerItemMendEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerItemMendEvent(handle, orb, itemstack, i);
 | 
			
		||||
+            i = event.getRepairAmount();
 | 
			
		||||
+            orb.discard();
 | 
			
		||||
+            if (!event.isCancelled()) {
 | 
			
		||||
+                amount -= orb.durabilityToXp(i);
 | 
			
		||||
+                itemstack.setDamageValue(itemstack.getDamageValue() - i);
 | 
			
		||||
+            }
 | 
			
		||||
+        }
 | 
			
		||||
+        return amount;
 | 
			
		||||
+    }
 | 
			
		||||
+
 | 
			
		||||
     @Override
 | 
			
		||||
-    public void giveExp(int exp) {
 | 
			
		||||
+    public void giveExp(int exp, boolean applyMending) {
 | 
			
		||||
+        if (applyMending) {
 | 
			
		||||
+            exp = this.applyMending(exp);
 | 
			
		||||
+        }
 | 
			
		||||
+        // Paper end
 | 
			
		||||
         this.getHandle().giveExperiencePoints(exp);
 | 
			
		||||
     }
 | 
			
		||||
 
 | 
			
		||||
| 
						 | 
				
			
			@ -1,36 +0,0 @@
 | 
			
		|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
 | 
			
		||||
From: Zach Brown <zach.brown@destroystokyo.com>
 | 
			
		||||
Date: Thu, 11 Jan 2018 16:47:28 -0600
 | 
			
		||||
Subject: [PATCH] Make max squid spawn height configurable
 | 
			
		||||
 | 
			
		||||
I don't know why upstream made only the minimum height configurable but
 | 
			
		||||
whatever
 | 
			
		||||
 | 
			
		||||
diff --git a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
 | 
			
		||||
index e2894138d3efb32161087ad2a1093b8c15c56a65..5892823425055efb92bf635b035d62981942b966 100644
 | 
			
		||||
--- a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
 | 
			
		||||
+++ b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
 | 
			
		||||
@@ -383,4 +383,9 @@ public class PaperWorldConfig {
 | 
			
		||||
         disableCreeperLingeringEffect = getBoolean("disable-creeper-lingering-effect", false);
 | 
			
		||||
         log("Creeper lingering effect: " + disableCreeperLingeringEffect);
 | 
			
		||||
     }
 | 
			
		||||
+
 | 
			
		||||
+    public double squidMaxSpawnHeight;
 | 
			
		||||
+    private void squidMaxSpawnHeight() {
 | 
			
		||||
+        squidMaxSpawnHeight = getDouble("squid-spawn-height.maximum", 0.0D);
 | 
			
		||||
+    }
 | 
			
		||||
 }
 | 
			
		||||
diff --git a/src/main/java/net/minecraft/world/entity/animal/Squid.java b/src/main/java/net/minecraft/world/entity/animal/Squid.java
 | 
			
		||||
index 3ffc1ee8a9ae63c8678c12736fab5d6ba0a21a5b..4da560f6e4da0750bda78b900b2d916d58adfccb 100644
 | 
			
		||||
--- a/src/main/java/net/minecraft/world/entity/animal/Squid.java
 | 
			
		||||
+++ b/src/main/java/net/minecraft/world/entity/animal/Squid.java
 | 
			
		||||
@@ -211,7 +211,8 @@ public class Squid extends WaterAnimal {
 | 
			
		||||
     }
 | 
			
		||||
 
 | 
			
		||||
     public static boolean checkSquidSpawnRules(EntityType<Squid> type, LevelAccessor world, MobSpawnType spawnReason, BlockPos pos, Random random) {
 | 
			
		||||
-        return pos.getY() > world.getMinecraftWorld().spigotConfig.squidSpawnRangeMin && pos.getY() < world.getSeaLevel(); // Spigot
 | 
			
		||||
+        final double maxHeight = world.getMinecraftWorld().paperConfig.squidMaxSpawnHeight > 0 ? world.getMinecraftWorld().paperConfig.squidMaxSpawnHeight : world.getSeaLevel(); // Paper
 | 
			
		||||
+        return pos.getY() > world.getMinecraftWorld().spigotConfig.squidSpawnRangeMin && pos.getY() < maxHeight; // Spigot // Paper
 | 
			
		||||
     }
 | 
			
		||||
 
 | 
			
		||||
     @Override
 | 
			
		||||
| 
						 | 
				
			
			@ -1,76 +0,0 @@
 | 
			
		|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
 | 
			
		||||
From: Aikar <aikar@aikar.co>
 | 
			
		||||
Date: Sun, 14 Jan 2018 17:36:02 -0500
 | 
			
		||||
Subject: [PATCH] PlayerNaturallySpawnCreaturesEvent
 | 
			
		||||
 | 
			
		||||
This event can be used for when you want to exclude a certain player
 | 
			
		||||
from triggering monster spawns on a server.
 | 
			
		||||
 | 
			
		||||
Also a highly more effecient way to blanket block spawns in a world
 | 
			
		||||
 | 
			
		||||
diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
 | 
			
		||||
index 4a2739edb01c97c99524dc96decbdcb12e0b7d4f..5e53ef4b71969737f900063ab631f4a1ce74cb90 100644
 | 
			
		||||
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
 | 
			
		||||
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
 | 
			
		||||
@@ -1024,11 +1024,21 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
 | 
			
		||||
         chunkRange = (chunkRange > level.spigotConfig.viewDistance) ? (byte) level.spigotConfig.viewDistance : chunkRange;
 | 
			
		||||
         chunkRange = (chunkRange > 8) ? 8 : chunkRange;
 | 
			
		||||
 
 | 
			
		||||
-        double blockRange = (reducedRange) ? Math.pow(chunkRange << 4, 2) : 16384.0D;
 | 
			
		||||
+        final int finalChunkRange = chunkRange; // Paper for lambda below
 | 
			
		||||
+        //double blockRange = (reducedRange) ? Math.pow(chunkRange << 4, 2) : 16384.0D; // Paper - use from event
 | 
			
		||||
         // Spigot end
 | 
			
		||||
         long i = chunkcoordintpair.toLong();
 | 
			
		||||
 
 | 
			
		||||
         return !this.distanceManager.hasPlayersNearby(i) ? true : this.playerMap.getPlayers(i).noneMatch((entityplayer) -> {
 | 
			
		||||
+            // Paper start - add PlayerNaturallySpawnCreaturesEvent
 | 
			
		||||
+            com.destroystokyo.paper.event.entity.PlayerNaturallySpawnCreaturesEvent event;
 | 
			
		||||
+            double blockRange = 16384.0D;
 | 
			
		||||
+            if (reducedRange) {
 | 
			
		||||
+                event = entityplayer.playerNaturallySpawnedEvent;
 | 
			
		||||
+                if (event == null || event.isCancelled()) return false;
 | 
			
		||||
+                blockRange = (double) ((event.getSpawnRadius() << 4) * (event.getSpawnRadius() << 4));
 | 
			
		||||
+            }
 | 
			
		||||
+            // Paper end
 | 
			
		||||
             return !entityplayer.isSpectator() && ChunkMap.euclideanDistanceSquared(chunkcoordintpair, (Entity) entityplayer) < blockRange; // Spigot
 | 
			
		||||
         });
 | 
			
		||||
     }
 | 
			
		||||
diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
 | 
			
		||||
index 3e2a5f83afcc3b9c9fe62748895d489135af03bf..1f3fe980e71c13b5e7852349cba1cb0e4aa42dcd 100644
 | 
			
		||||
--- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java
 | 
			
		||||
+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
 | 
			
		||||
@@ -791,6 +791,15 @@ public class ServerChunkCache extends ChunkSource {
 | 
			
		||||
             List<ChunkHolder> list = Lists.newArrayList(this.chunkMap.getChunks());
 | 
			
		||||
 
 | 
			
		||||
             Collections.shuffle(list);
 | 
			
		||||
+            // Paper start - call player naturally spawn event
 | 
			
		||||
+            int chunkRange = level.spigotConfig.mobSpawnRange;
 | 
			
		||||
+            chunkRange = (chunkRange > level.spigotConfig.viewDistance) ? (byte) level.spigotConfig.viewDistance : chunkRange;
 | 
			
		||||
+            chunkRange = Math.min(chunkRange, 8);
 | 
			
		||||
+            for (ServerPlayer entityPlayer : this.level.players()) {
 | 
			
		||||
+                entityPlayer.playerNaturallySpawnedEvent = new com.destroystokyo.paper.event.entity.PlayerNaturallySpawnCreaturesEvent(entityPlayer.getBukkitEntity(), (byte) chunkRange);
 | 
			
		||||
+                entityPlayer.playerNaturallySpawnedEvent.callEvent();
 | 
			
		||||
+            };
 | 
			
		||||
+            // Paper end
 | 
			
		||||
             this.level.timings.chunkTicks.startTiming(); // Paper
 | 
			
		||||
             list.forEach((playerchunk) -> {
 | 
			
		||||
                 Optional<LevelChunk> optional = ((Either) playerchunk.getTickingChunkFuture().getNow(ChunkHolder.UNLOADED_LEVEL_CHUNK)).left();
 | 
			
		||||
diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java
 | 
			
		||||
index d4614faa22485dce226f3dc17ef984212ac8fcb9..66434418fae67ff63450bc246796c7f3d4d09ae6 100644
 | 
			
		||||
--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java
 | 
			
		||||
+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java
 | 
			
		||||
@@ -1,5 +1,6 @@
 | 
			
		||||
 package net.minecraft.server.level;
 | 
			
		||||
 
 | 
			
		||||
+import com.destroystokyo.paper.event.entity.PlayerNaturallySpawnCreaturesEvent;
 | 
			
		||||
 import com.google.common.collect.Lists;
 | 
			
		||||
 import com.mojang.authlib.GameProfile;
 | 
			
		||||
 import com.mojang.datafixers.util.Either;
 | 
			
		||||
@@ -233,6 +234,7 @@ public class ServerPlayer extends Player {
 | 
			
		||||
     public boolean sentListPacket = false;
 | 
			
		||||
     public Integer clientViewDistance;
 | 
			
		||||
     // CraftBukkit end
 | 
			
		||||
+    public PlayerNaturallySpawnCreaturesEvent playerNaturallySpawnedEvent; // Paper
 | 
			
		||||
 
 | 
			
		||||
     public final com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<ServerPlayer> cachedSingleHashSet; // Paper
 | 
			
		||||
 
 | 
			
		||||
| 
						 | 
				
			
			@ -1,145 +0,0 @@
 | 
			
		|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
 | 
			
		||||
From: Aikar <aikar@aikar.co>
 | 
			
		||||
Date: Sun, 14 Jan 2018 17:01:31 -0500
 | 
			
		||||
Subject: [PATCH] PreCreatureSpawnEvent
 | 
			
		||||
 | 
			
		||||
Adds an event to fire before an Entity is created, so that plugins that need to cancel
 | 
			
		||||
CreatureSpawnEvent can do so from this event instead.
 | 
			
		||||
 | 
			
		||||
Cancelling CreatureSpawnEvent rapidly causes a lot of garbage collection and CPU waste
 | 
			
		||||
as it's done after the Entity object has been fully created.
 | 
			
		||||
 | 
			
		||||
Mob Limiting plugins and blanket "ban this type of monster" plugins should use this event
 | 
			
		||||
instead and save a lot of server resources.
 | 
			
		||||
 | 
			
		||||
See: https://github.com/PaperMC/Paper/issues/917
 | 
			
		||||
 | 
			
		||||
diff --git a/src/main/java/net/minecraft/world/entity/EntityType.java b/src/main/java/net/minecraft/world/entity/EntityType.java
 | 
			
		||||
index 28f1a53a2b9ebe9948509dabbf1a4ae84d8e147c..345ecbc7fc080e8581d285b638db1ee6e684010a 100644
 | 
			
		||||
--- a/src/main/java/net/minecraft/world/entity/EntityType.java
 | 
			
		||||
+++ b/src/main/java/net/minecraft/world/entity/EntityType.java
 | 
			
		||||
@@ -335,6 +335,20 @@ public class EntityType<T extends Entity> implements EntityTypeTest<Entity, T> {
 | 
			
		||||
 
 | 
			
		||||
     @Nullable
 | 
			
		||||
     public T spawnCreature(ServerLevel worldserver, @Nullable CompoundTag nbttagcompound, @Nullable Component ichatbasecomponent, @Nullable Player entityhuman, BlockPos blockposition, MobSpawnType enummobspawn, boolean flag, boolean flag1, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason spawnReason) {
 | 
			
		||||
+        // Paper start - Call PreCreatureSpawnEvent
 | 
			
		||||
+        org.bukkit.entity.EntityType type = org.bukkit.entity.EntityType.fromName(EntityType.getKey(this).getPath());
 | 
			
		||||
+        if (type != null) {
 | 
			
		||||
+            com.destroystokyo.paper.event.entity.PreCreatureSpawnEvent event;
 | 
			
		||||
+            event = new com.destroystokyo.paper.event.entity.PreCreatureSpawnEvent(
 | 
			
		||||
+                net.minecraft.server.MCUtil.toLocation(worldserver, blockposition),
 | 
			
		||||
+                type,
 | 
			
		||||
+                spawnReason
 | 
			
		||||
+            );
 | 
			
		||||
+            if (!event.callEvent()) {
 | 
			
		||||
+                return null;
 | 
			
		||||
+            }
 | 
			
		||||
+        }
 | 
			
		||||
+        // Paper end
 | 
			
		||||
         T t0 = this.create(worldserver, nbttagcompound, ichatbasecomponent, entityhuman, blockposition, enummobspawn, flag, flag1);
 | 
			
		||||
 
 | 
			
		||||
         if (t0 != null) {
 | 
			
		||||
diff --git a/src/main/java/net/minecraft/world/entity/npc/Villager.java b/src/main/java/net/minecraft/world/entity/npc/Villager.java
 | 
			
		||||
index 3839bacbd5f4d06eb13d0e604651232d9fbd7b6a..206aee8bf14ffc4ddbb8a7001bc3baae6a2ea849 100644
 | 
			
		||||
--- a/src/main/java/net/minecraft/world/entity/npc/Villager.java
 | 
			
		||||
+++ b/src/main/java/net/minecraft/world/entity/npc/Villager.java
 | 
			
		||||
@@ -987,6 +987,21 @@ public class Villager extends AbstractVillager implements ReputationEventHandler
 | 
			
		||||
             BlockPos blockposition1 = this.findSpawnPositionForGolemInColumn(blockposition, d0, d1);
 | 
			
		||||
 
 | 
			
		||||
             if (blockposition1 != null) {
 | 
			
		||||
+                // Paper start - Call PreCreatureSpawnEvent
 | 
			
		||||
+                com.destroystokyo.paper.event.entity.PreCreatureSpawnEvent event;
 | 
			
		||||
+                event = new com.destroystokyo.paper.event.entity.PreCreatureSpawnEvent(
 | 
			
		||||
+                    net.minecraft.server.MCUtil.toLocation(level, blockposition1),
 | 
			
		||||
+                    org.bukkit.entity.EntityType.IRON_GOLEM,
 | 
			
		||||
+                    org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.VILLAGE_DEFENSE
 | 
			
		||||
+                );
 | 
			
		||||
+                if (!event.callEvent()) {
 | 
			
		||||
+                    if (event.shouldAbortSpawn()) {
 | 
			
		||||
+                        GolemSensor.golemDetected(this); // Set Golem Last Seen to stop it from spawning another one
 | 
			
		||||
+                        return null;
 | 
			
		||||
+                    }
 | 
			
		||||
+                    break;
 | 
			
		||||
+                }
 | 
			
		||||
+                // Paper end
 | 
			
		||||
                 IronGolem entityirongolem = (IronGolem) EntityType.IRON_GOLEM.create(world, (CompoundTag) null, (Component) null, (Player) null, blockposition1, MobSpawnType.MOB_SUMMONED, false, false);
 | 
			
		||||
 
 | 
			
		||||
                 if (entityirongolem != null) {
 | 
			
		||||
diff --git a/src/main/java/net/minecraft/world/level/BaseSpawner.java b/src/main/java/net/minecraft/world/level/BaseSpawner.java
 | 
			
		||||
index 14188ac6f158b36755abe23c0a967763cf7367d8..572328fafb2347886900352983fd5b6490b0dd68 100644
 | 
			
		||||
--- a/src/main/java/net/minecraft/world/level/BaseSpawner.java
 | 
			
		||||
+++ b/src/main/java/net/minecraft/world/level/BaseSpawner.java
 | 
			
		||||
@@ -133,6 +133,27 @@ public abstract class BaseSpawner {
 | 
			
		||||
                     double d2 = j >= 3 ? nbttaglist.getDouble(2) : (double) pos.getZ() + (world.random.nextDouble() - world.random.nextDouble()) * (double) this.spawnRange + 0.5D;
 | 
			
		||||
 
 | 
			
		||||
                     if (world.noCollision(((EntityType) optional.get()).getAABB(d0, d1, d2)) && SpawnPlacements.checkSpawnRules((EntityType) optional.get(), world, MobSpawnType.SPAWNER, new BlockPos(d0, d1, d2), world.getRandom())) {
 | 
			
		||||
+                        // Paper start
 | 
			
		||||
+                        EntityType<?> entityType = optional.get();
 | 
			
		||||
+                        String key = EntityType.getKey(entityType).getPath();
 | 
			
		||||
+
 | 
			
		||||
+                        org.bukkit.entity.EntityType type = org.bukkit.entity.EntityType.fromName(key);
 | 
			
		||||
+                        if (type != null) {
 | 
			
		||||
+                            com.destroystokyo.paper.event.entity.PreCreatureSpawnEvent event;
 | 
			
		||||
+                            event = new com.destroystokyo.paper.event.entity.PreCreatureSpawnEvent(
 | 
			
		||||
+                                net.minecraft.server.MCUtil.toLocation(world, d0, d1, d2),
 | 
			
		||||
+                                type,
 | 
			
		||||
+                                org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.SPAWNER
 | 
			
		||||
+                            );
 | 
			
		||||
+                            if (!event.callEvent()) {
 | 
			
		||||
+                                flag = true;
 | 
			
		||||
+                                if (event.shouldAbortSpawn()) {
 | 
			
		||||
+                                    break;
 | 
			
		||||
+                                }
 | 
			
		||||
+                                continue;
 | 
			
		||||
+                            }
 | 
			
		||||
+                        }
 | 
			
		||||
+                        // Paper end
 | 
			
		||||
                         Entity entity = EntityType.loadEntityRecursive(nbttagcompound, world, (entity1) -> {
 | 
			
		||||
                             entity1.moveTo(d0, d1, d2, entity1.getYRot(), entity1.getXRot());
 | 
			
		||||
                             return entity1;
 | 
			
		||||
diff --git a/src/main/java/net/minecraft/world/level/NaturalSpawner.java b/src/main/java/net/minecraft/world/level/NaturalSpawner.java
 | 
			
		||||
index 0432ad7ab00c336e7c566f24c3ec92b399cb6e9d..ca0fcf46e67deb07a3fdb071b771a7603e0fc3d0 100644
 | 
			
		||||
--- a/src/main/java/net/minecraft/world/level/NaturalSpawner.java
 | 
			
		||||
+++ b/src/main/java/net/minecraft/world/level/NaturalSpawner.java
 | 
			
		||||
@@ -236,7 +236,13 @@ public final class NaturalSpawner {
 | 
			
		||||
                                         j1 = biomesettingsmobs_c.minCount + world.random.nextInt(1 + biomesettingsmobs_c.maxCount - biomesettingsmobs_c.minCount);
 | 
			
		||||
                                     }
 | 
			
		||||
 
 | 
			
		||||
-                                    if (NaturalSpawner.isValidSpawnPostitionForType(world, group, structuremanager, chunkgenerator, biomesettingsmobs_c, blockposition_mutableblockposition, d2) && checker.test(biomesettingsmobs_c.type, blockposition_mutableblockposition, chunk)) {
 | 
			
		||||
+                                    // Paper start
 | 
			
		||||
+                                    Boolean doSpawning = isValidSpawnPostitionForType(world, group, structuremanager, chunkgenerator, biomesettingsmobs_c, blockposition_mutableblockposition, d2);
 | 
			
		||||
+                                    if (doSpawning == null) {
 | 
			
		||||
+                                        return;
 | 
			
		||||
+                                    }
 | 
			
		||||
+                                    if (doSpawning && checker.test(biomesettingsmobs_c.type, blockposition_mutableblockposition, chunk)) {
 | 
			
		||||
+                                        // Paper end
 | 
			
		||||
                                         Mob entityinsentient = NaturalSpawner.getMobForSpawn(world, biomesettingsmobs_c.type);
 | 
			
		||||
 
 | 
			
		||||
                                         if (entityinsentient == null) {
 | 
			
		||||
@@ -283,9 +289,25 @@ public final class NaturalSpawner {
 | 
			
		||||
         return squaredDistance <= 576.0D ? false : (world.getSharedSpawnPos().closerThan((Position) (new Vec3((double) pos.getX() + 0.5D, (double) pos.getY(), (double) pos.getZ() + 0.5D)), 24.0D) ? false : Objects.equals(new ChunkPos(pos), chunk.getPos()) || world.isPositionEntityTicking((BlockPos) pos));
 | 
			
		||||
     }
 | 
			
		||||
 
 | 
			
		||||
-    private static boolean isValidSpawnPostitionForType(ServerLevel world, MobCategory group, StructureFeatureManager structureAccessor, ChunkGenerator chunkGenerator, MobSpawnSettings.SpawnerData spawnEntry, BlockPos.MutableBlockPos pos, double squaredDistance) {
 | 
			
		||||
+    private static Boolean isValidSpawnPostitionForType(ServerLevel world, MobCategory group, StructureFeatureManager structureAccessor, ChunkGenerator chunkGenerator, MobSpawnSettings.SpawnerData spawnEntry, BlockPos.MutableBlockPos pos, double squaredDistance) { // Paper
 | 
			
		||||
         EntityType<?> entitytypes = spawnEntry.type;
 | 
			
		||||
 
 | 
			
		||||
+        // Paper start
 | 
			
		||||
+        com.destroystokyo.paper.event.entity.PreCreatureSpawnEvent event;
 | 
			
		||||
+        org.bukkit.entity.EntityType type = org.bukkit.entity.EntityType.fromName(EntityType.getKey(entitytypes).getPath());
 | 
			
		||||
+        if (type != null) {
 | 
			
		||||
+            event = new com.destroystokyo.paper.event.entity.PreCreatureSpawnEvent(
 | 
			
		||||
+                net.minecraft.server.MCUtil.toLocation(world, pos),
 | 
			
		||||
+                type, SpawnReason.NATURAL
 | 
			
		||||
+            );
 | 
			
		||||
+            if (!event.callEvent()) {
 | 
			
		||||
+                if (event.shouldAbortSpawn()) {
 | 
			
		||||
+                    return null;
 | 
			
		||||
+                }
 | 
			
		||||
+                return false;
 | 
			
		||||
+            }
 | 
			
		||||
+        }
 | 
			
		||||
+        // Paper end
 | 
			
		||||
         if (entitytypes.getCategory() == MobCategory.MISC) {
 | 
			
		||||
             return false;
 | 
			
		||||
         } else if (!entitytypes.canSpawnFarFromPlayer() && squaredDistance > (double) (entitytypes.getCategory().getDespawnDistance() * entitytypes.getCategory().getDespawnDistance())) {
 | 
			
		||||
| 
						 | 
				
			
			@ -1,90 +0,0 @@
 | 
			
		|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
 | 
			
		||||
From: Aikar <aikar@aikar.co>
 | 
			
		||||
Date: Fri, 19 Jan 2018 00:36:25 -0500
 | 
			
		||||
Subject: [PATCH] Add setPlayerProfile API for Skulls
 | 
			
		||||
 | 
			
		||||
This allows you to create already filled textures on Skulls to avoid texture lookups
 | 
			
		||||
which commonly cause rate limit issues with Mojang API
 | 
			
		||||
 | 
			
		||||
diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftSkull.java b/src/main/java/org/bukkit/craftbukkit/block/CraftSkull.java
 | 
			
		||||
index d6a4638271644e31fbc38f5ae9150ded63a6d62f..e89eb5d631b4226d79caf49c89ebb44155e72a0f 100644
 | 
			
		||||
--- a/src/main/java/org/bukkit/craftbukkit/block/CraftSkull.java
 | 
			
		||||
+++ b/src/main/java/org/bukkit/craftbukkit/block/CraftSkull.java
 | 
			
		||||
@@ -1,5 +1,7 @@
 | 
			
		||||
 package org.bukkit.craftbukkit.block;
 | 
			
		||||
 
 | 
			
		||||
+import com.destroystokyo.paper.profile.CraftPlayerProfile;
 | 
			
		||||
+import com.destroystokyo.paper.profile.PlayerProfile;
 | 
			
		||||
 import com.google.common.base.Preconditions;
 | 
			
		||||
 import com.mojang.authlib.GameProfile;
 | 
			
		||||
 import net.minecraft.server.MinecraftServer;
 | 
			
		||||
@@ -100,6 +102,20 @@ public class CraftSkull extends CraftBlockEntityState<SkullBlockEntity> implemen
 | 
			
		||||
         }
 | 
			
		||||
     }
 | 
			
		||||
 
 | 
			
		||||
+    // Paper start
 | 
			
		||||
+    @Override
 | 
			
		||||
+    public void setPlayerProfile(PlayerProfile profile) {
 | 
			
		||||
+        Preconditions.checkNotNull(profile, "profile");
 | 
			
		||||
+        this.profile = CraftPlayerProfile.asAuthlibCopy(profile);
 | 
			
		||||
+    }
 | 
			
		||||
+
 | 
			
		||||
+    @javax.annotation.Nullable
 | 
			
		||||
+    @Override
 | 
			
		||||
+    public PlayerProfile getPlayerProfile() {
 | 
			
		||||
+        return profile != null ? CraftPlayerProfile.asBukkitCopy(profile) : null;
 | 
			
		||||
+    }
 | 
			
		||||
+    // Paper end
 | 
			
		||||
+
 | 
			
		||||
     @Override
 | 
			
		||||
     public BlockFace getRotation() {
 | 
			
		||||
         BlockData blockData = getBlockData();
 | 
			
		||||
diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaSkull.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaSkull.java
 | 
			
		||||
index 490df0dcfd0e1e0ab05943410493522f86444ef8..7cacc61fed0c610845c67894d1cc68e44f5e46fe 100644
 | 
			
		||||
--- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaSkull.java
 | 
			
		||||
+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaSkull.java
 | 
			
		||||
@@ -4,10 +4,8 @@ import com.google.common.collect.ImmutableMap.Builder;
 | 
			
		||||
 import com.mojang.authlib.GameProfile;
 | 
			
		||||
 import java.util.Map;
 | 
			
		||||
 import java.util.UUID;
 | 
			
		||||
-import net.minecraft.nbt.CompoundTag;
 | 
			
		||||
-import net.minecraft.nbt.NbtUtils;
 | 
			
		||||
-import net.minecraft.nbt.Tag;
 | 
			
		||||
-import net.minecraft.world.level.block.entity.SkullBlockEntity;
 | 
			
		||||
+import com.destroystokyo.paper.profile.CraftPlayerProfile;
 | 
			
		||||
+import com.destroystokyo.paper.profile.PlayerProfile;
 | 
			
		||||
 import org.bukkit.Bukkit;
 | 
			
		||||
 import org.bukkit.Material;
 | 
			
		||||
 import org.bukkit.OfflinePlayer;
 | 
			
		||||
@@ -18,6 +16,11 @@ import org.bukkit.craftbukkit.inventory.CraftMetaItem.SerializableMeta;
 | 
			
		||||
 import org.bukkit.craftbukkit.util.CraftMagicNumbers;
 | 
			
		||||
 import org.bukkit.inventory.meta.SkullMeta;
 | 
			
		||||
 
 | 
			
		||||
+import javax.annotation.Nullable;
 | 
			
		||||
+import net.minecraft.nbt.CompoundTag;
 | 
			
		||||
+import net.minecraft.nbt.NbtUtils;
 | 
			
		||||
+import net.minecraft.nbt.Tag;
 | 
			
		||||
+import net.minecraft.world.level.block.entity.SkullBlockEntity;
 | 
			
		||||
 @DelegateDeserialization(SerializableMeta.class)
 | 
			
		||||
 class CraftMetaSkull extends CraftMetaItem implements SkullMeta {
 | 
			
		||||
 
 | 
			
		||||
@@ -151,6 +154,19 @@ class CraftMetaSkull extends CraftMetaItem implements SkullMeta {
 | 
			
		||||
         return this.hasOwner() ? this.profile.getName() : null;
 | 
			
		||||
     }
 | 
			
		||||
 
 | 
			
		||||
+    // Paper start
 | 
			
		||||
+    @Override
 | 
			
		||||
+    public void setPlayerProfile(@Nullable PlayerProfile profile) {
 | 
			
		||||
+        setProfile((profile == null) ? null : CraftPlayerProfile.asAuthlibCopy(profile));
 | 
			
		||||
+    }
 | 
			
		||||
+
 | 
			
		||||
+    @Nullable
 | 
			
		||||
+    @Override
 | 
			
		||||
+    public PlayerProfile getPlayerProfile() {
 | 
			
		||||
+        return profile != null ? CraftPlayerProfile.asBukkitCopy(profile) : null;
 | 
			
		||||
+    }
 | 
			
		||||
+    // Paper end
 | 
			
		||||
+
 | 
			
		||||
     @Override
 | 
			
		||||
     public OfflinePlayer getOwningPlayer() {
 | 
			
		||||
         if (this.hasOwner()) {
 | 
			
		||||
| 
						 | 
				
			
			@ -1,42 +0,0 @@
 | 
			
		|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
 | 
			
		||||
From: Aikar <aikar@aikar.co>
 | 
			
		||||
Date: Tue, 2 Jan 2018 00:31:26 -0500
 | 
			
		||||
Subject: [PATCH] Fill Profile Property Events
 | 
			
		||||
 | 
			
		||||
Allows plugins to populate profile properties from local sources to avoid calls out to Mojang API
 | 
			
		||||
to fill in textures for example.
 | 
			
		||||
 | 
			
		||||
If Mojang API does need to be hit, event fire so you can get the results.
 | 
			
		||||
 | 
			
		||||
This is useful for implementing a ProfileCache for Player Skulls
 | 
			
		||||
 | 
			
		||||
diff --git a/src/main/java/com/destroystokyo/paper/profile/PaperMinecraftSessionService.java b/src/main/java/com/destroystokyo/paper/profile/PaperMinecraftSessionService.java
 | 
			
		||||
index 93d73c27340645c7502acafdc0b2cfbc1a759dd8..5c7d2ee19243d0911a3a00af3ae42078a2ccba94 100644
 | 
			
		||||
--- a/src/main/java/com/destroystokyo/paper/profile/PaperMinecraftSessionService.java
 | 
			
		||||
+++ b/src/main/java/com/destroystokyo/paper/profile/PaperMinecraftSessionService.java
 | 
			
		||||
@@ -1,6 +1,8 @@
 | 
			
		||||
 package com.destroystokyo.paper.profile;
 | 
			
		||||
 
 | 
			
		||||
 import com.mojang.authlib.Environment;
 | 
			
		||||
+import com.destroystokyo.paper.event.profile.FillProfileEvent;
 | 
			
		||||
+import com.destroystokyo.paper.event.profile.PreFillProfileEvent;
 | 
			
		||||
 import com.mojang.authlib.GameProfile;
 | 
			
		||||
 import com.mojang.authlib.minecraft.MinecraftProfileTexture;
 | 
			
		||||
 import com.mojang.authlib.yggdrasil.YggdrasilAuthenticationService;
 | 
			
		||||
@@ -20,7 +22,15 @@ public class PaperMinecraftSessionService extends YggdrasilMinecraftSessionServi
 | 
			
		||||
 
 | 
			
		||||
     @Override
 | 
			
		||||
     public GameProfile fillProfileProperties(GameProfile profile, boolean requireSecure) {
 | 
			
		||||
-        return super.fillProfileProperties(profile, requireSecure);
 | 
			
		||||
+        CraftPlayerProfile playerProfile = (CraftPlayerProfile) CraftPlayerProfile.asBukkitMirror(profile);
 | 
			
		||||
+        new PreFillProfileEvent(playerProfile).callEvent();
 | 
			
		||||
+        profile = playerProfile.getGameProfile();
 | 
			
		||||
+        if (profile.isComplete() && profile.getProperties().containsKey("textures")) {
 | 
			
		||||
+            return profile;
 | 
			
		||||
+        }
 | 
			
		||||
+        GameProfile gameProfile = super.fillProfileProperties(profile, requireSecure);
 | 
			
		||||
+        new FillProfileEvent(CraftPlayerProfile.asBukkitMirror(gameProfile)).callEvent();
 | 
			
		||||
+        return gameProfile;
 | 
			
		||||
     }
 | 
			
		||||
 
 | 
			
		||||
     @Override
 | 
			
		||||
| 
						 | 
				
			
			@ -1,23 +0,0 @@
 | 
			
		|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
 | 
			
		||||
From: BillyGalbreath <Blake.Galbreath@GMail.com>
 | 
			
		||||
Date: Fri, 19 Jan 2018 08:15:29 -0600
 | 
			
		||||
Subject: [PATCH] PlayerAdvancementCriterionGrantEvent
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
diff --git a/src/main/java/net/minecraft/server/PlayerAdvancements.java b/src/main/java/net/minecraft/server/PlayerAdvancements.java
 | 
			
		||||
index f26bdd3d6eb0ae38c1d7b50f29942fcf2207e3a1..3d82f984648605d58fae3c57f145d0da8a2ae225 100644
 | 
			
		||||
--- a/src/main/java/net/minecraft/server/PlayerAdvancements.java
 | 
			
		||||
+++ b/src/main/java/net/minecraft/server/PlayerAdvancements.java
 | 
			
		||||
@@ -277,6 +277,12 @@ public class PlayerAdvancements {
 | 
			
		||||
         boolean flag1 = advancementprogress.isDone();
 | 
			
		||||
 
 | 
			
		||||
         if (advancementprogress.grantProgress(criterionName)) {
 | 
			
		||||
+            // Paper start
 | 
			
		||||
+            if (!new com.destroystokyo.paper.event.player.PlayerAdvancementCriterionGrantEvent(this.player.getBukkitEntity(), advancement.bukkit, criterionName).callEvent()) {
 | 
			
		||||
+                advancementprogress.revokeProgress(criterionName);
 | 
			
		||||
+                return false;
 | 
			
		||||
+            }
 | 
			
		||||
+            // Paper end
 | 
			
		||||
             this.unregisterListeners(advancement);
 | 
			
		||||
             this.progressChanged.add(advancement);
 | 
			
		||||
             flag = true;
 | 
			
		||||
| 
						 | 
				
			
			@ -1,288 +0,0 @@
 | 
			
		|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
 | 
			
		||||
From: Zach Brown <zach.brown@destroystokyo.com>
 | 
			
		||||
Date: Sat, 27 Jan 2018 17:04:14 -0500
 | 
			
		||||
Subject: [PATCH] Add ArmorStand Item Meta
 | 
			
		||||
 | 
			
		||||
This is adds basic item meta for armor stands. It does not add all
 | 
			
		||||
possible metadata however.
 | 
			
		||||
 | 
			
		||||
There are armor, hand, and equipment types, as well as position data
 | 
			
		||||
that can also be added here. This initial addition should serve a
 | 
			
		||||
starting point for future additions in this area.
 | 
			
		||||
 | 
			
		||||
Fixes GH-559
 | 
			
		||||
 | 
			
		||||
diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaArmorStand.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaArmorStand.java
 | 
			
		||||
index aee796567f11c8b93ac9ec0b8cb8f3a8412b23ce..39b98305632271e7375afe6c7001f241c17e103d 100644
 | 
			
		||||
--- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaArmorStand.java
 | 
			
		||||
+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaArmorStand.java
 | 
			
		||||
@@ -9,9 +9,22 @@ import org.bukkit.configuration.serialization.DelegateDeserialization;
 | 
			
		||||
 import org.bukkit.craftbukkit.inventory.CraftMetaItem.ItemMetaKey;
 | 
			
		||||
 
 | 
			
		||||
 @DelegateDeserialization(CraftMetaItem.SerializableMeta.class)
 | 
			
		||||
-public class CraftMetaArmorStand extends CraftMetaItem {
 | 
			
		||||
+public class CraftMetaArmorStand extends CraftMetaItem implements com.destroystokyo.paper.inventory.meta.ArmorStandMeta { // Paper
 | 
			
		||||
 
 | 
			
		||||
     static final ItemMetaKey ENTITY_TAG = new ItemMetaKey("EntityTag", "entity-tag");
 | 
			
		||||
+    // Paper start
 | 
			
		||||
+    static final ItemMetaKey INVISIBLE = new ItemMetaKey("Invisible", "invisible");
 | 
			
		||||
+    static final ItemMetaKey NO_BASE_PLATE = new ItemMetaKey("NoBasePlate", "no-base-plate");
 | 
			
		||||
+    static final ItemMetaKey SHOW_ARMS = new ItemMetaKey("ShowArms", "show-arms");
 | 
			
		||||
+    static final ItemMetaKey SMALL = new ItemMetaKey("Small", "small");
 | 
			
		||||
+    static final ItemMetaKey MARKER = new ItemMetaKey("Marker", "marker");
 | 
			
		||||
+
 | 
			
		||||
+    private boolean invisible;
 | 
			
		||||
+    private boolean noBasePlate;
 | 
			
		||||
+    private boolean showArms;
 | 
			
		||||
+    private boolean small;
 | 
			
		||||
+    private boolean marker;
 | 
			
		||||
+    // Paper end
 | 
			
		||||
     CompoundTag entityTag;
 | 
			
		||||
 
 | 
			
		||||
     CraftMetaArmorStand(CraftMetaItem meta) {
 | 
			
		||||
@@ -22,6 +35,13 @@ public class CraftMetaArmorStand extends CraftMetaItem {
 | 
			
		||||
         }
 | 
			
		||||
 
 | 
			
		||||
         CraftMetaArmorStand armorStand = (CraftMetaArmorStand) meta;
 | 
			
		||||
+        // Paper start
 | 
			
		||||
+        this.invisible = armorStand.invisible;
 | 
			
		||||
+        this.noBasePlate = armorStand.noBasePlate;
 | 
			
		||||
+        this.showArms = armorStand.showArms;
 | 
			
		||||
+        this.small = armorStand.small;
 | 
			
		||||
+        this.marker = armorStand.marker;
 | 
			
		||||
+        // Paper end
 | 
			
		||||
         this.entityTag = armorStand.entityTag;
 | 
			
		||||
     }
 | 
			
		||||
 
 | 
			
		||||
@@ -30,11 +50,40 @@ public class CraftMetaArmorStand extends CraftMetaItem {
 | 
			
		||||
 
 | 
			
		||||
         if (tag.contains(ENTITY_TAG.NBT)) {
 | 
			
		||||
             this.entityTag = tag.getCompound(ENTITY_TAG.NBT);
 | 
			
		||||
+
 | 
			
		||||
+            // Paper start
 | 
			
		||||
+            if (entityTag.contains(INVISIBLE.NBT)) {
 | 
			
		||||
+                invisible = entityTag.getBoolean(INVISIBLE.NBT);
 | 
			
		||||
+            }
 | 
			
		||||
+
 | 
			
		||||
+            if (entityTag.contains(NO_BASE_PLATE.NBT)) {
 | 
			
		||||
+                noBasePlate = entityTag.getBoolean(NO_BASE_PLATE.NBT);
 | 
			
		||||
+            }
 | 
			
		||||
+
 | 
			
		||||
+            if (entityTag.contains(SHOW_ARMS.NBT)) {
 | 
			
		||||
+                showArms = entityTag.getBoolean(SHOW_ARMS.NBT);
 | 
			
		||||
+            }
 | 
			
		||||
+
 | 
			
		||||
+            if (entityTag.contains(SMALL.NBT)) {
 | 
			
		||||
+                small = entityTag.getBoolean(SMALL.NBT);
 | 
			
		||||
+            }
 | 
			
		||||
+
 | 
			
		||||
+            if (entityTag.contains(MARKER.NBT)) {
 | 
			
		||||
+                marker = entityTag.getBoolean(MARKER.NBT);
 | 
			
		||||
+            }
 | 
			
		||||
+            // Paper end
 | 
			
		||||
         }
 | 
			
		||||
     }
 | 
			
		||||
 
 | 
			
		||||
     CraftMetaArmorStand(Map<String, Object> map) {
 | 
			
		||||
         super(map);
 | 
			
		||||
+        // Paper start
 | 
			
		||||
+        this.invisible = SerializableMeta.getBoolean(map, INVISIBLE.BUKKIT);
 | 
			
		||||
+        this.noBasePlate = SerializableMeta.getBoolean(map, NO_BASE_PLATE.BUKKIT);
 | 
			
		||||
+        this.showArms = SerializableMeta.getBoolean(map, SHOW_ARMS.BUKKIT);
 | 
			
		||||
+        this.small = SerializableMeta.getBoolean(map, SMALL.BUKKIT);
 | 
			
		||||
+        this.marker = SerializableMeta.getBoolean(map, MARKER.BUKKIT);
 | 
			
		||||
+        // Paper end
 | 
			
		||||
     }
 | 
			
		||||
 
 | 
			
		||||
     @Override
 | 
			
		||||
@@ -57,6 +106,31 @@ public class CraftMetaArmorStand extends CraftMetaItem {
 | 
			
		||||
     void applyToItem(CompoundTag tag) {
 | 
			
		||||
         super.applyToItem(tag);
 | 
			
		||||
 
 | 
			
		||||
+        // Paper start
 | 
			
		||||
+        if (!isArmorStandEmpty() && this.entityTag == null) {
 | 
			
		||||
+            this.entityTag = new CompoundTag();
 | 
			
		||||
+        }
 | 
			
		||||
+
 | 
			
		||||
+        if (isInvisible()) {
 | 
			
		||||
+            this.entityTag.putBoolean(INVISIBLE.NBT, this.invisible);
 | 
			
		||||
+        }
 | 
			
		||||
+
 | 
			
		||||
+        if (hasNoBasePlate()) {
 | 
			
		||||
+            this.entityTag.putBoolean(NO_BASE_PLATE.NBT, this.noBasePlate);
 | 
			
		||||
+        }
 | 
			
		||||
+
 | 
			
		||||
+        if (shouldShowArms()) {
 | 
			
		||||
+            this.entityTag.putBoolean(SHOW_ARMS.NBT, this.showArms);
 | 
			
		||||
+        }
 | 
			
		||||
+
 | 
			
		||||
+        if (isSmall()) {
 | 
			
		||||
+            this.entityTag.putBoolean(SMALL.NBT, this.small);
 | 
			
		||||
+        }
 | 
			
		||||
+
 | 
			
		||||
+        if (isMarker()) {
 | 
			
		||||
+            this.entityTag.putBoolean(MARKER.NBT, this.marker);
 | 
			
		||||
+        }
 | 
			
		||||
+        // Paper end
 | 
			
		||||
         if (this.entityTag != null) {
 | 
			
		||||
             tag.put(ENTITY_TAG.NBT, entityTag);
 | 
			
		||||
         }
 | 
			
		||||
@@ -78,7 +152,7 @@ public class CraftMetaArmorStand extends CraftMetaItem {
 | 
			
		||||
     }
 | 
			
		||||
 
 | 
			
		||||
     boolean isArmorStandEmpty() {
 | 
			
		||||
-        return !(this.entityTag != null);
 | 
			
		||||
+        return !(this.isInvisible() || this.hasNoBasePlate() || this.shouldShowArms() || this.isSmall() || this.isMarker() || this.entityTag != null);
 | 
			
		||||
     }
 | 
			
		||||
 
 | 
			
		||||
     @Override
 | 
			
		||||
@@ -89,7 +163,13 @@ public class CraftMetaArmorStand extends CraftMetaItem {
 | 
			
		||||
         if (meta instanceof CraftMetaArmorStand) {
 | 
			
		||||
             CraftMetaArmorStand that = (CraftMetaArmorStand) meta;
 | 
			
		||||
 
 | 
			
		||||
-            return this.entityTag != null ? that.entityTag != null && this.entityTag.equals(that.entityTag) : this.entityTag == null;
 | 
			
		||||
+            // Paper start
 | 
			
		||||
+            return this.invisible == that.invisible &&
 | 
			
		||||
+                this.noBasePlate == that.noBasePlate &&
 | 
			
		||||
+                this.showArms == that.showArms &&
 | 
			
		||||
+                this.small == that.small &&
 | 
			
		||||
+                this.marker == that.marker;
 | 
			
		||||
+            // Paper end
 | 
			
		||||
         }
 | 
			
		||||
         return true;
 | 
			
		||||
     }
 | 
			
		||||
@@ -104,9 +184,14 @@ public class CraftMetaArmorStand extends CraftMetaItem {
 | 
			
		||||
         final int original;
 | 
			
		||||
         int hash = original = super.applyHash();
 | 
			
		||||
 
 | 
			
		||||
-        if (this.entityTag != null) {
 | 
			
		||||
-            hash = 73 * hash + this.entityTag.hashCode();
 | 
			
		||||
-        }
 | 
			
		||||
+        // Paper start
 | 
			
		||||
+        hash += this.entityTag != null ? 73 * hash + this.entityTag.hashCode() : 0;
 | 
			
		||||
+        hash += this.isInvisible() ? 61 * hash + 1231 : 0;
 | 
			
		||||
+        hash += this.hasNoBasePlate() ? 61 * hash + 1231 : 0;
 | 
			
		||||
+        hash += this.shouldShowArms() ? 61 * hash + 1231 : 0;
 | 
			
		||||
+        hash += this.isSmall() ? 61 * hash + 1231 : 0;
 | 
			
		||||
+        hash += this.isMarker() ? 61 * hash + 1231 : 0;
 | 
			
		||||
+        // Paper end
 | 
			
		||||
 
 | 
			
		||||
         return original != hash ? CraftMetaArmorStand.class.hashCode() ^ hash : hash;
 | 
			
		||||
     }
 | 
			
		||||
@@ -115,6 +200,28 @@ public class CraftMetaArmorStand extends CraftMetaItem {
 | 
			
		||||
     Builder<String, Object> serialize(Builder<String, Object> builder) {
 | 
			
		||||
         super.serialize(builder);
 | 
			
		||||
 
 | 
			
		||||
+        // Paper start
 | 
			
		||||
+        if (this.isInvisible()) {
 | 
			
		||||
+            builder.put(INVISIBLE.BUKKIT, invisible);
 | 
			
		||||
+        }
 | 
			
		||||
+
 | 
			
		||||
+        if (this.hasNoBasePlate()) {
 | 
			
		||||
+            builder.put(NO_BASE_PLATE.BUKKIT, noBasePlate);
 | 
			
		||||
+        }
 | 
			
		||||
+
 | 
			
		||||
+        if (this.shouldShowArms()) {
 | 
			
		||||
+            builder.put(SHOW_ARMS.BUKKIT, showArms);
 | 
			
		||||
+        }
 | 
			
		||||
+
 | 
			
		||||
+        if (this.isSmall()) {
 | 
			
		||||
+            builder.put(SMALL.BUKKIT, small);
 | 
			
		||||
+        }
 | 
			
		||||
+
 | 
			
		||||
+        if (this.isMarker()) {
 | 
			
		||||
+            builder.put(MARKER.BUKKIT, marker);
 | 
			
		||||
+        }
 | 
			
		||||
+        // Paper end
 | 
			
		||||
+
 | 
			
		||||
         return builder;
 | 
			
		||||
     }
 | 
			
		||||
 
 | 
			
		||||
@@ -128,4 +235,56 @@ public class CraftMetaArmorStand extends CraftMetaItem {
 | 
			
		||||
 
 | 
			
		||||
         return clone;
 | 
			
		||||
     }
 | 
			
		||||
+
 | 
			
		||||
+    // Paper start
 | 
			
		||||
+    @Override
 | 
			
		||||
+    public boolean isInvisible() {
 | 
			
		||||
+        return invisible;
 | 
			
		||||
+    }
 | 
			
		||||
+
 | 
			
		||||
+    @Override
 | 
			
		||||
+    public boolean hasNoBasePlate() {
 | 
			
		||||
+        return noBasePlate;
 | 
			
		||||
+    }
 | 
			
		||||
+
 | 
			
		||||
+    @Override
 | 
			
		||||
+    public boolean shouldShowArms() {
 | 
			
		||||
+        return showArms;
 | 
			
		||||
+    }
 | 
			
		||||
+
 | 
			
		||||
+    @Override
 | 
			
		||||
+    public boolean isSmall() {
 | 
			
		||||
+        return small;
 | 
			
		||||
+    }
 | 
			
		||||
+
 | 
			
		||||
+    @Override
 | 
			
		||||
+    public boolean isMarker() {
 | 
			
		||||
+        return marker;
 | 
			
		||||
+    }
 | 
			
		||||
+
 | 
			
		||||
+    @Override
 | 
			
		||||
+    public void setInvisible(boolean invisible) {
 | 
			
		||||
+        this.invisible = invisible;
 | 
			
		||||
+    }
 | 
			
		||||
+
 | 
			
		||||
+    @Override
 | 
			
		||||
+    public void setNoBasePlate(boolean noBasePlate) {
 | 
			
		||||
+        this.noBasePlate = noBasePlate;
 | 
			
		||||
+    }
 | 
			
		||||
+
 | 
			
		||||
+    @Override
 | 
			
		||||
+    public void setShowArms(boolean showArms) {
 | 
			
		||||
+        this.showArms = showArms;
 | 
			
		||||
+    }
 | 
			
		||||
+
 | 
			
		||||
+    @Override
 | 
			
		||||
+    public void setSmall(boolean small) {
 | 
			
		||||
+        this.small = small;
 | 
			
		||||
+    }
 | 
			
		||||
+
 | 
			
		||||
+    @Override
 | 
			
		||||
+    public void setMarker(boolean marker) {
 | 
			
		||||
+        this.marker = marker;
 | 
			
		||||
+    }
 | 
			
		||||
+    // Paper end
 | 
			
		||||
 }
 | 
			
		||||
diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaItem.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaItem.java
 | 
			
		||||
index 05d54f0eff89b721f01e90e79d2571baab297799..71320d9484842be3a694117de25159f3581bd2a3 100644
 | 
			
		||||
--- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaItem.java
 | 
			
		||||
+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaItem.java
 | 
			
		||||
@@ -1444,6 +1444,14 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta {
 | 
			
		||||
                         CraftMetaCrossbow.CHARGED.NBT,
 | 
			
		||||
                         CraftMetaCrossbow.CHARGED_PROJECTILES.NBT,
 | 
			
		||||
                         CraftMetaSuspiciousStew.EFFECTS.NBT,
 | 
			
		||||
+                        // Paper start
 | 
			
		||||
+                        CraftMetaArmorStand.ENTITY_TAG.NBT,
 | 
			
		||||
+                        CraftMetaArmorStand.INVISIBLE.NBT,
 | 
			
		||||
+                        CraftMetaArmorStand.NO_BASE_PLATE.NBT,
 | 
			
		||||
+                        CraftMetaArmorStand.SHOW_ARMS.NBT,
 | 
			
		||||
+                        CraftMetaArmorStand.SMALL.NBT,
 | 
			
		||||
+                        CraftMetaArmorStand.MARKER.NBT,
 | 
			
		||||
+                        // Paper end
 | 
			
		||||
                         CraftMetaCompass.LODESTONE_DIMENSION.NBT,
 | 
			
		||||
                         CraftMetaCompass.LODESTONE_POS.NBT,
 | 
			
		||||
                         CraftMetaCompass.LODESTONE_TRACKED.NBT,
 | 
			
		||||
diff --git a/src/test/java/org/bukkit/craftbukkit/inventory/ItemMetaTest.java b/src/test/java/org/bukkit/craftbukkit/inventory/ItemMetaTest.java
 | 
			
		||||
index a7505a08b952431d1dd7e4b332ede0c0d15eea64..f3a0578f53863dd0866b4c2cb957a30fa3bc6cc5 100644
 | 
			
		||||
--- a/src/test/java/org/bukkit/craftbukkit/inventory/ItemMetaTest.java
 | 
			
		||||
+++ b/src/test/java/org/bukkit/craftbukkit/inventory/ItemMetaTest.java
 | 
			
		||||
@@ -324,6 +324,7 @@ public class ItemMetaTest extends AbstractTestingBase {
 | 
			
		||||
                     final CraftMetaArmorStand meta = (CraftMetaArmorStand) cleanStack.getItemMeta();
 | 
			
		||||
                     meta.entityTag = new CompoundTag();
 | 
			
		||||
                     meta.entityTag.putBoolean("Small", true);
 | 
			
		||||
+                    meta.setInvisible(true); // Paper
 | 
			
		||||
                     cleanStack.setItemMeta(meta);
 | 
			
		||||
                     return cleanStack;
 | 
			
		||||
                 }
 | 
			
		||||
| 
						 | 
				
			
			@ -1,44 +0,0 @@
 | 
			
		|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
 | 
			
		||||
From: Shane Freeder <theboyetronic@gmail.com>
 | 
			
		||||
Date: Sun, 11 Feb 2018 10:43:46 +0000
 | 
			
		||||
Subject: [PATCH] Extend Player Interact cancellation
 | 
			
		||||
 | 
			
		||||
GUIs are opened on the client, meaning that the server cannot block them from opening,
 | 
			
		||||
However, it is possible to close these GUIs from the server.
 | 
			
		||||
 | 
			
		||||
Flower pots are also not updated on the client when interaction is cancelled, this patch
 | 
			
		||||
also resolves this.
 | 
			
		||||
 | 
			
		||||
Update adjacent blocks of doors, double plants, pistons and beds
 | 
			
		||||
when cancelling interaction.
 | 
			
		||||
 | 
			
		||||
diff --git a/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java b/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java
 | 
			
		||||
index ecfb88b4d9727ad20a2c33475cc6b1ec88821a19..315dad4789f5f2582ee9b4fc176affd1f57537ef 100644
 | 
			
		||||
--- a/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java
 | 
			
		||||
+++ b/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java
 | 
			
		||||
@@ -187,6 +187,11 @@ public class ServerPlayerGameMode {
 | 
			
		||||
                 PlayerInteractEvent event = CraftEventFactory.callPlayerInteractEvent(this.player, Action.LEFT_CLICK_BLOCK, pos, direction, this.player.getInventory().getSelected(), InteractionHand.MAIN_HAND);
 | 
			
		||||
                 if (event.isCancelled()) {
 | 
			
		||||
                     // Let the client know the block still exists
 | 
			
		||||
+                    // Paper start - brute force neighbor blocks for any attached blocks
 | 
			
		||||
+                    for (Direction dir : Direction.values()) {
 | 
			
		||||
+                        this.player.connection.send(new ClientboundBlockUpdatePacket(level, pos.relative(dir)));
 | 
			
		||||
+                    }
 | 
			
		||||
+                    // Paper end
 | 
			
		||||
                     this.player.connection.send(new ClientboundBlockUpdatePacket(this.level, pos));
 | 
			
		||||
                     // Update any tile entity data for this block
 | 
			
		||||
                     BlockEntity tileentity = this.level.getBlockEntity(pos);
 | 
			
		||||
@@ -502,7 +507,13 @@ public class ServerPlayerGameMode {
 | 
			
		||||
 
 | 
			
		||||
                 // send a correcting update to the client for the block above as well, this because of replaceable blocks (such as grass, sea grass etc)
 | 
			
		||||
                 player.connection.send(new ClientboundBlockUpdatePacket(world, blockposition.above()));
 | 
			
		||||
+            // Paper start  - extend Player Interact cancellation // TODO: consider merging this into the extracted method
 | 
			
		||||
+            } else if (iblockdata.getBlock() instanceof net.minecraft.world.level.block.StructureBlock) {
 | 
			
		||||
+                player.connection.send(new net.minecraft.network.protocol.game.ClientboundContainerClosePacket(this.player.containerMenu.containerId));
 | 
			
		||||
+            } else if (iblockdata.getBlock() instanceof net.minecraft.world.level.block.CommandBlock) {
 | 
			
		||||
+                player.connection.send(new net.minecraft.network.protocol.game.ClientboundContainerClosePacket(this.player.containerMenu.containerId));
 | 
			
		||||
             }
 | 
			
		||||
+            // Paper end - extend Player Interact cancellation
 | 
			
		||||
             player.getBukkitEntity().updateInventory(); // SPIGOT-2867
 | 
			
		||||
             enuminteractionresult = (event.useItemInHand() != Event.Result.ALLOW) ? InteractionResult.SUCCESS : InteractionResult.PASS;
 | 
			
		||||
         } else if (this.gameModeForPlayer == GameType.SPECTATOR) {
 | 
			
		||||
| 
						 | 
				
			
			@ -1,38 +0,0 @@
 | 
			
		|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
 | 
			
		||||
From: Aikar <aikar@aikar.co>
 | 
			
		||||
Date: Sat, 24 Feb 2018 01:14:55 -0500
 | 
			
		||||
Subject: [PATCH] Tameable#getOwnerUniqueId API
 | 
			
		||||
 | 
			
		||||
This is faster if all you need is the UUID, as .getOwner() will cause
 | 
			
		||||
an OfflinePlayer to be loaded from disk.
 | 
			
		||||
 | 
			
		||||
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftAbstractHorse.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftAbstractHorse.java
 | 
			
		||||
index 27a1ca43792644fc239af81dea5510f25d3328e9..69c95644b2531c1fe1c4a6cf7fee12e997dd67f4 100644
 | 
			
		||||
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftAbstractHorse.java
 | 
			
		||||
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftAbstractHorse.java
 | 
			
		||||
@@ -89,6 +89,10 @@ public abstract class CraftAbstractHorse extends CraftAnimals implements Abstrac
 | 
			
		||||
         }
 | 
			
		||||
     }
 | 
			
		||||
 
 | 
			
		||||
+    @Override
 | 
			
		||||
+    public UUID getOwnerUniqueId() {
 | 
			
		||||
+        return getOwnerUUID();
 | 
			
		||||
+    }
 | 
			
		||||
     public UUID getOwnerUUID() {
 | 
			
		||||
         return this.getHandle().getOwnerUUID();
 | 
			
		||||
     }
 | 
			
		||||
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftTameableAnimal.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftTameableAnimal.java
 | 
			
		||||
index cc90c09c26b04689e4fffa890baf0e89c38665a3..0b152d8d20924fc1ce7f5bafb050216d250f6536 100644
 | 
			
		||||
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftTameableAnimal.java
 | 
			
		||||
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftTameableAnimal.java
 | 
			
		||||
@@ -17,6 +17,10 @@ public class CraftTameableAnimal extends CraftAnimals implements Tameable, Creat
 | 
			
		||||
         return (TamableAnimal) super.getHandle();
 | 
			
		||||
     }
 | 
			
		||||
 
 | 
			
		||||
+    @Override
 | 
			
		||||
+    public UUID getOwnerUniqueId() {
 | 
			
		||||
+        return getOwnerUUID();
 | 
			
		||||
+    }
 | 
			
		||||
     public UUID getOwnerUUID() {
 | 
			
		||||
         try {
 | 
			
		||||
             return this.getHandle().getOwnerUUID();
 | 
			
		||||
| 
						 | 
				
			
			@ -1,34 +0,0 @@
 | 
			
		|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
 | 
			
		||||
From: MiniDigger <admin@minidigger.me>
 | 
			
		||||
Date: Sat, 10 Mar 2018 00:50:24 +0100
 | 
			
		||||
Subject: [PATCH] Toggleable player crits, helps mitigate hacked clients.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
diff --git a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
 | 
			
		||||
index 5892823425055efb92bf635b035d62981942b966..0e08f6e566d1c93cc89a179583d0b0939127de8b 100644
 | 
			
		||||
--- a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
 | 
			
		||||
+++ b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
 | 
			
		||||
@@ -233,6 +233,11 @@ public class PaperWorldConfig {
 | 
			
		||||
         disableChestCatDetection = getBoolean("game-mechanics.disable-chest-cat-detection", false);
 | 
			
		||||
     }
 | 
			
		||||
 
 | 
			
		||||
+    public boolean disablePlayerCrits;
 | 
			
		||||
+    private void disablePlayerCrits() {
 | 
			
		||||
+        disablePlayerCrits = getBoolean("game-mechanics.disable-player-crits", false);
 | 
			
		||||
+    }
 | 
			
		||||
+
 | 
			
		||||
     public boolean allChunksAreSlimeChunks;
 | 
			
		||||
     private void allChunksAreSlimeChunks() {
 | 
			
		||||
         allChunksAreSlimeChunks = getBoolean("all-chunks-are-slime-chunks", false);
 | 
			
		||||
diff --git a/src/main/java/net/minecraft/world/entity/player/Player.java b/src/main/java/net/minecraft/world/entity/player/Player.java
 | 
			
		||||
index 8c20af91c9298cb36fdb2700d042b1e2fccf5f54..dbab4d28c49d22807dfc582fb83353232396555b 100644
 | 
			
		||||
--- a/src/main/java/net/minecraft/world/entity/player/Player.java
 | 
			
		||||
+++ b/src/main/java/net/minecraft/world/entity/player/Player.java
 | 
			
		||||
@@ -1189,6 +1189,7 @@ public abstract class Player extends LivingEntity {
 | 
			
		||||
 
 | 
			
		||||
                     boolean flag2 = flag && this.fallDistance > 0.0F && !this.onGround && !this.onClimbable() && !this.isInWater() && !this.hasEffect(MobEffects.BLINDNESS) && !this.isPassenger() && target instanceof LivingEntity;
 | 
			
		||||
 
 | 
			
		||||
+                    flag2 = flag2 && !level.paperConfig.disablePlayerCrits; // Paper
 | 
			
		||||
                     flag2 = flag2 && !this.isSprinting();
 | 
			
		||||
                     if (flag2) {
 | 
			
		||||
                         f *= 1.5F;
 | 
			
		||||
| 
						 | 
				
			
			@ -1,34 +0,0 @@
 | 
			
		|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
 | 
			
		||||
From: Aikar <aikar@aikar.co>
 | 
			
		||||
Date: Sun, 11 Mar 2018 14:13:33 -0400
 | 
			
		||||
Subject: [PATCH] Disable Explicit Network Manager Flushing
 | 
			
		||||
 | 
			
		||||
This seems completely pointless, as packet dispatch uses .writeAndFlush.
 | 
			
		||||
 | 
			
		||||
Things seem to work fine without explicit flushing, but incase issues arise,
 | 
			
		||||
provide a System property to re-enable it using improved logic of doing the
 | 
			
		||||
flushing on the netty event loop, so it won't do the flush on the main thread.
 | 
			
		||||
 | 
			
		||||
Renable flushing by passing -Dpaper.explicit-flush=true
 | 
			
		||||
 | 
			
		||||
diff --git a/src/main/java/net/minecraft/network/Connection.java b/src/main/java/net/minecraft/network/Connection.java
 | 
			
		||||
index 06e965996a5c50bce617847e594ae0dd83403484..636ac646bec67dbd933f00614693af03481b6173 100644
 | 
			
		||||
--- a/src/main/java/net/minecraft/network/Connection.java
 | 
			
		||||
+++ b/src/main/java/net/minecraft/network/Connection.java
 | 
			
		||||
@@ -86,6 +86,7 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
 | 
			
		||||
     // Paper start - NetworkClient implementation
 | 
			
		||||
     public int protocolVersion;
 | 
			
		||||
     public java.net.InetSocketAddress virtualHost;
 | 
			
		||||
+    private static boolean enableExplicitFlush = Boolean.getBoolean("paper.explicit-flush");
 | 
			
		||||
     // Paper end
 | 
			
		||||
 
 | 
			
		||||
     public Connection(PacketFlow side) {
 | 
			
		||||
@@ -259,7 +260,7 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
 | 
			
		||||
         }
 | 
			
		||||
 
 | 
			
		||||
         if (this.channel != null) {
 | 
			
		||||
-            this.channel.flush();
 | 
			
		||||
+            if (enableExplicitFlush) this.channel.eventLoop().execute(() -> this.channel.flush()); // Paper - we don't need to explicit flush here, but allow opt in incase issues are found to a better version
 | 
			
		||||
         }
 | 
			
		||||
 
 | 
			
		||||
         if (this.tickCount++ % 20 == 0) {
 | 
			
		||||
| 
						 | 
				
			
			@ -1,250 +0,0 @@
 | 
			
		|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
 | 
			
		||||
From: Minecrell <minecrell@minecrell.net>
 | 
			
		||||
Date: Wed, 11 Oct 2017 15:56:26 +0200
 | 
			
		||||
Subject: [PATCH] Implement extended PaperServerListPingEvent
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
diff --git a/src/main/java/com/destroystokyo/paper/network/PaperServerListPingEventImpl.java b/src/main/java/com/destroystokyo/paper/network/PaperServerListPingEventImpl.java
 | 
			
		||||
new file mode 100644
 | 
			
		||||
index 0000000000000000000000000000000000000000..4ecd0c5bbea55f68549c85aa27e80e2c7e6265d4
 | 
			
		||||
--- /dev/null
 | 
			
		||||
+++ b/src/main/java/com/destroystokyo/paper/network/PaperServerListPingEventImpl.java
 | 
			
		||||
@@ -0,0 +1,31 @@
 | 
			
		||||
+package com.destroystokyo.paper.network;
 | 
			
		||||
+
 | 
			
		||||
+import com.destroystokyo.paper.event.server.PaperServerListPingEvent;
 | 
			
		||||
+import net.minecraft.server.MinecraftServer;
 | 
			
		||||
+import net.minecraft.server.level.ServerPlayer;
 | 
			
		||||
+import org.bukkit.entity.Player;
 | 
			
		||||
+import org.bukkit.util.CachedServerIcon;
 | 
			
		||||
+
 | 
			
		||||
+import javax.annotation.Nullable;
 | 
			
		||||
+
 | 
			
		||||
+class PaperServerListPingEventImpl extends PaperServerListPingEvent {
 | 
			
		||||
+
 | 
			
		||||
+    private final MinecraftServer server;
 | 
			
		||||
+
 | 
			
		||||
+    PaperServerListPingEventImpl(MinecraftServer server, StatusClient client, int protocolVersion, @Nullable CachedServerIcon icon) {
 | 
			
		||||
+        super(client, server.getMotd(), server.getPlayerCount(), server.getMaxPlayers(),
 | 
			
		||||
+                server.getServerModName() + ' ' + server.getServerVersion(), protocolVersion, icon);
 | 
			
		||||
+        this.server = server;
 | 
			
		||||
+    }
 | 
			
		||||
+
 | 
			
		||||
+    @Override
 | 
			
		||||
+    protected final Object[] getOnlinePlayers() {
 | 
			
		||||
+        return this.server.getPlayerList().players.toArray();
 | 
			
		||||
+    }
 | 
			
		||||
+
 | 
			
		||||
+    @Override
 | 
			
		||||
+    protected final Player getBukkitPlayer(Object player) {
 | 
			
		||||
+        return ((ServerPlayer) player).getBukkitEntity();
 | 
			
		||||
+    }
 | 
			
		||||
+
 | 
			
		||||
+}
 | 
			
		||||
diff --git a/src/main/java/com/destroystokyo/paper/network/PaperStatusClient.java b/src/main/java/com/destroystokyo/paper/network/PaperStatusClient.java
 | 
			
		||||
new file mode 100644
 | 
			
		||||
index 0000000000000000000000000000000000000000..d926ad804355ee2fdc5910b2505e8671602acdab
 | 
			
		||||
--- /dev/null
 | 
			
		||||
+++ b/src/main/java/com/destroystokyo/paper/network/PaperStatusClient.java
 | 
			
		||||
@@ -0,0 +1,11 @@
 | 
			
		||||
+package com.destroystokyo.paper.network;
 | 
			
		||||
+
 | 
			
		||||
+import net.minecraft.network.Connection;
 | 
			
		||||
+
 | 
			
		||||
+class PaperStatusClient extends PaperNetworkClient implements StatusClient {
 | 
			
		||||
+
 | 
			
		||||
+    PaperStatusClient(Connection networkManager) {
 | 
			
		||||
+        super(networkManager);
 | 
			
		||||
+    }
 | 
			
		||||
+
 | 
			
		||||
+}
 | 
			
		||||
diff --git a/src/main/java/com/destroystokyo/paper/network/StandardPaperServerListPingEventImpl.java b/src/main/java/com/destroystokyo/paper/network/StandardPaperServerListPingEventImpl.java
 | 
			
		||||
new file mode 100644
 | 
			
		||||
index 0000000000000000000000000000000000000000..4c2351b03b58511b80017b58ee9b20ab5193adc9
 | 
			
		||||
--- /dev/null
 | 
			
		||||
+++ b/src/main/java/com/destroystokyo/paper/network/StandardPaperServerListPingEventImpl.java
 | 
			
		||||
@@ -0,0 +1,110 @@
 | 
			
		||||
+package com.destroystokyo.paper.network;
 | 
			
		||||
+
 | 
			
		||||
+import com.destroystokyo.paper.profile.CraftPlayerProfile;
 | 
			
		||||
+import com.destroystokyo.paper.profile.PlayerProfile;
 | 
			
		||||
+import com.google.common.base.MoreObjects;
 | 
			
		||||
+import com.google.common.base.Strings;
 | 
			
		||||
+import com.mojang.authlib.GameProfile;
 | 
			
		||||
+import io.papermc.paper.adventure.AdventureComponent;
 | 
			
		||||
+import java.util.List;
 | 
			
		||||
+import java.util.UUID;
 | 
			
		||||
+import javax.annotation.Nonnull;
 | 
			
		||||
+import net.minecraft.network.Connection;
 | 
			
		||||
+import net.minecraft.network.protocol.status.ClientboundStatusResponsePacket;
 | 
			
		||||
+import net.minecraft.network.protocol.status.ServerStatus;
 | 
			
		||||
+import net.minecraft.server.MinecraftServer;
 | 
			
		||||
+
 | 
			
		||||
+public final class StandardPaperServerListPingEventImpl extends PaperServerListPingEventImpl {
 | 
			
		||||
+
 | 
			
		||||
+    private static final GameProfile[] EMPTY_PROFILES = new GameProfile[0];
 | 
			
		||||
+    private static final UUID FAKE_UUID = new UUID(0, 0);
 | 
			
		||||
+
 | 
			
		||||
+    private GameProfile[] originalSample;
 | 
			
		||||
+
 | 
			
		||||
+    private StandardPaperServerListPingEventImpl(MinecraftServer server, Connection networkManager, ServerStatus ping) {
 | 
			
		||||
+        super(server, new PaperStatusClient(networkManager), ping.getVersion() != null ? ping.getVersion().getProtocol() : -1, server.server.getServerIcon());
 | 
			
		||||
+        this.originalSample = ping.getPlayers() == null ? null : ping.getPlayers().getSample(); // GH-1473 - pre-tick race condition NPE
 | 
			
		||||
+    }
 | 
			
		||||
+
 | 
			
		||||
+    @Nonnull
 | 
			
		||||
+    @Override
 | 
			
		||||
+    public List<PlayerProfile> getPlayerSample() {
 | 
			
		||||
+        List<PlayerProfile> sample = super.getPlayerSample();
 | 
			
		||||
+
 | 
			
		||||
+        if (this.originalSample != null) {
 | 
			
		||||
+            for (GameProfile profile : this.originalSample) {
 | 
			
		||||
+                sample.add(CraftPlayerProfile.asBukkitCopy(profile));
 | 
			
		||||
+            }
 | 
			
		||||
+            this.originalSample = null;
 | 
			
		||||
+        }
 | 
			
		||||
+
 | 
			
		||||
+        return sample;
 | 
			
		||||
+    }
 | 
			
		||||
+
 | 
			
		||||
+    private GameProfile[] getPlayerSampleHandle() {
 | 
			
		||||
+        if (this.originalSample != null) {
 | 
			
		||||
+            return this.originalSample;
 | 
			
		||||
+        }
 | 
			
		||||
+
 | 
			
		||||
+        List<PlayerProfile> entries = super.getPlayerSample();
 | 
			
		||||
+        if (entries.isEmpty()) {
 | 
			
		||||
+            return EMPTY_PROFILES;
 | 
			
		||||
+        }
 | 
			
		||||
+
 | 
			
		||||
+        GameProfile[] profiles = new GameProfile[entries.size()];
 | 
			
		||||
+        for (int i = 0; i < profiles.length; i++) {
 | 
			
		||||
+            /*
 | 
			
		||||
+             * Avoid null UUIDs/names since that will make the response invalid
 | 
			
		||||
+             * on the client.
 | 
			
		||||
+             * Instead, fall back to a fake/empty UUID and an empty string as name.
 | 
			
		||||
+             * This can be used to create custom lines in the player list that do not
 | 
			
		||||
+             * refer to a specific player.
 | 
			
		||||
+             */
 | 
			
		||||
+
 | 
			
		||||
+            PlayerProfile profile = entries.get(i);
 | 
			
		||||
+            if (profile.getId() != null && profile.getName() != null) {
 | 
			
		||||
+                profiles[i] = CraftPlayerProfile.asAuthlib(profile);
 | 
			
		||||
+            } else {
 | 
			
		||||
+                profiles[i] = new GameProfile(MoreObjects.firstNonNull(profile.getId(), FAKE_UUID), Strings.nullToEmpty(profile.getName()));
 | 
			
		||||
+            }
 | 
			
		||||
+        }
 | 
			
		||||
+
 | 
			
		||||
+        return profiles;
 | 
			
		||||
+    }
 | 
			
		||||
+
 | 
			
		||||
+    @SuppressWarnings("deprecation")
 | 
			
		||||
+    public static void processRequest(MinecraftServer server, Connection networkManager) {
 | 
			
		||||
+        StandardPaperServerListPingEventImpl event = new StandardPaperServerListPingEventImpl(server, networkManager, server.getStatus());
 | 
			
		||||
+        server.server.getPluginManager().callEvent(event);
 | 
			
		||||
+
 | 
			
		||||
+        // Close connection immediately if event is cancelled
 | 
			
		||||
+        if (event.isCancelled()) {
 | 
			
		||||
+            networkManager.disconnect(null);
 | 
			
		||||
+            return;
 | 
			
		||||
+        }
 | 
			
		||||
+
 | 
			
		||||
+        // Setup response
 | 
			
		||||
+        ServerStatus ping = new ServerStatus();
 | 
			
		||||
+
 | 
			
		||||
+        // Description
 | 
			
		||||
+        ping.setDescription(new AdventureComponent(event.motd()));
 | 
			
		||||
+
 | 
			
		||||
+        // Players
 | 
			
		||||
+        if (!event.shouldHidePlayers()) {
 | 
			
		||||
+            ping.setPlayers(new ServerStatus.Players(event.getMaxPlayers(), event.getNumPlayers()));
 | 
			
		||||
+            ping.getPlayers().setSample(event.getPlayerSampleHandle());
 | 
			
		||||
+        }
 | 
			
		||||
+
 | 
			
		||||
+        // Version
 | 
			
		||||
+        ping.setVersion(new ServerStatus.Version(event.getVersion(), event.getProtocolVersion()));
 | 
			
		||||
+
 | 
			
		||||
+        // Favicon
 | 
			
		||||
+        if (event.getServerIcon() != null) {
 | 
			
		||||
+            ping.setFavicon(event.getServerIcon().getData());
 | 
			
		||||
+        }
 | 
			
		||||
+
 | 
			
		||||
+        // Send response
 | 
			
		||||
+        networkManager.send(new ClientboundStatusResponsePacket(ping));
 | 
			
		||||
+    }
 | 
			
		||||
+
 | 
			
		||||
+}
 | 
			
		||||
diff --git a/src/main/java/net/minecraft/network/protocol/status/ClientboundStatusResponsePacket.java b/src/main/java/net/minecraft/network/protocol/status/ClientboundStatusResponsePacket.java
 | 
			
		||||
index 67455a5ba75c9b816213e44d6872c5ddf8e27e98..23efad80934930beadf15e65781551d4ba7ff81b 100644
 | 
			
		||||
--- a/src/main/java/net/minecraft/network/protocol/status/ClientboundStatusResponsePacket.java
 | 
			
		||||
+++ b/src/main/java/net/minecraft/network/protocol/status/ClientboundStatusResponsePacket.java
 | 
			
		||||
@@ -10,7 +10,9 @@ import net.minecraft.util.GsonHelper;
 | 
			
		||||
 import net.minecraft.util.LowerCaseEnumTypeAdapterFactory;
 | 
			
		||||
 
 | 
			
		||||
 public class ClientboundStatusResponsePacket implements Packet<ClientStatusPacketListener> {
 | 
			
		||||
-    private static final Gson GSON = (new GsonBuilder()).registerTypeAdapter(ServerStatus.Version.class, new ServerStatus.Version.Serializer()).registerTypeAdapter(ServerStatus.Players.class, new ServerStatus.Players.Serializer()).registerTypeAdapter(ServerStatus.class, new ServerStatus.Serializer()).registerTypeHierarchyAdapter(Component.class, new Component.Serializer()).registerTypeHierarchyAdapter(Style.class, new Style.Serializer()).registerTypeAdapterFactory(new LowerCaseEnumTypeAdapterFactory()).create();
 | 
			
		||||
+    private static final Gson GSON = (new GsonBuilder()).registerTypeAdapter(ServerStatus.Version.class, new ServerStatus.Version.Serializer()).registerTypeAdapter(ServerStatus.Players.class, new ServerStatus.Players.Serializer()).registerTypeAdapter(ServerStatus.class, new ServerStatus.Serializer()).registerTypeHierarchyAdapter(Component.class, new Component.Serializer()).registerTypeHierarchyAdapter(Style.class, new Style.Serializer()).registerTypeAdapterFactory(new LowerCaseEnumTypeAdapterFactory())
 | 
			
		||||
+        .registerTypeAdapter(io.papermc.paper.adventure.AdventureComponent.class, new io.papermc.paper.adventure.AdventureComponent.Serializer())
 | 
			
		||||
+        .create();
 | 
			
		||||
     private final ServerStatus status;
 | 
			
		||||
 
 | 
			
		||||
     public ClientboundStatusResponsePacket(ServerStatus metadata) {
 | 
			
		||||
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
 | 
			
		||||
index 031bd2ed5c6fd2a859e8a69c48db3938cf71d61b..d9fab29a066367a5cf4a3486989fa7f35451801e 100644
 | 
			
		||||
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
 | 
			
		||||
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
 | 
			
		||||
@@ -2,6 +2,9 @@ package net.minecraft.server;
 | 
			
		||||
 
 | 
			
		||||
 import com.google.common.base.Splitter;
 | 
			
		||||
 import com.google.common.collect.ImmutableList;
 | 
			
		||||
+import co.aikar.timings.Timings;
 | 
			
		||||
+import com.destroystokyo.paper.event.server.PaperServerListPingEvent;
 | 
			
		||||
+import com.google.common.base.Stopwatch;
 | 
			
		||||
 import com.google.common.collect.Lists;
 | 
			
		||||
 import com.google.common.collect.Maps;
 | 
			
		||||
 import com.google.common.collect.Sets;
 | 
			
		||||
@@ -1331,7 +1334,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
 | 
			
		||||
         if (i - this.lastServerStatus >= 5000000000L) {
 | 
			
		||||
             this.lastServerStatus = i;
 | 
			
		||||
             this.status.setPlayers(new ServerStatus.Players(this.getMaxPlayers(), this.getPlayerCount()));
 | 
			
		||||
-            GameProfile[] agameprofile = new GameProfile[Math.min(this.getPlayerCount(), 12)];
 | 
			
		||||
+            GameProfile[] agameprofile = new GameProfile[Math.min(this.getPlayerCount(), org.spigotmc.SpigotConfig.playerSample)]; // Paper
 | 
			
		||||
             int j = Mth.nextInt(this.random, 0, this.getPlayerCount() - agameprofile.length);
 | 
			
		||||
 
 | 
			
		||||
             for (int k = 0; k < agameprofile.length; ++k) {
 | 
			
		||||
diff --git a/src/main/java/net/minecraft/server/network/ServerStatusPacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerStatusPacketListenerImpl.java
 | 
			
		||||
index 9baa56d6da9c24706f1dbc8851fd68ca752cab26..d65191a50349ec86fe35df4ac1070f94fbb77b4c 100644
 | 
			
		||||
--- a/src/main/java/net/minecraft/server/network/ServerStatusPacketListenerImpl.java
 | 
			
		||||
+++ b/src/main/java/net/minecraft/server/network/ServerStatusPacketListenerImpl.java
 | 
			
		||||
@@ -47,6 +47,8 @@ public class ServerStatusPacketListenerImpl implements ServerStatusPacketListene
 | 
			
		||||
             this.connection.disconnect(ServerStatusPacketListenerImpl.DISCONNECT_REASON);
 | 
			
		||||
         } else {
 | 
			
		||||
             this.hasRequestedStatus = true;
 | 
			
		||||
+            // Paper start - Replace everything
 | 
			
		||||
+            /*
 | 
			
		||||
             // CraftBukkit start
 | 
			
		||||
             // this.networkManager.sendPacket(new PacketStatusOutServerInfo(this.minecraftServer.getServerPing()));
 | 
			
		||||
             final Object[] players = this.server.getPlayerList().players.toArray();
 | 
			
		||||
@@ -142,6 +144,9 @@ public class ServerStatusPacketListenerImpl implements ServerStatusPacketListene
 | 
			
		||||
             ping.setVersion(new ServerStatus.Version(this.server.getServerModName() + " " + this.server.getServerVersion(), version));
 | 
			
		||||
 
 | 
			
		||||
             this.connection.send(new ClientboundStatusResponsePacket(ping));
 | 
			
		||||
+            */
 | 
			
		||||
+            com.destroystokyo.paper.network.StandardPaperServerListPingEventImpl.processRequest(this.server, this.connection);
 | 
			
		||||
+            // Paper end
 | 
			
		||||
         }
 | 
			
		||||
         // CraftBukkit end
 | 
			
		||||
     }
 | 
			
		||||
diff --git a/src/main/java/org/spigotmc/SpigotConfig.java b/src/main/java/org/spigotmc/SpigotConfig.java
 | 
			
		||||
index d73dfe72a54b621c0f944c90904df3e3bc709445..8e7630de11637a75a4a54a22283cbb2d0c7e6438 100644
 | 
			
		||||
--- a/src/main/java/org/spigotmc/SpigotConfig.java
 | 
			
		||||
+++ b/src/main/java/org/spigotmc/SpigotConfig.java
 | 
			
		||||
@@ -289,7 +289,7 @@ public class SpigotConfig
 | 
			
		||||
     public static int playerSample;
 | 
			
		||||
     private static void playerSample()
 | 
			
		||||
     {
 | 
			
		||||
-        SpigotConfig.playerSample = SpigotConfig.getInt( "settings.sample-count", 12 );
 | 
			
		||||
+        SpigotConfig.playerSample = Math.max( SpigotConfig.getInt( "settings.sample-count", 12 ), 0 ); // Paper - Avoid negative counts
 | 
			
		||||
         Bukkit.getLogger().log( Level.INFO, "Server Ping Player Sample Count: {0}", playerSample ); // Paper - Use logger
 | 
			
		||||
     }
 | 
			
		||||
 
 | 
			
		||||
| 
						 | 
				
			
			@ -1,45 +0,0 @@
 | 
			
		|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
 | 
			
		||||
From: Aikar <aikar@aikar.co>
 | 
			
		||||
Date: Sun, 18 Mar 2018 11:45:57 -0400
 | 
			
		||||
Subject: [PATCH] Ability to change PlayerProfile in AsyncPreLoginEvent
 | 
			
		||||
 | 
			
		||||
This will allow you to change the players name or skin on login.
 | 
			
		||||
 | 
			
		||||
diff --git a/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java
 | 
			
		||||
index 109d7f1bf37e442ff80f7f63d50e27e6e30e0b5e..261ebb134a5ff40406e74237f730aad1c78a8215 100644
 | 
			
		||||
--- a/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java
 | 
			
		||||
+++ b/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java
 | 
			
		||||
@@ -1,5 +1,7 @@
 | 
			
		||||
 package net.minecraft.server.network;
 | 
			
		||||
 
 | 
			
		||||
+import com.destroystokyo.paper.profile.CraftPlayerProfile;
 | 
			
		||||
+import com.destroystokyo.paper.profile.PlayerProfile;
 | 
			
		||||
 import com.mojang.authlib.GameProfile;
 | 
			
		||||
 import com.mojang.authlib.exceptions.AuthenticationUnavailableException;
 | 
			
		||||
 import java.math.BigInteger;
 | 
			
		||||
@@ -37,6 +39,7 @@ import org.apache.commons.lang3.Validate;
 | 
			
		||||
 import org.apache.logging.log4j.LogManager;
 | 
			
		||||
 import org.apache.logging.log4j.Logger;
 | 
			
		||||
 import io.papermc.paper.adventure.PaperAdventure; // Paper
 | 
			
		||||
+import org.bukkit.Bukkit;
 | 
			
		||||
 import org.bukkit.craftbukkit.util.Waitable;
 | 
			
		||||
 import org.bukkit.event.player.AsyncPlayerPreLoginEvent;
 | 
			
		||||
 import org.bukkit.event.player.PlayerPreLoginEvent;
 | 
			
		||||
@@ -336,8 +339,16 @@ public class ServerLoginPacketListenerImpl implements ServerLoginPacketListener
 | 
			
		||||
                         java.util.UUID uniqueId = ServerLoginPacketListenerImpl.this.gameProfile.getId();
 | 
			
		||||
                         final org.bukkit.craftbukkit.CraftServer server = ServerLoginPacketListenerImpl.this.server.server;
 | 
			
		||||
 
 | 
			
		||||
-                        AsyncPlayerPreLoginEvent asyncEvent = new AsyncPlayerPreLoginEvent(playerName, address, uniqueId);
 | 
			
		||||
+                            // Paper start
 | 
			
		||||
+                            PlayerProfile profile = Bukkit.createProfile(uniqueId, playerName);
 | 
			
		||||
+                            AsyncPlayerPreLoginEvent asyncEvent = new AsyncPlayerPreLoginEvent(playerName, address, uniqueId, profile);
 | 
			
		||||
                         server.getPluginManager().callEvent(asyncEvent);
 | 
			
		||||
+                            profile = asyncEvent.getPlayerProfile();
 | 
			
		||||
+                            profile.complete();
 | 
			
		||||
+                            gameProfile = CraftPlayerProfile.asAuthlibCopy(profile);
 | 
			
		||||
+                            playerName = gameProfile.getName();
 | 
			
		||||
+                            uniqueId = gameProfile.getId();
 | 
			
		||||
+                            // Paper end
 | 
			
		||||
 
 | 
			
		||||
                         if (PlayerPreLoginEvent.getHandlerList().getRegisteredListeners().length != 0) {
 | 
			
		||||
                             final PlayerPreLoginEvent event = new PlayerPreLoginEvent(playerName, address, uniqueId);
 | 
			
		||||
| 
						 | 
				
			
			@ -1,133 +0,0 @@
 | 
			
		|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
 | 
			
		||||
From: Aikar <aikar@aikar.co>
 | 
			
		||||
Date: Sun, 18 Mar 2018 12:29:48 -0400
 | 
			
		||||
Subject: [PATCH] Player.setPlayerProfile API
 | 
			
		||||
 | 
			
		||||
This can be useful for changing name or skins after a player has logged in.
 | 
			
		||||
 | 
			
		||||
diff --git a/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java
 | 
			
		||||
index 261ebb134a5ff40406e74237f730aad1c78a8215..39bdda56aaa5503efc15207261634127b462c3e7 100644
 | 
			
		||||
--- a/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java
 | 
			
		||||
+++ b/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java
 | 
			
		||||
@@ -340,12 +340,12 @@ public class ServerLoginPacketListenerImpl implements ServerLoginPacketListener
 | 
			
		||||
                         final org.bukkit.craftbukkit.CraftServer server = ServerLoginPacketListenerImpl.this.server.server;
 | 
			
		||||
 
 | 
			
		||||
                             // Paper start
 | 
			
		||||
-                            PlayerProfile profile = Bukkit.createProfile(uniqueId, playerName);
 | 
			
		||||
+                            PlayerProfile profile = CraftPlayerProfile.asBukkitMirror(ServerLoginPacketListenerImpl.this.gameProfile);
 | 
			
		||||
                             AsyncPlayerPreLoginEvent asyncEvent = new AsyncPlayerPreLoginEvent(playerName, address, uniqueId, profile);
 | 
			
		||||
                         server.getPluginManager().callEvent(asyncEvent);
 | 
			
		||||
                             profile = asyncEvent.getPlayerProfile();
 | 
			
		||||
-                            profile.complete();
 | 
			
		||||
-                            gameProfile = CraftPlayerProfile.asAuthlibCopy(profile);
 | 
			
		||||
+                            profile.complete(true);
 | 
			
		||||
+                            ServerLoginPacketListenerImpl.this.gameProfile = CraftPlayerProfile.asAuthlib(profile);
 | 
			
		||||
                             playerName = gameProfile.getName();
 | 
			
		||||
                             uniqueId = gameProfile.getId();
 | 
			
		||||
                             // Paper end
 | 
			
		||||
diff --git a/src/main/java/net/minecraft/world/entity/player/Player.java b/src/main/java/net/minecraft/world/entity/player/Player.java
 | 
			
		||||
index dbab4d28c49d22807dfc582fb83353232396555b..0ef9c95d40cd0cdff0d150121511e6f9efd65f4d 100644
 | 
			
		||||
--- a/src/main/java/net/minecraft/world/entity/player/Player.java
 | 
			
		||||
+++ b/src/main/java/net/minecraft/world/entity/player/Player.java
 | 
			
		||||
@@ -173,7 +173,7 @@ public abstract class Player extends LivingEntity {
 | 
			
		||||
     protected int enchantmentSeed;
 | 
			
		||||
     protected final float defaultFlySpeed = 0.02F;
 | 
			
		||||
     private int lastLevelUpTime;
 | 
			
		||||
-    private final GameProfile gameProfile;
 | 
			
		||||
+    public GameProfile gameProfile; // Paper - private->public
 | 
			
		||||
     private boolean reducedDebugInfo;
 | 
			
		||||
     private ItemStack lastItemInMainHand;
 | 
			
		||||
     private final ItemCooldowns cooldowns;
 | 
			
		||||
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
 | 
			
		||||
index 4c8617fbd2cd8e34c87634fe448d204ee7fb8a0f..95a1d83fb7cb9947beb56b951b0081e4db2de6c9 100644
 | 
			
		||||
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
 | 
			
		||||
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
 | 
			
		||||
@@ -71,6 +71,7 @@ import net.minecraft.world.item.enchantment.EnchantmentHelper;
 | 
			
		||||
 import net.minecraft.world.item.enchantment.Enchantments;
 | 
			
		||||
 import net.minecraft.world.level.GameType;
 | 
			
		||||
 import net.minecraft.world.level.block.Blocks;
 | 
			
		||||
+import net.minecraft.world.level.biome.BiomeManager;
 | 
			
		||||
 import net.minecraft.world.level.block.entity.SignBlockEntity;
 | 
			
		||||
 import net.minecraft.world.level.saveddata.maps.MapDecoration;
 | 
			
		||||
 import net.minecraft.world.level.saveddata.maps.MapItemSavedData;
 | 
			
		||||
@@ -1347,8 +1348,13 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
 | 
			
		||||
         this.hiddenPlayers.put(player.getUniqueId(), hidingPlugins);
 | 
			
		||||
 
 | 
			
		||||
         // Remove this player from the hidden player's EntityTrackerEntry
 | 
			
		||||
-        ChunkMap tracker = ((ServerLevel) entity.level).getChunkSource().chunkMap;
 | 
			
		||||
+        // Paper start
 | 
			
		||||
         ServerPlayer other = ((CraftPlayer) player).getHandle();
 | 
			
		||||
+        unregisterPlayer(other);
 | 
			
		||||
+    }
 | 
			
		||||
+    private void unregisterPlayer(ServerPlayer other) {
 | 
			
		||||
+        ChunkMap tracker = ((ServerLevel) entity.level).getChunkSource().chunkMap;
 | 
			
		||||
+        // Paper end
 | 
			
		||||
         ChunkMap.TrackedEntity entry = tracker.entityMap.get(other.getId());
 | 
			
		||||
         if (entry != null) {
 | 
			
		||||
             entry.removePlayer(this.getHandle());
 | 
			
		||||
@@ -1389,8 +1395,13 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
 | 
			
		||||
         }
 | 
			
		||||
         this.hiddenPlayers.remove(player.getUniqueId());
 | 
			
		||||
 
 | 
			
		||||
-        ChunkMap tracker = ((ServerLevel) entity.level).getChunkSource().chunkMap;
 | 
			
		||||
+        // Paper start
 | 
			
		||||
         ServerPlayer other = ((CraftPlayer) player).getHandle();
 | 
			
		||||
+        registerPlayer(other);
 | 
			
		||||
+    }
 | 
			
		||||
+    private void registerPlayer(ServerPlayer other) {
 | 
			
		||||
+        ChunkMap tracker = ((ServerLevel) entity.level).getChunkSource().chunkMap;
 | 
			
		||||
+        // Paper end
 | 
			
		||||
 
 | 
			
		||||
         this.getHandle().connection.send(new ClientboundPlayerInfoPacket(ClientboundPlayerInfoPacket.Action.ADD_PLAYER, other));
 | 
			
		||||
 
 | 
			
		||||
@@ -1399,6 +1410,50 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
 | 
			
		||||
             entry.updatePlayer(this.getHandle());
 | 
			
		||||
         }
 | 
			
		||||
     }
 | 
			
		||||
+    // Paper start
 | 
			
		||||
+    private void reregisterPlayer(ServerPlayer player) {
 | 
			
		||||
+        if (!hiddenPlayers.containsKey(player.getUUID())) {
 | 
			
		||||
+            unregisterPlayer(player);
 | 
			
		||||
+            registerPlayer(player);
 | 
			
		||||
+        }
 | 
			
		||||
+    }
 | 
			
		||||
+    public void setPlayerProfile(com.destroystokyo.paper.profile.PlayerProfile profile) {
 | 
			
		||||
+        ServerPlayer self = getHandle();
 | 
			
		||||
+        self.gameProfile = com.destroystokyo.paper.profile.CraftPlayerProfile.asAuthlibCopy(profile);
 | 
			
		||||
+        if (!self.sentListPacket) {
 | 
			
		||||
+            return;
 | 
			
		||||
+        }
 | 
			
		||||
+        List<ServerPlayer> players = server.getServer().getPlayerList().players;
 | 
			
		||||
+        for (ServerPlayer player : players) {
 | 
			
		||||
+            player.getBukkitEntity().reregisterPlayer(self);
 | 
			
		||||
+        }
 | 
			
		||||
+        refreshPlayer();
 | 
			
		||||
+    }
 | 
			
		||||
+    public com.destroystokyo.paper.profile.PlayerProfile getPlayerProfile() {
 | 
			
		||||
+        return new com.destroystokyo.paper.profile.CraftPlayerProfile(this).clone();
 | 
			
		||||
+    }
 | 
			
		||||
+
 | 
			
		||||
+    private void refreshPlayer() {
 | 
			
		||||
+        ServerPlayer handle = getHandle();
 | 
			
		||||
+
 | 
			
		||||
+        Location loc = getLocation();
 | 
			
		||||
+
 | 
			
		||||
+        ServerGamePacketListenerImpl connection = handle.connection;
 | 
			
		||||
+        reregisterPlayer(handle);
 | 
			
		||||
+
 | 
			
		||||
+        //Respawn the player then update their position and selected slot
 | 
			
		||||
+        ServerLevel worldserver = handle.getLevel();
 | 
			
		||||
+        connection.send(new net.minecraft.network.protocol.game.ClientboundRespawnPacket(worldserver.dimensionType(), worldserver.dimension(), BiomeManager.obfuscateSeed(worldserver.getSeed()), handle.gameMode.getGameModeForPlayer(), handle.gameMode.getPreviousGameModeForPlayer(), worldserver.isDebug(), worldserver.isFlat(), true));
 | 
			
		||||
+        handle.onUpdateAbilities();
 | 
			
		||||
+        connection.send(new net.minecraft.network.protocol.game.ClientboundPlayerPositionPacket(loc.getX(), loc.getY(), loc.getZ(), loc.getYaw(), loc.getPitch(), java.util.Collections.emptySet(), 0, false));
 | 
			
		||||
+        net.minecraft.server.MinecraftServer.getServer().getPlayerList().sendAllPlayerInfo(handle);
 | 
			
		||||
+
 | 
			
		||||
+        if (this.isOp()) {
 | 
			
		||||
+            this.setOp(false);
 | 
			
		||||
+            this.setOp(true);
 | 
			
		||||
+        }
 | 
			
		||||
+    }
 | 
			
		||||
+    // Paper end
 | 
			
		||||
 
 | 
			
		||||
     public void removeDisconnectingPlayer(Player player) {
 | 
			
		||||
         this.hiddenPlayers.remove(player.getUniqueId());
 | 
			
		||||
| 
						 | 
				
			
			@ -1,40 +0,0 @@
 | 
			
		|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
 | 
			
		||||
From: Aikar <aikar@aikar.co>
 | 
			
		||||
Date: Thu, 22 Mar 2018 01:40:24 -0400
 | 
			
		||||
Subject: [PATCH] getPlayerUniqueId API
 | 
			
		||||
 | 
			
		||||
Gets the unique ID of the player currently known as the specified player name
 | 
			
		||||
In Offline Mode, will return an Offline UUID
 | 
			
		||||
 | 
			
		||||
This is a more performant way to obtain a UUID for a name than loading an OfflinePlayer
 | 
			
		||||
 | 
			
		||||
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
 | 
			
		||||
index 435a68e6c99bffdc8d6ded9deda68d4e970a2d49..d10da7acb03a2e4a67cee41a6f6fc6357eb8efd6 100644
 | 
			
		||||
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
 | 
			
		||||
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
 | 
			
		||||
@@ -1668,6 +1668,25 @@ public final class CraftServer implements Server {
 | 
			
		||||
         return recipients.size();
 | 
			
		||||
     }
 | 
			
		||||
 
 | 
			
		||||
+    // Paper start
 | 
			
		||||
+    @Nullable
 | 
			
		||||
+    public UUID getPlayerUniqueId(String name) {
 | 
			
		||||
+        Player player = Bukkit.getPlayerExact(name);
 | 
			
		||||
+        if (player != null) {
 | 
			
		||||
+            return player.getUniqueId();
 | 
			
		||||
+        }
 | 
			
		||||
+        GameProfile profile;
 | 
			
		||||
+        // Only fetch an online UUID in online mode
 | 
			
		||||
+        if (com.destroystokyo.paper.PaperConfig.isProxyOnlineMode()) {
 | 
			
		||||
+            profile = console.getProfileCache().get(name).orElse(null);
 | 
			
		||||
+        } else {
 | 
			
		||||
+            // Make an OfflinePlayer using an offline mode UUID since the name has no profile
 | 
			
		||||
+            profile = new GameProfile(UUID.nameUUIDFromBytes(("OfflinePlayer:" + name).getBytes(Charsets.UTF_8)), name);
 | 
			
		||||
+        }
 | 
			
		||||
+        return profile != null ? profile.getId() : null;
 | 
			
		||||
+    }
 | 
			
		||||
+    // Paper end
 | 
			
		||||
+
 | 
			
		||||
     @Override
 | 
			
		||||
     @Deprecated
 | 
			
		||||
     public OfflinePlayer getOfflinePlayer(String name) {
 | 
			
		||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue