Version compatibility, craftbukkit error message

This commit is contained in:
Esophose 2020-05-04 20:50:02 -06:00
parent 26cf3d2d33
commit 2d55d21a2a
39 changed files with 1744 additions and 587 deletions

View file

@ -10,7 +10,7 @@ For information about how to use the plugin or API within the plugin, please ref
### Server Compatibility
This plugin is compatible with [Spigot](https://www.spigotmc.org/) and any forks, I recommend using [Paper](https://papermc.io/).
Using CraftBukkit will not work.
The versions of Minecraft that are currently supported are 1.9.4-1.15.x. Support for 1.8.8 will not be added.
The versions of Minecraft that are currently supported are `1.15.2-1.7.10`.
### Compilation

View file

@ -10,7 +10,7 @@ sourceCompatibility = 1.8
targetCompatibility = 1.8
compileJava.options.encoding = 'UTF-8'
group = 'dev.esophose'
version = '7.10'
version = '7.11'
java {
withJavadocJar()
@ -30,6 +30,8 @@ dependencies {
compile 'org.slf4j:slf4j-api:1.7.25'
compile 'org.slf4j:slf4j-nop:1.7.25'
compile 'com.zaxxer:HikariCP:3.2.0'
compile 'org.bstats:bstats-bukkit-lite:1.7'
shadow 'com.googlecode.json-simple:json-simple:1.1.1'
shadow 'org.jetbrains:annotations:16.0.2'
shadow 'me.clip:placeholderapi:2.10.4'
shadow 'org.xerial:sqlite-jdbc:3.23.1'
@ -39,6 +41,10 @@ dependencies {
shadowJar {
archiveClassifier.set(null)
minimize()
relocate('org.bstats', 'dev.esophose.playerparticles.libs.bstats')
relocate('org.slf4j', 'dev.esophose.playerparticles.libs.slf4j')
relocate('com.zaxxer.hikari', 'dev.esophose.playerparticles.libs.hikaricp')
}
processResources {

View file

@ -1,3 +1,9 @@
=== v7.11 ===
+ Added support for Spigot 1.8.8 and 1.7.10. I make no guarantees this will work perfectly.
* The plugin will now disable and print an error message on startup if the server is running CraftBukkit
* Removed deprecated API
* Other API-breaking changes (Moved the color data classes to a different package)
* Removed the version number from generating in new config.yml files
=== v7.10 ===
+ Added command '/pp reset <player>' to be able to reset the particles of an offline player
- Permission: 'playerparticles.reset.others'

View file

@ -23,9 +23,11 @@ import dev.esophose.playerparticles.manager.PermissionManager;
import dev.esophose.playerparticles.manager.PluginUpdateManager;
import dev.esophose.playerparticles.particles.listener.PPlayerCombatListener;
import dev.esophose.playerparticles.particles.listener.PPlayerMovementListener;
import dev.esophose.playerparticles.util.Metrics;
import dev.esophose.playerparticles.util.LegacyMetrics;
import dev.esophose.playerparticles.util.NMSUtil;
import java.util.LinkedHashMap;
import java.util.Map;
import org.bstats.bukkit.MetricsLite;
import org.bukkit.Bukkit;
import org.bukkit.plugin.PluginManager;
import org.bukkit.plugin.java.JavaPlugin;
@ -55,6 +57,12 @@ public class PlayerParticles extends JavaPlugin {
*/
@Override
public void onEnable() {
if (!NMSUtil.isSpigot()) {
this.getLogger().severe("This plugin is only compatible with Spigot and other forks. CraftBukkit is not supported. Disabling PlayerParticles.");
Bukkit.getPluginManager().disablePlugin(this);
return;
}
this.reload();
PluginManager pm = Bukkit.getPluginManager();
@ -62,8 +70,13 @@ public class PlayerParticles extends JavaPlugin {
pm.registerEvents(new PPlayerCombatListener(), this);
pm.registerEvents(new PlayerChatHook(), this);
if (Setting.SEND_METRICS.getBoolean())
new Metrics(this);
if (Setting.SEND_METRICS.getBoolean()) {
if (NMSUtil.getVersionNumber() > 7) {
new MetricsLite(this, 3531);
} else {
new LegacyMetrics(this);
}
}
if (PlaceholderAPIHook.enabled())
new ParticlePlaceholderExpansion(this).register();

View file

@ -10,10 +10,10 @@ import dev.esophose.playerparticles.particles.ConsolePPlayer;
import dev.esophose.playerparticles.particles.FixedParticleEffect;
import dev.esophose.playerparticles.particles.PPlayer;
import dev.esophose.playerparticles.particles.ParticleEffect;
import dev.esophose.playerparticles.particles.ParticleEffect.NoteColor;
import dev.esophose.playerparticles.particles.ParticleEffect.OrdinaryColor;
import dev.esophose.playerparticles.particles.ParticleGroup;
import dev.esophose.playerparticles.particles.ParticlePair;
import dev.esophose.playerparticles.particles.data.NoteColor;
import dev.esophose.playerparticles.particles.data.OrdinaryColor;
import dev.esophose.playerparticles.styles.ParticleStyle;
import java.util.ArrayList;
import java.util.Collection;
@ -1059,34 +1059,4 @@ public final class PlayerParticlesAPI {
//endregion
//region Registering Custom Styles
/**
* Registers a particle style with the plugin
*
* @param particleStyle The particle style to register
* @deprecated Use {@link ParticleStyleRegistrationEvent} instead
*/
@Deprecated
public void registerParticleStyle(@NotNull ParticleStyle particleStyle) {
Objects.requireNonNull(particleStyle);
this.playerParticles.getManager(ParticleStyleManager.class).registerStyle(particleStyle);
}
/**
* Registers an event-based particle style with the plugin
*
* @param particleStyle The particle style to register
* @deprecated Use {@link ParticleStyleRegistrationEvent} instead
*/
@Deprecated
public void registerEventParticleStyle(@NotNull ParticleStyle particleStyle) {
Objects.requireNonNull(particleStyle);
this.playerParticles.getManager(ParticleStyleManager.class).registerEventStyle(particleStyle);
}
//endregion
}

View file

@ -7,11 +7,11 @@ import dev.esophose.playerparticles.manager.ParticleStyleManager;
import dev.esophose.playerparticles.manager.PermissionManager;
import dev.esophose.playerparticles.particles.PPlayer;
import dev.esophose.playerparticles.particles.ParticleEffect;
import dev.esophose.playerparticles.particles.ParticleEffect.NoteColor;
import dev.esophose.playerparticles.particles.ParticleEffect.OrdinaryColor;
import dev.esophose.playerparticles.particles.ParticleEffect.ParticleProperty;
import dev.esophose.playerparticles.particles.ParticleGroup;
import dev.esophose.playerparticles.particles.ParticlePair;
import dev.esophose.playerparticles.particles.data.NoteColor;
import dev.esophose.playerparticles.particles.data.OrdinaryColor;
import dev.esophose.playerparticles.styles.ParticleStyle;
import dev.esophose.playerparticles.util.ParticleUtils;
import dev.esophose.playerparticles.util.StringPlaceholders;

View file

@ -6,11 +6,11 @@ import dev.esophose.playerparticles.manager.LocaleManager;
import dev.esophose.playerparticles.manager.PermissionManager;
import dev.esophose.playerparticles.particles.PPlayer;
import dev.esophose.playerparticles.particles.ParticleEffect;
import dev.esophose.playerparticles.particles.ParticleEffect.NoteColor;
import dev.esophose.playerparticles.particles.ParticleEffect.OrdinaryColor;
import dev.esophose.playerparticles.particles.ParticleEffect.ParticleProperty;
import dev.esophose.playerparticles.particles.ParticleGroup;
import dev.esophose.playerparticles.particles.ParticlePair;
import dev.esophose.playerparticles.particles.data.NoteColor;
import dev.esophose.playerparticles.particles.data.OrdinaryColor;
import dev.esophose.playerparticles.styles.ParticleStyle;
import dev.esophose.playerparticles.util.ParticleUtils;
import dev.esophose.playerparticles.util.StringPlaceholders;

View file

@ -7,10 +7,10 @@ import dev.esophose.playerparticles.manager.PermissionManager;
import dev.esophose.playerparticles.particles.FixedParticleEffect;
import dev.esophose.playerparticles.particles.PPlayer;
import dev.esophose.playerparticles.particles.ParticleEffect;
import dev.esophose.playerparticles.particles.ParticleEffect.NoteColor;
import dev.esophose.playerparticles.particles.ParticleEffect.OrdinaryColor;
import dev.esophose.playerparticles.particles.ParticleEffect.ParticleProperty;
import dev.esophose.playerparticles.particles.ParticlePair;
import dev.esophose.playerparticles.particles.data.NoteColor;
import dev.esophose.playerparticles.particles.data.OrdinaryColor;
import dev.esophose.playerparticles.styles.ParticleStyle;
import dev.esophose.playerparticles.util.ParticleUtils;
import dev.esophose.playerparticles.util.StringPlaceholders;

View file

@ -3,6 +3,7 @@ package dev.esophose.playerparticles.gui;
import dev.esophose.playerparticles.gui.GuiInventoryEditData.ColorData;
import dev.esophose.playerparticles.hook.PlaceholderAPIHook;
import dev.esophose.playerparticles.particles.PPlayer;
import dev.esophose.playerparticles.util.NMSUtil;
import java.util.ArrayList;
import java.util.List;
import org.bukkit.ChatColor;
@ -107,7 +108,9 @@ public class GuiActionButton {
if (this.colors[0].getMaterial() != null) { // Use Materials
itemStack = new ItemStack(this.colors[this.iconIndex].getMaterial());
} else { // Use Dyes
itemStack = new Dye(this.colors[this.iconIndex].getDyeColor()).toItemStack(1);
Dye dye = new Dye();
dye.setColor(this.colors[this.iconIndex].getDyeColor());
itemStack = dye.toItemStack(1);
}
}
@ -115,7 +118,8 @@ public class GuiActionButton {
if (itemMeta != null) {
itemMeta.setDisplayName(PlaceholderAPIHook.applyPlaceholders(pplayer.getPlayer(), this.name));
itemMeta.setLore(parseLore(pplayer, this.lore));
itemMeta.addItemFlags(ItemFlag.HIDE_ATTRIBUTES, ItemFlag.HIDE_POTION_EFFECTS);
if (NMSUtil.getVersionNumber() > 7)
itemMeta.addItemFlags(ItemFlag.HIDE_ATTRIBUTES, ItemFlag.HIDE_POTION_EFFECTS);
itemStack.setItemMeta(itemMeta);
}

View file

@ -3,6 +3,7 @@ package dev.esophose.playerparticles.gui;
import dev.esophose.playerparticles.PlayerParticles;
import dev.esophose.playerparticles.manager.ConfigurationManager.Setting;
import dev.esophose.playerparticles.particles.PPlayer;
import dev.esophose.playerparticles.util.NMSUtil;
import dev.esophose.playerparticles.util.ParticleUtils;
import java.util.ArrayList;
import java.util.List;
@ -56,7 +57,8 @@ public abstract class GuiInventory {
ItemMeta meta = borderIcon.getItemMeta();
if (meta != null) {
meta.setDisplayName(" ");
meta.addItemFlags(ItemFlag.HIDE_ATTRIBUTES, ItemFlag.HIDE_POTION_EFFECTS, ItemFlag.HIDE_ENCHANTS);
if (NMSUtil.getVersionNumber() > 7)
meta.addItemFlags(ItemFlag.HIDE_ATTRIBUTES, ItemFlag.HIDE_POTION_EFFECTS, ItemFlag.HIDE_ENCHANTS);
borderIcon.setItemMeta(meta);
}
@ -155,7 +157,11 @@ public abstract class GuiInventory {
button.handleClick(isShiftClick);
if (Setting.GUI_BUTTON_SOUND.getBoolean() && event.getWhoClicked() instanceof Player) {
Player player = (Player) event.getWhoClicked();
player.playSound(player.getLocation(), Sound.UI_BUTTON_CLICK, 0.5f, 1);
if (NMSUtil.getVersionNumber() > 8) {
player.playSound(player.getLocation(), Sound.UI_BUTTON_CLICK, 0.5f, 1);
} else {
player.playSound(player.getLocation(), Sound.valueOf("CLICK"), 0.5f, 1);
}
}
break;
}

View file

@ -6,10 +6,10 @@ import dev.esophose.playerparticles.manager.GuiManager;
import dev.esophose.playerparticles.manager.LocaleManager;
import dev.esophose.playerparticles.particles.PPlayer;
import dev.esophose.playerparticles.particles.ParticleEffect;
import dev.esophose.playerparticles.particles.ParticleEffect.NoteColor;
import dev.esophose.playerparticles.particles.ParticleEffect.OrdinaryColor;
import dev.esophose.playerparticles.particles.ParticleEffect.ParticleProperty;
import dev.esophose.playerparticles.particles.ParticlePair;
import dev.esophose.playerparticles.particles.data.NoteColor;
import dev.esophose.playerparticles.particles.data.OrdinaryColor;
import dev.esophose.playerparticles.util.NMSUtil;
import dev.esophose.playerparticles.util.ParticleUtils;
import dev.esophose.playerparticles.util.StringPlaceholders;

View file

@ -16,11 +16,11 @@ import org.bukkit.Material;
public class ConfigurationManager extends Manager {
private static final String[] HEADER = new String[] {
" _________ __ __________ __ __ __ _________",
" \\______ \\ | _____ ___ __ __________\\______ \\_____ ________/ |_|__| ____ | | ____ ______ \\______ \\",
" | ___/ | \\__ \\< | |/ __ \\_ __ \\ ___/\\__ \\\\_ __ \\ __\\ |/ ___\\| | _/ __ \\ / ___/ / /",
" | | | |__/ __ \\\\___ \\ ___/| | \\/ | / __ \\| | \\/| | | \\ \\___| |_\\ ___/ \\___ \\ / /",
" |____| |____(____ / ____|\\___ >__| |____| (____ /__| |__| |__|\\___ >____/\\___ >____ > /____/",
" _________ __ __________ __ __ __",
" \\______ \\ | _____ ___ __ __________\\______ \\_____ ________/ |_|__| ____ | | ____ ______",
" | ___/ | \\__ \\< | |/ __ \\_ __ \\ ___/\\__ \\\\_ __ \\ __\\ |/ ___\\| | _/ __ \\ / ___/",
" | | | |__/ __ \\\\___ \\ ___/| | \\/ | / __ \\| | \\/| | | \\ \\___| |_\\ ___/ \\___ \\",
" |____| |____(____ / ____|\\___ >__| |____| (____ /__| |__| |__|\\___ >____/\\___ >____ >",
" \\/\\/ \\/ \\/ \\/ \\/ \\/"
};
@ -332,7 +332,7 @@ public class ConfigurationManager extends Manager {
}
if (material == null)
material = Material.BARRIER;
material = ParticleUtils.FALLBACK_MATERIAL;
this.materials.put(configPath, material);

View file

@ -9,10 +9,10 @@ import dev.esophose.playerparticles.particles.ConsolePPlayer;
import dev.esophose.playerparticles.particles.FixedParticleEffect;
import dev.esophose.playerparticles.particles.PPlayer;
import dev.esophose.playerparticles.particles.ParticleEffect;
import dev.esophose.playerparticles.particles.ParticleEffect.NoteColor;
import dev.esophose.playerparticles.particles.ParticleEffect.OrdinaryColor;
import dev.esophose.playerparticles.particles.ParticleGroup;
import dev.esophose.playerparticles.particles.ParticlePair;
import dev.esophose.playerparticles.particles.data.NoteColor;
import dev.esophose.playerparticles.particles.data.OrdinaryColor;
import dev.esophose.playerparticles.styles.ParticleStyle;
import dev.esophose.playerparticles.util.ParticleUtils;
import java.sql.PreparedStatement;

View file

@ -3,12 +3,12 @@ package dev.esophose.playerparticles.manager;
import dev.esophose.playerparticles.PlayerParticles;
import dev.esophose.playerparticles.particles.PPlayer;
import dev.esophose.playerparticles.particles.ParticleEffect;
import dev.esophose.playerparticles.particles.ParticleEffect.NoteColor;
import dev.esophose.playerparticles.particles.ParticleEffect.OrdinaryColor;
import dev.esophose.playerparticles.particles.ParticleEffect.ParticleProperty;
import dev.esophose.playerparticles.particles.ParticleGroup;
import dev.esophose.playerparticles.particles.ParticleGroupPreset;
import dev.esophose.playerparticles.particles.ParticlePair;
import dev.esophose.playerparticles.particles.data.NoteColor;
import dev.esophose.playerparticles.particles.data.OrdinaryColor;
import dev.esophose.playerparticles.styles.ParticleStyle;
import dev.esophose.playerparticles.util.inputparser.InputParser;
import java.io.File;

View file

@ -7,10 +7,11 @@ import dev.esophose.playerparticles.particles.FixedParticleEffect;
import dev.esophose.playerparticles.particles.PParticle;
import dev.esophose.playerparticles.particles.PPlayer;
import dev.esophose.playerparticles.particles.ParticleEffect;
import dev.esophose.playerparticles.particles.ParticleEffect.NoteColor;
import dev.esophose.playerparticles.particles.ParticleEffect.OrdinaryColor;
import dev.esophose.playerparticles.particles.ParticlePair;
import dev.esophose.playerparticles.particles.data.NoteColor;
import dev.esophose.playerparticles.particles.data.OrdinaryColor;
import dev.esophose.playerparticles.styles.DefaultStyles;
import dev.esophose.playerparticles.util.NMSUtil;
import java.awt.Color;
import java.util.Collection;
import java.util.List;
@ -151,7 +152,7 @@ public class ParticleManager extends Manager implements Listener, Runnable {
// Don't show their particles if they are in spectator mode
// Don't spawn particles if the world doesn't allow it
if (player != null && player.getGameMode() != GameMode.SPECTATOR && permissionManager.isWorldEnabled(player.getWorld().getName()))
if (player != null && (NMSUtil.getVersionNumber() < 8 || player.getGameMode() != GameMode.SPECTATOR) && permissionManager.isWorldEnabled(player.getWorld().getName()))
for (ParticlePair particles : pplayer.getActiveParticles())
this.displayParticles(pplayer, particles, player.getLocation().clone().add(0, 1, 0));
@ -214,7 +215,7 @@ public class ParticleManager extends Manager implements Listener, Runnable {
*/
public void displayParticles(Player player, World world, ParticlePair particle, List<PParticle> particles, boolean isLongRange) {
PermissionManager permissionManager = this.playerParticles.getManager(PermissionManager.class);
if ((player != null && player.getGameMode() == GameMode.SPECTATOR) || !permissionManager.isWorldEnabled(world.getName()))
if ((player != null && (NMSUtil.getVersionNumber() < 8 || player.getGameMode() == GameMode.SPECTATOR)) || !permissionManager.isWorldEnabled(world.getName()))
return;
for (PParticle pparticle : particles)

View file

@ -74,43 +74,6 @@ public class ParticleStyleManager extends Manager {
}
/**
* Registers a style that is put into the plugin's update loop
*
* @param style The style to add
*/
@Deprecated
public void registerStyle(ParticleStyle style) {
if (style == null) {
throw new IllegalArgumentException("Tried to register a null style");
}
if (style.getInternalName() == null || style.getInternalName().trim().equals("")) {
throw new IllegalArgumentException("Tried to register a style with a null or empty name: '" + style.getInternalName() + "'");
}
for (ParticleStyle testAgainst : this.styles) {
if (testAgainst.equals(style)) {
throw new IllegalArgumentException("Tried to register the same style twice: '" + style.getInternalName() + "'");
} else if (testAgainst.getInternalName().equalsIgnoreCase(style.getInternalName())) {
throw new IllegalArgumentException("Tried to register two styles with the same internal name spelling: '" + style.getInternalName() + "'");
}
}
this.styles.add(style);
}
/**
* Registers a style that isn't updated on the normal update loop
*
* @param style The style to register
*/
@Deprecated
public void registerEventStyle(ParticleStyle style) {
this.registerStyle(style);
this.eventStyles.add(style);
}
/**
* Returns if a given style is customly handled
*

View file

@ -3,8 +3,13 @@ package dev.esophose.playerparticles.particles;
import com.google.common.collect.ObjectArrays;
import dev.esophose.playerparticles.PlayerParticles;
import dev.esophose.playerparticles.config.CommentedFileConfiguration;
import dev.esophose.playerparticles.manager.ConfigurationManager.Setting;
import dev.esophose.playerparticles.manager.ParticleManager;
import dev.esophose.playerparticles.particles.data.ParticleColor;
import dev.esophose.playerparticles.particles.spawning.ParticleSpawner;
import dev.esophose.playerparticles.particles.spawning.ParticleSpawner.ParticleColorException;
import dev.esophose.playerparticles.particles.spawning.ParticleSpawner.ParticleDataException;
import dev.esophose.playerparticles.particles.spawning.ReflectiveParticleSpawner;
import dev.esophose.playerparticles.particles.spawning.SpigotParticleSpawner;
import dev.esophose.playerparticles.particles.spawning.reflective.ReflectiveParticleEffectMapping;
import dev.esophose.playerparticles.util.NMSUtil;
import dev.esophose.playerparticles.util.ParticleUtils;
import java.io.File;
@ -12,19 +17,12 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.stream.Stream;
import org.bukkit.Color;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.Particle;
import org.bukkit.Particle.DustOptions;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.material.MaterialData;
import org.bukkit.metadata.MetadataValue;
@SuppressWarnings("deprecation")
public enum ParticleEffect {
// Ordered and named by their Minecraft 1.13+ internal names
@ -92,8 +90,14 @@ public enum ParticleEffect {
UNDERWATER("SUSPENDED_DEPTH", Arrays.asList("TURTLE_HELMET", "SPONGE")),
WITCH("SPELL_WITCH", Collections.singletonList("CAULDRON"));
private final static ParticleSpawner particleSpawner;
static {
particleSpawner = NMSUtil.getVersionNumber() >= 9 ? new SpigotParticleSpawner() : new ReflectiveParticleSpawner();
}
private Particle internalEnum;
private List<ParticleProperty> properties;
private boolean supported;
private CommentedFileConfiguration config;
private boolean enabledByDefault;
@ -128,7 +132,16 @@ public enum ParticleEffect {
this.properties = Arrays.asList(properties);
// Will be null if this server's version doesn't support this particle type
this.internalEnum = Stream.of(Particle.values()).filter(x -> x.name().equals(enumName)).findFirst().orElse(null);
if (NMSUtil.getVersionNumber() > 8) {
this.internalEnum = Stream.of(Particle.values()).filter(x -> x.name().equals(enumName)).findFirst().orElse(null);
this.supported = this.internalEnum != null;
} else {
try {
this.supported = ReflectiveParticleEffectMapping.valueOf(this.name()).isSupported();
} catch (Exception e) {
this.supported = false;
}
}
this.setDefaultSettings();
this.loadSettings(false);
@ -217,6 +230,13 @@ public enum ParticleEffect {
return this.effectName;
}
/**
* @return the Spigot enum this represents
*/
public Particle getSpigotEnum() {
return this.internalEnum;
}
/**
* @return The Material icon that represents this style in the GUI
*/
@ -240,7 +260,7 @@ public enum ParticleEffect {
* @return Whether the particle effect is supported or not
*/
public boolean isSupported() {
return this.internalEnum != null;
return this.supported;
}
/**
@ -317,12 +337,8 @@ public enum ParticleEffect {
* @param owner The player that owns the particles
* @throws ParticleDataException If the particle effect requires additional data
*/
public void display(double offsetX, double offsetY, double offsetZ, double speed, int amount, Location center, boolean isLongRange, Player owner) throws ParticleDataException {
if (this.hasProperty(ParticleProperty.REQUIRES_MATERIAL_DATA))
throw new ParticleDataException("This particle effect requires additional data");
for (Player player : this.getPlayersInRange(center, isLongRange, owner))
player.spawnParticle(this.internalEnum, center.getX(), center.getY(), center.getZ(), amount, offsetX, offsetY, offsetZ, speed);
public void display(double offsetX, double offsetY, double offsetZ, double speed, int amount, Location center, boolean isLongRange, Player owner) {
particleSpawner.display(this, offsetX, offsetY, offsetZ, speed, amount, center, isLongRange, owner);
}
/**
@ -334,21 +350,8 @@ public enum ParticleEffect {
* @param owner The player that owns the particles
* @throws ParticleColorException If the particle effect is not colorable or the color type is incorrect
*/
public void display(ParticleColor color, Location center, boolean isLongRange, Player owner) throws ParticleColorException {
if (!this.hasProperty(ParticleProperty.COLORABLE))
throw new ParticleColorException("This particle effect is not colorable");
if (this == DUST && NMSUtil.getVersionNumber() >= 13) { // DUST uses a special data object for spawning in 1.13+
OrdinaryColor dustColor = (OrdinaryColor) color;
DustOptions dustOptions = new DustOptions(Color.fromRGB(dustColor.getRed(), dustColor.getGreen(), dustColor.getBlue()), Setting.DUST_SIZE.getFloat());
for (Player player : this.getPlayersInRange(center, isLongRange, owner))
player.spawnParticle(this.internalEnum, center.getX(), center.getY(), center.getZ(), 1, 0, 0, 0, 0, dustOptions);
} else {
for (Player player : this.getPlayersInRange(center, isLongRange, owner)) {
// Minecraft clients require that you pass a non-zero value if the Red value should be zero
player.spawnParticle(this.internalEnum, center.getX(), center.getY(), center.getZ(), 0, this == ParticleEffect.DUST && color.getValueX() == 0 ? Float.MIN_VALUE : color.getValueX(), color.getValueY(), color.getValueZ(), 1);
}
}
public void display(ParticleColor color, Location center, boolean isLongRange, Player owner) {
particleSpawner.display(this, color, center, isLongRange, owner);
}
/**
@ -367,65 +370,8 @@ public enum ParticleEffect {
* @param owner The player that owns the particles
* @throws ParticleDataException If the particle effect does not require additional data or if the data type is incorrect
*/
public void display(Material spawnMaterial, double offsetX, double offsetY, double offsetZ, double speed, int amount, Location center, boolean isLongRange, Player owner) throws ParticleDataException {
if (!this.hasProperty(ParticleProperty.REQUIRES_MATERIAL_DATA)) {
throw new ParticleDataException("This particle effect does not require additional data");
}
Object extraData = null;
if (this.internalEnum.getDataType().getTypeName().equals("org.bukkit.block.data.BlockData")) {
extraData = spawnMaterial.createBlockData();
} else if (this.internalEnum.getDataType() == ItemStack.class) {
extraData = new ItemStack(spawnMaterial);
} else if (this.internalEnum.getDataType() == MaterialData.class) {
extraData = new MaterialData(spawnMaterial); // Deprecated, only used in versions < 1.13
}
for (Player player : this.getPlayersInRange(center, isLongRange, owner))
player.spawnParticle(this.internalEnum, center.getX(), center.getY(), center.getZ(), amount, offsetX, offsetY, offsetZ, speed, extraData);
}
/**
* Gets a List of Players within the particle display range
*
* @param center The center of the radius to check around
* @param isLongRange If the particle can be viewed from long range
* @param owner The player that owns the particles
* @return A List of Players within the particle display range
*/
private List<Player> getPlayersInRange(Location center, boolean isLongRange, Player owner) {
List<Player> players = new ArrayList<>();
int range = !isLongRange ? Setting.PARTICLE_RENDER_RANGE_PLAYER.getInt() : Setting.PARTICLE_RENDER_RANGE_FIXED_EFFECT.getInt();
range *= range;
for (PPlayer pplayer : PlayerParticles.getInstance().getManager(ParticleManager.class).getPPlayers()) {
Player p = pplayer.getPlayer();
if (!this.canSee(p, owner))
continue;
if (p != null && pplayer.canSeeParticles() && p.getWorld().equals(center.getWorld()) && center.distanceSquared(p.getLocation()) <= range)
players.add(p);
}
return players;
}
/**
* Checks if a player can see another player
*
* @param player The player
* @param target The target
* @return True if player can see target, otherwise false
*/
private boolean canSee(Player player, Player target) {
if (player == null || target == null)
return true;
for (MetadataValue meta : target.getMetadata("vanished"))
if (meta.asBoolean())
return false;
return player.canSee(target);
public void display(Material spawnMaterial, double offsetX, double offsetY, double offsetZ, double speed, int amount, Location center, boolean isLongRange, Player owner) {
particleSpawner.display(this, spawnMaterial, offsetX, offsetY, offsetZ, speed, amount, center, isLongRange, owner);
}
/**
@ -448,310 +394,4 @@ public enum ParticleEffect {
COLORABLE
}
/**
* Represents the color for effects like {@link ParticleEffect#ENTITY_EFFECT},
* {@link ParticleEffect#AMBIENT_ENTITY_EFFECT}, {@link ParticleEffect#DUST}
* and {@link ParticleEffect#NOTE}
* <p>
* This class is part of the <b>ParticleEffect Library</b> and follows the
* same usage conditions
*
* @author DarkBlade12
* @since 1.7
*/
public static abstract class ParticleColor {
/**
* Returns the value for the offsetX field
*
* @return The offsetX value
*/
public abstract float getValueX();
/**
* Returns the value for the offsetY field
*
* @return The offsetY value
*/
public abstract float getValueY();
/**
* Returns the value for the offsetZ field
*
* @return The offsetZ value
*/
public abstract float getValueZ();
}
/**
* Represents the color for effects like {@link ParticleEffect#ENTITY_EFFECT},
* {@link ParticleEffect#AMBIENT_ENTITY_EFFECT} and {@link ParticleEffect#NOTE}
* <p>
* This class is part of the <b>ParticleEffect Library</b> and follows the
* same usage conditions
*
* @author DarkBlade12
* @since 1.7
*/
public static final class OrdinaryColor extends ParticleColor {
public static final OrdinaryColor RAINBOW = new OrdinaryColor(999, 999, 999);
public static final OrdinaryColor RANDOM = new OrdinaryColor(998, 998, 998);
private final int red;
private final int green;
private final int blue;
/**
* Construct a new ordinary color
*
* @param red Red value of the RGB format
* @param green Green value of the RGB format
* @param blue Blue value of the RGB format
* @throws IllegalArgumentException If one of the values is lower than 0
* or higher than 255
*/
public OrdinaryColor(int red, int green, int blue) throws IllegalArgumentException {
if ((red == 999 && green == 999 && blue == 999) || (red == 998 && green == 998 && blue == 998)) { // Allow rainbow and random values
this.red = red;
this.green = green;
this.blue = blue;
} else {
if (red < 0) {
throw new IllegalArgumentException("The red value is lower than 0");
}
if (red > 255) {
throw new IllegalArgumentException("The red value is higher than 255");
}
this.red = red;
if (green < 0) {
throw new IllegalArgumentException("The green value is lower than 0");
}
if (green > 255) {
throw new IllegalArgumentException("The green value is higher than 255");
}
this.green = green;
if (blue < 0) {
throw new IllegalArgumentException("The blue value is lower than 0");
}
if (blue > 255) {
throw new IllegalArgumentException("The blue value is higher than 255");
}
this.blue = blue;
}
}
/**
* Returns the red value of the RGB format
*
* @return The red value
*/
public int getRed() {
return this.red;
}
/**
* Returns the green value of the RGB format
*
* @return The green value
*/
public int getGreen() {
return this.green;
}
/**
* Returns the blue value of the RGB format
*
* @return The blue value
*/
public int getBlue() {
return this.blue;
}
/**
* Returns the red value divided by 255
*
* @return The offsetX value
*/
@Override
public float getValueX() {
if (this.equals(OrdinaryColor.RAINBOW) || this.equals(OrdinaryColor.RANDOM))
return 0F;
return (float) this.red / 255F;
}
/**
* Returns the green value divided by 255
*
* @return The offsetY value
*/
@Override
public float getValueY() {
if (this.equals(OrdinaryColor.RAINBOW) || this.equals(OrdinaryColor.RANDOM))
return 0F;
return (float) this.green / 255F;
}
/**
* Returns the blue value divided by 255
*
* @return The offsetZ value
*/
@Override
public float getValueZ() {
if (this.equals(OrdinaryColor.RAINBOW) || this.equals(OrdinaryColor.RANDOM))
return 0F;
return (float) this.blue / 255F;
}
@Override
public boolean equals(Object other) {
if (!(other instanceof OrdinaryColor))
return false;
OrdinaryColor otherColor = (OrdinaryColor) other;
return this.red == otherColor.red && this.green == otherColor.green && this.blue == otherColor.blue;
}
@Override
public int hashCode() {
return Objects.hash(this.red, this.green, this.blue);
}
}
/**
* Represents the color for the {@link ParticleEffect#NOTE} effect
* <p>
* This class is part of the <b>ParticleEffect Library</b> and follows the
* same usage conditions
*
* @author DarkBlade12
* @since 1.7
*/
public static final class NoteColor extends ParticleColor {
public static final NoteColor RAINBOW = new NoteColor(99);
public static final NoteColor RANDOM = new NoteColor(98);
private final int note;
/**
* Construct a new note color
*
* @param note Note id which determines color
* @throws IllegalArgumentException If the note value is lower than 0 or
* higher than 24
*/
public NoteColor(int note) throws IllegalArgumentException {
if (note == 99 || note == 98) { // Allow rainbow and random values
this.note = note;
} else {
if (note < 0) {
throw new IllegalArgumentException("The note value is lower than 0");
}
if (note > 24) {
throw new IllegalArgumentException("The note value is higher than 24");
}
this.note = note;
}
}
/**
* Returns the note value
*
* @return The note value
*/
public int getNote() {
return this.note;
}
/**
* Returns the note value divided by 24
*
* @return The offsetX value
*/
@Override
public float getValueX() {
return (float) this.note / 24F;
}
/**
* Returns zero because the offsetY value is unused
*
* @return zero
*/
@Override
public float getValueY() {
return 0;
}
/**
* Returns zero because the offsetZ value is unused
*
* @return zero
*/
@Override
public float getValueZ() {
return 0;
}
@Override
public boolean equals(Object other) {
if (!(other instanceof NoteColor))
return false;
NoteColor otherColor = (NoteColor) other;
return this.note == otherColor.note;
}
@Override
public int hashCode() {
return Objects.hashCode(this.note);
}
}
/**
* Represents a runtime exception that is thrown either if the displayed
* particle effect requires data and has none or vice-versa or if the data
* type is incorrect
* <p>
* This class is part of the <b>ParticleEffect Library</b> 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
*/
public ParticleDataException(String message) {
super(message);
}
}
/**
* Represents a runtime exception that is thrown either if the displayed
* particle effect is not colorable or if the particle color type is
* incorrect
* <p>
* This class is part of the <b>ParticleEffect Library</b> and follows the
* same usage conditions
*
* @author DarkBlade12
* @since 1.7
*/
private static final class ParticleColorException extends RuntimeException {
private static final long serialVersionUID = 3203085387160737485L;
/**
* Construct a new particle color exception
*
* @param message Message that will be logged
*/
public ParticleColorException(String message) {
super(message);
}
}
}

View file

@ -3,10 +3,10 @@ package dev.esophose.playerparticles.particles;
import dev.esophose.playerparticles.PlayerParticles;
import dev.esophose.playerparticles.manager.LocaleManager;
import dev.esophose.playerparticles.manager.ParticleManager;
import dev.esophose.playerparticles.particles.ParticleEffect.NoteColor;
import dev.esophose.playerparticles.particles.ParticleEffect.OrdinaryColor;
import dev.esophose.playerparticles.particles.ParticleEffect.ParticleColor;
import dev.esophose.playerparticles.particles.ParticleEffect.ParticleProperty;
import dev.esophose.playerparticles.particles.data.NoteColor;
import dev.esophose.playerparticles.particles.data.OrdinaryColor;
import dev.esophose.playerparticles.particles.data.ParticleColor;
import dev.esophose.playerparticles.styles.DefaultStyles;
import dev.esophose.playerparticles.styles.ParticleStyle;
import dev.esophose.playerparticles.util.ParticleUtils;

View file

@ -0,0 +1,94 @@
package dev.esophose.playerparticles.particles.data;
import dev.esophose.playerparticles.particles.ParticleEffect;
import java.util.Objects;
/**
* Represents the color for the {@link ParticleEffect#NOTE} effect
* <p>
* This class is part of the <b>ParticleEffect Library</b> and follows the
* same usage conditions
*
* @author DarkBlade12
* @since 1.7
*/
public final class NoteColor extends ParticleColor {
public static final NoteColor RAINBOW = new NoteColor(99);
public static final NoteColor RANDOM = new NoteColor(98);
private final int note;
/**
* Construct a new note color
*
* @param note Note id which determines color
* @throws IllegalArgumentException If the note value is lower than 0 or
* higher than 24
*/
public NoteColor(int note) throws IllegalArgumentException {
if (note == 99 || note == 98) { // Allow rainbow and random values
this.note = note;
} else {
if (note < 0) {
throw new IllegalArgumentException("The note value is lower than 0");
}
if (note > 24) {
throw new IllegalArgumentException("The note value is higher than 24");
}
this.note = note;
}
}
/**
* Returns the note value
*
* @return The note value
*/
public int getNote() {
return this.note;
}
/**
* Returns the note value divided by 24
*
* @return The offsetX value
*/
@Override
public float getValueX() {
return (float) this.note / 24F;
}
/**
* Returns zero because the offsetY value is unused
*
* @return zero
*/
@Override
public float getValueY() {
return 0;
}
/**
* Returns zero because the offsetZ value is unused
*
* @return zero
*/
@Override
public float getValueZ() {
return 0;
}
@Override
public boolean equals(Object other) {
if (!(other instanceof NoteColor))
return false;
NoteColor otherColor = (NoteColor) other;
return this.note == otherColor.note;
}
@Override
public int hashCode() {
return Objects.hashCode(this.note);
}
}

View file

@ -0,0 +1,138 @@
package dev.esophose.playerparticles.particles.data;
import dev.esophose.playerparticles.particles.ParticleEffect;
import java.util.Objects;
/**
* Represents the color for effects like {@link ParticleEffect#ENTITY_EFFECT},
* {@link ParticleEffect#AMBIENT_ENTITY_EFFECT} and {@link ParticleEffect#NOTE}
* <p>
* This class is part of the <b>ParticleEffect Library</b> and follows the
* same usage conditions
*
* @author DarkBlade12
* @since 1.7
*/
public final class OrdinaryColor extends ParticleColor {
public static final OrdinaryColor RAINBOW = new OrdinaryColor(999, 999, 999);
public static final OrdinaryColor RANDOM = new OrdinaryColor(998, 998, 998);
private final int red;
private final int green;
private final int blue;
/**
* Construct a new ordinary color
*
* @param red Red value of the RGB format
* @param green Green value of the RGB format
* @param blue Blue value of the RGB format
* @throws IllegalArgumentException If one of the values is lower than 0
* or higher than 255
*/
public OrdinaryColor(int red, int green, int blue) throws IllegalArgumentException {
if ((red == 999 && green == 999 && blue == 999) || (red == 998 && green == 998 && blue == 998)) { // Allow rainbow and random values
this.red = red;
this.green = green;
this.blue = blue;
} else {
if (red < 0) {
throw new IllegalArgumentException("The red value is lower than 0");
}
if (red > 255) {
throw new IllegalArgumentException("The red value is higher than 255");
}
this.red = red;
if (green < 0) {
throw new IllegalArgumentException("The green value is lower than 0");
}
if (green > 255) {
throw new IllegalArgumentException("The green value is higher than 255");
}
this.green = green;
if (blue < 0) {
throw new IllegalArgumentException("The blue value is lower than 0");
}
if (blue > 255) {
throw new IllegalArgumentException("The blue value is higher than 255");
}
this.blue = blue;
}
}
/**
* Returns the red value of the RGB format
*
* @return The red value
*/
public int getRed() {
return this.red;
}
/**
* Returns the green value of the RGB format
*
* @return The green value
*/
public int getGreen() {
return this.green;
}
/**
* Returns the blue value of the RGB format
*
* @return The blue value
*/
public int getBlue() {
return this.blue;
}
/**
* Returns the red value divided by 255
*
* @return The offsetX value
*/
@Override
public float getValueX() {
if (this.equals(OrdinaryColor.RAINBOW) || this.equals(OrdinaryColor.RANDOM))
return 0F;
return (float) this.red / 255F;
}
/**
* Returns the green value divided by 255
*
* @return The offsetY value
*/
@Override
public float getValueY() {
if (this.equals(OrdinaryColor.RAINBOW) || this.equals(OrdinaryColor.RANDOM))
return 0F;
return (float) this.green / 255F;
}
/**
* Returns the blue value divided by 255
*
* @return The offsetZ value
*/
@Override
public float getValueZ() {
if (this.equals(OrdinaryColor.RAINBOW) || this.equals(OrdinaryColor.RANDOM))
return 0F;
return (float) this.blue / 255F;
}
@Override
public boolean equals(Object other) {
if (!(other instanceof OrdinaryColor))
return false;
OrdinaryColor otherColor = (OrdinaryColor) other;
return this.red == otherColor.red && this.green == otherColor.green && this.blue == otherColor.blue;
}
@Override
public int hashCode() {
return Objects.hash(this.red, this.green, this.blue);
}
}

View file

@ -0,0 +1,37 @@
package dev.esophose.playerparticles.particles.data;
import dev.esophose.playerparticles.particles.ParticleEffect;
/**
* Represents the color for effects like {@link ParticleEffect#ENTITY_EFFECT},
* {@link ParticleEffect#AMBIENT_ENTITY_EFFECT}, {@link ParticleEffect#DUST}
* and {@link ParticleEffect#NOTE}
* <p>
* This class is part of the <b>ParticleEffect Library</b> and follows the
* same usage conditions
*
* @author DarkBlade12
* @since 1.7
*/
public abstract class ParticleColor {
/**
* Returns the value for the offsetX field
*
* @return The offsetX value
*/
public abstract float getValueX();
/**
* Returns the value for the offsetY field
*
* @return The offsetY value
*/
public abstract float getValueY();
/**
* Returns the value for the offsetZ field
*
* @return The offsetZ value
*/
public abstract float getValueZ();
}

View file

@ -0,0 +1,156 @@
package dev.esophose.playerparticles.particles.spawning;
import dev.esophose.playerparticles.PlayerParticles;
import dev.esophose.playerparticles.manager.ConfigurationManager.Setting;
import dev.esophose.playerparticles.manager.ParticleManager;
import dev.esophose.playerparticles.particles.PPlayer;
import dev.esophose.playerparticles.particles.ParticleEffect;
import dev.esophose.playerparticles.particles.data.ParticleColor;
import java.util.ArrayList;
import java.util.List;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.metadata.MetadataValue;
public abstract class ParticleSpawner {
/**
* Displays a particle effect
*
* @param particleEffect The particle type to display
* @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 isLongRange If the particle can be viewed from long range
* @param owner The player that owns the particles
* @throws ParticleDataException If the particle effect requires additional data
*/
public abstract void display(ParticleEffect particleEffect, double offsetX, double offsetY, double offsetZ, double speed, int amount, Location center, boolean isLongRange, Player owner);
/**
* Displays a single particle which is colored
*
* @param particleEffect The particle type to display
* @param color Color of the particle
* @param center Center location of the effect
* @param isLongRange If the particle can be viewed from long range
* @param owner The player that owns the particles
* @throws ParticleColorException If the particle effect is not colorable or the color type is incorrect
*/
public abstract void display(ParticleEffect particleEffect, ParticleColor color, Location center, boolean isLongRange, Player owner);
/**
* 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 particleEffect The particle type to display
* @param spawnMaterial Material 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 isLongRange If the particle can be viewed from long range
* @param owner The player that owns the particles
* @throws ParticleDataException If the particle effect does not require additional data or if the data type is incorrect
*/
public abstract void display(ParticleEffect particleEffect, Material spawnMaterial, double offsetX, double offsetY, double offsetZ, double speed, int amount, Location center, boolean isLongRange, Player owner);
/**
* Gets a List of Players within the particle display range
*
* @param center The center of the radius to check around
* @param isLongRange If the particle can be viewed from long range
* @param owner The player that owns the particles
* @return A List of Players within the particle display range
*/
protected List<Player> getPlayersInRange(Location center, boolean isLongRange, Player owner) {
List<Player> players = new ArrayList<>();
int range = !isLongRange ? Setting.PARTICLE_RENDER_RANGE_PLAYER.getInt() : Setting.PARTICLE_RENDER_RANGE_FIXED_EFFECT.getInt();
range *= range;
for (PPlayer pplayer : PlayerParticles.getInstance().getManager(ParticleManager.class).getPPlayers()) {
Player p = pplayer.getPlayer();
if (p != owner && !this.canSee(p, owner))
continue;
if (p != null && pplayer.canSeeParticles() && p.getWorld().equals(center.getWorld()) && center.distanceSquared(p.getLocation()) <= range)
players.add(p);
}
return players;
}
/**
* Checks if a player can see another player
*
* @param player The player
* @param target The target
* @return True if player can see target, otherwise false
*/
private boolean canSee(Player player, Player target) {
if (player == null || target == null)
return true;
for (MetadataValue meta : target.getMetadata("vanished"))
if (meta.asBoolean())
return false;
return player.canSee(target);
}
/**
* Represents a runtime exception that is thrown either if the displayed
* particle effect requires data and has none or vice-versa or if the data
* type is incorrect
* <p>
* This class is part of the <b>ParticleEffect Library</b> and follows the
* same usage conditions
*
* @author DarkBlade12
* @since 1.6
*/
public 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
*/
public ParticleDataException(String message) {
super(message);
}
}
/**
* Represents a runtime exception that is thrown either if the displayed
* particle effect is not colorable or if the particle color type is
* incorrect
* <p>
* This class is part of the <b>ParticleEffect Library</b> and follows the
* same usage conditions
*
* @author DarkBlade12
* @since 1.7
*/
public static final class ParticleColorException extends RuntimeException {
private static final long serialVersionUID = 3203085387160737485L;
/**
* Construct a new particle color exception
*
* @param message Message that will be logged
*/
public ParticleColorException(String message) {
super(message);
}
}
}

View file

@ -0,0 +1,284 @@
package dev.esophose.playerparticles.particles.spawning;
import dev.esophose.playerparticles.particles.ParticleEffect;
import dev.esophose.playerparticles.particles.ParticleEffect.ParticleProperty;
import dev.esophose.playerparticles.particles.data.OrdinaryColor;
import dev.esophose.playerparticles.particles.data.ParticleColor;
import dev.esophose.playerparticles.particles.spawning.reflective.ReflectionUtils;
import dev.esophose.playerparticles.particles.spawning.reflective.ReflectionUtils.PackageType;
import dev.esophose.playerparticles.particles.spawning.reflective.ReflectiveParticleEffectMapping;
import dev.esophose.playerparticles.util.NMSUtil;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.List;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.entity.Player;
public class ReflectiveParticleSpawner extends ParticleSpawner {
@Override
public void display(ParticleEffect particleEffect, double offsetX, double offsetY, double offsetZ, double speed, int amount, Location center, boolean isLongRange, Player owner) {
if (particleEffect.hasProperty(ParticleProperty.REQUIRES_MATERIAL_DATA))
throw new ParticleDataException("This particle effect requires additional data");
new ParticlePacket(particleEffect, offsetX, offsetY, offsetZ, speed, amount, true, null).sendTo(center, this.getPlayersInRange(center, isLongRange, owner));
}
@Override
public void display(ParticleEffect particleEffect, ParticleColor color, Location center, boolean isLongRange, Player owner) {
if (!particleEffect.hasProperty(ParticleProperty.COLORABLE))
throw new ParticleColorException("This particle effect is not colorable");
new ParticlePacket(particleEffect, color, true).sendTo(center, this.getPlayersInRange(center, isLongRange, owner));
}
@Override
public void display(ParticleEffect particleEffect, Material spawnMaterial, double offsetX, double offsetY, double offsetZ, double speed, int amount, Location center, boolean isLongRange, Player owner) {
if (!particleEffect.hasProperty(ParticleProperty.REQUIRES_MATERIAL_DATA))
throw new ParticleDataException("This particle effect does not require additional data");
new ParticlePacket(particleEffect, offsetX, offsetY, offsetZ, speed, amount, true, spawnMaterial).sendTo(center, this.getPlayersInRange(center, isLongRange, owner));
}
/**
* Represents a particle effect packet with all attributes which is used for
* sending packets to the players
* <p>
* This class is part of the <b>ParticleEffect Library</b> and follows the
* same usage conditions
*
* @author DarkBlade12, Esophose
* @since 1.5
*/
public static final class ParticlePacket {
private static Class<?> enumParticle;
private static Constructor<?> packetConstructor;
private static Method getHandle;
private static Field playerConnection;
private static Method sendPacket;
static {
try {
if (NMSUtil.getVersionNumber() > 7) {
enumParticle = PackageType.MINECRAFT_SERVER.getClass("EnumParticle");
}
Class<?> packetClass = PackageType.MINECRAFT_SERVER.getClass(NMSUtil.getVersionNumber() < 7 ? "Packet63WorldParticles" : "PacketPlayOutWorldParticles");
packetConstructor = ReflectionUtils.getConstructor(packetClass);
getHandle = ReflectionUtils.getMethod("CraftPlayer", PackageType.CRAFTBUKKIT_ENTITY, "getHandle");
playerConnection = ReflectionUtils.getField("EntityPlayer", PackageType.MINECRAFT_SERVER, false, "playerConnection");
sendPacket = ReflectionUtils.getMethod(playerConnection.getType(), "sendPacket", PackageType.MINECRAFT_SERVER.getClass("Packet"));
} catch (ReflectiveOperationException e) {
e.printStackTrace();
}
}
private final ParticleEffect effect;
private float offsetX;
private final float offsetY;
private final float offsetZ;
private final float speed;
private final int amount;
private final boolean longDistance;
private final Material data;
private Object packet;
/**
* Construct a new particle packet
*
* @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 or amount is lower than 0
*/
public ParticlePacket(ParticleEffect effect, double offsetX, double offsetY, double offsetZ, double speed, int amount, boolean longDistance, Material data) throws IllegalArgumentException {
if (speed < 0)
throw new IllegalArgumentException("The speed is lower than 0");
if (amount < 0)
throw new IllegalArgumentException("The amount is lower than 0");
this.effect = effect;
this.offsetX = (float) offsetX;
this.offsetY = (float) offsetY;
this.offsetZ = (float) offsetZ;
this.speed = (float) speed;
this.amount = amount;
this.longDistance = longDistance;
this.data = data;
}
/**
* Construct a new particle packet of a single colored particle
*
* @param effect Particle effect
* @param color Color of the particle
* @param longDistance Indicates whether the maximum distance is increased from 256 to 65536
*/
public ParticlePacket(ParticleEffect effect, ParticleColor color, boolean longDistance) {
this(effect, color.getValueX(), color.getValueY(), color.getValueZ(), 1, 0, longDistance, null);
if (effect == ParticleEffect.DUST && color instanceof OrdinaryColor && ((OrdinaryColor) color).getRed() == 0)
this.offsetX = Float.MIN_NORMAL;
}
/**
* Initializes {@link #packet} with all set values
*
* @param center Center location of the effect
* @throws PacketInstantiationException If instantion fails due to an unknown error
*/
private void initializePacket(Location center) throws PacketInstantiationException {
if (this.packet != null) {
return;
}
try {
this.packet = packetConstructor.newInstance();
if (NMSUtil.getVersionNumber() < 8) {
String name = ReflectiveParticleEffectMapping.valueOf(this.effect.name()).getName();
if (this.data != null) {
name += getPacketDataString(this.data);
}
ReflectionUtils.setValue(this.packet, true, "a", name);
} else {
ReflectionUtils.setValue(this.packet, true, "a", enumParticle.getEnumConstants()[ReflectiveParticleEffectMapping.valueOf(this.effect.name()).getId()]);
ReflectionUtils.setValue(this.packet, true, "j", this.longDistance);
if (this.data != null) {
int[] packetData = getPacketData(this.data);
ReflectionUtils.setValue(this.packet, true, "k", this.effect == ParticleEffect.ITEM ? packetData : new int[] { packetData[0] | (packetData[1] << 12) });
}
}
ReflectionUtils.setValue(this.packet, true, "b", (float) center.getX());
ReflectionUtils.setValue(this.packet, true, "c", (float) center.getY());
ReflectionUtils.setValue(this.packet, true, "d", (float) center.getZ());
ReflectionUtils.setValue(this.packet, true, "e", this.offsetX);
ReflectionUtils.setValue(this.packet, true, "f", this.offsetY);
ReflectionUtils.setValue(this.packet, true, "g", this.offsetZ);
ReflectionUtils.setValue(this.packet, true, "h", this.speed);
ReflectionUtils.setValue(this.packet, true, "i", this.amount);
} catch (Exception exception) {
throw new PacketInstantiationException("Packet instantiation failed", exception);
}
}
/**
* Sends the packet to a single player and caches it
*
* @param center Center location of the effect
* @param player Receiver of the packet
* @throws PacketInstantiationException If instantion fails due to an unknown error
* @throws PacketSendingException If sending fails due to an unknown error
* @see #initializePacket(Location)
*/
public void sendTo(Location center, Player player) throws PacketInstantiationException, PacketSendingException {
this.initializePacket(center);
try {
sendPacket.invoke(playerConnection.get(getHandle.invoke(player)), this.packet);
} catch (Exception exception) {
throw new PacketSendingException("Failed to send the packet to player '" + player.getName() + "'", exception);
}
}
/**
* Sends the packet to all players in the list
*
* @param center Center location of the effect
* @param players Receivers of the packet
* @throws IllegalArgumentException If the player list is empty
* @see #sendTo(Location center, Player player)
*/
public void sendTo(Location center, List<Player> players) throws IllegalArgumentException {
if (players.isEmpty()) {
throw new IllegalArgumentException("The player list is empty");
}
for (Player player : players) {
this.sendTo(center, player);
}
}
/**
* Sends the packet to all players in a certain range
*
* @param center Center location of the effect
* @param range Range in which players will receive the packet (Maximum range for particles is usually 16, but it can differ for some types)
* @throws IllegalArgumentException If the range is lower than 1
* @see #sendTo(Location center, Player player)
*/
public void sendTo(Location center, double range) throws IllegalArgumentException {
if (range < 1) {
throw new IllegalArgumentException("The range is lower than 1");
}
String worldName = center.getWorld().getName();
double squared = range * range;
for (Player player : Bukkit.getOnlinePlayers()) {
if (!player.getWorld().getName().equals(worldName) || player.getLocation().distanceSquared(center) > squared) {
continue;
}
this.sendTo(center, player);
}
}
private static String getPacketDataString(Material data) {
int[] packetData = getPacketData(data);
return "_" + packetData[0] + "_" + packetData[1];
}
@SuppressWarnings("deprecation")
private static int[] getPacketData(Material data) {
return new int[] { data.getId(), 0 };
}
/**
* Represents a runtime exception that is thrown if packet instantiation fails
* <p>
* This class is part of the <b>ParticleEffect Library</b> and follows the same usage conditions
* </p>
*
* @author DarkBlade12
* @since 1.4
*/
private static final class PacketInstantiationException extends RuntimeException {
private static final long serialVersionUID = 3203085387160737484L;
/**
* Construct a new packet instantiation exception
*
* @param message Message that will be logged
* @param cause Cause of the exception
*/
public PacketInstantiationException(String message, Throwable cause) {
super(message, cause);
}
}
/**
* Represents a runtime exception that is thrown if packet sending fails
* <p>
* This class is part of the <b>ParticleEffect Library</b> and follows the same usage conditions
* </p>
*
* @author DarkBlade12
* @since 1.4
*/
private static final class PacketSendingException extends RuntimeException {
private static final long serialVersionUID = 3203085387160737484L;
/**
* Construct a new packet sending exception
*
* @param message Message that will be logged
* @param cause Cause of the exception
*/
public PacketSendingException(String message, Throwable cause) {
super(message, cause);
}
}
}
}

View file

@ -0,0 +1,65 @@
package dev.esophose.playerparticles.particles.spawning;
import dev.esophose.playerparticles.manager.ConfigurationManager.Setting;
import dev.esophose.playerparticles.particles.ParticleEffect;
import dev.esophose.playerparticles.particles.ParticleEffect.ParticleProperty;
import dev.esophose.playerparticles.particles.data.OrdinaryColor;
import dev.esophose.playerparticles.particles.data.ParticleColor;
import dev.esophose.playerparticles.util.NMSUtil;
import org.bukkit.Color;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.Particle.DustOptions;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.material.MaterialData;
public class SpigotParticleSpawner extends ParticleSpawner {
@Override
public void display(ParticleEffect particleEffect, double offsetX, double offsetY, double offsetZ, double speed, int amount, Location center, boolean isLongRange, Player owner) {
if (particleEffect.hasProperty(ParticleProperty.REQUIRES_MATERIAL_DATA))
throw new ParticleDataException("This particle effect requires additional data");
for (Player player : this.getPlayersInRange(center, isLongRange, owner))
player.spawnParticle(particleEffect.getSpigotEnum(), center.getX(), center.getY(), center.getZ(), amount, offsetX, offsetY, offsetZ, speed);
}
@Override
public void display(ParticleEffect particleEffect, ParticleColor color, Location center, boolean isLongRange, Player owner) {
if (!particleEffect.hasProperty(ParticleProperty.COLORABLE))
throw new ParticleColorException("This particle effect is not colorable");
if (particleEffect == ParticleEffect.DUST && NMSUtil.getVersionNumber() >= 13) { // DUST uses a special data object for spawning in 1.13+
OrdinaryColor dustColor = (OrdinaryColor) color;
DustOptions dustOptions = new DustOptions(Color.fromRGB(dustColor.getRed(), dustColor.getGreen(), dustColor.getBlue()), Setting.DUST_SIZE.getFloat());
for (Player player : this.getPlayersInRange(center, isLongRange, owner))
player.spawnParticle(particleEffect.getSpigotEnum(), center.getX(), center.getY(), center.getZ(), 1, 0, 0, 0, 0, dustOptions);
} else {
for (Player player : this.getPlayersInRange(center, isLongRange, owner)) {
// Minecraft clients require that you pass a non-zero value if the Red value should be zero
player.spawnParticle(particleEffect.getSpigotEnum(), center.getX(), center.getY(), center.getZ(), 0, particleEffect == ParticleEffect.DUST && color.getValueX() == 0 ? Float.MIN_VALUE : color.getValueX(), color.getValueY(), color.getValueZ(), 1);
}
}
}
@SuppressWarnings("deprecation")
@Override
public void display(ParticleEffect particleEffect, Material spawnMaterial, double offsetX, double offsetY, double offsetZ, double speed, int amount, Location center, boolean isLongRange, Player owner) {
if (!particleEffect.hasProperty(ParticleProperty.REQUIRES_MATERIAL_DATA))
throw new ParticleDataException("This particle effect does not require additional data");
Object extraData = null;
if (particleEffect.getSpigotEnum().getDataType().getTypeName().equals("org.bukkit.block.data.BlockData")) {
extraData = spawnMaterial.createBlockData();
} else if (particleEffect.getSpigotEnum().getDataType() == ItemStack.class) {
extraData = new ItemStack(spawnMaterial);
} else if (particleEffect.getSpigotEnum().getDataType() == MaterialData.class) {
extraData = new MaterialData(spawnMaterial); // Deprecated, only used in versions < 1.13
}
for (Player player : this.getPlayersInRange(center, isLongRange, owner))
player.spawnParticle(particleEffect.getSpigotEnum(), center.getX(), center.getY(), center.getZ(), amount, offsetX, offsetY, offsetZ, speed, extraData);
}
}

View file

@ -0,0 +1,649 @@
package dev.esophose.playerparticles.particles.spawning.reflective;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import org.bukkit.Bukkit;
/**
* <b>ReflectionUtils</b>
* <p>
* This class provides useful methods which makes dealing with reflection much
* easier, especially when working with Bukkit
* <p>
* You are welcome to use it, modify it and redistribute it under the following
* conditions:
* <ul>
* <li>Don't claim this class as your own
* <li>Don't remove this disclaimer
* </ul>
* <p>
* <i>It would be nice if you provide credit to me if you use this class in a
* published project</i>
*
* @author DarkBlade12
* @version 1.1
*/
public final class ReflectionUtils {
// Prevent accidental construction
private ReflectionUtils() {
}
/**
* Returns the constructor of a given class with the given parameter types
*
* @param clazz Target class
* @param parameterTypes Parameter types of the desired constructor
* @return The constructor of the target class with the specified parameter
* types
* @throws NoSuchMethodException If the desired constructor with the
* specified parameter types cannot be found
* @see DataType
* @see DataType#getPrimitive(Class[])
* @see DataType#compare(Class[], Class[])
*/
public static Constructor<?> getConstructor(Class<?> clazz, Class<?>... parameterTypes) throws NoSuchMethodException {
Class<?>[] primitiveTypes = DataType.getPrimitive(parameterTypes);
for (Constructor<?> constructor : clazz.getConstructors()) {
if (!DataType.compare(DataType.getPrimitive(constructor.getParameterTypes()), primitiveTypes)) {
continue;
}
return constructor;
}
throw new NoSuchMethodException("There is no such constructor in this class with the specified parameter types");
}
/**
* Returns the constructor of a desired class with the given parameter types
*
* @param className Name of the desired target class
* @param packageType Package where the desired target class is located
* @param parameterTypes Parameter types of the desired constructor
* @return The constructor of the desired target class with the specified
* parameter types
* @throws NoSuchMethodException If the desired constructor with the
* specified parameter types cannot be found
* @throws ClassNotFoundException ClassNotFoundException If the desired
* target class with the specified name and package cannot be
* found
*/
public static Constructor<?> getConstructor(String className, PackageType packageType, Class<?>... parameterTypes) throws NoSuchMethodException, ClassNotFoundException {
return getConstructor(packageType.getClass(className), parameterTypes);
}
/**
* Returns an instance of a class with the given arguments
*
* @param clazz Target class
* @param arguments Arguments which are used to construct an object of the
* target class
* @return The instance of the target class with the specified arguments
* @throws InstantiationException If you cannot create an instance of the
* target class due to certain circumstances
* @throws IllegalAccessException If the desired constructor cannot be
* accessed due to certain circumstances
* @throws IllegalArgumentException If the types of the arguments do not
* match the parameter types of the constructor (this should not
* occur since it searches for a constructor with the types of
* the arguments)
* @throws InvocationTargetException If the desired constructor cannot be
* invoked
* @throws NoSuchMethodException If the desired constructor with the
* specified arguments cannot be found
*/
public static Object instantiateObject(Class<?> clazz, Object... arguments) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException {
return getConstructor(clazz, DataType.getPrimitive(arguments)).newInstance(arguments);
}
/**
* Returns an instance of a desired class with the given arguments
*
* @param className Name of the desired target class
* @param packageType Package where the desired target class is located
* @param arguments Arguments which are used to construct an object of the
* desired target class
* @return The instance of the desired target class with the specified
* arguments
* @throws InstantiationException If you cannot create an instance of the
* desired target class due to certain circumstances
* @throws IllegalAccessException If the desired constructor cannot be
* accessed due to certain circumstances
* @throws IllegalArgumentException If the types of the arguments do not
* match the parameter types of the constructor (this should not
* occur since it searches for a constructor with the types of
* the arguments)
* @throws InvocationTargetException If the desired constructor cannot be
* invoked
* @throws NoSuchMethodException If the desired constructor with the
* specified arguments cannot be found
* @throws ClassNotFoundException If the desired target class with the
* specified name and package cannot be found
*/
public static Object instantiateObject(String className, PackageType packageType, Object... arguments) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, ClassNotFoundException {
return instantiateObject(packageType.getClass(className), arguments);
}
/**
* Returns a method of a class with the given parameter types
*
* @param clazz Target class
* @param methodName Name of the desired method
* @param parameterTypes Parameter types of the desired method
* @return The method of the target class with the specified name and
* parameter types
* @throws NoSuchMethodException If the desired method of the target class
* with the specified name and parameter types cannot be found
* @see DataType#getPrimitive(Class[])
* @see DataType#compare(Class[], Class[])
*/
public static Method getMethod(Class<?> clazz, String methodName, Class<?>... parameterTypes) throws NoSuchMethodException {
Class<?>[] primitiveTypes = DataType.getPrimitive(parameterTypes);
for (Method method : clazz.getMethods()) {
if (!method.getName().equals(methodName) || !DataType.compare(DataType.getPrimitive(method.getParameterTypes()), primitiveTypes)) {
continue;
}
return method;
}
throw new NoSuchMethodException("There is no such method in this class with the specified name and parameter types");
}
/**
* Returns a method of a desired class with the given parameter types
*
* @param className Name of the desired target class
* @param packageType Package where the desired target class is located
* @param methodName Name of the desired method
* @param parameterTypes Parameter types of the desired method
* @return The method of the desired target class with the specified name
* and parameter types
* @throws NoSuchMethodException If the desired method of the desired target
* class with the specified name and parameter types cannot be
* found
* @throws ClassNotFoundException If the desired target class with the
* specified name and package cannot be found
*/
public static Method getMethod(String className, PackageType packageType, String methodName, Class<?>... parameterTypes) throws NoSuchMethodException, ClassNotFoundException {
return getMethod(packageType.getClass(className), methodName, parameterTypes);
}
/**
* Invokes a method on an object with the given arguments
*
* @param instance Target object
* @param methodName Name of the desired method
* @param arguments Arguments which are used to invoke the desired method
* @return The result of invoking the desired method on the target object
* @throws IllegalAccessException If the desired method cannot be accessed
* due to certain circumstances
* @throws IllegalArgumentException If the types of the arguments do not
* match the parameter types of the method (this should not
* occur since it searches for a method with the types of the
* arguments)
* @throws InvocationTargetException If the desired method cannot be invoked
* on the target object
* @throws NoSuchMethodException If the desired method of the class of the
* target object with the specified name and arguments cannot be
* found
*/
public static Object invokeMethod(Object instance, String methodName, Object... arguments) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException {
return getMethod(instance.getClass(), methodName, DataType.getPrimitive(arguments)).invoke(instance, arguments);
}
/**
* Invokes a method of the target class on an object with the given
* arguments
*
* @param instance Target object
* @param clazz Target class
* @param methodName Name of the desired method
* @param arguments Arguments which are used to invoke the desired method
* @return The result of invoking the desired method on the target object
* @throws IllegalAccessException If the desired method cannot be accessed
* due to certain circumstances
* @throws IllegalArgumentException If the types of the arguments do not
* match the parameter types of the method (this should not
* occur since it searches for a method with the types of the
* arguments)
* @throws InvocationTargetException If the desired method cannot be invoked
* on the target object
* @throws NoSuchMethodException If the desired method of the target class
* with the specified name and arguments cannot be found
* @see #getMethod(Class, String, Class...)
* @see DataType#getPrimitive(Object[])
*/
public static Object invokeMethod(Object instance, Class<?> clazz, String methodName, Object... arguments) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException {
return getMethod(clazz, methodName, DataType.getPrimitive(arguments)).invoke(instance, arguments);
}
/**
* Invokes a method of a desired class on an object with the given arguments
*
* @param instance Target object
* @param className Name of the desired target class
* @param packageType Package where the desired target class is located
* @param methodName Name of the desired method
* @param arguments Arguments which are used to invoke the desired method
* @return The result of invoking the desired method on the target object
* @throws IllegalAccessException If the desired method cannot be accessed
* due to certain circumstances
* @throws IllegalArgumentException If the types of the arguments do not
* match the parameter types of the method (this should not
* occur since it searches for a method with the types of the
* arguments)
* @throws InvocationTargetException If the desired method cannot be invoked
* on the target object
* @throws NoSuchMethodException If the desired method of the desired target
* class with the specified name and arguments cannot be found
* @throws ClassNotFoundException If the desired target class with the
* specified name and package cannot be found
*/
public static Object invokeMethod(Object instance, String className, PackageType packageType, String methodName, Object... arguments) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, ClassNotFoundException {
return invokeMethod(instance, packageType.getClass(className), methodName, arguments);
}
/**
* Returns a field of the target class with the given name
*
* @param clazz Target class
* @param declared Whether the desired field is declared or not
* @param fieldName Name of the desired field
* @return The field of the target class with the specified name
* @throws NoSuchFieldException If the desired field of the given class
* cannot be found
* @throws SecurityException If the desired field cannot be made accessible
*/
public static Field getField(Class<?> clazz, boolean declared, String fieldName) throws NoSuchFieldException, SecurityException {
Field field = declared ? clazz.getDeclaredField(fieldName) : clazz.getField(fieldName);
field.setAccessible(true);
return field;
}
/**
* Returns a field of a desired class with the given name
*
* @param className Name of the desired target class
* @param packageType Package where the desired target class is located
* @param declared Whether the desired field is declared or not
* @param fieldName Name of the desired field
* @return The field of the desired target class with the specified name
* @throws NoSuchFieldException If the desired field of the desired class
* cannot be found
* @throws SecurityException If the desired field cannot be made accessible
* @throws ClassNotFoundException If the desired target class with the
* specified name and package cannot be found
* @see #getField(Class, boolean, String)
*/
public static Field getField(String className, PackageType packageType, boolean declared, String fieldName) throws NoSuchFieldException, SecurityException, ClassNotFoundException {
return getField(packageType.getClass(className), declared, fieldName);
}
/**
* Returns the value of a field of the given class of an object
*
* @param instance Target object
* @param clazz Target class
* @param declared Whether the desired field is declared or not
* @param fieldName Name of the desired field
* @return The value of field of the target object
* @throws IllegalArgumentException If the target object does not feature
* the desired field
* @throws IllegalAccessException If the desired field cannot be accessed
* @throws NoSuchFieldException If the desired field of the target class
* cannot be found
* @throws SecurityException If the desired field cannot be made accessible
* @see #getField(Class, boolean, String)
*/
public static Object getValue(Object instance, Class<?> clazz, boolean declared, String fieldName) throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException {
return getField(clazz, declared, fieldName).get(instance);
}
/**
* Returns the value of a field of a desired class of an object
*
* @param instance Target object
* @param className Name of the desired target class
* @param packageType Package where the desired target class is located
* @param declared Whether the desired field is declared or not
* @param fieldName Name of the desired field
* @return The value of field of the target object
* @throws IllegalArgumentException If the target object does not feature
* the desired field
* @throws IllegalAccessException If the desired field cannot be accessed
* @throws NoSuchFieldException If the desired field of the desired class
* cannot be found
* @throws SecurityException If the desired field cannot be made accessible
* @throws ClassNotFoundException If the desired target class with the
* specified name and package cannot be found
* @see #getValue(Object, Class, boolean, String)
*/
public static Object getValue(Object instance, String className, PackageType packageType, boolean declared, String fieldName) throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException, ClassNotFoundException {
return getValue(instance, packageType.getClass(className), declared, fieldName);
}
/**
* Returns the value of a field with the given name of an object
*
* @param instance Target object
* @param declared Whether the desired field is declared or not
* @param fieldName Name of the desired field
* @return The value of field of the target object
* @throws IllegalArgumentException If the target object does not feature
* the desired field (should not occur since it searches for a
* field with the given name in the class of the object)
* @throws IllegalAccessException If the desired field cannot be accessed
* @throws NoSuchFieldException If the desired field of the target object
* cannot be found
* @throws SecurityException If the desired field cannot be made accessible
* @see #getValue(Object, Class, boolean, String)
*/
public static Object getValue(Object instance, boolean declared, String fieldName) throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException {
return getValue(instance, instance.getClass(), declared, fieldName);
}
/**
* Sets the value of a field of the given class of an object
*
* @param instance Target object
* @param clazz Target class
* @param declared Whether the desired field is declared or not
* @param fieldName Name of the desired field
* @param value New value
* @throws IllegalArgumentException If the type of the value does not match
* the type of the desired field
* @throws IllegalAccessException If the desired field cannot be accessed
* @throws NoSuchFieldException If the desired field of the target class
* cannot be found
* @throws SecurityException If the desired field cannot be made accessible
* @see #getField(Class, boolean, String)
*/
public static void setValue(Object instance, Class<?> clazz, boolean declared, String fieldName, Object value) throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException {
getField(clazz, declared, fieldName).set(instance, value);
}
/**
* Sets the value of a field of a desired class of an object
*
* @param instance Target object
* @param className Name of the desired target class
* @param packageType Package where the desired target class is located
* @param declared Whether the desired field is declared or not
* @param fieldName Name of the desired field
* @param value New value
* @throws IllegalArgumentException If the type of the value does not match
* the type of the desired field
* @throws IllegalAccessException If the desired field cannot be accessed
* @throws NoSuchFieldException If the desired field of the desired class
* cannot be found
* @throws SecurityException If the desired field cannot be made accessible
* @throws ClassNotFoundException If the desired target class with the
* specified name and package cannot be found
* @see #setValue(Object, Class, boolean, String, Object)
*/
public static void setValue(Object instance, String className, PackageType packageType, boolean declared, String fieldName, Object value) throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException, ClassNotFoundException {
setValue(instance, packageType.getClass(className), declared, fieldName, value);
}
/**
* Sets the value of a field with the given name of an object
*
* @param instance Target object
* @param declared Whether the desired field is declared or not
* @param fieldName Name of the desired field
* @param value New value
* @throws IllegalArgumentException If the type of the value does not match
* the type of the desired field
* @throws IllegalAccessException If the desired field cannot be accessed
* @throws NoSuchFieldException If the desired field of the target object
* cannot be found
* @throws SecurityException If the desired field cannot be made accessible
* @see #setValue(Object, Class, boolean, String, Object)
*/
public static void setValue(Object instance, boolean declared, String fieldName, Object value) throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException {
setValue(instance, instance.getClass(), declared, fieldName, value);
}
/**
* Represents an enumeration of dynamic packages of NMS and CraftBukkit
* <p>
* This class is part of the <b>ReflectionUtils</b> and follows the same
* usage conditions
*
* @author DarkBlade12
* @since 1.0
*/
public enum PackageType {
MINECRAFT_SERVER("net.minecraft.server." + getServerVersion()), CRAFTBUKKIT("org.bukkit.craftbukkit." + getServerVersion()), CRAFTBUKKIT_BLOCK(CRAFTBUKKIT, "block"), CRAFTBUKKIT_CHUNKIO(CRAFTBUKKIT, "chunkio"), CRAFTBUKKIT_COMMAND(CRAFTBUKKIT, "command"), CRAFTBUKKIT_CONVERSATIONS(CRAFTBUKKIT, "conversations"), CRAFTBUKKIT_ENCHANTMENS(CRAFTBUKKIT, "enchantments"), CRAFTBUKKIT_ENTITY(CRAFTBUKKIT, "entity"), CRAFTBUKKIT_EVENT(CRAFTBUKKIT, "event"), CRAFTBUKKIT_GENERATOR(CRAFTBUKKIT, "generator"), CRAFTBUKKIT_HELP(CRAFTBUKKIT, "help"), CRAFTBUKKIT_INVENTORY(CRAFTBUKKIT, "inventory"), CRAFTBUKKIT_MAP(CRAFTBUKKIT, "map"), CRAFTBUKKIT_METADATA(CRAFTBUKKIT, "metadata"), CRAFTBUKKIT_POTION(CRAFTBUKKIT, "potion"), CRAFTBUKKIT_PROJECTILES(CRAFTBUKKIT, "projectiles"), CRAFTBUKKIT_SCHEDULER(CRAFTBUKKIT, "scheduler"), CRAFTBUKKIT_SCOREBOARD(CRAFTBUKKIT, "scoreboard"), CRAFTBUKKIT_UPDATER(CRAFTBUKKIT, "updater"), CRAFTBUKKIT_UTIL(CRAFTBUKKIT, "util");
private final String path;
/**
* Construct a new package type
*
* @param path Path of the package
*/
private PackageType(String path) {
this.path = path;
}
/**
* Construct a new package type
*
* @param parent Parent package of the package
* @param path Path of the package
*/
private PackageType(PackageType parent, String path) {
this(parent + "." + path);
}
/**
* Returns the path of this package type
*
* @return The path
*/
public String getPath() {
return path;
}
/**
* Returns the class with the given name
*
* @param className Name of the desired class
* @return The class with the specified name
* @throws ClassNotFoundException If the desired class with the
* specified name and package cannot be found
*/
public Class<?> getClass(String className) throws ClassNotFoundException {
return Class.forName(this + "." + className);
}
// Override for convenience
@Override
public String toString() {
return path;
}
/**
* Returns the version of your server
*
* @return The server version
*/
public static String getServerVersion() {
return Bukkit.getServer().getClass().getPackage().getName().substring(23);
}
}
/**
* Represents an enumeration of Java data types with corresponding classes
* <p>
* This class is part of the <b>ReflectionUtils</b> and follows the same
* usage conditions
*
* @author DarkBlade12
* @since 1.0
*/
public enum DataType {
BYTE(byte.class, Byte.class), SHORT(short.class, Short.class), INTEGER(int.class, Integer.class), LONG(long.class, Long.class), CHARACTER(char.class, Character.class), FLOAT(float.class, Float.class), DOUBLE(double.class, Double.class), BOOLEAN(boolean.class, Boolean.class);
private static final Map<Class<?>, DataType> CLASS_MAP = new HashMap<Class<?>, DataType>();
private final Class<?> primitive;
private final Class<?> reference;
// Initialize map for quick class lookup
static {
for (DataType type : values()) {
CLASS_MAP.put(type.primitive, type);
CLASS_MAP.put(type.reference, type);
}
}
/**
* Construct a new data type
*
* @param primitive Primitive class of this data type
* @param reference Reference class of this data type
*/
private DataType(Class<?> primitive, Class<?> reference) {
this.primitive = primitive;
this.reference = reference;
}
/**
* Returns the primitive class of this data type
*
* @return The primitive class
*/
public Class<?> getPrimitive() {
return primitive;
}
/**
* Returns the reference class of this data type
*
* @return The reference class
*/
public Class<?> getReference() {
return reference;
}
/**
* Returns the data type with the given primitive/reference class
*
* @param clazz Primitive/Reference class of the data type
* @return The data type
*/
public static DataType fromClass(Class<?> clazz) {
return CLASS_MAP.get(clazz);
}
/**
* Returns the primitive class of the data type with the given reference
* class
*
* @param clazz Reference class of the data type
* @return The primitive class
*/
public static Class<?> getPrimitive(Class<?> clazz) {
DataType type = fromClass(clazz);
return type == null ? clazz : type.getPrimitive();
}
/**
* Returns the reference class of the data type with the given primitive
* class
*
* @param clazz Primitive class of the data type
* @return The reference class
*/
public static Class<?> getReference(Class<?> clazz) {
DataType type = fromClass(clazz);
return type == null ? clazz : type.getReference();
}
/**
* Returns the primitive class array of the given class array
*
* @param classes Given class array
* @return The primitive class array
*/
public static Class<?>[] getPrimitive(Class<?>[] classes) {
int length = classes == null ? 0 : classes.length;
Class<?>[] types = new Class<?>[length];
for (int index = 0; index < length; index++) {
types[index] = getPrimitive(classes[index]);
}
return types;
}
/**
* Returns the reference class array of the given class array
*
* @param classes Given class array
* @return The reference class array
*/
public static Class<?>[] getReference(Class<?>[] classes) {
int length = classes == null ? 0 : classes.length;
Class<?>[] types = new Class<?>[length];
for (int index = 0; index < length; index++) {
types[index] = getReference(classes[index]);
}
return types;
}
/**
* Returns the primitive class array of the given object array
*
* @param objects Given object array
* @return The primitive class array
*/
public static Class<?>[] getPrimitive(Object[] objects) {
int length = objects == null ? 0 : objects.length;
Class<?>[] types = new Class<?>[length];
for (int index = 0; index < length; index++) {
types[index] = getPrimitive(objects[index].getClass());
}
return types;
}
/**
* Returns the reference class array of the given object array
*
* @param objects Given object array
* @return The reference class array
*/
public static Class<?>[] getReference(Object[] objects) {
int length = objects == null ? 0 : objects.length;
Class<?>[] types = new Class<?>[length];
for (int index = 0; index < length; index++) {
types[index] = getReference(objects[index].getClass());
}
return types;
}
/**
* Compares two class arrays on equivalence
*
* @param primary Primary class array
* @param secondary Class array which is compared to the primary array
* @return Whether these arrays are equal or not
*/
public static boolean compare(Class<?>[] primary, Class<?>[] secondary) {
if (primary == null || secondary == null || primary.length != secondary.length) {
return false;
}
for (int index = 0; index < primary.length; index++) {
Class<?> primaryClass = primary[index];
Class<?> secondaryClass = secondary[index];
if (primaryClass.equals(secondaryClass) || primaryClass.isAssignableFrom(secondaryClass)) {
continue;
}
return false;
}
return true;
}
}
}

View file

@ -0,0 +1,84 @@
package dev.esophose.playerparticles.particles.spawning.reflective;
import dev.esophose.playerparticles.util.NMSUtil;
public enum ReflectiveParticleEffectMapping {
POOF("explode", 0),
EXPLOSION("largeexplode", 1),
EXPLOSION_EMITTER("hugeexplosion", 2),
FIREWORK("fireworksSpark", 3),
BUBBLE("bubble", 4),
SPLASH("splash", 5),
FISHING("wake", 6),
//SUSPENDED("suspended", 7),
MYCELIUM("depthSuspend", 8),
CRIT("crit", 9),
ENCHANTED_HIT("magicCrit", 10),
SMOKE("smoke", 11),
LARGE_SMOKE("largesmoke", 12),
SPELL("spell", 13),
INSTANT_EFFECT("instantSpell", 14),
ENTITY_EFFECT("mobSpell", 15),
AMBIENT_ENTITY_EFFECT("mobSpellAmbient", 16),
WITCH("witchMagic", 17),
DRIPPING_WATER("dripWater", 18),
DRIPPING_LAVA("dripLava", 19),
ANGRY_VILLAGER("angryVillager", 20),
HAPPY_VILLAGER("happyVillager", 21),
NOTE("note", 23),
PORTAL("portal", 24),
ENCHANT("enchantmenttable", 25),
FLAME("flame", 26),
LAVA("lava", 27),
FOOTSTEP("footstep", 28),
CLOUD("cloud", 29),
DUST("reddust", 30),
ITEM_SNOWBALL("snowballpoof", 31),
//SNOW_SHOVEL("snowshovel", 32),
ITEM_SLIME("slime", 33),
HEART("heart", 34),
BARRIER("barrier", 35, 8),
ITEM("iconcrack", 36),
BLOCK("blockcrack", 37),
//BLOCK_DUST("blockdust", 38),
RAIN("droplet", 39, 8),
//ITEM_TAKE("take", 40, 8),
ELDER_GUARDIAN("mobappearance", 41, 8);
private final String name;
private final int id;
private final boolean supported;
ReflectiveParticleEffectMapping(String name, int id, int requiredVersion) {
this.name = name;
this.id = id;
this.supported = NMSUtil.getVersionNumber() >= requiredVersion;
}
ReflectiveParticleEffectMapping(String name, int id) {
this(name, id, -1);
}
/**
* @return the name of the internal minecraft particle effect
*/
public String getName() {
return this.name;
}
/**
* @return the id of the internal minecraft particle effect
*/
public int getId() {
return this.id;
}
/**
* @return true if this particle effect is supported, otherwise false
*/
public boolean isSupported() {
return this.supported;
}
}

View file

@ -4,6 +4,7 @@ import dev.esophose.playerparticles.PlayerParticles;
import dev.esophose.playerparticles.manager.ParticleStyleManager;
import dev.esophose.playerparticles.particles.PParticle;
import dev.esophose.playerparticles.particles.ParticlePair;
import dev.esophose.playerparticles.util.ParticleUtils;
import java.util.Collections;
import java.util.List;
import org.bukkit.Location;
@ -48,7 +49,7 @@ public interface ParticleStyle {
* @return The Material icon that represents this style in the GUI
*/
default Material getGuiIconMaterial() {
return Material.BARRIER;
return ParticleUtils.FALLBACK_MATERIAL;
}
/**

View file

@ -10,6 +10,7 @@ import dev.esophose.playerparticles.particles.PPlayer;
import dev.esophose.playerparticles.particles.ParticleEffect;
import dev.esophose.playerparticles.particles.ParticlePair;
import dev.esophose.playerparticles.util.MathL;
import dev.esophose.playerparticles.util.NMSUtil;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@ -60,7 +61,7 @@ public class ParticleStyleCelebration extends DefaultParticleStyle {
Random random = new Random();
for (PPlayer pplayer : particleManager.getPPlayers()) {
Player player = pplayer.getPlayer();
if (player != null && player.getGameMode() != GameMode.SPECTATOR && permissionManager.isWorldEnabled(player.getWorld().getName()))
if (player != null && (NMSUtil.getVersionNumber() < 8 || player.getGameMode() != GameMode.SPECTATOR) && permissionManager.isWorldEnabled(player.getWorld().getName()))
for (ParticlePair particle : pplayer.getActiveParticles())
if (particle.getStyle() == this)
this.spawnFirework(player.getLocation(), pplayer, pplayer.getPlayer(), particle, random);

View file

@ -54,7 +54,7 @@ public class ParticleStylePopper extends DefaultParticleStyle {
@Override
protected List<String> getGuiIconMaterialNames() {
return Arrays.asList("POPPED_CHORUS_FRUIT", "CHORUS_FRUIT_POPPED");
return Arrays.asList("POPPED_CHORUS_FRUIT", "CHORUS_FRUIT_POPPED", "PUMPKIN_SEEDS");
}
@Override

View file

@ -7,6 +7,7 @@ import dev.esophose.playerparticles.manager.ParticleManager;
import dev.esophose.playerparticles.particles.PParticle;
import dev.esophose.playerparticles.particles.PPlayer;
import dev.esophose.playerparticles.particles.ParticlePair;
import dev.esophose.playerparticles.util.NMSUtil;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@ -76,9 +77,17 @@ public class ParticleStyleSwords extends DefaultParticleStyle implements Listene
Player player = (Player) event.getDamager();
LivingEntity entity = (LivingEntity) event.getEntity();
PPlayer pplayer = PlayerParticles.getInstance().getManager(DataManager.class).getPPlayer(player.getUniqueId());
if (pplayer == null || !this.isSword(player.getInventory().getItemInMainHand()))
if (pplayer == null)
return;
if (NMSUtil.getVersionNumber() > 8) {
if (!this.isSword(player.getInventory().getItemInMainHand()))
return;
} else {
if (!this.isSword(player.getInventory().getItemInHand()))
return;
}
for (ParticlePair particle : pplayer.getActiveParticlesForStyle(DefaultStyles.SWORDS)) {
Location loc = entity.getLocation().clone().add(0, 1, 0);
particleManager.displayParticles(player, player.getWorld(), particle, DefaultStyles.SWORDS.getParticles(particle, loc), false);

View file

@ -6,6 +6,7 @@ import dev.esophose.playerparticles.particles.ParticlePair;
import dev.esophose.playerparticles.util.MathL;
import dev.esophose.playerparticles.util.VectorUtils;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import org.bukkit.Location;
@ -44,7 +45,7 @@ public class ParticleStyleWings extends DefaultParticleStyle {
@Override
protected List<String> getGuiIconMaterialNames() {
return Collections.singletonList("ELYTRA");
return Arrays.asList("ELYTRA", "RAW_CHICKEN");
}
@Override

View file

@ -1,8 +1,5 @@
package dev.esophose.playerparticles.util;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
@ -27,6 +24,8 @@ import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.RegisteredServiceProvider;
import org.bukkit.plugin.ServicePriority;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
/**
* bStats collects some data for plugin authors.
@ -34,7 +33,7 @@ import org.bukkit.plugin.ServicePriority;
* Check out https://bStats.org/ to learn more about bStats!
*/
@SuppressWarnings({"WeakerAccess", "unused"})
public class Metrics {
public class LegacyMetrics {
static {
// You can use the property to disable the check in your test environment
@ -44,7 +43,7 @@ public class Metrics {
new byte[]{'o', 'r', 'g', '.', 'b', 's', 't', 'a', 't', 's', '.', 'b', 'u', 'k', 'k', 'i', 't'});
final String examplePackage = new String(new byte[]{'y', 'o', 'u', 'r', '.', 'p', 'a', 'c', 'k', 'a', 'g', 'e'});
// We want to make sure nobody just copy & pastes the example and use the wrong package names
if (Metrics.class.getPackage().getName().equals(defaultPackage) || Metrics.class.getPackage().getName().equals(examplePackage)) {
if (LegacyMetrics.class.getPackage().getName().equals(defaultPackage) || LegacyMetrics.class.getPackage().getName().equals(examplePackage)) {
throw new IllegalStateException("bStats Metrics class has not been relocated correctly!");
}
}
@ -79,7 +78,7 @@ public class Metrics {
*
* @param plugin The plugin which stats should be submitted.
*/
public Metrics(Plugin plugin) {
public LegacyMetrics(Plugin plugin) {
if (plugin == null) {
throw new IllegalArgumentException("Plugin cannot be null!");
}
@ -119,10 +118,8 @@ public class Metrics {
// Load the data
serverUUID = config.getString("serverUuid");
logFailedRequests = config.getBoolean("logFailedRequests", false);
this.enabled = config.getBoolean("enabled", true);
logSentData = config.getBoolean("logSentData", false);
logResponseStatusText = config.getBoolean("logResponseStatusText", false);
if (this.enabled) {
enabled = config.getBoolean("enabled", true);
if (enabled) {
boolean found = false;
// Search for all other bStats Metrics classes to see if we are the first one
for (Class<?> service : Bukkit.getServicesManager().getKnownServices()) {
@ -133,10 +130,10 @@ public class Metrics {
} catch (NoSuchFieldException ignored) { }
}
// Register our service
Bukkit.getServicesManager().register(Metrics.class, this, plugin, ServicePriority.Normal);
Bukkit.getServicesManager().register(LegacyMetrics.class, this, plugin, ServicePriority.Normal);
if (!found) {
// We are the first!
this.startSubmitting();
startSubmitting();
}
}
}
@ -147,7 +144,7 @@ public class Metrics {
* @return Whether bStats is enabled or not.
*/
public boolean isEnabled() {
return this.enabled;
return enabled;
}
/**
@ -158,13 +155,13 @@ public class Metrics {
timer.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
if (!Metrics.this.plugin.isEnabled()) { // Plugin was disabled
if (!plugin.isEnabled()) { // Plugin was disabled
timer.cancel();
return;
}
// Nevertheless we want our code to run in the Bukkit main thread, so we have to use the Bukkit scheduler
// Don't be afraid! The connection to the bStats server is still async, only the stats collection is sync ;)
Bukkit.getScheduler().runTask(Metrics.this.plugin, Metrics.this::submitData);
Bukkit.getScheduler().runTask(plugin, () -> submitData());
}
}, 1000 * 60 * 5, 1000 * 60 * 30);
// Submit the data every 30 minutes, first time after 5 minutes to give other plugins enough time to start
@ -178,15 +175,16 @@ public class Metrics {
*
* @return The plugin specific data.
*/
public JsonObject getPluginData() {
JsonObject data = new JsonObject();
public JSONObject getPluginData() {
JSONObject data = new JSONObject();
String pluginName = this.plugin.getDescription().getName();
String pluginVersion = this.plugin.getDescription().getVersion();
String pluginName = plugin.getDescription().getName();
String pluginVersion = plugin.getDescription().getVersion();
data.addProperty("pluginName", pluginName); // Append the name of the plugin
data.addProperty("pluginVersion", pluginVersion); // Append the version of the plugin
data.add("customCharts", new JsonArray());
data.put("pluginName", pluginName); // Append the name of the plugin
data.put("pluginVersion", pluginVersion); // Append the version of the plugin
JSONArray customCharts = new JSONArray();
data.put("customCharts", customCharts);
return data;
}
@ -196,7 +194,7 @@ public class Metrics {
*
* @return The server specific data.
*/
private JsonObject getServerData() {
private JSONObject getServerData() {
// Minecraft specific data
int playerAmount;
try {
@ -211,7 +209,6 @@ public class Metrics {
}
int onlineMode = Bukkit.getOnlineMode() ? 1 : 0;
String bukkitVersion = Bukkit.getVersion();
String bukkitName = Bukkit.getName();
// OS/Java specific data
String javaVersion = System.getProperty("java.version");
@ -220,20 +217,19 @@ public class Metrics {
String osVersion = System.getProperty("os.version");
int coreCount = Runtime.getRuntime().availableProcessors();
JsonObject data = new JsonObject();
JSONObject data = new JSONObject();
data.addProperty("serverUUID", serverUUID);
data.put("serverUUID", serverUUID);
data.addProperty("playerAmount", playerAmount);
data.addProperty("onlineMode", onlineMode);
data.addProperty("bukkitVersion", bukkitVersion);
data.addProperty("bukkitName", bukkitName);
data.put("playerAmount", playerAmount);
data.put("onlineMode", onlineMode);
data.put("bukkitVersion", bukkitVersion);
data.addProperty("javaVersion", javaVersion);
data.addProperty("osName", osName);
data.addProperty("osArch", osArch);
data.addProperty("osVersion", osVersion);
data.addProperty("coreCount", coreCount);
data.put("javaVersion", javaVersion);
data.put("osName", osName);
data.put("osArch", osArch);
data.put("osVersion", osVersion);
data.put("coreCount", coreCount);
return data;
}
@ -242,9 +238,9 @@ public class Metrics {
* Collects the data and sends it afterwards.
*/
private void submitData() {
final JsonObject data = this.getServerData();
final JSONObject data = getServerData();
JsonArray pluginData = new JsonArray();
JSONArray pluginData = new JSONArray();
// Search for all other bStats Metrics classes to get their plugin data
for (Class<?> service : Bukkit.getServicesManager().getKnownServices()) {
try {
@ -252,43 +248,27 @@ public class Metrics {
for (RegisteredServiceProvider<?> provider : Bukkit.getServicesManager().getRegistrations(service)) {
try {
Object plugin = provider.getService().getMethod("getPluginData").invoke(provider.getProvider());
if (plugin instanceof JsonObject) {
pluginData.add((JsonObject) plugin);
} else { // old bstats version compatibility
try {
Class<?> jsonObjectJsonSimple = Class.forName("org.json.simple.JSONObject");
if (plugin.getClass().isAssignableFrom(jsonObjectJsonSimple)) {
Method jsonStringGetter = jsonObjectJsonSimple.getDeclaredMethod("toJSONString");
jsonStringGetter.setAccessible(true);
String jsonString = (String) jsonStringGetter.invoke(plugin);
JsonObject object = new JsonParser().parse(jsonString).getAsJsonObject();
pluginData.add(object);
}
} catch (ClassNotFoundException e) {
// minecraft version 1.14+
if (logFailedRequests) {
this.plugin.getLogger().log(Level.SEVERE, "Encountered unexpected exception ", e);
}
}
}
pluginData.add(provider.getService().getMethod("getPluginData").invoke(provider.getProvider()));
} catch (NullPointerException | NoSuchMethodException | IllegalAccessException | InvocationTargetException ignored) {
}
}
} catch (NoSuchFieldException ignored) { }
}
data.add("plugins", pluginData);
data.put("plugins", pluginData);
// Create a new thread for the connection to the bStats server
new Thread(() -> {
try {
// Send the data
sendData(this.plugin, data);
} catch (Exception e) {
// Something went wrong! :(
if (logFailedRequests) {
this.plugin.getLogger().log(Level.WARNING, "Could not submit plugin stats of " + this.plugin.getName(), e);
new Thread(new Runnable() {
@Override
public void run() {
try {
// Send the data
sendData(plugin, data);
} catch (Exception e) {
// Something went wrong! :(
if (logFailedRequests) {
plugin.getLogger().log(Level.WARNING, "Could not submit plugin stats of " + plugin.getName(), e);
}
}
}
}).start();
@ -301,7 +281,7 @@ public class Metrics {
* @param data The data to send.
* @throws Exception If the request failed.
*/
private static void sendData(Plugin plugin, JsonObject data) throws Exception {
private static void sendData(Plugin plugin, JSONObject data) throws Exception {
if (data == null) {
throw new IllegalArgumentException("Data cannot be null!");
}

View file

@ -33,4 +33,16 @@ public final class NMSUtil {
return cachedVersionNumber;
}
/**
* @return true if the server is running Spigot or a fork, false otherwise
*/
public static boolean isSpigot() {
try {
Class.forName("org.spigotmc.SpigotConfig");
return true;
} catch (ClassNotFoundException e) {
return false;
}
}
}

View file

@ -1,15 +1,25 @@
package dev.esophose.playerparticles.util;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.apache.commons.lang.WordUtils;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.entity.Player;
public final class ParticleUtils {
public final static Material FALLBACK_MATERIAL;
private static List<String> blockMaterials, itemMaterials;
static {
if (NMSUtil.getVersionNumber() > 7) {
FALLBACK_MATERIAL = Material.BARRIER;
} else {
FALLBACK_MATERIAL = Material.BEDROCK;
}
blockMaterials = new ArrayList<>();
itemMaterials = new ArrayList<>();
@ -43,19 +53,19 @@ public final class ParticleUtils {
* Finds a block/item as a material from a list of possible strings
* Contains a fallback to the barrier icon just in case
*
* @param barrierFallback If the material should fall back to barrier
* @param fallback If the material should fall back to barrier
* @param input A list of material names
* @return The first matching material
*/
public static Material closestMatchWithFallback(boolean barrierFallback, String... input) {
public static Material closestMatchWithFallback(boolean fallback, String... input) {
for (String name : input) {
Material mat = closestMatch(name);
if (mat != null)
return mat;
}
if (barrierFallback)
return Material.BARRIER;
if (fallback)
return FALLBACK_MATERIAL;
return null;
}

View file

@ -2,8 +2,8 @@ package dev.esophose.playerparticles.util.inputparser;
import dev.esophose.playerparticles.particles.PPlayer;
import dev.esophose.playerparticles.particles.ParticleEffect;
import dev.esophose.playerparticles.particles.ParticleEffect.NoteColor;
import dev.esophose.playerparticles.particles.ParticleEffect.OrdinaryColor;
import dev.esophose.playerparticles.particles.data.NoteColor;
import dev.esophose.playerparticles.particles.data.OrdinaryColor;
import dev.esophose.playerparticles.styles.ParticleStyle;
import dev.esophose.playerparticles.util.inputparser.parsable.ParsableInteger;
import dev.esophose.playerparticles.util.inputparser.parsable.ParsableLocation;

View file

@ -1,7 +1,11 @@
package dev.esophose.playerparticles.util.inputparser.parsable;
import dev.esophose.playerparticles.particles.PPlayer;
import dev.esophose.playerparticles.util.NMSUtil;
import dev.esophose.playerparticles.util.inputparser.Parsable;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.bukkit.Bukkit;
@ -9,10 +13,22 @@ import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player;
public class ParsableLocation extends Parsable<Location> {
private static Method LivingEntity_getTargetBlock;
static {
if (NMSUtil.getVersionNumber() < 8) {
try {
LivingEntity_getTargetBlock = LivingEntity.class.getDeclaredMethod("getTargetBlock", HashSet.class, int.class);
} catch (ReflectiveOperationException e) {
e.printStackTrace();
}
}
}
public ParsableLocation() {
super(Location.class);
}
@ -23,7 +39,18 @@ public class ParsableLocation extends Parsable<Location> {
Player player = pplayer.getPlayer();
if (player != null && input.equalsIgnoreCase("looking")) {
Block targetBlock = player.getTargetBlock((Set<Material>) null, 8); // Need the Set<Material> cast for 1.9 support
Block targetBlock;
if (NMSUtil.getVersionNumber() > 7) {
targetBlock = player.getTargetBlock((Set<Material>) null, 8); // Need the Set<Material> cast for 1.9 support
} else {
try {
targetBlock = (Block) LivingEntity_getTargetBlock.invoke(player, null, 8);
} catch (ReflectiveOperationException e) {
targetBlock = player.getLocation().getBlock();
e.printStackTrace();
}
}
int maxDistanceSqrd = 6 * 6;
if (targetBlock.getLocation().distanceSquared(player.getLocation()) > maxDistanceSqrd)
return null; // Looking at a block too far away

View file

@ -1,7 +1,7 @@
package dev.esophose.playerparticles.util.inputparser.parsable;
import dev.esophose.playerparticles.particles.PPlayer;
import dev.esophose.playerparticles.particles.ParticleEffect.NoteColor;
import dev.esophose.playerparticles.particles.data.NoteColor;
import dev.esophose.playerparticles.util.inputparser.Parsable;
import java.util.List;

View file

@ -1,7 +1,7 @@
package dev.esophose.playerparticles.util.inputparser.parsable;
import dev.esophose.playerparticles.particles.PPlayer;
import dev.esophose.playerparticles.particles.ParticleEffect.OrdinaryColor;
import dev.esophose.playerparticles.particles.data.OrdinaryColor;
import dev.esophose.playerparticles.util.inputparser.Parsable;
import java.awt.Color;
import java.util.Collections;

View file

@ -51,7 +51,7 @@ raincloud:
style: 'overhead'
data: ''
2:
effect: 'rain'
effect: 'dripping_water'
style: 'overhead'
data: ''
rainbows: