From 5a6ff9baff7de832156b37c5d9805d4a7e993a0d Mon Sep 17 00:00:00 2001 From: Jake Potrebic Date: Thu, 16 May 2024 09:37:02 -0700 Subject: [PATCH] Restore vanilla teleportation logic for non-players --- .../server/1046-fixup-More-Teleport-API.patch | 50 ++++ ...047-fixup-Add-EntityPortalReadyEvent.patch | 23 ++ ...anilla-teleportation-for-non-players.patch | 270 ++++++++++++++++++ .../io/papermc/testplugin/TestPlugin.java | 33 +++ 4 files changed, 376 insertions(+) create mode 100644 patches/server/1046-fixup-More-Teleport-API.patch create mode 100644 patches/server/1047-fixup-Add-EntityPortalReadyEvent.patch create mode 100644 patches/server/1048-Restore-vanilla-teleportation-for-non-players.patch diff --git a/patches/server/1046-fixup-More-Teleport-API.patch b/patches/server/1046-fixup-More-Teleport-API.patch new file mode 100644 index 0000000000000..f71ab1e1c6540 --- /dev/null +++ b/patches/server/1046-fixup-More-Teleport-API.patch @@ -0,0 +1,50 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jake Potrebic +Date: Thu, 16 May 2024 11:14:16 -0700 +Subject: [PATCH] fixup! More Teleport API + + +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java +index a2d336ceb52b63db5c03432ee7bc94dc6a742b82..0f64e3e3ce1e932a8df6b27ab9bbfc2156f58f04 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java +@@ -239,20 +239,21 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity { + location.checkFinite(); + // Paper start - Teleport passenger API + Set flagSet = Set.of(flags); +- boolean dismount = !flagSet.contains(io.papermc.paper.entity.TeleportFlag.EntityState.RETAIN_VEHICLE); +- boolean ignorePassengers = flagSet.contains(io.papermc.paper.entity.TeleportFlag.EntityState.RETAIN_PASSENGERS); +- // Don't allow teleporting between worlds while keeping passengers +- if (flagSet.contains(io.papermc.paper.entity.TeleportFlag.EntityState.RETAIN_PASSENGERS) && this.entity.isVehicle() && location.getWorld() != this.getWorld()) { ++ final boolean retainVehicle = flagSet.contains(io.papermc.paper.entity.TeleportFlag.EntityState.RETAIN_VEHICLE); ++ final boolean retainPassengers = flagSet.contains(io.papermc.paper.entity.TeleportFlag.EntityState.RETAIN_PASSENGERS); ++ final boolean sameWorld = location.getWorld() == this.getWorld(); ++ ++ if (this.entity.isVehicle() && retainPassengers && !sameWorld) { ++ // if target is a vehicle (has passengers) and RETAIN_PASSENGERS is specified and the tp is inter-world + return false; + } + +- // Don't allow to teleport between worlds if remaining on vehicle +- if (!dismount && this.entity.isPassenger() && location.getWorld() != this.getWorld()) { +- return false; ++ if (this.entity.isPassenger() && retainVehicle) { ++ return false; // You can't teleport a passenger and keep the vehicle + } + // Paper end + +- if ((!ignorePassengers && this.entity.isVehicle()) || this.entity.isRemoved()) { // Paper - Teleport passenger API ++ if (this.entity.isRemoved()) { // Paper - Teleport passenger API (move vehicle check up a bit) + return false; + } + +@@ -268,7 +269,8 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity { + // Paper end + + // If this entity is riding another entity, we must dismount before teleporting. +- if (dismount) this.entity.stopRiding(); // Paper - Teleport passenger API ++ this.entity.stopRiding(); // Paper - Teleport passenger API - always stop riding; if RETAIN_VEHICLE was specified, it returned early above ++ if (!retainPassengers) this.entity.ejectPassengers(); // Paper - Teleport passenger API + + // Let the server handle cross world teleports + if (location.getWorld() != null && !location.getWorld().equals(this.getWorld())) { diff --git a/patches/server/1047-fixup-Add-EntityPortalReadyEvent.patch b/patches/server/1047-fixup-Add-EntityPortalReadyEvent.patch new file mode 100644 index 0000000000000..904174c6faa4f --- /dev/null +++ b/patches/server/1047-fixup-Add-EntityPortalReadyEvent.patch @@ -0,0 +1,23 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jake Potrebic +Date: Thu, 16 May 2024 11:35:16 -0700 +Subject: [PATCH] fixup! Add EntityPortalReadyEvent + + +diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java +index 2bc85351e6e52f90da5fdb29d8d042a06132d742..e7e9c5995b854dfbeb2dfe788bf86ba3457c3086 100644 +--- a/src/main/java/net/minecraft/world/entity/Entity.java ++++ b/src/main/java/net/minecraft/world/entity/Entity.java +@@ -3224,10 +3224,10 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess + this.portalTime = i; + // Paper start - Add EntityPortalReadyEvent + io.papermc.paper.event.entity.EntityPortalReadyEvent event = new io.papermc.paper.event.entity.EntityPortalReadyEvent(this.getBukkitEntity(), worldserver1 == null ? null : worldserver1.getWorld(), org.bukkit.PortalType.NETHER); +- if (!event.callEvent()) { ++ if (!event.callEvent() || event.getTargetWorld() == null) { + this.portalTime = 0; + } else { +- worldserver1 = event.getTargetWorld() == null ? null : ((CraftWorld) event.getTargetWorld()).getHandle(); ++ worldserver1 = ((CraftWorld) event.getTargetWorld()).getHandle(); + // Paper end - Add EntityPortalReadyEvent + this.setPortalCooldown(); + // CraftBukkit start diff --git a/patches/server/1048-Restore-vanilla-teleportation-for-non-players.patch b/patches/server/1048-Restore-vanilla-teleportation-for-non-players.patch new file mode 100644 index 0000000000000..31ab77617d99e --- /dev/null +++ b/patches/server/1048-Restore-vanilla-teleportation-for-non-players.patch @@ -0,0 +1,270 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jake Potrebic +Date: Thu, 16 May 2024 11:38:24 -0700 +Subject: [PATCH] Restore vanilla teleportation for non-players + + +diff --git a/src/main/java/net/minecraft/server/commands/TeleportCommand.java b/src/main/java/net/minecraft/server/commands/TeleportCommand.java +index 54851f6cc0d5fddb32a9a1e84a4f5ae41af18758..849c80876f7cbc6af4c7eef994f9df7f32d46a58 100644 +--- a/src/main/java/net/minecraft/server/commands/TeleportCommand.java ++++ b/src/main/java/net/minecraft/server/commands/TeleportCommand.java +@@ -161,31 +161,7 @@ public class TeleportCommand { + float f2 = Mth.wrapDegrees(yaw); + float f3 = Mth.wrapDegrees(pitch); + +- // CraftBukkit start - Teleport event +- boolean result; +- if (target instanceof ServerPlayer player) { +- result = player.teleportTo(world, x, y, z, movementFlags, f2, f3, PlayerTeleportEvent.TeleportCause.COMMAND); +- } else { +- Location to = new Location(world.getWorld(), x, y, z, f2, f3); +- EntityTeleportEvent event = new EntityTeleportEvent(target.getBukkitEntity(), target.getBukkitEntity().getLocation(), to); +- world.getCraftServer().getPluginManager().callEvent(event); +- if (event.isCancelled() || event.getTo() == null) { // Paper +- return; +- } +- to = event.getTo(); // Paper - actually track new location +- +- x = to.getX(); +- y = to.getY(); +- z = to.getZ(); +- f2 = to.getYaw(); +- f3 = to.getPitch(); +- world = ((CraftWorld) to.getWorld()).getHandle(); +- +- result = target.teleportTo(world, x, y, z, movementFlags, f2, f3); +- } +- +- if (result) { +- // CraftBukkit end ++ if (target.teleportTo(world, x, y, z, movementFlags, f2, f3, PlayerTeleportEvent.TeleportCause.COMMAND)) { // Paper - restore vanilla + if (facingLocation != null) { + facingLocation.perform(source, target); + } +diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java +index c10401e587a710d49c4af481c1e531b4bf51f755..f3907242aa3a85bee100962d200af69a64f3ebab 100644 +--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java ++++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java +@@ -2081,13 +2081,21 @@ public class ServerPlayer extends Player { + this.connection.teleport(this.getX() + offsetX, this.getY() + offsetY, this.getZ() + offsetZ, this.getYRot(), this.getXRot(), RelativeMovement.ALL); + } + +- @Override ++ @Override @Deprecated @io.papermc.paper.annotation.DoNotUse // Paper + public boolean teleportTo(ServerLevel world, double destX, double destY, double destZ, Set flags, float yaw, float pitch) { + // CraftBukkit start + return this.teleportTo(world, destX, destY, destZ, flags, yaw, pitch, TeleportCause.UNKNOWN); + } + ++ @Override // Paper + public boolean teleportTo(ServerLevel worldserver, double d0, double d1, double d2, Set set, float f, float f1, TeleportCause cause) { ++ // Paper start ++ return this.teleportToWithoutEvent(worldserver, d0, d1, d2, set, f, f1, cause); ++ } ++ ++ @Override ++ public boolean teleportToWithoutEvent(ServerLevel worldserver, double d0, double d1, double d2, Set set, float f, float f1, TeleportCause cause) { ++ // Paper end + // CraftBukkit end + ChunkPos chunkcoordintpair = new ChunkPos(BlockPos.containing(d0, d1, d2)); + +diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java +index e7e9c5995b854dfbeb2dfe788bf86ba3457c3086..868e4277e67688e4e61066f822d0b68aa8036503 100644 +--- a/src/main/java/net/minecraft/world/entity/Entity.java ++++ b/src/main/java/net/minecraft/world/entity/Entity.java +@@ -469,7 +469,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess + ((ServerPlayer) this).changeDimension(worldserver, PlayerTeleportEvent.TeleportCause.END_PORTAL); + return; + } +- this.teleportTo(worldserver, null); ++ this.changeDimension(worldserver); // Paper + } + // Paper end - make end portalling safe + // Paper start - optimise entity tracking +@@ -3683,17 +3683,10 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess + } + + @Nullable +- public Entity changeDimension(ServerLevel destination) { +- // CraftBukkit start +- return this.teleportTo(destination, null); +- } +- +- @Nullable +- public Entity teleportTo(ServerLevel worldserver, Vec3 location) { +- // CraftBukkit end ++ public Entity changeDimension(@Nullable ServerLevel worldserver) { // Paper - use old name for less diff + // Paper start - Fix item duplication and teleport issues + if (!this.isAlive() || !this.valid) { +- LOGGER.warn("Illegal Entity Teleport " + this + " to " + worldserver + ":" + location, new Throwable()); ++ LOGGER.warn("Illegal Entity Teleport {} to {}", this, worldserver, new Throwable()); + return null; + } + // Paper end - Fix item duplication and teleport issues +@@ -3706,7 +3699,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess + } + // CraftBukkit end + this.level().getProfiler().push("reposition"); +- PortalInfo shapedetectorshape = (location == null) ? this.findDimensionEntryPoint(worldserver) : new PortalInfo(new Vec3(location.x(), location.y(), location.z()), Vec3.ZERO, this.yRot, this.xRot, worldserver, null); // CraftBukkit ++ PortalInfo shapedetectorshape = this.findDimensionEntryPoint(worldserver); // Paper - restore vanilla + + if (shapedetectorshape == null) { + return null; +@@ -3737,10 +3730,40 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess + // Paper end - Call EntityPortalExitEvent + if (worldserver == this.level) { + // SPIGOT-6782: Just move the entity if a plugin changed the world to the one the entity is already in +- this.moveTo(shapedetectorshape.pos.x, shapedetectorshape.pos.y, shapedetectorshape.pos.z, shapedetectorshape.yRot, shapedetectorshape.xRot); +- this.setDeltaMovement(shapedetectorshape.speed); ++ // Paper start - call EntityTeleportEvent for intra-world teleports ++ if (!this.teleportTo( ++ (ServerLevel) this.level, ++ position.x, ++ position.y, ++ position.z, ++ java.util.Collections.emptySet(), ++ yaw, ++ pitch, ++ PlayerTeleportEvent.TeleportCause.UNKNOWN // unknown is fine because entity events don't have a cause yet ++ )) { ++ return null; ++ } ++ // Paper end - call EntityTeleportEvent for intra-world teleports + return this; ++ // Paper start - call EntityTeleportEvent for inter-world teleports ++ } else { ++ final org.bukkit.event.entity.EntityTeleportEvent tpEvent = org.bukkit.craftbukkit.event.CraftEventFactory.callEntityTeleportEvent( ++ this, ++ position.x, ++ position.y, ++ position.z, ++ yaw, ++ pitch ++ ); ++ if (tpEvent.isCancelled() || tpEvent.getTo() == null) { ++ return null; ++ } ++ worldserver = ((CraftWorld) event.getTo().getWorld()).getHandle(); ++ position = CraftLocation.toVec3D(event.getTo()); ++ yaw = event.getTo().getYaw(); ++ pitch = event.getTo().getPitch(); + } ++ // Paper end - call EntityTeleportEvent for inter-world teleports + this.unRide(); + // CraftBukkit end + +@@ -4011,13 +4034,28 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess + } + } + +- // CraftBukkit start +- public boolean teleportTo(ServerLevel worldserver, double d0, double d1, double d2, Set set, float f, float f1, org.bukkit.event.player.PlayerTeleportEvent.TeleportCause cause) { +- return this.teleportTo(worldserver, d0, d1, d2, set, f, f1); ++ @Deprecated @io.papermc.paper.annotation.DoNotUse // Paper ++ public boolean teleportTo(ServerLevel worldserver, double d0, double d1, double d2, Set set, float f, float f1) { ++ return this.teleportTo(worldserver, d0, d1, d2, set, f, f1, PlayerTeleportEvent.TeleportCause.UNKNOWN); // Paper ++ } ++ ++ // Paper start - call EntityTeleportEvent ++ public boolean teleportTo(ServerLevel world, double destX, double destY, double destZ, Set flags, float yaw, float pitch, PlayerTeleportEvent.TeleportCause cause) { ++ final org.bukkit.event.entity.EntityTeleportEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callEntityTeleportEvent(this, destX, destY, destZ, yaw, pitch); ++ if (event.isCancelled() || event.getTo() == null) { ++ return false; ++ } ++ world = ((org.bukkit.craftbukkit.CraftWorld) event.getTo().getWorld()).getHandle(); ++ destX = event.getTo().getX(); ++ destY = event.getTo().getY(); ++ destZ = event.getTo().getZ(); ++ yaw = event.getTo().getYaw(); ++ pitch = event.getTo().getPitch(); ++ return this.teleportToWithoutEvent(world, destX, destY, destZ, flags, yaw, pitch, cause); + } +- // CraftBukkit end + +- public boolean teleportTo(ServerLevel world, double destX, double destY, double destZ, Set flags, float yaw, float pitch) { ++ public boolean teleportToWithoutEvent(ServerLevel world, double destX, double destY, double destZ, Set flags, float yaw, float pitch, PlayerTeleportEvent.TeleportCause cause) { ++ // Paper end - call EntityTeleportEvent + float f2 = Mth.clamp(pitch, -90.0F, 90.0F); + + if (world == this.level()) { +diff --git a/src/main/java/net/minecraft/world/entity/projectile/ThrownEnderpearl.java b/src/main/java/net/minecraft/world/entity/projectile/ThrownEnderpearl.java +index 519755b7f8bc7e8bb9fab135fc5bf7de3a9419f9..6ae4a9aa72f4745fb3a124a7fe11aee36a22cda3 100644 +--- a/src/main/java/net/minecraft/world/entity/projectile/ThrownEnderpearl.java ++++ b/src/main/java/net/minecraft/world/entity/projectile/ThrownEnderpearl.java +@@ -117,7 +117,7 @@ public class ThrownEnderpearl extends ThrowableItemProjectile { + public Entity changeDimension(ServerLevel destination) { + Entity entity = this.getOwner(); + +- if (entity != null && destination != null && entity.level().dimension() != destination.dimension()) { // CraftBukkit - SPIGOT-6113 ++ if (entity != null && entity.level().dimension() != destination.dimension()) { // Paper - restore vanilla + this.setOwner((Entity) null); + } + +diff --git a/src/main/java/org/bukkit/craftbukkit/CraftRegionAccessor.java b/src/main/java/org/bukkit/craftbukkit/CraftRegionAccessor.java +index 1d16cd01e55f3e5bb8f49ad4c9f777d30180aab5..40027e17fd3c484ffa86ef65f6a06fca469b640a 100644 +--- a/src/main/java/org/bukkit/craftbukkit/CraftRegionAccessor.java ++++ b/src/main/java/org/bukkit/craftbukkit/CraftRegionAccessor.java +@@ -426,7 +426,7 @@ public abstract class CraftRegionAccessor implements RegionAccessor { + Preconditions.checkArgument(!entity.isInWorld(), "Entity has already been added to a world"); + net.minecraft.world.entity.Entity nmsEntity = ((CraftEntity) entity).getHandle(); + if (nmsEntity.level() != this.getHandle().getLevel()) { +- nmsEntity = nmsEntity.changeDimension(this.getHandle().getLevel()); ++ throw new IllegalArgumentException(entity + " wasn't created with this world, you must create the entity with the world you want to add it to."); // Paper - throw instead of teleporting + } + + this.addEntityWithPassengers(nmsEntity, CreatureSpawnEvent.SpawnReason.CUSTOM); +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java +index 0f64e3e3ce1e932a8df6b27ab9bbfc2156f58f04..faa7deb2234bd8faeb9bc5ea1e897c5dd4465aeb 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java +@@ -273,19 +273,20 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity { + if (!retainPassengers) this.entity.ejectPassengers(); // Paper - Teleport passenger API + + // Let the server handle cross world teleports +- if (location.getWorld() != null && !location.getWorld().equals(this.getWorld())) { +- // Prevent teleportation to an other world during world generation ++ // Paper start - restore more vanilla teleport logic ++ if (!sameWorld) { + Preconditions.checkState(!this.entity.generation, "Cannot teleport entity to an other world during world generation"); +- this.entity.teleportTo(((CraftWorld) location.getWorld()).getHandle(), CraftLocation.toVec3D(location)); +- return true; + } +- +- // entity.setLocation() throws no event, and so cannot be cancelled +- entity.moveTo(location.getX(), location.getY(), location.getZ(), location.getYaw(), location.getPitch()); // Paper - use proper moveTo, as per vanilla teleporting +- // SPIGOT-619: Force sync head rotation also +- this.entity.setYHeadRot(location.getYaw()); +- +- return true; ++ return this.entity.teleportToWithoutEvent( // event fired above ++ (ServerLevel) (location.getWorld() != null ? ((org.bukkit.craftbukkit.CraftWorld) location.getWorld()).getHandle() : this.entity.level()), ++ location.getX(), ++ location.getY(), ++ location.getZ(), ++ java.util.Collections.emptySet(), // relative movement has no effect for entities ++ location.getYaw(), ++ location.getPitch(), ++ cause ++ ); + } + + @Override +diff --git a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java +index 34b91eff3190848bae38b20e1d956ece497b1473..d611ccb88ce74da1fbbd318e0b59c55fa03a34a6 100644 +--- a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java ++++ b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java +@@ -2042,8 +2042,13 @@ public class CraftEventFactory { + } + + public static EntityTeleportEvent callEntityTeleportEvent(Entity nmsEntity, double x, double y, double z) { ++ // Paper start ++ return callEntityTeleportEvent(nmsEntity, x, y, z, nmsEntity.getYRot(), nmsEntity.getXRot()); ++ } ++ public static EntityTeleportEvent callEntityTeleportEvent(Entity nmsEntity, double x, double y, double z, float yaw, float pitch) { ++ // Paper end + CraftEntity entity = nmsEntity.getBukkitEntity(); +- Location to = new Location(entity.getWorld(), x, y, z, nmsEntity.getYRot(), nmsEntity.getXRot()); ++ Location to = new Location(entity.getWorld(), x, y, z, yaw, pitch); // Paper + EntityTeleportEvent event = new org.bukkit.event.entity.EntityTeleportEvent(entity, entity.getLocation(), to); + + Bukkit.getPluginManager().callEvent(event); diff --git a/test-plugin/src/main/java/io/papermc/testplugin/TestPlugin.java b/test-plugin/src/main/java/io/papermc/testplugin/TestPlugin.java index 671c37fa40963..e5a4ce1e3dd39 100644 --- a/test-plugin/src/main/java/io/papermc/testplugin/TestPlugin.java +++ b/test-plugin/src/main/java/io/papermc/testplugin/TestPlugin.java @@ -1,6 +1,14 @@ package io.papermc.testplugin; +import io.papermc.paper.entity.TeleportFlag; +import io.papermc.paper.event.player.ChatEvent; +import java.util.Collection; +import org.bukkit.entity.Pig; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; +import org.bukkit.event.entity.EntityTeleportEvent; +import org.bukkit.event.player.PlayerTeleportEvent; import org.bukkit.plugin.java.JavaPlugin; public final class TestPlugin extends JavaPlugin implements Listener { @@ -12,4 +20,29 @@ public void onEnable() { // io.papermc.testplugin.brigtests.Registration.registerViaOnEnable(this); } + @EventHandler + public void onEvent(ChatEvent event) { + final Player player = event.getPlayer(); + final Collection nearby = player.getWorld().getNearbyEntitiesByType(Pig.class, player.getLocation(), 10); + for (final Pig pig : nearby) { + if (!pig.getPassengers().isEmpty()) { + System.out.println("Teleport: " + pig.teleport(player.getLocation())); + } + } + } + + @EventHandler + public void onEvent(EntityTeleportEvent event) { + System.out.println("EntityTeleportEvent"); + System.out.println(event.getEntity()); + System.out.println(event.getClass().getSimpleName()); + } + + @EventHandler + public void onEvent(PlayerTeleportEvent event) { + System.out.println("PlayerTeleportEvent"); + System.out.println(event.getPlayer()); + System.out.println(event.getClass().getSimpleName()); + } + }