From e4efe14ed0bbdfca6101feb0d6453e54b011a93e Mon Sep 17 00:00:00 2001 From: TylerS1066 Date: Sun, 31 Jul 2022 18:05:00 -0500 Subject: [PATCH] Smooth teleportation (#536) * Initial attempt at v1_17_R1 smooth teleportataion * Fix compile errors for v1_17_R1 smooth teleportation * Revert worldhandler changes * Finish cleanup * Cleanup and reorganize * Move and start new teleport utils * Finish Spigot mapped teleport with placeholder mojang mapped teleport * Initial attempt at mojang mapped * Fix typo * Convert to spigot mappings, remove unknown fields * Update AbstractTeleport.java * Initial 1.18 test * Clarified teleportation classes and modes * Rename teleportation implementations, add 1.19.1 * Cleanup and add smooth teleportation to Manoverboard --- .../commands/ManOverboardCommand.java | 12 +- .../update/EntityUpdateCommand.java | 5 +- .../movecraft/util/TeleportUtils.java | 152 ------------------ .../util/teleport/AbstractTeleport.java | 14 ++ .../util/teleport/SpigotMappedTeleport.java | 119 ++++++++++++++ .../util/teleport/TeleportUtils.java | 101 ++++++++++++ .../util/teleport/V1_17Teleport.java | 118 ++++++++++++++ .../util/teleport/V1_18Teleport.java | 118 ++++++++++++++ .../util/teleport/V1_19Teleport.java | 118 ++++++++++++++ .../compat/v1_17_R1/IWorldHandler.java | 6 - 10 files changed, 597 insertions(+), 166 deletions(-) delete mode 100644 modules/Movecraft/src/main/java/net/countercraft/movecraft/util/TeleportUtils.java create mode 100644 modules/Movecraft/src/main/java/net/countercraft/movecraft/util/teleport/AbstractTeleport.java create mode 100644 modules/Movecraft/src/main/java/net/countercraft/movecraft/util/teleport/SpigotMappedTeleport.java create mode 100644 modules/Movecraft/src/main/java/net/countercraft/movecraft/util/teleport/TeleportUtils.java create mode 100644 modules/Movecraft/src/main/java/net/countercraft/movecraft/util/teleport/V1_17Teleport.java create mode 100644 modules/Movecraft/src/main/java/net/countercraft/movecraft/util/teleport/V1_18Teleport.java create mode 100644 modules/Movecraft/src/main/java/net/countercraft/movecraft/util/teleport/V1_19Teleport.java diff --git a/modules/Movecraft/src/main/java/net/countercraft/movecraft/commands/ManOverboardCommand.java b/modules/Movecraft/src/main/java/net/countercraft/movecraft/commands/ManOverboardCommand.java index 4a15f77fb..b06e93dcd 100644 --- a/modules/Movecraft/src/main/java/net/countercraft/movecraft/commands/ManOverboardCommand.java +++ b/modules/Movecraft/src/main/java/net/countercraft/movecraft/commands/ManOverboardCommand.java @@ -8,6 +8,7 @@ import net.countercraft.movecraft.events.ManOverboardEvent; import net.countercraft.movecraft.localisation.I18nSupport; import net.countercraft.movecraft.util.MathUtils; +import net.countercraft.movecraft.util.teleport.TeleportUtils; import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.command.Command; @@ -15,10 +16,11 @@ import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; import org.bukkit.util.Vector; +import org.jetbrains.annotations.NotNull; import static net.countercraft.movecraft.util.ChatUtils.MOVECRAFT_COMMAND_PREFIX; -public class ManOverboardCommand implements CommandExecutor{ +public class ManOverboardCommand implements CommandExecutor { @Override public boolean onCommand(CommandSender commandSender, Command command, String s, String[] strings) { @@ -71,13 +73,13 @@ public boolean onCommand(CommandSender commandSender, Command command, String s, player.setVelocity(new Vector(0, 0, 0)); player.setFallDistance(0); - player.teleport(telPoint); + TeleportUtils.teleport(player, telPoint, 0, 0); return true; } - private Location getCraftTeleportPoint(Craft craft) { - double telX = (craft.getHitBox().getMinX() + craft.getHitBox().getMaxX())/2D + 0.5D; - double telZ = (craft.getHitBox().getMinZ() + craft.getHitBox().getMaxZ())/2D + 0.5D; + private @NotNull Location getCraftTeleportPoint(@NotNull Craft craft) { + double telX = ((craft.getHitBox().getMinX() + craft.getHitBox().getMaxX()) / 2D) + 0.5D; + double telZ = ((craft.getHitBox().getMinZ() + craft.getHitBox().getMaxZ()) / 2D) + 0.5D; double telY = craft.getHitBox().getMaxY() + 1; return new Location(craft.getWorld(), telX, telY, telZ); } diff --git a/modules/Movecraft/src/main/java/net/countercraft/movecraft/mapUpdater/update/EntityUpdateCommand.java b/modules/Movecraft/src/main/java/net/countercraft/movecraft/mapUpdater/update/EntityUpdateCommand.java index 9f6a66861..853fde2f1 100644 --- a/modules/Movecraft/src/main/java/net/countercraft/movecraft/mapUpdater/update/EntityUpdateCommand.java +++ b/modules/Movecraft/src/main/java/net/countercraft/movecraft/mapUpdater/update/EntityUpdateCommand.java @@ -17,7 +17,7 @@ package net.countercraft.movecraft.mapUpdater.update; -import net.countercraft.movecraft.util.TeleportUtils; +import net.countercraft.movecraft.util.teleport.TeleportUtils; import org.bukkit.Location; import org.bukkit.Sound; import org.bukkit.World; @@ -88,9 +88,8 @@ public void doUpdate() { entity.teleport(new Location(world, x + playerLoc.getX(),y + playerLoc.getY(),z + playerLoc.getZ(),yaw + playerLoc.getYaw(),pitch + playerLoc.getPitch())); return; } - //Movecraft.getInstance().getWorldHandler().addPlayerLocation((Player) entity,x,y,z,yaw,pitch); Location location = new Location(world, playerLoc.getX() + x, playerLoc.getY() + y, playerLoc.getZ() + z); - TeleportUtils.teleport((Player) entity, location, yaw); + TeleportUtils.teleport((Player) entity, location, yaw, pitch); if (sound != null) { ((Player) entity).playSound(location, sound, volume, 1.0f); } diff --git a/modules/Movecraft/src/main/java/net/countercraft/movecraft/util/TeleportUtils.java b/modules/Movecraft/src/main/java/net/countercraft/movecraft/util/TeleportUtils.java deleted file mode 100644 index 5c0bbd83a..000000000 --- a/modules/Movecraft/src/main/java/net/countercraft/movecraft/util/TeleportUtils.java +++ /dev/null @@ -1,152 +0,0 @@ -package net.countercraft.movecraft.util; - -import net.countercraft.movecraft.Movecraft; -import org.bukkit.Bukkit; -import org.bukkit.Location; -import org.bukkit.entity.Entity; -import org.bukkit.entity.Player; - -import java.lang.reflect.Constructor; -import java.lang.reflect.Field; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashSet; -import java.util.Set; - -/** - * Code taken with permission from MicleBrick - * https://www.spigotmc.org/threads/teleport-player-smoothly.317416/ - */ -public class TeleportUtils { - private static Set teleportFlags; - - private static Constructor packetConstructor; - private static Constructor vec3D; - - private static Method position; - private static Method closeInventory; - private static Method sendMethod; - - private static Field connectionField; - private static Field justTeleportedField; - private static Field teleportPosField; - private static Field lastPosXField; - private static Field lastPosYField; - private static Field lastPosZField; - private static Field teleportAwaitField; - private static Field AField; - private static Field eField; - private static Field yaw; - private static Field pitch; - private static Field activeContainer; - private static Field defaultContainer; - private static final boolean intialized; - - static { - boolean sucess = false; - try { - Class packet = getNmsClass("Packet"); - Class entity = getNmsClass("Entity"); - Class entityPlayer = getNmsClass("EntityPlayer"); - Class entityHuman = getNmsClass("EntityHuman"); - Class connectionClass = getNmsClass("PlayerConnection"); - Class packetClass = getNmsClass("PacketPlayOutPosition"); - Class vecClass = getNmsClass("Vec3D"); - sendMethod = connectionClass.getMethod("sendPacket", packet); - - position = entity.getDeclaredMethod("setLocation", Double.TYPE, Double.TYPE, Double.TYPE, Float.TYPE, Float.TYPE); - closeInventory = entityPlayer.getDeclaredMethod("closeInventory"); - - yaw = getField(entity, "yaw"); - pitch = getField(entity, "pitch"); - connectionField = getField(entityPlayer, "playerConnection"); - activeContainer = getField(entityHuman, "activeContainer"); - defaultContainer = getField(entityHuman, "defaultContainer"); - - packetConstructor = packetClass.getConstructor(Double.TYPE, Double.TYPE, Double.TYPE, Float.TYPE, Float.TYPE, Set.class, Integer.TYPE); - vec3D = vecClass.getConstructor(Double.TYPE, Double.TYPE, Double.TYPE); - - Object[] enumObjects = getNmsClass("PacketPlayOutPosition$EnumPlayerTeleportFlags").getEnumConstants(); - teleportFlags = Set.of(enumObjects[4], enumObjects[3]); - - justTeleportedField = getField(connectionClass, "justTeleported"); - teleportPosField = getField(connectionClass, "teleportPos"); - lastPosXField = getField(connectionClass, "lastPosX"); - lastPosYField = getField(connectionClass, "lastPosY"); - lastPosZField = getField(connectionClass, "lastPosZ"); - teleportAwaitField = getField(connectionClass, "teleportAwait"); - AField = getField(connectionClass, "A"); - eField = getField(connectionClass, "e"); - sucess = true; - } catch (ClassNotFoundException | NoSuchFieldException | NoSuchMethodException | SecurityException e) { - Bukkit.getLogger().warning("Failed to access internal teleportation handle, switching to fallback"); - Bukkit.getLogger().info("This is expected for 1.17 and up."); // TODO: Figure this out - //e.printStackTrace(); - } - intialized = sucess; - } - - private static Field getField(Class clazz, String name) throws NoSuchFieldException { - Field field = clazz.getDeclaredField(name); - field.setAccessible(true); - return field; - } - - public static void teleport(Player player, Location location, float yawChange) { - if(!intialized){ - Movecraft.getInstance().getWorldHandler().addPlayerLocation(player, location.getX() - player.getLocation().getX(), location.getY() - player.getLocation().getY(), location.getZ() - player.getLocation().getZ(), yawChange, 0); - return; - } - double x = location.getX(); - double y = location.getY(); - double z = location.getZ(); - Object handle = getHandle(player); - try { - position.invoke(handle, x, y, z, yaw.get(handle), pitch.get(handle)); - Object connection = connectionField.get(handle); - justTeleportedField.set(connection, true); - teleportPosField.set(connection, vec3D.newInstance(x, y, z)); - lastPosXField.set(connection, x); - lastPosYField.set(connection, y); - lastPosZField.set(connection, z); - int teleportAwait = teleportAwaitField.getInt(connection) + 1; - if (teleportAwait == 2147483647) teleportAwait = 0; - teleportAwaitField.set(connection, teleportAwait); - AField.set(connection, eField.get(connection)); - - Object packet = packetConstructor.newInstance(x, y, z, yawChange, 0, teleportFlags, teleportAwait); - sendPacket(packet, player); - } catch (IllegalAccessException | IllegalArgumentException | InstantiationException | InvocationTargetException e) { - e.printStackTrace(); - } - } - - private static void sendPacket(Object packet, Player p) { - try { - Object handle = getHandle(p); - Object pConnection = connectionField.get(handle); - sendMethod.invoke(pConnection, packet); - } catch (Exception var9) { - var9.printStackTrace(); - } - } - - private static Object getHandle(Entity entity) { - try { - Method entity_getHandle = entity.getClass().getMethod("getHandle"); - return entity_getHandle.invoke(entity); - } catch (Exception var2) { - var2.printStackTrace(); - return null; - } - } - private static Class getNmsClass(String name) throws ClassNotFoundException { - return Class.forName("net.minecraft.server." + getVersion() + "." + name); - } - - private static String getVersion() { - return Bukkit.getServer().getClass().getPackage().getName().substring(23); - } -} diff --git a/modules/Movecraft/src/main/java/net/countercraft/movecraft/util/teleport/AbstractTeleport.java b/modules/Movecraft/src/main/java/net/countercraft/movecraft/util/teleport/AbstractTeleport.java new file mode 100644 index 000000000..90c15055f --- /dev/null +++ b/modules/Movecraft/src/main/java/net/countercraft/movecraft/util/teleport/AbstractTeleport.java @@ -0,0 +1,14 @@ +package net.countercraft.movecraft.util.teleport; + +import org.bukkit.Location; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; + +public abstract class AbstractTeleport { + + public static boolean initialize() { + return false; + } + + public static void teleport(Player player, @NotNull Location location, float yawChange, float pitchChange) { } +} diff --git a/modules/Movecraft/src/main/java/net/countercraft/movecraft/util/teleport/SpigotMappedTeleport.java b/modules/Movecraft/src/main/java/net/countercraft/movecraft/util/teleport/SpigotMappedTeleport.java new file mode 100644 index 000000000..855a62cee --- /dev/null +++ b/modules/Movecraft/src/main/java/net/countercraft/movecraft/util/teleport/SpigotMappedTeleport.java @@ -0,0 +1,119 @@ +package net.countercraft.movecraft.util.teleport; + +import org.bukkit.Location; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Set; + +/** + * Code taken with permission from MicleBrick + * https://www.spigotmc.org/threads/teleport-player-smoothly.317416/ + * Used for 1.14.4 to 1.16.5 + */ +public class SpigotMappedTeleport extends AbstractTeleport { + private static Set teleportFlags; + + private static Constructor packetConstructor; + private static Constructor vec3D; + + private static Method position; + private static Method sendMethod; + + private static Field connectionField; + private static Field justTeleportedField; + private static Field teleportPosField; + private static Field lastPosXField; + private static Field lastPosYField; + private static Field lastPosZField; + private static Field teleportAwaitField; + private static Field AField; + private static Field eField; + private static Field yaw; + private static Field pitch; + + private static @NotNull Class getNmsClass(String name) throws ClassNotFoundException { + return Class.forName("net.minecraft.server." + TeleportUtils.getVersion() + "." + name); + } + + private static void sendPacket(Object packet, Player p) { + try { + Object handle = TeleportUtils.getHandle(p); + Object pConnection = connectionField.get(handle); + sendMethod.invoke(pConnection, packet); + } + catch (Exception e) { + e.printStackTrace(); + } + } + + public static boolean initialize() { + boolean success = false; + try { + Class packet = getNmsClass("Packet"); + Class entity = getNmsClass("Entity"); + Class entityPlayer = getNmsClass("EntityPlayer"); + Class connectionClass = getNmsClass("PlayerConnection"); + Class packetClass = getNmsClass("PacketPlayOutPosition"); + Class vecClass = getNmsClass("Vec3D"); + sendMethod = connectionClass.getMethod("sendPacket", packet); + + position = entity.getDeclaredMethod("setLocation", Double.TYPE, Double.TYPE, Double.TYPE, Float.TYPE, Float.TYPE); + + yaw = TeleportUtils.getField(entity, "yaw"); + pitch = TeleportUtils.getField(entity, "pitch"); + connectionField = TeleportUtils.getField(entityPlayer, "playerConnection"); + + packetConstructor = packetClass.getConstructor(Double.TYPE, Double.TYPE, Double.TYPE, Float.TYPE, Float.TYPE, Set.class, Integer.TYPE); + vec3D = vecClass.getConstructor(Double.TYPE, Double.TYPE, Double.TYPE); + + Object[] enumObjects = getNmsClass("PacketPlayOutPosition$EnumPlayerTeleportFlags").getEnumConstants(); + teleportFlags = Set.of(enumObjects[4], enumObjects[3]); + + justTeleportedField = TeleportUtils.getField(connectionClass, "justTeleported"); + teleportPosField = TeleportUtils.getField(connectionClass, "teleportPos"); + lastPosXField = TeleportUtils.getField(connectionClass, "lastPosX"); + lastPosYField = TeleportUtils.getField(connectionClass, "lastPosY"); + lastPosZField = TeleportUtils.getField(connectionClass, "lastPosZ"); + teleportAwaitField = TeleportUtils.getField(connectionClass, "teleportAwait"); + AField = TeleportUtils.getField(connectionClass, "A"); + eField = TeleportUtils.getField(connectionClass, "e"); + success = true; + } + catch (ClassNotFoundException | NoSuchFieldException | NoSuchMethodException | SecurityException e) { + e.printStackTrace(); + } + return success; + } + + public static void teleport(Player player, @NotNull Location location, float yawChange, float pitchChange) { + double x = location.getX(); + double y = location.getY(); + double z = location.getZ(); + Object handle = TeleportUtils.getHandle(player); + try { + position.invoke(handle, x, y, z, yaw.get(handle), pitch.get(handle)); + Object connection = connectionField.get(handle); + justTeleportedField.set(connection, true); + teleportPosField.set(connection, vec3D.newInstance(x, y, z)); + lastPosXField.set(connection, x); + lastPosYField.set(connection, y); + lastPosZField.set(connection, z); + int teleportAwait = teleportAwaitField.getInt(connection) + 1; + if (teleportAwait == Integer.MAX_VALUE) + teleportAwait = 0; + teleportAwaitField.set(connection, teleportAwait); + AField.set(connection, eField.get(connection)); + + Object packet = packetConstructor.newInstance(x, y, z, yawChange, pitchChange, teleportFlags, teleportAwait); + sendPacket(packet, player); + } + catch (IllegalAccessException | IllegalArgumentException | InstantiationException | InvocationTargetException e) { + e.printStackTrace(); + } + } +} diff --git a/modules/Movecraft/src/main/java/net/countercraft/movecraft/util/teleport/TeleportUtils.java b/modules/Movecraft/src/main/java/net/countercraft/movecraft/util/teleport/TeleportUtils.java new file mode 100644 index 000000000..877715084 --- /dev/null +++ b/modules/Movecraft/src/main/java/net/countercraft/movecraft/util/teleport/TeleportUtils.java @@ -0,0 +1,101 @@ +package net.countercraft.movecraft.util.teleport; + +import net.countercraft.movecraft.Movecraft; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; + +/** + * Code taken with permission from MicleBrick + * https://www.spigotmc.org/threads/teleport-player-smoothly.317416/ + */ +public class TeleportUtils { + protected static @Nullable Object getHandle(Entity entity) { + try { + Method entity_getHandle = entity.getClass().getMethod("getHandle"); + return entity_getHandle.invoke(entity); + } + catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + protected static @NotNull Field getField(@NotNull Class clazz, String name) throws NoSuchFieldException { + Field field = clazz.getDeclaredField(name); + field.setAccessible(true); + return field; + } + + protected static @NotNull String getVersion() { + return Bukkit.getServer().getClass().getPackage().getName().substring(23); + } + + + private enum Mode { + UNINITIALIZED, + FALLBACK, + SPIGOT_MAPPED, + V1_17, + V1_18, + V1_19 + } + + private static Mode mode = Mode.UNINITIALIZED; + + private static void initialize() { + int version = Integer.parseInt(getVersion().split("_")[1]); + if (version < 17 && SpigotMappedTeleport.initialize()) { + mode = Mode.SPIGOT_MAPPED; + } + else if (version <= 17 && V1_17Teleport.initialize()) { + mode = Mode.V1_17; + } + else if (version <= 18 && V1_18Teleport.initialize()) { + mode = Mode.V1_18; + } + else if (version <= 19 && V1_19Teleport.initialize()) { + mode = Mode.V1_19; + } + else { + Bukkit.getLogger().warning("Failed to access internal teleportation handle, switching to fallback"); + mode = Mode.FALLBACK; + } + } + + public static void teleport(Player player, @NotNull Location location, float yawChange, float pitchChange) { + if (mode == Mode.UNINITIALIZED) + initialize(); + + switch (mode) { + case SPIGOT_MAPPED: + SpigotMappedTeleport.teleport(player, location, yawChange, pitchChange); + break; + case V1_17: + V1_17Teleport.teleport(player, location, yawChange, pitchChange); + break; + case V1_18: + V1_18Teleport.teleport(player, location, yawChange, pitchChange); + break; + case V1_19: + V1_19Teleport.teleport(player, location, yawChange, pitchChange); + break; + case FALLBACK: + Movecraft.getInstance().getWorldHandler().addPlayerLocation(player, + location.getX() - player.getLocation().getX(), + location.getY() - player.getLocation().getY(), + location.getZ() - player.getLocation().getZ(), + yawChange, pitchChange + ); + break; + default: + throw new IllegalStateException("Unknown mode: " + mode); + } + } +} diff --git a/modules/Movecraft/src/main/java/net/countercraft/movecraft/util/teleport/V1_17Teleport.java b/modules/Movecraft/src/main/java/net/countercraft/movecraft/util/teleport/V1_17Teleport.java new file mode 100644 index 000000000..0900c1c1d --- /dev/null +++ b/modules/Movecraft/src/main/java/net/countercraft/movecraft/util/teleport/V1_17Teleport.java @@ -0,0 +1,118 @@ +package net.countercraft.movecraft.util.teleport; + +import org.bukkit.Location; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +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.17.1 + */ +public class V1_17Teleport extends AbstractTeleport { + private static Set teleportFlags; + + private static Method positionMethod; + private static Method sendMethod; + + private static Constructor vec3Constructor; + private static Constructor packetConstructor; + + private static Field connectionField; + //private static Field justTeleportedField; + private static Field teleportPosField; + //private static Field lastPosXField; + //private static Field lastPosYField; + //private static Field lastPosZField; + private static Field teleportAwaitField; + private static Field awaitingTeleportTimeField; + private static Field tickCountField; + private static Field yawField; + private static Field pitchField; + + private static @NotNull Class getNmClass(String name) throws ClassNotFoundException { + return Class.forName("net.minecraft." + name); + } + + private static void sendPacket(Object packet, Player p) { + try { + Object handle = TeleportUtils.getHandle(p); + Object pConnection = connectionField.get(handle); + sendMethod.invoke(pConnection, packet); + } + catch (Exception e) { + e.printStackTrace(); + } + } + + public static boolean initialize() { + boolean success = false; + try { + Class packetClass = getNmClass("network.protocol.Packet"); + Class positionPacketClass = getNmClass("network.protocol.game.PacketPlayOutPosition"); // ClientboundPlayerPositionPacket + Class entityClass = getNmClass("world.entity.Entity"); + Class playerClass = getNmClass("server.level.EntityPlayer"); // ServerPlayer + Class connectionClass = getNmClass("server.network.PlayerConnection"); // ServerGamePacketListenerImpl + Class vectorClass = getNmClass("world.phys.Vec3D"); // Vec3 + + Object[] flags = getNmClass("network.protocol.game.PacketPlayOutPosition$EnumPlayerTeleportFlags").getEnumConstants(); // $RelativeArgument + teleportFlags = Set.of(flags[4], flags[3]); // X_ROT, Y_ROT + + positionMethod = entityClass.getDeclaredMethod("setLocation", Double.TYPE, Double.TYPE, Double.TYPE, Float.TYPE, Float.TYPE); // absMoveTo + sendMethod = connectionClass.getMethod("sendPacket", packetClass); // send + + vec3Constructor = vectorClass.getConstructor(Double.TYPE, Double.TYPE, Double.TYPE); + packetConstructor = positionPacketClass.getConstructor(Double.TYPE, Double.TYPE, Double.TYPE, Float.TYPE, Float.TYPE, Set.class, Integer.TYPE, Boolean.TYPE); + + connectionField = TeleportUtils.getField(playerClass, "b"); // connection + //justTeleportedField = TeleportUtils.getField(connectionClass, "justTeleported"); + teleportPosField = TeleportUtils.getField(connectionClass, "y"); // awaitingPositionFromClient + //lastPosXField = TeleportUtils.getField(connectionClass, "lastPosX"); + //lastPosYField = TeleportUtils.getField(connectionClass, "lastPosY"); + //lastPosZField = TeleportUtils.getField(connectionClass, "lastPosZ"); + teleportAwaitField = TeleportUtils.getField(connectionClass, "z"); // awaitingTeleport + awaitingTeleportTimeField = TeleportUtils.getField(connectionClass, "A"); // awaitingTeleportTime + tickCountField = TeleportUtils.getField(connectionClass, "f"); // tickCount + yawField = TeleportUtils.getField(entityClass, "az"); // xRot + pitchField = TeleportUtils.getField(entityClass, "ay"); // yRot + success = true; + } + catch (ClassNotFoundException | NoSuchFieldException | NoSuchMethodException e) { + e.printStackTrace(); + } + return success; + } + + public static void teleport(Player player, @NotNull Location location, float yawChange, float pitchChange) { + double x = location.getX(); + double y = location.getY(); + double z = location.getZ(); + Object handle = TeleportUtils.getHandle(player); + try { + positionMethod.invoke(handle, x, y, z, yawField.get(handle), pitchField.get(handle)); + Object connection = connectionField.get(handle); + //justTeleportedField.set(connection, true); + teleportPosField.set(connection, vec3Constructor.newInstance(x, y, z)); + //lastPosXField.set(connection, x); + //lastPosYField.set(connection, y); + //lastPosZField.set(connection, z); + int teleportAwait = teleportAwaitField.getInt(connection) + 1; + if (teleportAwait == Integer.MAX_VALUE) + teleportAwait = 0; + teleportAwaitField.setInt(connection, teleportAwait); + awaitingTeleportTimeField.set(connection, tickCountField.get(connection)); + + Object packet = packetConstructor.newInstance(x, y, z, yawChange, pitchChange, teleportFlags, teleportAwait, false); + sendPacket(packet, player); + } + catch (IllegalAccessException | InvocationTargetException | InstantiationException e) { + e.printStackTrace(); + } + } +} diff --git a/modules/Movecraft/src/main/java/net/countercraft/movecraft/util/teleport/V1_18Teleport.java b/modules/Movecraft/src/main/java/net/countercraft/movecraft/util/teleport/V1_18Teleport.java new file mode 100644 index 000000000..5255d6eea --- /dev/null +++ b/modules/Movecraft/src/main/java/net/countercraft/movecraft/util/teleport/V1_18Teleport.java @@ -0,0 +1,118 @@ +package net.countercraft.movecraft.util.teleport; + +import org.bukkit.Location; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +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.18.2 + */ +public class V1_18Teleport extends AbstractTeleport { + private static Set teleportFlags; + + private static Method positionMethod; + private static Method sendMethod; + + private static Constructor vec3Constructor; + private static Constructor packetConstructor; + + private static Field connectionField; + //private static Field justTeleportedField; + private static Field teleportPosField; + //private static Field lastPosXField; + //private static Field lastPosYField; + //private static Field lastPosZField; + private static Field teleportAwaitField; + private static Field awaitingTeleportTimeField; + private static Field tickCountField; + private static Field yawField; + private static Field pitchField; + + private static @NotNull Class getNmClass(String name) throws ClassNotFoundException { + return Class.forName("net.minecraft." + name); + } + + private static void sendPacket(Object packet, Player p) { + try { + Object handle = TeleportUtils.getHandle(p); + Object pConnection = connectionField.get(handle); + sendMethod.invoke(pConnection, packet); + } + catch (Exception e) { + e.printStackTrace(); + } + } + + public static boolean initialize() { + boolean success = false; + try { + Class packetClass = getNmClass("network.protocol.Packet"); + Class positionPacketClass = getNmClass("network.protocol.game.PacketPlayOutPosition"); // ClientboundPlayerPositionPacket + Class entityClass = getNmClass("world.entity.Entity"); + Class playerClass = getNmClass("server.level.EntityPlayer"); // ServerPlayer + Class connectionClass = getNmClass("server.network.PlayerConnection"); // ServerGamePacketListenerImpl + Class vectorClass = getNmClass("world.phys.Vec3D"); // Vec3 + + Object[] flags = getNmClass("network.protocol.game.PacketPlayOutPosition$EnumPlayerTeleportFlags").getEnumConstants(); // $RelativeArgument + teleportFlags = Set.of(flags[4], flags[3]); // X_ROT, Y_ROT + + positionMethod = entityClass.getDeclaredMethod("a", Double.TYPE, Double.TYPE, Double.TYPE, Float.TYPE, Float.TYPE); // absMoveTo + sendMethod = connectionClass.getMethod("a", packetClass); // send + + vec3Constructor = vectorClass.getConstructor(Double.TYPE, Double.TYPE, Double.TYPE); + packetConstructor = positionPacketClass.getConstructor(Double.TYPE, Double.TYPE, Double.TYPE, Float.TYPE, Float.TYPE, Set.class, Integer.TYPE, Boolean.TYPE); + + connectionField = TeleportUtils.getField(playerClass, "b"); // connection + //justTeleportedField = TeleportUtils.getField(connectionClass, "justTeleported"); // justTeleported + teleportPosField = TeleportUtils.getField(connectionClass, "y"); // awaitingPositionFromClient + //lastPosXField = TeleportUtils.getField(connectionClass, "lastPosX"); // lastPosX + //lastPosYField = TeleportUtils.getField(connectionClass, "lastPosY"); // lastPosY + //lastPosZField = TeleportUtils.getField(connectionClass, "lastPosZ"); // lastPosZ + teleportAwaitField = TeleportUtils.getField(connectionClass, "z"); // awaitingTeleport + awaitingTeleportTimeField = TeleportUtils.getField(connectionClass, "A"); // awaitingTeleportTime + tickCountField = TeleportUtils.getField(connectionClass, "f"); // tickCount + yawField = TeleportUtils.getField(entityClass, "aB"); // xRot + pitchField = TeleportUtils.getField(entityClass, "aA"); // yRot + success = true; + } + catch (ClassNotFoundException | NoSuchFieldException | NoSuchMethodException e) { + e.printStackTrace(); + } + return success; + } + + public static void teleport(Player player, @NotNull Location location, float yawChange, float pitchChange) { + double x = location.getX(); + double y = location.getY(); + double z = location.getZ(); + Object handle = TeleportUtils.getHandle(player); + try { + positionMethod.invoke(handle, x, y, z, yawField.get(handle), pitchField.get(handle)); + Object connection = connectionField.get(handle); + //justTeleportedField.set(connection, true); + teleportPosField.set(connection, vec3Constructor.newInstance(x, y, z)); + //lastPosXField.set(connection, x); + //lastPosYField.set(connection, y); + //lastPosZField.set(connection, z); + int teleportAwait = teleportAwaitField.getInt(connection) + 1; + if (teleportAwait == Integer.MAX_VALUE) + teleportAwait = 0; + teleportAwaitField.setInt(connection, teleportAwait); + awaitingTeleportTimeField.set(connection, tickCountField.get(connection)); + + Object packet = packetConstructor.newInstance(x, y, z, yawChange, pitchChange, teleportFlags, teleportAwait, false); + sendPacket(packet, player); + } + catch (IllegalAccessException | InvocationTargetException | InstantiationException e) { + e.printStackTrace(); + } + } +} diff --git a/modules/Movecraft/src/main/java/net/countercraft/movecraft/util/teleport/V1_19Teleport.java b/modules/Movecraft/src/main/java/net/countercraft/movecraft/util/teleport/V1_19Teleport.java new file mode 100644 index 000000000..67e3eabd5 --- /dev/null +++ b/modules/Movecraft/src/main/java/net/countercraft/movecraft/util/teleport/V1_19Teleport.java @@ -0,0 +1,118 @@ +package net.countercraft.movecraft.util.teleport; + +import org.bukkit.Location; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +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.19.1 + */ +public class V1_19Teleport extends AbstractTeleport { + private static Set teleportFlags; + + private static Method positionMethod; + private static Method sendMethod; + + private static Constructor vec3Constructor; + private static Constructor packetConstructor; + + private static Field connectionField; + //private static Field justTeleportedField; + private static Field teleportPosField; + //private static Field lastPosXField; + //private static Field lastPosYField; + //private static Field lastPosZField; + private static Field teleportAwaitField; + private static Field awaitingTeleportTimeField; + private static Field tickCountField; + private static Field yawField; + private static Field pitchField; + + private static @NotNull Class getNmClass(String name) throws ClassNotFoundException { + return Class.forName("net.minecraft." + name); + } + + private static void sendPacket(Object packet, Player p) { + try { + Object handle = TeleportUtils.getHandle(p); + Object pConnection = connectionField.get(handle); + sendMethod.invoke(pConnection, packet); + } + catch (Exception e) { + e.printStackTrace(); + } + } + + public static boolean initialize() { + boolean success = false; + try { + Class packetClass = getNmClass("network.protocol.Packet"); + Class positionPacketClass = getNmClass("network.protocol.game.PacketPlayOutPosition"); // ClientboundPlayerPositionPacket + Class entityClass = getNmClass("world.entity.Entity"); + Class playerClass = getNmClass("server.level.EntityPlayer"); // ServerPlayer + Class connectionClass = getNmClass("server.network.PlayerConnection"); // ServerGamePacketListenerImpl + Class vectorClass = getNmClass("world.phys.Vec3D"); // Vec3 + + Object[] flags = getNmClass("network.protocol.game.PacketPlayOutPosition$EnumPlayerTeleportFlags").getEnumConstants(); // $RelativeArgument + teleportFlags = Set.of(flags[4], flags[3]); // X_ROT, Y_ROT + + positionMethod = entityClass.getDeclaredMethod("a", Double.TYPE, Double.TYPE, Double.TYPE, Float.TYPE, Float.TYPE); // absMoveTo + sendMethod = connectionClass.getMethod("a", packetClass); // send + + vec3Constructor = vectorClass.getConstructor(Double.TYPE, Double.TYPE, Double.TYPE); + packetConstructor = positionPacketClass.getConstructor(Double.TYPE, Double.TYPE, Double.TYPE, Float.TYPE, Float.TYPE, Set.class, Integer.TYPE, Boolean.TYPE); + + connectionField = TeleportUtils.getField(playerClass, "b"); // connection + //justTeleportedField = TeleportUtils.getField(connectionClass, "justTeleported"); // justTeleported + teleportPosField = TeleportUtils.getField(connectionClass, "C"); // awaitingPositionFromClient + //lastPosXField = TeleportUtils.getField(connectionClass, "lastPosX"); // lastPosX + //lastPosYField = TeleportUtils.getField(connectionClass, "lastPosY"); // lastPosY + //lastPosZField = TeleportUtils.getField(connectionClass, "lastPosZ"); // lastPosZ + teleportAwaitField = TeleportUtils.getField(connectionClass, "D"); // awaitingTeleport + awaitingTeleportTimeField = TeleportUtils.getField(connectionClass, "E"); // awaitingTeleportTime + tickCountField = TeleportUtils.getField(connectionClass, "i"); // tickCount + yawField = TeleportUtils.getField(entityClass, "aB"); // xRot + pitchField = TeleportUtils.getField(entityClass, "aA"); // yRot + success = true; + } + catch (ClassNotFoundException | NoSuchFieldException | NoSuchMethodException e) { + e.printStackTrace(); + } + return success; + } + + public static void teleport(Player player, @NotNull Location location, float yawChange, float pitchChange) { + double x = location.getX(); + double y = location.getY(); + double z = location.getZ(); + Object handle = TeleportUtils.getHandle(player); + try { + positionMethod.invoke(handle, x, y, z, yawField.get(handle), pitchField.get(handle)); + Object connection = connectionField.get(handle); + //justTeleportedField.set(connection, true); + teleportPosField.set(connection, vec3Constructor.newInstance(x, y, z)); + //lastPosXField.set(connection, x); + //lastPosYField.set(connection, y); + //lastPosZField.set(connection, z); + int teleportAwait = teleportAwaitField.getInt(connection) + 1; + if (teleportAwait == Integer.MAX_VALUE) + teleportAwait = 0; + teleportAwaitField.setInt(connection, teleportAwait); + awaitingTeleportTimeField.set(connection, tickCountField.get(connection)); + + Object packet = packetConstructor.newInstance(x, y, z, yawChange, pitchChange, teleportFlags, teleportAwait, false); + sendPacket(packet, player); + } + catch (IllegalAccessException | InvocationTargetException | InstantiationException e) { + e.printStackTrace(); + } + } +} diff --git a/modules/v1_17_R1/src/main/java/net/countercraft/movecraft/compat/v1_17_R1/IWorldHandler.java b/modules/v1_17_R1/src/main/java/net/countercraft/movecraft/compat/v1_17_R1/IWorldHandler.java index 48d17be8b..7515ccda0 100644 --- a/modules/v1_17_R1/src/main/java/net/countercraft/movecraft/compat/v1_17_R1/IWorldHandler.java +++ b/modules/v1_17_R1/src/main/java/net/countercraft/movecraft/compat/v1_17_R1/IWorldHandler.java @@ -45,12 +45,6 @@ public class IWorldHandler extends WorldHandler { public IWorldHandler() {} -// @Override -// public void addPlayerLocation(Player player, double x, double y, double z, float yaw, float pitch){ -// ServerPlayer ePlayer = ((CraftPlayer) player).getHandle(); -// ePlayer.connection.teleport(x, y, z, yaw, pitch, EnumSet.allOf(ClientboundPlayerPositionPacket.RelativeArgument.class)); -// } - @Override public void rotateCraft(@NotNull Craft craft, @NotNull MovecraftLocation originPoint, @NotNull MovecraftRotation rotation) { //*******************************************