From 61c79dda8acaaf3cd2556d5c7f8915714b1583df Mon Sep 17 00:00:00 2001 From: TylerS1066 Date: Sun, 25 Aug 2024 15:39:01 -0500 Subject: [PATCH] Paper smooth teleportation (#702) * First attempt * Second attempt * Third attempt * Working * Remove inventory access for 1.20+ * Fix world change logic * Update 1.21 implementation * Fix manoverboard * Revert "Remove inventory access for 1.20+" This reverts commit 161ad50c260be65ba3e481f17af7ff5ff2fe4cb4. * Deprecate access for 1.20+ * Fix 1.18.2 implementation --- .../commands/ManOverboardCommand.java | 5 +- .../update/EntityUpdateCommand.java | 20 ++++--- .../movecraft/SmoothTeleport.java | 2 +- .../countercraft/movecraft/WorldHandler.java | 6 +- .../movecraft/util/BukkitTeleport.java | 4 +- .../support/v1_18/ISmoothTeleport.java | 4 +- .../movecraft/compat/v1_20/IWorldHandler.java | 20 +------ .../support/v1_20/ISmoothTeleport.java | 60 ++++--------------- .../movecraft/compat/v1_21/IWorldHandler.java | 20 +------ .../support/v1_21/ISmoothTeleport.java | 60 ++++--------------- 10 files changed, 54 insertions(+), 147 deletions(-) diff --git a/Movecraft/src/main/java/net/countercraft/movecraft/commands/ManOverboardCommand.java b/Movecraft/src/main/java/net/countercraft/movecraft/commands/ManOverboardCommand.java index e3118c255..98882f49c 100644 --- a/Movecraft/src/main/java/net/countercraft/movecraft/commands/ManOverboardCommand.java +++ b/Movecraft/src/main/java/net/countercraft/movecraft/commands/ManOverboardCommand.java @@ -8,7 +8,6 @@ import net.countercraft.movecraft.events.ManOverboardEvent; import net.countercraft.movecraft.localisation.I18nSupport; import net.countercraft.movecraft.util.MathUtils; -import net.countercraft.movecraft.util.ReflectUtils; import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.command.Command; @@ -71,9 +70,11 @@ public boolean onCommand(CommandSender commandSender, Command command, String s, ManOverboardEvent event = new ManOverboardEvent(craft, telPoint); Bukkit.getServer().getPluginManager().callEvent(event); + telPoint.setYaw(player.getLocation().getYaw()); + telPoint.setPitch(player.getLocation().getPitch()); player.setVelocity(new Vector(0, 0, 0)); player.setFallDistance(0); - Movecraft.getInstance().getSmoothTeleport().teleport(player, telPoint, 0, 0); + Movecraft.getInstance().getSmoothTeleport().teleport(player, telPoint); return true; } diff --git a/Movecraft/src/main/java/net/countercraft/movecraft/mapUpdater/update/EntityUpdateCommand.java b/Movecraft/src/main/java/net/countercraft/movecraft/mapUpdater/update/EntityUpdateCommand.java index c334f4e5b..40f7ce2b8 100644 --- a/Movecraft/src/main/java/net/countercraft/movecraft/mapUpdater/update/EntityUpdateCommand.java +++ b/Movecraft/src/main/java/net/countercraft/movecraft/mapUpdater/update/EntityUpdateCommand.java @@ -83,17 +83,21 @@ public Entity getEntity() { @Override public void doUpdate() { - Location playerLoc = entity.getLocation(); - // Use bukkit teleporting API for changing worlds because it won't be smooth anyway - if (!(entity instanceof Player) || !playerLoc.getWorld().equals(world)) { - entity.teleport(new Location(world, x + playerLoc.getX(),y + playerLoc.getY(),z + playerLoc.getZ(),yaw + playerLoc.getYaw(),pitch + playerLoc.getPitch())); - return; - } - Location location = new Location(world, playerLoc.getX() + x, playerLoc.getY() + y, playerLoc.getZ() + z); - Movecraft.getInstance().getSmoothTeleport().teleport((Player) entity, location, yaw, pitch); + Location location = entity.getLocation().add(x, y, z); + location.setYaw(location.getYaw() + yaw); + location.setPitch(location.getPitch() + pitch); + location.setWorld(world); if (sound != null) { ((Player) entity).playSound(location, sound, volume, 1.0f); } + + // Use bukkit teleporting API for changing worlds because it won't be smooth anyway + if (!(entity instanceof Player) || !entity.getLocation().getWorld().equals(world)) { + entity.teleport(location); + return; + } + + Movecraft.getInstance().getSmoothTeleport().teleport((Player) entity, location); } @Override diff --git a/api/src/main/java/net/countercraft/movecraft/SmoothTeleport.java b/api/src/main/java/net/countercraft/movecraft/SmoothTeleport.java index f6e9e898c..97e7fbb31 100644 --- a/api/src/main/java/net/countercraft/movecraft/SmoothTeleport.java +++ b/api/src/main/java/net/countercraft/movecraft/SmoothTeleport.java @@ -5,5 +5,5 @@ import org.jetbrains.annotations.NotNull; public abstract class SmoothTeleport { - public abstract void teleport(Player player, @NotNull Location location, float yawChange, float pitchChange); + public abstract void teleport(@NotNull Player player, @NotNull Location location); } diff --git a/api/src/main/java/net/countercraft/movecraft/WorldHandler.java b/api/src/main/java/net/countercraft/movecraft/WorldHandler.java index bbf829daf..3b72e569f 100644 --- a/api/src/main/java/net/countercraft/movecraft/WorldHandler.java +++ b/api/src/main/java/net/countercraft/movecraft/WorldHandler.java @@ -15,8 +15,10 @@ public abstract class WorldHandler { public abstract void translateCraft(@NotNull Craft craft, @NotNull MovecraftLocation newLocation, @NotNull World world); public abstract void setBlockFast(@NotNull Location location, @NotNull BlockData data); public abstract void setBlockFast(@NotNull Location location, @NotNull MovecraftRotation rotation, @NotNull BlockData data); - public abstract @Nullable Location getAccessLocation(@NotNull InventoryView inventoryView); - public abstract void setAccessLocation(@NotNull InventoryView inventoryView, @NotNull Location location); + @Deprecated(forRemoval = true) + public abstract @Nullable Location getAccessLocation(@NotNull InventoryView inventoryView); // Not needed for 1.20+, remove when dropping support for 1.18.2 + @Deprecated(forRemoval = true) + public abstract void setAccessLocation(@NotNull InventoryView inventoryView, @NotNull Location location); // Not needed for 1.20+, remove when dropping support for 1.18.2 public static @NotNull String getPackageName(@NotNull String minecraftVersion) { String[] parts = minecraftVersion.split("\\."); diff --git a/api/src/main/java/net/countercraft/movecraft/util/BukkitTeleport.java b/api/src/main/java/net/countercraft/movecraft/util/BukkitTeleport.java index 7bc69dc9e..702d3b26f 100644 --- a/api/src/main/java/net/countercraft/movecraft/util/BukkitTeleport.java +++ b/api/src/main/java/net/countercraft/movecraft/util/BukkitTeleport.java @@ -7,9 +7,7 @@ public class BukkitTeleport extends SmoothTeleport { @Override - public void teleport(Player player, @NotNull Location location, float yawChange, float pitchChange) { - location.setYaw(player.getLocation().getYaw() + yawChange); - location.setPitch(player.getLocation().getPitch() + pitchChange); + public void teleport(@NotNull Player player, @NotNull Location location) { player.teleport(location); } } diff --git a/v1_18/src/main/java/net/countercraft/movecraft/support/v1_18/ISmoothTeleport.java b/v1_18/src/main/java/net/countercraft/movecraft/support/v1_18/ISmoothTeleport.java index 5c8beb91f..be1b7b0be 100644 --- a/v1_18/src/main/java/net/countercraft/movecraft/support/v1_18/ISmoothTeleport.java +++ b/v1_18/src/main/java/net/countercraft/movecraft/support/v1_18/ISmoothTeleport.java @@ -75,10 +75,12 @@ public ISmoothTeleport() throws ClassNotFoundException, NoSuchMethodException, N pitchField = ReflectUtils.getField(entityClass, "aA"); // yRot } - public void teleport(Player player, @NotNull Location location, float yawChange, float pitchChange) { + public void teleport(@NotNull Player player, @NotNull Location location) { double x = location.getX(); double y = location.getY(); double z = location.getZ(); + float yawChange = location.getYaw() - player.getLocation().getYaw(); + float pitchChange = player.getLocation().getPitch() - location.getPitch(); Object handle = ReflectUtils.getHandle(player); try { positionMethod.invoke(handle, x, y, z, yawField.get(handle), pitchField.get(handle)); diff --git a/v1_20/src/main/java/net/countercraft/movecraft/compat/v1_20/IWorldHandler.java b/v1_20/src/main/java/net/countercraft/movecraft/compat/v1_20/IWorldHandler.java index 676ea054f..cd2a5565a 100644 --- a/v1_20/src/main/java/net/countercraft/movecraft/compat/v1_20/IWorldHandler.java +++ b/v1_20/src/main/java/net/countercraft/movecraft/compat/v1_20/IWorldHandler.java @@ -262,29 +262,13 @@ public void setBlockFast(@NotNull Location location, @NotNull MovecraftRotation @Override public @Nullable Location getAccessLocation(@NotNull InventoryView inventoryView) { - AbstractContainerMenu menu = ((CraftInventoryView) inventoryView).getHandle(); - Field field = UnsafeUtils.getFieldOfType(ContainerLevelAccess.class, menu.getClass()); - if (field != null) { - try { - field.setAccessible(true); - return ((ContainerLevelAccess) field.get(menu)).getLocation(); - } catch (IllegalAccessException e) { - e.printStackTrace(); - } - } + // Not needed for 1.20+, remove when dropping support for 1.18.2 return null; } @Override public void setAccessLocation(@NotNull InventoryView inventoryView, @NotNull Location location) { - if (location.getWorld() == null) - return; - ServerLevel level = ((CraftWorld) location.getWorld()).getHandle(); - BlockPos position = new BlockPos(location.getBlockX(), location.getBlockY(), location.getBlockZ()); - ContainerLevelAccess access = ContainerLevelAccess.create(level, position); - - AbstractContainerMenu menu = ((CraftInventoryView) inventoryView).getHandle(); - UnsafeUtils.trySetFieldOfType(ContainerLevelAccess.class, menu, access); + // Not needed for 1.20+, remove when dropping support for 1.18.2 } private void moveBlockEntity(@NotNull Level nativeWorld, @NotNull BlockPos newPosition, @NotNull BlockEntity tile) { diff --git a/v1_20/src/main/java/net/countercraft/movecraft/support/v1_20/ISmoothTeleport.java b/v1_20/src/main/java/net/countercraft/movecraft/support/v1_20/ISmoothTeleport.java index f1224b7ea..ca4a4731c 100644 --- a/v1_20/src/main/java/net/countercraft/movecraft/support/v1_20/ISmoothTeleport.java +++ b/v1_20/src/main/java/net/countercraft/movecraft/support/v1_20/ISmoothTeleport.java @@ -1,57 +1,23 @@ package net.countercraft.movecraft.support.v1_20; +import io.papermc.paper.entity.TeleportFlag; import net.countercraft.movecraft.SmoothTeleport; -import net.countercraft.movecraft.util.ReflectUtils; -import net.minecraft.network.protocol.game.ClientboundPlayerPositionPacket; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.server.network.ServerGamePacketListenerImpl; -import net.minecraft.world.entity.RelativeMovement; -import net.minecraft.world.phys.Vec3; import org.bukkit.Location; import org.bukkit.entity.Player; import org.jetbrains.annotations.NotNull; -import java.lang.reflect.Field; -import java.util.Set; - -/** - * Code derived from code taken with permission from MicleBrick - * https://www.spigotmc.org/threads/teleport-player-smoothly.317416/ - * Used for 1.20.6 - */ public class ISmoothTeleport extends SmoothTeleport { - private final Field teleportPosField; - private final Field teleportAwaitField; - private final Field awaitingTeleportTimeField; - private final Field tickCountField; - - public ISmoothTeleport() throws NoSuchFieldException, ClassNotFoundException { - teleportPosField = ReflectUtils.getField(ServerGamePacketListenerImpl.class, "F"); // awaitingPositionFromClient - teleportAwaitField = ReflectUtils.getField(ServerGamePacketListenerImpl.class, "G"); // awaitingTeleport - awaitingTeleportTimeField = ReflectUtils.getField(ServerGamePacketListenerImpl.class, "H"); // awaitingTeleportTime - tickCountField = ReflectUtils.getField(ServerGamePacketListenerImpl.class, "o"); // tickCount - } - - public void teleport(Player player, @NotNull Location location, float yawChange, float pitchChange) { - double x = location.getX(); - double y = location.getY(); - double z = location.getZ(); - ServerPlayer handle = (ServerPlayer) ReflectUtils.getHandle(player); - - try { - handle.absMoveTo(x, y, z, handle.getXRot(), handle.getYRot()); - ServerGamePacketListenerImpl connection = handle.connection; - teleportPosField.set(connection, new Vec3(x, y, z)); - int teleportAwait = teleportAwaitField.getInt(connection) + 1; - if (teleportAwait == Integer.MAX_VALUE) - teleportAwait = 0; - teleportAwaitField.setInt(connection, teleportAwait); - awaitingTeleportTimeField.set(connection, tickCountField.get(connection)); - - ClientboundPlayerPositionPacket packet = new ClientboundPlayerPositionPacket(x, y, z, yawChange, pitchChange, Set.of(RelativeMovement.X_ROT, RelativeMovement.Y_ROT), teleportAwait); - connection.send(packet); - } catch (IllegalAccessException e) { - e.printStackTrace(); - } + public void teleport(@NotNull Player player, @NotNull Location location) { + player.teleport( + location, + TeleportFlag.Relative.X, + TeleportFlag.Relative.Y, + TeleportFlag.Relative.Z, + TeleportFlag.Relative.PITCH, + TeleportFlag.Relative.YAW, + TeleportFlag.EntityState.RETAIN_OPEN_INVENTORY, + TeleportFlag.EntityState.RETAIN_VEHICLE, + TeleportFlag.EntityState.RETAIN_PASSENGERS + ); } } diff --git a/v1_21/src/main/java/net/countercraft/movecraft/compat/v1_21/IWorldHandler.java b/v1_21/src/main/java/net/countercraft/movecraft/compat/v1_21/IWorldHandler.java index 4a48783c8..30f1e8182 100644 --- a/v1_21/src/main/java/net/countercraft/movecraft/compat/v1_21/IWorldHandler.java +++ b/v1_21/src/main/java/net/countercraft/movecraft/compat/v1_21/IWorldHandler.java @@ -257,29 +257,13 @@ public void setBlockFast(@NotNull Location location, @NotNull MovecraftRotation @Override public @Nullable Location getAccessLocation(@NotNull InventoryView inventoryView) { - AbstractContainerMenu menu = ((CraftInventoryView) inventoryView).getHandle(); - Field field = UnsafeUtils.getFieldOfType(ContainerLevelAccess.class, menu.getClass()); - if (field != null) { - try { - field.setAccessible(true); - return ((ContainerLevelAccess) field.get(menu)).getLocation(); - } catch (IllegalAccessException e) { - e.printStackTrace(); - } - } + // Not needed for 1.20+, remove when dropping support for 1.18.2 return null; } @Override public void setAccessLocation(@NotNull InventoryView inventoryView, @NotNull Location location) { - if (location.getWorld() == null) - return; - ServerLevel level = ((CraftWorld) location.getWorld()).getHandle(); - BlockPos position = new BlockPos(location.getBlockX(), location.getBlockY(), location.getBlockZ()); - ContainerLevelAccess access = ContainerLevelAccess.create(level, position); - - AbstractContainerMenu menu = ((CraftInventoryView) inventoryView).getHandle(); - UnsafeUtils.trySetFieldOfType(ContainerLevelAccess.class, menu, access); + // Not needed for 1.20+, remove when dropping support for 1.18.2 } private void moveBlockEntity(@NotNull Level nativeWorld, @NotNull BlockPos newPosition, @NotNull BlockEntity tile) { diff --git a/v1_21/src/main/java/net/countercraft/movecraft/support/v1_21/ISmoothTeleport.java b/v1_21/src/main/java/net/countercraft/movecraft/support/v1_21/ISmoothTeleport.java index 0d77c7f9a..edd20345f 100644 --- a/v1_21/src/main/java/net/countercraft/movecraft/support/v1_21/ISmoothTeleport.java +++ b/v1_21/src/main/java/net/countercraft/movecraft/support/v1_21/ISmoothTeleport.java @@ -1,57 +1,23 @@ package net.countercraft.movecraft.support.v1_21; +import io.papermc.paper.entity.TeleportFlag; import net.countercraft.movecraft.SmoothTeleport; -import net.countercraft.movecraft.util.ReflectUtils; -import net.minecraft.network.protocol.game.ClientboundPlayerPositionPacket; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.server.network.ServerGamePacketListenerImpl; -import net.minecraft.world.entity.RelativeMovement; -import net.minecraft.world.phys.Vec3; import org.bukkit.Location; import org.bukkit.entity.Player; import org.jetbrains.annotations.NotNull; -import java.lang.reflect.Field; -import java.util.Set; - -/** - * Code derived from code taken with permission from MicleBrick - * https://www.spigotmc.org/threads/teleport-player-smoothly.317416/ - * Used for 1.21.1 - */ public class ISmoothTeleport extends SmoothTeleport { - private final Field teleportPosField; - private final Field teleportAwaitField; - private final Field awaitingTeleportTimeField; - private final Field tickCountField; - - public ISmoothTeleport() throws NoSuchFieldException, ClassNotFoundException { - teleportPosField = ReflectUtils.getField(ServerGamePacketListenerImpl.class, "F"); // awaitingPositionFromClient - teleportAwaitField = ReflectUtils.getField(ServerGamePacketListenerImpl.class, "G"); // awaitingTeleport - awaitingTeleportTimeField = ReflectUtils.getField(ServerGamePacketListenerImpl.class, "H"); // awaitingTeleportTime - tickCountField = ReflectUtils.getField(ServerGamePacketListenerImpl.class, "o"); // tickCount - } - - public void teleport(Player player, @NotNull Location location, float yawChange, float pitchChange) { - double x = location.getX(); - double y = location.getY(); - double z = location.getZ(); - ServerPlayer handle = (ServerPlayer) ReflectUtils.getHandle(player); - - try { - handle.absMoveTo(x, y, z, handle.getXRot(), handle.getYRot()); - ServerGamePacketListenerImpl connection = handle.connection; - teleportPosField.set(connection, new Vec3(x, y, z)); - int teleportAwait = teleportAwaitField.getInt(connection) + 1; - if (teleportAwait == Integer.MAX_VALUE) - teleportAwait = 0; - teleportAwaitField.setInt(connection, teleportAwait); - awaitingTeleportTimeField.set(connection, tickCountField.get(connection)); - - ClientboundPlayerPositionPacket packet = new ClientboundPlayerPositionPacket(x, y, z, yawChange, pitchChange, Set.of(RelativeMovement.X_ROT, RelativeMovement.Y_ROT), teleportAwait); - connection.send(packet); - } catch (IllegalAccessException e) { - e.printStackTrace(); - } + public void teleport(@NotNull Player player, @NotNull Location location) { + player.teleport( + location, + TeleportFlag.Relative.X, + TeleportFlag.Relative.Y, + TeleportFlag.Relative.Z, + TeleportFlag.Relative.PITCH, + TeleportFlag.Relative.YAW, + TeleportFlag.EntityState.RETAIN_OPEN_INVENTORY, + TeleportFlag.EntityState.RETAIN_VEHICLE, + TeleportFlag.EntityState.RETAIN_PASSENGERS + ); } }