@@ -150,7 +150,7 @@ public abstract class NPC {
|
|||||||
|
|
||||||
shown.remove(player.getUniqueId());
|
shown.remove(player.getUniqueId());
|
||||||
|
|
||||||
if (player.getLocation().distance(location) <= autoHideDistance) {
|
if (player.getWorld().equals(location.getWorld()) && player.getLocation().distance(location) <= autoHideDistance) {
|
||||||
sendHidePackets(player);
|
sendHidePackets(player);
|
||||||
} else {
|
} else {
|
||||||
if (autoHidden.contains(player.getUniqueId())) {
|
if (autoHidden.contains(player.getUniqueId())) {
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ public class ChunkListener implements Listener {
|
|||||||
for (NPC npc : NPCManager.getAllNPCs()) {
|
for (NPC npc : NPCManager.getAllNPCs()) {
|
||||||
Chunk npcChunk = npc.getLocation().getChunk();
|
Chunk npcChunk = npc.getLocation().getChunk();
|
||||||
|
|
||||||
if (chunk.getX() == npcChunk.getX() && chunk.getZ() == npcChunk.getZ()) {
|
if (chunk.equals(npcChunk)) {
|
||||||
// Unloaded chunk with NPC in it. Hiding it from all players currently shown to.
|
// Unloaded chunk with NPC in it. Hiding it from all players currently shown to.
|
||||||
|
|
||||||
for (UUID uuid : npc.getShown()) {
|
for (UUID uuid : npc.getShown()) {
|
||||||
@@ -51,7 +51,7 @@ public class ChunkListener implements Listener {
|
|||||||
for (NPC npc : NPCManager.getAllNPCs()) {
|
for (NPC npc : NPCManager.getAllNPCs()) {
|
||||||
Chunk npcChunk = npc.getLocation().getChunk();
|
Chunk npcChunk = npc.getLocation().getChunk();
|
||||||
|
|
||||||
if (chunk.getX() == npcChunk.getX() && chunk.getZ() == npcChunk.getZ()) {
|
if (chunk.equals(npcChunk)) {
|
||||||
// Loaded chunk with NPC in it. Showing it to the players again.
|
// Loaded chunk with NPC in it. Showing it to the players again.
|
||||||
|
|
||||||
for (UUID uuid : npc.getShown()) {
|
for (UUID uuid : npc.getShown()) {
|
||||||
@@ -62,6 +62,10 @@ public class ChunkListener implements Listener {
|
|||||||
|
|
||||||
Player player = Bukkit.getPlayer(uuid);
|
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 hideDistance = npc.getAutoHideDistance();
|
||||||
double distanceSquared = player.getLocation().distanceSquared(npc.getLocation());
|
double distanceSquared = player.getLocation().distanceSquared(npc.getLocation());
|
||||||
boolean inRange = distanceSquared <= (hideDistance * hideDistance) || distanceSquared <= (Bukkit.getViewDistance() << 4);
|
boolean inRange = distanceSquared <= (hideDistance * hideDistance) || distanceSquared <= (Bukkit.getViewDistance() << 4);
|
||||||
|
|||||||
@@ -8,9 +8,11 @@ import net.jitse.npclib.NPCManager;
|
|||||||
import net.jitse.npclib.api.NPC;
|
import net.jitse.npclib.api.NPC;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.Location;
|
import org.bukkit.Location;
|
||||||
|
import org.bukkit.World;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.event.EventHandler;
|
import org.bukkit.event.EventHandler;
|
||||||
import org.bukkit.event.Listener;
|
import org.bukkit.event.Listener;
|
||||||
|
import org.bukkit.event.player.PlayerChangedWorldEvent;
|
||||||
import org.bukkit.event.player.PlayerMoveEvent;
|
import org.bukkit.event.player.PlayerMoveEvent;
|
||||||
import org.bukkit.event.player.PlayerQuitEvent;
|
import org.bukkit.event.player.PlayerQuitEvent;
|
||||||
import org.bukkit.event.player.PlayerTeleportEvent;
|
import org.bukkit.event.player.PlayerTeleportEvent;
|
||||||
@@ -28,8 +30,25 @@ public class PlayerListener implements Listener {
|
|||||||
npc.getAutoHidden().remove(player.getUniqueId());
|
npc.getAutoHidden().remove(player.getUniqueId());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (npc.isActuallyShown(player)) {
|
// Don't need to use NPC#hide since the entity is not registered in the NMS server.
|
||||||
npc.hide(player);
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -52,11 +71,16 @@ public class PlayerListener implements Listener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void handleMove(Player player) {
|
private void handleMove(Player player) {
|
||||||
|
World world = player.getWorld();
|
||||||
for (NPC npc : NPCManager.getAllNPCs()) {
|
for (NPC npc : NPCManager.getAllNPCs()) {
|
||||||
if (!npc.getShown().contains(player.getUniqueId())) {
|
if (!npc.getShown().contains(player.getUniqueId())) {
|
||||||
continue; // NPC was never supposed to be shown to the player.
|
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.
|
// If Bukkit doesn't track the NPC entity anymore, bypass the hiding distance variable.
|
||||||
// This will cause issues otherwise (e.g. custom skin disappearing).
|
// This will cause issues otherwise (e.g. custom skin disappearing).
|
||||||
double hideDistance = npc.getAutoHideDistance();
|
double hideDistance = npc.getAutoHideDistance();
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ package net.jitse.npclib.nms.holograms;
|
|||||||
|
|
||||||
import com.comphenix.tinyprotocol.Reflection;
|
import com.comphenix.tinyprotocol.Reflection;
|
||||||
import org.bukkit.Location;
|
import org.bukkit.Location;
|
||||||
|
import org.bukkit.World;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -25,49 +26,49 @@ public class Hologram {
|
|||||||
private Set<Object> destroyPackets = new HashSet<>();
|
private Set<Object> destroyPackets = new HashSet<>();
|
||||||
|
|
||||||
// Classes:
|
// Classes:
|
||||||
private final Class<?> entityArmorStandClazz = Reflection.getMinecraftClass("EntityArmorStand");
|
private static final Class<?> ENTITY_ARMOR_STAND_CLAZZ = Reflection.getMinecraftClass("EntityArmorStand");
|
||||||
private final Class<?> entityLivingClazz = Reflection.getMinecraftClass("EntityLiving");
|
private static final Class<?> ENTITY_LIVING_CLAZZ = Reflection.getMinecraftClass("EntityLiving");
|
||||||
private final Class<?> entityClazz = Reflection.getMinecraftClass("Entity");
|
private static final Class<?> ENTITY_CLAZZ = Reflection.getMinecraftClass("Entity");
|
||||||
private final Class<?> craftWorldClazz = Reflection.getCraftBukkitClass("CraftWorld");
|
private static final Class<?> CRAFT_BUKKIT_CLASS = Reflection.getCraftBukkitClass("CraftWorld");
|
||||||
private final Class<?> craftPlayerClazz = Reflection.getCraftBukkitClass("entity.CraftPlayer");
|
private static final Class<?> CRAFT_PLAYER_CLAZZ = Reflection.getCraftBukkitClass("entity.CraftPlayer");
|
||||||
private final Class<?> packetPlayOutSpawnEntityLivingClazz = Reflection.getMinecraftClass(
|
private static final Class<?> PACKET_PLAY_OUT_SPAWN_ENTITY_LIVING_CLAZZ = Reflection.getMinecraftClass(
|
||||||
"PacketPlayOutSpawnEntityLiving");
|
"PacketPlayOutSpawnEntityLiving");
|
||||||
private final Class<?> packetPlayOutEntityDestroyClazz = Reflection.getMinecraftClass(
|
private static final Class<?> PACKET_PLAY_OUT_ENTITY_DESTROY_CLAZZ = Reflection.getMinecraftClass(
|
||||||
"PacketPlayOutEntityDestroy");
|
"PacketPlayOutEntityDestroy");
|
||||||
private final Class<?> entityPlayerClazz = Reflection.getMinecraftClass("EntityPlayer");
|
private static final Class<?> ENTITY_PLAYER_CLAZZ = Reflection.getMinecraftClass("EntityPlayer");
|
||||||
private final Class<?> playerConnectionClazz = Reflection.getMinecraftClass("PlayerConnection");
|
private static final Class<?> PLAYER_CONNECTION_CLAZZ = Reflection.getMinecraftClass("PlayerConnection");
|
||||||
private final Class<?> packetClazz = Reflection.getMinecraftClass("Packet");
|
private static final Class<?> PACKET_CLAZZ = Reflection.getMinecraftClass("Packet");
|
||||||
|
|
||||||
// Constructors:
|
// Constructors:
|
||||||
private final Reflection.ConstructorInvoker packetPlayOutSpawnEntityLivingConstructor = Reflection
|
private static final Reflection.ConstructorInvoker PACKET_PLAY_OUT_SPAWN_ENTITY_LIVING_CONSTRUCTOR = Reflection
|
||||||
.getConstructor(packetPlayOutSpawnEntityLivingClazz, entityLivingClazz);
|
.getConstructor(PACKET_PLAY_OUT_SPAWN_ENTITY_LIVING_CLAZZ, ENTITY_LIVING_CLAZZ);
|
||||||
private final Reflection.ConstructorInvoker packetPlayOutEntityDestroyConstructor = Reflection
|
private static final Reflection.ConstructorInvoker PACKET_PLAY_OUT_ENTITY_DESTROY_CONSTRUCTOR = Reflection
|
||||||
.getConstructor(packetPlayOutEntityDestroyClazz, int[].class);
|
.getConstructor(PACKET_PLAY_OUT_ENTITY_DESTROY_CLAZZ, int[].class);
|
||||||
|
|
||||||
// Fields:
|
// Fields:
|
||||||
private final Reflection.FieldAccessor playerConnectionField = Reflection.getField(entityPlayerClazz,
|
private static final Reflection.FieldAccessor playerConnectionField = Reflection.getField(ENTITY_PLAYER_CLAZZ,
|
||||||
"playerConnection", playerConnectionClazz);
|
"playerConnection", PLAYER_CONNECTION_CLAZZ);
|
||||||
|
|
||||||
// Methods:
|
// Methods:
|
||||||
private final Reflection.MethodInvoker setLocationMethod = Reflection.getMethod(entityArmorStandClazz,
|
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);
|
"setLocation", double.class, double.class, double.class, float.class, float.class);
|
||||||
private final Reflection.MethodInvoker setCustomNameMethod = Reflection.getMethod(entityArmorStandClazz,
|
private static final Reflection.MethodInvoker SET_CUSTOM_NAME_METHOD = Reflection.getMethod(ENTITY_ARMOR_STAND_CLAZZ,
|
||||||
"setCustomName", String.class);
|
"setCustomName", String.class);
|
||||||
private final Reflection.MethodInvoker setCustomNameVisibleMethod = Reflection.getMethod(entityArmorStandClazz,
|
private static final Reflection.MethodInvoker SET_CUSTOM_NAME_VISIBLE_METHOD = Reflection.getMethod(ENTITY_ARMOR_STAND_CLAZZ,
|
||||||
"setCustomNameVisible", boolean.class);
|
"setCustomNameVisible", boolean.class);
|
||||||
private final Reflection.MethodInvoker setSmallMethod = Reflection.getMethod(entityArmorStandClazz,
|
private static final Reflection.MethodInvoker SET_SMALL_METHOD = Reflection.getMethod(ENTITY_ARMOR_STAND_CLAZZ,
|
||||||
"setSmall", boolean.class);
|
"setSmall", boolean.class);
|
||||||
private final Reflection.MethodInvoker setInvisibleMethod = Reflection.getMethod(entityArmorStandClazz,
|
private static final Reflection.MethodInvoker SET_INVISIBLE_METHOD = Reflection.getMethod(ENTITY_ARMOR_STAND_CLAZZ,
|
||||||
"setInvisible", boolean.class);
|
"setInvisible", boolean.class);
|
||||||
private final Reflection.MethodInvoker setBasePlateMethod = Reflection.getMethod(entityArmorStandClazz,
|
private static final Reflection.MethodInvoker SET_BASE_PLATE_METHOD = Reflection.getMethod(ENTITY_ARMOR_STAND_CLAZZ,
|
||||||
"setBasePlate", boolean.class);
|
"setBasePlate", boolean.class);
|
||||||
private final Reflection.MethodInvoker setArmsMethod = Reflection.getMethod(entityArmorStandClazz,
|
private static final Reflection.MethodInvoker SET_ARMS_METHOD = Reflection.getMethod(ENTITY_ARMOR_STAND_CLAZZ,
|
||||||
"setArms", boolean.class);
|
"setArms", boolean.class);
|
||||||
private final Reflection.MethodInvoker playerGetHandleMethod = Reflection.getMethod(craftPlayerClazz,
|
private static final Reflection.MethodInvoker PLAYER_GET_HANDLE_METHOD = Reflection.getMethod(CRAFT_PLAYER_CLAZZ,
|
||||||
"getHandle");
|
"getHandle");
|
||||||
private final Reflection.MethodInvoker sendPacketMethod = Reflection.getMethod(playerConnectionClazz,
|
private static final Reflection.MethodInvoker SEND_PACKET_METHOD = Reflection.getMethod(PLAYER_CONNECTION_CLAZZ,
|
||||||
"sendPacket", packetClazz);
|
"sendPacket", PACKET_CLAZZ);
|
||||||
private final Reflection.MethodInvoker getIdMethod = Reflection.getMethod(entityArmorStandClazz,
|
private static final Reflection.MethodInvoker GET_ID_METHOD = Reflection.getMethod(ENTITY_ARMOR_STAND_CLAZZ,
|
||||||
"getId");
|
"getId");
|
||||||
|
|
||||||
private final Location start;
|
private final Location start;
|
||||||
@@ -78,32 +79,37 @@ public class Hologram {
|
|||||||
this.start = location;
|
this.start = location;
|
||||||
this.lines = lines;
|
this.lines = lines;
|
||||||
|
|
||||||
this.worldServer = Reflection.getMethod(craftWorldClazz, "getHandle")
|
this.worldServer = Reflection.getMethod(CRAFT_BUKKIT_CLASS, "getHandle")
|
||||||
.invoke(craftWorldClazz.cast(location.getWorld()));
|
.invoke(CRAFT_BUKKIT_CLASS.cast(location.getWorld()));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void generatePackets(boolean above1_9_r2) {
|
public void generatePackets(boolean above1_9_r2) {
|
||||||
Reflection.MethodInvoker gravityMethod = (above1_9_r2 ? Reflection.getMethod(entityClazz,
|
Reflection.MethodInvoker gravityMethod = (above1_9_r2 ? Reflection.getMethod(ENTITY_CLAZZ,
|
||||||
"setNoGravity", boolean.class) : Reflection.getMethod(entityArmorStandClazz,
|
"setNoGravity", boolean.class) : Reflection.getMethod(ENTITY_ARMOR_STAND_CLAZZ,
|
||||||
"setGravity", boolean.class));
|
"setGravity", boolean.class));
|
||||||
|
|
||||||
Location location = start.clone().add(0, delta * lines.size(), 0);
|
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
|
Reflection.ConstructorInvoker entityArmorStandConstructor = Reflection
|
||||||
.getConstructor(entityArmorStandClazz, worldServer.getClass().getSuperclass());
|
.getConstructor(ENTITY_ARMOR_STAND_CLAZZ, worldClass);
|
||||||
|
|
||||||
for (String line : lines) {
|
for (String line : lines) {
|
||||||
Object entityArmorStand = entityArmorStandConstructor.invoke(worldServer);
|
Object entityArmorStand = entityArmorStandConstructor.invoke(worldServer);
|
||||||
|
|
||||||
setLocationMethod.invoke(entityArmorStand, location.getX(), location.getY(), location.getZ(), 0, 0);
|
SET_LOCATION_METHOD.invoke(entityArmorStand, location.getX(), location.getY(), location.getZ(), 0, 0);
|
||||||
setCustomNameMethod.invoke(entityArmorStand, line);
|
SET_CUSTOM_NAME_METHOD.invoke(entityArmorStand, line);
|
||||||
setCustomNameVisibleMethod.invoke(entityArmorStand, true);
|
SET_CUSTOM_NAME_VISIBLE_METHOD.invoke(entityArmorStand, true);
|
||||||
gravityMethod.invoke(entityArmorStand, (above1_9_r2 ? true : false));
|
gravityMethod.invoke(entityArmorStand, above1_9_r2);
|
||||||
setSmallMethod.invoke(entityArmorStand, true);
|
SET_SMALL_METHOD.invoke(entityArmorStand, true);
|
||||||
setInvisibleMethod.invoke(entityArmorStand, true);
|
SET_INVISIBLE_METHOD.invoke(entityArmorStand, true);
|
||||||
setBasePlateMethod.invoke(entityArmorStand, false);
|
SET_BASE_PLATE_METHOD.invoke(entityArmorStand, false);
|
||||||
setArmsMethod.invoke(entityArmorStand, false);
|
SET_ARMS_METHOD.invoke(entityArmorStand, false);
|
||||||
|
|
||||||
location.subtract(0, delta, 0);
|
location.subtract(0, delta, 0);
|
||||||
|
|
||||||
@@ -113,30 +119,30 @@ public class Hologram {
|
|||||||
|
|
||||||
armorStands.add(entityArmorStand);
|
armorStands.add(entityArmorStand);
|
||||||
|
|
||||||
Object spawnPacket = packetPlayOutSpawnEntityLivingConstructor.invoke(entityArmorStand);
|
Object spawnPacket = PACKET_PLAY_OUT_SPAWN_ENTITY_LIVING_CONSTRUCTOR.invoke(entityArmorStand);
|
||||||
spawnPackets.add(spawnPacket);
|
spawnPackets.add(spawnPacket);
|
||||||
|
|
||||||
Object destroyPacket = packetPlayOutEntityDestroyConstructor
|
Object destroyPacket = PACKET_PLAY_OUT_ENTITY_DESTROY_CONSTRUCTOR
|
||||||
.invoke(new int[]{(int) getIdMethod.invoke(entityArmorStand)});
|
.invoke(new int[]{(int) GET_ID_METHOD.invoke(entityArmorStand)});
|
||||||
destroyPackets.add(destroyPacket);
|
destroyPackets.add(destroyPacket);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void spawn(Player player) {
|
public void spawn(Player player) {
|
||||||
Object playerConnection = playerConnectionField.get(playerGetHandleMethod
|
Object playerConnection = playerConnectionField.get(PLAYER_GET_HANDLE_METHOD
|
||||||
.invoke(craftPlayerClazz.cast(player)));
|
.invoke(CRAFT_PLAYER_CLAZZ.cast(player)));
|
||||||
|
|
||||||
for (Object packet : spawnPackets) {
|
for (Object packet : spawnPackets) {
|
||||||
sendPacketMethod.invoke(playerConnection, packet);
|
SEND_PACKET_METHOD.invoke(playerConnection, packet);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void destroy(Player player) {
|
public void destroy(Player player) {
|
||||||
Object playerConnection = playerConnectionField.get(playerGetHandleMethod
|
Object playerConnection = playerConnectionField.get(PLAYER_GET_HANDLE_METHOD
|
||||||
.invoke(craftPlayerClazz.cast(player)));
|
.invoke(CRAFT_PLAYER_CLAZZ.cast(player)));
|
||||||
|
|
||||||
for (Object packet : destroyPackets) {
|
for (Object packet : destroyPackets) {
|
||||||
sendPacketMethod.invoke(playerConnection, packet);
|
SEND_PACKET_METHOD.invoke(playerConnection, packet);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user