diff --git a/.travis.yml b/.travis.yml index fb99088..b81ba3c 100755 --- a/.travis.yml +++ b/.travis.yml @@ -4,6 +4,8 @@ cache: - $HOME/.m2/ install: - wget https://hub.spigotmc.org/jenkins/job/BuildTools/lastSuccessfulBuild/artifact/target/BuildTools.jar +- wget https://github.com/Sarabveer/CraftBukkit-Spigot-Binary/blob/master/spigot-1.7.10-R0.1/spigot-1.7.10-R0.1-1649.jar +- mvn install:install-file -Dfile=spigot-1.7.10-R0.1-1649.jar -DgroupId=org.spigotmc -DartifactId=spigot -Dversion=1.7.10-R0.1-SNAPSHOT -Dpackaging=jar - ls $HOME/.m2/repository/org/spigotmc/spigot/1.8-R0.1-SNAPSHOT >> /dev/null 2>&1 || java -jar BuildTools.jar --rev 1.8 >> /dev/null 2>&1 - ls $HOME/.m2/repository/org/spigotmc/spigot/1.8.3-R0.1-SNAPSHOT >> /dev/null 2>&1 || java -jar BuildTools.jar --rev 1.8.3 >> /dev/null 2>&1 - ls $HOME/.m2/repository/org/spigotmc/spigot/1.8.8-R0.1-SNAPSHOT >> /dev/null 2>&1 || java -jar BuildTools.jar --rev 1.8.8 >> /dev/null 2>&1 diff --git a/api/pom.xml b/api/pom.xml index 27be49f..3f4c2b3 100755 --- a/api/pom.xml +++ b/api/pom.xml @@ -19,6 +19,12 @@ ${project.version} compile + + net.jitse + npclib-nms-v1_7_R4 + ${project.version} + compile + net.jitse npclib-nms-v1_8_R1 diff --git a/nms/pom.xml b/nms/pom.xml index 93bb047..39613f0 100755 --- a/nms/pom.xml +++ b/nms/pom.xml @@ -13,6 +13,7 @@ npclib-nms + v1_7_R4 v1_8_R1 v1_8_R2 v1_8_R3 diff --git a/nms/v1_7_R4/pom.xml b/nms/v1_7_R4/pom.xml new file mode 100755 index 0000000..5324123 --- /dev/null +++ b/nms/v1_7_R4/pom.xml @@ -0,0 +1,35 @@ + + + 4.0.0 + + + net.jitse + npclib-nms + 1.3 + + + npclib-nms-v1_7_R4 + + + + org.spigotmc + spigot + 1.7.10-R0.1-SNAPSHOT + provided + + + com.google.code.gson + gson + 2.8.5 + compile + + + io.netty + netty-all + 4.0.23.Final + compile + + + diff --git a/nms/v1_7_R4/src/main/java/net/jitse/npclib/nms/v1_7_R4/NPC_v1_7_R4.java b/nms/v1_7_R4/src/main/java/net/jitse/npclib/nms/v1_7_R4/NPC_v1_7_R4.java new file mode 100755 index 0000000..3ddfb17 --- /dev/null +++ b/nms/v1_7_R4/src/main/java/net/jitse/npclib/nms/v1_7_R4/NPC_v1_7_R4.java @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2018 Jitse Boonstra + */ + +package net.jitse.npclib.nms.v1_7_R4; + +import net.jitse.npclib.api.NPC; +import net.jitse.npclib.nms.holograms.Hologram; +import net.jitse.npclib.nms.v1_7_R4.packets.PacketPlayOutEntityHeadRotationWrapper; +import net.jitse.npclib.nms.v1_7_R4.packets.PacketPlayOutNamedEntitySpawnWrapper; +import net.jitse.npclib.nms.v1_7_R4.packets.PacketPlayOutPlayerInfoWrapper; +import net.jitse.npclib.nms.v1_7_R4.packets.PacketPlayOutScoreboardTeamWrapper; +import net.jitse.npclib.skin.Skin; +import net.minecraft.server.v1_7_R4.*; +import net.minecraft.util.com.mojang.authlib.GameProfile; +import net.minecraft.util.com.mojang.authlib.properties.Property; +import org.bukkit.Bukkit; +import org.bukkit.craftbukkit.v1_7_R4.entity.CraftPlayer; +import org.bukkit.entity.Player; +import org.bukkit.plugin.java.JavaPlugin; + +import java.util.List; +import java.util.UUID; + +/** + * @author Jitse Boonstra + */ +public class NPC_v1_7_R4 extends NPC { + + private Hologram hologram; + private PacketPlayOutNamedEntitySpawn packetPlayOutNamedEntitySpawn; + private PacketPlayOutScoreboardTeam packetPlayOutScoreboardTeamRegister, packetPlayOutScoreboardTeamUnregister; + private PacketPlayOutPlayerInfo packetPlayOutPlayerInfoAdd, packetPlayOutPlayerInfoRemove; + private PacketPlayOutEntityHeadRotation packetPlayOutEntityHeadRotation; + private PacketPlayOutEntityDestroy packetPlayOutEntityDestroy; + + private GameProfile legacyGameProfile; + + public NPC_v1_7_R4(JavaPlugin plugin, Skin skin, double autoHideDistance, List lines) { + super(plugin, skin, autoHideDistance, lines); + } + + @Override + public void createPackets() { + this.hologram = new Hologram(location.clone().add(0, 0.5, 0), lines); + hologram.generatePackets(false, false); + + this.legacyGameProfile = generateLegacyGameProfile(uuid, name); + PacketPlayOutPlayerInfoWrapper packetPlayOutPlayerInfoWrapper = new PacketPlayOutPlayerInfoWrapper(); + + // Packets for spawning the NPC: + this.packetPlayOutScoreboardTeamRegister = new PacketPlayOutScoreboardTeamWrapper() + .createRegisterTeam(name); // First packet to send. + + this.packetPlayOutPlayerInfoAdd = packetPlayOutPlayerInfoWrapper + .create(0, legacyGameProfile, 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(4, legacyGameProfile, name); // Fifth packet to send (delayed). + + // Packet for destroying the NPC: + this.packetPlayOutEntityDestroy = new PacketPlayOutEntityDestroy(entityId); // First packet to send. + + // Second packet to send is "packetPlayOutPlayerInfoRemove". + + this.packetPlayOutScoreboardTeamUnregister = new PacketPlayOutScoreboardTeamWrapper() + .createUnregisterTeam(name); // Third packet to send. + } + + @Override + public void sendShowPackets(Player player) { + PlayerConnection playerConnection = ((CraftPlayer) player).getHandle().playerConnection; + + playerConnection.sendPacket(packetPlayOutScoreboardTeamRegister); + playerConnection.sendPacket(packetPlayOutPlayerInfoAdd); + playerConnection.sendPacket(packetPlayOutNamedEntitySpawn); + playerConnection.sendPacket(packetPlayOutEntityHeadRotation); + + hologram.spawn(player); + + Bukkit.getScheduler().runTaskLater(plugin, () -> + playerConnection.sendPacket(packetPlayOutPlayerInfoRemove), 50); + } + + @Override + public void sendHidePackets(Player player, boolean scheduler) { + PlayerConnection playerConnection = ((CraftPlayer) player).getHandle().playerConnection; + + playerConnection.sendPacket(packetPlayOutEntityDestroy); + playerConnection.sendPacket(packetPlayOutPlayerInfoRemove); + + hologram.destroy(player); + + if (scheduler) { + // Sending this a bit later so the player doesn't see the name (for that split second). + Bukkit.getScheduler().runTaskLater(plugin, () -> + playerConnection.sendPacket(packetPlayOutScoreboardTeamUnregister), 5); + } else { + playerConnection.sendPacket(packetPlayOutScoreboardTeamUnregister); + } + } + + private GameProfile generateLegacyGameProfile(UUID uuid, String name) { + GameProfile gameProfile = new GameProfile(uuid, name); + + if (skin != null) { + gameProfile.getProperties().put("textures", new Property("textures", skin.getValue(), skin.getSignature())); + } + + return gameProfile; + } +} diff --git a/nms/v1_7_R4/src/main/java/net/jitse/npclib/nms/v1_7_R4/packets/PacketPlayOutEntityHeadRotationWrapper.java b/nms/v1_7_R4/src/main/java/net/jitse/npclib/nms/v1_7_R4/packets/PacketPlayOutEntityHeadRotationWrapper.java new file mode 100755 index 0000000..a8af781 --- /dev/null +++ b/nms/v1_7_R4/src/main/java/net/jitse/npclib/nms/v1_7_R4/packets/PacketPlayOutEntityHeadRotationWrapper.java @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2018 Jitse Boonstra + */ + +package net.jitse.npclib.nms.v1_7_R4.packets; + +import com.comphenix.tinyprotocol.Reflection; +import net.minecraft.server.v1_7_R4.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_7_R4/src/main/java/net/jitse/npclib/nms/v1_7_R4/packets/PacketPlayOutNamedEntitySpawnWrapper.java b/nms/v1_7_R4/src/main/java/net/jitse/npclib/nms/v1_7_R4/packets/PacketPlayOutNamedEntitySpawnWrapper.java new file mode 100755 index 0000000..95823a7 --- /dev/null +++ b/nms/v1_7_R4/src/main/java/net/jitse/npclib/nms/v1_7_R4/packets/PacketPlayOutNamedEntitySpawnWrapper.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2018 Jitse Boonstra + */ + +package net.jitse.npclib.nms.v1_7_R4.packets; + +import com.comphenix.tinyprotocol.Reflection; +import net.minecraft.server.v1_7_R4.DataWatcher; +import net.minecraft.server.v1_7_R4.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", int.class) + .set(packetPlayOutNamedEntitySpawn, (int) Math.floor(location.getX() * 32.0D)); + Reflection.getField(packetPlayOutNamedEntitySpawn.getClass(), "d", int.class) + .set(packetPlayOutNamedEntitySpawn, (int) Math.floor(location.getY() * 32.0D)); + Reflection.getField(packetPlayOutNamedEntitySpawn.getClass(), "e", int.class) + .set(packetPlayOutNamedEntitySpawn, (int) Math.floor(location.getZ() * 32.0D)); + 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))); + + DataWatcher dataWatcher = new DataWatcher(null); + dataWatcher.a(10, (byte) 127); + + Reflection.getField(packetPlayOutNamedEntitySpawn.getClass(), "i", DataWatcher.class) + .set(packetPlayOutNamedEntitySpawn, dataWatcher); + + return packetPlayOutNamedEntitySpawn; + } +} diff --git a/nms/v1_7_R4/src/main/java/net/jitse/npclib/nms/v1_7_R4/packets/PacketPlayOutPlayerInfoWrapper.java b/nms/v1_7_R4/src/main/java/net/jitse/npclib/nms/v1_7_R4/packets/PacketPlayOutPlayerInfoWrapper.java new file mode 100755 index 0000000..3673d16 --- /dev/null +++ b/nms/v1_7_R4/src/main/java/net/jitse/npclib/nms/v1_7_R4/packets/PacketPlayOutPlayerInfoWrapper.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2018 Jitse Boonstra + */ + +package net.jitse.npclib.nms.v1_7_R4.packets; + +import com.comphenix.tinyprotocol.Reflection; +import net.minecraft.server.v1_7_R4.PacketPlayOutPlayerInfo; +import net.minecraft.util.com.mojang.authlib.GameProfile; + +/** + * @author Jitse Boonstra + */ +public class PacketPlayOutPlayerInfoWrapper { + + public PacketPlayOutPlayerInfo create(int action, GameProfile gameProfile, String name) { + PacketPlayOutPlayerInfo packetPlayOutPlayerInfo = new PacketPlayOutPlayerInfo(); + + // Action values: + // 0 = Add player + // 1 = Update gamemode + // 2 = Update latency + // 3 = Update display name + // 4 = Remove player + Reflection.getField(packetPlayOutPlayerInfo.getClass(), "action", int.class) + .set(packetPlayOutPlayerInfo, action); + Reflection.getField(packetPlayOutPlayerInfo.getClass(), "player", GameProfile.class) + .set(packetPlayOutPlayerInfo, gameProfile); + Reflection.getField(packetPlayOutPlayerInfo.getClass(), "username", String.class) + .set(packetPlayOutPlayerInfo, name); + Reflection.getField(packetPlayOutPlayerInfo.getClass(), "ping", int.class) + .set(packetPlayOutPlayerInfo, 0); + Reflection.getField(packetPlayOutPlayerInfo.getClass(), "gamemode", int.class) + .set(packetPlayOutPlayerInfo, 1); + + return packetPlayOutPlayerInfo; + } +} diff --git a/nms/v1_7_R4/src/main/java/net/jitse/npclib/nms/v1_7_R4/packets/PacketPlayOutScoreboardTeamWrapper.java b/nms/v1_7_R4/src/main/java/net/jitse/npclib/nms/v1_7_R4/packets/PacketPlayOutScoreboardTeamWrapper.java new file mode 100755 index 0000000..31002f1 --- /dev/null +++ b/nms/v1_7_R4/src/main/java/net/jitse/npclib/nms/v1_7_R4/packets/PacketPlayOutScoreboardTeamWrapper.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2018 Jitse Boonstra + */ + +package net.jitse.npclib.nms.v1_7_R4.packets; + +import com.comphenix.tinyprotocol.Reflection; +import net.minecraft.server.v1_7_R4.PacketPlayOutScoreboardTeam; +import org.bukkit.ChatColor; + +import java.util.Collection; + +/** + * @author Jitse Boonstra + */ +public class PacketPlayOutScoreboardTeamWrapper { + + public PacketPlayOutScoreboardTeam createRegisterTeam(String name) { + PacketPlayOutScoreboardTeam packetPlayOutScoreboardTeam = new PacketPlayOutScoreboardTeam(); + + Reflection.getField(packetPlayOutScoreboardTeam.getClass(), "h", int.class) + .set(packetPlayOutScoreboardTeam, 0); + Reflection.getField(packetPlayOutScoreboardTeam.getClass(), "b", String.class) + .set(packetPlayOutScoreboardTeam, name); + Reflection.getField(packetPlayOutScoreboardTeam.getClass(), "a", String.class) + .set(packetPlayOutScoreboardTeam, name); + Reflection.getField(packetPlayOutScoreboardTeam.getClass(), "e", String.class) + .set(packetPlayOutScoreboardTeam, "never"); + Reflection.getField(packetPlayOutScoreboardTeam.getClass(), "i", int.class) + .set(packetPlayOutScoreboardTeam, 1); + // Could not get this working in the PacketPlayOutPlayerInfoWrapper class. + Reflection.getField(packetPlayOutScoreboardTeam.getClass(), "c", String.class) + .set(packetPlayOutScoreboardTeam, ChatColor.BLUE + "[NPC] "); + Reflection.FieldAccessor collectionFieldAccessor = Reflection.getField( + packetPlayOutScoreboardTeam.getClass(), "g", 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(), "h", int.class) + .set(packetPlayOutScoreboardTeam, 1); + Reflection.getField(packetPlayOutScoreboardTeam.getClass(), "a", String.class) + .set(packetPlayOutScoreboardTeam, name); + + return packetPlayOutScoreboardTeam; + } +}