From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Aikar Date: Sun, 10 Apr 2016 22:50:33 -0400 Subject: [PATCH] Allow Vehicle/Passenger Teleporting for Bukkit API If Bukkit teleport is called, teleport the whole set of entities together and maintain the chain. Patch from EMC: https://github.com/starlis/empirecraft/blob/f73510d91048d8b01bf8c50b0a2b0d054cb7c0b5/patches/server/0056-Allow-Vehicle-Passenger-Teleporting-for-Bukkit-API.patch diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java index 4032f499fd533e8d35d050758ea2358ad6878b94..c35331ea354b7c464677acc3c724d2e22bb375c1 100644 --- a/src/main/java/net/minecraft/server/players/PlayerList.java +++ b/src/main/java/net/minecraft/server/players/PlayerList.java @@ -819,7 +819,7 @@ public abstract class PlayerList { public ServerPlayer moveToWorld(ServerPlayer entityplayer, ServerLevel worldserver, boolean flag, Location location, boolean avoidSuffocation, org.bukkit.event.player.PlayerRespawnEvent.RespawnFlag...respawnFlags) { // Paper end - entityplayer.stopRiding(); // CraftBukkit + //entityplayer.stopRiding(); // CraftBukkit // Parchment - remove stop riding this.players.remove(entityplayer); this.playersByName.remove(entityplayer.getScoreboardName().toLowerCase(java.util.Locale.ROOT)); // Spigot entityplayer.getLevel().removePlayerImmediately(entityplayer, Entity.RemovalReason.DISCARDED); diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java index 94857a736d2a16e8ade286c6f2ddf8bd798008eb..8a5f4d0f01ce1d22790ccf6b11547b5e7cdd99bb 100644 --- a/src/main/java/net/minecraft/world/entity/Entity.java +++ b/src/main/java/net/minecraft/world/entity/Entity.java @@ -217,7 +217,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, n public ImmutableList passengers; protected int boardingCooldown; @Nullable - private Entity vehicle; + public Entity vehicle; // Parchment - make public public Level level; public double xo; public double yo; diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java index 2712aa554383a3b2b742c945e2f0be7ee96eea69..b3e21bc2347854fb39ba7c25c6de586f084119d4 100644 --- a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java @@ -3,6 +3,7 @@ package org.bukkit.craftbukkit.entity; import com.google.common.base.Function; import com.google.common.base.Preconditions; import com.google.common.base.Predicates; +import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; import java.util.List; import java.util.Set; @@ -556,23 +557,103 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity { return this.teleport(location, TeleportCause.PLUGIN); } + // Parchment start - vehicle passenger teleporting + private static CraftEntity teleportingEntity; + private CraftEntity storedRoot; + private ImmutableList storedPassengers; + void detachEntities() { + if (teleportingEntity == null) { + teleportingEntity = this; + this.storedRoot = entity.getRootVehicle().getBukkitEntity(); + this.storedRoot.detachEntities(); + teleportingEntity = null; + return; + } + this.storedPassengers = this.entity.passengers.stream().map(Entity::getBukkitEntity).collect(ImmutableList.toImmutableList()); + this.forceEjectPassengers(); + for (CraftEntity ent : this.storedPassengers) { + ent.detachEntities(); + } + } + void forceEjectPassengers() { + for (int i = this.entity.passengers.size() - 1; i >= 0; --i) { + Entity passenger = (this.entity.passengers.get(i)); + passenger.vehicle = null; + } + this.entity.passengers = ImmutableList.of(); + } + void teleportAndReattachEntities(Location location) { + if (teleportingEntity == null) { + if (this.storedRoot != null) { + teleportingEntity = this; + this.storedRoot.teleportAndReattachEntities(location); + CraftEntity root = this.storedRoot; + this.storedRoot = null; + root.reattachPassengers(); + teleportingEntity = null; + } + return; + } + if (!teleportingEntity.equals(this)) { + this.teleportEntity(location); + } + for (CraftEntity ent : this.storedPassengers) { + ent.teleportAndReattachEntities(location); + } + } + void teleportEntity(Location location) { + if (getWorld().equals(location.getWorld())) { + if (this.storedPassengers == null || this.storedPassengers.isEmpty()) { + this.entity.moveTo(location.getX(), location.getY(), location.getZ(), location.getYaw(), location.getPitch()); + this.entity.setYHeadRot(location.getYaw()); + } else { + ServerLevel world = (ServerLevel) this.entity.level; + Entity target = this.entity.getType().create(world); + if (target == null) { + return; + } + target.restoreFrom(this.entity); + target.moveTo(location.getX(), location.getY(), location.getZ(), location.getYaw(), location.getPitch()); + target.setYHeadRot(location.getYaw()); + this.entity.setRemoved(Entity.RemovalReason.CHANGED_DIMENSION); + this.setHandle(target); + world.addDuringTeleport(target); + } + } else { + this.entity.teleportTo(((CraftWorld) location.getWorld()).getHandle(), new BlockPos(location.getX(), location.getY(), location.getZ())); + } + } + private void reattachPassengers() { + if (this.storedPassengers == null) { + return; + } + this.entity.passengers = this.storedPassengers.stream().map(CraftEntity::getHandle).collect(ImmutableList.toImmutableList()); + for (CraftEntity passenger : this.storedPassengers) { + passenger.getHandle().vehicle = this.getHandle(); + passenger.reattachPassengers(); + } + this.storedPassengers = null; + } + // Parchment end - vehicle passenger teleporting @Override public boolean teleport(Location location, TeleportCause cause) { Preconditions.checkArgument(location != null, "location"); location.checkFinite(); - if (this.entity.isVehicle() || this.entity.isRemoved()) { + if (this.entity.isRemoved()) { // Parchment return false; } // If this entity is riding another entity, we must dismount before teleporting. - this.entity.stopRiding(); + //this.entity.stopRiding(); // Parchment + this.detachEntities(); // Parchment // Let the server handle cross world teleports if (!location.getWorld().equals(this.getWorld())) { // Prevent teleportation to an other world during world generation Preconditions.checkState(!entity.generation, "Cannot teleport entity to an other world during world generation"); this.entity.teleportTo(((CraftWorld) location.getWorld()).getHandle(), new BlockPos(location.getX(), location.getY(), location.getZ())); + this.teleportAndReattachEntities(location); // Parchment return true; } @@ -581,6 +662,7 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity { // SPIGOT-619: Force sync head rotation also this.entity.setYHeadRot(location.getYaw()); + this.teleportAndReattachEntities(location); // Parchment 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 2b8e31ae00e786dfbfbf5bb5228b846752cd2543..1a09026ccbe785a37e935e77e35f629068df4d83 100644 --- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java @@ -1021,7 +1021,7 @@ public class CraftPlayer extends CraftHumanEntity implements Player { } // If this player is riding another entity, we must dismount before teleporting. - entity.stopRiding(); + //entity.stopRiding(); // Parchment // SPIGOT-5509: Wakeup, similar to riding if (this.isSleeping()) { @@ -1043,12 +1043,27 @@ public class CraftPlayer extends CraftHumanEntity implements Player { // Check if the fromWorld and toWorld are the same. if (fromWorld == toWorld) { + this.detachEntities(); // Parchment entity.connection.teleport(to); } else { + this.detachEntities(); // Parchment server.getHandle().moveToWorld(entity, toWorld, true, to, !toWorld.paperConfig.disableTeleportationSuffocationCheck); // Paper } + this.teleportAndReattachEntities(location); // Parchment return true; } + // Parchment start + @Override + void teleportEntity(Location location) { + ServerPlayer entity = this.getHandle(); + if (getWorld().equals(location.getWorld())) { + entity.connection.teleport(location); + } else { + ServerLevel toWorld = ((CraftWorld) location.getWorld()).getHandle(); + server.getHandle().moveToWorld(entity, toWorld, true, location, !toWorld.paperConfig.disableTeleportationSuffocationCheck); + } + } + // Parchment end // Paper start - Ugly workaround for SPIGOT-1915 & GH-114 @Override