Add Maven !

This commit is contained in:
MrMicky
2018-04-26 13:52:49 +02:00
parent f74d57962a
commit c9a58ccd48
72 changed files with 4557 additions and 0 deletions
@@ -0,0 +1,115 @@
/*
* 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.skin.Skin;
import org.bukkit.ChatColor;
import org.bukkit.Server;
import org.bukkit.plugin.PluginManager;
import org.bukkit.plugin.java.JavaPlugin;
import java.util.List;
/**
* @author Jitse Boonstra
*/
public class NPCLib {
private final Server server;
private final JavaPlugin plugin;
private final Class<?> npcClass;
public NPCLib(JavaPlugin plugin) {
this.plugin = plugin;
this.server = plugin.getServer();
String versionName = server.getClass().getPackage().getName().split("\\.")[3];
Class<?> npcClass = null;
try {
npcClass = Class.forName("net.jitse.npclib.nms." + versionName + ".NPC_" + versionName);
} catch (ClassNotFoundException e) {
// Version not supported, error below
}
this.npcClass = npcClass;
if (npcClass == null) {
server.getConsoleSender().sendMessage(ChatColor.RED + "NPCLib failed to initiate. Your server's version ("
+ versionName + ") is not supported.");
return;
}
server.getConsoleSender().sendMessage(ChatColor.BLUE + "[NPCLib] " + ChatColor.WHITE + "Enabled for version " + versionName + ".");
registerInternal();
}
private void registerInternal() {
PluginManager pluginManager = server.getPluginManager();
pluginManager.registerEvents(new PlayerListener(), plugin);
pluginManager.registerEvents(new ChunkListener(), plugin);
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);
} catch (Exception exception) {
server.getConsoleSender().sendMessage(ChatColor.RED + "NPCLib failed to create NPC. Please report this stacktrace:");
exception.printStackTrace();
}
return null;
}
/**
* Create a new non-player character (NPC).
*
* @param skin The skin you want the NPC to have.
* @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, List<String> lines) {
return createNPC(skin, 50, lines);
}
/**
* Create a new non-player character (NPC).
*
* @param skin The skin you want the NPC to have.
* @return The NPC object you may use to sendShowPackets it to players.
*/
public NPC createNPC(Skin skin) {
return createNPC(skin, 50, 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, 50, null);
}
}
@@ -0,0 +1,35 @@
/*
* Copyright (c) 2018 Jitse Boonstra
*/
package net.jitse.npclib;
import net.jitse.npclib.api.NPC;
import java.util.HashSet;
import java.util.Set;
/**
* @author Jitse Boonstra
*/
public final class NPCManager {
private static Set<NPC> npcs = new HashSet<>();
public static Set<NPC> getAllNPCs() {
return npcs;
}
public static void add(NPC npc) {
npcs.add(npc);
}
public static void remove(NPC npc) {
npcs.remove(npc);
}
private NPCManager() {
throw new SecurityException("You cannot initialize this class.");
}
}
@@ -0,0 +1,165 @@
/*
* Copyright (c) 2018 Jitse Boonstra
*/
package net.jitse.npclib.api;
import com.mojang.authlib.GameProfile;
import com.mojang.authlib.properties.Property;
import net.jitse.npclib.NPCManager;
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;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import org.bukkit.plugin.java.JavaPlugin;
import java.util.*;
/**
* @author Jitse Boonstra
*/
public abstract class NPC {
protected final UUID uuid = UUID.randomUUID();
protected final String name = uuid.toString().replace("-", "").substring(0, 10);
protected final int entityId = (int) Math.ceil(Math.random() * 100000) + 100000;
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 JavaPlugin plugin;
protected GameProfile gameProfile;
protected Location location;
public NPC(JavaPlugin plugin, Skin skin, double autoHideDistance, List<String> lines) {
this.plugin = plugin;
this.skin = skin;
this.autoHideDistance = autoHideDistance;
this.lines = (lines == null ? Collections.emptyList() : lines);
NPCManager.add(this);
}
protected GameProfile generateGameProfile(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;
}
public void destroy() {
NPCManager.remove(this);
// Destroy NPC for every player that is still seeing it.
for (UUID uuid : shown) {
if (autoHidden.contains(uuid)) {
continue;
}
hide(Bukkit.getPlayer(uuid), true);
}
}
public Set<UUID> getShown() {
return shown;
}
public Set<UUID> getAutoHidden() {
return autoHidden;
}
public Location getLocation() {
return location;
}
public double getAutoHideDistance() {
return autoHideDistance;
}
public int getEntityId() {
return entityId;
}
public boolean isActuallyShown(Player player) {
return shown.contains(player.getUniqueId()) && !autoHidden.contains(player.getUniqueId());
}
// Generate packets.
public abstract void create(Location location);
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);
if (event.isCancelled()) {
return;
}
if (auto) {
sendShowPackets(player);
} else {
if (shown.contains(player.getUniqueId())) {
throw new RuntimeException("Cannot call show method twice.");
}
shown.add(player.getUniqueId());
if (player.getLocation().distance(location) <= autoHideDistance) {
sendShowPackets(player);
} else {
if (!autoHidden.contains(player.getUniqueId())) {
autoHidden.add(player.getUniqueId());
}
}
}
}
// Internal method.
protected abstract void sendShowPackets(Player player);
public void hide(Player player) {
hide(player, false);
}
public void hide(Player player, boolean auto) {
NPCDestroyEvent event = new NPCDestroyEvent(this, player, auto ? TriggerType.AUTOMATIC : TriggerType.MANUAL);
plugin.getServer().getPluginManager().callEvent(event);
if (event.isCancelled()) {
return;
}
if (auto) {
sendHidePackets(player);
} else {
if (!shown.contains(player.getUniqueId())) {
throw new RuntimeException("Cannot call hide method without calling NPC#show.");
}
shown.remove(player.getUniqueId());
if (player.getWorld().equals(location.getWorld()) && player.getLocation().distance(location) <= autoHideDistance) {
sendHidePackets(player);
} else {
if (autoHidden.contains(player.getUniqueId())) {
autoHidden.remove(player.getUniqueId());
}
}
}
}
// Internal method.
protected abstract void sendHidePackets(Player player);
}
@@ -0,0 +1,63 @@
/*
* Copyright (c) 2018 Jitse Boonstra
*/
package net.jitse.npclib.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;
import org.bukkit.event.HandlerList;
/**
* @author Jitse Boonstra
*/
public class NPCDestroyEvent extends Event implements Cancellable {
private static final HandlerList handlers = new HandlerList();
private boolean cancelled = false;
private final NPC npc;
private final Player player;
private final TriggerType trigger;
public NPCDestroyEvent(NPC npc, Player player, TriggerType trigger) {
this.npc = npc;
this.player = player;
this.trigger = trigger;
}
@Override
public void setCancelled(boolean cancelled) {
this.cancelled = cancelled;
}
public NPC getNPC() {
return npc;
}
public Player getPlayer() {
return player;
}
public TriggerType getTrigger() {
return trigger;
}
@Override
public boolean isCancelled() {
return cancelled;
}
public HandlerList getHandlers() {
return handlers;
}
public static HandlerList getHandlerList() {
return handlers;
}
}
@@ -0,0 +1,49 @@
/*
* Copyright (c) 2018 Jitse Boonstra
*/
package net.jitse.npclib.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;
/**
* @author Jitse Boonstra
*/
public class NPCInteractEvent extends Event {
private static final HandlerList handlers = new HandlerList();
private final Player player;
private final ClickType clickType;
private final NPC npc;
public NPCInteractEvent(Player player, ClickType clickType, NPC npc) {
this.player = player;
this.clickType = clickType;
this.npc = npc;
}
public Player getWhoClicked() {
return this.player;
}
public ClickType getClickType() {
return this.clickType;
}
public NPC getNPC() {
return this.npc;
}
public HandlerList getHandlers() {
return handlers;
}
public static HandlerList getHandlerList() {
return handlers;
}
}
@@ -0,0 +1,62 @@
/*
* Copyright (c) 2018 Jitse Boonstra
*/
package net.jitse.npclib.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;
import org.bukkit.event.HandlerList;
/**
* @author Jitse Boonstra
*/
public class NPCSpawnEvent extends Event implements Cancellable {
private static final HandlerList handlers = new HandlerList();
private boolean cancelled = false;
private final NPC npc;
private final Player player;
private final TriggerType trigger;
public NPCSpawnEvent(NPC npc, Player player, TriggerType trigger) {
this.npc = npc;
this.player = player;
this.trigger = trigger;
}
@Override
public void setCancelled(boolean cancelled) {
this.cancelled = cancelled;
}
public NPC getNPC() {
return npc;
}
public Player getPlayer() {
return player;
}
public TriggerType getTrigger() {
return trigger;
}
@Override
public boolean isCancelled() {
return cancelled;
}
public HandlerList getHandlers() {
return handlers;
}
public static HandlerList getHandlerList() {
return handlers;
}
}
@@ -0,0 +1,13 @@
/*
* Copyright (c) 2018 Jitse Boonstra
*/
package net.jitse.npclib.events.click;
/**
* @author Jitse Boonstra
*/
public enum ClickType {
LEFT_CLICK, RIGHT_CLICK
}
@@ -0,0 +1,6 @@
package net.jitse.npclib.events.trigger;
public enum TriggerType {
MANUAL, AUTOMATIC
}
@@ -0,0 +1,81 @@
/*
* Copyright (c) 2018 Jitse Boonstra
*/
package net.jitse.npclib.listeners;
import net.jitse.npclib.NPCManager;
import net.jitse.npclib.api.NPC;
import org.bukkit.Bukkit;
import org.bukkit.Chunk;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.world.ChunkLoadEvent;
import org.bukkit.event.world.ChunkUnloadEvent;
import java.util.UUID;
/**
* @author Jitse Boonstras
*/
public class ChunkListener implements Listener {
@EventHandler
public void onChunkUnload(ChunkUnloadEvent event) {
Chunk chunk = event.getChunk();
for (NPC npc : NPCManager.getAllNPCs()) {
Chunk npcChunk = npc.getLocation().getChunk();
if (chunk.equals(npcChunk)) {
// Unloaded chunk with NPC in it. Hiding it from all players currently shown to.
for (UUID uuid : npc.getShown()) {
// Safety check so it doesn't send packets if the NPC has already
// been automatically despawned by the system.
if (npc.getAutoHidden().contains(uuid)) {
continue;
}
npc.hide(Bukkit.getPlayer(uuid), true);
}
}
}
}
@EventHandler
public void onChunkLoad(ChunkLoadEvent event) {
Chunk chunk = event.getChunk();
for (NPC npc : NPCManager.getAllNPCs()) {
Chunk npcChunk = npc.getLocation().getChunk();
if (chunk.equals(npcChunk)) {
// Loaded chunk with NPC in it. Showing it to the players again.
for (UUID uuid : npc.getShown()) {
// Make sure not to respawn a not-hidden NPC.
if (!npc.getAutoHidden().contains(uuid)) {
continue;
}
Player player = Bukkit.getPlayer(uuid);
if (!npcChunk.getWorld().equals(player.getWorld())) {
continue; // Player and NPC are not in the same world.
}
double hideDistance = npc.getAutoHideDistance();
double distanceSquared = player.getLocation().distanceSquared(npc.getLocation());
boolean inRange = distanceSquared <= (hideDistance * hideDistance) || distanceSquared <= (Bukkit.getViewDistance() << 4);
// Show the NPC (if in range).
if (inRange) {
npc.show(Bukkit.getPlayer(uuid), true);
}
}
}
}
}
}
@@ -0,0 +1,72 @@
/*
* Copyright (c) 2018 Jitse Boonstra
*/
package net.jitse.npclib.listeners;
import com.comphenix.tinyprotocol.Reflection;
import com.comphenix.tinyprotocol.TinyProtocol;
import io.netty.channel.Channel;
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 org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.plugin.java.JavaPlugin;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
/**
* @author Jitse Boonstra
*/
public class PacketListener {
// Classes:
private final Class<?> packetPlayInUseEntityClazz = Reflection.getMinecraftClass("PacketPlayInUseEntity");
// Fields:
private final Reflection.FieldAccessor entityIdField = Reflection.getField(packetPlayInUseEntityClazz, "a", int.class);
private final Reflection.FieldAccessor actionField = Reflection.getField(packetPlayInUseEntityClazz, "action", Object.class);
// Prevent players from clicking at very high speeds.
private final Set<UUID> delay = new HashSet<>();
public void start(JavaPlugin plugin) {
new TinyProtocol(plugin) {
@Override
public Object onPacketInAsync(Player player, Channel channel, Object packet) {
if (packetPlayInUseEntityClazz.isInstance(packet)) {
NPC npc = NPCManager.getAllNPCs().stream().filter(
check -> check.isActuallyShown(player) && check.getEntityId() == (int) entityIdField.get(packet))
.findFirst().orElse(null);
if (npc == null) {
// Default player, not doing magic with the packet.
return super.onPacketInAsync(player, channel, packet);
}
if (delay.contains(player.getUniqueId())) {
return null;
}
ClickType clickType = actionField.get(packet).toString()
.equals("ATTACK") ? ClickType.LEFT_CLICK : ClickType.RIGHT_CLICK;
Bukkit.getPluginManager().callEvent(new NPCInteractEvent(player, clickType, npc));
UUID uuid = player.getUniqueId();
delay.add(uuid);
Bukkit.getScheduler().runTaskLater(plugin, () -> delay.remove(uuid), 1);
return null;
} else {
return super.onPacketInAsync(player, channel, packet);
}
}
};
}
}
@@ -0,0 +1,104 @@
/*
* Copyright (c) 2018 Jitse Boonstra
*/
package net.jitse.npclib.listeners;
import net.jitse.npclib.NPCManager;
import net.jitse.npclib.api.NPC;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerChangedWorldEvent;
import org.bukkit.event.player.PlayerMoveEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.event.player.PlayerTeleportEvent;
/**
* @author Jitse Boonstra
*/
public class PlayerListener implements Listener {
@EventHandler
public void onPlayerQuit(PlayerQuitEvent event) {
Player player = event.getPlayer();
for (NPC npc : NPCManager.getAllNPCs()) {
if (npc.getAutoHidden().contains(player.getUniqueId())) {
npc.getAutoHidden().remove(player.getUniqueId());
}
// Don't need to use NPC#hide since the entity is not registered in the NMS server.
if (npc.getShown().contains(player.getUniqueId())) {
npc.getShown().remove(player.getUniqueId());
}
}
}
@EventHandler
public void onPlayerChangedWorld(PlayerChangedWorldEvent event) {
Player player = event.getPlayer();
World from = event.getFrom();
// The PlayerTeleportEvent is call, and will handle visibility in the new world.
for (NPC npc : NPCManager.getAllNPCs()) {
if (npc.getLocation().getWorld().equals(from)) {
if (!npc.getAutoHidden().contains(player.getUniqueId())) {
npc.getAutoHidden().add(player.getUniqueId());
npc.hide(player, true);
}
}
}
}
@EventHandler
public void onPlayerMove(PlayerMoveEvent event) {
Location from = event.getFrom();
Location to = event.getTo();
if (from.getX() == to.getX() && from.getY() == to.getY() && from.getZ() == to.getZ()) {
return;
}
handleMove(event.getPlayer());
}
@EventHandler
public void onPlayerTeleport(PlayerTeleportEvent event) {
handleMove(event.getPlayer());
}
private void handleMove(Player player) {
World world = player.getWorld();
for (NPC npc : NPCManager.getAllNPCs()) {
if (!npc.getShown().contains(player.getUniqueId())) {
continue; // NPC was never supposed to be shown to the player.
}
if (!npc.getLocation().getWorld().equals(world)) {
continue; // NPC is not in the same world.
}
// 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 distanceSquared = player.getLocation().distanceSquared(npc.getLocation());
boolean inRange = distanceSquared <= (hideDistance * hideDistance) || distanceSquared <= (Bukkit.getViewDistance() << 4);
if (npc.getAutoHidden().contains(player.getUniqueId())) {
// Check if the player and NPC are within the range to sendShowPackets it again.
if (inRange) {
npc.show(player, true);
npc.getAutoHidden().remove(player.getUniqueId());
}
} else {
// Check if the player and NPC are out of range to sendHidePackets it.
if (!inRange) {
npc.hide(player, true);
npc.getAutoHidden().add(player.getUniqueId());
}
}
}
}
}
@@ -0,0 +1,148 @@
/*
* Copyright (c) 2018 Jitse Boonstra
*/
package net.jitse.npclib.nms.holograms;
import com.comphenix.tinyprotocol.Reflection;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.entity.Player;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* @author Jitse Boonstra
*/
public class Hologram {
private final double delta = 0.3;
private List<Object> armorStands = new ArrayList<>();
private Set<Object> spawnPackets = new HashSet<>();
private Set<Object> destroyPackets = new HashSet<>();
// Classes:
private static final Class<?> ENTITY_ARMOR_STAND_CLAZZ = Reflection.getMinecraftClass("EntityArmorStand");
private static final Class<?> ENTITY_LIVING_CLAZZ = Reflection.getMinecraftClass("EntityLiving");
private static final Class<?> ENTITY_CLAZZ = Reflection.getMinecraftClass("Entity");
private static final Class<?> CRAFT_BUKKIT_CLASS = Reflection.getCraftBukkitClass("CraftWorld");
private static final Class<?> CRAFT_PLAYER_CLAZZ = Reflection.getCraftBukkitClass("entity.CraftPlayer");
private static final Class<?> PACKET_PLAY_OUT_SPAWN_ENTITY_LIVING_CLAZZ = Reflection.getMinecraftClass(
"PacketPlayOutSpawnEntityLiving");
private static final Class<?> PACKET_PLAY_OUT_ENTITY_DESTROY_CLAZZ = Reflection.getMinecraftClass(
"PacketPlayOutEntityDestroy");
private static final Class<?> ENTITY_PLAYER_CLAZZ = Reflection.getMinecraftClass("EntityPlayer");
private static final Class<?> PLAYER_CONNECTION_CLAZZ = Reflection.getMinecraftClass("PlayerConnection");
private static final Class<?> PACKET_CLAZZ = Reflection.getMinecraftClass("Packet");
// Constructors:
private static final Reflection.ConstructorInvoker PACKET_PLAY_OUT_SPAWN_ENTITY_LIVING_CONSTRUCTOR = Reflection
.getConstructor(PACKET_PLAY_OUT_SPAWN_ENTITY_LIVING_CLAZZ, ENTITY_LIVING_CLAZZ);
private static final Reflection.ConstructorInvoker PACKET_PLAY_OUT_ENTITY_DESTROY_CONSTRUCTOR = Reflection
.getConstructor(PACKET_PLAY_OUT_ENTITY_DESTROY_CLAZZ, int[].class);
// Fields:
private static final Reflection.FieldAccessor playerConnectionField = Reflection.getField(ENTITY_PLAYER_CLAZZ,
"playerConnection", PLAYER_CONNECTION_CLAZZ);
// Methods:
private static final Reflection.MethodInvoker SET_LOCATION_METHOD = Reflection.getMethod(ENTITY_ARMOR_STAND_CLAZZ,
"setLocation", double.class, double.class, double.class, float.class, float.class);
private static final Reflection.MethodInvoker SET_CUSTOM_NAME_METHOD = Reflection.getMethod(ENTITY_ARMOR_STAND_CLAZZ,
"setCustomName", String.class);
private static final Reflection.MethodInvoker SET_CUSTOM_NAME_VISIBLE_METHOD = Reflection.getMethod(ENTITY_ARMOR_STAND_CLAZZ,
"setCustomNameVisible", boolean.class);
private static final Reflection.MethodInvoker SET_SMALL_METHOD = Reflection.getMethod(ENTITY_ARMOR_STAND_CLAZZ,
"setSmall", boolean.class);
private static final Reflection.MethodInvoker SET_INVISIBLE_METHOD = Reflection.getMethod(ENTITY_ARMOR_STAND_CLAZZ,
"setInvisible", boolean.class);
private static final Reflection.MethodInvoker SET_BASE_PLATE_METHOD = Reflection.getMethod(ENTITY_ARMOR_STAND_CLAZZ,
"setBasePlate", boolean.class);
private static final Reflection.MethodInvoker SET_ARMS_METHOD = Reflection.getMethod(ENTITY_ARMOR_STAND_CLAZZ,
"setArms", boolean.class);
private static final Reflection.MethodInvoker PLAYER_GET_HANDLE_METHOD = Reflection.getMethod(CRAFT_PLAYER_CLAZZ,
"getHandle");
private static final Reflection.MethodInvoker SEND_PACKET_METHOD = Reflection.getMethod(PLAYER_CONNECTION_CLAZZ,
"sendPacket", PACKET_CLAZZ);
private static final Reflection.MethodInvoker GET_ID_METHOD = Reflection.getMethod(ENTITY_ARMOR_STAND_CLAZZ,
"getId");
private final Location start;
private final List<String> lines;
private final Object worldServer;
public Hologram(Location location, List<String> lines) {
this.start = location;
this.lines = lines;
this.worldServer = Reflection.getMethod(CRAFT_BUKKIT_CLASS, "getHandle")
.invoke(CRAFT_BUKKIT_CLASS.cast(location.getWorld()));
}
public void generatePackets(boolean above1_9_r2) {
Reflection.MethodInvoker gravityMethod = (above1_9_r2 ? Reflection.getMethod(ENTITY_CLAZZ,
"setNoGravity", boolean.class) : Reflection.getMethod(ENTITY_ARMOR_STAND_CLAZZ,
"setGravity", boolean.class));
Location location = start.clone().add(0, delta * lines.size(), 0);
Class<?> worldClass = worldServer.getClass().getSuperclass();
if (start.getWorld().getEnvironment() != World.Environment.NORMAL) {
worldClass = worldClass.getSuperclass();
}
Reflection.ConstructorInvoker entityArmorStandConstructor = Reflection
.getConstructor(ENTITY_ARMOR_STAND_CLAZZ, worldClass);
for (String line : lines) {
Object entityArmorStand = entityArmorStandConstructor.invoke(worldServer);
SET_LOCATION_METHOD.invoke(entityArmorStand, location.getX(), location.getY(), location.getZ(), 0, 0);
SET_CUSTOM_NAME_METHOD.invoke(entityArmorStand, line);
SET_CUSTOM_NAME_VISIBLE_METHOD.invoke(entityArmorStand, true);
gravityMethod.invoke(entityArmorStand, above1_9_r2);
SET_SMALL_METHOD.invoke(entityArmorStand, true);
SET_INVISIBLE_METHOD.invoke(entityArmorStand, true);
SET_BASE_PLATE_METHOD.invoke(entityArmorStand, false);
SET_ARMS_METHOD.invoke(entityArmorStand, false);
location.subtract(0, delta, 0);
if (line.isEmpty()) {
continue;
}
armorStands.add(entityArmorStand);
Object spawnPacket = PACKET_PLAY_OUT_SPAWN_ENTITY_LIVING_CONSTRUCTOR.invoke(entityArmorStand);
spawnPackets.add(spawnPacket);
Object destroyPacket = PACKET_PLAY_OUT_ENTITY_DESTROY_CONSTRUCTOR
.invoke(new int[]{(int) GET_ID_METHOD.invoke(entityArmorStand)});
destroyPackets.add(destroyPacket);
}
}
public void spawn(Player player) {
Object playerConnection = playerConnectionField.get(PLAYER_GET_HANDLE_METHOD
.invoke(CRAFT_PLAYER_CLAZZ.cast(player)));
for (Object packet : spawnPackets) {
SEND_PACKET_METHOD.invoke(playerConnection, packet);
}
}
public void destroy(Player player) {
Object playerConnection = playerConnectionField.get(PLAYER_GET_HANDLE_METHOD
.invoke(CRAFT_PLAYER_CLAZZ.cast(player)));
for (Object packet : destroyPackets) {
SEND_PACKET_METHOD.invoke(playerConnection, packet);
}
}
}
@@ -0,0 +1,63 @@
/*
* Copyright (c) 2018 Jitse Boonstra
*/
package net.jitse.npclib.skin;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Scanner;
/**
* @author Jitse Boonstra
*/
public class MineSkinFetcher {
private static final String MINESKIN_API = "https://api.mineskin.org/get/id/";
public static void fetchSkinFromIdAsync(int id, Callback callback) {
new Thread(() -> {
try {
StringBuilder builder = new StringBuilder();
HttpURLConnection httpURLConnection = (HttpURLConnection) new URL(MINESKIN_API + id).openConnection();
httpURLConnection.setRequestMethod("GET");
httpURLConnection.setDoOutput(true);
httpURLConnection.setDoInput(true);
httpURLConnection.connect();
Scanner scanner = new Scanner(httpURLConnection.getInputStream());
while (scanner.hasNextLine()) {
builder.append(scanner.nextLine());
}
scanner.close();
httpURLConnection.disconnect();
JsonObject jsonObject = (JsonObject) new JsonParser().parse(builder.toString());
JsonObject textures = jsonObject.get("data").getAsJsonObject().get("texture").getAsJsonObject();
String value = textures.get("value").getAsString();
String signature = textures.get("signature").getAsString();
callback.call(new Skin(value, signature));
} catch (IOException exception) {
Bukkit.getConsoleSender().sendMessage(ChatColor.RED + "Could not fetch skin! (Id: " + id + "). Message: " + exception.getMessage());
exception.printStackTrace();
callback.failed();
}
}).start();
}
public interface Callback {
void call(Skin skinData);
default void failed() {
}
}
}
@@ -0,0 +1,26 @@
/*
* Copyright (c) 2018 Jitse Boonstra
*/
package net.jitse.npclib.skin;
/**
* @author Jitse Boonstra
*/
public class Skin {
private final String value, signature;
public Skin(String value, String signature) {
this.value = value;
this.signature = signature;
}
public String getValue() {
return this.value;
}
public String getSignature() {
return this.signature;
}
}