All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.shynixn.petblocks.bukkit.logic.persistence.entity.ParticleEffectData Maven / Gradle / Ivy

package com.github.shynixn.petblocks.bukkit.logic.persistence.entity;

import com.github.shynixn.petblocks.api.persistence.entity.ParticleEffectMeta;
import com.github.shynixn.petblocks.bukkit.PetBlocksPlugin;
import com.github.shynixn.petblocks.bukkit.nms.v1_12_R1.MaterialCompatibility12;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.configuration.serialization.ConfigurationSerializable;
import org.bukkit.entity.Player;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;
import java.util.logging.Level;

/**
 * Copyright 2017 Shynixn
 * 

* Do not remove this header! *

* Version 1.0 *

* MIT License *

* Copyright (c) 2016 *

* Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: *

* The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. *

* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ public class ParticleEffectData extends PersistenceObject implements ParticleEffectMeta, ConfigurationSerializable { private String effect; private int amount; private double speed; private double offsetX; private double offsetY; private double offsetZ; private Integer material; private Byte data; /** * Initializes a new ParticleEffectData */ public ParticleEffectData() { super(); } /** * Initializes a new ParticleEffectData with the given params * * @param effectName effect * @param amount amount * @param speed speed * @param offsetX x * @param offsetY y * @param offsetZ z */ public ParticleEffectData(String effectName, int amount, double speed, double offsetX, double offsetY, double offsetZ) { super(); if (effectName == null) throw new IllegalArgumentException("Effect cannot be null!"); if (amount < 0) throw new IllegalArgumentException("Amount cannot be less than 0"); if (getParticleEffectFromName(effectName) == null) throw new IllegalArgumentException("Cannot find particleEffect for name!"); this.effect = effectName; this.amount = amount; this.speed = speed; this.offsetX = (float) offsetX; this.offsetY = (float) offsetY; this.offsetZ = (float) offsetZ; } /** * Parses the potioneffect out of the map * * @param items items * @throws Exception mapParseException */ public ParticleEffectData(Map items) throws Exception { super(); this.effect = (String) items.get("name"); this.amount = (int) items.get("amount"); this.speed = (double) items.get("speed"); if (items.containsKey("offx")) this.offsetX = (float) (double) items.get("offx"); if (items.containsKey("offy")) this.offsetY = (float) (double) items.get("offy"); if (items.containsKey("offz")) this.offsetZ = ((float) (double) items.get("offz")); if (items.containsKey("id")) this.material = (Integer) items.get("id"); if (items.containsKey("damage")) this.data = (byte) (int) (Integer) items.get("damage"); if (items.containsKey("red")) this.setRed((Integer) items.get("red")); if (items.containsKey("green")) this.setGreen((Integer) items.get("green")); if (items.containsKey("blue")) this.setBlue((Integer) items.get("blue")); } /** * Sets the RGB colors of the particleEffect * * @param red red * @param green green * @param blue blue * @return builder */ @Override public ParticleEffectData setColor(int red, int green, int blue) { this.setRed(red); this.setBlue(blue); this.setGreen(green); return this; } /** * Sets the color of the particleEffect * * @param particleColor particleColor * @return builder */ @Override public ParticleEffectData setColor(ParticleEffectData.ParticleColor particleColor) { if (particleColor == null) throw new IllegalArgumentException("Color cannot be null!"); this.setColor(particleColor.getRed(), particleColor.getGreen(), particleColor.getBlue()); return this; } /** * Sets the color for note particleEffect * * @param color color * @return builder */ @Override public ParticleEffectData setNoteColor(int color) { if (color > 20 || color < 0) { this.offsetX = 5; } else { this.offsetX = color; } return this; } /** * Sets the amount of particles of the particleEffect * * @param amount amount * @return builder */ @Override public ParticleEffectData setAmount(int amount) { if (amount < 0) throw new IllegalArgumentException("Amount cannot be less than 0"); this.amount = amount; return this; } /** * Sets the speed of the particleEffect * * @param speed speed * @return builder */ @Override public ParticleEffectData setSpeed(double speed) { this.speed = speed; return this; } /** * Sets the offsetX of the particleEffect * * @param offsetX offsetX * @return builder */ @Override public ParticleEffectData setOffsetX(double offsetX) { this.offsetX = offsetX; return this; } /** * Sets the offsetY of the particleEffect * * @param offsetY offsetY * @return builder */ @Override public ParticleEffectData setOffsetY(double offsetY) { this.offsetY = offsetY; return this; } /** * Sets the offsetZ of the particleEffect * * @param offsetZ offsetZ * @return builder */ @Override public ParticleEffectData setOffsetZ(double offsetZ) { this.offsetZ = offsetZ; return this; } /** * Sets the offset of the particleEffect * * @param offsetX offsetX * @param offsetY offsetY * @param offsetZ offsetZ * @return instance */ @Override public ParticleEffectData setOffset(double offsetX, double offsetY, double offsetZ) { this.setOffsetX(offsetX); this.setOffsetY(offsetY); this.setOffsetZ(offsetZ); return this; } /** * Sets the effectType of the particleEffect * * @param name name * @return builder */ @Override public ParticleEffectData setEffectName(String name) { if (name == null) throw new IllegalArgumentException("Name cannot be null!"); this.effect = name; return this; } /** * Sets the effectType of the particlEffect * * @param type type * @return builder */ @Override public ParticleEffectData setEffectType(ParticleEffectData.ParticleEffectType type) { if (type == null) throw new IllegalArgumentException("Type cannot be null!"); this.effect = type.getSimpleName(); return this; } /** * Sets the blue of the RGB color * * @param blue blue * @return builder */ @Override public ParticleEffectData setBlue(int blue) { this.offsetZ = blue / 255.0F; return this; } /** * Sets the red of the RGB color * * @param red red * @return builder */ @Override public ParticleEffectData setRed(int red) { this.offsetX = red / 255.0F; if (red == 0) { this.offsetX = Float.MIN_NORMAL; } return this; } /** * Sets the green of the RGB color * * @param green green * @return builder */ @Override public ParticleEffectData setGreen(int green) { this.offsetY = green / 255.0F; return this; } /** * Sets the material of the particleEffect * * @param material material * @return builder */ @Override public ParticleEffectData setMaterial(Object material) { if (material != null && material instanceof Integer) { this.material = (Integer) material; } else if (material != null) { this.material = MaterialCompatibility12.getIdFromMaterial((Material) material); } else { this.material = null; } return this; } /** * Sets the data of the material of the particleEffect * * @param data data * @return builder */ @Override public ParticleEffectData setData(Byte data) { this.data = data; return this; } /** * Returns the effect of the particleEffect * * @return effectName */ @Override public String getEffectName() { return this.effect; } /** * Returns the particleEffectType of the particleEffect * * @return effectType */ @Override public ParticleEffectData.ParticleEffectType getEffectType() { return getParticleEffectFromName(this.effect); } /** * Returns the amount of particles of the particleEffect * * @return amount */ @Override public int getAmount() { return this.amount; } /** * Returns the speed of the particleEffect * * @return speed */ @Override public double getSpeed() { return this.speed; } /** * Returns the offsetX of the particleEffect * * @return offsetX */ @Override public double getOffsetX() { return this.offsetX; } /** * Returns the offsetY of the particleEffect * * @return offsetY */ @Override public double getOffsetY() { return this.offsetY; } /** * Returns the offsetZ of the particleEffect * * @return offsetZ */ @Override public double getOffsetZ() { return this.offsetZ; } /** * Returns the RGB color blue of the particleEffect * * @return blue */ @Override public int getBlue() { return (int) this.offsetZ * 255; } /** * Returns the RGB color red of the particleEffect * * @return red */ @Override public int getRed() { return (int) this.offsetX * 255; } /** * Returns the RGB color green of the particleEffect * * @return green */ @Override public int getGreen() { return (int) this.offsetY * 255; } /** * Returns the material of the particleEffect * * @return material */ @Override public Object getMaterial() { if (this.material == null || MaterialCompatibility12.getMaterialFromId(this.material) == null) return null; return MaterialCompatibility12.getMaterialFromId(this.material); } /** * Returns the data of the particleEffect * * @return data */ @Override public Byte getData() { return this.data; } /** * Copies the current builder * * @return copyOfBuilder */ @Override public ParticleEffectData clone() { final ParticleEffectData particle = new ParticleEffectData(); particle.effect = this.effect; particle.amount = this.amount; particle.offsetX = this.offsetX; particle.offsetY = this.offsetY; particle.offsetZ = this.offsetZ; particle.speed = this.speed; particle.material = this.material; particle.data = this.data; return particle; } /** * Returns if the particleEffect is a color particleEffect * * @return isColor */ @Override public boolean isColorParticleEffect() { return this.effect.equalsIgnoreCase(ParticleEffectData.ParticleEffectType.SPELL_MOB.getSimpleName()) || this.effect.equalsIgnoreCase(ParticleEffectData.ParticleEffectType.SPELL_MOB_AMBIENT.getSimpleName()) || this.effect.equalsIgnoreCase(ParticleEffectData.ParticleEffectType.REDSTONE.getSimpleName()) || this.effect.equalsIgnoreCase(ParticleEffectData.ParticleEffectType.NOTE.getSimpleName()); } /** * Returns if the particleEffect is a note particleEffect * * @return isNote */ @Override public boolean isNoteParticleEffect() { return this.effect.equalsIgnoreCase(ParticleEffectData.ParticleEffectType.NOTE.getSimpleName()); } /** * Returns if the particleEffect is a materialParticleEffect * * @return isMaterial */ @Override public boolean isMaterialParticleEffect() { return this.effect.equalsIgnoreCase(ParticleEffectData.ParticleEffectType.BLOCK_CRACK.getSimpleName()) || this.effect.equalsIgnoreCase(ParticleEffectData.ParticleEffectType.BLOCK_DUST.getSimpleName()) || this.effect.equalsIgnoreCase(ParticleEffectData.ParticleEffectType.ITEM_CRACK.getSimpleName()); } /** * Plays the effect at the given location to the given players. * * @param location location * @param players players */ @Override public void apply(Object location, Collection players) { if (players == null) throw new IllegalArgumentException("Players cannot be null!"); this.applyTo((Location) location, players.toArray(new Player[players.size()])); } /** * Plays the effect at the given location to the given players. * * @param location location */ @Override public void apply(Object location) { this.applyTo((Location) location); } /** * Plays the effect at the given location to the given players. * * @param location location * @param players players */ public void applyTo(Location location, Player... players) { try { if (location == null) throw new IllegalArgumentException("Location cannot be null!"); if (this.effect.equals("none")) return; final Player[] playingPlayers; if (players.length == 0) { playingPlayers = location.getWorld().getPlayers().toArray(new Player[location.getWorld().getPlayers().size()]); } else { playingPlayers = players; } final float speed; final int amount; if (this.effect.equals(ParticleEffectData.ParticleEffectType.REDSTONE.getSimpleName()) || this.effect.equals(ParticleEffectData.ParticleEffectType.NOTE.getSimpleName())) { amount = 0; speed = 1.0F; } else { amount = this.getAmount(); speed = (float) this.getSpeed(); } final Object enumParticle = invokeMethod(null, findClass("net.minecraft.server.VERSION.EnumParticle"), "valueOf", new Class[]{String.class}, new Object[]{this.getEffectType().name().toUpperCase()}); int[] additionalInfo = null; if (this.getMaterial() != null) { if (this.getEffectType() == ParticleEffectData.ParticleEffectType.ITEM_CRACK) { additionalInfo = new int[]{this.material, this.getData()}; } else { additionalInfo = new int[]{this.material, this.getData() << 12}; } } final Object packet = invokeConstructor(findClass("net.minecraft.server.VERSION.PacketPlayOutWorldParticles"), new Class[]{ enumParticle.getClass(), boolean.class, float.class, float.class, float.class, float.class, float.class, float.class, float.class, int.class, int[].class }, new Object[]{ enumParticle, isLongDistance(location, players), (float) location.getX(), (float) location.getY(), (float) location.getZ(), (float) this.getOffsetX(), (float) this.getOffsetY(), (float) this.getOffsetZ(), speed, amount, additionalInfo }); for (final Player player : playingPlayers) { sendPacket(player, packet); } } catch (InstantiationException | NoSuchMethodException | InvocationTargetException | ClassNotFoundException | IllegalAccessException | NoSuchFieldException e) { PetBlocksPlugin.logger().log(Level.WARNING, "Failed to send packet.", e); } } /** * Checks if 2 builders are equal * * @param o secondBuilder * @return isSame */ @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || this.getClass() != o.getClass()) return false; final ParticleEffectData that = (ParticleEffectData) o; return this.amount == that.amount && Double.compare(that.speed, this.speed) == 0 && Double.compare(that.offsetX, this.offsetX) == 0 && Double.compare(that.offsetY, this.offsetY) == 0 && Double.compare(that.offsetZ, this.offsetZ) == 0 & (this.effect != null ? this.effect.equals(that.effect) : that.effect == null) && Objects.equals(this.material, that.material) && (this.data != null ? this.data.equals(that.data) : that.data == null); } /** * Displays the builder as string * * @return string */ @Override public String toString() { return "effect {" + "name " + this.effect + " amound " + this.amount + " speed " + this.speed + '}'; } /** * Serializes the particleEffect data to be stored to the filesystem * * @return serializedContent */ @Override public Map serialize() { final Map map = new LinkedHashMap<>(); map.put("effect", this.effect.toUpperCase()); map.put("amount", this.amount); map.put("speed", this.speed); final Map tmp3 = new LinkedHashMap<>(); tmp3.put("x", this.offsetX); tmp3.put("y", this.offsetY); tmp3.put("z", this.offsetZ); map.put("size", tmp3); final Map tmp2 = new LinkedHashMap<>(); if (this.material != null) tmp2.put("material", this.material); else tmp2.put("material", null); tmp2.put("damage", this.data); map.put("block", tmp2); return map; } /** * Returns a text of all particleEffects to let the user easily view them * * @return potionEffects */ public static String getParticlesText() { final StringBuilder builder = new StringBuilder(); for (final ParticleEffectData.ParticleEffectType particleEffect : ParticleEffectData.ParticleEffectType.values()) { if (builder.length() != 0) { builder.append(", "); } builder.append(particleEffect.getSimpleName()); } return builder.toString(); } /** * Returns the particleEffectType from name * * @param name name * @return particleEffectType */ public static ParticleEffectData.ParticleEffectType getParticleEffectFromName(String name) { for (final ParticleEffectData.ParticleEffectType particleEffect : ParticleEffectData.ParticleEffectType.values()) { if (name != null && particleEffect.getSimpleName().equalsIgnoreCase(name)) return particleEffect; } return null; } /** * Sends a packet to the client player * * @param player player * @param packet packet * @throws ClassNotFoundException exception * @throws IllegalAccessException exception * @throws NoSuchMethodException exception * @throws InvocationTargetException exception * @throws NoSuchFieldException exception */ private static void sendPacket(Player player, Object packet) throws ClassNotFoundException, IllegalAccessException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException { final Object craftPlayer = findClass("org.bukkit.craftbukkit.VERSION.entity.CraftPlayer").cast(player); final Object entityPlayer = invokeMethod(craftPlayer, craftPlayer.getClass(), "getHandle", new Class[]{}, new Object[]{}); final Field field = findClass("net.minecraft.server.VERSION.EntityPlayer").getDeclaredField("playerConnection"); field.setAccessible(true); final Object connection = field.get(entityPlayer); invokeMethod(connection, connection.getClass(), "sendPacket", new Class[]{packet.getClass().getInterfaces()[0]}, new Object[]{packet}); } /** * Invokes a constructor by the given parameters * * @param clazz clazz * @param paramTypes paramTypes * @param params params * @return instance * @throws NoSuchMethodException exception * @throws IllegalAccessException exception * @throws InvocationTargetException exception * @throws InstantiationException exception */ private static Object invokeConstructor(Class clazz, Class[] paramTypes, Object[] params) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { final Constructor constructor = clazz.getDeclaredConstructor(paramTypes); constructor.setAccessible(true); return constructor.newInstance(params); } /** * Invokes a method by the given parameters * * @param instance instance * @param clazz clazz * @param name name * @param paramTypes paramTypes * @param params params * @return returnedObject * @throws InvocationTargetException exception * @throws IllegalAccessException exception * @throws NoSuchMethodException exception */ private static Object invokeMethod(Object instance, Class clazz, String name, Class[] paramTypes, Object[] params) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException { final Method method = clazz.getDeclaredMethod(name, paramTypes); method.setAccessible(true); return method.invoke(instance, params); } /** * Finds a class regarding of the server Version * * @param name name * @return clazz * @throws ClassNotFoundException exception */ private static Class findClass(String name) throws ClassNotFoundException { return Class.forName(name.replace("VERSION", Bukkit.getServer().getClass().getPackage().getName().replace(".", ",").split(",")[3])); } /** * Checks if longDistance attribute is necessary * * @param location location * @param players players * @return isNecessary */ private static boolean isLongDistance(Location location, Player[] players) { for (final Player player : players) { if (location.getWorld().getName().equals(player.getLocation().getWorld().getName()) && player.getLocation().distanceSquared(location) > 65536) { return true; } } return false; } }