From e834964baabaf064e33a7e41f27281c880196805 Mon Sep 17 00:00:00 2001 From: Olof Larsson Date: Fri, 12 Dec 2014 10:28:18 +0100 Subject: [PATCH] Updated ParticleLib. Fixes MassiveCraft/Factions#723. --- .../particleeffect/ParticleEffect.java | 1042 ++++++++++++----- .../particleeffect/ReflectionUtils.java | 154 +-- 2 files changed, 721 insertions(+), 475 deletions(-) diff --git a/src/main/java/com/massivecraft/massivecore/particleeffect/ParticleEffect.java b/src/main/java/com/massivecraft/massivecore/particleeffect/ParticleEffect.java index 34ffcc1d..e38c2d5a 100644 --- a/src/main/java/com/massivecraft/massivecore/particleeffect/ParticleEffect.java +++ b/src/main/java/com/massivecraft/massivecore/particleeffect/ParticleEffect.java @@ -12,15 +12,15 @@ import java.util.Map.Entry; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.entity.Player; +import org.bukkit.util.Vector; import com.massivecraft.massivecore.particleeffect.ReflectionUtils.PackageType; -import com.massivecraft.massivecore.particleeffect.ReflectionUtils.PacketType; import com.massivecraft.massivecore.util.MUtil; /** * ParticleEffect Library *

- * This library was created by @DarkBlade12 based on content related to particles of @microgeek (names and packet parameters), it allows you to display all Minecraft particle effects on a Bukkit server + * This library was created by @DarkBlade12 and allows you to display all Minecraft particle effects on a Bukkit server *

* You are welcome to use it, modify it and redistribute it under the following conditions: *

*

+ * Special thanks: + *

+ *

* It would be nice if you provide credit to me if you use this class in a published project * * @author DarkBlade12 - * @version 1.5 + * @version 1.6 */ public enum ParticleEffect { - /** - * A particle effect which is displayed by exploding tnt and creepers: - *

- */ - HUGE_EXPLOSION("hugeexplosion"), - /** - * A particle effect which is displayed by exploding ghast fireballs and wither skulls: - * - */ - LARGE_EXPLODE("largeexplode"), - /** - * A particle effect which is displayed by launching fireworks: - * - */ - FIREWORKS_SPARK("fireworksSpark"), - /** - * A particle effect which is displayed by swimming entities and arrows in water: - * - */ - BUBBLE("bubble", true), - /** - * A particle effect which is displayed by water: - * - */ - SUSPEND("suspend", true), - /** - * A particle effect which is displayed by air when close to bedrock and the in the void: - * - */ - DEPTH_SUSPEND("depthSuspend"), - /** - * A particle effect which is displayed by mycelium: - * - */ - TOWN_AURA("townaura"), - /** - * A particle effect which is displayed when landing a critical hit and by arrows: - * - */ - CRIT("crit"), - /** - * A particle effect which is displayed when landing a hit with an enchanted weapon: - * - */ - MAGIC_CRIT("magicCrit"), - /** - * A particle effect which is displayed by primed tnt, torches, droppers, dispensers, end portals, brewing stands and monster spawners: - * - */ - SMOKE("smoke"), - /** - * A particle effect which is displayed by entities with active potion effects: - * - */ - MOB_SPELL("mobSpell"), - /** - * A particle effect which is displayed by entities with active potion effects applied through a beacon: - * - */ - MOB_SPELL_AMBIENT("mobSpellAmbient"), - /** - * A particle effect which is displayed when splash potions or bottles o' enchanting hit something: - * - */ - SPELL("spell"), - /** - * A particle effect which is displayed when instant splash potions hit something: - * - */ - INSTANT_SPELL("instantSpell"), - /** - * A particle effect which is displayed by witches: - * - */ - WITCH_MAGIC("witchMagic"), - /** - * A particle effect which is displayed by note blocks: - * - */ - NOTE("note"), - /** - * A particle effect which is displayed by nether portals, endermen, ender pearls, eyes of ender, ender chests and dragon eggs: - * - */ - PORTAL("portal"), - /** - * A particle effect which is displayed by enchantment tables which are nearby bookshelves: - * - */ - ENCHANTMENT_TABLE("enchantmenttable"), /** * A particle effect which is displayed by exploding tnt and creepers: * */ - EXPLODE("explode"), + EXPLOSION_NORMAL("explode", 0, -1), /** - * A particle effect which is displayed by torches, active furnaces, magma cubes and monster spawners: + * A particle effect which is displayed by exploding ghast fireballs and wither skulls: * + */ + EXPLOSION_LARGE("largeexplode", 1, -1), + /** + * A particle effect which is displayed by exploding tnt and creepers: + * + */ + EXPLOSION_HUGE("hugeexplosion", 2, -1), + /** + * A particle effect which is displayed by launching fireworks: + * */ - FLAME("flame"), + FIREWORKS_SPARK("fireworksSpark", 3, -1), /** - * A particle effect which is displayed by lava: + * A particle effect which is displayed by swimming entities and arrows in water: * */ - LAVA("lava"), + WATER_BUBBLE("bubble", 4, -1, false, true), /** - * A particle effect which is currently unused: - * - */ - FOOTSTEP("footstep"), - /** - * A particle effect which is displayed by swimming entities, rain dropping on the ground and shaking wolves: + * A particle effect which is displayed by swimming entities and shaking wolves: * */ - SPLASH("splash"), + WATER_SPLASH("splash", 5, -1), /** * A particle effect which is displayed on water when fishing: * */ - WAKE("wake"), + WATER_WAKE("wake", 6, 7), + /** + * A particle effect which is displayed by water: + * + */ + SUSPENDED("suspended", 7, -1, false, true), + /** + * A particle effect which is displayed by air when close to bedrock and the in the void: + * + */ + SUSPENDED_DEPTH("depthSuspend", 8, -1), + /** + * A particle effect which is displayed when landing a critical hit and by arrows: + * + */ + CRIT("crit", 9, -1), + /** + * A particle effect which is displayed when landing a hit with an enchanted weapon: + * + */ + CRIT_MAGIC("magicCrit", 10, -1), + /** + * A particle effect which is displayed by primed tnt, torches, droppers, dispensers, end portals, brewing stands and monster spawners: + * + */ + SMOKE_NORMAL("smoke", 11, -1), /** * A particle effect which is displayed by fire, minecarts with furnace and blazes: * */ - LARGE_SMOKE("largesmoke"), + SMOKE_LARGE("largesmoke", 12, -1), /** - * A particle effect which is displayed when a mob dies: + * A particle effect which is displayed when splash potions or bottles o' enchanting hit something: * */ - CLOUD("cloud"), + SPELL("spell", 13, -1), /** - * A particle effect which is displayed by redstone ore, powered redstone, redstone torches and redstone repeaters: + * A particle effect which is displayed when instant splash potions hit something: * */ - RED_DUST("reddust"), + SPELL_INSTANT("instantSpell", 14, -1), /** - * A particle effect which is displayed when snowballs or eggs hit something: + * A particle effect which is displayed by entities with active potion effects: * */ - SNOWBALL_POOF("snowballpoof"), + SPELL_MOB("mobSpell", 15, -1), + /** + * A particle effect which is displayed by entities with active potion effects applied through a beacon: + * + */ + SPELL_MOB_AMBIENT("mobSpellAmbient", 16, -1), + /** + * A particle effect which is displayed by witches: + * + */ + SPELL_WITCH("witchMagic", 17, -1), /** * A particle effect which is displayed by blocks beneath a water source: * */ - DRIP_WATER("dripWater"), + DRIP_WATER("dripWater", 18, -1), /** * A particle effect which is displayed by blocks beneath a lava source: * */ - DRIP_LAVA("dripLava"), - /** - * A particle effect which is currently unused: - * - */ - SNOW_SHOVEL("snowshovel"), - /** - * A particle effect which is displayed by slimes: - * - */ - SLIME("slime"), - /** - * A particle effect which is displayed when breeding and taming animals: - * - */ - HEART("heart"), + DRIP_LAVA("dripLava", 19, -1), /** * A particle effect which is displayed when attacking a villager in a village: * */ - ANGRY_VILLAGER("angryVillager"), + VILLAGER_ANGRY("angryVillager", 20, -1), /** * A particle effect which is displayed when using bone meal and trading with a villager in a village: * */ - HAPPY_VILLAGER("happyVillager"); + VILLAGER_HAPPY("happyVillager", 21, -1), + /** + * A particle effect which is displayed by mycelium: + * + */ + TOWN_AURA("townaura", 22, -1), + /** + * A particle effect which is displayed by note blocks: + * + */ + NOTE("note", 23, -1), + /** + * A particle effect which is displayed by nether portals, endermen, ender pearls, eyes of ender, ender chests and dragon eggs: + * + */ + PORTAL("portal", 24, -1), + /** + * A particle effect which is displayed by enchantment tables which are nearby bookshelves: + * + */ + ENCHANTMENT_TABLE("enchantmenttable", 25, -1), + /** + * A particle effect which is displayed by torches, active furnaces, magma cubes and monster spawners: + * + */ + FLAME("flame", 26, -1), + /** + * A particle effect which is displayed by lava: + * + */ + LAVA("lava", 27, -1), + /** + * A particle effect which is currently unused: + * + */ + FOOTSTEP("footstep", 28, -1), + /** + * A particle effect which is displayed when a mob dies: + * + */ + CLOUD("cloud", 29, -1), + /** + * A particle effect which is displayed by redstone ore, powered redstone, redstone torches and redstone repeaters: + * + */ + REDSTONE("reddust", 30, -1), + /** + * A particle effect which is displayed when snowballs hit a block: + * + */ + SNOWBALL("snowballpoof", 31, -1), + /** + * A particle effect which is currently unused: + * + */ + SNOW_SHOVEL("snowshovel", 32, -1), + /** + * A particle effect which is displayed by slimes: + * + */ + SLIME("slime", 33, -1), + /** + * A particle effect which is displayed when breeding and taming animals: + * + */ + HEART("heart", 34, -1), + /** + * A particle effect which is displayed by barriers: + * + */ + BARRIER("barrier", 35, 8), + /** + * A particle effect which is displayed when breaking a tool or eggs hit a block: + * + */ + ITEM_CRACK("iconcrack", 36, -1, true), + /** + * A particle effect which is displayed when breaking blocks or sprinting: + * + */ + BLOCK_CRACK("blockcrack", 37, -1, true), + /** + * A particle effect which is displayed when falling: + * + */ + BLOCK_DUST("blockdust", 38, 7, true), + /** + * A particle effect which is displayed when rain hits the ground: + * + */ + WATER_DROP("droplet", 39, 8), + /** + * A particle effect which is currently unused: + * + */ + ITEM_TAKE("take", 40, 8), + /** + * A particle effect which is displayed by elder guardians: + * + */ + MOB_APPEARANCE("mobappearance", 41, 8); private static final Map NAME_MAP = new HashMap(); + private static final Map ID_MAP = new HashMap(); private final String name; + private final int id; + private final int requiredVersion; + private final boolean requiresData; private final boolean requiresWater; - // Initialize map for quick name lookup + // Initialize map for quick name and id lookup static { for (ParticleEffect effect : values()) { NAME_MAP.put(effect.name, effect); + ID_MAP.put(effect.id, effect); } } @@ -330,10 +397,16 @@ public enum ParticleEffect { * Construct a new particle effect * * @param name Name of this particle effect + * @param id Id of this particle effect + * @param requiredVersion Version which is required (1.x) + * @param requiresData Indicates whether additional data is required for this particle effect * @param requiresWater Indicates whether water is required for this particle effect to display properly */ - private ParticleEffect(String name, boolean requiresWater) { + private ParticleEffect(String name, int id, int requiredVersion, boolean requiresData, boolean requiresWater) { this.name = name; + this.id = id; + this.requiredVersion = requiredVersion; + this.requiresData = requiresData; this.requiresWater = requiresWater; } @@ -341,10 +414,26 @@ public enum ParticleEffect { * Construct a new particle effect with {@link #requiresWater} set to false * * @param name Name of this particle effect - * @see #ParticleEffect(String, boolean) + * @param id Id of this particle effect + * @param requiredVersion Version which is required (1.x) + * @param requiresData Indicates whether additional data is required for this particle effect + * @see #ParticleEffect(String, int, boolean, boolean) */ - private ParticleEffect(String name) { - this(name, false); + private ParticleEffect(String name, int id, int requiredVersion, boolean requiresData) { + this(name, id, requiredVersion, requiresData, false); + } + + /** + * Construct a new particle effect with {@link #requiresData} and {@link #requiresWater} set to false + * + * @param name Name of this particle effect + * @param id Id of this particle effect + * @param requiredVersion Version which is required (1.x) + * @param requiresData Indicates whether additional data is required for this particle effect + * @see #ParticleEffect(String, int, boolean) + */ + private ParticleEffect(String name, int id, int requiredVersion) { + this(name, id, requiredVersion, false); } /** @@ -356,6 +445,33 @@ public enum ParticleEffect { return name; } + /** + * Returns the id of this particle effect + * + * @return The id + */ + public int getId() { + return id; + } + + /** + * Returns the required version for this particle effect (1.x) + * + * @return The required version + */ + public int getRequiredVersion() { + return requiredVersion; + } + + /** + * Determine if additional data is required for this particle effect + * + * @return Whether additional data is required or not + */ + public boolean getRequiresData() { + return requiresData; + } + /** * Determine if water is required for this particle effect to display properly * @@ -365,6 +481,18 @@ public enum ParticleEffect { return requiresWater; } + /** + * Determine if this particle effect is supported by your current server version + * + * @return Whether the particle effect is supported or not + */ + public boolean isSupported() { + if (requiredVersion == -1) { + return true; + } + return ParticlePacket.getVersion() >= requiredVersion; + } + /** * Returns the particle effect with the given name * @@ -381,6 +509,22 @@ public enum ParticleEffect { return null; } + /** + * Returns the particle effect with the given id + * + * @param id Id of the particle effect + * @return The particle effect + */ + public static ParticleEffect fromId(int id) { + for (Entry entry : ID_MAP.entrySet()) { + if (entry.getKey() != id) { + continue; + } + return entry.getValue(); + } + return null; + } + /** * Determine if water is at a certain location * @@ -393,15 +537,19 @@ public enum ParticleEffect { } /** - * Determine if an id is a block id + * Determine if the distance between @param location and one of the players exceeds 256 * - * @param id Id to check - * @return Whether id is a block or not + * @param location Location to check + * @return Whether the distance exceeds 256 or not */ - @SuppressWarnings("deprecation") - private static boolean isBlock(int id) { - Material material = Material.getMaterial(id); - return material != null && material.isBlock(); + private static boolean isLongDistance(Location location, List players) { + for (Player player : players) { + if (player.getLocation().distance(location) < 256) { + continue; + } + return true; + } + return false; } /** @@ -413,16 +561,24 @@ public enum ParticleEffect { * @param speed Display speed of the particles * @param amount Amount of particles * @param center Center location of the effect - * @param range Range of the visibility (Maximum range for particles is usually 16, but it can differ for some types) + * @param range Range of the visibility + * @throws ParticleVersionException If the particle effect is not supported by the server version + * @throws ParticleDataException If the particle effect requires additional data * @throws IllegalArgumentException If the particle effect requires water and none is at the center location - * @see ParticleEffectPacket - * @see ParticleEffectPacket#sendTo(Location, double) + * @see ParticlePacket + * @see ParticlePacket#sendTo(Location, double) */ - public void display(float offsetX, float offsetY, float offsetZ, float speed, int amount, Location center, double range) throws IllegalArgumentException { + public void display(float offsetX, float offsetY, float offsetZ, float speed, int amount, Location center, double range) throws ParticleVersionException, ParticleDataException, IllegalArgumentException { + if (!isSupported()) { + throw new ParticleVersionException("This particle effect is not supported by your server version"); + } + if (requiresData) { + throw new ParticleDataException("This particle effect requires additional data"); + } if (requiresWater && !isWater(center)) { throw new IllegalArgumentException("There is no water at the center location"); } - new ParticleEffectPacket(name, offsetX, offsetY, offsetZ, speed, amount).sendTo(center, range); + new ParticlePacket(this, offsetX, offsetY, offsetZ, speed, amount, range > 256, null).sendTo(center, range); } /** @@ -435,41 +591,107 @@ public enum ParticleEffect { * @param amount Amount of particles * @param center Center location of the effect * @param players Receivers of the effect + * @throws ParticleVersionException If the particle effect is not supported by the server version + * @throws ParticleDataException If the particle effect requires additional data * @throws IllegalArgumentException If the particle effect requires water and none is at the center location - * @see ParticleEffectPacket - * @see ParticleEffectPacket#sendTo(Location, List) + * @see ParticlePacket + * @see ParticlePacket#sendTo(Location, List) */ - public void display(float offsetX, float offsetY, float offsetZ, float speed, int amount, Location center, List players) throws IllegalArgumentException { + public void display(float offsetX, float offsetY, float offsetZ, float speed, int amount, Location center, List players) throws ParticleVersionException, ParticleDataException, IllegalArgumentException { + if (!isSupported()) { + throw new ParticleVersionException("This particle effect is not supported by your server version"); + } + if (requiresData) { + throw new ParticleDataException("This particle effect requires additional data"); + } if (requiresWater && !isWater(center)) { throw new IllegalArgumentException("There is no water at the center location"); } - new ParticleEffectPacket(name, offsetX, offsetY, offsetZ, speed, amount).sendTo(center, players); + new ParticlePacket(this, offsetX, offsetY, offsetZ, speed, amount, isLongDistance(center, players), null).sendTo(center, players); } /** - * Displays an icon crack (item break) particle effect which is only visible for all players within a certain range in the world of @param center + * Displays a single particle which flies into a determined direction and is only visible for all players within a certain range in the world of @param center * - * @param id Id of the icon - * @param data Data value + * @param direction Direction of the particle + * @param speed Display speed of the particle + * @param center Center location of the effect + * @param range Range of the visibility + * @throws ParticleVersionException If the particle effect is not supported by the server version + * @throws ParticleDataException If the particle effect requires additional data + * @throws IllegalArgumentException If the particle effect requires water and none is at the center location + * @see ParticlePacket + * @see ParticlePacket#sendTo(Location, double) + */ + public void display(Vector direction, float speed, Location center, double range) throws ParticleVersionException, ParticleDataException, IllegalArgumentException { + if (!isSupported()) { + throw new ParticleVersionException("This particle effect is not supported by your server version"); + } + if (requiresData) { + throw new ParticleDataException("This particle effect requires additional data"); + } + if (requiresWater && !isWater(center)) { + throw new IllegalArgumentException("There is no water at the center location"); + } + new ParticlePacket(this, direction, speed, range > 256, null).sendTo(center, range); + } + + /** + * Displays a single particle which flies into a determined direction and is only visible for the specified players + * + * @param direction Direction of the particle + * @param speed Display speed of the particle + * @param center Center location of the effect + * @param players Receivers of the effect + * @throws ParticleVersionException If the particle effect is not supported by the server version + * @throws ParticleDataException If the particle effect requires additional data + * @throws IllegalArgumentException If the particle effect requires water and none is at the center location + * @see ParticlePacket + * @see ParticlePacket#sendTo(Location, List) + */ + public void display(Vector direction, float speed, Location center, List players) throws ParticleVersionException, ParticleDataException, IllegalArgumentException { + if (!isSupported()) { + throw new ParticleVersionException("This particle effect is not supported by your server version"); + } + if (requiresData) { + throw new ParticleDataException("This particle effect requires additional data"); + } + if (requiresWater && !isWater(center)) { + throw new IllegalArgumentException("There is no water at the center location"); + } + new ParticlePacket(this, direction, speed, isLongDistance(center, players), null).sendTo(center, players); + } + + /** + * Displays a particle effect which requires additional data and is only visible for all players within a certain range in the world of @param center + * + * @param data Data of the effect * @param offsetX Maximum distance particles can fly away from the center on the x-axis * @param offsetY Maximum distance particles can fly away from the center on the y-axis * @param offsetZ Maximum distance particles can fly away from the center on the z-axis * @param speed Display speed of the particles * @param amount Amount of particles * @param center Center location of the effect - * @param range Range of the visibility (Maximum range for particles is usually 16, but it can differ for some types) - * @see ParticleEffectPacket - * @see ParticleEffectPacket#sendTo(Location, double) + * @param range Range of the visibility + * @throws ParticleVersionException If the particle effect is not supported by the server version + * @throws ParticleDataException If the particle effect does not require additional data + * @see ParticlePacket + * @see ParticlePacket#sendTo(Location, double) */ - public static void displayIconCrack(int id, byte data, float offsetX, float offsetY, float offsetZ, float speed, int amount, Location center, double range) { - new ParticleEffectPacket("iconcrack_" + id + "_" + data, offsetX, offsetY, offsetZ, speed, amount).sendTo(center, range); + public void display(ParticleData data, float offsetX, float offsetY, float offsetZ, float speed, int amount, Location center, double range) throws ParticleVersionException, ParticleDataException { + if (!isSupported()) { + throw new ParticleVersionException("This particle effect is not supported by your server version"); + } + if (!requiresData) { + throw new ParticleDataException("This particle effect does not require additional data"); + } + new ParticlePacket(this, offsetX, offsetY, offsetZ, speed, amount, range > 256, data).sendTo(center, range); } /** - * Displays an icon crack (item break) particle effect which is only visible for the specified players + * Displays a particle effect which requires additional data and is only visible for the specified players * - * @param id Id of the icon - * @param data Data value + * @param data Data of the effect * @param offsetX Maximum distance particles can fly away from the center on the x-axis * @param offsetY Maximum distance particles can fly away from the center on the y-axis * @param offsetZ Maximum distance particles can fly away from the center on the z-axis @@ -477,101 +699,218 @@ public enum ParticleEffect { * @param amount Amount of particles * @param center Center location of the effect * @param players Receivers of the effect - * @see ParticleEffectPacket - * @see ParticleEffectPacket#sendTo(Location, List) + * @throws ParticleVersionException If the particle effect is not supported by the server version + * @throws ParticleDataException If the particle effect does not require additional data + * @see ParticlePacket + * @see ParticlePacket#sendTo(Location, List) */ - public static void displayIconCrack(int id, byte data, float offsetX, float offsetY, float offsetZ, float speed, int amount, Location center, List players) { - new ParticleEffectPacket("iconcrack_" + id + "_" + data, offsetX, offsetY, offsetZ, speed, amount).sendTo(center, players); - } - - /** - * Displays a block crack (block break) particle effect which is only visible for all players within a certain range in the world of @param center - * - * @param id Id of the block - * @param data Data value - * @param offsetX Maximum distance particles can fly away from the center on the x-axis - * @param offsetY Maximum distance particles can fly away from the center on the y-axis - * @param offsetZ Maximum distance particles can fly away from the center on the z-axis - * @param amount Amount of particles - * @param center Center location of the effect - * @param range Range of the visibility (Maximum range for particles is usually 16, but it can differ for some types) - * @throws IllegalArgumentException If the specified id is not a block id - * @see ParticleEffectPacket - * @see ParticleEffectPacket#sendTo(Location, double) - */ - public static void displayBlockCrack(int id, byte data, float offsetX, float offsetY, float offsetZ, int amount, Location center, double range) throws IllegalArgumentException { - if (!isBlock(id)) { - throw new IllegalArgumentException("Invalid block id"); + public void display(ParticleData data, float offsetX, float offsetY, float offsetZ, float speed, int amount, Location center, List players) throws ParticleVersionException, ParticleDataException { + if (!isSupported()) { + throw new ParticleVersionException("This particle effect is not supported by your server version"); } - new ParticleEffectPacket("blockcrack_" + id + "_" + data, offsetX, offsetY, offsetZ, 0, amount).sendTo(center, range); + if (!requiresData) { + throw new ParticleDataException("This particle effect does not require additional data"); + } + new ParticlePacket(this, offsetX, offsetY, offsetZ, speed, amount, isLongDistance(center, players), data).sendTo(center, players); } /** - * Displays a block crack (block break) particle effect which is only visible for the specified players + * Displays a single particle which requires additional data that flies into a determined direction and is only visible for all players within a certain range in the world of @param center * - * @param id Id of the block - * @param data Data value - * @param offsetX Maximum distance particles can fly away from the center on the x-axis - * @param offsetY Maximum distance particles can fly away from the center on the y-axis - * @param offsetZ Maximum distance particles can fly away from the center on the z-axis - * @param amount Amount of particles + * @param data Data of the effect + * @param direction Direction of the particle + * @param speed Display speed of the particles + * @param center Center location of the effect + * @param range Range of the visibility + * @throws ParticleVersionException If the particle effect is not supported by the server version + * @throws ParticleDataException If the particle effect does not require additional data + * @see ParticlePacket + * @see ParticlePacket#sendTo(Location, double) + */ + public void display(ParticleData data, Vector direction, float speed, Location center, double range) throws ParticleVersionException, ParticleDataException { + if (!isSupported()) { + throw new ParticleVersionException("This particle effect is not supported by your server version"); + } + if (!requiresData) { + throw new ParticleDataException("This particle effect does not require additional data"); + } + new ParticlePacket(this, direction, speed, range > 256, data).sendTo(center, range); + } + + /** + * Displays a single particle which requires additional data that flies into a determined direction and is only visible for the specified players + * + * @param data Data of the effect + * @param direction Direction of the particle + * @param speed Display speed of the particles * @param center Center location of the effect * @param players Receivers of the effect - * @throws IllegalArgumentException If the specified id is not a block id - * @see ParticleEffectPacket - * @see ParticleEffectPacket#sendTo(Location, List) + * @throws ParticleVersionException If the particle effect is not supported by the server version + * @throws ParticleDataException If the particle effect does not require additional data + * @see ParticlePacket + * @see ParticlePacket#sendTo(Location, List) */ - public static void displayBlockCrack(int id, byte data, float offsetX, float offsetY, float offsetZ, int amount, Location center, List players) throws IllegalArgumentException { - if (!isBlock(id)) { - throw new IllegalArgumentException("Invalid block id"); + public void display(ParticleData data, Vector direction, float speed, Location center, List players) throws ParticleVersionException, ParticleDataException { + if (!isSupported()) { + throw new ParticleVersionException("This particle effect is not supported by your server version"); } - new ParticleEffectPacket("blockcrack_" + id + "_" + data, offsetX, offsetY, offsetZ, 0, amount).sendTo(center, players); + if (!requiresData) { + throw new ParticleDataException("This particle effect does not require additional data"); + } + new ParticlePacket(this, direction, speed, isLongDistance(center, players), data).sendTo(center, players); } /** - * Displays a block dust particle effect which is only visible for all players within a certain range in the world of @param center + * Represents the particle data for effects like {@link ParticleEffect#ITEM_CRACK}, {@link ParticleEffect#BLOCK_CRACK} and {@link ParticleEffect#BLOCK_DUST} + *

+ * This class is part of the ParticleEffect Library and follows the same usage conditions * - * @param id Id of the block - * @param data Data value - * @param offsetX Maximum distance particles can fly away from the center on the x-axis - * @param offsetY Maximum distance particles can fly away from the center on the y-axis - * @param offsetZ Maximum distance particles can fly away from the center on the z-axis - * @param speed Display speed of the particles - * @param amount Amount of particles - * @param center Center location of the effect - * @param range Range of the visibility (Maximum range for particles is usually 16, but it can differ for some types) - * @throws IllegalArgumentException If the specified id is not a block id - * @see ParticleEffectPacket - * @see ParticleEffectPacket#sendTo(Location, double) + * @author DarkBlade12 + * @since 1.6 */ - public static void displayBlockDust(int id, byte data, float offsetX, float offsetY, float offsetZ, float speed, int amount, Location center, double range) throws IllegalArgumentException { - if (!isBlock(id)) { - throw new IllegalArgumentException("Invalid block id"); + public static abstract class ParticleData { + private final Material material; + private final byte data; + private final int[] packetData; + + /** + * Construct a new particle data + * + * @param material Material of the item/block + * @param data Data value of the item/block + */ + @SuppressWarnings("deprecation") + public ParticleData(Material material, byte data) { + this.material = material; + this.data = data; + this.packetData = new int[] { material.getId(), data }; + } + + /** + * Returns the material of this data + * + * @return The material + */ + public Material getMaterial() { + return material; + } + + /** + * Returns the data value of this data + * + * @return The data value + */ + public byte getData() { + return data; + } + + /** + * Returns the data as an int array for packet construction + * + * @return The data for the packet + */ + public int[] getPacketData() { + return packetData; + } + + /** + * Returns the data as a string for pre 1.8 versions + * + * @return The data string for the packet + */ + public String getPacketDataString() { + return "_" + packetData[0] + "_" + packetData[1]; } - new ParticleEffectPacket("blockdust_" + id + "_" + data, offsetX, offsetY, offsetZ, speed, amount).sendTo(center, range); } /** - * Displays a block dust particle effect which is only visible for the specified players + * Represents the item data for the {@link ParticleEffect#ITEM_CRACK} effect + *

+ * This class is part of the ParticleEffect Library and follows the same usage conditions * - * @param id Id of the block - * @param data Data value - * @param offsetX Maximum distance particles can fly away from the center on the x-axis - * @param offsetY Maximum distance particles can fly away from the center on the y-axis - * @param offsetZ Maximum distance particles can fly away from the center on the z-axis - * @param speed Display speed of the particles - * @param amount Amount of particles - * @param center Center location of the effect - * @param players Receivers of the effect - * @throws IllegalArgumentException If the specified id is not a block id - * @see ParticleEffectPacket - * @see ParticleEffectPacket#sendTo(Location, List) + * @author DarkBlade12 + * @since 1.6 */ - public static void displayBlockDust(int id, byte data, float offsetX, float offsetY, float offsetZ, float speed, int amount, Location center, List players) throws IllegalArgumentException { - if (!isBlock(id)) { - throw new IllegalArgumentException("Invalid block id"); + public static final class ItemData extends ParticleData { + /** + * Construct a new item data + * + * @param material Material of the item + * @param data Data value of the item + * @see ParticleData#ParticleData(Material, byte) + */ + public ItemData(Material material, byte data) { + super(material, data); + } + } + + /** + * Represents the block data for the {@link ParticleEffect#BLOCK_CRACK} and {@link ParticleEffect#BLOCK_DUST} effects + *

+ * This class is part of the ParticleEffect Library and follows the same usage conditions + * + * @author DarkBlade12 + * @since 1.6 + */ + public static final class BlockData extends ParticleData { + /** + * Construct a new block data + * + * @param material Material of the block + * @param data Data value of the block + * @throws IllegalArgumentException If the material is not a block + * @see ParticleData#ParticleData(Material, byte) + */ + public BlockData(Material material, byte data) throws IllegalArgumentException { + super(material, data); + if (!material.isBlock()) { + throw new IllegalArgumentException("The material is not a block"); + } + } + } + + /** + * Represents a runtime exception that is thrown if the displayed particle effect requires data and has none or vice-versa + *

+ * This class is part of the ParticleEffect Library and follows the same usage conditions + * + * @author DarkBlade12 + * @since 1.6 + */ + private static final class ParticleDataException extends RuntimeException { + private static final long serialVersionUID = 3203085387160737484L; + + /** + * Construct a new particle data exception + * + * @param message Message that will be logged + * @param cause Cause of the exception + */ + public ParticleDataException(String message) { + super(message); + } + } + + /** + * Represents a runtime exception that is thrown if the displayed particle effect requires a newer version + *

+ * This class is part of the ParticleEffect Library and follows the same usage conditions + * + * @author DarkBlade12 + * @since 1.6 + */ + private static final class ParticleVersionException extends RuntimeException { + private static final long serialVersionUID = 3203085387160737484L; + + /** + * Construct a new particle version exception + * + * @param message Message that will be logged + * @param cause Cause of the exception + */ + public ParticleVersionException(String message) { + super(message); } - new ParticleEffectPacket("blockdust_" + id + "_" + data, offsetX, offsetY, offsetZ, speed, amount).sendTo(center, players); } /** @@ -582,33 +921,39 @@ public enum ParticleEffect { * @author DarkBlade12 * @since 1.5 */ - public static final class ParticleEffectPacket { + public static final class ParticlePacket { + private static int version; + private static Class enumParticle; private static Constructor packetConstructor; private static Method getHandle; private static Field playerConnection; private static Method sendPacket; private static boolean initialized; - private final String name; + private final ParticleEffect effect; private final float offsetX; private final float offsetY; private final float offsetZ; private final float speed; private final int amount; + private final boolean longDistance; + private final ParticleData data; private Object packet; /** - * Construct a new particle effect packet + * Construct a new particle packet * - * @param name Name of the effect + * @param effect Particle effect * @param offsetX Maximum distance particles can fly away from the center on the x-axis * @param offsetY Maximum distance particles can fly away from the center on the y-axis * @param offsetZ Maximum distance particles can fly away from the center on the z-axis * @param speed Display speed of the particles * @param amount Amount of particles + * @param longDistance Indicates whether the maximum distance is increased from 256 to 65536 + * @param data Data of the effect * @throws IllegalArgumentException If the speed is lower than 0 or the amount is lower than 1 * @see #initialize() */ - public ParticleEffectPacket(String name, float offsetX, float offsetY, float offsetZ, float speed, int amount) throws IllegalArgumentException { + public ParticlePacket(ParticleEffect effect, float offsetX, float offsetY, float offsetZ, float speed, int amount, boolean longDistance, ParticleData data) throws IllegalArgumentException { initialize(); if (speed < 0) { throw new IllegalArgumentException("The speed is lower than 0"); @@ -616,12 +961,40 @@ public enum ParticleEffect { if (amount < 1) { throw new IllegalArgumentException("The amount is lower than 1"); } - this.name = name; + this.effect = effect; this.offsetX = offsetX; this.offsetY = offsetY; this.offsetZ = offsetZ; this.speed = speed; this.amount = amount; + this.longDistance = longDistance; + this.data = data; + } + + /** + * Construct a new particle packet of a single particle flying into a determined direction + * + * @param effect Particle effect + * @param direction Direction of the particle + * @param speed Display speed of the particle + * @param longDistance Indicates whether the maximum distance is increased from 256 to 65536 + * @param data Data of the effect + * @throws IllegalArgumentException If the speed is lower than 0 + * @see #initialize() + */ + public ParticlePacket(ParticleEffect effect, Vector direction, float speed, boolean longDistance, ParticleData data) throws IllegalArgumentException { + initialize(); + if (speed < 0) { + throw new IllegalArgumentException("The speed is lower than 0"); + } + this.effect = effect; + this.offsetX = (float) direction.getX(); + this.offsetY = (float) direction.getY(); + this.offsetZ = (float) direction.getZ(); + this.speed = speed; + this.amount = 0; + this.longDistance = longDistance; + this.data = data; } /** @@ -629,15 +1002,18 @@ public enum ParticleEffect { *

* Note: These fields only have to be initialized once, so it will return if {@link #initialized} is already set to true * - * @throws VersionIncompatibleException if accessed packets, fields or methods differ in your bukkit version + * @throws VersionIncompatibleException if your bukkit version is not supported by this library */ public static void initialize() throws VersionIncompatibleException { if (initialized) { return; } try { - int version = Integer.parseInt(Character.toString(PackageType.getServerVersion().charAt(3))); - Class packetClass = PackageType.MINECRAFT_SERVER.getClass(version < 7 ? "Packet63WorldParticles" : PacketType.PLAY_OUT_WORLD_PARTICLES.getName()); + version = Integer.parseInt(Character.toString(PackageType.getServerVersion().charAt(3))); + if (version > 7) { + enumParticle = PackageType.MINECRAFT_SERVER.getClass("EnumParticle"); + } + Class packetClass = PackageType.MINECRAFT_SERVER.getClass(version < 7 ? "Packet63WorldParticles" : "PacketPlayOutWorldParticles"); packetConstructor = ReflectionUtils.getConstructor(packetClass); getHandle = ReflectionUtils.getMethod("CraftPlayer", PackageType.CRAFTBUKKIT_ENTITY, "getHandle"); playerConnection = ReflectionUtils.getField("EntityPlayer", PackageType.MINECRAFT_SERVER, false, "playerConnection"); @@ -648,6 +1024,15 @@ public enum ParticleEffect { initialized = true; } + /** + * Returns the version of your server (1.x) + * + * @return The version number + */ + public static int getVersion() { + return version; + } + /** * Determine if {@link #packetConstructor}, {@link #getHandle}, {@link #playerConnection} and {@link #sendPacket} are initialized * @@ -670,7 +1055,16 @@ public enum ParticleEffect { if (packet == null) { try { packet = packetConstructor.newInstance(); - ReflectionUtils.setValue(packet, true, "a", name); + Object id; + if (version < 8) { + id = effect.getName(); + if (data != null) { + id = (String)id + data.getPacketDataString(); + } + } else { + id = enumParticle.getEnumConstants()[effect.getId()]; + } + ReflectionUtils.setValue(packet, true, "a", id); ReflectionUtils.setValue(packet, true, "b", (float) center.getX()); ReflectionUtils.setValue(packet, true, "c", (float) center.getY()); ReflectionUtils.setValue(packet, true, "d", (float) center.getZ()); @@ -679,6 +1073,10 @@ public enum ParticleEffect { ReflectionUtils.setValue(packet, true, "g", offsetZ); ReflectionUtils.setValue(packet, true, "h", speed); ReflectionUtils.setValue(packet, true, "i", amount); + if (version > 7) { + ReflectionUtils.setValue(packet, true, "j", longDistance); + ReflectionUtils.setValue(packet, true, "k", data == null ? new int[0] : data.getPacketData()); + } } catch (Exception exception) { throw new PacketInstantiationException("Packet instantiation failed", exception); } diff --git a/src/main/java/com/massivecraft/massivecore/particleeffect/ReflectionUtils.java b/src/main/java/com/massivecraft/massivecore/particleeffect/ReflectionUtils.java index 33abc67a..ef48a8a5 100644 --- a/src/main/java/com/massivecraft/massivecore/particleeffect/ReflectionUtils.java +++ b/src/main/java/com/massivecraft/massivecore/particleeffect/ReflectionUtils.java @@ -26,7 +26,7 @@ import org.bukkit.Bukkit; * @version 1.1 */ public final class ReflectionUtils { - // Prevent accidential construction + // Prevent accidental construction private ReflectionUtils() {} /** @@ -603,156 +603,4 @@ public final class ReflectionUtils { return true; } } - - /** - * Represents an enumeration of all packet types that are featured in Minecraft 1.7.10 - *

- * If this enumeration is no longer up-to-date, please let me know in my forum post - *

- * This class is part of the ReflectionUtils and follows the same usage conditions - * - * @author DarkBlade12 - * @since 1.0 - */ - public enum PacketType { - HANDSHAKING_IN_SET_PROTOCOL("PacketHandshakingInSetProtocol"), - LOGIN_IN_ENCRYPTION_BEGIN("PacketLoginInEncryptionBegin"), - LOGIN_IN_START("PacketLoginInStart"), - LOGIN_OUT_DISCONNECT("PacketLoginOutDisconnect"), - LOGIN_OUT_ENCRYPTION_BEGIN("PacketLoginOutEncryptionBegin"), - LOGIN_OUT_SUCCESS("PacketLoginOutSuccess"), - PLAY_IN_ABILITIES("PacketPlayInAbilities"), - PLAY_IN_ARM_ANIMATION("PacketPlayInArmAnimation"), - PLAY_IN_BLOCK_DIG("PacketPlayInBlockDig"), - PLAY_IN_BLOCK_PLACE("PacketPlayInBlockPlace"), - PLAY_IN_CHAT("PacketPlayInChat"), - PLAY_IN_CLIENT_COMMAND("PacketPlayInClientCommand"), - PLAY_IN_CLOSE_WINDOW("PacketPlayInCloseWindow"), - PLAY_IN_CUSTOM_PAYLOAD("PacketPlayInCustomPayload"), - PLAY_IN_ENCHANT_ITEM("PacketPlayInEnchantItem"), - PLAY_IN_ENTITY_ACTION("PacketPlayInEntityAction"), - PLAY_IN_FLYING("PacketPlayInFlying"), - PLAY_IN_HELD_ITEM_SLOT("PacketPlayInHeldItemSlot"), - PLAY_IN_KEEP_ALIVE("PacketPlayInKeepAlive"), - PLAY_IN_LOOK("PacketPlayInLook"), - PLAY_IN_POSITION("PacketPlayInPosition"), - PLAY_IN_POSITION_LOOK("PacketPlayInPositionLook"), - PLAY_IN_SET_CREATIVE_SLOT("PacketPlayInSetCreativeSlot "), - PLAY_IN_SETTINGS("PacketPlayInSettings"), - PLAY_IN_STEER_VEHICLE("PacketPlayInSteerVehicle"), - PLAY_IN_TAB_COMPLETE("PacketPlayInTabComplete"), - PLAY_IN_TRANSACTION("PacketPlayInTransaction"), - PLAY_IN_UPDATE_SIGN("PacketPlayInUpdateSign"), - PLAY_IN_USE_ENTITY("PacketPlayInUseEntity"), - PLAY_IN_WINDOW_CLICK("PacketPlayInWindowClick"), - PLAY_OUT_ABILITIES("PacketPlayOutAbilities"), - PLAY_OUT_ANIMATION("PacketPlayOutAnimation"), - PLAY_OUT_ATTACH_ENTITY("PacketPlayOutAttachEntity"), - PLAY_OUT_BED("PacketPlayOutBed"), - PLAY_OUT_BLOCK_ACTION("PacketPlayOutBlockAction"), - PLAY_OUT_BLOCK_BREAK_ANIMATION("PacketPlayOutBlockBreakAnimation"), - PLAY_OUT_BLOCK_CHANGE("PacketPlayOutBlockChange"), - PLAY_OUT_CHAT("PacketPlayOutChat"), - PLAY_OUT_CLOSE_WINDOW("PacketPlayOutCloseWindow"), - PLAY_OUT_COLLECT("PacketPlayOutCollect"), - PLAY_OUT_CRAFT_PROGRESS_BAR("PacketPlayOutCraftProgressBar"), - PLAY_OUT_CUSTOM_PAYLOAD("PacketPlayOutCustomPayload"), - PLAY_OUT_ENTITY("PacketPlayOutEntity"), - PLAY_OUT_ENTITY_DESTROY("PacketPlayOutEntityDestroy"), - PLAY_OUT_ENTITY_EFFECT("PacketPlayOutEntityEffect"), - PLAY_OUT_ENTITY_EQUIPMENT("PacketPlayOutEntityEquipment"), - PLAY_OUT_ENTITY_HEAD_ROTATION("PacketPlayOutEntityHeadRotation"), - PLAY_OUT_ENTITY_LOOK("PacketPlayOutEntityLook"), - PLAY_OUT_ENTITY_METADATA("PacketPlayOutEntityMetadata"), - PLAY_OUT_ENTITY_STATUS("PacketPlayOutEntityStatus"), - PLAY_OUT_ENTITY_TELEPORT("PacketPlayOutEntityTeleport"), - PLAY_OUT_ENTITY_VELOCITY("PacketPlayOutEntityVelocity"), - PLAY_OUT_EXPERIENCE("PacketPlayOutExperience"), - PLAY_OUT_EXPLOSION("PacketPlayOutExplosion"), - PLAY_OUT_GAME_STATE_CHANGE("PacketPlayOutGameStateChange"), - PLAY_OUT_HELD_ITEM_SLOT("PacketPlayOutHeldItemSlot"), - PLAY_OUT_KEEP_ALIVE("PacketPlayOutKeepAlive"), - PLAY_OUT_KICK_DISCONNECT("PacketPlayOutKickDisconnect"), - PLAY_OUT_LOGIN("PacketPlayOutLogin"), - PLAY_OUT_MAP("PacketPlayOutMap"), - PLAY_OUT_MAP_CHUNK("PacketPlayOutMapChunk"), - PLAY_OUT_MAP_CHUNK_BULK("PacketPlayOutMapChunkBulk"), - PLAY_OUT_MULTI_BLOCK_CHANGE("PacketPlayOutMultiBlockChange"), - PLAY_OUT_NAMED_ENTITY_SPAWN("PacketPlayOutNamedEntitySpawn"), - PLAY_OUT_NAMED_SOUND_EFFECT("PacketPlayOutNamedSoundEffect"), - PLAY_OUT_OPEN_SIGN_EDITOR("PacketPlayOutOpenSignEditor"), - PLAY_OUT_OPEN_WINDOW("PacketPlayOutOpenWindow"), - PLAY_OUT_PLAYER_INFO("PacketPlayOutPlayerInfo"), - PLAY_OUT_POSITION("PacketPlayOutPosition"), - PLAY_OUT_REL_ENTITY_MOVE("PacketPlayOutRelEntityMove"), - PLAY_OUT_REL_ENTITY_MOVE_LOOK("PacketPlayOutRelEntityMoveLook"), - PLAY_OUT_REMOVE_ENTITY_EFFECT("PacketPlayOutRemoveEntityEffect"), - PLAY_OUT_RESPAWN("PacketPlayOutRespawn"), - PLAY_OUT_SCOREBOARD_DISPLAY_OBJECTIVE("PacketPlayOutScoreboardDisplayObjective"), - PLAY_OUT_SCOREBOARD_OBJECTIVE("PacketPlayOutScoreboardObjective"), - PLAY_OUT_SCOREBOARD_SCORE("PacketPlayOutScoreboardScore"), - PLAY_OUT_SCOREBOARD_TEAM("PacketPlayOutScoreboardTeam"), - PLAY_OUT_SET_SLOT("PacketPlayOutSetSlot"), - PLAY_OUT_SPAWN_ENTITY("PacketPlayOutSpawnEntity"), - PLAY_OUT_SPAWN_ENTITY_EXPERIENCE_ORB("PacketPlayOutSpawnEntityExperienceOrb"), - PLAY_OUT_SPAWN_ENTITY_LIVING("PacketPlayOutSpawnEntityLiving"), - PLAY_OUT_SPAWN_ENTITY_PAINTING("PacketPlayOutSpawnEntityPainting"), - PLAY_OUT_SPAWN_ENTITY_WEATHER("PacketPlayOutSpawnEntityWeather"), - PLAY_OUT_SPAWN_POSITION("PacketPlayOutSpawnPosition"), - PLAY_OUT_STATISTIC("PacketPlayOutStatistic"), - PLAY_OUT_TAB_COMPLETE("PacketPlayOutTabComplete"), - PLAY_OUT_TILE_ENTITY_DATA("PacketPlayOutTileEntityData"), - PLAY_OUT_TRANSACTION("PacketPlayOutTransaction"), - PLAY_OUT_UPDATE_ATTRIBUTES("PacketPlayOutUpdateAttributes"), - PLAY_OUT_UPDATE_HEALTH("PacketPlayOutUpdateHealth"), - PLAY_OUT_UPDATE_SIGN("PacketPlayOutUpdateSign"), - PLAY_OUT_UPDATE_TIME("PacketPlayOutUpdateTime"), - PLAY_OUT_WINDOW_ITEMS("PacketPlayOutWindowItems"), - PLAY_OUT_WORLD_EVENT("PacketPlayOutWorldEvent"), - PLAY_OUT_WORLD_PARTICLES("PacketPlayOutWorldParticles"), - STATUS_IN_PING("PacketStatusInPing"), - STATUS_IN_START("PacketStatusInStart"), - STATUS_OUT_PONG("PacketStatusOutPong"), - STATUS_OUT_SERVER_INFO("PacketStatusOutServerInfo"); - - private static final Map NAME_MAP = new HashMap(); - private final String name; - private Class packet; - - // Initialize map for quick name lookup - static { - for (PacketType type : values()) { - NAME_MAP.put(type.name, type); - } - } - - /** - * Construct a new packet type - * - * @param name Name of this packet - */ - private PacketType(String name) { - this.name = name; - } - - /** - * Returns the name of this packet type - * - * @return The name - */ - public String getName() { - return name; - } - - /** - * Returns the class of this packet - * - * @return The packet class - * @throws ClassNotFoundException If the packet class cannot be found (the name differs in your Bukkit version) - * - */ - public Class getPacket() throws ClassNotFoundException { - return packet == null ? (packet = PackageType.MINECRAFT_SERVER.getClass(name)) : packet; - } - } } \ No newline at end of file