From 4ed0d4998469427c2096ac3341c9e736b68615dd Mon Sep 17 00:00:00 2001 From: MrMicky Date: Wed, 19 Feb 2020 14:58:18 +0100 Subject: [PATCH] Add Minecraft 1.15 support --- .travis.yml | 1 + nms/v1_15_R1/pom.xml | 24 ++++ .../npclib/nms/v1_15_R1/NPC_v1_15_R1.java | 110 ++++++++++++++++++ ...acketPlayOutEntityHeadRotationWrapper.java | 26 +++++ .../PacketPlayOutEntityMetadataWrapper.java | 39 +++++++ .../PacketPlayOutNamedEntitySpawnWrapper.java | 38 ++++++ .../PacketPlayOutPlayerInfoWrapper.java | 43 +++++++ .../PacketPlayOutScoreboardTeamWrapper.java | 53 +++++++++ plugin/pom.xml | 6 + 9 files changed, 340 insertions(+) create mode 100644 nms/v1_15_R1/pom.xml create mode 100644 nms/v1_15_R1/src/main/java/net/jitse/npclib/nms/v1_15_R1/NPC_v1_15_R1.java create mode 100644 nms/v1_15_R1/src/main/java/net/jitse/npclib/nms/v1_15_R1/packets/PacketPlayOutEntityHeadRotationWrapper.java create mode 100644 nms/v1_15_R1/src/main/java/net/jitse/npclib/nms/v1_15_R1/packets/PacketPlayOutEntityMetadataWrapper.java create mode 100644 nms/v1_15_R1/src/main/java/net/jitse/npclib/nms/v1_15_R1/packets/PacketPlayOutNamedEntitySpawnWrapper.java create mode 100644 nms/v1_15_R1/src/main/java/net/jitse/npclib/nms/v1_15_R1/packets/PacketPlayOutPlayerInfoWrapper.java create mode 100644 nms/v1_15_R1/src/main/java/net/jitse/npclib/nms/v1_15_R1/packets/PacketPlayOutScoreboardTeamWrapper.java diff --git a/.travis.yml b/.travis.yml index 14fa452..7dec16a 100755 --- a/.travis.yml +++ b/.travis.yml @@ -17,5 +17,6 @@ install: - ls $HOME/.m2/repository/org/spigotmc/spigot/1.13-R0.1-SNAPSHOT >> /dev/null 2>&1 || java -jar BuildTools.jar --rev 1.13 >> /dev/null 2>&1 - ls $HOME/.m2/repository/org/spigotmc/spigot/1.13.2-R0.1-SNAPSHOT >> /dev/null 2>&1 || java -jar BuildTools.jar --rev 1.13.2 >> /dev/null 2>&1 - ls $HOME/.m2/repository/org/spigotmc/spigot/1.14.4-R0.1-SNAPSHOT >> /dev/null 2>&1 || java -jar BuildTools.jar --rev 1.14.4 >> /dev/null 2>&1 + - ls $HOME/.m2/repository/org/spigotmc/spigot/1.15.2-R0.1-SNAPSHOT >> /dev/null 2>&1 || java -jar BuildTools.jar --rev 1.15.2 >> /dev/null 2>&1 script: - mvn clean install \ No newline at end of file diff --git a/nms/v1_15_R1/pom.xml b/nms/v1_15_R1/pom.xml new file mode 100644 index 0000000..adf6b0f --- /dev/null +++ b/nms/v1_15_R1/pom.xml @@ -0,0 +1,24 @@ + + + 4.0.0 + jar + + + net.jitse + npclib-nms + 2.3.1-SNAPSHOT + + + npclib-nms-v1_15_R1 + + + + org.spigotmc + spigot + 1.15.2-R0.1-SNAPSHOT + provided + + + diff --git a/nms/v1_15_R1/src/main/java/net/jitse/npclib/nms/v1_15_R1/NPC_v1_15_R1.java b/nms/v1_15_R1/src/main/java/net/jitse/npclib/nms/v1_15_R1/NPC_v1_15_R1.java new file mode 100644 index 0000000..c8f1ad6 --- /dev/null +++ b/nms/v1_15_R1/src/main/java/net/jitse/npclib/nms/v1_15_R1/NPC_v1_15_R1.java @@ -0,0 +1,110 @@ +package net.jitse.npclib.nms.v1_15_R1; + +import net.jitse.npclib.NPCLib; +import net.jitse.npclib.api.state.NPCSlot; +import net.jitse.npclib.hologram.Hologram; +import net.jitse.npclib.internal.MinecraftVersion; +import net.jitse.npclib.internal.NPCBase; +import net.jitse.npclib.nms.v1_15_R1.packets.*; +import net.minecraft.server.v1_15_R1.*; +import org.bukkit.Bukkit; +import org.bukkit.craftbukkit.v1_15_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_15_R1.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; + +import java.util.List; + +/** + * @author Jitse Boonstra + */ +public class NPC_v1_15_R1 extends NPCBase { + + private PacketPlayOutNamedEntitySpawn packetPlayOutNamedEntitySpawn; + private PacketPlayOutScoreboardTeam packetPlayOutScoreboardTeamRegister; + private PacketPlayOutPlayerInfo packetPlayOutPlayerInfoAdd, packetPlayOutPlayerInfoRemove; + private PacketPlayOutEntityHeadRotation packetPlayOutEntityHeadRotation; + private PacketPlayOutEntityDestroy packetPlayOutEntityDestroy; + + public NPC_v1_15_R1(NPCLib instance, List lines) { + super(instance, lines); + } + + @Override + public void createPackets() { + this.hologram = new Hologram(MinecraftVersion.V1_15_R1, location.clone().add(0, 0.5, 0), text); + + PacketPlayOutPlayerInfoWrapper packetPlayOutPlayerInfoWrapper = new PacketPlayOutPlayerInfoWrapper(); + + // Packets for spawning the NPC: + this.packetPlayOutScoreboardTeamRegister = new PacketPlayOutScoreboardTeamWrapper() + .createRegisterTeam(name); // First packet to send. + + this.packetPlayOutPlayerInfoAdd = packetPlayOutPlayerInfoWrapper + .create(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.ADD_PLAYER, gameProfile, name); // Second packet to send. + + this.packetPlayOutNamedEntitySpawn = new PacketPlayOutNamedEntitySpawnWrapper() + .create(uuid, location, entityId); // Third packet to send. + + this.packetPlayOutEntityHeadRotation = new PacketPlayOutEntityHeadRotationWrapper() + .create(location, entityId); // Fourth packet to send. + + this.packetPlayOutPlayerInfoRemove = packetPlayOutPlayerInfoWrapper + .create(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.REMOVE_PLAYER, gameProfile, name); // Fifth packet to send (delayed). + + // Packet for destroying the NPC: + this.packetPlayOutEntityDestroy = new PacketPlayOutEntityDestroy(entityId); // First packet to send. + } + + @Override + public void onLogout(Player player) { + super.onLogout(player); + hasTeamRegistered.remove(player.getUniqueId()); + } + + @Override + public void sendShowPackets(Player player) { + PlayerConnection playerConnection = ((CraftPlayer) player).getHandle().playerConnection; + + if (hasTeamRegistered.add(player.getUniqueId())) + playerConnection.sendPacket(packetPlayOutScoreboardTeamRegister); + playerConnection.sendPacket(packetPlayOutPlayerInfoAdd); + playerConnection.sendPacket(packetPlayOutNamedEntitySpawn); + playerConnection.sendPacket(packetPlayOutEntityHeadRotation); + sendMetadataPacket(player); + + hologram.show(player); + + Bukkit.getScheduler().runTaskLater(instance.getPlugin(), () -> + playerConnection.sendPacket(packetPlayOutPlayerInfoRemove), 50); + } + + @Override + public void sendHidePackets(Player player) { + PlayerConnection playerConnection = ((CraftPlayer) player).getHandle().playerConnection; + + playerConnection.sendPacket(packetPlayOutEntityDestroy); + playerConnection.sendPacket(packetPlayOutPlayerInfoRemove); + + hologram.hide(player); + } + + @Override + public void sendMetadataPacket(Player player) { + PlayerConnection playerConnection = ((CraftPlayer) player).getHandle().playerConnection; + PacketPlayOutEntityMetadata packet = new PacketPlayOutEntityMetadataWrapper().create(activeStates, entityId); + + playerConnection.sendPacket(packet); + } + + @Override + public void sendEquipmentPacket(Player player, NPCSlot slot, boolean auto) { + PlayerConnection playerConnection = ((CraftPlayer) player).getHandle().playerConnection; + + EnumItemSlot nmsSlot = slot.getNmsEnum(EnumItemSlot.class); + ItemStack item = getItem(slot); + + PacketPlayOutEntityEquipment packet = new PacketPlayOutEntityEquipment(entityId, nmsSlot, CraftItemStack.asNMSCopy(item)); + playerConnection.sendPacket(packet); + } +} diff --git a/nms/v1_15_R1/src/main/java/net/jitse/npclib/nms/v1_15_R1/packets/PacketPlayOutEntityHeadRotationWrapper.java b/nms/v1_15_R1/src/main/java/net/jitse/npclib/nms/v1_15_R1/packets/PacketPlayOutEntityHeadRotationWrapper.java new file mode 100644 index 0000000..84c48b7 --- /dev/null +++ b/nms/v1_15_R1/src/main/java/net/jitse/npclib/nms/v1_15_R1/packets/PacketPlayOutEntityHeadRotationWrapper.java @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2018 Jitse Boonstra + */ + +package net.jitse.npclib.nms.v1_15_R1.packets; + +import com.comphenix.tinyprotocol.Reflection; +import net.minecraft.server.v1_15_R1.PacketPlayOutEntityHeadRotation; +import org.bukkit.Location; + +/** + * @author Jitse Boonstra + */ +public class PacketPlayOutEntityHeadRotationWrapper { + + public PacketPlayOutEntityHeadRotation create(Location location, int entityId) { + PacketPlayOutEntityHeadRotation packetPlayOutEntityHeadRotation = new PacketPlayOutEntityHeadRotation(); + + Reflection.getField(packetPlayOutEntityHeadRotation.getClass(), "a", int.class). + set(packetPlayOutEntityHeadRotation, entityId); + Reflection.getField(packetPlayOutEntityHeadRotation.getClass(), "b", byte.class) + .set(packetPlayOutEntityHeadRotation, (byte) ((int) location.getYaw() * 256.0F / 360.0F)); + + return packetPlayOutEntityHeadRotation; + } +} diff --git a/nms/v1_15_R1/src/main/java/net/jitse/npclib/nms/v1_15_R1/packets/PacketPlayOutEntityMetadataWrapper.java b/nms/v1_15_R1/src/main/java/net/jitse/npclib/nms/v1_15_R1/packets/PacketPlayOutEntityMetadataWrapper.java new file mode 100644 index 0000000..8b28392 --- /dev/null +++ b/nms/v1_15_R1/src/main/java/net/jitse/npclib/nms/v1_15_R1/packets/PacketPlayOutEntityMetadataWrapper.java @@ -0,0 +1,39 @@ +package net.jitse.npclib.nms.v1_15_R1.packets; + +import net.jitse.npclib.api.state.NPCState; +import net.minecraft.server.v1_15_R1.DataWatcher; +import net.minecraft.server.v1_15_R1.DataWatcherObject; +import net.minecraft.server.v1_15_R1.DataWatcherRegistry; +import net.minecraft.server.v1_15_R1.PacketPlayOutEntityMetadata; + +import java.util.Collection; + +public class PacketPlayOutEntityMetadataWrapper { + + public PacketPlayOutEntityMetadata create(Collection activateStates, int entityId) { + DataWatcher dataWatcher = new DataWatcher(null); + dataWatcher.register(new DataWatcherObject<>(16, DataWatcherRegistry.a), (byte) 127); + + byte masked = NPCState.getMasked(activateStates); + // TODO: Find out why NPCState#CROUCHED doesn't work. + dataWatcher.register(new DataWatcherObject<>(0, DataWatcherRegistry.a), masked); + +// for (Player online : Bukkit.getOnlinePlayers()) { +// DataWatcher watcher = ((CraftPlayer) online).getHandle().getDataWatcher(); +// try { +// Field entriesField = watcher.getClass().getDeclaredField("entries"); +// entriesField.setAccessible(true); +// +// Int2ObjectOpenHashMap> entries = (Int2ObjectOpenHashMap>) entriesField.get(watcher); +// entries.forEach((integer, item) -> { +// if (item.b() instanceof Boolean || item.b() instanceof Byte) +// online.sendMessage(integer + ": " + item.b() + " type = " + item.b().getClass().toString()); +// }); +// } catch (NoSuchFieldException | IllegalAccessException e) { +// e.printStackTrace(); +// } +// } + + return new PacketPlayOutEntityMetadata(entityId, dataWatcher, true); + } +} diff --git a/nms/v1_15_R1/src/main/java/net/jitse/npclib/nms/v1_15_R1/packets/PacketPlayOutNamedEntitySpawnWrapper.java b/nms/v1_15_R1/src/main/java/net/jitse/npclib/nms/v1_15_R1/packets/PacketPlayOutNamedEntitySpawnWrapper.java new file mode 100644 index 0000000..64aa8f0 --- /dev/null +++ b/nms/v1_15_R1/src/main/java/net/jitse/npclib/nms/v1_15_R1/packets/PacketPlayOutNamedEntitySpawnWrapper.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2018 Jitse Boonstra + */ + +package net.jitse.npclib.nms.v1_15_R1.packets; + +import com.comphenix.tinyprotocol.Reflection; +import net.minecraft.server.v1_15_R1.PacketPlayOutNamedEntitySpawn; +import org.bukkit.Location; + +import java.util.UUID; + +/** + * @author Jitse Boonstra + */ +public class PacketPlayOutNamedEntitySpawnWrapper { + + public PacketPlayOutNamedEntitySpawn create(UUID uuid, Location location, int entityId) { + PacketPlayOutNamedEntitySpawn packetPlayOutNamedEntitySpawn = new PacketPlayOutNamedEntitySpawn(); + + Reflection.getField(packetPlayOutNamedEntitySpawn.getClass(), "a", int.class) + .set(packetPlayOutNamedEntitySpawn, entityId); + Reflection.getField(packetPlayOutNamedEntitySpawn.getClass(), "b", UUID.class) + .set(packetPlayOutNamedEntitySpawn, uuid); + Reflection.getField(packetPlayOutNamedEntitySpawn.getClass(), "c", double.class) + .set(packetPlayOutNamedEntitySpawn, location.getX()); + Reflection.getField(packetPlayOutNamedEntitySpawn.getClass(), "d", double.class) + .set(packetPlayOutNamedEntitySpawn, location.getY()); + Reflection.getField(packetPlayOutNamedEntitySpawn.getClass(), "e", double.class) + .set(packetPlayOutNamedEntitySpawn, location.getZ()); + Reflection.getField(packetPlayOutNamedEntitySpawn.getClass(), "f", byte.class) + .set(packetPlayOutNamedEntitySpawn, (byte) ((int) (location.getYaw() * 256.0F / 360.0F))); + Reflection.getField(packetPlayOutNamedEntitySpawn.getClass(), "g", byte.class) + .set(packetPlayOutNamedEntitySpawn, (byte) ((int) (location.getPitch() * 256.0F / 360.0F))); + + return packetPlayOutNamedEntitySpawn; + } +} diff --git a/nms/v1_15_R1/src/main/java/net/jitse/npclib/nms/v1_15_R1/packets/PacketPlayOutPlayerInfoWrapper.java b/nms/v1_15_R1/src/main/java/net/jitse/npclib/nms/v1_15_R1/packets/PacketPlayOutPlayerInfoWrapper.java new file mode 100644 index 0000000..8eb1a1f --- /dev/null +++ b/nms/v1_15_R1/src/main/java/net/jitse/npclib/nms/v1_15_R1/packets/PacketPlayOutPlayerInfoWrapper.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2018 Jitse Boonstra + */ + +package net.jitse.npclib.nms.v1_15_R1.packets; + +import com.comphenix.tinyprotocol.Reflection; +import com.mojang.authlib.GameProfile; +import net.minecraft.server.v1_15_R1.EnumGamemode; +import net.minecraft.server.v1_15_R1.IChatBaseComponent; +import net.minecraft.server.v1_15_R1.PacketPlayOutPlayerInfo; +import org.bukkit.ChatColor; + +import java.util.List; + +/** + * @author Jitse Boonstra + */ +public class PacketPlayOutPlayerInfoWrapper { + + private final Class packetPlayOutPlayerInfoClazz = Reflection.getMinecraftClass("PacketPlayOutPlayerInfo"); + private final Class playerInfoDataClazz = Reflection.getMinecraftClass("PacketPlayOutPlayerInfo$PlayerInfoData"); + private final Reflection.ConstructorInvoker playerInfoDataConstructor = Reflection.getConstructor(playerInfoDataClazz, + packetPlayOutPlayerInfoClazz, GameProfile.class, int.class, EnumGamemode.class, IChatBaseComponent.class); + + public PacketPlayOutPlayerInfo create(PacketPlayOutPlayerInfo.EnumPlayerInfoAction action, GameProfile gameProfile, String name) { + PacketPlayOutPlayerInfo packetPlayOutPlayerInfo = new PacketPlayOutPlayerInfo(); + Reflection.getField(packetPlayOutPlayerInfo.getClass(), "a", PacketPlayOutPlayerInfo.EnumPlayerInfoAction.class) + .set(packetPlayOutPlayerInfo, action); + + Object playerInfoData = playerInfoDataConstructor.invoke(packetPlayOutPlayerInfo, + gameProfile, 1, EnumGamemode.NOT_SET, + IChatBaseComponent.ChatSerializer.b("{\"text\":\"" + ChatColor.BLUE + "[NPC] " + name + "\"}") + ); + + Reflection.FieldAccessor fieldAccessor = Reflection.getField(packetPlayOutPlayerInfo.getClass(), "b", List.class); + List list = fieldAccessor.get(packetPlayOutPlayerInfo); + list.add(playerInfoData); + fieldAccessor.set(packetPlayOutPlayerInfo, list); + + return packetPlayOutPlayerInfo; + } +} diff --git a/nms/v1_15_R1/src/main/java/net/jitse/npclib/nms/v1_15_R1/packets/PacketPlayOutScoreboardTeamWrapper.java b/nms/v1_15_R1/src/main/java/net/jitse/npclib/nms/v1_15_R1/packets/PacketPlayOutScoreboardTeamWrapper.java new file mode 100644 index 0000000..804045c --- /dev/null +++ b/nms/v1_15_R1/src/main/java/net/jitse/npclib/nms/v1_15_R1/packets/PacketPlayOutScoreboardTeamWrapper.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2018 Jitse Boonstra + */ + +package net.jitse.npclib.nms.v1_15_R1.packets; + +import com.comphenix.tinyprotocol.Reflection; +import net.minecraft.server.v1_15_R1.ChatComponentText; +import net.minecraft.server.v1_15_R1.IChatBaseComponent; +import net.minecraft.server.v1_15_R1.PacketPlayOutScoreboardTeam; + +import java.util.Collection; + +/** + * @author Jitse Boonstra + */ +public class PacketPlayOutScoreboardTeamWrapper { + + public PacketPlayOutScoreboardTeam createRegisterTeam(String name) { + PacketPlayOutScoreboardTeam packetPlayOutScoreboardTeam = new PacketPlayOutScoreboardTeam(); + + Reflection.getField(packetPlayOutScoreboardTeam.getClass(), "i", int.class) + .set(packetPlayOutScoreboardTeam, 0); + Reflection.getField(packetPlayOutScoreboardTeam.getClass(), "a", String.class) + .set(packetPlayOutScoreboardTeam, name); + Reflection.getField(packetPlayOutScoreboardTeam.getClass(), "b", IChatBaseComponent.class) + .set(packetPlayOutScoreboardTeam, new ChatComponentText(name)); + Reflection.getField(packetPlayOutScoreboardTeam.getClass(), "e", String.class) + .set(packetPlayOutScoreboardTeam, "never"); + Reflection.getField(packetPlayOutScoreboardTeam.getClass(), "f", String.class) + .set(packetPlayOutScoreboardTeam, "never"); + Reflection.getField(packetPlayOutScoreboardTeam.getClass(), "j", int.class) + .set(packetPlayOutScoreboardTeam, 0); + Reflection.FieldAccessor collectionFieldAccessor = Reflection.getField( + packetPlayOutScoreboardTeam.getClass(), "h", Collection.class); + Collection collection = collectionFieldAccessor.get(packetPlayOutScoreboardTeam); + collection.add(name); + collectionFieldAccessor.set(packetPlayOutScoreboardTeam, collection); + + return packetPlayOutScoreboardTeam; + } + + public PacketPlayOutScoreboardTeam createUnregisterTeam(String name) { + PacketPlayOutScoreboardTeam packetPlayOutScoreboardTeam = new PacketPlayOutScoreboardTeam(); + + Reflection.getField(packetPlayOutScoreboardTeam.getClass(), "i", int.class) + .set(packetPlayOutScoreboardTeam, 1); + Reflection.getField(packetPlayOutScoreboardTeam.getClass(), "a", String.class) + .set(packetPlayOutScoreboardTeam, name); + + return packetPlayOutScoreboardTeam; + } +} diff --git a/plugin/pom.xml b/plugin/pom.xml index 3464668..2352bc3 100644 --- a/plugin/pom.xml +++ b/plugin/pom.xml @@ -86,6 +86,12 @@ ${project.version} compile + + net.jitse + npclib-nms-v1_15_R1 + ${project.version} + compile +