Updated the library.

master
Jitse Boonstra 4 years ago
parent b8a7dcaddb
commit e0752c5d7e
  1. 53
      api/pom.xml
  2. 4
      api/src/main/java/com/comphenix/tinyprotocol/Reflection.java
  3. 47
      api/src/main/java/com/comphenix/tinyprotocol/TinyProtocol.java
  4. 113
      api/src/main/java/net/jitse/npclib/NPCLib.java
  5. 83
      api/src/main/java/net/jitse/npclib/api/NPC.java
  6. 19
      api/src/main/java/net/jitse/npclib/api/events/NPCHideEvent.java
  7. 7
      api/src/main/java/net/jitse/npclib/api/events/NPCInteractEvent.java
  8. 19
      api/src/main/java/net/jitse/npclib/api/events/NPCShowEvent.java
  9. 2
      api/src/main/java/net/jitse/npclib/api/skin/MineSkinFetcher.java
  10. 5
      api/src/main/java/net/jitse/npclib/api/skin/Skin.java
  11. 46
      api/src/main/java/net/jitse/npclib/api/utilities/Logger.java
  12. 46
      api/src/main/java/net/jitse/npclib/hologram/Hologram.java
  13. 15
      api/src/main/java/net/jitse/npclib/internal/MinecraftVersion.java
  14. 12
      api/src/main/java/net/jitse/npclib/internal/NPCManager.java
  15. 2
      api/src/main/java/net/jitse/npclib/internal/PacketHandler.java
  16. 101
      api/src/main/java/net/jitse/npclib/internal/SimpleNPC.java
  17. 17
      api/src/main/java/net/jitse/npclib/listeners/ChunkListener.java
  18. 25
      api/src/main/java/net/jitse/npclib/listeners/PacketListener.java
  19. 19
      api/src/main/java/net/jitse/npclib/listeners/PlayerListener.java
  20. 51
      commons/pom.xml
  21. 120
      commons/src/main/java/net/jitse/npclib/NPCLib.java
  22. 17
      commons/src/main/java/net/jitse/npclib/api/ActionHandler.java
  23. 21
      commons/src/main/java/net/jitse/npclib/api/packet/NPCPacket.java
  24. 56
      commons/src/main/java/net/jitse/npclib/api/wrapper/GameProfileWrapper.java
  25. 13
      commons/src/main/java/net/jitse/npclib/events/click/ClickType.java
  26. 13
      commons/src/main/java/net/jitse/npclib/events/trigger/TriggerType.java
  27. 26
      commons/src/main/java/net/jitse/npclib/logging/NPCLibLogger.java
  28. 38
      nms/pom.xml
  29. 3
      nms/v1_10_R1/pom.xml
  30. 21
      nms/v1_10_R1/src/main/java/net/jitse/npclib/nms/v1_10_R1/NPC_v1_10_R1.java
  31. 5
      nms/v1_10_R1/src/main/java/net/jitse/npclib/nms/v1_10_R1/packets/PacketPlayOutPlayerInfoWrapper.java
  32. 3
      nms/v1_11_R1/pom.xml
  33. 21
      nms/v1_11_R1/src/main/java/net/jitse/npclib/nms/v1_11_R1/NPC_v1_11_R1.java
  34. 5
      nms/v1_11_R1/src/main/java/net/jitse/npclib/nms/v1_11_R1/packets/PacketPlayOutPlayerInfoWrapper.java
  35. 3
      nms/v1_12_R1/pom.xml
  36. 22
      nms/v1_12_R1/src/main/java/net/jitse/npclib/nms/v1_12_R1/NPC_v1_12_R1.java
  37. 5
      nms/v1_12_R1/src/main/java/net/jitse/npclib/nms/v1_12_R1/packets/PacketPlayOutPlayerInfoWrapper.java
  38. 3
      nms/v1_13_R1/pom.xml
  39. 22
      nms/v1_13_R1/src/main/java/net/jitse/npclib/nms/v1_13_R1/NPC_v1_13_R1.java
  40. 5
      nms/v1_13_R1/src/main/java/net/jitse/npclib/nms/v1_13_R1/packets/PacketPlayOutPlayerInfoWrapper.java
  41. 3
      nms/v1_13_R2/pom.xml
  42. 21
      nms/v1_13_R2/src/main/java/net/jitse/npclib/nms/v1_13_R2/NPC_v1_13_R2.java
  43. 5
      nms/v1_13_R2/src/main/java/net/jitse/npclib/nms/v1_13_R2/packets/PacketPlayOutPlayerInfoWrapper.java
  44. 24
      nms/v1_14_R1/pom.xml
  45. 99
      nms/v1_14_R1/src/main/java/net/jitse/npclib/nms/v1_14_R1/NPC_v1_14_R1.java
  46. 26
      nms/v1_14_R1/src/main/java/net/jitse/npclib/nms/v1_14_R1/packets/PacketPlayOutEntityHeadRotationWrapper.java
  47. 47
      nms/v1_14_R1/src/main/java/net/jitse/npclib/nms/v1_14_R1/packets/PacketPlayOutNamedEntitySpawnWrapper.java
  48. 43
      nms/v1_14_R1/src/main/java/net/jitse/npclib/nms/v1_14_R1/packets/PacketPlayOutPlayerInfoWrapper.java
  49. 53
      nms/v1_14_R1/src/main/java/net/jitse/npclib/nms/v1_14_R1/packets/PacketPlayOutScoreboardTeamWrapper.java
  50. 21
      nms/v1_8_R1/pom.xml
  51. 21
      nms/v1_8_R1/src/main/java/net/jitse/npclib/nms/v1_8_R1/NPC_v1_8_R1.java
  52. 5
      nms/v1_8_R1/src/main/java/net/jitse/npclib/nms/v1_8_R1/packets/PacketPlayOutPlayerInfoWrapper.java
  53. 3
      nms/v1_8_R2/pom.xml
  54. 21
      nms/v1_8_R2/src/main/java/net/jitse/npclib/nms/v1_8_R2/NPC_v1_8_R2.java
  55. 5
      nms/v1_8_R2/src/main/java/net/jitse/npclib/nms/v1_8_R2/packets/PacketPlayOutPlayerInfoWrapper.java
  56. 3
      nms/v1_8_R3/pom.xml
  57. 21
      nms/v1_8_R3/src/main/java/net/jitse/npclib/nms/v1_8_R3/NPC_v1_8_R3.java
  58. 5
      nms/v1_8_R3/src/main/java/net/jitse/npclib/nms/v1_8_R3/packets/PacketPlayOutPlayerInfoWrapper.java
  59. 3
      nms/v1_9_R1/pom.xml
  60. 21
      nms/v1_9_R1/src/main/java/net/jitse/npclib/nms/v1_9_R1/NPC_v1_9_R1.java
  61. 5
      nms/v1_9_R1/src/main/java/net/jitse/npclib/nms/v1_9_R1/packets/PacketPlayOutPlayerInfoWrapper.java
  62. 3
      nms/v1_9_R2/pom.xml
  63. 21
      nms/v1_9_R2/src/main/java/net/jitse/npclib/nms/v1_9_R2/NPC_v1_9_R2.java
  64. 5
      nms/v1_9_R2/src/main/java/net/jitse/npclib/nms/v1_9_R2/packets/PacketPlayOutPlayerInfoWrapper.java
  65. 110
      plugin/pom.xml
  66. 8
      plugin/src/main/java/net/jitse/npclib/plugin/NPCLibPlugin.java
  67. 6
      plugin/src/main/resources/plugin.yml
  68. 94
      pom.xml

@ -0,0 +1,53 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<packaging>jar</packaging>
<parent>
<artifactId>npclib</artifactId>
<groupId>net.jitse</groupId>
<version>2.0-SNAPSHOT</version>
</parent>
<artifactId>npclib-api</artifactId>
<repositories>
<repository>
<id>spigot-repo</id>
<url>https://hub.spigotmc.org/nexus/content/repositories/snapshots/</url>
</repository>
<repository>
<id>minecraft-repo</id>
<url>https://libraries.minecraft.net</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot-api</artifactId>
<version>1.14.4-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot</artifactId>
<version>1.14.4-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.mojang</groupId>
<artifactId>authlib</artifactId>
<version>1.5.21</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.33.Final</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>

@ -1,3 +1,7 @@
/*
* Copyright (c) 2018 Jitse Boonstra
*/
package com.comphenix.tinyprotocol;
import org.bukkit.Bukkit;

@ -1,11 +1,13 @@
/*
* Copyright (c) 2018 Jitse Boonstra
*/
package com.comphenix.tinyprotocol;
import com.comphenix.tinyprotocol.Reflection.FieldAccessor;
import com.comphenix.tinyprotocol.Reflection.MethodInvoker;
import com.google.common.collect.Lists;
import com.google.common.collect.MapMaker;
import io.netty.channel.*;
import net.jitse.npclib.logging.NPCLibLogger;
import net.jitse.npclib.NPCLib;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
@ -19,8 +21,6 @@ import org.bukkit.scheduler.BukkitRunnable;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Minimized version of TinyProtocol by Kristian suited for NPCLib.
@ -30,35 +30,35 @@ public abstract class TinyProtocol {
private static final AtomicInteger ID = new AtomicInteger(0);
// Used in order to lookup a channel
private static final MethodInvoker getPlayerHandle = Reflection.getMethod("{obc}.entity.CraftPlayer", "getHandle");
private static final FieldAccessor<Object> getConnection = Reflection.getField("{nms}.EntityPlayer", "playerConnection", Object.class);
private static final FieldAccessor<Object> getManager = Reflection.getField("{nms}.PlayerConnection", "networkManager", Object.class);
private static final FieldAccessor<Channel> getChannel = Reflection.getField("{nms}.NetworkManager", Channel.class, 0);
private static final Reflection.MethodInvoker getPlayerHandle = Reflection.getMethod("{obc}.entity.CraftPlayer", "getHandle");
private static final Reflection.FieldAccessor<Object> getConnection = Reflection.getField("{nms}.EntityPlayer", "playerConnection", Object.class);
private static final Reflection.FieldAccessor<Object> getManager = Reflection.getField("{nms}.PlayerConnection", "networkManager", Object.class);
private static final Reflection.FieldAccessor<Channel> getChannel = Reflection.getField("{nms}.NetworkManager", Channel.class, 0);
// Looking up ServerConnection
private static final Class<Object> minecraftServerClass = Reflection.getUntypedClass("{nms}.MinecraftServer");
private static final Class<Object> serverConnectionClass = Reflection.getUntypedClass("{nms}.ServerConnection");
private static final FieldAccessor<Object> getMinecraftServer = Reflection.getField("{obc}.CraftServer", minecraftServerClass, 0);
private static final FieldAccessor<Object> getServerConnection = Reflection.getField(minecraftServerClass, serverConnectionClass, 0);
private static final MethodInvoker getNetworkMarkers = Reflection.getTypedMethod(serverConnectionClass, null, List.class, serverConnectionClass);
private static final Reflection.FieldAccessor<Object> getMinecraftServer = Reflection.getField("{obc}.CraftServer", minecraftServerClass, 0);
private static final Reflection.FieldAccessor<Object> getServerConnection = Reflection.getField(minecraftServerClass, serverConnectionClass, 0);
private static final Reflection.MethodInvoker getNetworkMarkers = Reflection.getTypedMethod(serverConnectionClass, null, List.class, serverConnectionClass);
// Packets we have to intercept
private static final Class<?> PACKET_LOGIN_IN_START = Reflection.getMinecraftClass("PacketLoginInStart");
private static final FieldAccessor getGameProfile = Reflection.getField(PACKET_LOGIN_IN_START,
private static final Reflection.FieldAccessor getGameProfile = Reflection.getField(PACKET_LOGIN_IN_START,
Reflection.getClass("com.mojang.authlib.GameProfile"), 0);
// Speedup channel lookup
private Map<String, Channel> channelLookup = new MapMaker().weakValues().makeMap();
private Listener listener;
private Logger logger;
// Channels that have already been removed
private Set<Channel> uninjectedChannels = Collections.newSetFromMap(new MapMaker().weakKeys().makeMap());
// List of network markers
private List<Object> networkManagers;
private final NPCLib instance;
// Injected channel handlers
private List<Channel> serverChannels = Lists.newArrayList();
private ChannelInboundHandlerAdapter serverChannelHandler;
@ -71,9 +71,9 @@ public abstract class TinyProtocol {
private volatile boolean closed;
protected Plugin plugin;
protected TinyProtocol(final Plugin plugin) {
this.plugin = plugin;
this.logger = new NPCLibLogger(plugin);
protected TinyProtocol(NPCLib instance) {
this.plugin = instance.getPlugin();
this.instance = instance;
// Compute handler name
this.handlerName = "tiny-" + plugin.getName() + "-" + ID.incrementAndGet();
@ -82,19 +82,19 @@ public abstract class TinyProtocol {
registerBukkitEvents();
try {
logger.info("Attempting to inject into netty");
instance.getLogger().info("Attempting to inject into netty");
registerChannelHandler();
registerPlayers(plugin);
} catch (IllegalArgumentException exception) {
// Damn you, late bind
logger.log(Level.WARNING, "Attempting to delay injection");
instance.getLogger().warning("Attempting to delay injection");
new BukkitRunnable() {
@Override
public void run() {
registerChannelHandler();
registerPlayers(plugin);
logger.info("Injection complete");
instance.getLogger().info("Injection complete");
}
}.runTask(plugin);
}
@ -116,7 +116,8 @@ public abstract class TinyProtocol {
}
}
} catch (Exception exception) {
logger.log(Level.SEVERE, "Cannot inject incomming channel " + channel, exception);
instance.getLogger().severe("Cannot inject incomming channel " + channel + ". Message: "
+ exception.getMessage());
}
}
@ -305,7 +306,7 @@ public abstract class TinyProtocol {
try {
msg = onPacketInAsync(player, msg);
} catch (Exception exception) {
logger.log(Level.SEVERE, "Error in onPacketInAsync()", exception);
instance.getLogger().severe("Error in onPacketInAsync(). Message: " + exception.getMessage());
}
if (msg != null) {

@ -0,0 +1,113 @@
/*
* Copyright (c) 2018 Jitse Boonstra
*/
package net.jitse.npclib;
import net.jitse.npclib.api.NPC;
import net.jitse.npclib.api.utilities.Logger;
import net.jitse.npclib.listeners.ChunkListener;
import net.jitse.npclib.listeners.PacketListener;
import net.jitse.npclib.listeners.PlayerListener;
import org.bukkit.plugin.PluginManager;
import org.bukkit.plugin.java.JavaPlugin;
import java.util.List;
public final class NPCLib {
private final JavaPlugin plugin;
private final Logger logger;
private final Class<?> npcClass;
private double autoHideDistance = 50.0;
public NPCLib(JavaPlugin plugin) {
this.plugin = plugin;
this.logger = new Logger("NPCLib");
String versionName = plugin.getServer().getClass().getPackage().getName().split("\\.")[3];
Class<?> npcClass = null;
try {
npcClass = Class.forName("net.jitse.npclib.nms." + versionName + ".NPC_" + versionName);
} catch (ClassNotFoundException exception) {
// Version not supported, error below.
}
this.npcClass = npcClass;
if (npcClass == null) {
logger.severe("Failed to initiate. Your server's version ("
+ versionName + ") is not supported");
return;
}
PluginManager pluginManager = plugin.getServer().getPluginManager();
pluginManager.registerEvents(new PlayerListener(this), plugin);
pluginManager.registerEvents(new ChunkListener(this), plugin);
// Boot the according packet listener.
new PacketListener().start(this);
logger.info("Enabled for Minecraft " + versionName);
}
/**
* @return The JavaPlugin instance.
*/
public JavaPlugin getPlugin() {
return plugin;
}
/**
* Set a new value for the auto-hide distance.
* A recommended value (and default) is 50 blocks.
*
* @param autoHideDistance The new value.
*/
public void setAutoHideDistance(double autoHideDistance) {
this.autoHideDistance = autoHideDistance;
}
/**
* @return The auto-hide distance.
*/
public double getAutoHideDistance() {
return autoHideDistance;
}
/**
* @return The logger NPCLib uses.
*/
public Logger getLogger() {
return logger;
}
/**
* Create a new non-player character (NPC).
*
* @param lines The text you want to sendShowPackets above the NPC (null = no text).
* @return The NPC object you may use to sendShowPackets it to players.
*/
public NPC createNPC(List<String> lines) {
try {
return (NPC) npcClass.getConstructors()[0].newInstance(this, lines);
} catch (Exception exception) {
logger.warning("Failed to create NPC. Please report the following stacktrace message: " + exception.getMessage());
}
return null;
}
/**
* Create a new non-player character (NPC).
*
* @return The NPC object you may use to sendShowPackets it to players.
*/
public NPC createNPC() {
return createNPC(null);
}
}

@ -0,0 +1,83 @@
/*
* Copyright (c) 2018 Jitse Boonstra
*/
package net.jitse.npclib.api;
import net.jitse.npclib.api.skin.Skin;
import org.bukkit.Location;
import org.bukkit.entity.Player;
public interface NPC {
/**
* Set the NPC's location.
* Use this method before using {@link NPC#create}.
*
* @param location The spawn location for the NPC.
* @return object instance.
*/
NPC setLocation(Location location);
/**
* Set the NPC's skin.
* Use this method before using {@link NPC#create}.
*
* @param skin The skin(data) you'd like to apply.
* @return object instance.
*/
NPC setSkin(Skin skin);
/**
* Get the location of the NPC.
*
* @return The location of the NPC.
*/
Location getLocation();
/**
* Create all necessary packets for the NPC so it can be shown to players.
*
* @return object instance.
*/
NPC create();
/**
* Get the ID of the NPC.
*
* @return the ID of the NPC.
*/
String getId();
/**
* Test if a player can see the NPC.
* E.g. is the player is out of range, this method will return false as the NPC is automatically hidden by the library.
*
* @param player The player you'd like to check.
* @return Value on whether the player can see the NPC.
*/
boolean isShown(Player player);
/**
* Show the NPC to a player.
* Requires {@link NPC#create} to be used first.
*
* @param player the player to show the NPC to.
*/
void show(Player player);
/**
* Hide the NPC from a player.
* Will not do anything if NPC isn't shown to the player.
* Requires {@link NPC#create} to be used first.
*
* @param player The player to hide the NPC from.
*/
void hide(Player player);
/**
* Destroy the NPC, i.e. remove it from the registry.
* Requires {@link NPC#create} to be used first.
*/
void destroy();
}

@ -2,10 +2,9 @@
* Copyright (c) 2018 Jitse Boonstra
*/
package net.jitse.npclib.events;
package net.jitse.npclib.api.events;
import net.jitse.npclib.api.NPC;
import net.jitse.npclib.events.trigger.TriggerType;
import org.bukkit.entity.Player;
import org.bukkit.event.Cancellable;
import org.bukkit.event.Event;
@ -14,7 +13,7 @@ import org.bukkit.event.HandlerList;
/**
* @author Jitse Boonstra
*/
public class NPCSpawnEvent extends Event implements Cancellable {
public class NPCHideEvent extends Event implements Cancellable {
private static final HandlerList handlers = new HandlerList();
@ -22,12 +21,12 @@ public class NPCSpawnEvent extends Event implements Cancellable {
private final NPC npc;
private final Player player;
private final TriggerType trigger;
private final boolean automatic;
public NPCSpawnEvent(NPC npc, Player player, TriggerType trigger) {
public NPCHideEvent(NPC npc, Player player, boolean automatic) {
this.npc = npc;
this.player = player;
this.trigger = trigger;
this.automatic = automatic;
}
@Override
@ -43,8 +42,11 @@ public class NPCSpawnEvent extends Event implements Cancellable {
return player;
}
public TriggerType getTrigger() {
return trigger;
/**
* @return Value on whether the hiding was triggered automatically.
*/
public boolean isAutomatic() {
return automatic;
}
@Override
@ -60,3 +62,4 @@ public class NPCSpawnEvent extends Event implements Cancellable {
return handlers;
}
}

@ -2,10 +2,9 @@
* Copyright (c) 2018 Jitse Boonstra
*/
package net.jitse.npclib.events;
package net.jitse.npclib.api.events;
import net.jitse.npclib.api.NPC;
import net.jitse.npclib.events.click.ClickType;
import org.bukkit.entity.Player;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
@ -46,4 +45,8 @@ public class NPCInteractEvent extends Event {
public static HandlerList getHandlerList() {
return handlers;
}
public enum ClickType {
LEFT_CLICK, RIGHT_CLICK
}
}

@ -2,10 +2,9 @@
* Copyright (c) 2018 Jitse Boonstra
*/
package net.jitse.npclib.events;
package net.jitse.npclib.api.events;
import net.jitse.npclib.api.NPC;
import net.jitse.npclib.events.trigger.TriggerType;
import org.bukkit.entity.Player;
import org.bukkit.event.Cancellable;
import org.bukkit.event.Event;
@ -14,7 +13,7 @@ import org.bukkit.event.HandlerList;
/**
* @author Jitse Boonstra
*/
public class NPCDestroyEvent extends Event implements Cancellable {
public class NPCShowEvent extends Event implements Cancellable {
private static final HandlerList handlers = new HandlerList();
@ -22,12 +21,12 @@ public class NPCDestroyEvent extends Event implements Cancellable {
private final NPC npc;
private final Player player;
private final TriggerType trigger;
private final boolean automatic;
public NPCDestroyEvent(NPC npc, Player player, TriggerType trigger) {
public NPCShowEvent(NPC npc, Player player, boolean automatic) {
this.npc = npc;
this.player = player;
this.trigger = trigger;
this.automatic = automatic;
}
@Override
@ -43,8 +42,11 @@ public class NPCDestroyEvent extends Event implements Cancellable {
return player;
}
public TriggerType getTrigger() {
return trigger;
/**
* @return Value on whether the spawn was triggered automatically.
*/
public boolean isAutomatic() {
return automatic;
}
@Override
@ -60,4 +62,3 @@ public class NPCDestroyEvent extends Event implements Cancellable {
return handlers;
}
}

@ -2,7 +2,7 @@
* Copyright (c) 2018 Jitse Boonstra
*/
package net.jitse.npclib.skin;
package net.jitse.npclib.api.skin;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;

@ -2,11 +2,8 @@
* Copyright (c) 2018 Jitse Boonstra
*/
package net.jitse.npclib.skin;
package net.jitse.npclib.api.skin;
/**
* @author Jitse Boonstra
*/
public class Skin {
private final String value, signature;

@ -0,0 +1,46 @@
/*
* Copyright (c) 2018 Jitse Boonstra
*/
package net.jitse.npclib.api.utilities;
import org.bukkit.Bukkit;
public class Logger {
private final String prefix;
private boolean enabled = true;
public Logger(String prefix) {
this.prefix = prefix + " ";
}
public void disable() {
this.enabled = false;
}
public void info(String info) {
if (!enabled) {
return;
}
Bukkit.getLogger().info(prefix + info);
}
public void warning(String warning) {
if (!enabled) {
return;
}
Bukkit.getLogger().warning(prefix + warning);
}
public void severe(String severe) {
if (!enabled) {
return;
}
Bukkit.getLogger().severe(prefix + severe);
}
}

@ -2,9 +2,10 @@
* Copyright (c) 2018 Jitse Boonstra
*/
package net.jitse.npclib.nms.holograms;
package net.jitse.npclib.hologram;
import com.comphenix.tinyprotocol.Reflection;
import net.jitse.npclib.internal.MinecraftVersion;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.entity.Player;
@ -14,9 +15,6 @@ import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* @author Jitse Boonstra
*/
public class Hologram {
private final double delta = 0.3;
@ -84,18 +82,18 @@ public class Hologram {
}
public void generatePackets(boolean above1_9_r2, boolean above_1_12_r1) {
Reflection.MethodInvoker gravityMethod = (above1_9_r2 ? Reflection.getMethod(ENTITY_CLAZZ,
"setNoGravity", boolean.class) : Reflection.getMethod(ENTITY_ARMOR_STAND_CLAZZ,
"setGravity", boolean.class));
public void generatePackets(MinecraftVersion version) {
Reflection.MethodInvoker gravityMethod = (version.isAboveOrEqual(MinecraftVersion.V1_9_R2) ?
Reflection.getMethod(ENTITY_CLAZZ, "setNoGravity", boolean.class) :
Reflection.getMethod(ENTITY_ARMOR_STAND_CLAZZ, "setGravity", boolean.class));
Reflection.MethodInvoker customNameMethod = (above_1_12_r1 ? Reflection.getMethod(ENTITY_CLAZZ,
"setCustomName", CHAT_BASE_COMPONENT_CLAZZ) : Reflection.getMethod(ENTITY_ARMOR_STAND_CLAZZ,
"setCustomName", String.class));
Reflection.MethodInvoker customNameMethod = (version.isAboveOrEqual(MinecraftVersion.V1_12_R1) ?
Reflection.getMethod(ENTITY_CLAZZ, "setCustomName", CHAT_BASE_COMPONENT_CLAZZ) :
Reflection.getMethod(ENTITY_ARMOR_STAND_CLAZZ, "setCustomName", String.class));
Reflection.MethodInvoker customNameVisibilityMethod = (above_1_12_r1 ? Reflection.getMethod(ENTITY_CLAZZ,
"setCustomNameVisible", boolean.class) : Reflection.getMethod(ENTITY_ARMOR_STAND_CLAZZ,
"setCustomNameVisible", boolean.class));
Reflection.MethodInvoker customNameVisibilityMethod = (version.isAboveOrEqual(MinecraftVersion.V1_12_R1) ?
Reflection.getMethod(ENTITY_CLAZZ, "setCustomNameVisible", boolean.class) :
Reflection.getMethod(ENTITY_ARMOR_STAND_CLAZZ, "setCustomNameVisible", boolean.class));
Location location = start.clone().add(0, delta * lines.size(), 0);
Class<?> worldClass = worldServer.getClass().getSuperclass();
@ -104,16 +102,23 @@ public class Hologram {
worldClass = worldClass.getSuperclass();
}
Reflection.ConstructorInvoker entityArmorStandConstructor = Reflection
.getConstructor(ENTITY_ARMOR_STAND_CLAZZ, worldClass);
Reflection.ConstructorInvoker entityArmorStandConstructor = (version.isAboveOrEqual(MinecraftVersion.V1_14_R1) ?
Reflection.getConstructor(ENTITY_ARMOR_STAND_CLAZZ, worldClass, double.class, double.class, double.class) :
Reflection.getConstructor(ENTITY_ARMOR_STAND_CLAZZ, worldClass));
for (String line : lines) {
Object entityArmorStand = entityArmorStandConstructor.invoke(worldServer);
Object entityArmorStand = (version.isAboveOrEqual(MinecraftVersion.V1_14_R1) ?
entityArmorStandConstructor.invoke(worldServer, location.getX(), location.getY(), location.getZ()) :
entityArmorStandConstructor.invoke(worldServer));
if (!version.isAboveOrEqual(MinecraftVersion.V1_14_R1)) {
SET_LOCATION_METHOD.invoke(entityArmorStand, location.getX(), location.getY(), location.getZ(), 0, 0);
}
SET_LOCATION_METHOD.invoke(entityArmorStand, location.getX(), location.getY(), location.getZ(), 0, 0);
customNameMethod.invoke(entityArmorStand, above_1_12_r1 ? CHAT_COMPONENT_TEXT_CONSTRUCTOR.invoke(line) : line);
customNameMethod.invoke(entityArmorStand, version.isAboveOrEqual(MinecraftVersion.V1_12_R1) ?
CHAT_COMPONENT_TEXT_CONSTRUCTOR.invoke(line) : line);
customNameVisibilityMethod.invoke(entityArmorStand, true);
gravityMethod.invoke(entityArmorStand, above1_9_r2);
gravityMethod.invoke(entityArmorStand, version.isAboveOrEqual(MinecraftVersion.V1_9_R2));
SET_SMALL_METHOD.invoke(entityArmorStand, true);
SET_INVISIBLE_METHOD.invoke(entityArmorStand, true);
SET_BASE_PLATE_METHOD.invoke(entityArmorStand, false);
@ -170,3 +175,4 @@ public class Hologram {
}
}
}

@ -0,0 +1,15 @@
/*
* Copyright (c) 2018 Jitse Boonstra
*/
package net.jitse.npclib.internal;
public enum MinecraftVersion {
V1_8_R1, V1_8_R2, V1_8_R3, V1_9_R1, V1_9_R2, V1_10_R1, V1_11_R1, V1_12_R1, V1_13_R1, V1_13_R2, V1_14_R1;
public boolean isAboveOrEqual(MinecraftVersion compare) {
return ordinal() >= compare.ordinal();
}
}

@ -2,9 +2,7 @@
* Copyright (c) 2018 Jitse Boonstra
*/
package net.jitse.npclib;
import net.jitse.npclib.api.NPC;
package net.jitse.npclib.internal;
import java.util.HashSet;
import java.util.Set;
@ -14,17 +12,17 @@ import java.util.Set;
*/
public final class NPCManager {
private static Set<NPC> npcs = new HashSet<>();
private static Set<SimpleNPC> npcs = new HashSet<>();
public static Set<NPC> getAllNPCs() {
public static Set<SimpleNPC> getAllNPCs() {
return npcs;
}
public static void add(NPC npc) {
public static void add(SimpleNPC npc) {
npcs.add(npc);
}
public static void remove(NPC npc) {
public static void remove(SimpleNPC npc) {
npcs.remove(npc);
}

@ -2,7 +2,7 @@
* Copyright (c) 2018 Jitse Boonstra
*/
package net.jitse.npclib.api;
package net.jitse.npclib.internal;
import org.bukkit.entity.Player;

@ -2,65 +2,74 @@
* Copyright (c) 2018 Jitse Boonstra
*/
package net.jitse.npclib.api;
import net.jitse.npclib.NPCManager;
import net.jitse.npclib.api.wrapper.GameProfileWrapper;
import net.jitse.npclib.events.NPCDestroyEvent;
import net.jitse.npclib.events.NPCSpawnEvent;
import net.jitse.npclib.events.trigger.TriggerType;
import net.jitse.npclib.skin.Skin;
package net.jitse.npclib.internal;
import com.mojang.authlib.GameProfile;
import com.mojang.authlib.properties.Property;
import net.jitse.npclib.NPCLib;
import net.jitse.npclib.api.NPC;
import net.jitse.npclib.api.events.NPCHideEvent;
import net.jitse.npclib.api.events.NPCShowEvent;
import net.jitse.npclib.api.skin.Skin;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import org.bukkit.plugin.java.JavaPlugin;
import org.bukkit.util.Vector;
import java.util.*;
/**
* @author Jitse Boonstra
*/
public abstract class NPC implements PacketHandler, ActionHandler {
public abstract class SimpleNPC implements NPC, PacketHandler {
protected final UUID uuid = UUID.randomUUID();
// Below was previously = (int) Math.ceil(Math.random() * 100000) + 100000 (new is experimental).
protected final int entityId = Integer.MAX_VALUE - NPCManager.getAllNPCs().size();
protected final String name = uuid.toString().replace("-", "").substring(0, 10);
protected final GameProfile gameProfile = new GameProfile(uuid, name);
protected double cosFOV = Math.cos(Math.toRadians(60));
protected String name = uuid.toString().replace("-", "").substring(0, 10);
protected final List<String> lines;
private final Set<UUID> shown = new HashSet<>();
private final Set<UUID> autoHidden = new HashSet<>();
protected final double autoHideDistance;
protected final Skin skin;
protected final List<String> lines;
protected double cosFOV = Math.cos(Math.toRadians(60));
protected JavaPlugin plugin;
protected GameProfileWrapper gameProfile;
protected NPCLib instance;
protected Location location;
protected Skin skin;
public NPC(JavaPlugin plugin, Skin skin, double autoHideDistance, List<String> lines) {
this.plugin = plugin;
this.skin = skin;
this.autoHideDistance = autoHideDistance;
public SimpleNPC(NPCLib instance, List<String> lines) {
this.instance = instance;
this.lines = lines == null ? Collections.emptyList() : lines;
NPCManager.add(this);
}
protected GameProfileWrapper generateGameProfile(UUID uuid, String name) {
GameProfileWrapper gameProfile = new GameProfileWrapper(uuid, name);
protected GameProfile generateGameProfile(UUID uuid, String name) {
GameProfile gameProfile = new GameProfile(uuid, name);
if (skin != null) {
gameProfile.addSkin(skin);
gameProfile.getProperties().get("textures").clear();
gameProfile.getProperties().get("textures").add(new Property(skin.getValue(), skin.getSignature()));
}
return gameProfile;
}
public NPCLib getInstance() {
return instance;
}
@Override
public String getId() {
return name;
}
@Override
public NPC setSkin(Skin skin) {
this.skin = skin;
return this;
}
@Override
public void destroy() {
destroy(true);
}
@ -83,7 +92,7 @@ public abstract class NPC implements PacketHandler, ActionHandler {
}
public void setFOV(double fov) {
this.cosFOV = Math.cos(Math.toRadians(60));
this.cosFOV = Math.cos(Math.toRadians(fov));
}
public Set<UUID> getShown() {
@ -94,35 +103,40 @@ public abstract class NPC implements PacketHandler, ActionHandler {
return autoHidden;
}
@Override
public Location getLocation() {
return location;
}
public double getAutoHideDistance() {
return autoHideDistance;
}
public int getEntityId() {
return entityId;
}
public boolean isActuallyShown(Player player) {
@Override
public boolean isShown(Player player) {
return shown.contains(player.getUniqueId()) && !autoHidden.contains(player.getUniqueId());
}
public void create(Location location) {
@Override
public NPC setLocation(Location location) {
this.location = location;
return this;
}
@Override
public NPC create() {
createPackets();
return this;
}
@Override
public void show(Player player) {
show(player, false);
}
public void show(Player player, boolean auto) {
NPCSpawnEvent event = new NPCSpawnEvent(this, player, auto ? TriggerType.AUTOMATIC : TriggerType.MANUAL);
plugin.getServer().getPluginManager().callEvent(event);
NPCShowEvent event = new NPCShowEvent(this, player, auto);
Bukkit.getServer().getPluginManager().callEvent(event);
if (event.isCancelled()) {
return;
}
@ -139,7 +153,7 @@ public abstract class NPC implements PacketHandler, ActionHandler {
if (auto) {
sendShowPackets(player);
} else {
if (isActuallyShown(player)) {
if (isShown(player)) {
throw new RuntimeException("Cannot call show method twice.");
}
@ -149,7 +163,8 @@ public abstract class NPC implements PacketHandler, ActionHandler {
shown.add(player.getUniqueId());
if (player.getWorld().equals(location.getWorld()) && player.getLocation().distance(location) <= autoHideDistance) {
if (player.getWorld().equals(location.getWorld()) && player.getLocation().distance(location)
<= instance.getAutoHideDistance()) {
sendShowPackets(player);
} else {
autoHidden.add(player.getUniqueId());
@ -162,13 +177,14 @@ public abstract class NPC implements PacketHandler, ActionHandler {
return dir.dot(player.getLocation().getDirection()) >= cosFOV;
}
@Override
public void hide(Player player) {
hide(player, false, true);
}
public void hide(Player player, boolean auto, boolean scheduler) {
NPCDestroyEvent event = new NPCDestroyEvent(this, player, auto ? TriggerType.AUTOMATIC : TriggerType.MANUAL);
plugin.getServer().getPluginManager().callEvent(event);
NPCHideEvent event = new NPCHideEvent(this, player, auto);
Bukkit.getServer().getPluginManager().callEvent(event);
if (event.isCancelled()) {
return;
}
@ -182,7 +198,8 @@ public abstract class NPC implements PacketHandler, ActionHandler {
shown.remove(player.getUniqueId());
if (player.getWorld().equals(location.getWorld()) && player.getLocation().distance(location) <= autoHideDistance) {
if (player.getWorld().equals(location.getWorld()) && player.getLocation().distance(location)
<= instance.getAutoHideDistance()) {
sendHidePackets(player, scheduler);
} else {
autoHidden.remove(player.getUniqueId());

@ -4,8 +4,9 @@
package net.jitse.npclib.listeners;
import net.jitse.npclib.NPCManager;
import net.jitse.npclib.api.NPC;
import net.jitse.npclib.NPCLib;
import net.jitse.npclib.internal.NPCManager;
import net.jitse.npclib.internal.SimpleNPC;
import org.bukkit.Bukkit;
import org.bukkit.Chunk;
import org.bukkit.entity.Player;
@ -21,11 +22,17 @@ import java.util.UUID;
*/
public class ChunkListener implements Listener {
private final NPCLib instance;
public ChunkListener(NPCLib instance) {
this.instance = instance;
}
@EventHandler
public void onChunkUnload(ChunkUnloadEvent event) {
Chunk chunk = event.getChunk();
for (NPC npc : NPCManager.getAllNPCs()) {
for (SimpleNPC npc : NPCManager.getAllNPCs()) {
Chunk npcChunk = npc.getLocation().getChunk();
if (chunk.equals(npcChunk)) {
@ -48,7 +55,7 @@ public class ChunkListener implements Listener {
public void onChunkLoad(ChunkLoadEvent event) {
Chunk chunk = event.getChunk();
for (NPC npc : NPCManager.getAllNPCs()) {
for (SimpleNPC npc : NPCManager.getAllNPCs()) {
Chunk npcChunk = npc.getLocation().getChunk();
if (chunk.equals(npcChunk)) {
@ -66,7 +73,7 @@ public class ChunkListener implements Listener {
continue; // Player and NPC are not in the same world.
}
double hideDistance = npc.getAutoHideDistance();
double hideDistance = instance.getAutoHideDistance();
double distanceSquared = player.getLocation().distanceSquared(npc.getLocation());
boolean inRange = distanceSquared <= (hideDistance * hideDistance) || distanceSquared <= (Bukkit.getViewDistance() << 4);

@ -6,10 +6,10 @@ package net.jitse.npclib.listeners;
import com.comphenix.tinyprotocol.Reflection;
import com.comphenix.tinyprotocol.TinyProtocol;
import net.jitse.npclib.NPCManager;
import net.jitse.npclib.api.NPC;
import net.jitse.npclib.events.NPCInteractEvent;
import net.jitse.npclib.events.click.ClickType;
import net.jitse.npclib.NPCLib;
import net.jitse.npclib.api.events.NPCInteractEvent;
import net.jitse.npclib.internal.NPCManager;
import net.jitse.npclib.internal.SimpleNPC;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
@ -35,10 +35,10 @@ public class PacketListener {
private Plugin plugin;
public void start(Plugin plugin) {
this.plugin = plugin;
public void start(NPCLib instance) {
this.plugin = instance.getPlugin();
new TinyProtocol(plugin) {
new TinyProtocol(instance) {
@Override
public Object onPacketInAsync(Player player, Object packet) {
@ -49,8 +49,8 @@ public class PacketListener {
private boolean handleInteractPacket(Player player, Object packet) {
if (packetPlayInUseEntityClazz.isInstance(packet)) {
NPC npc = NPCManager.getAllNPCs().stream().filter(
check -> check.isActuallyShown(player) && check.getEntityId() == (int) entityIdField.get(packet))
SimpleNPC npc = NPCManager.getAllNPCs().stream().filter(
check -> check.isShown(player) && check.getEntityId() == (int) entityIdField.get(packet))
.findFirst().orElse(null);
if (npc == null) {
@ -62,10 +62,11 @@ public class PacketListener {
return false;
}
ClickType clickType = actionField.get(packet).toString()
.equals("ATTACK") ? ClickType.LEFT_CLICK : ClickType.RIGHT_CLICK;
NPCInteractEvent.ClickType clickType = actionField.get(packet).toString().equals("ATTACK")
? NPCInteractEvent.ClickType.LEFT_CLICK : NPCInteractEvent.ClickType.RIGHT_CLICK;
Bukkit.getPluginManager().callEvent(new NPCInteractEvent(player, clickType, npc));
Bukkit.getScheduler().runTask(plugin, () ->
Bukkit.getPluginManager().callEvent(new NPCInteractEvent(player, clickType, npc)));
UUID uuid = player.getUniqueId();
delay.add(uuid);

@ -4,8 +4,9 @@
package net.jitse.npclib.listeners;
import net.jitse.npclib.NPCManager;
import net.jitse.npclib.api.NPC;
import net.jitse.npclib.NPCLib;
import net.jitse.npclib.internal.NPCManager;
import net.jitse.npclib.internal.SimpleNPC;
import org.bukkit.Bukkit;
import org.bukkit.World;
import org.bukkit.entity.Player;
@ -21,10 +22,16 @@ import org.bukkit.event.player.PlayerTeleportEvent;
*/
public class PlayerListener implements Listener {
private final NPCLib instance;
public PlayerListener(NPCLib instance) {
this.instance = instance;
}
@EventHandler
public void onPlayerQuit(PlayerQuitEvent event) {
Player player = event.getPlayer();
for (NPC npc : NPCManager.getAllNPCs()) {
for (SimpleNPC npc : NPCManager.getAllNPCs()) {
npc.getAutoHidden().remove(player.getUniqueId());
// Don't need to use NPC#hide since the entity is not registered in the NMS server.
@ -38,7 +45,7 @@ public class PlayerListener implements Listener {
World from = event.getFrom();
// The PlayerTeleportEvent is call, and will handle visibility in the new world.
for (NPC npc : NPCManager.getAllNPCs()) {
for (SimpleNPC npc : NPCManager.getAllNPCs()) {
if (npc.getLocation().getWorld().equals(from)) {
if (!npc.getAutoHidden().contains(player.getUniqueId())) {
npc.getAutoHidden().add(player.getUniqueId());
@ -60,7 +67,7 @@ public class PlayerListener implements Listener {
private void handleMove(Player player) {
World world = player.getWorld();
for (NPC npc : NPCManager.getAllNPCs()) {
for (SimpleNPC npc : NPCManager.getAllNPCs()) {
if (!npc.getShown().contains(player.getUniqueId())) {
continue; // NPC was never supposed to be shown to the player.
}
@ -71,7 +78,7 @@ public class PlayerListener implements Listener {
// If Bukkit doesn't track the NPC entity anymore, bypass the hiding distance variable.
// This will cause issues otherwise (e.g. custom skin disappearing).
double hideDistance = npc.getAutoHideDistance();
double hideDistance = instance.getAutoHideDistance();
double distanceSquared = player.getLocation().distanceSquared(npc.getLocation());
boolean inRange = distanceSquared <= (Math.pow(hideDistance, 2))
&& distanceSquared <= (Math.pow(Bukkit.getViewDistance() << 4, 2));

@ -1,51 +0,0 @@
<?xml version="1.0"?>
<project
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>net.jitse</groupId>
<artifactId>npclib</artifactId>
<version>1.5-SNAPSHOT</version>
</parent>
<artifactId>npclib-commons</artifactId>
<!--
<build>
<resources>
<resource>
<targetPath>.</targetPath>
<directory>${basedir}/src/main/java/net/jitse/npclib</directory>
<filtering>true</filtering>
<includes>
<include>NPCLib.java</include>
</includes>
</resource>
</resources>
</build>
-->
<repositories>
<repository>
<id>spigot-repo</id>
<url>https://hub.spigotmc.org/nexus/content/repositories/snapshots/</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot-api</artifactId>
<version>1.13.2-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.33.Final</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>

@ -1,120 +0,0 @@
/*
* Copyright (c) 2018 Jitse Boonstra
*/
package net.jitse.npclib;
import net.jitse.npclib.api.NPC;
import net.jitse.npclib.listeners.ChunkListener;
import net.jitse.npclib.listeners.PacketListener;
import net.jitse.npclib.listeners.PlayerListener;
import net.jitse.npclib.logging.NPCLibLogger;
import net.jitse.npclib.skin.Skin;
import org.bukkit.plugin.PluginManager;
import org.bukkit.plugin.java.JavaPlugin;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* @author Jitse Boonstra
*/
public class NPCLib {
private final JavaPlugin plugin;
private final Class<?> npcClass;
private Logger logger;
public NPCLib(JavaPlugin plugin) {
this.plugin = plugin;
this.logger = new NPCLibLogger(plugin);
// TODO: Change this variable to a dynamic variable (maven file filtering?).
// logger.info("Initiating NPCLib v1.4");
String versionName = plugin.getServer().getClass().getPackage().getName().split("\\.")[3];
Class<?> npcClass = null;
try {
npcClass = Class.forName("net.jitse.npclib.nms." + versionName + ".NPC_" + versionName);
} catch (ClassNotFoundException exception) {
// Version not supported, error below.
}
this.npcClass = npcClass;
if (npcClass == null) {
logger.log(Level.SEVERE, "Failed to initiate. Your server's version ("
+ versionName + ") is not supported");
return;
}
logger.info("Enabled for MC " + versionName);
registerInternal();
}
private void registerInternal() {
PluginManager pluginManager = plugin.getServer().getPluginManager();
pluginManager.registerEvents(new PlayerListener(), plugin);
pluginManager.registerEvents(new ChunkListener(), plugin);
// Boot the according packet listener.
new PacketListener().start(plugin);
}
/**
* Create a new non-player character (NPC).
*
* @param skin The skin you want the NPC to have.
* @param autoHideDistance Distance from where you want to NPC to hide from the player (50 recommended).
* @param lines The text you want to sendShowPackets above the NPC (null = no text).
* @return The NPC object you may use to sendShowPackets it to players.
*/
public NPC createNPC(Skin skin, double autoHideDistance, List<String> lines) {
try {
return (NPC) npcClass.getConstructors()[0].newInstance(plugin, skin, autoHideDistance, lines);