Merge branch 'master' into version-compat

This commit is contained in:
Esophose 2020-06-13 13:07:47 -06:00
parent 9567b06e23
commit a82b4d125e
93 changed files with 1496 additions and 698 deletions

View file

@ -1,88 +1,100 @@
package dev.esophose.playerparticles.particles;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public enum ParticleEffect {
AMBIENT_ENTITY_EFFECT(ParticleProperty.REQUIRES_COLOR_DATA),
ANGRY_VILLAGER,
ASH,
BARRIER,
BLOCK(ParticleProperty.REQUIRES_BLOCK_DATA),
BUBBLE,
BUBBLE_COLUMN_UP,
BUBBLE_POP,
CAMPFIRE_COSY_SMOKE,
CAMPFIRE_SIGNAL_SMOKE,
CLOUD,
COMPOSTER,
CRIMSON_SPORE,
CRIT,
CURRENT_DOWN,
DAMAGE_INDICATOR,
DOLPHIN,
DRAGON_BREATH,
DRIPPING_HONEY,
DRIPPING_LAVA,
DRIPPING_OBSIDIAN_TEAR,
DRIPPING_WATER,
DUST(ParticleProperty.REQUIRES_COLOR_DATA),
ELDER_GUARDIAN,
ENCHANT,
ENCHANTED_HIT,
END_ROD,
ENTITY_EFFECT(ParticleProperty.REQUIRES_COLOR_DATA),
EXPLOSION,
EXPLOSION_EMITTER,
FALLING_DUST(ParticleProperty.REQUIRES_BLOCK_DATA),
FALLING_HONEY,
FALLING_LAVA,
FALLING_NECTAR,
FALLING_OBSIDIAN_TEAR,
FALLING_WATER,
FIREWORK,
FISHING,
FLAME,
FLASH,
FOOTSTEP,
HAPPY_VILLAGER,
HEART,
INSTANT_EFFECT,
ITEM(ParticleProperty.REQUIRES_ITEM_DATA),
ITEM_SLIME,
ITEM_SNOWBALL,
LANDING_HONEY,
LANDING_LAVA,
LANDING_OBSIDIAN_TEAR,
LARGE_SMOKE,
LAVA,
MYCELIUM,
NAUTILUS,
NOTE(ParticleProperty.REQUIRES_COLOR_DATA),
POOF,
PORTAL,
RAIN,
SMOKE,
SNEEZE,
SOUL,
SOUL_FIRE_FLAME,
SPELL, // The Minecraft internal name for this is actually "effect", but that's the command name, so it's SPELL for the plugin instead
SPIT,
SPLASH,
SQUID_INK,
SWEEP_ATTACK,
TOTEM_OF_UNDYING,
UNDERWATER,
WARPED_SPORE,
WITCH;
AMBIENT_ENTITY_EFFECT(Collections.singletonList("BEACON"), ParticleProperty.REQUIRES_COLOR_DATA),
ANGRY_VILLAGER(Collections.singletonList("IRON_DOOR")),
ASH(Collections.singletonList("BLACKSTONE")),
BARRIER(Collections.singletonList("BARRIER")),
BLOCK(Collections.singletonList("STONE"), ParticleProperty.REQUIRES_BLOCK_DATA),
BUBBLE(Arrays.asList("BUBBLE_CORAL", "GLASS")),
BUBBLE_COLUMN_UP(Collections.singletonList("MAGMA_BLOCK")),
BUBBLE_POP(Collections.singletonList("BUBBLE_CORAL_FAN")),
CAMPFIRE_COSY_SMOKE(Collections.singletonList("CAMPFIRE")),
CAMPFIRE_SIGNAL_SMOKE(Collections.singletonList("REDSTONE_TORCH")),
CLOUD(Arrays.asList("WHITE_WOOL", "WOOL")),
COMPOSTER(Collections.singletonList("COMPOSTER")),
CRIMSON_SPORE(Collections.singletonList("CRIMSON_SPORE")),
CRIT(Collections.singletonList("IRON_SWORD")),
CURRENT_DOWN(Collections.singletonList("SOUL_SAND")),
DAMAGE_INDICATOR(Collections.singletonList("BOW")),
DOLPHIN(Collections.singletonList("DOLPHIN_SPAWN_EGG")),
DRAGON_BREATH(Arrays.asList("DRAGON_BREATH", "DRAGONS_BREATH")),
DRIPPING_HONEY(Collections.singletonList("BEE_NEST")),
DRIPPING_LAVA(Collections.singletonList("LAVA_BUCKET")),
DRIPPING_OBSIDIAN_TEAR(Collections.singletonList("CRYING_OBSIDIAN")),
DRIPPING_WATER(Collections.singletonList("WATER_BUCKET")),
DUST(Collections.singletonList("REDSTONE"), ParticleProperty.REQUIRES_COLOR_DATA),
ELDER_GUARDIAN(Arrays.asList("ELDER_GUARDIAN_SPAWN_EGG", "PRISMARINE_CRYSTALS")),
ENCHANT(Arrays.asList("ENCHANTING_TABLE", "ENCHANTMENT_TABLE")),
ENCHANTED_HIT(Collections.singletonList("DIAMOND_SWORD")),
END_ROD(Collections.singletonList("END_ROD")),
ENTITY_EFFECT(Collections.singletonList("GLOWSTONE_DUST"), ParticleProperty.REQUIRES_COLOR_DATA),
EXPLOSION(Arrays.asList("FIRE_CHARGE", "FIREBALL")),
EXPLOSION_EMITTER(Collections.singletonList("TNT")),
FALLING_DUST(Collections.singletonList("SAND"), ParticleProperty.REQUIRES_BLOCK_DATA),
FALLING_HONEY(Collections.singletonList("HONEY_BOTTLE")),
FALLING_LAVA(Collections.singletonList("RED_DYE")),
FALLING_NECTAR(Collections.singletonList("HONEYCOMB")),
FALLING_OBSIDIAN_TEAR(Collections.singletonList("ANCIENT_DEBRIS")),
FALLING_WATER(Collections.singletonList("BLUE_DYE")),
FIREWORK(Arrays.asList("FIREWORK_ROCKET", "FIREWORK")),
FISHING(Collections.singletonList("FISHING_ROD")),
FLAME(Collections.singletonList("BLAZE_POWDER")),
FLASH(Collections.singletonList("GOLD_INGOT")),
FOOTSTEP(Collections.singletonList("GRASS")),
HAPPY_VILLAGER(Arrays.asList("DARK_OAK_DOOR_ITEM", "DARK_OAK_DOOR")),
HEART(Arrays.asList("POPPY", "RED_ROSE")),
INSTANT_EFFECT(Arrays.asList("SPLASH_POTION", "POTION")),
ITEM(Collections.singletonList("ITEM_FRAME"), ParticleProperty.REQUIRES_ITEM_DATA),
ITEM_SLIME(Collections.singletonList("SLIME_BALL")),
ITEM_SNOWBALL(Arrays.asList("SNOWBALL", "SNOW_BALL")),
LANDING_HONEY(Collections.singletonList("HONEY_BLOCK")),
LANDING_LAVA(Collections.singletonList("ORANGE_DYE")),
LANDING_OBSIDIAN_TEAR(Collections.singletonList("NETHERITE_BLOCK")),
LARGE_SMOKE(Arrays.asList("COBWEB", "WEB")),
LAVA(Collections.singletonList("MAGMA_CREAM")),
MYCELIUM(Arrays.asList("MYCELIUM", "MYCEL")),
NAUTILUS(Collections.singletonList("HEART_OF_THE_SEA")),
NOTE(Collections.singletonList("NOTE_BLOCK"), ParticleProperty.REQUIRES_COLOR_DATA),
POOF(Arrays.asList("FIREWORK_STAR", "FIREWORK_CHARGE")),
PORTAL(Collections.singletonList("OBSIDIAN")),
RAIN(Arrays.asList("PUFFERFISH_BUCKET", "LAPIS_BLOCK")),
REVERSE_PORTAL(Collections.singletonList("FLINT_AND_STEEL")),
SMOKE(Collections.singletonList("TORCH")),
SNEEZE(Collections.singletonList("BAMBOO")),
SOUL(Collections.singletonList("SOUL_LANTERN")),
SOUL_FIRE_FLAME(Collections.singletonList("SOUL_CAMPFIRE")),
SPELL(Arrays.asList("POTION", "GLASS_BOTTLE")), // The Minecraft internal name for this is actually "effect", but that's the command name, so it's SPELL for the plugin instead
SPIT(Arrays.asList("LLAMA_SPAWN_EGG", "PUMPKIN_SEEDS")),
SPLASH(Arrays.asList("SALMON", "FISH", "RAW_FISH")),
SQUID_INK(Collections.singletonList("INK_SAC")),
SWEEP_ATTACK(Arrays.asList("GOLDEN_SWORD", "GOLD_SWORD")),
TOTEM_OF_UNDYING(Arrays.asList("TOTEM_OF_UNDYING", "TOTEM")),
UNDERWATER(Arrays.asList("TURTLE_HELMET", "SPONGE")),
WARPED_SPORE(Collections.singletonList("WARPED_FUNGUS")),
WHITE_ASH(Collections.singletonList("BASALT")),
WITCH(Collections.singletonList("CAULDRON"));
private List<ParticleProperty> properties;
private List<String> defaultIconMaterialNames;
ParticleEffect(ParticleProperty... properties) {
ParticleEffect(List<String> defaultIconMaterialNames, ParticleProperty... properties) {
this.defaultIconMaterialNames = defaultIconMaterialNames;
this.properties = Arrays.asList(properties);
}
/**
* @return The Material icon that represents this style in the GUI
*/
public List<String> getGuiIconMaterialNames() {
return this.defaultIconMaterialNames;
}
/**
* Determine if this particle effect has a specific property
*

View file

@ -21,11 +21,14 @@ dependencies {
shadow 'me.clip:placeholderapi:2.10.4'
shadow 'org.xerial:sqlite-jdbc:3.23.1'
shadow 'com.comphenix.protocol:ProtocolLib:4.5.0'
shadow 'com.googlecode.json-simple:json-simple:1.1.1'
// Dependencies that will be shaded into the jar
implementation 'org.slf4j:slf4j-api:1.7.25'
implementation 'org.slf4j:slf4j-nop:1.7.25'
implementation 'com.zaxxer:HikariCP:3.2.0'
implementation 'org.bstats:bstats-bukkit-lite:1.7'
implementation 'org.codemc.worldguardwrapper:worldguardwrapper:1.1.6-SNAPSHOT'
}
processResources {

View file

@ -3,14 +3,8 @@
* + Add ability to create/manage fixed effects from the GUI
* * Convert fixed effect ids into names
*/
package dev.esophose.playerparticles;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.ProtocolLibrary;
import com.comphenix.protocol.ProtocolManager;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.wrappers.EnumWrappers.TitleAction;
import dev.esophose.playerparticles.gui.hook.PlayerChatHook;
import dev.esophose.playerparticles.hook.ParticlePlaceholderExpansion;
import dev.esophose.playerparticles.hook.PlaceholderAPIHook;
@ -28,21 +22,15 @@ 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 io.netty.buffer.Unpooled;
import java.awt.Color;
import dev.esophose.playerparticles.util.LegacyMetrics;
import dev.esophose.playerparticles.util.NMSUtil;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.Map;
import net.minecraft.server.v1_15_R1.PacketDataSerializer;
import net.minecraft.server.v1_15_R1.PacketPlayOutTitle;
import org.bstats.bukkit.MetricsLite;
import org.bukkit.Bukkit;
import org.bukkit.plugin.PluginManager;
import org.bukkit.plugin.java.JavaPlugin;
import org.bukkit.scheduler.BukkitRunnable;
/**
* @author Esophose
@ -69,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();
@ -76,80 +70,16 @@ 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();
ProtocolManager protocolManager = ProtocolLibrary.getProtocolManager();
new BukkitRunnable() {
private LinkedList<String> queue = new LinkedList<>();
private String message = "Snapshot 20w17a Chat Hex Code Colors!";
@Override
public void run() {
if (queue.size() >= message.length())
queue.poll();
Color color = getRainbowColor();
String hex = String.format("#%02x%02x%02x", color.getRed(), color.getGreen(), color.getBlue());
queue.add(hex);
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("[");
boolean isFirst = true;
for (int i = 0; i < queue.size(); i++) {
if (!isFirst)
stringBuilder.append(",");
isFirst = false;
stringBuilder.append("{");
stringBuilder.append("\"color\":\"").append(queue.get(i)).append("\",");
stringBuilder.append("\"text\":\"").append(message.charAt(i)).append("\"");
stringBuilder.append("}");
}
stringBuilder.append("]");
PacketContainer timePacket = protocolManager.createPacket(PacketType.fromClass(PacketPlayOutTitle.class));
timePacket.getTitleActions().write(0, TitleAction.TIMES);
timePacket.getIntegers().write(0, 0).write(1, 20).write(2, 0);
PacketDataSerializer dataSerializer = new PacketDataSerializer(Unpooled.buffer());
try {
PacketPlayOutTitle titlePacket = new PacketPlayOutTitle() {
@Override
public void b(PacketDataSerializer var0) {
var0.a(EnumTitleAction.TITLE);
var0.a(stringBuilder.toString());
}
};
titlePacket.b(dataSerializer);
} catch (IOException e) {
e.printStackTrace();
}
byte[] bytes = new byte[dataSerializer.readableBytes()];
dataSerializer.readBytes(bytes);
Bukkit.getOnlinePlayers().forEach(x -> {
try {
protocolManager.sendServerPacket(x, timePacket);
protocolManager.sendWirePacket(x, 0x50, bytes);
} catch (InvocationTargetException e) {
e.printStackTrace();
}
});
}
}.runTaskTimer(this, 0, 1);
PlayerChatHook.setup();
}
private float hue = 0;
private Color getRainbowColor() {
this.hue = (this.hue + 4) % 362;
return Color.getHSBColor(this.hue / 360F, 1.0F, 1.0F);
}
@Override
@ -180,16 +110,6 @@ public class PlayerParticles extends JavaPlugin {
return null;
}
}
/**
* Returns the file which contains this plugin
* Exposes the JavaPlugin.getFile() method
*
* @return File containing this plugin
*/
public File getJarFile() {
return this.getFile();
}
/**
* Reloads the plugin

View file

@ -1,19 +1,18 @@
package dev.esophose.playerparticles.api;
import dev.esophose.playerparticles.PlayerParticles;
import dev.esophose.playerparticles.manager.DataManager;
import dev.esophose.playerparticles.manager.GuiManager;
import dev.esophose.playerparticles.manager.ParticleManager;
import dev.esophose.playerparticles.manager.ParticleStyleManager;
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.ParticleGroup;
import dev.esophose.playerparticles.particles.ParticlePair;
import dev.esophose.playerparticles.particles.color.NoteColor;
import dev.esophose.playerparticles.particles.color.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.PlayerParticles;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
@ -22,7 +21,9 @@ import java.util.Objects;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.command.CommandSender;
@ -51,12 +52,21 @@ public final class PlayerParticlesAPI {
/**
* @return the instance of the PlayerParticlesAPI
*/
@NotNull
public static PlayerParticlesAPI getInstance() {
if (INSTANCE == null)
INSTANCE = new PlayerParticlesAPI();
return INSTANCE;
}
/**
* @return the currently installed version of the plugin
*/
@NotNull
public String getVersion() {
return this.playerParticles.getDescription().getVersion();
}
//region Get PPlayer
/**
@ -441,6 +451,7 @@ public final class PlayerParticlesAPI {
* @param player The player to remove from
* @return The number of particles removed or null if failed
*/
@Nullable
public Integer resetActivePlayerParticles(@NotNull Player player) {
DataManager dataManager = this.playerParticles.getManager(DataManager.class);
PPlayer pplayer = this.getPPlayer(player);
@ -453,6 +464,28 @@ public final class PlayerParticlesAPI {
return amount;
}
/**
* Attempts to reset the active particles for the given player name.
* This works even if the player is offline.
*
* @param playerName The name of the player to reset the active particles for
* @param successConsumer The callback to execute when finished
*/
public void resetActivePlayerParticles(@NotNull String playerName, @Nullable Consumer<Boolean> successConsumer) {
Objects.requireNonNull(playerName);
if (successConsumer == null)
successConsumer = success -> {};
Player player = Bukkit.getPlayer(playerName);
if (player != null) {
this.resetActivePlayerParticles(player);
successConsumer.accept(true);
} else {
this.playerParticles.getManager(DataManager.class).resetActiveParticleGroup(playerName, successConsumer);
}
}
/**
* Gets all active particles from a player
*
@ -1033,30 +1066,4 @@ public final class PlayerParticlesAPI {
//endregion
//region Registering Custom Styles
/**
* Registers a particle style with the plugin
*
* @param particleStyle The particle style to register
*/
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
*/
public void registerEventParticleStyle(@NotNull ParticleStyle particleStyle) {
Objects.requireNonNull(particleStyle);
this.playerParticles.getManager(ParticleStyleManager.class).registerEventStyle(particleStyle);
}
//endregion
}

View file

@ -11,8 +11,8 @@ import dev.esophose.playerparticles.particles.ParticleGroup;
import dev.esophose.playerparticles.particles.ParticlePair;
import dev.esophose.playerparticles.PlayerParticles;
import dev.esophose.playerparticles.api.PlayerParticlesAPI;
import dev.esophose.playerparticles.particles.color.NoteColor;
import dev.esophose.playerparticles.particles.color.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.ParticleUtils;
import dev.esophose.playerparticles.util.StringPlaceholders;

View file

@ -10,8 +10,8 @@ import dev.esophose.playerparticles.particles.ParticlePair;
import dev.esophose.playerparticles.PlayerParticles;
import dev.esophose.playerparticles.api.PlayerParticlesAPI;
import dev.esophose.playerparticles.particles.ParticleProperty;
import dev.esophose.playerparticles.particles.color.NoteColor;
import dev.esophose.playerparticles.particles.color.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.ParticleUtils;
import dev.esophose.playerparticles.util.StringPlaceholders;

View file

@ -10,8 +10,8 @@ import dev.esophose.playerparticles.particles.ParticlePair;
import dev.esophose.playerparticles.PlayerParticles;
import dev.esophose.playerparticles.api.PlayerParticlesAPI;
import dev.esophose.playerparticles.particles.ParticleProperty;
import dev.esophose.playerparticles.particles.color.NoteColor;
import dev.esophose.playerparticles.particles.color.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.ParticleUtils;
import dev.esophose.playerparticles.util.StringPlaceholders;

View file

@ -8,6 +8,7 @@ import dev.esophose.playerparticles.particles.PPlayer;
import dev.esophose.playerparticles.PlayerParticles;
import java.util.ArrayList;
import java.util.List;
import org.bukkit.Bukkit;
public class GUICommandModule implements CommandModule {
@ -45,7 +46,7 @@ public class GUICommandModule implements CommandModule {
return;
}
guiManager.openDefault(pplayer);
Bukkit.getScheduler().runTask(PlayerParticles.getInstance(), () -> guiManager.openDefault(pplayer));
}
public List<String> onTabComplete(PPlayer pplayer, String[] args) {

View file

@ -55,7 +55,9 @@ public class RemoveCommandModule implements CommandModule {
} else { // Removing by effect/style name
ParticleEffect effect = particleManager.getEffectFromName(args[0]);
ParticleStyle style = ParticleStyle.fromName(args[0]);
boolean removed = false;
if (effect != null) {
Set<Integer> toRemove = new HashSet<>();
ParticleGroup activeGroup = pplayer.getActiveParticleGroup();
@ -64,14 +66,18 @@ public class RemoveCommandModule implements CommandModule {
toRemove.add(id);
for (int id : toRemove)
activeGroup.getParticles().remove(id);
if (toRemove.size() > 0) {
PlayerParticlesAPI.getInstance().savePlayerParticleGroup(pplayer.getPlayer(), activeGroup);
localeManager.sendMessage(pplayer, "remove-effect-success", StringPlaceholders.builder("amount", toRemove.size()).addPlaceholder("effect", particleManager.getEffectSettings(effect).getName()).build());
} else {
removed = true;
} else if (style == null) {
localeManager.sendMessage(pplayer, "remove-effect-none", StringPlaceholders.single("effect", particleManager.getEffectSettings(effect).getName()));
return;
}
} else if (style != null) {
}
if (style != null) {
Set<Integer> toRemove = new HashSet<>();
ParticleGroup activeGroup = pplayer.getActiveParticleGroup();
for (int id : activeGroup.getParticles().keySet())
@ -79,15 +85,23 @@ public class RemoveCommandModule implements CommandModule {
toRemove.add(id);
for (int id : toRemove)
activeGroup.getParticles().remove(id);
if (toRemove.size() > 0) {
PlayerParticlesAPI.getInstance().savePlayerParticleGroup(pplayer.getPlayer(), activeGroup);
localeManager.sendMessage(pplayer, "remove-style-success", StringPlaceholders.builder("amount", toRemove.size()).addPlaceholder("style", style.getName()).build());
} else {
removed = true;
} else if (effect == null) {
localeManager.sendMessage(pplayer, "remove-style-none", StringPlaceholders.single("style", style.getName()));
return;
}
}
if (!removed) {
if (effect != null && style != null) {
localeManager.sendMessage(pplayer, "remove-effect-style-none", StringPlaceholders.single("name", style.getName()));
} else {
localeManager.sendMessage(pplayer, "remove-unknown", StringPlaceholders.single("name", args[0]));
}
} else {
localeManager.sendMessage(pplayer, "remove-unknown", StringPlaceholders.single("name", args[0]));
}
}
}

View file

@ -1,6 +1,7 @@
package dev.esophose.playerparticles.command;
import dev.esophose.playerparticles.manager.LocaleManager;
import dev.esophose.playerparticles.manager.PermissionManager;
import dev.esophose.playerparticles.particles.PPlayer;
import dev.esophose.playerparticles.particles.ParticleGroup;
import dev.esophose.playerparticles.PlayerParticles;
@ -8,16 +9,43 @@ import dev.esophose.playerparticles.api.PlayerParticlesAPI;
import dev.esophose.playerparticles.util.StringPlaceholders;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.util.StringUtil;
public class ResetCommandModule implements CommandModule {
public void onCommandExecute(PPlayer pplayer, String[] args) {
int particleCount = pplayer.getActiveParticles().size();
PlayerParticlesAPI.getInstance().savePlayerParticleGroup(pplayer.getPlayer(), ParticleGroup.getDefaultGroup());
PlayerParticles.getInstance().getManager(LocaleManager.class).sendMessage(pplayer, "reset-success", StringPlaceholders.single("amount", particleCount));
}
LocaleManager localeManager = PlayerParticles.getInstance().getManager(LocaleManager.class);
boolean isConsole = pplayer.getPlayer() == null;
if (isConsole && args.length == 0) {
localeManager.sendCustomMessage(Bukkit.getConsoleSender(), "&cOnly players can use this command. Did you mean to use '/pp reset [other]'?");
return;
}
if (args.length == 0 || !PlayerParticles.getInstance().getManager(PermissionManager.class).canResetOthers(pplayer)) {
Integer particleCount = PlayerParticlesAPI.getInstance().resetActivePlayerParticles(pplayer.getPlayer());
if (particleCount != null)
localeManager.sendMessage(pplayer, "reset-success", StringPlaceholders.single("amount", particleCount));
} else {
PlayerParticlesAPI.getInstance().resetActivePlayerParticles(args[0], success -> {
if (success) {
localeManager.sendMessage(pplayer, "reset-others-success", StringPlaceholders.single("other", args[0]));
} else {
localeManager.sendMessage(pplayer, "reset-others-none", StringPlaceholders.single("other", args[0]));
}
});
}}
public List<String> onTabComplete(PPlayer pplayer, String[] args) {
if (args.length == 1 && PlayerParticles.getInstance().getManager(PermissionManager.class).canResetOthers(pplayer)) {
List<String> replacements = Bukkit.getOnlinePlayers().stream().map(Player::getName).collect(Collectors.toList());
List<String> suggestions = new ArrayList<>();
StringUtil.copyPartialMatches(args[0], replacements, suggestions);
return suggestions;
}
return new ArrayList<>();
}
@ -30,7 +58,7 @@ public class ResetCommandModule implements CommandModule {
}
public String getArguments() {
return "";
return "[other]";
}
public boolean requiresEffectsAndStyles() {
@ -38,7 +66,7 @@ public class ResetCommandModule implements CommandModule {
}
public boolean canConsoleExecute() {
return false;
return true;
}
}

View file

@ -2,6 +2,7 @@ package dev.esophose.playerparticles.database;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import dev.esophose.playerparticles.manager.ConfigurationManager.Setting;
import java.sql.Connection;
import java.sql.SQLException;
import org.bukkit.plugin.Plugin;
@ -19,7 +20,7 @@ public class MySQLConnector implements DatabaseConnector {
config.setJdbcUrl("jdbc:mysql://" + hostname + ":" + port + "/" + database + "?useSSL=" + useSSL);
config.setUsername(username);
config.setPassword(password);
config.setMaximumPoolSize(2);
config.setMaximumPoolSize(Setting.MYSQL_CONNECTION_POOL_SIZE.getInt());
try {
this.hikari = new HikariDataSource(config);

View file

@ -0,0 +1,91 @@
package dev.esophose.playerparticles.event;
import dev.esophose.playerparticles.styles.ParticleStyle;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
/**
* An event that gets called during the PlayerParticles style registration
*/
public class ParticleStyleRegistrationEvent extends Event {
private static final HandlerList HANDLER_LIST = new HandlerList();
private Map<String, ParticleStyle> registeredStyles;
private Map<String, ParticleStyle> registeredEventStyles;
public ParticleStyleRegistrationEvent() {
this.registeredStyles = new HashMap<>();
this.registeredEventStyles = new HashMap<>();
}
/**
* @return An unmodifiable map of registered styles keyed by the style internal name
*/
public Map<String, ParticleStyle> getRegisteredStyles() {
return Collections.unmodifiableMap(this.registeredStyles);
}
/**
* @return An unmodifiable map of registered event styles keyed by the style internal name
*/
public Map<String, ParticleStyle> getRegisteredEventStyles() {
return Collections.unmodifiableMap(this.registeredEventStyles);
}
/**
* Registers a ParticleStyle, overwriting any existing styles with the same name
*
* @param style The ParticleStyle to register
* @return true if registered without replacing an existing style, false if an existing style was replaced
*/
public boolean registerStyle(ParticleStyle style) {
if (this.registeredEventStyles.containsKey(style.getInternalName())) {
this.registeredEventStyles.remove(style.getInternalName());
this.registeredStyles.put(style.getInternalName(), style);
return false;
}
return this.registeredStyles.put(style.getInternalName(), style) == null;
}
/**
* Registers an event-based ParticleStyle, overwriting any existing styles with the same name.
* Styles registered with this method bypass the normal update loop, and must instead be spawned manually.
*
* @param style The ParticleStyle to register
* @return true if registered without replacing an existing style, false if an existing style was replaced
*/
public boolean registerEventStyle(ParticleStyle style) {
if (this.registeredStyles.containsKey(style.getInternalName())) {
this.registeredStyles.remove(style.getInternalName());
this.registeredEventStyles.put(style.getInternalName(), style);
return false;
}
return this.registeredEventStyles.put(style.getInternalName(), style) == null;
}
/**
* Unregisters a ParticleStyle
*
* @param internalName The internal name of the ParticleStyle to unregister
* @return true if a style was unregistered, false otherwise
*/
public boolean unregisterStyle(String internalName) {
return this.registeredStyles.remove(internalName) != null || this.registeredEventStyles.remove(internalName) != null;
}
@Override
public HandlerList getHandlers() {
return HANDLER_LIST;
}
public static HandlerList getHandlerList() {
return HANDLER_LIST;
}
}

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

@ -2,6 +2,7 @@ package dev.esophose.playerparticles.gui;
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 dev.esophose.playerparticles.PlayerParticles;
import java.util.ArrayList;
@ -12,12 +13,11 @@ import org.bukkit.Sound;
import org.bukkit.entity.Player;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.InventoryHolder;
import org.bukkit.inventory.ItemFlag;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
public abstract class GuiInventory implements InventoryHolder {
public abstract class GuiInventory {
protected enum BorderColor {
WHITE(0, "WHITE_STAINED_GLASS_PANE"),
@ -57,7 +57,8 @@ public abstract class GuiInventory implements InventoryHolder {
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);
}
@ -156,7 +157,11 @@ public abstract class GuiInventory implements InventoryHolder {
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

@ -8,8 +8,8 @@ import dev.esophose.playerparticles.particles.PPlayer;
import dev.esophose.playerparticles.particles.ParticleEffect;
import dev.esophose.playerparticles.particles.ParticlePair;
import dev.esophose.playerparticles.particles.ParticleProperty;
import dev.esophose.playerparticles.particles.color.NoteColor;
import dev.esophose.playerparticles.particles.color.OrdinaryColor;
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

@ -40,7 +40,7 @@ public class GuiInventoryEditEffect extends GuiInventory {
ParticleEffectSettings effectSettings = particleManager.getEffectSettings(effect);
GuiActionButton selectButton = new GuiActionButton(
slot,
GuiIcon.EFFECT.get(effectSettings.getInternalName()),
effectSettings.getGuiIconMaterial(),
localeManager.getLocaleMessage("gui-color-icon-name") + ParticleUtils.formatName(effectSettings.getName()),
new String[]{localeManager.getLocaleMessage("gui-color-info") + localeManager.getLocaleMessage("gui-select-effect-description", StringPlaceholders.single("effect", ParticleUtils.formatName(effectSettings.getName())))},
(button, isShiftClick) -> {

View file

@ -36,7 +36,7 @@ public class GuiInventoryEditStyle extends GuiInventory {
ParticleStyle style = stylesUserHasPermissionFor.get(i);
GuiActionButton selectButton = new GuiActionButton(
slot,
GuiIcon.STYLE.get(style.getInternalName()),
style.getGuiIconMaterial(),
localeManager.getLocaleMessage("gui-color-icon-name") + ParticleUtils.formatName(style.getName()),
new String[]{localeManager.getLocaleMessage("gui-color-info") + localeManager.getLocaleMessage("gui-select-style-description", StringPlaceholders.single("style", ParticleUtils.formatName(style.getName())))},
(button, isShiftClick) -> {

View file

@ -1,9 +1,11 @@
package dev.esophose.playerparticles.gui.hook;
import dev.esophose.playerparticles.manager.ConfigurationManager.Setting;
import dev.esophose.playerparticles.manager.LocaleManager;
import dev.esophose.playerparticles.util.NMSUtil;
import dev.esophose.playerparticles.util.StringPlaceholders;
import dev.esophose.playerparticles.PlayerParticles;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import net.md_5.bungee.api.ChatMessageType;
@ -25,7 +27,7 @@ public class PlayerChatHook extends BukkitRunnable implements Listener {
* Initializes all the static values for this class
*/
public static void setup() {
hooks = new HashSet<>();
hooks = Collections.synchronizedSet(new HashSet<>());
if (hookTask != null)
hookTask.cancel();
hookTask = new PlayerChatHook().runTaskTimer(PlayerParticles.getInstance(), 0, 20);
@ -42,7 +44,7 @@ public class PlayerChatHook extends BukkitRunnable implements Listener {
if (hook.getPlayerUUID().equals(event.getPlayer().getUniqueId())) {
event.setCancelled(true);
hooks.remove(hook);
Bukkit.getScheduler().scheduleSyncDelayedTask(PlayerParticles.getInstance(), () -> hook.triggerCallback(event.getMessage()));
hook.triggerCallback(event.getMessage());
return;
}
}
@ -53,30 +55,44 @@ public class PlayerChatHook extends BukkitRunnable implements Listener {
*/
public void run() {
Set<PlayerChatHookData> hooksToRemove = new HashSet<>();
for (PlayerChatHookData hook : hooks) {
hook.decrementHookLength();
if (hook.timedOut()) {
hook.triggerCallback(null);
hooksToRemove.add(hook);
continue;
}
Player player = Bukkit.getPlayer(hook.getPlayerUUID());
if (player == null) {
hooksToRemove.remove(hook);
} else {
LocaleManager localeManager = PlayerParticles.getInstance().getManager(LocaleManager.class);
if (NMSUtil.getVersionNumber() == 9) {
if (hook.getMaxHookLength() == hook.getTimeRemaining())
player.sendMessage(localeManager.getLocaleMessage("gui-save-group-hotbar-message", StringPlaceholders.single("seconds", hook.getTimeRemaining())));
String message = localeManager.getLocaleMessage("gui-save-group-hotbar-message", StringPlaceholders.single("seconds", hook.getTimeRemaining()));
if (NMSUtil.getVersionNumber() >= 11) {
switch (Setting.GUI_GROUP_CREATION_MESSAGE_DISPLAY_AREA.getString().toUpperCase()) {
case "ACTION_BAR":
player.spigot().sendMessage(ChatMessageType.ACTION_BAR, TextComponent.fromLegacyText(localeManager.getLocaleMessage("gui-save-group-hotbar-message", StringPlaceholders.single("seconds", hook.getTimeRemaining()))));
break;
case "TITLE":
player.sendTitle("", message, 5, 40, 10);
break;
default:
if (hook.getMaxHookLength() == hook.getTimeRemaining())
player.sendMessage(message);
break;
}
} else {
player.spigot().sendMessage(ChatMessageType.ACTION_BAR, TextComponent.fromLegacyText(localeManager.getLocaleMessage("gui-save-group-hotbar-message", StringPlaceholders.single("seconds", hook.getTimeRemaining()))));
if (hook.getMaxHookLength() == hook.getTimeRemaining())
player.sendMessage(message);
}
}
hook.decrementHookLength();
}
for (PlayerChatHookData hookToRemove : hooksToRemove)
for (PlayerChatHookData hookToRemove : hooksToRemove)
hooks.remove(hookToRemove);
}

View file

@ -37,6 +37,8 @@ public class ParticlePlaceholderExpansion extends PlaceholderExpansion {
return String.valueOf(pplayer.isMoving());
case "is_in_combat":
return String.valueOf(pplayer.isInCombat());
case "is_in_allowed_region":
return String.valueOf(pplayer.isInAllowedRegion());
case "can_see_particles":
return String.valueOf(pplayer.canSeeParticles());
}

View file

@ -0,0 +1,54 @@
package dev.esophose.playerparticles.hook;
import dev.esophose.playerparticles.manager.ConfigurationManager.Setting;
import java.util.List;
import java.util.Set;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.codemc.worldguardwrapper.WorldGuardWrapper;
import org.codemc.worldguardwrapper.region.IWrappedRegion;
public class WorldGuardHook {
private static Boolean enabled;
private static WorldGuardWrapper worldGuardWrapper;
/**
* @return true if WorldGuard is enabled, otherwise false
*/
public static boolean enabled() {
if (enabled != null)
return enabled;
enabled = Bukkit.getPluginManager().getPlugin("WorldGuard") != null;
if (enabled)
worldGuardWrapper = WorldGuardWrapper.getInstance();
return enabled;
}
/**
* Checks if a location is in a region that allows particles to spawn
*
* @param location The location to check
* @return true if the location is in an allowed region, otherwise false
*/
public static boolean isInAllowedRegion(Location location) {
if (!enabled())
return true;
Set<IWrappedRegion> regions = worldGuardWrapper.getRegions(location);
List<String> disallowedRegionIds = Setting.WORLDGUARD_DISALLOWED_REGIONS.getStringList();
if (regions.stream().map(IWrappedRegion::getId).anyMatch(disallowedRegionIds::contains))
return false;
if (Setting.WORLDGUARD_USE_ALLOWED_REGIONS.getBoolean()) {
List<String> allowedRegionIds = Setting.WORLDGUARD_ALLOWED_REGIONS.getStringList();
return regions.stream().map(IWrappedRegion::getId).anyMatch(allowedRegionIds::contains);
}
return true;
}
}

View file

@ -118,6 +118,7 @@ public class EnglishLocale implements Locale {
this.put("remove-effect-none", "&cYou do not have any particles applied with the effect &b%effect%&c!");
this.put("remove-style-success", "&aRemoved &b%amount% &aof your particles with the style of &b%style%&a!");
this.put("remove-style-none", "&cYou do not have any particles applied with the style &b%style%&c!");
this.put("remove-effect-style-none", "&cYou do not have any particles applied with the effect or style &b%name%&c!");
this.put("remove-unknown", "&cAn effect or style with the name of &b%name% &cdoes not exist!");
this.put("#10", "List Messages");
@ -166,6 +167,8 @@ public class EnglishLocale implements Locale {
this.put("#17", "Reset Message");
this.put("reset-success", "&aRemoved &b%amount% &aactive particle(s)!");
this.put("reset-others-success", "&aRemoved particles for &b%other%&a!");
this.put("reset-others-none", "&eNo particles were removed for &b%other%&e.");
this.put("#18", "Fixed Create Messages");
this.put("fixed-create-missing-args", "&cUnable to create fixed effect, you are missing &b%amount% &crequired arguments!");

View file

@ -118,6 +118,7 @@ public class FrenchLocale implements Locale {
this.put("remove-effect-none", "&cVous n'avez pas de particules appliquées avec l'effet &b%effect% &c!");
this.put("remove-style-success", "&aSuppression &b%amount% &ade votre particule avec le style &b%style% &a!");
this.put("remove-style-none", "&cVous n'avez pas de particules appliquées avec le style &b%style% &c!");
this.put("remove-effect-style-none", "&cVous n'avez pas de particules appliquées avec l'effet ou le style &b%name% &c!");
this.put("remove-unknown", "&cL'effect avec le nom ou le style &b%name% &cn'existe pas !");
this.put("#10", "List Messages");
@ -166,6 +167,8 @@ public class FrenchLocale implements Locale {
this.put("#17", "Reset Message");
this.put("reset-success", "&b%amount% &aparticule(s) actives supprimées !");
this.put("reset-others-success", "&aParticules enlevées pour &b%other%&a !");
this.put("reset-others-none", "&eAucune particule n'a été enlevée pour &b%other%&e.");
this.put("#18", "Fixed Create Messages");
this.put("fixed-create-missing-args", "&cImpossible de créer un effet fixe, vous oubliez des arguments : &b%amount%");

View file

@ -118,6 +118,7 @@ public class GermanLocale implements Locale {
this.put("remove-effect-none", "&cSie haben keine Partikel mit dem Effekt &b%effect%&cangelegt!");
this.put("remove-style-success", "&b%amount% &adeiner Partikel im Stil von &b%style% &aentfernt!");
this.put("remove-style-none", "&cSie haben keine Partikel mit dem Stil &b%style%&cangelegt!");
this.put("remove-effect-style-none", "&cSie haben keine Partikel mit der Wirkung oder dem Stil &b%name% &cangewendet!");
this.put("remove-unknown", "&cEs existiert kein Effekt oder Stil mit dem Namen &b%name%&c!");
this.put("#10", "List Messages");
@ -166,6 +167,8 @@ public class GermanLocale implements Locale {
this.put("#17", "Reset Message");
this.put("reset-success", "&b%amount% &aaktive Partikel entfernt!");
this.put("reset-others-success", "&aEntfertigte Partikel für &b%other%&a!");
this.put("reset-others-none", "&eFür &b%other%&e wurden keine Partikel entfernt.");
this.put("#18", "Fixed Create Messages");
this.put("fixed-create-missing-args", "&cFixer Effekt kann nicht erstellt werden, es fehlen &b%amount% &cerforderliche Argumente!");
@ -240,6 +243,7 @@ public class GermanLocale implements Locale {
this.put("gui-color-icon-name", "&a");
this.put("gui-color-info", "&e");
this.put("gui-color-subtext", "&b");
this.put("gui-color-unavailable", "&c");
this.put("#28", "GUI Info Messages");
this.put("gui-commands-info", "Informationen zu Befehlen finden Sie mit Hilfe von &b/pp help");

View file

@ -118,6 +118,7 @@ public class RussianLocale implements Locale {
this.put("remove-effect-none", "&cУ Вас нет каких-либо частиц с эффектом &b%effect%&c!");
this.put("remove-style-success", "&aКоличество удалённых частиц - &b%amount% &a, стилей - &b%style%&a!");
this.put("remove-style-none", "&cУ Вас нет каких-либо частиц со стилем &b%style%&c!");
this.put("remove-effect-style-none", "&cУ вас нет никаких частиц с эффектом или стилем &b%name%&c!");
this.put("remove-unknown", "&cЭффект или стиль под названием &b%name% &cне существует!");
this.put("#10", "List Messages");
@ -166,6 +167,8 @@ public class RussianLocale implements Locale {
this.put("#17", "Reset Message");
this.put("reset-success", "&aУдалено &aактивных частиц - &b%amount%!");
this.put("reset-others-success", "&aУдаленные частицы для &b%other%&a!");
this.put("reset-others-none", "&eНикакие частицы не были удалены для &b%other% &e.");
this.put("#18", "Fixed Create Messages");
this.put("fixed-create-missing-args", "&cНевозможно создать эффект, не введено запрашиваемых аргументов - &b%amount%!");

View file

@ -118,6 +118,7 @@ public class SimplifiedChineseLocale implements Locale {
this.put("remove-effect-none", "&c你没有使用特效&b%effect%&c的粒子!");
this.put("remove-style-success", "&已成功删除&b%amount%&a个使用了风格&b%style%&a的粒子!");
this.put("remove-style-none", "&c你没有已使用风格&b%style%&c的粒子!");
this.put("remove-effect-style-none", "&c您没有应用任何颗粒效果或样式&b%name%&c!");
this.put("remove-unknown", "&c名为&b%name%&c的特效或风格不存在!");
this.put("#10", "List Messages");
@ -166,6 +167,8 @@ public class SimplifiedChineseLocale implements Locale {
this.put("#17", "Reset Message");
this.put("reset-success", "&a已删除&b%amount%个&a激活的粒子特效!");
this.put("reset-others-success", "&a已删除&b%other%&a的颗粒!");
this.put("reset-others-none", "&e没有除去&b%other%&e的颗粒.");
this.put("#18", "Fixed Create Messages");
this.put("fixed-create-missing-args", "&c无法创建定点特效 缺少 &b%amount% &c必要参数!");
@ -243,7 +246,7 @@ public class SimplifiedChineseLocale implements Locale {
this.put("gui-color-unavailable", "&c");
this.put("#28", "GUI Info Messages");
this.put("gui-commands-info", "商人 &b/pp help 查看指令帮助");
this.put("gui-commands-info", "输入 &b/pp help 查看指令帮助");
this.put("gui-back-button", "返回");
this.put("gui-next-page-button", "下一页 (%start%/%end%)");
this.put("gui-previous-page-button", "上一页 (%start%/%end%)");

View file

@ -118,6 +118,7 @@ public class VietnameseLocale implements Locale {
this.put("remove-effect-none", "&cBạn không có bất kì Hạt hiệu ứng nào để áp dụng hiệu ứng &b%effect%&c!");
this.put("remove-style-success", "&aĐã xóa &b%amount% &aHạt hiệu ứng của bạn với style &b%style%&a!");
this.put("remove-style-none", "&cBạn không có bất kì Hạt hiệu ứng nào để áp dụng style &b%style%&c!");
this.put("remove-effect-style-none", "&cBạn không có bất kỳ hạt nào được áp dụng với hiệu ứng hoặc kiểu %name%&c!");
this.put("remove-unknown", "&cEffect hoặc Style với tên &b%name% &ckhông tồn tại!");
this.put("#10", "List Messages");
@ -166,6 +167,8 @@ public class VietnameseLocale implements Locale {
this.put("#17", "Reset Message");
this.put("reset-success", "&aĐã xóa &b%amount% &aHạt hiệu ứng hoạt động!");
this.put("reset-others-success", "&aCác hạt bị loại bỏ cho &b%other%&a!");
this.put("reset-others-none", "&eKhông có hạt nào được loại bỏ cho &b%other%&e.");
this.put("#18", "Fixed Create Messages");
this.put("fixed-create-missing-args", "&cKhông thể tạo Hiệu ứng cố định, bạn đã quên &b%amount% &cđối số yêu cầu!");

View file

@ -26,8 +26,8 @@ import dev.esophose.playerparticles.command.WorldsCommandModule;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import net.md_5.bungee.api.ChatColor;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;

View file

@ -16,11 +16,11 @@ import org.bukkit.Material;
public class ConfigurationManager extends Manager {
private static final String[] HEADER = new String[] {
" _________ __ __________ __ __ __ _________",
" \\______ \\ | _____ ___ __ __________\\______ \\_____ ________/ |_|__| ____ | | ____ ______ \\______ \\",
" | ___/ | \\__ \\< | |/ __ \\_ __ \\ ___/\\__ \\\\_ __ \\ __\\ |/ ___\\| | _/ __ \\ / ___/ / /",
" | | | |__/ __ \\\\___ \\ ___/| | \\/ | / __ \\| | \\/| | | \\ \\___| |_\\ ___/ \\___ \\ / /",
" |____| |____(____ / ____|\\___ >__| |____| (____ /__| |__| |__|\\___ >____/\\___ >____ > /____/",
" _________ __ __________ __ __ __",
" \\______ \\ | _____ ___ __ __________\\______ \\_____ ________/ |_|__| ____ | | ____ ______",
" | ___/ | \\__ \\< | |/ __ \\_ __ \\ ___/\\__ \\\\_ __ \\ __\\ |/ ___\\| | _/ __ \\ / ___/",
" | | | |__/ __ \\\\___ \\ ___/| | \\/ | / __ \\| | \\/| | | \\ \\___| |_\\ ___/ \\___ \\",
" |____| |____(____ / ____|\\___ >__| |____| (____ /__| |__| |__|\\___ >____/\\___ >____ >",
" \\/\\/ \\/ \\/ \\/ \\/ \\/"
};
@ -41,9 +41,9 @@ public class ConfigurationManager extends Manager {
GUI_PRESETS_ONLY("gui-presets-only", false, "If true, only the preset groups will be available in the GUI", "Permissions to open the GUI will change to only open if the user has any preset groups available"),
GUI_CLOSE_AFTER_GROUP_SELECTED("gui-close-after-group-selected", true, "If true, the GUI will close after selecting a group (either saved or preset)"),
GUI_BUTTON_SOUND("gui-button-sound", true, "If clicking a GUI button should make a noise"),
TOGGLE_ON_MOVE("toggle-on-move", false, "If true, styles will not display while the player is moving", "They will instead have the effect displayed at their feet", "Note: Not all styles abide by this rule, but most will"),
TOGGLE_ON_MOVE("toggle-on-move", "NONE", "Valid values: DISPLAY_FEET, DISPLAY_NORMAL, DISPLAY_OVERHEAD, HIDE, NONE", "DISPLAY_FEET will display particles using the feet style while moving", "DISPLAY_NORMAL will display particles using the normal style while moving", "DISPLAY_OVERHEAD will display particles using the overhead style while moving", "HIDE will hide particles while moving", "NONE will make this setting do nothing", "Note: You can change what styles follow this setting in their individual setting files"),
TOGGLE_ON_MOVE_DELAY("toggle-on-move-delay", 9, "The time (in ticks) a player has to be standing still before they are considered to be stopped", "This setting has no effect if toggle-on-move is set to false", "The value must be a positive whole number"),
TOGGLE_ON_COMBAT("toggle-on-combat", false, "If true, particles will be completely disabled while the player is in combat"),
TOGGLE_ON_COMBAT("toggle-on-combat", false, "If true, particles will be completely disabled while the player is in combat", "Note: You can change what styles follow this setting in their individual setting files"),
TOGGLE_ON_COMBAT_DELAY("toggle-on-combat-delay", 15, "The time (in seconds) a player has to not be damaged/attacked to be considered out of combat", "This setting has no effect if toggle-on-combat is set to false", "The value must be a positive whole number"),
DISABLED_WORLDS("disabled-worlds", Collections.singletonList("disabled_world_name"), "A list of worlds that the plugin is disabled in"),
MAX_PARTICLES("max-particles", 3, "The maximum number of particles a player can apply at once", "The GUI will only display up to 21, don't set this any higher than that"),
@ -55,7 +55,17 @@ public class ConfigurationManager extends Manager {
PARTICLE_RENDER_RANGE_FIXED_EFFECT("particle-render-range-fixed-effect", 192, "From how many blocks away should a player be able to see the particles from a fixed effect?"),
RAINBOW_CYCLE_SPEED("rainbow-cycle-speed", 2, "How many out of 360 hue ticks to move per game tick", "Higher values make the rainbow cycle faster", "Note: Must be a positive whole number"),
DUST_SIZE("dust-size", 1.0, "How large should dust particles appear?", "Note: Can include decimals", "Only works in 1.13+"),
GUI_GROUP_CREATION_MESSAGE_DISPLAY_AREA("gui-group-creation-message-display-area", "ACTION_BAR", "Valid values: ACTION_BAR, TITLE, CHAT", "Where should the GUI group creation countdown message be displayed?", "Note: Server versions less than 1.11.2 will always use CHAT"),
OVERRIDE_PARTICLE_VERSION("override-particle-version", -1, "Allows you to override the version of Minecraft that will be assumed for spawning particles", "This should follow this format: 9, 12, 15, etc. 9 means 1.9, 14 means 1.14... and so on"),
WORLDGUARD_SETTINGS("worldguard-settings", null, "Settings for WorldGuard", "If WorldGuard is not installed, these settings will do nothing"),
WORLDGUARD_USE_ALLOWED_REGIONS("worldguard-settings.use-allowed-regions", false, "If true, particles will only be able to spawn if they are in an allowed region and not a disallowed region", "If false, particles will be able to spawn as long as they are not in a disallowed region"),
WORLDGUARD_ALLOWED_REGIONS("worldguard-settings.allowed-regions", Arrays.asList("example_region_1", "example_region_2"), "Regions that particles will be allowed to spawn in"),
WORLDGUARD_DISALLOWED_REGIONS("worldguard-settings.disallowed-regions", Arrays.asList("example_region_3", "example_region_4"), "Regions that particles will be blocked from spawning in", "This overrides allowed regions if they overlap"),
WORLDGUARD_CHECK_INTERVAL("worldguard-settings.check-interval", 10, "How often to check if a player is in a region that allows spawning particles", "Measured in ticks"),
WORLDGUARD_ENABLE_BYPASS_PERMISSION("worldguard-settings.enable-bypass-permission", false, "If true, the permission playerparticles.worldguard.bypass will allow", "the player to bypass the region requirements"),
MYSQL_SETTINGS("mysql-settings", null, "Settings for if you want to use MySQL for data management"),
MYSQL_ENABLED("mysql-settings.enabled", false, "Enable MySQL", "If false, SQLite will be used instead"),
MYSQL_HOSTNAME("mysql-settings.hostname", "", "MySQL Database Hostname"),
@ -63,14 +73,16 @@ public class ConfigurationManager extends Manager {
MYSQL_DATABASE_NAME("mysql-settings.database-name", "", "MySQL Database Name"),
MYSQL_USER_NAME("mysql-settings.user-name", "", "MySQL Database User Name"),
MYSQL_USER_PASSWORD("mysql-settings.user-password", "", "MySQL Database User Password"),
MYSQL_TABLE_PREFIX("mysql-settings.table-prefix", PlayerParticles.getInstance().getDescription().getName().toLowerCase() + "_", "The prefix of the tables in the database", "Do not change this after tables have already been created or you will have data loss"),
MYSQL_USE_SSL("mysql-settings.use-ssl", false, "If the database connection should use SSL", "You should enable this if your database supports SSL"),
MYSQL_CONNECTION_POOL_SIZE("mysql-settings.connection-pool-size", 5, "The size of the connection pool to the database", "Not recommended to go below 2 or above 5"),
GUI_ICON("gui-icon", null,
"This configuration option allows you to change any of the GUI",
"icons to whatever block/item you want.",
"This configuration option allows you to change the GUI",
"icons to whatever block/item you want. If you want to change an effect",
"or style icon, use their respective config files.",
"Notes: If any of the block/item names are invalid the icon in the GUI",
"will be the barrier icon to show that it failed to load.",
"Do NOT change the particle/style name",
"You MUST use the Spigot-given name for it to work. You can see",
"all the Spigot-given names at the link below:",
"https://hub.spigotmc.org/javadocs/spigot/org/bukkit/Material.html",
@ -87,105 +99,7 @@ public class ConfigurationManager extends Manager {
GUI_ICON_MISC_EDIT_EFFECT("gui-icon.misc.edit_effect", Arrays.asList("FIREWORK_ROCKET", "FIREWORK")),
GUI_ICON_MISC_EDIT_STYLE("gui-icon.misc.edit_style", Collections.singletonList("NETHER_STAR")),
GUI_ICON_MISC_EDIT_DATA("gui-icon.misc.edit_data", Collections.singletonList("BOOK")),
GUI_ICON_MISC_RESET("gui-icon.misc.reset", Collections.singletonList("BARRIER")),
GUI_ICON_EFFECT("gui-icon.effect", null),
GUI_ICON_EFFECT_AMBIENT_ENTITY_EFFECT("gui-icon.effect.ambient_entity_effect", Collections.singletonList("BEACON")),
GUI_ICON_EFFECT_ANGRY_VILLAGER("gui-icon.effect.angry_villager", Collections.singletonList("IRON_DOOR")),
GUI_ICON_EFFECT_BARRIER("gui-icon.effect.barrier", Collections.singletonList("BARRIER")),
GUI_ICON_EFFECT_BLOCK("gui-icon.effect.block", Collections.singletonList("STONE")),
GUI_ICON_EFFECT_BUBBLE("gui-icon.effect.bubble", Arrays.asList("BUBBLE_CORAL", "GLASS")),
GUI_ICON_EFFECT_BUBBLE_COLUMN_UP("gui-icon.effect.bubble_column_up", Collections.singletonList("MAGMA_BLOCK")),
GUI_ICON_EFFECT_BUBBLE_POP("gui-icon.effect.bubble_pop", Collections.singletonList("BUBBLE_CORAL_FAN")),
GUI_ICON_EFFECT_CAMPFIRE_COSY_SMOKE("gui-icon.effect.campfire_cosy_smoke", Collections.singletonList("CAMPFIRE")),
GUI_ICON_EFFECT_CAMPFIRE_SIGNAL_SMOKE("gui-icon.effect.campfire_signal_smoke", Collections.singletonList("REDSTONE_TORCH")),
GUI_ICON_EFFECT_CLOUD("gui-icon.effect.cloud", Arrays.asList("WHITE_WOOL", "WOOL")),
GUI_ICON_EFFECT_COMPOSTER("gui-icon.effect.composter", Collections.singletonList("COMPOSTER")),
GUI_ICON_EFFECT_CRIT("gui-icon.effect.crit", Collections.singletonList("IRON_SWORD")),
GUI_ICON_EFFECT_CURRENT_DOWN("gui-icon.effect.current_down", Collections.singletonList("SOUL_SAND")),
GUI_ICON_EFFECT_DAMAGE_INDICATOR("gui-icon.effect.damage_indicator", Collections.singletonList("BOW")),
GUI_ICON_EFFECT_DOLPHIN("gui-icon.effect.dolphin", Collections.singletonList("DOLPHIN_SPAWN_EGG")),
GUI_ICON_EFFECT_DRAGON_BREATH("gui-icon.effect.dragon_breath", Arrays.asList("DRAGON_BREATH", "DRAGONS_BREATH")),
GUI_ICON_EFFECT_DRIPPING_HONEY("gui-icon.effect.dripping_honey", Collections.singletonList("BEE_NEST")),
GUI_ICON_EFFECT_DRIPPING_LAVA("gui-icon.effect.dripping_lava", Collections.singletonList("LAVA_BUCKET")),
GUI_ICON_EFFECT_DRIPPING_WATER("gui-icon.effect.dripping_water", Collections.singletonList("WATER_BUCKET")),
GUI_ICON_EFFECT_DUST("gui-icon.effect.dust", Collections.singletonList("REDSTONE")),
GUI_ICON_EFFECT_ELDER_GUARDIAN("gui-icon.effect.elder_guardian", Arrays.asList("ELDER_GUARDIAN_SPAWN_EGG", "PRISMARINE_CRYSTALS")),
GUI_ICON_EFFECT_ENCHANT("gui-icon.effect.enchant", Arrays.asList("ENCHANTING_TABLE", "ENCHANTMENT_TABLE")),
GUI_ICON_EFFECT_ENCHANTED_HIT("gui-icon.effect.enchanted_hit", Collections.singletonList("DIAMOND_SWORD")),
GUI_ICON_EFFECT_END_ROD("gui-icon.effect.end_rod", Collections.singletonList("END_ROD")),
GUI_ICON_EFFECT_ENTITY_EFFECT("gui-icon.effect.entity_effect", Collections.singletonList("GLOWSTONE_DUST")),
GUI_ICON_EFFECT_EXPLOSION("gui-icon.effect.explosion", Arrays.asList("FIRE_CHARGE", "FIREBALL")),
GUI_ICON_EFFECT_EXPLOSION_EMITTER("gui-icon.effect.explosion_emitter", Collections.singletonList("TNT")),
GUI_ICON_EFFECT_FALLING_DUST("gui-icon.effect.falling_dust", Collections.singletonList("SAND")),
GUI_ICON_EFFECT_FALLING_HONEY("gui-icon.effect.falling_honey", Collections.singletonList("HONEY_BOTTLE")),
GUI_ICON_EFFECT_FALLING_LAVA("gui-icon.effect.falling_lava", Collections.singletonList("RED_DYE")),
GUI_ICON_EFFECT_FALLING_NECTAR("gui-icon.effect.falling_nectar", Collections.singletonList("HONEYCOMB")),
GUI_ICON_EFFECT_FALLING_WATER("gui-icon.effect.falling_water", Collections.singletonList("BLUE_DYE")),
GUI_ICON_EFFECT_FIREWORK("gui-icon.effect.firework", Arrays.asList("FIREWORK_ROCKET", "FIREWORK")),
GUI_ICON_EFFECT_FISHING("gui-icon.effect.fishing", Collections.singletonList("FISHING_ROD")),
GUI_ICON_EFFECT_FLAME("gui-icon.effect.flame", Collections.singletonList("BLAZE_POWDER")),
GUI_ICON_EFFECT_FLASH("gui-icon.effect.flash", Collections.singletonList("GOLD_INGOT")),
GUI_ICON_EFFECT_FOOTSTEP("gui-icon.effect.footstep", Collections.singletonList("GRASS")),
GUI_ICON_EFFECT_HAPPY_VILLAGER("gui-icon.effect.happy_villager", Arrays.asList("DARK_OAK_DOOR_ITEM", "DARK_OAK_DOOR")),
GUI_ICON_EFFECT_HEART("gui-icon.effect.heart", Arrays.asList("POPPY", "RED_ROSE")),
GUI_ICON_EFFECT_INSTANT_EFFECT("gui-icon.effect.instant_effect", Arrays.asList("SPLASH_POTION", "POTION")),
GUI_ICON_EFFECT_ITEM("gui-icon.effect.item", Collections.singletonList("ITEM_FRAME")),
GUI_ICON_EFFECT_ITEM_SLIME("gui-icon.effect.item_slime", Collections.singletonList("SLIME_BALL")),
GUI_ICON_EFFECT_ITEM_SNOWBALL("gui-icon.effect.item_snowball", Arrays.asList("SNOWBALL", "SNOW_BALL")),
GUI_ICON_EFFECT_LARGE_SMOKE("gui-icon.effect.large_smoke", Arrays.asList("COBWEB", "WEB")),
GUI_ICON_EFFECT_LANDING_HONEY("gui-icon.effect.landing_honey", Collections.singletonList("HONEY_BLOCK")),
GUI_ICON_EFFECT_LANDING_LAVA("gui-icon.effect.landing_lava", Collections.singletonList("ORANGE_DYE")),
GUI_ICON_EFFECT_LAVA("gui-icon.effect.lava", Collections.singletonList("MAGMA_CREAM")),
GUI_ICON_EFFECT_MYCELIUM("gui-icon.effect.mycelium", Arrays.asList("MYCELIUM", "MYCEL")),
GUI_ICON_EFFECT_NAUTILUS("gui-icon.effect.nautilus", Collections.singletonList("HEART_OF_THE_SEA")),
GUI_ICON_EFFECT_NOTE("gui-icon.effect.note", Collections.singletonList("NOTE_BLOCK")),
GUI_ICON_EFFECT_POOF("gui-icon.effect.poof", Arrays.asList("FIREWORK_STAR", "FIREWORK_CHARGE")),
GUI_ICON_EFFECT_PORTAL("gui-icon.effect.portal", Collections.singletonList("OBSIDIAN")),
GUI_ICON_EFFECT_RAIN("gui-icon.effect.rain", Arrays.asList("PUFFERFISH_BUCKET", "LAPIS_BLOCK")),
GUI_ICON_EFFECT_SMOKE("gui-icon.effect.smoke", Collections.singletonList("TORCH")),
GUI_ICON_EFFECT_SNEEZE("gui-icon.effect.sneeze", Collections.singletonList("BAMBOO")),
GUI_ICON_EFFECT_SPELL("gui-icon.effect.spell", Arrays.asList("POTION", "GLASS_BOTTLE")),
GUI_ICON_EFFECT_SPIT("gui-icon.effect.spit", Arrays.asList("LLAMA_SPAWN_EGG", "PUMPKIN_SEEDS")),
GUI_ICON_EFFECT_SPLASH("gui-icon.effect.splash", Arrays.asList("SALMON", "FISH", "RAW_FISH")),
GUI_ICON_EFFECT_SQUID_INK("gui-icon.effect.squid_ink", Collections.singletonList("INK_SAC")),
GUI_ICON_EFFECT_SWEEP_ATTACK("gui-icon.effect.sweep_attack", Arrays.asList("GOLDEN_SWORD", "GOLD_SWORD")),
GUI_ICON_EFFECT_TOTEM_OF_UNDYING("gui-icon.effect.totem_of_undying", Arrays.asList("TOTEM_OF_UNDYING", "TOTEM")),
GUI_ICON_EFFECT_UNDERWATER("gui-icon.effect.underwater", Arrays.asList("TURTLE_HELMET", "SPONGE")),
GUI_ICON_EFFECT_WITCH("gui-icon.effect.witch", Collections.singletonList("CAULDRON")),
GUI_ICON_STYLE("gui-icon.style", null),
GUI_ICON_STYLE_ARROWS("gui-icon.style.arrows", Collections.singletonList("BOW")),
GUI_ICON_STYLE_BATMAN("gui-icon.style.batman", Arrays.asList("BAT_SPAWN_EGG", "COAL")),
GUI_ICON_STYLE_BEAM("gui-icon.style.beam", Collections.singletonList("POWERED_RAIL")),
GUI_ICON_STYLE_BLOCKBREAK("gui-icon.style.blockbreak", Collections.singletonList("IRON_PICKAXE")),
GUI_ICON_STYLE_BLOCKPLACE("gui-icon.style.blockplace", Arrays.asList("OAK_PLANKS", "WOOD")),
GUI_ICON_STYLE_CELEBRATION("gui-icon.style.celebration", Arrays.asList("FIREWORK_ROCKET", "FIREWORK")),
GUI_ICON_STYLE_CHAINS("gui-icon.style.chains", Collections.singletonList("TRIPWIRE_HOOK")),
GUI_ICON_STYLE_COMPANION("gui-icon.style.companion", Collections.singletonList("NAME_TAG")),
GUI_ICON_STYLE_CUBE("gui-icon.style.cube", Collections.singletonList("STONE")),
GUI_ICON_STYLE_FEET("gui-icon.style.feet", Collections.singletonList("GRASS")),
GUI_ICON_STYLE_HALO("gui-icon.style.halo", Arrays.asList("END_PORTAL_FRAME", "ENDER_PORTAL_FRAME")),
GUI_ICON_STYLE_HURT("gui-icon.style.hurt", Collections.singletonList("CACTUS")),
GUI_ICON_STYLE_INVOCATION("gui-icon.style.invocation", Arrays.asList("ENDER_EYE", "EYE_OF_ENDER")),
GUI_ICON_STYLE_MOVE("gui-icon.style.move", Arrays.asList("PISTON", "PISTON_BASE")),
GUI_ICON_STYLE_NORMAL("gui-icon.style.normal", Collections.singletonList("DIRT")),
GUI_ICON_STYLE_ORBIT("gui-icon.style.orbit", Arrays.asList("ENCHANTING_TABLE", "ENCHANTMENT_TABLE")),
GUI_ICON_STYLE_OVERHEAD("gui-icon.style.overhead", Collections.singletonList("GLOWSTONE")),
GUI_ICON_STYLE_POINT("gui-icon.style.point", Collections.singletonList("STONE_BUTTON")),
GUI_ICON_STYLE_POPPER("gui-icon.style.popper", Arrays.asList("POPPED_CHORUS_FRUIT", "CHORUS_FRUIT_POPPED")),
GUI_ICON_STYLE_PULSE("gui-icon.style.pulse", Arrays.asList("REDSTONE_TORCH", "REDSTONE_TORCH_ON")),
GUI_ICON_STYLE_QUADHELIX("gui-icon.style.quadhelix", Arrays.asList("NAUTILUS_SHELL", "ACTIVATOR_RAIL")),
GUI_ICON_STYLE_RINGS("gui-icon.style.rings", Arrays.asList("LEAD", "LEASH")),
GUI_ICON_STYLE_SPHERE("gui-icon.style.sphere", Arrays.asList("HEART_OF_THE_SEA", "SNOWBALL", "SNOW_BALL")),
GUI_ICON_STYLE_SPIN("gui-icon.style.spin", Collections.singletonList("BEACON")),
GUI_ICON_STYLE_SPIRAL("gui-icon.style.spiral", Collections.singletonList("HOPPER")),
GUI_ICON_STYLE_SWORDS("gui-icon.style.swords", Collections.singletonList("IRON_SWORD")),
GUI_ICON_STYLE_THICK("gui-icon.style.thick", Arrays.asList("COBWEB", "WEB")),
GUI_ICON_STYLE_TRAIL("gui-icon.style.trail", Collections.singletonList("GHAST_TEAR")),
GUI_ICON_STYLE_TWINS("gui-icon.style.twins", Arrays.asList("OAK_FENCE", "FENCE")),
GUI_ICON_STYLE_VORTEX("gui-icon.style.vortex", Collections.singletonList("GLOWSTONE_DUST")),
GUI_ICON_STYLE_WHIRL("gui-icon.style.whirl", Collections.singletonList("FEATHER")),
GUI_ICON_STYLE_WHIRLWIND("gui-icon.style.whirlwind", Collections.singletonList("STRING")),
GUI_ICON_STYLE_WINGS("gui-icon.style.wings", Collections.singletonList("ELYTRA"));
GUI_ICON_MISC_RESET("gui-icon.misc.reset", Collections.singletonList("BARRIER"));
private final String key;
private final Object defaultValue;
@ -245,7 +159,7 @@ public class ConfigurationManager extends Manager {
*/
public String getString() {
this.loadValue();
return (String) this.value;
return String.valueOf(this.value);
}
private double getNumber() {
@ -386,10 +300,7 @@ public class ConfigurationManager extends Manager {
EDIT_EFFECT,
EDIT_STYLE,
EDIT_DATA,
RESET,
EFFECT,
STYLE;
RESET;
private Map<String, Material> materials;
@ -407,17 +318,6 @@ public class ConfigurationManager extends Manager {
return this.getInternal("gui-icon.misc." + this.name().toLowerCase());
}
/**
* Gets the Material for a subsection of this icon in the config.yml
* Tries to get from cache first, otherwise loads it
*
* @param subsection The name of the icon in the section
* @return The Material for this Icon
*/
public Material get(String subsection) {
return this.getInternal("gui-icon." + this.name().toLowerCase() + "." + subsection);
}
/**
* Gets the Material for this icon
* Tries to get from cache first, otherwise loads it
@ -441,7 +341,7 @@ public class ConfigurationManager extends Manager {
}
if (material == null)
material = Material.BARRIER;
material = ParticleUtils.FALLBACK_MATERIAL;
this.materials.put(configPath, material);

View file

@ -1,23 +1,22 @@
package dev.esophose.playerparticles.manager;
import dev.esophose.playerparticles.PlayerParticles;
import dev.esophose.playerparticles.database.DatabaseConnector;
import dev.esophose.playerparticles.database.MySQLConnector;
import dev.esophose.playerparticles.database.SQLiteConnector;
import dev.esophose.playerparticles.manager.ConfigurationManager.Setting;
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.ParticleGroup;
import dev.esophose.playerparticles.particles.ParticlePair;
import dev.esophose.playerparticles.PlayerParticles;
import dev.esophose.playerparticles.manager.ConfigurationManager.Setting;
import dev.esophose.playerparticles.particles.color.NoteColor;
import dev.esophose.playerparticles.particles.color.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.ParticleUtils;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.Collection;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
@ -27,6 +26,7 @@ import java.util.function.Consumer;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.OfflinePlayer;
import org.bukkit.World;
/**
@ -81,13 +81,10 @@ public class DataManager extends Manager {
* @return The PPlayer from cache
*/
public PPlayer getPPlayer(UUID playerUUID) {
Collection<PPlayer> pplayers;
synchronized (pplayers = this.playerParticles.getManager(ParticleManager.class).getPPlayers()) { // Under rare circumstances, the PPlayers list can be changed while it's looping
for (PPlayer pp : pplayers)
if (pp.getUniqueId().equals(playerUUID))
return pp;
return null;
}
for (PPlayer pp : this.playerParticles.getManager(ParticleManager.class).getPPlayers())
if (pp.getUniqueId().equals(playerUUID))
return pp;
return null;
}
/**
@ -253,12 +250,8 @@ public class DataManager extends Manager {
}
this.sync(() -> {
synchronized (loadedPPlayer) {
if (this.getPPlayer(playerUUID) == null) { // Make sure the PPlayer still isn't added, since this is async it's possible it got ran twice
this.playerParticles.getManager(ParticleManager.class).addPPlayer(loadedPPlayer); // This will be fine now since loadedPPlayer is synchronized
callback.accept(loadedPPlayer);
}
}
this.playerParticles.getManager(ParticleManager.class).addPPlayer(loadedPPlayer);
callback.accept(loadedPPlayer);
});
});
});
@ -312,6 +305,7 @@ public class DataManager extends Manager {
this.async(() -> this.databaseConnector.connect((connection) -> {
String groupUUID;
boolean existingGroup;
String groupUUIDQuery = "SELECT uuid FROM " + this.getTablePrefix() + "group WHERE owner_uuid = ? AND name = ?";
try (PreparedStatement statement = connection.prepareStatement(groupUUIDQuery)) {
@ -321,21 +315,25 @@ public class DataManager extends Manager {
ResultSet result = statement.executeQuery();
if (result.next()) { // Clear out particles from existing group
groupUUID = result.getString("uuid");
String particlesDeleteQuery = "DELETE FROM " + this.getTablePrefix() + "particle WHERE group_uuid = ?";
PreparedStatement particlesDeleteStatement = connection.prepareStatement(particlesDeleteQuery);
particlesDeleteStatement.setString(1, result.getString("uuid"));
particlesDeleteStatement.executeUpdate();
existingGroup = true;
} else { // Create new group
groupUUID = UUID.randomUUID().toString();
existingGroup = false;
}
}
String groupCreateQuery = "INSERT INTO " + this.getTablePrefix() + "group (uuid, owner_uuid, name) VALUES (?, ?, ?)";
PreparedStatement groupCreateStatement = connection.prepareStatement(groupCreateQuery);
if (existingGroup) {
String particlesDeleteQuery = "DELETE FROM " + this.getTablePrefix() + "particle WHERE group_uuid = ?";
try (PreparedStatement particlesDeleteStatement = connection.prepareStatement(particlesDeleteQuery)) {
particlesDeleteStatement.setString(1, groupUUID);
particlesDeleteStatement.executeUpdate();
}
} else {
String groupCreateQuery = "INSERT INTO " + this.getTablePrefix() + "group (uuid, owner_uuid, name) VALUES (?, ?, ?)";
try (PreparedStatement groupCreateStatement = connection.prepareStatement(groupCreateQuery)) {
groupCreateStatement.setString(1, groupUUID);
groupCreateStatement.setString(2, playerUUID.toString());
groupCreateStatement.setString(3, group.getName());
groupCreateStatement.executeUpdate();
}
}
@ -403,6 +401,27 @@ public class DataManager extends Manager {
}));
}
/**
* Attempts to reset the active particle group for the given player name.
* This works even if the player is offline.
*
* @param playerName The name of the player to reset the active particle group for
* @param callback The callback to execute when finished, true for successful, otherwise false
*/
public void resetActiveParticleGroup(String playerName, Consumer<Boolean> callback) {
this.async(() -> this.databaseConnector.connect((connection) -> {
@SuppressWarnings("deprecation")
OfflinePlayer offlinePlayer = Bukkit.getOfflinePlayer(playerName);
String query = "DELETE FROM " + this.getTablePrefix() + "particle WHERE group_uuid IN (SELECT uuid FROM " + this.getTablePrefix() + "group WHERE owner_uuid = ? AND name = ?)";
try (PreparedStatement statement = connection.prepareStatement(query)) {
statement.setString(1, offlinePlayer.getUniqueId().toString());
statement.setString(2, ParticleGroup.DEFAULT_NAME);
callback.accept(statement.executeUpdate() > 0);
}
}));
}
/**
* Saves a fixed effect to save data
* Does not perform a check to see if a fixed effect with this id already exists
@ -554,7 +573,11 @@ public class DataManager extends Manager {
* @return the prefix to be used by all table names
*/
public String getTablePrefix() {
return this.playerParticles.getDescription().getName().toLowerCase() + '_';
if (this.databaseConnector instanceof MySQLConnector) {
return Setting.MYSQL_TABLE_PREFIX.getString();
} else {
return this.playerParticles.getDescription().getName().toLowerCase() + '_';
}
}
}

View file

@ -7,6 +7,7 @@ import dev.esophose.playerparticles.gui.GuiInventoryDefault;
import dev.esophose.playerparticles.gui.GuiInventoryLoadPresetGroups;
import dev.esophose.playerparticles.manager.ConfigurationManager.Setting;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
@ -25,7 +26,7 @@ public class GuiManager extends Manager implements Listener, Runnable {
public GuiManager(PlayerParticles playerParticles) {
super(playerParticles);
this.guiInventories = new ArrayList<>();
this.guiInventories = Collections.synchronizedList(new ArrayList<>());
this.guiTask = null;
Bukkit.getPluginManager().registerEvents(this, this.playerParticles);
@ -108,15 +109,17 @@ public class GuiManager extends Manager implements Listener, Runnable {
* @param pplayer The PPlayer to open the GUI screen for
*/
public void openDefault(PPlayer pplayer) {
GuiInventory inventoryToOpen;
if (!Setting.GUI_PRESETS_ONLY.getBoolean()) {
inventoryToOpen = new GuiInventoryDefault(pplayer);
} else {
inventoryToOpen = new GuiInventoryLoadPresetGroups(pplayer, true);
}
Bukkit.getScheduler().runTask(this.playerParticles, () -> {
GuiInventory inventoryToOpen;
if (!Setting.GUI_PRESETS_ONLY.getBoolean()) {
inventoryToOpen = new GuiInventoryDefault(pplayer);
} else {
inventoryToOpen = new GuiInventoryLoadPresetGroups(pplayer, true);
}
this.guiInventories.add(inventoryToOpen);
Bukkit.getScheduler().runTask(this.playerParticles, () -> pplayer.getPlayer().openInventory(inventoryToOpen.getInventory()));
pplayer.getPlayer().openInventory(inventoryToOpen.getInventory());
this.guiInventories.add(inventoryToOpen);
});
}
/**
@ -125,8 +128,10 @@ public class GuiManager extends Manager implements Listener, Runnable {
* @param nextInventory The GuiInventory to transition to
*/
public void transition(GuiInventory nextInventory) {
this.guiInventories.add(nextInventory);
Bukkit.getScheduler().runTask(this.playerParticles, () -> nextInventory.getPPlayer().getPlayer().openInventory(nextInventory.getInventory()));
Bukkit.getScheduler().runTask(this.playerParticles, () -> {
nextInventory.getPPlayer().getPlayer().openInventory(nextInventory.getInventory());
this.guiInventories.add(nextInventory);
});
}
/**
@ -141,14 +146,5 @@ public class GuiManager extends Manager implements Listener, Runnable {
.findFirst()
.orElse(null);
}
/**
* Removes a GuiInventory from guiInventories by a PPlayer
*
* @param pplayer The PPlayer who owns the GuiInventory
*/
private void removeGuiInventory(PPlayer pplayer) {
this.guiInventories.removeIf(x -> x.getPPlayer().getUniqueId().equals(pplayer.getUniqueId()));
}
}

View file

@ -124,6 +124,9 @@ public class LocaleManager extends Manager {
* @param stringPlaceholders The placeholders to apply
*/
public void sendMessage(CommandSender sender, String messageKey, StringPlaceholders stringPlaceholders) {
if (!Setting.MESSAGES_ENABLED.getBoolean())
return;
sender.sendMessage(this.getLocaleMessage("prefix") + this.getLocaleMessage(messageKey, stringPlaceholders));
}
@ -135,6 +138,9 @@ public class LocaleManager extends Manager {
* @param stringPlaceholders The placeholders to apply
*/
public void sendMessage(PPlayer pplayer, String messageKey, StringPlaceholders stringPlaceholders) {
if (!Setting.MESSAGES_ENABLED.getBoolean())
return;
pplayer.getUnderlyingExecutor().sendMessage(this.parsePlaceholders(pplayer.getPlayer(), this.getLocaleMessage("prefix") + this.getLocaleMessage(messageKey, stringPlaceholders)));
}
@ -145,6 +151,9 @@ public class LocaleManager extends Manager {
* @param messageKey The message key of the Locale to send
*/
public void sendMessage(CommandSender sender, String messageKey) {
if (!Setting.MESSAGES_ENABLED.getBoolean())
return;
this.sendMessage(sender, messageKey, StringPlaceholders.empty());
}
@ -155,6 +164,9 @@ public class LocaleManager extends Manager {
* @param messageKey The message key of the Locale to send
*/
public void sendMessage(PPlayer pplayer, String messageKey) {
if (!Setting.MESSAGES_ENABLED.getBoolean())
return;
this.sendMessage(pplayer, messageKey, StringPlaceholders.empty());
}
@ -166,6 +178,9 @@ public class LocaleManager extends Manager {
* @param stringPlaceholders The placeholders to apply
*/
public void sendSimpleMessage(CommandSender sender, String messageKey, StringPlaceholders stringPlaceholders) {
if (!Setting.MESSAGES_ENABLED.getBoolean())
return;
sender.sendMessage(this.getLocaleMessage(messageKey, stringPlaceholders));
}
@ -177,6 +192,9 @@ public class LocaleManager extends Manager {
* @param stringPlaceholders The placeholders to apply
*/
public void sendSimpleMessage(PPlayer pplayer, String messageKey, StringPlaceholders stringPlaceholders) {
if (!Setting.MESSAGES_ENABLED.getBoolean())
return;
pplayer.getUnderlyingExecutor().sendMessage(this.parsePlaceholders(pplayer.getPlayer(), this.getLocaleMessage(messageKey, stringPlaceholders)));
}
@ -187,6 +205,9 @@ public class LocaleManager extends Manager {
* @param messageKey The message key of the Locale to send
*/
public void sendSimpleMessage(CommandSender sender, String messageKey) {
if (!Setting.MESSAGES_ENABLED.getBoolean())
return;
this.sendMessage(sender, messageKey, StringPlaceholders.empty());
}
@ -197,6 +218,9 @@ public class LocaleManager extends Manager {
* @param messageKey The message key of the Locale to send
*/
public void sendSimpleMessage(PPlayer pplayer, String messageKey) {
if (!Setting.MESSAGES_ENABLED.getBoolean())
return;
this.sendMessage(pplayer, messageKey, StringPlaceholders.empty());
}
@ -207,6 +231,9 @@ public class LocaleManager extends Manager {
* @param message The message to send
*/
public void sendCustomMessage(CommandSender sender, String message) {
if (!Setting.MESSAGES_ENABLED.getBoolean())
return;
sender.sendMessage(message);
}
@ -217,6 +244,9 @@ public class LocaleManager extends Manager {
* @param message The message to send
*/
public void sendCustomMessage(PPlayer pplayer, String message) {
if (!Setting.MESSAGES_ENABLED.getBoolean())
return;
this.sendCustomMessage(pplayer.getUnderlyingExecutor(), this.parsePlaceholders(pplayer.getPlayer(), message));
}

View file

@ -7,8 +7,8 @@ import dev.esophose.playerparticles.particles.ParticleGroupPreset;
import dev.esophose.playerparticles.particles.ParticlePair;
import dev.esophose.playerparticles.PlayerParticles;
import dev.esophose.playerparticles.particles.ParticleProperty;
import dev.esophose.playerparticles.particles.color.NoteColor;
import dev.esophose.playerparticles.particles.color.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.InputParser;
import java.io.File;

View file

@ -1,16 +1,17 @@
package dev.esophose.playerparticles.manager;
import dev.esophose.playerparticles.PlayerParticles;
import dev.esophose.playerparticles.hook.WorldGuardHook;
import dev.esophose.playerparticles.manager.ConfigurationManager.Setting;
import dev.esophose.playerparticles.nms.wrapper.ParticleHandler;
import dev.esophose.playerparticles.particles.ConsolePPlayer;
import dev.esophose.playerparticles.particles.FixedParticleEffect;
import dev.esophose.playerparticles.particles.ParticleEffectSettings;
import dev.esophose.playerparticles.particles.color.NoteColor;
import dev.esophose.playerparticles.particles.color.OrdinaryColor;
import dev.esophose.playerparticles.particles.data.NoteColor;
import dev.esophose.playerparticles.particles.data.OrdinaryColor;
import dev.esophose.playerparticles.particles.PParticle;
import dev.esophose.playerparticles.particles.PPlayer;
import dev.esophose.playerparticles.particles.color.ParticleColor;
import dev.esophose.playerparticles.particles.data.ParticleColor;
import dev.esophose.playerparticles.particles.ParticleEffect;
import dev.esophose.playerparticles.particles.ParticlePair;
import dev.esophose.playerparticles.particles.ParticleProperty;
@ -58,6 +59,11 @@ public class ParticleManager extends Manager implements Listener, Runnable {
*/
private BukkitTask particleTask;
/**
* The task that checks player worldguard region statuses
*/
private BukkitTask worldGuardTask;
private ParticleHandler particleHandler;
private Map<ParticleEffect, ParticleEffectSettings> supportedParticleEffects;
@ -85,6 +91,11 @@ public class ParticleManager extends Manager implements Listener, Runnable {
if (this.particleTask != null)
this.particleTask.cancel();
if (this.worldGuardTask != null) {
this.worldGuardTask.cancel();
this.worldGuardTask = null;
}
int overrideVersion = Setting.OVERRIDE_PARTICLE_VERSION.getInt();
VersionMapping versionMapping = VersionMapping.getVersionMapping(overrideVersion != -1 ? overrideVersion : NMSUtil.getVersionNumber());
this.particleHandler = NMSUtil.getHandler(versionMapping);
@ -95,8 +106,13 @@ public class ParticleManager extends Manager implements Listener, Runnable {
Bukkit.getScheduler().runTaskLater(this.playerParticles, () -> {
long ticks = Setting.TICKS_PER_PARTICLE.getLong();
this.particleTask = Bukkit.getScheduler().runTaskTimerAsynchronously(this.playerParticles, this, 5, ticks);
}, 1);
this.particleTask = Bukkit.getScheduler().runTaskTimerAsynchronously(this.playerParticles, this, 0, ticks);
if (WorldGuardHook.enabled()) {
long worldGuardTicks = Setting.WORLDGUARD_CHECK_INTERVAL.getLong();
this.worldGuardTask = Bukkit.getScheduler().runTaskTimer(this.playerParticles, this::updateWorldGuardStatuses, 0, worldGuardTicks);
}
}, 5);
this.particlePlayers.clear();
DataManager dataManager = this.playerParticles.getManager(DataManager.class);
@ -179,7 +195,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));
@ -191,6 +207,25 @@ public class ParticleManager extends Manager implements Listener, Runnable {
}
}
/**
* Updates the WorldGuard region statuses for players
*/
private void updateWorldGuardStatuses() {
PermissionManager permissionManager = this.playerParticles.getManager(PermissionManager.class);
for (PPlayer pplayer : this.particlePlayers.values()) {
Player player = pplayer.getPlayer();
if (player == null)
continue;
boolean inAllowedRegion = WorldGuardHook.isInAllowedRegion(player.getLocation());
if (!inAllowedRegion && Setting.WORLDGUARD_ENABLE_BYPASS_PERMISSION.getBoolean())
inAllowedRegion = permissionManager.hasWorldGuardBypass(player);
pplayer.setInAllowedRegion(inAllowedRegion);
}
}
/**
* Displays particles at the given player location with their settings
*
@ -200,17 +235,36 @@ public class ParticleManager extends Manager implements Listener, Runnable {
*/
private void displayParticles(PPlayer pplayer, ParticlePair particle, Location location) {
if (!this.playerParticles.getManager(ParticleStyleManager.class).isEventHandled(particle.getStyle())) {
if (Setting.TOGGLE_ON_COMBAT.getBoolean() && pplayer.isInCombat())
if (Setting.TOGGLE_ON_COMBAT.getBoolean() && particle.getStyle().canToggleWithCombat() && pplayer.isInCombat())
return;
List<PParticle> particles;
if (Setting.TOGGLE_ON_MOVE.getBoolean() && particle.getStyle().canToggleWithMovement() && pplayer.isMoving()) {
particles = DefaultStyles.FEET.getParticles(particle, location);
} else {
particles = particle.getStyle().getParticles(particle, location);
if (!pplayer.isInAllowedRegion())
return;
if (particle.getStyle().canToggleWithMovement() && pplayer.isMoving()) {
switch (Setting.TOGGLE_ON_MOVE.getString().toUpperCase()) {
case "DISPLAY_FEET":
case "TRUE": // Old default value, keep here for legacy config compatibility
for (PParticle pparticle : DefaultStyles.FEET.getParticles(particle, location))
this.displayParticles(particle, pparticle, particle.getStyle().hasLongRangeVisibility(), pplayer.getPlayer());
return;
case "DISPLAY_NORMAL":
for (PParticle pparticle : DefaultStyles.NORMAL.getParticles(particle, location))
this.displayParticles(particle, pparticle, particle.getStyle().hasLongRangeVisibility(), pplayer.getPlayer());
return;
case "DISPLAY_OVERHEAD":
for (PParticle pparticle : DefaultStyles.OVERHEAD.getParticles(particle, location))
this.displayParticles(particle, pparticle, particle.getStyle().hasLongRangeVisibility(), pplayer.getPlayer());
return;
case "NONE":
case "FALSE": // Old default value, keep here for legacy config compatibility
break;
default:
return;
}
}
for (PParticle pparticle : particles)
for (PParticle pparticle : particle.getStyle().getParticles(particle, location))
this.displayParticles(particle, pparticle, particle.getStyle().hasLongRangeVisibility(), pplayer.getPlayer());
}
}
@ -218,19 +272,27 @@ public class ParticleManager extends Manager implements Listener, Runnable {
/**
* An alternative method used for event styles
*
* @param source The player the particles are spawning from, nullable for special cases
* @param pplayer The PPlayer the particles are spawning from, nullable for special cases
* @param world The world the particles are spawning in
* @param particle The ParticlePair to use for getting particle settings
* @param particles The particles to display
* @param isLongRange If the particle can be viewed from long range
*/
public void displayParticles(Player source, World world, ParticlePair particle, List<PParticle> particles, boolean isLongRange) {
public void displayParticles(PPlayer pplayer, World world, ParticlePair particle, List<PParticle> particles, boolean isLongRange) {
PermissionManager permissionManager = this.playerParticles.getManager(PermissionManager.class);
if ((source != null && source.getGameMode() == GameMode.SPECTATOR) || !permissionManager.isWorldEnabled(world.getName()))
Player player = null;
if (pplayer != null) {
if (!pplayer.isInAllowedRegion())
return;
player = pplayer.getPlayer();
}
if ((player != null && (NMSUtil.getVersionNumber() < 8 || player.getGameMode() == GameMode.SPECTATOR)) || !permissionManager.isWorldEnabled(world.getName()))
return;
for (PParticle pparticle : particles)
this.displayParticles(particle, pparticle, isLongRange, source);
this.displayParticles(particle, pparticle, isLongRange, player);
}
/**

View file

@ -1,11 +1,15 @@
package dev.esophose.playerparticles.manager;
import dev.esophose.playerparticles.PlayerParticles;
import dev.esophose.playerparticles.event.ParticleStyleRegistrationEvent;
import dev.esophose.playerparticles.styles.DefaultStyles;
import dev.esophose.playerparticles.styles.ParticleStyle;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;
import org.bukkit.Bukkit;
public class ParticleStyleManager extends Manager {
@ -20,12 +24,49 @@ public class ParticleStyleManager extends Manager {
this.styles = new ArrayList<>();
this.eventStyles = new ArrayList<>();
DefaultStyles.registerStyles(this);
DefaultStyles.initStyles();
}
@Override
public void reload() {
// Styles List is never reset so you don't need to re-register styles each time the plugin reloads
this.styles.clear();
this.eventStyles.clear();
// Call registration event
// We use this event internally, so no other action needs to be done for us to register the default styles
ParticleStyleRegistrationEvent event = new ParticleStyleRegistrationEvent();
Bukkit.getPluginManager().callEvent(event);
Collection<ParticleStyle> eventStyles = event.getRegisteredEventStyles().values();
List<ParticleStyle> styles = new ArrayList<>(event.getRegisteredStyles().values());
styles.addAll(eventStyles);
styles.sort(Comparator.comparing(ParticleStyle::getName));
for (ParticleStyle style : styles) {
try {
if (style == null)
throw new IllegalArgumentException("Tried to register a null style");
if (style.getInternalName() == null || style.getInternalName().trim().isEmpty())
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);
if (eventStyles.contains(style))
this.eventStyles.add(style);
} catch (IllegalArgumentException ex) {
ex.printStackTrace();
}
}
}
@Override
@ -33,41 +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
*/
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
*/
public void registerEventStyle(ParticleStyle style) {
this.registerStyle(style);
this.eventStyles.add(style);
}
/**
* Returns if a given style is customly handled
*

View file

@ -18,6 +18,7 @@ import org.bukkit.command.ConsoleCommandSender;
import org.bukkit.entity.Player;
import org.bukkit.permissions.Permissible;
import org.bukkit.permissions.Permission;
import org.bukkit.permissions.PermissionAttachmentInfo;
import org.bukkit.plugin.PluginManager;
public class PermissionManager extends Manager {
@ -29,17 +30,24 @@ public class PermissionManager extends Manager {
STYLE("style"),
FIXED("fixed"),
FIXED_MAX("fixed.max"),
FIXED_UNLIMITED("fixed.unlimited"),
FIXED_CLEAR("fixed.clear"),
FIXED_TELEPORT("fixed.teleport"),
RELOAD("reload"),
OVERRIDE("override"),
RESET_OTHERS("reset.others"),
GUI("gui"),
PARTICLES_MAX("particles.max"),
PARTICLES_UNLIMITED("particles.unlimited"),
GROUPS_UNLIMITED("groups.unlimited");
GROUPS_MAX("groups.max"),
GROUPS_UNLIMITED("groups.unlimited"),
WORLDGUARD_BYPASS("worldguard.bypass");;
private final String permissionString;
@ -69,6 +77,11 @@ public class PermissionManager extends Manager {
String permission = PERMISSION_PREFIX + this.permissionString + '.' + subPermission;
return p.hasPermission(permission);
}
@Override
public String toString() {
return PERMISSION_PREFIX + this.permissionString;
}
}
public PermissionManager(PlayerParticles playerParticles) {
@ -87,6 +100,9 @@ public class PermissionManager extends Manager {
// Effects
Map<String, Boolean> effectPermissions = new HashMap<>();
for (ParticleEffect effect : particleManager.getEnabledEffects()) {
if (particleManager.getEffectSettings(effect) == null)
continue;
Permission permission = new Permission("playerparticles.effect." + particleManager.getEffectSettings(effect).getInternalName());
pluginManager.addPermission(permission);
effectPermissions.put(permission.getName(), true);
@ -109,6 +125,7 @@ public class PermissionManager extends Manager {
// Fixed
pluginManager.addPermission(new Permission("playerparticles.fixed"));
pluginManager.addPermission(new Permission("playerparticles.fixed.max"));
pluginManager.addPermission(new Permission("playerparticles.fixed.unlimited"));
pluginManager.addPermission(new Permission("playerparticles.fixed.clear"));
pluginManager.addPermission(new Permission("playerparticles.fixed.teleport"));
@ -116,10 +133,18 @@ public class PermissionManager extends Manager {
// Misc
pluginManager.addPermission(new Permission("playerparticles.reload"));
pluginManager.addPermission(new Permission("playerparticles.override"));
pluginManager.addPermission(new Permission("playerparticles.reset.others"));
pluginManager.addPermission(new Permission("playerparticles.gui"));
pluginManager.addPermission(new Permission("playerparticles.particles.max"));
pluginManager.addPermission(new Permission("playerparticles.particles.unlimited"));
pluginManager.addPermission(new Permission("playerparticles.groups.max"));
pluginManager.addPermission(new Permission("playerparticles.groups.unlimited"));
// WorldGuard
pluginManager.addPermission(new Permission("playerparticles.worldguard.bypass"));
// Register all non-child permissions
Map<String, Boolean> childPermissions = new HashMap<>();
for (Permission permission : allPermissions) {
@ -155,7 +180,7 @@ public class PermissionManager extends Manager {
if (executor != pplayer)
return false;
return pplayer.getActiveParticles().size() >= Setting.MAX_PARTICLES.getInt();
return pplayer.getActiveParticles().size() >= this.getPermissionAmount(pplayer.getUnderlyingExecutor(), PPermission.PARTICLES_MAX, Setting.MAX_PARTICLES.getInt());
}
/**
@ -172,7 +197,7 @@ public class PermissionManager extends Manager {
if (executor != pplayer)
return false;
return executor.getParticleGroups().size() - 1 >= Setting.MAX_GROUPS.getInt();
return executor.getParticleGroups().size() - 1 >= this.getPermissionAmount(pplayer.getUnderlyingExecutor(), PPermission.GROUPS_MAX, Setting.MAX_GROUPS.getInt());
}
/**
@ -185,7 +210,7 @@ public class PermissionManager extends Manager {
if (PPermission.GROUPS_UNLIMITED.check(pplayer.getUnderlyingExecutor()))
return true;
return Setting.MAX_GROUPS.getInt() != 0;
return this.getPermissionAmount(pplayer.getUnderlyingExecutor(), PPermission.GROUPS_MAX, Setting.MAX_GROUPS.getInt()) != 0;
}
/**
@ -202,7 +227,7 @@ public class PermissionManager extends Manager {
if (executor != pplayer)
return false;
return pplayer.getFixedEffectIds().size() >= Setting.MAX_FIXED_EFFECTS.getInt();
return pplayer.getFixedEffectIds().size() >= this.getPermissionAmount(pplayer.getUnderlyingExecutor(), PPermission.FIXED_MAX, Setting.MAX_FIXED_EFFECTS.getInt());
}
/**
@ -228,7 +253,7 @@ public class PermissionManager extends Manager {
if (executor != pplayer)
return Integer.MAX_VALUE;
return Setting.MAX_PARTICLES.getInt();
return this.getPermissionAmount(pplayer.getUnderlyingExecutor(), PPermission.PARTICLES_MAX, Setting.MAX_PARTICLES.getInt());
}
/**
@ -250,6 +275,16 @@ public class PermissionManager extends Manager {
return Setting.DISABLED_WORLDS.getStringList();
}
/**
* Checks if a player can reset another offline player's particles
*
* @param player The player to check the permission for
* @return True if the player has permission, otherwise false
*/
public boolean canResetOthers(PPlayer player) {
return PPermission.RESET_OTHERS.check(player.getUnderlyingExecutor());
}
/**
* Checks if a player has permission to use an effect
*
@ -397,10 +432,10 @@ public class PermissionManager extends Manager {
}
/**
* Checks if a player can use /ppo
* Checks if a CommandSender can use /ppo
*
* @param sender The CommandSender to check
* @return If the player can use /ppo
* @return If the sender can use /ppo
*/
public boolean canOverride(CommandSender sender) {
if (this.isConsole(sender))
@ -409,6 +444,16 @@ public class PermissionManager extends Manager {
return PPermission.OVERRIDE.check(sender);
}
/**
* Checks if a player has the WorldGuard bypass permission
*
* @param player The Player to check
* @return If the player has the WorldGuard bypass permission
*/
public boolean hasWorldGuardBypass(Player player) {
return PPermission.WORLDGUARD_BYPASS.check(player);
}
private boolean isConsole(PPlayer pplayer) {
return this.isConsole(pplayer.getUnderlyingExecutor());
}
@ -429,4 +474,17 @@ public class PermissionManager extends Manager {
return pplayer;
}
private int getPermissionAmount(Permissible permissible, PPermission permission, int lowerBound) {
int amount = lowerBound;
for (PermissionAttachmentInfo info : permissible.getEffectivePermissions()) {
String target = info.getPermission().toLowerCase();
if (target.startsWith(permission.toString()) && info.getValue()) {
try {
amount = Math.max(amount, Integer.parseInt(target.substring(target.lastIndexOf('.') + 1)));
} catch (NumberFormatException ignored) { }
}
}
return amount;
}
}

View file

@ -48,6 +48,11 @@ public class PPlayer {
*/
private boolean inCombat;
/**
* If the player is in an allowed region
*/
private boolean inAllowedRegion;
/**
* Constructs a new PPlayer
*
@ -64,6 +69,7 @@ public class PPlayer {
this.particlesHidden = particlesHidden;
this.isMoving = false;
this.inCombat = false;
this.inAllowedRegion = true;
}
/**
@ -103,7 +109,7 @@ public class PPlayer {
/**
* Gets if the Player can see particles spawned by the plugin or not
*
* @return True if the player can see particles, otherwise false
* @return true if the player can see particles, otherwise false
*/
public boolean canSeeParticles() {
return !this.particlesHidden;
@ -112,7 +118,7 @@ public class PPlayer {
/**
* Sets if the player can see particles spawned by the plugin or not
*
* @param hidden True if the player can see particles, otherwise false
* @param hidden true if the player can see particles, otherwise false
*/
public void setParticlesHidden(boolean hidden) {
this.particlesHidden = hidden;
@ -130,7 +136,7 @@ public class PPlayer {
/**
* Sets the player's movement state
*
* @param isMoving True if the player is moving, otherwise false if they are standing still
* @param isMoving true if the player is moving, otherwise false if they are standing still
*/
public void setMoving(boolean isMoving) {
this.isMoving = isMoving;
@ -139,7 +145,7 @@ public class PPlayer {
/**
* Gets if a player is moving
*
* @return True if the player is moving
* @return true if the player is moving
*/
public boolean isMoving() {
return this.isMoving;
@ -148,7 +154,7 @@ public class PPlayer {
/**
* Sets the player's combat state
*
* @param inCombat True if the player is in combat, otherwise false
* @param inCombat true if the player is in combat, otherwise false
*/
public void setInCombat(boolean inCombat) {
this.inCombat = inCombat;
@ -157,12 +163,28 @@ public class PPlayer {
/**
* Gets if a player is in combat
*
* @return True if the player is in combat
* @return true if the player is in combat
*/
public boolean isInCombat() {
return this.inCombat;
}
/**
* Sets the player's region state
*
* @param inAllowedRegion true if the player is in an allowed region, otherwise false
*/
public void setInAllowedRegion(boolean inAllowedRegion) {
this.inAllowedRegion = inAllowedRegion;
}
/**
* @return true if the player is in an allowed region, otherwise false
*/
public boolean isInAllowedRegion() {
return this.inAllowedRegion;
}
/**
* Gets a ParticleGroup this player has by its name
*

View file

@ -7,6 +7,7 @@ import dev.esophose.playerparticles.util.ParticleUtils;
import java.io.File;
import java.util.Arrays;
import java.util.List;
import org.bukkit.Material;
public class ParticleEffectSettings {
@ -19,6 +20,7 @@ public class ParticleEffectSettings {
private String effectName;
private boolean enabled;
private Material guiIconMaterial;
public ParticleEffectSettings(ParticleEffect particleEffect) {
this.particleEffect = particleEffect;
@ -40,6 +42,7 @@ public class ParticleEffectSettings {
boolean changed = this.setIfNotExists("effect-name", this.getInternalName(), "The name the effect will display as");
changed |= this.setIfNotExists("enabled", this.enabledByDefault, "If the effect is enabled or not");
changed |= this.setIfNotExists("gui-icon-material", this.particleEffect.getGuiIconMaterialNames());
if (changed)
this.config.save();
@ -56,6 +59,7 @@ public class ParticleEffectSettings {
this.effectName = this.config.getString("effect-name");
this.enabled = this.config.getBoolean("enabled");
this.guiIconMaterial = ParticleUtils.closestMatchWithFallback(true, this.config.getStringList("gui-icon-material").toArray(new String[0]));
}
/**
@ -95,6 +99,13 @@ public class ParticleEffectSettings {
return this.effectName;
}
/**
* @return The Material icon that represents this style in the GUI
*/
public Material getGuiIconMaterial() {
return this.guiIconMaterial;
}
/**
* @return true if this effect is enabled, otherwise false
*/

View file

@ -3,9 +3,9 @@ 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.color.NoteColor;
import dev.esophose.playerparticles.particles.color.OrdinaryColor;
import dev.esophose.playerparticles.particles.color.ParticleColor;
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

@ -1,4 +1,4 @@
package dev.esophose.playerparticles.particles.color;
package dev.esophose.playerparticles.particles.data;
import dev.esophose.playerparticles.particles.ParticleEffect;
import java.util.Objects;

View file

@ -1,4 +1,4 @@
package dev.esophose.playerparticles.particles.color;
package dev.esophose.playerparticles.particles.data;
import dev.esophose.playerparticles.particles.ParticleEffect;
import java.util.Objects;

View file

@ -1,4 +1,4 @@
package dev.esophose.playerparticles.particles.color;
package dev.esophose.playerparticles.particles.data;
import dev.esophose.playerparticles.particles.ParticleEffect;

View file

@ -10,6 +10,7 @@ import java.util.List;
import java.util.Map;
import java.util.UUID;
import org.bukkit.Bukkit;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Player;
import org.bukkit.entity.Projectile;
import org.bukkit.event.EventHandler;
@ -55,10 +56,7 @@ public class PPlayerCombatListener implements Listener {
*/
@EventHandler(priority = EventPriority.MONITOR)
public void onPlayerAttack(EntityDamageByEntityEvent event) {
if (!Setting.TOGGLE_ON_COMBAT.getBoolean())
return;
if (!(event.getEntity() instanceof Player))
if (event.getEntity().getType() != EntityType.PLAYER)
return;
Player attacker;

View file

@ -11,22 +11,37 @@ import java.util.Map;
import java.util.UUID;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.entity.Player;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerMoveEvent;
import org.bukkit.util.Vector;
public class PPlayerMovementListener implements Listener {
private static final int CHECK_INTERVAL = 3;
private Map<UUID, Integer> timeSinceLastMovement = new HashMap<>();
private Map<UUID, Integer> timeSinceLastMovement;
private Map<UUID, Vector> previousVectors;
public PPlayerMovementListener() {
DataManager dataManager = PlayerParticles.getInstance().getManager(DataManager.class);
this.timeSinceLastMovement = new HashMap<>();
this.previousVectors = new HashMap<>();
Bukkit.getScheduler().runTaskTimer(PlayerParticles.getInstance(), () -> {
if (!Setting.TOGGLE_ON_MOVE.getBoolean())
return;
for (Player player : Bukkit.getOnlinePlayers()) {
UUID playerUUID = player.getUniqueId();
Vector previousVector = this.previousVectors.get(playerUUID);
Location currentLocation = player.getLocation();
Vector currentVector = new Vector(currentLocation.getBlockX(), currentLocation.getBlockY(), currentLocation.getBlockZ());
this.previousVectors.put(playerUUID, currentVector);
if (previousVector == null || !previousVector.equals(currentVector)) {
if (!this.timeSinceLastMovement.containsKey(playerUUID)) {
this.timeSinceLastMovement.put(playerUUID, 0);
} else {
this.timeSinceLastMovement.replace(playerUUID, 0);
}
}
}
List<UUID> toRemove = new ArrayList<>();
@ -47,27 +62,4 @@ public class PPlayerMovementListener implements Listener {
}, 0, CHECK_INTERVAL);
}
/**
* Used to detect if the player is moving
*
* @param event The event
*/
@EventHandler(priority = EventPriority.MONITOR)
public void onPlayerMove(PlayerMoveEvent event) {
if (!Setting.TOGGLE_ON_MOVE.getBoolean())
return;
Location to = event.getTo();
Location from = event.getFrom();
if (to == null || (to.getBlockX() == from.getBlockX() && to.getBlockY() == from.getBlockY() && to.getBlockZ() == from.getBlockZ()))
return;
UUID playerUUID = event.getPlayer().getUniqueId();
if (!this.timeSinceLastMovement.containsKey(playerUUID)) {
this.timeSinceLastMovement.put(playerUUID, 0);
} else {
this.timeSinceLastMovement.replace(playerUUID, 0);
}
}
}

View file

@ -5,6 +5,8 @@ import dev.esophose.playerparticles.PlayerParticles;
import dev.esophose.playerparticles.config.CommentedFileConfiguration;
import dev.esophose.playerparticles.util.ParticleUtils;
import java.io.File;
import java.util.List;
import org.bukkit.Material;
public abstract class DefaultParticleStyle implements ParticleStyle {
@ -15,18 +17,22 @@ public abstract class DefaultParticleStyle implements ParticleStyle {
private String internalStyleName;
private boolean canBeFixedByDefault;
private boolean canToggleWithMovementByDefault;
private boolean canToggleWithCombatByDefault;
private double fixedEffectOffsetByDefault;
private String styleName;
private boolean enabled;
private boolean canBeFixed;
private boolean canToggleWithMovement;
private boolean canToggleWithCombat;
private double fixedEffectOffset;
private Material guiIconMaterial;
public DefaultParticleStyle(String internalStyleName, boolean canBeFixedByDefault, boolean canToggleWithMovementByDefault, double fixedEffectOffsetByDefault) {
this.internalStyleName = internalStyleName;
this.canBeFixedByDefault = canBeFixedByDefault;
this.canToggleWithMovementByDefault = canToggleWithMovementByDefault;
this.canToggleWithCombatByDefault = true;
this.fixedEffectOffsetByDefault = fixedEffectOffsetByDefault;
this.playerParticles = PlayerParticles.getInstance();
@ -49,7 +55,9 @@ public abstract class DefaultParticleStyle implements ParticleStyle {
this.setIfNotExists("enabled", true, "If the style is enabled or not");
this.setIfNotExists("can-be-fixed", this.canBeFixedByDefault, "If the style can be used in /pp fixed");
this.setIfNotExists("can-toggle-with-movement", this.canToggleWithMovementByDefault, "If the style will only be shown at the player's feet while moving");
this.setIfNotExists("can-toggle-with-combat", this.canToggleWithCombatByDefault, "If particles for this style will be hidden if the player is in combat");
this.setIfNotExists("fixed-effect-offset", this.fixedEffectOffsetByDefault, "How far vertically to offset the style position for fixed effects");
this.setIfNotExists("gui-icon-material", this.getGuiIconMaterialNames(), "The material of the icon to display in the GUI");
this.setDefaultSettings(this.config);
@ -70,7 +78,9 @@ public abstract class DefaultParticleStyle implements ParticleStyle {
this.enabled = this.config.getBoolean("enabled");
this.canBeFixed = this.config.getBoolean("can-be-fixed");
this.canToggleWithMovement = this.config.getBoolean("can-toggle-with-movement");
this.canToggleWithCombat = this.config.getBoolean("can-toggle-with-combat");
this.fixedEffectOffset = this.config.getDouble("fixed-effect-offset");
this.guiIconMaterial = ParticleUtils.closestMatchWithFallback(true, this.config.getStringList("gui-icon-material").toArray(new String[0]));
this.loadSettings(this.config);
}
@ -102,14 +112,19 @@ public abstract class DefaultParticleStyle implements ParticleStyle {
return this.enabled;
}
@Override
public final String getInternalName() {
return this.internalStyleName;
}
@Override
public final String getName() {
return this.styleName;
}
@Override
public final String getInternalName() {
return this.internalStyleName;
public final Material getGuiIconMaterial() {
return this.guiIconMaterial;
}
@Override
@ -122,11 +137,21 @@ public abstract class DefaultParticleStyle implements ParticleStyle {
return this.canToggleWithMovement;
}
@Override
public final boolean canToggleWithCombat() {
return this.canToggleWithCombat;
}
@Override
public final double getFixedEffectOffset() {
return this.fixedEffectOffset;
}
/**
* @return A list of Strings to try to turn into Materials
*/
protected abstract List<String> getGuiIconMaterialNames();
/**
* Sets the default settings for this style
*

View file

@ -1,12 +1,15 @@
package dev.esophose.playerparticles.styles;
import dev.esophose.playerparticles.event.ParticleStyleRegistrationEvent;
import dev.esophose.playerparticles.manager.ParticleStyleManager;
import dev.esophose.playerparticles.PlayerParticles;
import org.bukkit.Bukkit;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.plugin.PluginManager;
public class DefaultStyles {
public class DefaultStyles implements Listener {
/**
* All the styles that are available by default from this plugin
@ -21,6 +24,7 @@ public class DefaultStyles {
public static final ParticleStyle COMPANION = new ParticleStyleCompanion();
public static final ParticleStyle CUBE = new ParticleStyleCube();
public static final ParticleStyle FEET = new ParticleStyleFeet();
public static final ParticleStyle FISHING = new ParticleStyleFishing();
public static final ParticleStyle HALO = new ParticleStyleHalo();
public static final ParticleStyle HURT = new ParticleStyleHurt();
public static final ParticleStyle INVOCATION = new ParticleStyleInvocation();
@ -37,6 +41,7 @@ public class DefaultStyles {
public static final ParticleStyle SPIN = new ParticleStyleSpin();
public static final ParticleStyle SPIRAL = new ParticleStyleSpiral();
public static final ParticleStyle SWORDS = new ParticleStyleSwords();
public static final ParticleStyle TELEPORT = new ParticleStyleTeleport();
public static final ParticleStyle THICK = new ParticleStyleThick();
public static final ParticleStyle TRAIL = new ParticleStyleTrail();
public static final ParticleStyle TWINS = new ParticleStyleTwins();
@ -46,58 +51,65 @@ public class DefaultStyles {
public static final ParticleStyle WINGS = new ParticleStyleWings();
/**
* Registers all the default styles to the ParticleStyleManager
* Registered in alphabetical order
*
* @param particleStyleManager The ParticleStyleManager instance
* Initializes all the default styles
*/
public static void registerStyles(ParticleStyleManager particleStyleManager) {
particleStyleManager.registerStyle(ARROWS);
particleStyleManager.registerStyle(BATMAN);
particleStyleManager.registerStyle(BEAM);
particleStyleManager.registerEventStyle(BLOCKBREAK);
particleStyleManager.registerEventStyle(BLOCKPLACE);
particleStyleManager.registerStyle(CELEBRATION);
particleStyleManager.registerStyle(CHAINS);
particleStyleManager.registerStyle(COMPANION);
particleStyleManager.registerStyle(CUBE);
particleStyleManager.registerStyle(FEET);
particleStyleManager.registerStyle(HALO);
particleStyleManager.registerEventStyle(HURT);
particleStyleManager.registerStyle(INVOCATION);
particleStyleManager.registerEventStyle(MOVE);
particleStyleManager.registerStyle(NORMAL);
particleStyleManager.registerStyle(ORBIT);
particleStyleManager.registerStyle(OVERHEAD);
particleStyleManager.registerStyle(POINT);
particleStyleManager.registerStyle(POPPER);
particleStyleManager.registerStyle(PULSE);
particleStyleManager.registerStyle(QUADHELIX);
particleStyleManager.registerStyle(RINGS);
particleStyleManager.registerStyle(SPHERE);
particleStyleManager.registerStyle(SPIN);
particleStyleManager.registerStyle(SPIRAL);
particleStyleManager.registerEventStyle(SWORDS);
particleStyleManager.registerStyle(THICK);
particleStyleManager.registerEventStyle(TRAIL);
particleStyleManager.registerStyle(TWINS);
particleStyleManager.registerStyle(VORTEX);
particleStyleManager.registerStyle(WHIRL);
particleStyleManager.registerStyle(WHIRLWIND);
particleStyleManager.registerStyle(WINGS);
public static void initStyles() {
// Register event
Bukkit.getPluginManager().registerEvents(new DefaultStyles(), PlayerParticles.getInstance());
// Register their events
// Register style events
PluginManager pluginManager = Bukkit.getPluginManager();
PlayerParticles playerParticles = PlayerParticles.getInstance();
pluginManager.registerEvents((Listener) ARROWS, playerParticles);
pluginManager.registerEvents((Listener) BLOCKBREAK, playerParticles);
pluginManager.registerEvents((Listener) BLOCKPLACE, playerParticles);
pluginManager.registerEvents((Listener) FISHING, playerParticles);
pluginManager.registerEvents((Listener) HURT, playerParticles);
pluginManager.registerEvents((Listener) MOVE, playerParticles);
pluginManager.registerEvents((Listener) SWORDS, playerParticles);
pluginManager.registerEvents((Listener) TELEPORT, playerParticles);
pluginManager.registerEvents((Listener) TRAIL, playerParticles);
}
@EventHandler(priority = EventPriority.LOWEST)
public void onParticleStyleRegistration(ParticleStyleRegistrationEvent event) {
event.registerStyle(ARROWS);
event.registerStyle(BATMAN);
event.registerStyle(BEAM);
event.registerEventStyle(BLOCKBREAK);
event.registerEventStyle(BLOCKPLACE);
event.registerStyle(CELEBRATION);
event.registerStyle(CHAINS);
event.registerStyle(COMPANION);
event.registerStyle(CUBE);
event.registerStyle(FEET);
event.registerStyle(FISHING);
event.registerStyle(HALO);
event.registerEventStyle(HURT);
event.registerStyle(INVOCATION);
event.registerEventStyle(MOVE);
event.registerStyle(NORMAL);
event.registerStyle(ORBIT);
event.registerStyle(OVERHEAD);
event.registerStyle(POINT);
event.registerStyle(POPPER);
event.registerStyle(PULSE);
event.registerStyle(QUADHELIX);
event.registerStyle(RINGS);
event.registerStyle(SPHERE);
event.registerStyle(SPIN);
event.registerStyle(SPIRAL);
event.registerEventStyle(SWORDS);
event.registerEventStyle(TELEPORT);
event.registerStyle(THICK);
event.registerEventStyle(TRAIL);
event.registerStyle(TWINS);
event.registerStyle(VORTEX);
event.registerStyle(WHIRL);
event.registerStyle(WHIRLWIND);
event.registerStyle(WINGS);
}
/**
* Reloads the settings for all default styles
*

View file

@ -4,8 +4,10 @@ import dev.esophose.playerparticles.manager.ParticleStyleManager;
import dev.esophose.playerparticles.particles.PParticle;
import dev.esophose.playerparticles.particles.ParticlePair;
import dev.esophose.playerparticles.PlayerParticles;
import dev.esophose.playerparticles.util.ParticleUtils;
import java.util.List;
import org.bukkit.Location;
import org.bukkit.Material;
public interface ParticleStyle {
@ -42,6 +44,13 @@ public interface ParticleStyle {
return this.getInternalName();
}
/**
* @return The Material icon that represents this style in the GUI
*/
default Material getGuiIconMaterial() {
return ParticleUtils.FALLBACK_MATERIAL;
}
/**
* Gets if the style can be used in a FixedParticleEffect
*
@ -50,13 +59,22 @@ public interface ParticleStyle {
boolean canBeFixed();
/**
* Gets if the style can be replaced with DefaultStyles.FEET when the player is moving
* Gets if the style can be displayed differently based on the toggle-on-move setting when the player is moving
*
* @return True if it can be, otherwise False
*/
default boolean canToggleWithMovement() {
return true;
}
/**
* Gets if the style can be hidden if the player is in combat with the toggle-on-combat setting
*
* @return True if it can be, otherwise False
*/
default boolean canToggleWithCombat() {
return true;
}
/**
* The Y-axis offset to be applied when using '/pp fixed create looking'

View file

@ -1,11 +1,14 @@
package dev.esophose.playerparticles.styles;
import dev.esophose.playerparticles.PlayerParticles;
import dev.esophose.playerparticles.particles.PParticle;
import dev.esophose.playerparticles.particles.ParticlePair;
import dev.esophose.playerparticles.config.CommentedFileConfiguration;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import org.bukkit.entity.Projectile;
@ -15,7 +18,7 @@ import org.bukkit.event.entity.ProjectileLaunchEvent;
public class ParticleStyleArrows extends DefaultParticleStyle implements Listener {
private List<Projectile> projectiles = new ArrayList<>();
private List<Projectile> projectiles;
private int maxArrowsPerPlayer;
private boolean onlySpawnIfFlying;
@ -24,6 +27,17 @@ public class ParticleStyleArrows extends DefaultParticleStyle implements Listene
public ParticleStyleArrows() {
super("arrows", false, false, 0);
this.projectiles = new ArrayList<>();
// Removes all arrows that are considered dead
Bukkit.getScheduler().runTaskTimer(PlayerParticles.getInstance(), () -> {
for (int i = this.projectiles.size() - 1; i >= 0; i--) {
Projectile projectile = this.projectiles.get(i);
if ((this.arrowTrackingTime != -1 && projectile.getTicksLived() >= this.arrowTrackingTime) || !projectile.isValid() || projectile.getShooter() == null)
this.projectiles.remove(i);
}
}, 0L, 5L);
}
@Override
@ -31,8 +45,9 @@ public class ParticleStyleArrows extends DefaultParticleStyle implements Listene
List<PParticle> particles = new ArrayList<>();
int count = 0;
for (int i = this.projectiles.size() - 1; i >= 0; i--) { // Loop backwards so the last-fired projectiles are the ones that have particles if they go over the max
Projectile projectile = this.projectiles.get(i);
List<Projectile> listCopy = new ArrayList<>(this.projectiles); // Copy in case of modification while looping due to async
for (int i = listCopy.size() - 1; i >= 0; i--) { // Loop backwards so the last-fired projectiles are the ones that have particles if they go over the max
Projectile projectile = listCopy.get(i);
if (this.onlySpawnIfFlying && projectile.isOnGround())
continue;
@ -48,16 +63,9 @@ public class ParticleStyleArrows extends DefaultParticleStyle implements Listene
return particles;
}
/**
* Removes all arrows that are considered dead
*/
@Override
public void updateTimers() {
for (int i = this.projectiles.size() - 1; i >= 0; i--) {
Projectile projectile = this.projectiles.get(i);
if ((this.arrowTrackingTime != -1 && projectile.getTicksLived() >= this.arrowTrackingTime) || projectile.isDead() || !projectile.isValid() || projectile.getShooter() == null)
this.projectiles.remove(i);
}
}
@Override
@ -65,6 +73,11 @@ public class ParticleStyleArrows extends DefaultParticleStyle implements Listene
return true;
}
@Override
protected List<String> getGuiIconMaterialNames() {
return Collections.singletonList("BOW");
}
/**
* The event used to get all projectiles fired by players
* Adds all projectiles fired from players to the array

View file

@ -5,6 +5,7 @@ import dev.esophose.playerparticles.particles.ParticlePair;
import dev.esophose.playerparticles.config.CommentedFileConfiguration;
import dev.esophose.playerparticles.util.VectorUtils;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.bukkit.Location;
import org.bukkit.util.Vector;
@ -120,6 +121,11 @@ public class ParticleStyleBatman extends DefaultParticleStyle {
this.step = (this.step + 1) % this.spawnDelay; // Only spawn once per second
}
@Override
protected List<String> getGuiIconMaterialNames() {
return Arrays.asList("BAT_SPAWN_EGG", "COAL");
}
@Override
protected void setDefaultSettings(CommentedFileConfiguration config) {
this.setIfNotExists("spawn-delay", 20, "The number of ticks to wait between particle spawns");

View file

@ -5,6 +5,7 @@ import dev.esophose.playerparticles.particles.ParticlePair;
import dev.esophose.playerparticles.config.CommentedFileConfiguration;
import dev.esophose.playerparticles.util.MathL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.bukkit.Location;
@ -46,6 +47,11 @@ public class ParticleStyleBeam extends DefaultParticleStyle {
}
}
@Override
protected List<String> getGuiIconMaterialNames() {
return Collections.singletonList("POWERED_RAIL");
}
@Override
protected void setDefaultSettings(CommentedFileConfiguration config) {
this.setIfNotExists("points", 16, "The number of points in the circle");

View file

@ -8,6 +8,7 @@ import dev.esophose.playerparticles.particles.ParticlePair;
import dev.esophose.playerparticles.PlayerParticles;
import dev.esophose.playerparticles.config.CommentedFileConfiguration;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.bukkit.Location;
import org.bukkit.entity.Player;
@ -43,6 +44,11 @@ public class ParticleStyleBlockBreak extends DefaultParticleStyle implements Lis
}
@Override
protected List<String> getGuiIconMaterialNames() {
return Collections.singletonList("IRON_PICKAXE");
}
@Override
protected void setDefaultSettings(CommentedFileConfiguration config) {
this.setIfNotExists("particle-amount", 10, "The number of particles to spawn");
@ -63,11 +69,12 @@ public class ParticleStyleBlockBreak extends DefaultParticleStyle implements Lis
Player player = event.getPlayer();
PPlayer pplayer = PlayerParticles.getInstance().getManager(DataManager.class).getPPlayer(player.getUniqueId());
if (pplayer != null) {
for (ParticlePair particle : pplayer.getActiveParticlesForStyle(DefaultStyles.BLOCKBREAK)) {
Location loc = event.getBlock().getLocation().clone();
particleManager.displayParticles(player, player.getWorld(), particle, DefaultStyles.BLOCKBREAK.getParticles(particle, loc), false);
}
if (pplayer == null)
return;
for (ParticlePair particle : pplayer.getActiveParticlesForStyle(DefaultStyles.BLOCKBREAK)) {
Location loc = event.getBlock().getLocation().clone();
particleManager.displayParticles(pplayer, player.getWorld(), particle, DefaultStyles.BLOCKBREAK.getParticles(particle, loc), false);
}
}

View file

@ -8,6 +8,7 @@ import dev.esophose.playerparticles.particles.ParticlePair;
import dev.esophose.playerparticles.PlayerParticles;
import dev.esophose.playerparticles.config.CommentedFileConfiguration;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.bukkit.Location;
import org.bukkit.entity.Player;
@ -43,6 +44,11 @@ public class ParticleStyleBlockPlace extends DefaultParticleStyle implements Lis
}
@Override
protected List<String> getGuiIconMaterialNames() {
return Arrays.asList("OAK_PLANKS", "WOOD");
}
@Override
protected void setDefaultSettings(CommentedFileConfiguration config) {
this.setIfNotExists("particle-amount", 10, "The number of particles to spawn");
@ -63,11 +69,12 @@ public class ParticleStyleBlockPlace extends DefaultParticleStyle implements Lis
Player player = event.getPlayer();
PPlayer pplayer = PlayerParticles.getInstance().getManager(DataManager.class).getPPlayer(player.getUniqueId());
if (pplayer != null) {
for (ParticlePair particle : pplayer.getActiveParticlesForStyle(DefaultStyles.BLOCKPLACE)) {
Location loc = event.getBlock().getLocation().clone();
particleManager.displayParticles(player, player.getWorld(), particle, DefaultStyles.BLOCKPLACE.getParticles(particle, loc), false);
}
if (pplayer == null)
return;
for (ParticlePair particle : pplayer.getActiveParticlesForStyle(DefaultStyles.BLOCKPLACE)) {
Location loc = event.getBlock().getLocation().clone();
particleManager.displayParticles(pplayer, player.getWorld(), particle, DefaultStyles.BLOCKPLACE.getParticles(particle, loc), false);
}
}

View file

@ -10,7 +10,9 @@ import dev.esophose.playerparticles.particles.ParticlePair;
import dev.esophose.playerparticles.PlayerParticles;
import dev.esophose.playerparticles.config.CommentedFileConfiguration;
import dev.esophose.playerparticles.util.MathL;
import dev.esophose.playerparticles.util.NMSUtil;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Random;
@ -59,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);
@ -71,6 +73,11 @@ public class ParticleStyleCelebration extends DefaultParticleStyle {
}
}
@Override
protected List<String> getGuiIconMaterialNames() {
return Arrays.asList("FIREWORK_ROCKET", "FIREWORK");
}
@Override
protected void setDefaultSettings(CommentedFileConfiguration config) {
this.setIfNotExists("spawn-frequency", 15, "How many ticks to wait between spawns");
@ -122,7 +129,7 @@ public class ParticleStyleCelebration extends DefaultParticleStyle {
trail.setEffect(ParticleStyleCelebration.this.fuseEffect);
trail.setStyle(DefaultStyles.CELEBRATION);
particleManager.displayParticles(player, this.location.getWorld(), trail, Collections.singletonList(new PParticle(this.location)), true);
particleManager.displayParticles(pplayer, this.location.getWorld(), trail, Collections.singletonList(new PParticle(this.location)), true);
this.location.add(0, ParticleStyleCelebration.this.fuseSpacing, 0);
} else {
@ -139,7 +146,7 @@ public class ParticleStyleCelebration extends DefaultParticleStyle {
particles.add(new PParticle(this.location.clone().add(dx, dy, dz)));
}
particleManager.displayParticles(player, this.location.getWorld(), particle, particles, true);
particleManager.displayParticles(pplayer, this.location.getWorld(), particle, particles, true);
this.cancel();
}

View file

@ -4,6 +4,7 @@ import dev.esophose.playerparticles.particles.PParticle;
import dev.esophose.playerparticles.particles.ParticlePair;
import dev.esophose.playerparticles.config.CommentedFileConfiguration;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.bukkit.Location;
@ -34,6 +35,11 @@ public class ParticleStyleChains extends DefaultParticleStyle {
}
@Override
protected List<String> getGuiIconMaterialNames() {
return Collections.singletonList("TRIPWIRE_HOOK");
}
@Override
protected void setDefaultSettings(CommentedFileConfiguration config) {
this.setIfNotExists("chain-particle-amount", 8, "The number of particles per chain");

View file

@ -28,6 +28,7 @@ import dev.esophose.playerparticles.particles.ParticlePair;
import dev.esophose.playerparticles.config.CommentedFileConfiguration;
import dev.esophose.playerparticles.util.MathL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.bukkit.Location;
import org.bukkit.util.Vector;
@ -71,6 +72,11 @@ public class ParticleStyleCompanion extends DefaultParticleStyle {
this.step++;
}
@Override
protected List<String> getGuiIconMaterialNames() {
return Collections.singletonList("NAME_TAG");
}
@Override
protected void setDefaultSettings(CommentedFileConfiguration config) {
this.setIfNotExists("particle-amount", 150, "The number of total particles in the animation cycle");

View file

@ -28,6 +28,7 @@ import dev.esophose.playerparticles.particles.ParticlePair;
import dev.esophose.playerparticles.config.CommentedFileConfiguration;
import dev.esophose.playerparticles.util.VectorUtils;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.bukkit.Location;
import org.bukkit.util.Vector;
@ -96,6 +97,11 @@ public class ParticleStyleCube extends DefaultParticleStyle {
this.step++;
}
@Override
protected List<String> getGuiIconMaterialNames() {
return Collections.singletonList("STONE");
}
@Override
protected void setDefaultSettings(CommentedFileConfiguration config) {
this.setIfNotExists("edge-length", 2.0, "The length (in blocks) of the edges of the cube");

View file

@ -4,6 +4,7 @@ import dev.esophose.playerparticles.particles.PParticle;
import dev.esophose.playerparticles.particles.ParticlePair;
import dev.esophose.playerparticles.config.CommentedFileConfiguration;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.bukkit.Location;
@ -31,6 +32,11 @@ public class ParticleStyleFeet extends DefaultParticleStyle {
}
@Override
protected List<String> getGuiIconMaterialNames() {
return Collections.singletonList("GRASS");
}
@Override
protected void setDefaultSettings(CommentedFileConfiguration config) {
this.setIfNotExists("feet-offset", -0.95, "How far to offset the player location vertically");

View file

@ -0,0 +1,103 @@
package dev.esophose.playerparticles.styles;
import dev.esophose.playerparticles.PlayerParticles;
import dev.esophose.playerparticles.config.CommentedFileConfiguration;
import dev.esophose.playerparticles.particles.PParticle;
import dev.esophose.playerparticles.particles.ParticlePair;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import org.bukkit.entity.Projectile;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerFishEvent;
import org.bukkit.projectiles.ProjectileSource;
public class ParticleStyleFishing extends DefaultParticleStyle implements Listener {
// I hate legacy versions. The Spigot API changed the PlayerFishEvent#getHook method from returning a Fish to a FishHook in 1.13
private static Method PlayerFishEvent_getHook;
static {
try {
PlayerFishEvent_getHook = PlayerFishEvent.class.getDeclaredMethod("getHook");
} catch (ReflectiveOperationException ex) {
ex.printStackTrace();
}
}
private Set<Projectile> projectiles;
public ParticleStyleFishing() {
super("fishing", false, false, 0);
this.projectiles = new HashSet<>();
// Removes all fish hooks that are considered dead
Bukkit.getScheduler().runTaskTimer(PlayerParticles.getInstance(), () -> this.projectiles.removeIf(x -> !x.isValid()), 0L, 5L);
}
@Override
public List<PParticle> getParticles(ParticlePair particle, Location location) {
List<PParticle> particles = new ArrayList<>();
List<Projectile> listCopy = new ArrayList<>(this.projectiles); // Copy in case of modification while looping due to async
for (Projectile projectile : listCopy) {
ProjectileSource shooter = projectile.getShooter();
if (shooter instanceof Player && ((Player) shooter).getUniqueId().equals(particle.getOwnerUniqueId()))
particles.add(new PParticle(projectile.getLocation(), 0.05F, 0.05F, 0.05F, 0.0F));
}
return particles;
}
@Override
public void updateTimers() {
}
@Override
protected List<String> getGuiIconMaterialNames() {
return Collections.singletonList("FISHING_ROD");
}
@Override
public boolean hasLongRangeVisibility() {
return false;
}
@EventHandler
public void onPlayerFish(PlayerFishEvent event) {
// Done through a string switch for 1.9.4 compatibility
switch (event.getState().toString()) {
case "FISHING":
try {
this.projectiles.add((Projectile) PlayerFishEvent_getHook.invoke(event));
} catch (ReflectiveOperationException ignored) { }
break;
case "CAUGHT_FISH":
case "CAUGHT_ENTITY":
case "REEL_IN":
try {
this.projectiles.remove((Projectile) PlayerFishEvent_getHook.invoke(event));
} catch (ReflectiveOperationException ignored) { }
break;
}
}
@Override
protected void setDefaultSettings(CommentedFileConfiguration config) {
}
@Override
protected void loadSettings(CommentedFileConfiguration config) {
}
}

View file

@ -5,6 +5,7 @@ import dev.esophose.playerparticles.particles.ParticlePair;
import dev.esophose.playerparticles.config.CommentedFileConfiguration;
import dev.esophose.playerparticles.util.MathL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.bukkit.Location;
@ -43,6 +44,11 @@ public class ParticleStyleHalo extends DefaultParticleStyle {
this.skipNextSpawn = !this.skipNextSpawn;
}
@Override
protected List<String> getGuiIconMaterialNames() {
return Arrays.asList("END_PORTAL_FRAME", "ENDER_PORTAL_FRAME");
}
@Override
protected void setDefaultSettings(CommentedFileConfiguration config) {
this.setIfNotExists("particle-amount", 16, "The number of points in the halo");

View file

@ -8,6 +8,7 @@ import dev.esophose.playerparticles.particles.ParticlePair;
import dev.esophose.playerparticles.PlayerParticles;
import dev.esophose.playerparticles.config.CommentedFileConfiguration;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.bukkit.Location;
import org.bukkit.entity.Player;
@ -49,12 +50,17 @@ public class ParticleStyleHurt extends DefaultParticleStyle implements Listener
if (pplayer != null) {
for (ParticlePair particle : pplayer.getActiveParticlesForStyle(DefaultStyles.HURT)) {
Location loc = player.getLocation().clone().add(0, 1, 0);
particleManager.displayParticles(player, player.getWorld(), particle, DefaultStyles.HURT.getParticles(particle, loc), false);
particleManager.displayParticles(pplayer, player.getWorld(), particle, DefaultStyles.HURT.getParticles(particle, loc), false);
}
}
}
}
@Override
protected List<String> getGuiIconMaterialNames() {
return Collections.singletonList("CACTUS");
}
@Override
protected void setDefaultSettings(CommentedFileConfiguration config) {
this.setIfNotExists("thick-multiplier", 3, "How much to multiply the particles by", "This style uses the same spawning as the 'thick' style");

View file

@ -6,6 +6,7 @@ import dev.esophose.playerparticles.particles.ParticlePair;
import dev.esophose.playerparticles.config.CommentedFileConfiguration;
import dev.esophose.playerparticles.util.MathL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.bukkit.Location;
@ -97,6 +98,11 @@ public class ParticleStyleInvocation extends DefaultParticleStyle {
this.circleStep = (this.circleStep + 1) % this.numSteps;
}
@Override
protected List<String> getGuiIconMaterialNames() {
return Arrays.asList("ENDER_EYE", "EYE_OF_ENDER");
}
@Override
protected void setDefaultSettings(CommentedFileConfiguration config) {
this.setIfNotExists("spinning-points", 6, "The number of points that spin around the circle in each direction");

View file

@ -8,6 +8,7 @@ import dev.esophose.playerparticles.particles.ParticlePair;
import dev.esophose.playerparticles.PlayerParticles;
import dev.esophose.playerparticles.config.CommentedFileConfiguration;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.bukkit.Location;
import org.bukkit.entity.Player;
@ -39,6 +40,11 @@ public class ParticleStyleMove extends DefaultParticleStyle implements Listener
}
@Override
protected List<String> getGuiIconMaterialNames() {
return Arrays.asList("PISTON", "PISTON_BASE");
}
@Override
protected void setDefaultSettings(CommentedFileConfiguration config) {
this.setIfNotExists("multiplier", 1, "The multiplier for the number of particles to spawn", "This style uses the same spawning as the 'normal' style");
@ -55,12 +61,13 @@ public class ParticleStyleMove extends DefaultParticleStyle implements Listener
Player player = event.getPlayer();
PPlayer pplayer = PlayerParticles.getInstance().getManager(DataManager.class).getPPlayer(player.getUniqueId());
if (pplayer != null) {
for (ParticlePair particle : pplayer.getActiveParticlesForStyle(DefaultStyles.MOVE)) {
Location loc = player.getLocation().clone();
loc.setY(loc.getY() + 0.05);
particleManager.displayParticles(player, player.getWorld(), particle, DefaultStyles.MOVE.getParticles(particle, loc), false);
}
if (pplayer == null)
return;
for (ParticlePair particle : pplayer.getActiveParticlesForStyle(DefaultStyles.MOVE)) {
Location loc = player.getLocation().clone();
loc.setY(loc.getY() + 0.05);
particleManager.displayParticles(pplayer, player.getWorld(), particle, DefaultStyles.MOVE.getParticles(particle, loc), false);
}
}

View file

@ -4,6 +4,7 @@ import dev.esophose.playerparticles.config.CommentedFileConfiguration;
import dev.esophose.playerparticles.particles.PParticle;
import dev.esophose.playerparticles.particles.ParticlePair;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.bukkit.Location;
@ -59,6 +60,11 @@ public class ParticleStyleNormal extends DefaultParticleStyle {
}
@Override
protected List<String> getGuiIconMaterialNames() {
return Collections.singletonList("DIRT");
}
@Override
protected void setDefaultSettings(CommentedFileConfiguration config) {

View file

@ -5,6 +5,7 @@ import dev.esophose.playerparticles.particles.ParticlePair;
import dev.esophose.playerparticles.config.CommentedFileConfiguration;
import dev.esophose.playerparticles.util.MathL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.bukkit.Location;
@ -36,6 +37,11 @@ public class ParticleStyleOrbit extends DefaultParticleStyle {
this.step = (this.step + 1) % this.numSteps;
}
@Override
protected List<String> getGuiIconMaterialNames() {
return Arrays.asList("ENCHANTING_TABLE", "ENCHANTMENT_TABLE");
}
@Override
protected void setDefaultSettings(CommentedFileConfiguration config) {
this.setIfNotExists("orbs", 3, "The number of orbs that orbit the player");

View file

@ -4,6 +4,7 @@ import dev.esophose.playerparticles.particles.PParticle;
import dev.esophose.playerparticles.particles.ParticlePair;
import dev.esophose.playerparticles.config.CommentedFileConfiguration;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.bukkit.Location;
@ -33,6 +34,11 @@ public class ParticleStyleOverhead extends DefaultParticleStyle {
}
@Override
protected List<String> getGuiIconMaterialNames() {
return Collections.singletonList("GLOWSTONE");
}
@Override
protected void setDefaultSettings(CommentedFileConfiguration config) {
this.setIfNotExists("head-offset", 1.75, "How far to offset the player location vertically");

View file

@ -25,6 +25,11 @@ public class ParticleStylePoint extends DefaultParticleStyle {
}
@Override
protected List<String> getGuiIconMaterialNames() {
return Collections.singletonList("STONE_BUTTON");
}
@Override
protected void setDefaultSettings(CommentedFileConfiguration config) {
this.setIfNotExists("player-offset", 1.5, "How far to offset the player location vertically");

View file

@ -5,6 +5,7 @@ import dev.esophose.playerparticles.particles.ParticlePair;
import dev.esophose.playerparticles.config.CommentedFileConfiguration;
import dev.esophose.playerparticles.util.MathL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.bukkit.Location;
import org.bukkit.util.Vector;
@ -51,6 +52,11 @@ public class ParticleStylePopper extends DefaultParticleStyle {
this.step = (this.step + 1) % this.maxStep;
}
@Override
protected List<String> getGuiIconMaterialNames() {
return Arrays.asList("POPPED_CHORUS_FRUIT", "CHORUS_FRUIT_POPPED", "PUMPKIN_SEEDS");
}
@Override
protected void setDefaultSettings(CommentedFileConfiguration config) {
this.setIfNotExists("radius", 1.0, "The radius at the bottom of the vortex");

View file

@ -6,6 +6,7 @@ import dev.esophose.playerparticles.particles.ParticlePair;
import dev.esophose.playerparticles.config.CommentedFileConfiguration;
import dev.esophose.playerparticles.util.MathL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.bukkit.Location;
@ -77,6 +78,11 @@ public class ParticleStylePulse extends DefaultParticleStyle {
this.step = (this.step + 1) % this.numSteps;
}
@Override
protected List<String> getGuiIconMaterialNames() {
return Arrays.asList("REDSTONE_TORCH", "REDSTONE_TORCH_ON");
}
@Override
protected void setDefaultSettings(CommentedFileConfiguration config) {
this.setIfNotExists("points", 50, "The number of points to spawn in the pulse circle");

View file

@ -5,6 +5,7 @@ import dev.esophose.playerparticles.particles.ParticlePair;
import dev.esophose.playerparticles.config.CommentedFileConfiguration;
import dev.esophose.playerparticles.util.MathL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.bukkit.Location;
@ -51,6 +52,11 @@ public class ParticleStyleQuadhelix extends DefaultParticleStyle {
}
}
@Override
protected List<String> getGuiIconMaterialNames() {
return Arrays.asList("NAUTILUS_SHELL", "ACTIVATOR_RAIL");
}
@Override
protected void setDefaultSettings(CommentedFileConfiguration config) {
this.setIfNotExists("orbs", 4, "The number of orbs to spawn");

View file

@ -5,6 +5,7 @@ import dev.esophose.playerparticles.particles.ParticlePair;
import dev.esophose.playerparticles.config.CommentedFileConfiguration;
import dev.esophose.playerparticles.util.MathL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.bukkit.Location;
@ -38,6 +39,11 @@ public class ParticleStyleRings extends DefaultParticleStyle {
this.step = (this.step + 1) % this.maxStep;
}
@Override
protected List<String> getGuiIconMaterialNames() {
return Arrays.asList("LEAD", "LEASH");
}
@Override
protected void setDefaultSettings(CommentedFileConfiguration config) {
this.setIfNotExists("particles-per-ring", 32, "The number of particles that will spawn for each ring");

View file

@ -5,6 +5,7 @@ import dev.esophose.playerparticles.particles.ParticlePair;
import dev.esophose.playerparticles.config.CommentedFileConfiguration;
import dev.esophose.playerparticles.util.MathL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.bukkit.Location;
@ -40,6 +41,11 @@ public class ParticleStyleSphere extends DefaultParticleStyle {
}
@Override
protected List<String> getGuiIconMaterialNames() {
return Arrays.asList("HEART_OF_THE_SEA", "SNOWBALL", "SNOW_BALL");
}
@Override
protected void setDefaultSettings(CommentedFileConfiguration config) {
this.setIfNotExists("density", 15, "The number of particles to spawn per tick");

View file

@ -35,6 +35,11 @@ public class ParticleStyleSpin extends DefaultParticleStyle {
this.step = (this.step + 1) % this.maxSteps;
}
@Override
protected List<String> getGuiIconMaterialNames() {
return Collections.singletonList("BEACON");
}
@Override
protected void setDefaultSettings(CommentedFileConfiguration config) {
this.setIfNotExists("particles-per-rotation", 30, "The number of particles to spawn per rotation");

View file

@ -5,6 +5,7 @@ import dev.esophose.playerparticles.particles.ParticlePair;
import dev.esophose.playerparticles.config.CommentedFileConfiguration;
import dev.esophose.playerparticles.util.MathL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.bukkit.Location;
@ -37,6 +38,11 @@ public class ParticleStyleSpiral extends DefaultParticleStyle {
this.stepX++;
}
@Override
protected List<String> getGuiIconMaterialNames() {
return Collections.singletonList("HOPPER");
}
@Override
protected void setDefaultSettings(CommentedFileConfiguration config) {
this.setIfNotExists("particles", 12, "The number of particles to spawn around the player");

View file

@ -7,8 +7,10 @@ import dev.esophose.playerparticles.particles.PPlayer;
import dev.esophose.playerparticles.particles.ParticlePair;
import dev.esophose.playerparticles.PlayerParticles;
import dev.esophose.playerparticles.config.CommentedFileConfiguration;
import dev.esophose.playerparticles.util.NMSUtil;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import org.bukkit.Location;
import org.bukkit.entity.LivingEntity;
@ -17,16 +19,18 @@ import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.entity.EntityDamageByEntityEvent;
import org.bukkit.inventory.ItemStack;
public class ParticleStyleSwords extends DefaultParticleStyle implements Listener {
private static final List<String> SWORD_NAMES;
private static final List<String> DEFAULT_SWORD_NAMES;
private int multiplier;
private List<String> swordNames;
static {
SWORD_NAMES = new ArrayList<>();
SWORD_NAMES.addAll(Arrays.asList("WOOD_SWORD", "STONE_SWORD", "IRON_SWORD", "GOLD_SWORD", "GOLDEN_SWORD", "DIAMOND_SWORD", "TRIDENT"));
DEFAULT_SWORD_NAMES = new ArrayList<>();
DEFAULT_SWORD_NAMES.addAll(Arrays.asList("WOOD_SWORD", "WOODEN_SWORD", "STONE_SWORD", "IRON_SWORD", "GOLD_SWORD", "GOLDEN_SWORD", "DIAMOND_SWORD", "TRIDENT"));
}
public ParticleStyleSwords() {
@ -48,14 +52,21 @@ public class ParticleStyleSwords extends DefaultParticleStyle implements Listene
}
@Override
protected List<String> getGuiIconMaterialNames() {
return Collections.singletonList("IRON_SWORD");
}
@Override
protected void setDefaultSettings(CommentedFileConfiguration config) {
this.setIfNotExists("multiplier", 15, "The multiplier for the number of particles to spawn", "This style uses the same spawning as the 'normal' style");
this.setIfNotExists("sword-materials", DEFAULT_SWORD_NAMES, "The materails that are considered swords", "Set to [] to allow everything to be considered a sword", "Use AIR to allow a bare hand to be considered a sword");
}
@Override
protected void loadSettings(CommentedFileConfiguration config) {
this.multiplier = config.getInt("multiplier");
this.swordNames = config.getStringList("sword-materials");
}
@EventHandler(priority = EventPriority.MONITOR)
@ -66,13 +77,32 @@ 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 && SWORD_NAMES.contains(player.getInventory().getItemInMainHand().getType().name())) {
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);
}
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(pplayer, player.getWorld(), particle, DefaultStyles.SWORDS.getParticles(particle, loc), false);
}
}
}
private boolean isSword(ItemStack itemStack) {
if (this.swordNames.isEmpty())
return true;
if (itemStack == null)
return this.swordNames.contains("AIR");
return this.swordNames.contains(itemStack.getType().name());
}
}

View file

@ -0,0 +1,103 @@
package dev.esophose.playerparticles.styles;
import dev.esophose.playerparticles.PlayerParticles;
import dev.esophose.playerparticles.config.CommentedFileConfiguration;
import dev.esophose.playerparticles.manager.DataManager;
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 java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerTeleportEvent;
import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause;
public class ParticleStyleTeleport extends DefaultParticleStyle implements Listener {
private boolean before;
private boolean after;
private float amount;
private float spread;
private float speed;
public ParticleStyleTeleport() {
super("teleport", false, false, 0);
}
@Override
public List<PParticle> getParticles(ParticlePair particle, Location location) {
List<PParticle> particles = new ArrayList<>();
for (int i = 0; i < this.amount; i++)
particles.add(new PParticle(location, this.spread, this.spread, this.spread, this.speed));
return particles;
}
@Override
public void updateTimers() {
}
@Override
protected List<String> getGuiIconMaterialNames() {
return Collections.singletonList("ENDER_PEARL");
}
@Override
protected void setDefaultSettings(CommentedFileConfiguration config) {
this.setIfNotExists("before", true, "Spawn the particles at the teleporting position");
this.setIfNotExists("after", true, "Spawn the particles after the teleport in the new position");
this.setIfNotExists("amount", 25, "The number of particles to spawn");
this.setIfNotExists("spread", 0.5, "How much to spread the particles");
this.setIfNotExists("speed", 0.05, "If the particle supports speed, how much speed to apply");
}
@Override
protected void loadSettings(CommentedFileConfiguration config) {
this.before = config.getBoolean("before");
this.after = config.getBoolean("after");
this.amount = config.getFloat("amount");
this.spread = config.getFloat("spread");
this.speed = config.getFloat("speed");
}
@EventHandler(priority = EventPriority.MONITOR)
public void onPlayerTeleport(PlayerTeleportEvent event) {
TeleportCause cause = event.getCause();
if (cause == TeleportCause.UNKNOWN)
return;
ParticleManager particleManager = PlayerParticles.getInstance().getManager(ParticleManager.class);
Player player = event.getPlayer();
PPlayer pplayer = PlayerParticles.getInstance().getManager(DataManager.class).getPPlayer(player.getUniqueId());
if (pplayer == null)
return;
for (ParticlePair particle : pplayer.getActiveParticlesForStyle(DefaultStyles.TELEPORT)) {
if (this.before) {
Location loc1 = player.getLocation().clone();
loc1.setY(loc1.getY() + 1);
particleManager.displayParticles(pplayer, player.getWorld(), particle, DefaultStyles.TELEPORT.getParticles(particle, loc1), false);
}
if (this.after) {
Bukkit.getScheduler().runTaskLater(PlayerParticles.getInstance(), () -> {
Location loc2 = player.getLocation().clone();
loc2.setY(loc2.getY() + 1);
particleManager.displayParticles(pplayer, player.getWorld(), particle, DefaultStyles.TELEPORT.getParticles(particle, loc2), false);
}, 1);
}
}
}
}

View file

@ -4,6 +4,7 @@ import dev.esophose.playerparticles.particles.PParticle;
import dev.esophose.playerparticles.particles.ParticlePair;
import dev.esophose.playerparticles.config.CommentedFileConfiguration;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.bukkit.Location;
@ -30,6 +31,11 @@ public class ParticleStyleThick extends DefaultParticleStyle {
}
@Override
protected List<String> getGuiIconMaterialNames() {
return Arrays.asList("COBWEB", "WEB");
}
@Override
protected void setDefaultSettings(CommentedFileConfiguration config) {
this.setIfNotExists("multiplier", 1, "The multiplier for the number of particles to spawn", "This style uses the same spawning as the 'normal' style");

View file

@ -36,6 +36,11 @@ public class ParticleStyleTrail extends DefaultParticleStyle implements Listener
}
@Override
protected List<String> getGuiIconMaterialNames() {
return Collections.singletonList("GHAST_TEAR");
}
@Override
protected void setDefaultSettings(CommentedFileConfiguration config) {
this.setIfNotExists("player-offset", 0.0, "How far to offset the player location vertically");
@ -56,12 +61,13 @@ public class ParticleStyleTrail extends DefaultParticleStyle implements Listener
Player player = event.getPlayer();
PPlayer pplayer = PlayerParticles.getInstance().getManager(DataManager.class).getPPlayer(player.getUniqueId());
if (pplayer != null) {
for (ParticlePair particle : pplayer.getActiveParticlesForStyle(DefaultStyles.TRAIL)) {
Location loc = player.getLocation().clone();
loc.setY(loc.getY() + 1);
particleManager.displayParticles(player, player.getWorld(), particle, DefaultStyles.TRAIL.getParticles(particle, loc), false);
}
if (pplayer == null)
return;
for (ParticlePair particle : pplayer.getActiveParticlesForStyle(DefaultStyles.TRAIL)) {
Location loc = player.getLocation().clone();
loc.setY(loc.getY() + 1);
particleManager.displayParticles(pplayer, player.getWorld(), particle, DefaultStyles.TRAIL.getParticles(particle, loc), false);
}
}

View file

@ -5,6 +5,7 @@ import dev.esophose.playerparticles.particles.ParticlePair;
import dev.esophose.playerparticles.config.CommentedFileConfiguration;
import dev.esophose.playerparticles.util.MathL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.bukkit.Location;
@ -56,6 +57,11 @@ public class ParticleStyleTwins extends DefaultParticleStyle {
}
}
@Override
protected List<String> getGuiIconMaterialNames() {
return Arrays.asList("OAK_FENCE", "FENCE");
}
@Override
protected void setDefaultSettings(CommentedFileConfiguration config) {
this.setIfNotExists("orbs", 2, "The number of particle orbs to spawn");

View file

@ -28,6 +28,7 @@ import dev.esophose.playerparticles.particles.ParticlePair;
import dev.esophose.playerparticles.config.CommentedFileConfiguration;
import dev.esophose.playerparticles.util.MathL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.bukkit.Location;
import org.bukkit.util.Vector;
@ -66,6 +67,11 @@ public class ParticleStyleVortex extends DefaultParticleStyle {
this.step = (this.step + 1) % this.maxStep;
}
@Override
protected List<String> getGuiIconMaterialNames() {
return Collections.singletonList("GLOWSTONE_DUST");
}
@Override
protected void setDefaultSettings(CommentedFileConfiguration config) {
this.setIfNotExists("radius", 2.0, "The bottom radius of the vortex");

View file

@ -6,6 +6,7 @@ import dev.esophose.playerparticles.particles.ParticlePair;
import dev.esophose.playerparticles.config.CommentedFileConfiguration;
import dev.esophose.playerparticles.util.MathL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.bukkit.Location;
@ -74,6 +75,11 @@ public class ParticleStyleWhirl extends DefaultParticleStyle {
this.step = (this.step + Math.PI * 2 / this.numSteps) % this.numSteps;
}
@Override
protected List<String> getGuiIconMaterialNames() {
return Collections.singletonList("FEATHER");
}
@Override
protected void setDefaultSettings(CommentedFileConfiguration config) {
this.setIfNotExists("rays", 2, "The number of rays to spawn");

View file

@ -6,6 +6,7 @@ import dev.esophose.playerparticles.particles.ParticlePair;
import dev.esophose.playerparticles.config.CommentedFileConfiguration;
import dev.esophose.playerparticles.util.MathL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.bukkit.Location;
@ -74,6 +75,11 @@ public class ParticleStyleWhirlwind extends DefaultParticleStyle {
this.step = (this.step + Math.PI * 2 / this.numSteps) % this.numSteps;
}
@Override
protected List<String> getGuiIconMaterialNames() {
return Collections.singletonList("STRING");
}
@Override
protected void setDefaultSettings(CommentedFileConfiguration config) {
this.setIfNotExists("rays", 3, "The number of rays to spawn");

View file

@ -6,6 +6,8 @@ import dev.esophose.playerparticles.config.CommentedFileConfiguration;
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;
import org.bukkit.util.Vector;
@ -41,6 +43,11 @@ public class ParticleStyleWings extends DefaultParticleStyle {
this.spawnTimer %= this.spawnDelay;
}
@Override
protected List<String> getGuiIconMaterialNames() {
return Arrays.asList("ELYTRA", "RAW_CHICKEN");
}
@Override
protected void setDefaultSettings(CommentedFileConfiguration config) {
this.setIfNotExists("spawn-delay", 3, "The number of ticks to wait between particle spawns");

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

@ -40,4 +40,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

@ -6,10 +6,17 @@ import org.apache.commons.lang.WordUtils;
import org.bukkit.Material;
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 +50,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.color.NoteColor;
import dev.esophose.playerparticles.particles.color.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.ParsableMaterial;
import dev.esophose.playerparticles.util.inputparser.parsable.ParsableNoteColor;

View file

@ -1,7 +1,10 @@
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.Method;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.bukkit.Bukkit;
@ -9,10 +12,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 +38,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.color.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.color.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

@ -1,11 +1,11 @@
name: PlayerParticles
main: dev.esophose.playerparticles.PlayerParticles
version: @version@
api-version: 1.13
version: '@version@'
api-version: '1.13'
description: Display particles around your player and blocks using customized styles and data!
author: Esophose
website: https://www.spigotmc.org/resources/playerparticles.40261/
softdepend: [PlaceholderAPI]
softdepend: [PlaceholderAPI, WorldGuard, WorldEdit]
commands:
pp:
description: The main PlayerParticles command. By default, opens the GUI.

View file

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

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

@ -51,6 +51,11 @@ dependencies {
shadowJar {
archiveClassifier.set(null)
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')
relocate('org.codemc.worldguardwrapper', 'dev.esophose.playerparticles.libs.worldguardwrapper')
}
jar {

View file

@ -1,4 +1,4 @@
rootProject.name = 'PlayerParticles'
rootProject.name = 'playerparticles'
include(':Plugin')
project(':Plugin').projectDir = file('Plugin')