diff --git a/Spigot-Server-Patches/Call-player-spectator-target-events-and-improve-impl.patch b/Spigot-Server-Patches/Call-player-spectator-target-events-and-improve-impl.patch
new file mode 100644
index 0000000000..6bb2fe43d0
--- /dev/null
+++ b/Spigot-Server-Patches/Call-player-spectator-target-events-and-improve-impl.patch
@@ -0,0 +1,101 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Caleb Bassham <caleb.bassham@gmail.com>
+Date: Fri, 28 Sep 2018 02:32:19 -0500
+Subject: [PATCH] Call player spectator target events and improve
+ implementation
+
+Use a proper teleport for teleporting to entities in different
+worlds.
+
+Implementation improvements authored by Spottedleaf <Spottedleaf@users.noreply.github.com>
+Validate that the target entity is valid and deny spectate
+requests from frozen players.
+
+Also, make sure the entity is spawned to the client before
+sending the camera packet. If the entity isn't spawned clientside
+when it receives the camera packet, then the client will not
+spectate the target entity.
+
+Co-authored-by: Spottedleaf <Spottedleaf@users.noreply.github.com>
+
+diff --git a/src/main/java/net/minecraft/server/EntityPlayer.java b/src/main/java/net/minecraft/server/EntityPlayer.java
+index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
+--- a/src/main/java/net/minecraft/server/EntityPlayer.java
++++ b/src/main/java/net/minecraft/server/EntityPlayer.java
+@@ -0,0 +0,0 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
+         return (Entity) (this.spectatedEntity == null ? this : this.spectatedEntity);
+     }
+ 
+-    public void setSpectatorTarget(Entity entity) {
++    public void setSpectatorTarget(Entity newSpectatorTarget) {
++        // Paper start - Add PlayerStartSpectatingEntityEvent and PlayerStopSpectatingEntity Event and improve implementation
+         Entity entity1 = this.getSpecatorTarget();
+ 
+-        this.spectatedEntity = (Entity) (entity == null ? this : entity);
+-        if (entity1 != this.spectatedEntity) {
+-            this.playerConnection.sendPacket(new PacketPlayOutCamera(this.spectatedEntity));
+-            this.playerConnection.a(this.spectatedEntity.locX(), this.spectatedEntity.locY(), this.spectatedEntity.locZ(), this.yaw, this.pitch, TeleportCause.SPECTATE); // CraftBukkit
++        if (newSpectatorTarget == null) {
++            newSpectatorTarget = this;
+         }
+ 
++        if (entity1 == newSpectatorTarget) return; // new spec target is the current spec target
++
++        if (newSpectatorTarget == this) {
++            com.destroystokyo.paper.event.player.PlayerStopSpectatingEntityEvent playerStopSpectatingEntityEvent = new com.destroystokyo.paper.event.player.PlayerStopSpectatingEntityEvent(this.getBukkitEntity(), entity1.getBukkitEntity());
++
++            if (!playerStopSpectatingEntityEvent.callEvent()) {
++                return;
++            }
++        } else {
++            com.destroystokyo.paper.event.player.PlayerStartSpectatingEntityEvent playerStartSpectatingEntityEvent = new com.destroystokyo.paper.event.player.PlayerStartSpectatingEntityEvent(this.getBukkitEntity(), entity1.getBukkitEntity(), newSpectatorTarget.getBukkitEntity());
++
++            if (!playerStartSpectatingEntityEvent.callEvent()) {
++                return;
++            }
++        }
++        // Validate
++        if (newSpectatorTarget != this) {
++            if (newSpectatorTarget.dead || newSpectatorTarget.shouldBeRemoved || !newSpectatorTarget.valid || newSpectatorTarget.world == null) {
++                MinecraftServer.LOGGER.info("Blocking player " + this.toString() + " from spectating invalid entity " + newSpectatorTarget.toString());
++                return;
++            }
++            if (this.isFrozen()) {
++                // use debug: clients might maliciously spam this
++                MinecraftServer.LOGGER.debug("Blocking frozen player " + this.toString() + " from spectating entity " + newSpectatorTarget.toString());
++                return;
++            }
++        }
++
++        this.spectatedEntity = newSpectatorTarget; // only set after validating state
++
++        if (newSpectatorTarget != this) {
++            // Make sure we're in the right place
++            this.ejectPassengers(); // teleport can fail if we have passengers...
++            this.getBukkitEntity().teleport(new Location(newSpectatorTarget.getWorld().getWorld(), newSpectatorTarget.locX(), newSpectatorTarget.locY(), newSpectatorTarget.locZ(), this.yaw, this.pitch), TeleportCause.SPECTATE); // Correctly handle cross-world entities from api calls by using CB teleport
++
++            // Make sure we're tracking the entity before sending
++            PlayerChunkMap.EntityTracker tracker = ((WorldServer)newSpectatorTarget.world).getChunkProvider().playerChunkMap.trackedEntities.get(newSpectatorTarget.getId());
++            if (tracker != null) { // dumb plugins...
++                tracker.updatePlayer(this);
++            }
++        } else {
++            this.playerConnection.teleport(this.spectatedEntity.locX(), this.spectatedEntity.locY(), this.spectatedEntity.locZ(), this.yaw, this.pitch, TeleportCause.SPECTATE); // CraftBukkit
++        }
++        this.playerConnection.sendPacket(new PacketPlayOutCamera(newSpectatorTarget));
++        // Paper end
+     }
+ 
+     @Override
+diff --git a/src/main/java/net/minecraft/server/PlayerConnection.java b/src/main/java/net/minecraft/server/PlayerConnection.java
+index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
+--- a/src/main/java/net/minecraft/server/PlayerConnection.java
++++ b/src/main/java/net/minecraft/server/PlayerConnection.java
+@@ -0,0 +0,0 @@ public class PlayerConnection implements PacketListenerPlayIn {
+     }
+ 
+     // CraftBukkit start - Delegate to teleport(Location)
++    public final void teleport(double d0, double d1, double d2, float f, float f1, PlayerTeleportEvent.TeleportCause cause) { this.a(d0, d1, d2, f, f1, cause); } // Paper - OBFHELPER
+     public void a(double d0, double d1, double d2, float f, float f1, PlayerTeleportEvent.TeleportCause cause) {
+         this.a(d0, d1, d2, f, f1, Collections.<PacketPlayOutPosition.EnumPlayerTeleportFlags>emptySet(), cause);
+     }
diff --git a/Spigot-Server-Patches/Call-player-spectator-target-events.patch b/Spigot-Server-Patches/Call-player-spectator-target-events.patch
deleted file mode 100644
index 6d3b557c61..0000000000
--- a/Spigot-Server-Patches/Call-player-spectator-target-events.patch
+++ /dev/null
@@ -1,68 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Caleb Bassham <caleb.bassham@gmail.com>
-Date: Fri, 28 Sep 2018 02:32:19 -0500
-Subject: [PATCH] Call player spectator target events
-
-
-diff --git a/src/main/java/net/minecraft/server/EntityPlayer.java b/src/main/java/net/minecraft/server/EntityPlayer.java
-index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
---- a/src/main/java/net/minecraft/server/EntityPlayer.java
-+++ b/src/main/java/net/minecraft/server/EntityPlayer.java
-@@ -0,0 +0,0 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
-     private EnumChatVisibility ch;
-     private boolean ci = true;
-     private long cj = SystemUtils.getMonotonicMillis();
--    private Entity spectatedEntity;
-+    private Entity spectatedEntity; private void setSpectatorTargetField(Entity e) { this.spectatedEntity = e; } // Paper - OBFHELPER
-     public boolean worldChangeInvuln;
-     private boolean cm; private void setHasSeenCredits(boolean has) { this.cm = has; } // Paper - OBFHELPER
-     private final RecipeBookServer recipeBook;
-@@ -0,0 +0,0 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
-         return (Entity) (this.spectatedEntity == null ? this : this.spectatedEntity);
-     }
- 
--    public void setSpectatorTarget(Entity entity) {
-+    public void setSpectatorTarget(Entity newSpectatorTarget) {
-+        // Paper start - Add PlayerStartSpectatingEntityEvent and PlayerStopSpectatingEntity Event
-         Entity entity1 = this.getSpecatorTarget();
- 
--        this.spectatedEntity = (Entity) (entity == null ? this : entity);
--        if (entity1 != this.spectatedEntity) {
--            this.playerConnection.sendPacket(new PacketPlayOutCamera(this.spectatedEntity));
--            this.playerConnection.a(this.spectatedEntity.locX(), this.spectatedEntity.locY(), this.spectatedEntity.locZ(), this.yaw, this.pitch, TeleportCause.SPECTATE); // CraftBukkit
-+        if (newSpectatorTarget == null) {
-+            newSpectatorTarget = this;
-         }
- 
-+        if (entity1 == newSpectatorTarget) return; // new spec target is the current spec target
-+
-+        if (newSpectatorTarget == this) {
-+            com.destroystokyo.paper.event.player.PlayerStopSpectatingEntityEvent playerStopSpectatingEntityEvent = new com.destroystokyo.paper.event.player.PlayerStopSpectatingEntityEvent(this.getBukkitEntity(), entity1.getBukkitEntity());
-+
-+            if (!playerStopSpectatingEntityEvent.callEvent()) {
-+                return;
-+            }
-+        } else {
-+            com.destroystokyo.paper.event.player.PlayerStartSpectatingEntityEvent playerStartSpectatingEntityEvent = new com.destroystokyo.paper.event.player.PlayerStartSpectatingEntityEvent(this.getBukkitEntity(), entity1.getBukkitEntity(), newSpectatorTarget.getBukkitEntity());
-+
-+            if (!playerStartSpectatingEntityEvent.callEvent()) {
-+                return;
-+            }
-+        }
-+
-+        setSpectatorTargetField(newSpectatorTarget);
-+
-+        this.playerConnection.sendPacket(new PacketPlayOutCamera(newSpectatorTarget));
-+        this.playerConnection.a(this.spectatedEntity.locX(), this.spectatedEntity.locY(), this.spectatedEntity.locZ(), this.yaw, this.pitch, TeleportCause.SPECTATE); // CraftBukkit
-+        // Paper end
-     }
- 
-     @Override
-@@ -0,0 +0,0 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
-         if (this.portalCooldown > 0 && !this.worldChangeInvuln) {
-             --this.portalCooldown;
-         }
--
-     }
- 
-     @Override
diff --git a/Spigot-Server-Patches/Implement-Player-Client-Options-API.patch b/Spigot-Server-Patches/Implement-Player-Client-Options-API.patch
index 3b1c632645..c2a4a04419 100644
--- a/Spigot-Server-Patches/Implement-Player-Client-Options-API.patch
+++ b/Spigot-Server-Patches/Implement-Player-Client-Options-API.patch
@@ -116,7 +116,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 -    private boolean ci = true;
 +    private boolean ci = true; public boolean hasChatColorsEnabled() { return this.ci; } // Paper - OBFHELPER
      private long cj = SystemUtils.getMonotonicMillis();
-     private Entity spectatedEntity; private void setSpectatorTargetField(Entity e) { this.spectatedEntity = e; } // Paper - OBFHELPER
+     private Entity spectatedEntity;
      public boolean worldChangeInvuln;
 @@ -0,0 +0,0 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
      }