From 2ff1311dc2176774d948542b5af43ab978cd95c8 Mon Sep 17 00:00:00 2001 From: Luuuuuis <45736619+Luuuuuis@users.noreply.github.com> Date: Fri, 11 Sep 2020 23:02:16 +0200 Subject: [PATCH] NPC --- pom.xml | 31 ++-- .../privateserver/spigot/PrivateServer.java | 15 +- .../spigot/events/NPCListener.java | 127 ++++++++++++++++ .../spigot/util/reflections/NMSUtils.java | 102 +++++++++++++ .../spigot/util/reflections/Reflections.java | 138 ++++++++++++++++++ 5 files changed, 397 insertions(+), 16 deletions(-) create mode 100644 src/main/java/de/luuuuuis/privateserver/spigot/events/NPCListener.java create mode 100644 src/main/java/de/luuuuuis/privateserver/spigot/util/reflections/NMSUtils.java create mode 100644 src/main/java/de/luuuuuis/privateserver/spigot/util/reflections/Reflections.java diff --git a/pom.xml b/pom.xml index bae3a9b..0e7aa61 100644 --- a/pom.xml +++ b/pom.xml @@ -34,19 +34,22 @@ - maven-assembly-plugin - 2.5.3 + org.apache.maven.plugins + maven-shade-plugin + 3.1.0 - - jar-with-dependencies - + + + net.jitse.npclib + de.luuuuuis.privateserver + + - make-assembly package - single + shade @@ -64,8 +67,8 @@ https://oss.sonatype.org/content/repositories/snapshots - spigot-repo - https://hub.spigotmc.org/nexus/content/repositories/snapshots/ + ossrh + https://oss.sonatype.org/content/groups/public/ @@ -88,10 +91,18 @@ org.spigotmc - spigot-api + spigot 1.8.8-R0.1-SNAPSHOT provided + + + + net.jitse + npclib-plugin + 2.10-SNAPSHOT + compile + \ No newline at end of file diff --git a/src/main/java/de/luuuuuis/privateserver/spigot/PrivateServer.java b/src/main/java/de/luuuuuis/privateserver/spigot/PrivateServer.java index d97cd7f..2602e6d 100644 --- a/src/main/java/de/luuuuuis/privateserver/spigot/PrivateServer.java +++ b/src/main/java/de/luuuuuis/privateserver/spigot/PrivateServer.java @@ -3,8 +3,10 @@ import com.google.gson.Gson; import com.google.gson.GsonBuilder; import de.luuuuuis.privateserver.spigot.events.JoinListener; +import de.luuuuuis.privateserver.spigot.events.NPCListener; import de.luuuuuis.privateserver.spigot.util.Config; import de.luuuuuis.privateserver.spigot.util.Owner; +import net.jitse.npclib.NPCLib; import org.bukkit.plugin.PluginManager; import org.bukkit.plugin.java.JavaPlugin; @@ -13,6 +15,7 @@ public class PrivateServer extends JavaPlugin { public static final Gson GSON = new GsonBuilder().setPrettyPrinting().create(); private static PrivateServer instance; private Owner owner; + private NPCLib npcLib; public static PrivateServer getInstance() { return instance; @@ -22,6 +25,7 @@ public static PrivateServer getInstance() { public void onEnable() { super.onEnable(); instance = this; + npcLib = new NPCLib(this); // init Config Config.init(getDataFolder()); @@ -30,16 +34,15 @@ public void onEnable() { owner = new Owner(); PluginManager pluginManager = getServer().getPluginManager(); - if (owner.getUuid() == null) { - // This is not a private server - System.err.println("[PrivateServer] This is not a privateserver. Disabling plugin."); - pluginManager.disablePlugin(this); - return; - } pluginManager.registerEvents(new JoinListener(), this); + pluginManager.registerEvents(new NPCListener(this), this); } public Owner getOwner() { return owner; } + + public NPCLib getNpcLib() { + return npcLib; + } } diff --git a/src/main/java/de/luuuuuis/privateserver/spigot/events/NPCListener.java b/src/main/java/de/luuuuuis/privateserver/spigot/events/NPCListener.java new file mode 100644 index 0000000..2d61f97 --- /dev/null +++ b/src/main/java/de/luuuuuis/privateserver/spigot/events/NPCListener.java @@ -0,0 +1,127 @@ +package de.luuuuuis.privateserver.spigot.events; + +import de.luuuuuis.privateserver.spigot.PrivateServer; +import de.luuuuuis.privateserver.spigot.util.reflections.NMSUtils; +import de.luuuuuis.privateserver.spigot.util.reflections.Reflections; +import net.jitse.npclib.api.NPC; +import net.jitse.npclib.api.events.NPCInteractEvent; +import net.jitse.npclib.api.skin.Skin; +import net.jitse.npclib.internal.NPCBase; +import net.minecraft.server.v1_8_R3.PacketPlayOutEntity; +import org.bukkit.Location; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerJoinEvent; +import org.bukkit.event.player.PlayerQuitEvent; +import org.bukkit.scheduler.BukkitRunnable; +import org.bukkit.util.NumberConversions; +import org.bukkit.util.Vector; + +import java.lang.reflect.InvocationTargetException; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +public class NPCListener implements Listener { + + private final PrivateServer privateServer; + private final Map npcs = new HashMap<>(); + + public NPCListener(PrivateServer privateServer) { + this.privateServer = privateServer; + } + + @EventHandler + public void onJoin(PlayerJoinEvent e) { + Player p = e.getPlayer(); + + Location location = new Location(p.getWorld(), p.getLocation().getX(), p.getLocation().getY(), p.getLocation().getZ()); + NPC npc = privateServer.getNpcLib().createNPC(Collections.singletonList("§aPrivate Server")); + npc.setLocation(location); + + Objects.requireNonNull(NMSUtils.getProfile(p)).getProperties().get("textures").stream() + .findFirst().ifPresent(playerProperty -> npc.setSkin(new Skin(playerProperty.getValue(), playerProperty.getSignature()))); + + npc.create(); + npc.show(p); + + npcs.put(p, npc); + + // Sets head rotation for the npc if the player is in a range of 15 blocks + // Author: @yanjulang + new BukkitRunnable() { + + @Override + public void run() { + if (!p.isOnline()) { + cancel(); + return; + } + + if (npc.getLocation().getWorld() == p.getWorld() && p.getLocation().distance(npc.getLocation()) < 15) { + Vector look = p.getLocation().toVector().add(new Vector(0, 1, 0)).subtract(npc.getLocation().toVector().add(new Vector(0, 1.54, 0))); + float[] rots = vecToRots(look); + + if (rots[0] > 180) { + rots[0] -= 360; + } + + Object packet = null; + try { + packet = Objects.requireNonNull(NMSUtils.getNMSClass("PacketPlayOutEntityHeadRotation")).getConstructor().newInstance(); + } catch (InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException instantiationException) { + instantiationException.printStackTrace(); + } + + Reflections.setValue(packet, "a", ((NPCBase) npc).getEntityId()); + Reflections.setValue(packet, "b", toAngle(rots[0])); + NMSUtils.sendPacket(p, packet); + NMSUtils.sendPacket(p, new PacketPlayOutEntity.PacketPlayOutEntityLook(((NPCBase) npc).getEntityId(), toAngle(rots[0]), toAngle(rots[1]), true)); + } + } + + private float[] vecToRots(Vector vector) { + double x = vector.getX(); + double z = vector.getZ(); + if (x == 0.0D && z == 0.0D) { + return new float[]{0f, (float) (vector.getY() > 0.0D ? -90 : 90)}; + } else { + double theta = Math.atan2(-x, z); + float yaw = (float) Math.toDegrees((theta + 6.283185307179586D) % 6.283185307179586D); + double x2 = NumberConversions.square(x); + double z2 = NumberConversions.square(z); + double xz = Math.sqrt(x2 + z2); + float pitch = (float) Math.toDegrees(Math.atan(-vector.getY() / xz)); + return new float[]{yaw, pitch}; + } + } + + public byte toAngle(float value) { + return (byte) ((int) (value * 256.0F / 360.0F)); + } + }.runTaskTimerAsynchronously(privateServer, 0, 2); + + } + + @EventHandler + public void onQuit(PlayerQuitEvent e) { + Player p = e.getPlayer(); + + NPC npc = npcs.get(p); + npc.destroy(); + + npcs.remove(p); + } + + @EventHandler + public void onInteract(NPCInteractEvent e) { + NPC npc = npcs.get(e.getWhoClicked()); + + if (e.getNPC().equals(npc)) { + + } + + } +} diff --git a/src/main/java/de/luuuuuis/privateserver/spigot/util/reflections/NMSUtils.java b/src/main/java/de/luuuuuis/privateserver/spigot/util/reflections/NMSUtils.java new file mode 100644 index 0000000..bb279b1 --- /dev/null +++ b/src/main/java/de/luuuuuis/privateserver/spigot/util/reflections/NMSUtils.java @@ -0,0 +1,102 @@ +package de.luuuuuis.privateserver.spigot.util.reflections; + +import com.mojang.authlib.GameProfile; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; + +import java.lang.reflect.InvocationTargetException; +import java.util.Objects; + +public class NMSUtils { + + /** + * Send a packet to a player without using direct nms imports + * + * @param player to send the packet to + * @param packet to send + */ + public static void sendPacket(Player player, Object packet) { + try { + Object handle = player.getClass().getMethod("getHandle").invoke(player); + Object connection = handle.getClass().getField("playerConnection").get(handle); + connection.getClass().getMethod("sendPacket", getNMSClass("Packet")).invoke(connection, packet); + } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException + | SecurityException | NoSuchFieldException e) { + e.printStackTrace(); + } + } + + /** + * Get an NMS Class by its Name + * Class Path: Net.Ninecraft.Server..ClassName + * + * @param name of the Class + * @return Class + */ + public static Class getNMSClass(String name) { + String version = Bukkit.getServer().getClass().getPackage().getName().split("\\.")[3]; + try { + return Class.forName("net.minecraft.server." + version + "." + name); + } catch (ClassNotFoundException e) { + e.printStackTrace(); + return null; + } + } + + /** + * Get an CraftBukkit Class by its Name + * Class Path: org.bukkit.craftbukkit..ClassName + * + * @param name of the Class + * @return Class + */ + public static Class getCraftBukkitClass(String name) { + String version = Bukkit.getServer().getClass().getPackage().getName().split("\\.")[3]; + try { + return Class.forName("org.bukkit.craftbukkit." + version + "." + name); + } catch (ClassNotFoundException e) { + e.printStackTrace(); + return null; + } + } + + /** + * Get the NMS Version String from Bukkit.getServer() - package + * + * @return String + */ + public static String getVersion() { + return Bukkit.getServer().getClass().getPackage().getName().split("\\.")[3]; + } + + /** + * Get the {@link GameProfile} from a player + * + * @param player to get the GameProfile from + * @return GameProfile + */ + public static GameProfile getProfile(Player player) { + try { + return (GameProfile) Objects.requireNonNull(NMSUtils.getHandle(player)).getClass().getMethod("getProfile").invoke(NMSUtils.getHandle(player)); + } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException + | SecurityException e) { + e.printStackTrace(); + return null; + } + } + + /** + * Get an NMS - EntityPlayer - Object by a org.bukkit.Player - Object + * + * @param player (Bukkit Player) + * @return NMS EntityPlayer Object + */ + public static Object getHandle(Player player) { + try { + return player.getClass().getMethod("getHandle").invoke(player); + } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException + | SecurityException e) { + return null; + } + } +} \ No newline at end of file diff --git a/src/main/java/de/luuuuuis/privateserver/spigot/util/reflections/Reflections.java b/src/main/java/de/luuuuuis/privateserver/spigot/util/reflections/Reflections.java new file mode 100644 index 0000000..63fef73 --- /dev/null +++ b/src/main/java/de/luuuuuis/privateserver/spigot/util/reflections/Reflections.java @@ -0,0 +1,138 @@ +package de.luuuuuis.privateserver.spigot.util.reflections; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Method; + +/** + * Created by @author yanjulang + * on @date 28.06.2017 + */ +public class Reflections { + + /** + * Set a field value in an Object + * + * @param obj to set the field + * @param name of the field + * @param value for the field + */ + public static void setValue(Object obj, String name, Object value) { + try { + Field field = obj.getClass().getDeclaredField(name); + field.setAccessible(true); + field.set(obj, value); + } catch (Exception ignored) { + } + } + + /** + * Get a Field value from an Object + * + * @param obj to get the field from + * @param name of the field + * @return Object (Value) + */ + public static Object getValue(Object obj, String name) { + try { + Field field = obj.getClass().getDeclaredField(name); + field.setAccessible(true); + return field.get(obj); + } catch (Exception ignored) { + } + + return null; + } + + /** + * Get a {@link Method} from a class + * + * @param clazz to get the Method from + * @param methodName name of the Method + * @param parameters all Classes that are parameters of this Method + * @return The Method + */ + public static Method getMethod(Class clazz, String methodName, Class... parameters) { + try { + return clazz.getDeclaredMethod(methodName, parameters); + } catch (NoSuchMethodException | SecurityException e) { + return null; + } + } + + /** + * Get a {@link Constructor} from a Class + * + * @param clazz to get the Constructor from + * @param parameters + * @return The Constructor + */ + public static Constructor getConstructor(Class clazz, Class... parameters) { + try { + return clazz.getConstructor(parameters); + } catch (NoSuchMethodException | SecurityException e) { + return null; + } + } + + /** + * Get a {@link Boolean} from an Object + * + * @param obj to get the boolean from + * @param fieldName - the name of the field + * @return boolean + */ + public static boolean getBoolean(Object obj, String fieldName) { + try { + return obj.getClass().getDeclaredField(fieldName).getBoolean(obj); + } catch (IllegalArgumentException | IllegalAccessException | NoSuchFieldException | SecurityException e) { + return false; + } + } + + /** + * Get a {@link String} from an Object + * + * @param obj to get the String from + * @param fieldName - the name of the field + * @return String + */ + public static String getString(Object obj, String fieldName) { + try { + return (String) obj.getClass().getDeclaredField(fieldName).get(obj); + } catch (IllegalArgumentException | IllegalAccessException | NoSuchFieldException | SecurityException e) { + return null; + } + } + + /** + * Get a {@link Integer} from an Object + * + * @param obj to get the Integer from + * @param fieldName - the name of the field + * @return int + */ + public static int getInt(Object obj, String fieldName) { + try { + return obj.getClass().getDeclaredField(fieldName).getInt(obj); + } catch (IllegalArgumentException | IllegalAccessException | NoSuchFieldException | SecurityException e) { + return -1; + } + } + + /** + * Get a {@link Double} from an Object + * + * @param obj to get the Double from + * @param fieldName - the name of the field + * @return double + */ + public static double getDouble(Object obj, String fieldName) { + try { + return obj.getClass().getDeclaredField(fieldName).getDouble(obj); + } catch (IllegalArgumentException | IllegalAccessException | NoSuchFieldException | SecurityException e) { + return -1; + } + } + +} \ No newline at end of file