/** * Base client-side entity * * @author Illusion */ public abstract class ClientsideEntity { private static int ENTITY_ID = 10000; // Static entity-id, when a new instance is created, this index changes private final int entityId; // Internal entity-id private final List> clickAction = new ArrayList<>(); // List of click listeners private final PacketContainer hidePacket; // Destroy packet, as it's common for all instnaces public ClientsideEntity() { entityId = ENTITY_ID++; hidePacket = new PacketContainer(PacketType.Play.Server.ENTITY_DESTROY); hidePacket.getIntegerArrays().writeSafely(0, new int[] {entityId}); //hidePacket.getIntegers().writeSafely(0, entityId); } public void onClick(Consumer consumer) { clickAction.add(consumer); } public void registerClick(ClickData data) { for (Consumer consumer : clickAction) consumer.accept(data); } public int getEntityId() { return entityId; } /** * Displays a client-sided entity to a specific player * * @param player - The player to display to */ public abstract T show(Player player); /** * Hides a client-sided entity from a specific player * Can be overriden, but shouldn't be by default. * * @param player - The player to display to */ public void hide(Player player) { try { ProtocolLibrary.getProtocolManager().sendServerPacket(player, hidePacket); } catch (InvocationTargetException e) { e.printStackTrace(); } } /** * Hides a client-sided entity from all players on the server. * To modify its behavior, override hide(player) */ public final void hide() { for (Player player : Bukkit.getOnlinePlayers()) hide(player); } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; ClientsideEntity that = (ClientsideEntity) o; return Objects.equals(getEntityId(), that.getEntityId()); } @Override public int hashCode() { return Objects.hash(getEntityId()); } // -- PROTOCOLLIB / Reflection START /** * Creates an NMS entity instance using world,x,y,z constructor * * @param clazzName - The NMS entity class name, example: "EntityItem" * @return NMS entity instance */ public Object createInstance(String clazzName) { ConstructorAccessor constructor = Accessors.getConstructorAccessor(MinecraftReflection.getMinecraftClass(clazzName), MinecraftReflection.getNmsWorldClass(), Double.TYPE, Double.TYPE, Double.TYPE); Object world = BukkitUnwrapper.getInstance().unwrapItem(Bukkit.getWorlds().get(0)); return constructor.invoke(world, 0, 0, 0); } /** * Creates an NMS entity instance using EntityTypes,world constructor * * @param clazzName - The class name, example: "EntityFox" * @param entityTypeName - The EntityTypes id, example: "FOX" * @return NMS entity instance, NULL if entity type is not found */ public Object createInstance(String clazzName, String entityTypeName) { Class types = MinecraftReflection.getMinecraftClass("EntityTypes"); Optional type = Optional.empty(); try { type = (Optional) types.getMethod("getByName", String.class).invoke(null, entityTypeName); // Obtains type } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) { e.printStackTrace(); } if (type.isEmpty()) // Checks if type exists return null; Object val = type.get(); ConstructorAccessor constructor = Accessors.getConstructorAccessor(MinecraftReflection.getMinecraftClass(clazzName), val.getClass(), MinecraftReflection.getNmsWorldClass()); Object world = BukkitUnwrapper.getInstance().unwrapItem(Bukkit.getWorlds().get(0)); return constructor.invoke(type, world); } protected void sendPacket(PacketContainer packet, Player player) { try { ProtocolLibrary.getProtocolManager().sendServerPacket(player, packet); } catch (InvocationTargetException e) { e.printStackTrace(); } } // -- PROTOCOLLIB / Reflection END public List> getExtraEntities() { return Collections.emptyList(); } // Called on player log off, override if tracking players public void stopTracking(Player player) { hide(player); } }