Preset groups finished, Rework permissions/settings

This commit is contained in:
Esophose 2018-10-30 03:17:23 -06:00
parent 22c7610259
commit 3077ddaa35
20 changed files with 684 additions and 201 deletions

View file

@ -5,22 +5,8 @@
* * Permission to allow players to overrule the max particle groups allowed in the config playerparticles.groups.unlimited
* * Setting in config.yml to disable non-event styles while the player is moving
* * Setting in config.yml for max particles allowed per player, default 3
* * Permission to allow players to overrule the max particles allowed playerparticles.particle.max
* * Permissions for the following:
* - playerparticles.particles.max.1
* - playerparticles.particles.max.2
* - playerparticles.particles.max.3
* - playerparticles.particles.max.4
* - playerparticles.particles.max.5
* - playerparticles.particles.max.6
* - playerparticles.particles.max.7
* - playerparticles.particles.max.8
* - playerparticles.particles.max.9
* - playerparticles.particles.max.10
* * Permission to allow players to overrule the max particles allowed in the config.yml
* - playerparticles.particles.max.unlimited
* Note: The default max particles in the config.yml is used instead if the playerparticles.particles.max.# is lower
* Note: The highest number the user has permission for is how many they are able to use
* Ex. they have 4 and 7, they will have a max of 7
*/
package com.esophose.playerparticles;
@ -41,9 +27,11 @@ import com.esophose.playerparticles.database.DatabaseConnector;
import com.esophose.playerparticles.database.MySqlDatabaseConnector;
import com.esophose.playerparticles.database.SqliteDatabaseConnector;
import com.esophose.playerparticles.gui.PlayerParticlesGui;
import com.esophose.playerparticles.manager.DataManager;
import com.esophose.playerparticles.manager.LangManager;
import com.esophose.playerparticles.manager.ParticleManager;
import com.esophose.playerparticles.manager.SettingManager;
import com.esophose.playerparticles.manager.SettingManager.PSetting;
import com.esophose.playerparticles.particles.ParticleGroup;
import com.esophose.playerparticles.styles.DefaultStyles;
import com.esophose.playerparticles.updater.PluginUpdateListener;
import com.esophose.playerparticles.updater.Updater;
@ -89,8 +77,9 @@ public class PlayerParticles extends JavaPlugin {
Bukkit.getPluginManager().registerEvents(new PlayerParticlesGui(), this);
saveDefaultConfig();
double configVersion = getConfig().getDouble("version");
if (configVersion < Double.parseDouble(getDescription().getVersion())) {
double configVersion = PSetting.VERSION.getDouble();
boolean updatePluginSettings = configVersion < Double.parseDouble(getDescription().getVersion());
if (updatePluginSettings) {
File configFile = new File(getDataFolder(), "config.yml");
if (configFile.exists()) {
configFile.delete();
@ -100,7 +89,7 @@ public class PlayerParticles extends JavaPlugin {
getLogger().warning("The config.yml has been updated to v" + getDescription().getVersion() + "!");
}
if (shouldCheckUpdates()) {
if (PSetting.CHECK_UPDATES.getBoolean()) {
new BukkitRunnable() {
public void run() {
try { // This can throw an exception if the server has no internet connection or if the Curse API is down
@ -116,7 +105,7 @@ public class PlayerParticles extends JavaPlugin {
}.runTaskAsynchronously(this);
}
this.reload();
this.reload(updatePluginSettings);
}
/**
@ -131,7 +120,7 @@ public class PlayerParticles extends JavaPlugin {
/**
* Reloads the settings of the plugin
*/
public void reload() {
public void reload(boolean updatePluginSettings) {
this.reloadConfig();
// If not null, plugin is already loaded
@ -145,10 +134,12 @@ public class PlayerParticles extends JavaPlugin {
DefaultStyles.registerStyles(); // Only ever load styles once
}
configureDatabase(getConfig().getBoolean("database-enable"));
// This runs before the SettingManager is reloaded, the credentials will not be stored in memory for more than a few milliseconds
configureDatabase(PSetting.DATABASE_ENABLE.getBoolean());
DataManager.reload();
LangManager.reload();
SettingManager.reload();
LangManager.reload(updatePluginSettings);
ParticleGroup.reload();
PlayerParticlesGui.setup();
@ -174,15 +165,6 @@ public class PlayerParticles extends JavaPlugin {
return databaseConnector;
}
/**
* Checks the config if the plugin can look for updates
*
* @return True if check-updates is set to true in the config
*/
public boolean shouldCheckUpdates() {
return getConfig().getBoolean("check-updates");
}
/**
* Checks if database-enable is true in the config, if it is then uses MySql
* Gets the database connection information from the config and tries to connect to the server
@ -193,7 +175,7 @@ public class PlayerParticles extends JavaPlugin {
*/
private void configureDatabase(boolean useMySql) {
if (useMySql) {
databaseConnector = new MySqlDatabaseConnector(this.getConfig());
databaseConnector = new MySqlDatabaseConnector();
} else {
databaseConnector = new SqliteDatabaseConnector(this.getDataFolder().getAbsolutePath());
}
@ -260,7 +242,7 @@ public class PlayerParticles extends JavaPlugin {
final Plugin playerParticles = this;
new BukkitRunnable() {
public void run() {
long ticks = getConfig().getLong("ticks-per-particle");
long ticks = PSetting.TICKS_PER_PARTICLE.getLong();
particleTask = new ParticleManager().runTaskTimer(playerParticles, 0, ticks);
}
}.runTaskLater(playerParticles, 1);

View file

@ -19,6 +19,7 @@ import com.esophose.playerparticles.particles.ParticleEffect.ParticleProperty;
import com.esophose.playerparticles.particles.ParticleGroup;
import com.esophose.playerparticles.particles.ParticlePair;
import com.esophose.playerparticles.styles.api.ParticleStyle;
import com.esophose.playerparticles.styles.api.ParticleStyleManager;
import com.esophose.playerparticles.util.ParticleUtils;
public class AddCommandModule implements CommandModule {
@ -29,6 +30,12 @@ public class AddCommandModule implements CommandModule {
return;
}
int maxParticlesAllowed = PermissionManager.getMaxParticlesAllowed(pplayer.getPlayer());
if (pplayer.getActiveParticles().size() >= maxParticlesAllowed) {
LangManager.sendMessage(pplayer, Lang.ADD_REACHED_MAX, maxParticlesAllowed);
return;
}
ParticleEffect effect = ParticleEffect.fromName(args[0]);
if (effect == null) {
LangManager.sendMessage(pplayer, Lang.EFFECT_INVALID, args[0]);
@ -125,6 +132,10 @@ public class AddCommandModule implements CommandModule {
DataManager.saveParticleGroup(pplayer.getUniqueId(), group);
LangManager.sendMessage(pplayer, Lang.ADD_PARTICLE_APPLIED, newParticle.getEffect().getName(), newParticle.getStyle().getName(), newParticle.getDataString());
if (ParticleStyleManager.isCustomHandled(newParticle.getStyle())) {
LangManager.sendMessage(pplayer, Lang.STYLE_EVENT_SPAWNING_INFO, newParticle.getStyle().getName());
}
}
public List<String> onTabComplete(PPlayer pplayer, String[] args) {

View file

@ -12,7 +12,7 @@ public class DefaultCommandModule implements CommandModule {
public void onCommandExecute(PPlayer pplayer, String[] args) {
// The default command just opens the GUI, execute the GUICommandModule
ParticleCommandHandler.findMatchingCommand("gui").onCommandExecute(pplayer, new String[] { "byDefault" });
ParticleCommandHandler.findMatchingCommand("gui").onCommandExecute(pplayer, new String[] { "_byDefault_" });
}
public List<String> onTabComplete(PPlayer pplayer, String[] args) {

View file

@ -89,7 +89,7 @@ public class FixedCommandModule implements CommandModule {
* @param args The command arguments
*/
private void handleCreate(PPlayer pplayer, Player p, String[] args) {
boolean reachedMax = DataManager.hasPlayerReachedMaxFixedEffects(pplayer);
boolean reachedMax = PermissionManager.hasPlayerReachedMaxFixedEffects(pplayer);
if (reachedMax) {
LangManager.sendMessage(p, Lang.FIXED_MAX_REACHED);
return;
@ -153,7 +153,7 @@ public class FixedCommandModule implements CommandModule {
}
double distanceFromEffect = p.getLocation().distance(new Location(p.getWorld(), xPos, yPos, zPos));
int maxCreationDistance = DataManager.getMaxFixedEffectCreationDistance();
int maxCreationDistance = PermissionManager.getMaxFixedEffectCreationDistance();
if (maxCreationDistance != 0 && distanceFromEffect > maxCreationDistance) {
LangManager.sendMessage(p, Lang.FIXED_CREATE_OUT_OF_RANGE, maxCreationDistance + "");
return;
@ -377,7 +377,7 @@ public class FixedCommandModule implements CommandModule {
* @param args The command arguments
*/
private void handleClear(PPlayer pplayer, Player p, String[] args) {
if (!p.hasPermission("playerparticles.fixed.clear")) {
if (!PermissionManager.canClearFixedEffects(p)) {
LangManager.sendMessage(p, Lang.FIXED_CLEAR_NO_PERMISSION);
return;
}

View file

@ -14,7 +14,7 @@ public class GUICommandModule implements CommandModule {
public void onCommandExecute(PPlayer pplayer, String[] args) {
boolean byDefault = false;
if (args.length > 0 && args[0].equals("byDefault")) {
if (args.length > 0 && args[0].equals("_byDefault_")) {
byDefault = true;
}

View file

@ -10,6 +10,7 @@ import org.bukkit.util.StringUtil;
import com.esophose.playerparticles.manager.DataManager;
import com.esophose.playerparticles.manager.LangManager;
import com.esophose.playerparticles.manager.LangManager.Lang;
import com.esophose.playerparticles.manager.PermissionManager;
import com.esophose.playerparticles.particles.PPlayer;
import com.esophose.playerparticles.particles.ParticleGroup;
import com.esophose.playerparticles.particles.ParticlePair;
@ -73,13 +74,25 @@ public class GroupCommandModule implements CommandModule {
return;
}
// Check if the player actually has any particles
if (pplayer.getActiveParticles().size() == 0) {
LangManager.sendMessage(pplayer, Lang.GROUP_SAVE_NO_PARTICLES);
return;
}
// The database column can only hold up to 100 characters, cut it off there
if (groupName.length() >= 100) {
groupName = groupName.substring(0, 100);
}
// Check if they are creating a new group, if they are, check that they haven't gone over their limit
if (pplayer.getParticleGroupByName(groupName) == null && PermissionManager.hasPlayerReachedMaxGroups(pplayer)) {
LangManager.sendMessage(pplayer, Lang.GROUP_SAVE_REACHED_MAX);
return;
}
// Use the existing group if available, otherwise create a new one
ParticleGroup group = pplayer.getParticlesByName(groupName);
ParticleGroup group = pplayer.getParticleGroupByName(groupName);
boolean groupUpdated = false;
if (group == null) {
List<ParticlePair> particles = new ArrayList<ParticlePair>();
@ -113,10 +126,22 @@ public class GroupCommandModule implements CommandModule {
}
// Get the group
ParticleGroup group = pplayer.getParticlesByName(groupName);
boolean isPreset = false;
ParticleGroup group = pplayer.getParticleGroupByName(groupName);
if (group == null) {
LangManager.sendMessage(pplayer, Lang.GROUP_INVALID, groupName);
return;
// Didn't find a saved group, look at the presets
group = ParticleGroup.getPresetGroup(groupName);
if (group == null) {
LangManager.sendMessage(pplayer, Lang.GROUP_INVALID, groupName);
return;
}
if (!group.canPlayerUse(pplayer.getPlayer())) {
LangManager.sendMessage(pplayer, Lang.GROUP_PRESET_NO_PERMISSION, groupName);
return;
}
isPreset = true;
}
// Empty out the active group and fill it with clones from the target group
@ -127,7 +152,11 @@ public class GroupCommandModule implements CommandModule {
// Update group and notify player
DataManager.saveParticleGroup(pplayer.getUniqueId(), activeGroup);
LangManager.sendMessage(pplayer, Lang.GROUP_LOAD_SUCCESS, activeGroup.getParticles().size(), groupName);
if (!isPreset)
LangManager.sendMessage(pplayer, Lang.GROUP_LOAD_SUCCESS, activeGroup.getParticles().size(), groupName);
else
LangManager.sendMessage(pplayer, Lang.GROUP_LOAD_PRESET_SUCCESS, activeGroup.getParticles().size(), groupName);
}
/**
@ -143,11 +172,14 @@ public class GroupCommandModule implements CommandModule {
return;
}
// Get the group
ParticleGroup group = pplayer.getParticlesByName(groupName);
ParticleGroup group = pplayer.getParticleGroupByName(groupName);
if (group == null) {
LangManager.sendMessage(pplayer, Lang.GROUP_INVALID, groupName);
return;
// Didn't find a saved group, look at the presets
group = ParticleGroup.getPresetGroup(groupName);
if (group != null) {
LangManager.sendMessage(pplayer, Lang.GROUP_REMOVE_PRESET);
return;
}
}
// Delete the group and notify player
@ -168,11 +200,19 @@ public class GroupCommandModule implements CommandModule {
return;
}
// Get the group
ParticleGroup group = pplayer.getParticlesByName(groupName);
ParticleGroup group = pplayer.getParticleGroupByName(groupName);
if (group == null) {
LangManager.sendMessage(pplayer, Lang.GROUP_INVALID, groupName);
return;
// Didn't find a saved group, look at the presets
group = ParticleGroup.getPresetGroup(groupName);
if (group == null) {
LangManager.sendMessage(pplayer, Lang.GROUP_INVALID, groupName);
return;
}
if (!group.canPlayerUse(pplayer.getPlayer())) {
LangManager.sendMessage(pplayer, Lang.GROUP_PRESET_NO_PERMISSION, groupName);
return;
}
}
LangManager.sendMessage(pplayer, Lang.GROUP_INFO_HEADER, groupName);
@ -187,10 +227,6 @@ public class GroupCommandModule implements CommandModule {
*/
private void onList(PPlayer pplayer) {
List<ParticleGroup> groups = pplayer.getParticleGroups();
if (groups.size() == 1) {
LangManager.sendMessage(pplayer, Lang.GROUP_LIST_NONE);
return;
}
String groupsList = "";
for (ParticleGroup group : groups)
@ -200,8 +236,25 @@ public class GroupCommandModule implements CommandModule {
if (groupsList.endsWith(", "))
groupsList = groupsList.substring(0, groupsList.length() - 2);
LangManager.sendMessage(pplayer, Lang.GROUP_LIST_OUTPUT, groupsList);
// TODO: Implement Group Presets and output them here
String presetsList = "";
for (ParticleGroup group : ParticleGroup.getPresetGroupsForPlayer(pplayer.getPlayer()))
presetsList += group.getName() + ", ";
if (presetsList.endsWith(", "))
presetsList = presetsList.substring(0, presetsList.length() - 2);
if (groupsList.isEmpty() && presetsList.isEmpty()) {
LangManager.sendMessage(pplayer, Lang.GROUP_LIST_NONE);
return;
}
if (!groupsList.isEmpty()) {
LangManager.sendMessage(pplayer, Lang.GROUP_LIST_OUTPUT, groupsList);
}
if (!presetsList.isEmpty()) {
LangManager.sendMessage(pplayer, Lang.GROUP_LIST_PRESETS, presetsList);
}
}
public List<String> onTabComplete(PPlayer pplayer, String[] args) {
@ -219,7 +272,9 @@ public class GroupCommandModule implements CommandModule {
for (ParticleGroup group : pplayer.getParticleGroups())
if (!group.getName().equals(ParticleGroup.DEFAULT_NAME))
groupNames.add(group.getName());
// TODO: Include Group Presets and add them to groupNames
if (!args[0].equals("remove"))
for (ParticleGroup group : ParticleGroup.getPresetGroupsForPlayer(pplayer.getPlayer()))
groupNames.add(group.getName());
StringUtil.copyPartialMatches(args[1], groupNames, matches);
}
}

View file

@ -6,13 +6,14 @@ import java.util.List;
import com.esophose.playerparticles.PlayerParticles;
import com.esophose.playerparticles.manager.LangManager;
import com.esophose.playerparticles.manager.LangManager.Lang;
import com.esophose.playerparticles.manager.PermissionManager;
import com.esophose.playerparticles.particles.PPlayer;
public class ReloadCommandModule implements CommandModule {
public void onCommandExecute(PPlayer pplayer, String[] args) {
if (pplayer.getPlayer().hasPermission("playerparticles.reload")) {
((PlayerParticles)PlayerParticles.getPlugin()).reload();
if (PermissionManager.canReloadPlugin(pplayer.getPlayer())) {
((PlayerParticles)PlayerParticles.getPlugin()).reload(false);
LangManager.sendMessage(pplayer, Lang.RELOAD_SUCCESS);
} else {
LangManager.sendMessage(pplayer, Lang.RELOAD_NO_PERMISSION);

View file

@ -3,21 +3,21 @@ package com.esophose.playerparticles.command;
import java.util.ArrayList;
import java.util.List;
import com.esophose.playerparticles.manager.DataManager;
import com.esophose.playerparticles.manager.LangManager;
import com.esophose.playerparticles.manager.LangManager.Lang;
import com.esophose.playerparticles.manager.PermissionManager;
import com.esophose.playerparticles.particles.PPlayer;
public class WorldsCommandModule implements CommandModule {
public void onCommandExecute(PPlayer pplayer, String[] args) {
if (DataManager.getDisabledWorlds() == null || DataManager.getDisabledWorlds().isEmpty()) {
if (PermissionManager.getDisabledWorlds() == null || PermissionManager.getDisabledWorlds().isEmpty()) {
LangManager.sendMessage(pplayer, Lang.DISABLED_WORLDS_NONE);
return;
}
String worlds = "";
for (String s : DataManager.getDisabledWorlds()) {
for (String s : PermissionManager.getDisabledWorlds()) {
worlds += s + ", ";
}
if (worlds.length() > 2) worlds = worlds.substring(0, worlds.length() - 2);

View file

@ -3,9 +3,8 @@ package com.esophose.playerparticles.database;
import java.sql.Connection;
import java.sql.SQLException;
import org.bukkit.configuration.file.FileConfiguration;
import com.esophose.playerparticles.PlayerParticles;
import com.esophose.playerparticles.manager.SettingManager.PSetting;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
@ -14,12 +13,12 @@ public class MySqlDatabaseConnector implements DatabaseConnector {
private HikariDataSource hikari;
private boolean initializedSuccessfully = false;
public MySqlDatabaseConnector(FileConfiguration pluginConfig) {
String hostname = pluginConfig.getString("database-hostname");
String port = pluginConfig.getString("database-port");
String database = pluginConfig.getString("database-name");
String user = pluginConfig.getString("database-user-name");
String pass = pluginConfig.getString("database-user-password");
public MySqlDatabaseConnector() {
String hostname = PSetting.DATABASE_HOSTNAME.getString();
String port = PSetting.DATABASE_PORT.getString();
String database = PSetting.DATABASE_NAME.getString();
String user = PSetting.DATABASE_USER_NAME.getString();
String pass = PSetting.DATABASE_USER_PASSWORD.getString();
HikariConfig config = new HikariConfig();
config.setJdbcUrl("jdbc:mysql://" + hostname + ":" + port + "/" + database);

View file

@ -33,7 +33,6 @@ import com.esophose.playerparticles.PlayerParticles;
import com.esophose.playerparticles.manager.DataManager;
import com.esophose.playerparticles.manager.LangManager;
import com.esophose.playerparticles.manager.LangManager.Lang;
import com.esophose.playerparticles.manager.ParticleManager;
import com.esophose.playerparticles.manager.PermissionManager;
import com.esophose.playerparticles.particles.PPlayer;
import com.esophose.playerparticles.particles.ParticleEffect;

View file

@ -26,24 +26,6 @@ import com.esophose.playerparticles.util.ParticleUtils;
*/
public class DataManager {
/**
* The disabled worlds cached for quick access
*/
private static List<String> disabledWorlds = null;
/**
* The max number of fixed effects a player can have, defined in the config
*/
private static int maxFixedEffects = -1;
/**
* The max distance a fixed effect can be created relative to the player
*/
private static int maxFixedEffectCreationDistance = -1;
/**
* This is not instantiable
*/
private DataManager() {
}
@ -403,64 +385,6 @@ public class DataManager {
});
}
/**
* Checks if the given player has reached the max number of fixed effects
*
* @param pplayer The player to check
* @return If the player has reached the max number of fixed effects
*/
public static boolean hasPlayerReachedMaxFixedEffects(PPlayer pplayer) {
if (maxFixedEffects == -1) { // Initialize on the fly
maxFixedEffects = PlayerParticles.getPlugin().getConfig().getInt("max-fixed-effects");
}
if (pplayer.getPlayer().hasPermission("playerparticles.fixed.unlimited")) return false;
return pplayer.getFixedEffectIds().size() >= maxFixedEffects;
}
/**
* Gets the max distance a fixed effect can be created from the player
*
* @return The max distance a fixed effect can be created from the player
*/
public static int getMaxFixedEffectCreationDistance() {
if (maxFixedEffectCreationDistance == -1) { // Initialize on the fly
maxFixedEffectCreationDistance = PlayerParticles.getPlugin().getConfig().getInt("max-fixed-effect-creation-distance");
}
return maxFixedEffectCreationDistance;
}
/**
* Checks if a world is disabled for particles to spawn in
*
* @param world The world name to check
* @return True if the world is disabled
*/
public static boolean isWorldDisabled(String world) {
return getDisabledWorlds().contains(world);
}
/**
* Gets all the worlds that are disabled
*
* @return All world names that are disabled
*/
public static List<String> getDisabledWorlds() {
if (disabledWorlds == null) { // Initialize on the fly
disabledWorlds = PlayerParticles.getPlugin().getConfig().getStringList("disabled-worlds");
}
return disabledWorlds;
}
/**
* Resets all config-related settings
*/
public static void reload() {
maxFixedEffects = -1;
maxFixedEffectCreationDistance = -1;
disabledWorlds = null;
}
/**
* Asynchronizes the callback with it's own thread
*

View file

@ -8,11 +8,11 @@ import java.nio.file.Paths;
import java.text.MessageFormat;
import org.bukkit.ChatColor;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.entity.Player;
import com.esophose.playerparticles.PlayerParticles;
import com.esophose.playerparticles.manager.SettingManager.PSetting;
import com.esophose.playerparticles.particles.PPlayer;
public class LangManager {
@ -64,6 +64,7 @@ public class LangManager {
ID_UNKNOWN,
// Add Command
ADD_REACHED_MAX,
ADD_PARTICLE_APPLIED,
// Data Command
@ -77,6 +78,7 @@ public class LangManager {
// Group Command
GROUP_INVALID,
GROUP_PRESET_NO_PERMISSION,
GROUP_RESERVED,
GROUP_NO_NAME,
GROUP_SAVE_REACHED_MAX,
@ -84,6 +86,8 @@ public class LangManager {
GROUP_SAVE_SUCCESS,
GROUP_SAVE_SUCCESS_OVERWRITE,
GROUP_LOAD_SUCCESS,
GROUP_LOAD_PRESET_SUCCESS,
GROUP_REMOVE_PRESET,
GROUP_REMOVE_SUCCESS,
GROUP_INFO_HEADER,
GROUP_LIST_NONE,
@ -114,6 +118,7 @@ public class LangManager {
// Styles
STYLE_NO_PERMISSION,
STYLE_EVENT_SPAWNING_INFO,
STYLE_INVALID,
STYLE_LIST,
@ -212,37 +217,24 @@ public class LangManager {
}
}
/**
* Stores if messages and their prefixes should be displayed
*/
private static boolean messagesEnabled, prefixEnabled;
/**
* The prefix to place before all sent messages contained in the config
*/
private static String messagePrefix;
/**
* The current lang file name
*/
private static String langFileName;
private LangManager() {
}
/**
* Used to set up the LangManager
* This should only get called once by the PlayerParticles class, however
* calling it multiple times wont affect anything negatively
*/
public static void reload() {
FileConfiguration config = PlayerParticles.getPlugin().getConfig();
messagesEnabled = config.getBoolean("messages-enabled");
prefixEnabled = config.getBoolean("use-message-prefix");
messagePrefix = parseColors(config.getString("message-prefix"));
YamlConfiguration lang = configureLangFile(config);
if (lang == null) {
messagesEnabled = false;
} else {
for (Lang messageType : Lang.values())
messageType.setMessage(lang);
}
public static void reload(boolean resetLangFile) {
YamlConfiguration lang = configureLangFile(resetLangFile);
for (Lang messageType : Lang.values())
messageType.setMessage(lang);
}
/**
@ -250,14 +242,21 @@ public class LangManager {
* If it doesn't exist, default to default.lang
* If default.lang doesn't exist, copy the file from this .jar to the target directory
*
* @param config The plugin's configuration file
* @return The YamlConfiguration of the target .lang file
*/
private static YamlConfiguration configureLangFile(FileConfiguration config) {
private static YamlConfiguration configureLangFile(boolean resetLangFile) {
File pluginDataFolder = PlayerParticles.getPlugin().getDataFolder();
langFileName = config.getString("lang-file");
langFileName = PSetting.LANG_FILE.getString();
File targetLangFile = new File(pluginDataFolder.getAbsolutePath() + "/lang/" + langFileName);
if (resetLangFile) {
File defaultLangFile = new File(pluginDataFolder.getAbsolutePath() + "/lang/default.lang");
if (defaultLangFile.exists()) {
defaultLangFile.delete();
PlayerParticles.getPlugin().getLogger().warning("default.lang has been reset!");
}
}
if (!targetLangFile.exists()) { // Target .lang file didn't exist, default to default.lang
if (!langFileName.equals("default.lang")) {
PlayerParticles.getPlugin().getLogger().warning("Couldn't find lang file '" + langFileName + "', defaulting to default.lang");
@ -272,7 +271,7 @@ public class LangManager {
return YamlConfiguration.loadConfiguration(targetLangFile);
} catch (IOException ex) {
ex.printStackTrace();
PlayerParticles.getPlugin().getLogger().severe("Unable to write default.lang to disk! All messages for the plugin have been disabled until this is fixed!");
PlayerParticles.getPlugin().getLogger().severe("Unable to write default.lang to disk! You and your players will be seeing lots of error messages!");
return null;
}
}
@ -300,14 +299,14 @@ public class LangManager {
* @param replacements The replacements for the message
*/
public static void sendMessage(Player player, Lang messageType, Object... replacements) {
if (!messagesEnabled) return;
if (!PSetting.MESSAGES_ENABLED.getBoolean()) return;
String message = messageType.get(replacements);
if (message.length() == 0) return;
if (prefixEnabled) {
message = messagePrefix + " " + message;
if (PSetting.USE_MESSAGE_PREFIX.getBoolean()) {
message = parseColors(PSetting.MESSAGE_PREFIX.getString()) + " " + message;
}
if (message.trim().equals("")) return;
@ -334,12 +333,12 @@ public class LangManager {
* @param message The message to send to the player
*/
public static void sendCustomMessage(Player player, String message) {
if (!messagesEnabled) return;
if (!PSetting.MESSAGES_ENABLED.getBoolean()) return;
if (message.trim().length() == 0) return;
if (prefixEnabled) {
message = messagePrefix + " " + message;
if (PSetting.USE_MESSAGE_PREFIX.getBoolean()) {
message = parseColors(PSetting.MESSAGE_PREFIX.getString()) + " " + message;
}
player.sendMessage(message);

View file

@ -102,14 +102,14 @@ public class ParticleManager extends BukkitRunnable implements Listener {
// 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 && !DataManager.isWorldDisabled(player.getWorld().getName()))
if (player != null && player.getGameMode() != GameMode.SPECTATOR && !PermissionManager.isWorldDisabled(player.getWorld().getName()))
for (ParticlePair particles : pplayer.getActiveParticles())
displayParticles(particles, player.getLocation().clone().add(0, 1, 0));
// Loop for FixedParticleEffects
// Don't spawn particles if the world doesn't allow it
for (FixedParticleEffect effect : pplayer.getFixedParticles())
if (!DataManager.isWorldDisabled(effect.getLocation().getWorld().getName()))
if (!PermissionManager.isWorldDisabled(effect.getLocation().getWorld().getName()))
displayFixedParticleEffect(effect);
}
}

View file

@ -5,6 +5,8 @@ import java.util.List;
import org.bukkit.entity.Player;
import com.esophose.playerparticles.manager.SettingManager.PSetting;
import com.esophose.playerparticles.particles.PPlayer;
import com.esophose.playerparticles.particles.ParticleEffect;
import com.esophose.playerparticles.styles.DefaultStyles;
import com.esophose.playerparticles.styles.api.ParticleStyle;
@ -12,33 +14,159 @@ import com.esophose.playerparticles.styles.api.ParticleStyleManager;
public class PermissionManager {
private static final String PERMISSION_PREFIX = "playerparticles.";
public enum PPermission {
ALL("*"),
EFFECT_ALL("effect.*"),
EFFECT("effect"),
STYLE_ALL("style.*"),
STYLE("style"),
FIXED("fixed"),
FIXED_UNLIMITED("fixed.unlimited"),
FIXED_CLEAR("fixed.clear"),
RELOAD("reload"),
PARTICLES_UNLIMITED("particles.unlimited"),
GROUPS_UNLIMITED("groups.unlimited");
private final String permissionString;
private PPermission(String permissionString) {
this.permissionString = permissionString;
}
/**
* Checks if a Player has a PlayerParticles permission
*
* @param p The Player
* @return True if the Player has permission
*/
public boolean check(Player p) {
String permission = PERMISSION_PREFIX + this.permissionString;
return p.hasPermission(permission);
}
/**
* Checks if a Player has a PlayerParticles permission with a sub-permission
*
* @param p The Player
* @param subPermission The sub-permission
* @return True if the Player has permission
*/
public boolean check(Player p, String subPermission) {
String permission = PERMISSION_PREFIX + this.permissionString + '.' + subPermission;
return p.hasPermission(permission);
}
}
private PermissionManager() {
}
/**
* Checks if the given player has reached the max number of particles in their active group
*
* @param pplayer The player to check
* @return If the player has reached the max number of particles in their active group
*/
public static boolean hasPlayerReachedMaxParticles(PPlayer pplayer) {
if (PPermission.ALL.check(pplayer.getPlayer())) return false;
if (PPermission.PARTICLES_UNLIMITED.check(pplayer.getPlayer())) return false;
return pplayer.getActiveParticles().size() >= PSetting.MAX_PARTICLES.getInt();
}
/**
* Checks if the given player has reached the max number of saved particle groups
*
* @param pplayer The player to check
* @return If the player has reached the max number of saved particle groups
*/
public static boolean hasPlayerReachedMaxGroups(PPlayer pplayer) {
if (PPermission.ALL.check(pplayer.getPlayer())) return false;
if (PPermission.GROUPS_UNLIMITED.check(pplayer.getPlayer())) return false;
return pplayer.getParticleGroups().size() >= PSetting.MAX_GROUPS.getInt();
}
/**
* Checks if the given player has reached the max number of fixed effects
*
* @param pplayer The player to check
* @return If the player has reached the max number of fixed effects
*/
public static boolean hasPlayerReachedMaxFixedEffects(PPlayer pplayer) {
if (PPermission.ALL.check(pplayer.getPlayer())) return false;
if (PPermission.FIXED_UNLIMITED.check(pplayer.getPlayer())) return false;
return pplayer.getFixedEffectIds().size() >= PSetting.MAX_FIXED_EFFECTS.getInt();
}
/**
* Gets the max distance a fixed effect can be created from the player
*
* @return The max distance a fixed effect can be created from the player
*/
public static int getMaxFixedEffectCreationDistance() {
return PSetting.MAX_FIXED_EFFECT_CREATION_DISTANCE.getInt();
}
/**
* Gets the maximum number of particles a player is allowed to use
*
* @param player The player to check
* @return The maximum number of particles based on the config.yml value, or unlimited
*/
public static int getMaxParticlesAllowed(Player player) {
if (PPermission.ALL.check(player) || PPermission.PARTICLES_UNLIMITED.check(player)) return Integer.MAX_VALUE;
return PSetting.MAX_PARTICLES.getInt();
}
/**
* Checks if a world is disabled for particles to spawn in
*
* @param world The world name to check
* @return True if the world is disabled
*/
public static boolean isWorldDisabled(String world) {
return getDisabledWorlds().contains(world);
}
/**
* Gets all the worlds that are disabled
*
* @return All world names that are disabled
*/
public static List<String> getDisabledWorlds() {
return PSetting.DISABLED_WORLDS.getStringList();
}
/**
* Checks if a player has permission to use an effect
* Always returns true for 'none'
*
* @param player The player to check the permission for
* @param effect The effect to check
* @return True if the player has permission to use the effect
*/
public static boolean hasEffectPermission(Player player, ParticleEffect effect) {
if (player.hasPermission("playerparticles.*") || player.hasPermission("playerparticles.effect.*")) return true;
if (player.hasPermission("playerparticles.effect." + effect.getName())) return true;
return false;
if (PPermission.ALL.check(player) || PPermission.EFFECT_ALL.check(player)) return true;
return PPermission.EFFECT.check(player, effect.getName());
}
/**
* Checks if a player has permission to use a style
* Always returns true for 'none' so they can be reset
* Always returns true for 'normal', a player needs at least one style to apply particles
*
* @param player The player to check the permission for
* @param style The style to check
* @return If the player has permission to use the style
*/
public static boolean hasStylePermission(Player player, ParticleStyle style) {
if (player.hasPermission("playerparticles.*") || player.hasPermission("playerparticles.style.*")) return true;
if (player.hasPermission("playerparticles.style." + style.getName())) return true;
if (style == DefaultStyles.NORMAL) return true;
return false;
if (PPermission.ALL.check(player) || PPermission.STYLE_ALL.check(player)) return true;
return PPermission.STYLE.check(player, style.getName());
}
/**
@ -76,7 +204,27 @@ public class PermissionManager {
* @return True if the player has permission
*/
public static boolean canUseFixedEffects(Player player) {
return player.hasPermission("playerparticles.*") || player.hasPermission("playerparticles.fixed");
return PPermission.ALL.check(player) || PPermission.FIXED.check(player);
}
/**
* Checks if a player has permission to clear fixed effects
*
* @param player The player to check the permission for
* @return True if the player has permission to use /pp fixed clear
*/
public static boolean canClearFixedEffects(Player player) {
return PPermission.ALL.check(player) || PPermission.FIXED_CLEAR.check(player);
}
/**
* Checks if a player has permission to use /pp reload
*
* @param player The player to check the permission for
* @return True if the player has permission to reload the plugin's settings
*/
public static boolean canReloadPlugin(Player player) {
return PPermission.ALL.check(player) || PPermission.RELOAD.check(player);
}
}

View file

@ -0,0 +1,159 @@
package com.esophose.playerparticles.manager;
import java.util.List;
import com.esophose.playerparticles.PlayerParticles;
public class SettingManager {
private enum PSettingType {
BOOLEAN,
INTEGER,
LONG,
DOUBLE,
STRING,
STRING_LIST
}
public enum PSetting {
VERSION(PSettingType.DOUBLE),
TICKS_PER_PARTICLE(PSettingType.LONG),
CHECK_UPDATES(PSettingType.BOOLEAN),
MESSAGES_ENABLED(PSettingType.BOOLEAN),
USE_MESSAGE_PREFIX(PSettingType.BOOLEAN),
MESSAGE_PREFIX(PSettingType.STRING),
DATABASE_ENABLE(PSettingType.BOOLEAN),
DATABASE_HOSTNAME(PSettingType.STRING),
DATABASE_PORT(PSettingType.STRING),
DATABASE_NAME(PSettingType.STRING),
DATABASE_USER_NAME(PSettingType.STRING),
DATABASE_USER_PASSWORD(PSettingType.STRING),
MAX_FIXED_EFFECTS(PSettingType.INTEGER),
MAX_FIXED_EFFECT_CREATION_DISTANCE(PSettingType.INTEGER),
MAX_PARTICLES(PSettingType.INTEGER),
MAX_GROUPS(PSettingType.INTEGER),
DISABLED_WORLDS(PSettingType.STRING_LIST),
LANG_FILE(PSettingType.STRING);
private final PSettingType settingType;
private Object value = null;
private PSetting(PSettingType settingType) {
this.settingType = settingType;
}
/**
* Resets the setting's value so it will be fetched from the config the next time it's needed
*/
private void resetDefault() {
this.value = null;
}
/**
* Gets the value from cache, or the config.yml if it isn't loaded yet
*
* @return The value of this setting
*/
private Object getValue() {
if (this.value == null) {
String configPath = this.name().toLowerCase().replaceAll("_", "-");
switch (this.settingType) {
case BOOLEAN:
this.value = PlayerParticles.getPlugin().getConfig().getBoolean(configPath);
break;
case INTEGER:
this.value = PlayerParticles.getPlugin().getConfig().getInt(configPath);
break;
case LONG:
this.value = PlayerParticles.getPlugin().getConfig().getLong(configPath);
break;
case DOUBLE:
this.value = PlayerParticles.getPlugin().getConfig().getDouble(configPath);
break;
case STRING:
this.value = PlayerParticles.getPlugin().getConfig().getString(configPath);
break;
case STRING_LIST:
this.value = PlayerParticles.getPlugin().getConfig().getStringList(configPath);
break;
}
}
return this.value;
}
/**
* Gets the setting's value as a boolean
*
* @return The setting's value as a boolean
*/
public boolean getBoolean() {
return (boolean) this.getValue();
}
/**
* Gets the setting's value as an int
*
* @return The setting's value as an int
*/
public int getInt() {
return (int) this.getValue();
}
/**
* Gets the setting's value as a long
*
* @return The setting's value as a long
*/
public long getLong() {
return (long) this.getValue();
}
/**
* Gets the setting's value as a double
*
* @return The setting's value as a double
*/
public double getDouble() {
return (double) this.getValue();
}
/**
* Gets the setting's value as a String
*
* @return The setting's value as a String
*/
public String getString() {
return (String) this.getValue();
}
/**
* Gets the setting's value as a List of Strings
*
* @return The setting's value as a List of Strings
*/
@SuppressWarnings("unchecked")
public List<String> getStringList() {
return (List<String>) this.getValue();
}
}
private SettingManager() {
}
/**
* Resets the settings to their default values
*/
public static void reload() {
for (PSetting setting : PSetting.values())
setting.resetDefault();
}
}

View file

@ -73,7 +73,7 @@ public class PPlayer {
* @param name The name of the ParticleGroup
* @return The target named ParticleGroup
*/
public ParticleGroup getParticlesByName(String name) {
public ParticleGroup getParticleGroupByName(String name) {
for (ParticleGroup group : this.particleGroups)
if (group.getName().equalsIgnoreCase(name))
return group;

View file

@ -1,11 +1,32 @@
package com.esophose.playerparticles.particles;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import org.bukkit.Material;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.entity.Player;
import com.esophose.playerparticles.PlayerParticles;
import com.esophose.playerparticles.manager.PermissionManager;
import com.esophose.playerparticles.particles.ParticleEffect.NoteColor;
import com.esophose.playerparticles.particles.ParticleEffect.OrdinaryColor;
import com.esophose.playerparticles.particles.ParticleEffect.ParticleProperty;
import com.esophose.playerparticles.styles.api.ParticleStyle;
import com.esophose.playerparticles.util.ParticleUtils;
public class ParticleGroup {
public static final String DEFAULT_NAME = "active";
private static List<ParticleGroup> presetGroups;
private String name;
private List<ParticlePair> particles;
@ -34,6 +55,21 @@ public class ParticleGroup {
return this.particles;
}
/**
* Checks if a Player can use this ParticleGroup
*
* @param player The Player
* @return True if the Player can use this ParticleGroup
*/
public boolean canPlayerUse(Player player) {
if (PermissionManager.getMaxParticlesAllowed(player) < this.particles.size()) return false;
for (ParticlePair particle : this.particles) {
if (!PermissionManager.hasEffectPermission(player, particle.getEffect())) return false;
if (!PermissionManager.hasStylePermission(player, particle.getStyle())) return false;
}
return true;
}
/**
* Gets an empty ParticleGroup
*
@ -43,4 +79,158 @@ public class ParticleGroup {
return new ParticleGroup(DEFAULT_NAME, new ArrayList<ParticlePair>());
}
/**
* Loads the preset groups from the groups.yml file
*
* @param pluginDataFolder
*/
public static void reload() {
presetGroups = new ArrayList<ParticleGroup>();
File pluginDataFolder = PlayerParticles.getPlugin().getDataFolder();
File groupsFile = new File(pluginDataFolder.getAbsolutePath() + File.separator + "groups.yml");
// Create the file if it doesn't exist
if (!groupsFile.exists()) {
try (InputStream inStream = PlayerParticles.getPlugin().getResource("groups.yml")) {
Files.copy(inStream, Paths.get(groupsFile.getAbsolutePath()));
} catch (IOException e) {
e.printStackTrace();
}
}
// Parse groups.yml file
YamlConfiguration groupsYaml = YamlConfiguration.loadConfiguration(groupsFile);
Set<String> groupNames = groupsYaml.getKeys(false);
for (String groupName : groupNames) {
try {
List<ParticlePair> particles = new ArrayList<ParticlePair>();
ConfigurationSection groupSection = groupsYaml.getConfigurationSection(groupName);
Set<String> particleKeys = groupSection.getKeys(false);
for (String stringId : particleKeys) {
ConfigurationSection particleSection = groupSection.getConfigurationSection(stringId);
int id = Integer.parseInt(stringId);
ParticleEffect effect = ParticleEffect.fromName(particleSection.getString("effect"));
ParticleStyle style = ParticleStyle.fromName(particleSection.getString("style"));
if (effect == null) {
PlayerParticles.getPlugin().getLogger().severe("Invalid effect name: '" + particleSection.getString("effect") + "'!");
throw new Exception();
}
if (style == null) {
PlayerParticles.getPlugin().getLogger().severe("Invalid style name: '" + particleSection.getString("style") + "'!");
throw new Exception();
}
Material itemData = null;
Material blockData = null;
OrdinaryColor colorData = null;
NoteColor noteColorData = null;
String dataString = particleSection.getString("data");
if (!dataString.isEmpty()) {
String[] args = dataString.split(" ");
if (effect.hasProperty(ParticleProperty.COLORABLE)) {
if (effect == ParticleEffect.NOTE) {
if (args[0].equalsIgnoreCase("rainbow")) {
noteColorData = new NoteColor(99);
} else {
int note = -1;
try {
note = Integer.parseInt(args[0]);
} catch (Exception e) {
PlayerParticles.getPlugin().getLogger().severe("Invalid note: '" + args[0] + "'!");
throw new Exception();
}
if (note < 0 || note > 23) {
PlayerParticles.getPlugin().getLogger().severe("Invalid note: '" + args[0] + "'!");
throw new Exception();
}
noteColorData = new NoteColor(note);
}
} else {
if (args[0].equalsIgnoreCase("rainbow")) {
colorData = new OrdinaryColor(999, 999, 999);
} else {
int r = -1;
int g = -1;
int b = -1;
try {
r = Integer.parseInt(args[0]);
g = Integer.parseInt(args[1]);
b = Integer.parseInt(args[2]);
} catch (Exception e) {
PlayerParticles.getPlugin().getLogger().severe("Invalid color: '" + args[0] + " " + args[1] + " " + args[2] + "'!");
throw new Exception();
}
if (r < 0 || r > 255 || g < 0 || g > 255 || b < 0 || b > 255) {
PlayerParticles.getPlugin().getLogger().severe("Invalid color: '" + args[0] + " " + args[1] + " " + args[2] + "'!");
throw new Exception();
}
colorData = new OrdinaryColor(r, g, b);
}
}
} else if (effect.hasProperty(ParticleProperty.REQUIRES_MATERIAL_DATA)) {
if (effect == ParticleEffect.BLOCK || effect == ParticleEffect.FALLING_DUST) {
try {
blockData = ParticleUtils.closestMatch(args[0]);
if (blockData == null || !blockData.isBlock()) throw new Exception();
} catch (Exception e) {
PlayerParticles.getPlugin().getLogger().severe("Invalid block: '" + args[0] + "'!");
throw new Exception();
}
} else if (effect == ParticleEffect.ITEM) {
try {
itemData = ParticleUtils.closestMatch(args[0]);
if (itemData == null || itemData.isBlock()) throw new Exception();
} catch (Exception e) {
PlayerParticles.getPlugin().getLogger().severe("Invalid item: '" + args[0] + "'!");
throw new Exception();
}
}
}
}
particles.add(new ParticlePair(null, id, effect, style, blockData, blockData, colorData, noteColorData));
}
presetGroups.add(new ParticleGroup(groupName, particles));
} catch (Exception ex) {
PlayerParticles.getPlugin().getLogger().severe("An error occurred while parsing the groups.yml file!");
}
}
}
/**
* Gets all the preset ParticleGroups that a player can use
*
* @param player The player
* @return a List of preset ParticleGroups the player can use
*/
public static List<ParticleGroup> getPresetGroupsForPlayer(Player player) {
return presetGroups.stream().filter(x -> x.canPlayerUse(player)).collect(Collectors.toList());
}
/**
* Gets a preset ParticleGroup by its name
*
* @param groupName The ParticleGroup name
* @return The preset ParticleGroup if it exists, otherwise null
*/
public static ParticleGroup getPresetGroup(String groupName) {
for (ParticleGroup group : presetGroups)
if (group.getName().equalsIgnoreCase(groupName))
return group;
return null;
}
}

View file

@ -48,6 +48,14 @@ disabled-worlds: []
# - your_world_name_here
# - add_more_under_these
# The maximum number of particles a player can apply at once
# Default: 3
max-particles: 3
# The maximum number of groups a player can have saved
# Default: 10
max-groups: 10
# Max fixed effects per player
# Default: 5
max-fixed-effects: 5
@ -56,8 +64,8 @@ max-fixed-effects: 5
# Determines how far away a player may create a fixed effect from themselves
# This measurement is in blocks
# Set to 0 for infinite distance
# Default: 128
max-fixed-effect-creation-distance: 128
# Default: 32
max-fixed-effect-creation-distance: 32
# How many ticks to wait before spawning more particles
# Increasing this value may cause less lag (if there was any), but will decrease prettiness

View file

@ -6,6 +6,9 @@
# effect and style! #
# * Feel free to create your own, they will be #
# available for users to select within the GUI! #
# * This file is not automatically updated. If you #
# want to reset the file to its defaults, simply #
# delete it and run the command '/pp reload'. #
# ==================================================== #
raincloud:

View file

@ -50,6 +50,7 @@ id-invalid: '&cThe ID you entered is invalid, it must be a positive whole number
id-unknown: '&cYou do not have a particle applied with the ID &b{0}&c!'
# Add Command
add-reached-max: '&cUnable to apply particle, you have reached the maximum amount of &b{0} &callowed!'
add-particle-applied: '&aA new particle has been applied with the effect &b{0}&a, style &b{1}&a, and data &b{2}&a!'
# Data Command
@ -62,14 +63,17 @@ edit-success-style: '&aYour particle with an ID of &b{0} &ahas had its style cha
edit-success-data: '&aYour particle with an ID of &b{0} &ahas had its data changed to &b{1}&a!'
# Group Command
group-invalid: '&cA group does not exist with the name &b{0}&c!'
group-invalid: '&cA saved group or preset group does not exist with the name &b{0}&c!'
group-preset-no-permission: '&cYou are missing permission for an effect or style to use the preset group &b{0}&c!'
group-reserved: '&cThe group name &bactive &cis reserved and cannot be used!'
group-no-name: '&cYou did not provide a group name! &b/pp {0} <groupName>'
group-save-reached-max: '&cUnable to save group, you have reached the max number of groups!'
group-save-no-particles: '&cUnable to save group, you do not have any particles applied!'
group-save-success: '&aYour current particles have been saved under the group named &b{0}&a!'
group-save-success-overwrite: '&aThe group named &b{0} &ahas been updated with your current particles!'
group-load-success: '&aApplied &b{0} &aparticles from the group &b{1}&a!'
group-load-success: '&aApplied &b{0} &aparticle(s) from your saved group named &b{1}&a!'
group-load-preset-success: '&aApplied &b{0} &aparticle(s) from the preset group named &b{1}&a!'
group-remove-preset: '&cYou cannot remove a preset group!'
group-remove-success: '&aRemoved the particle group named &b{0}&a!'
group-info-header: '&eThe group &b{0} &ehas the following particles:'
group-list-none: '&eYou do not have any particle groups saved!'
@ -100,6 +104,7 @@ effect-list-empty: '&cYou do not have permission to use any effects!'
# Styles
style-no-permission: '&cYou do not have permission to use the style &b{0} &c!'
style-event-spawning-info: '&eNote: The style &b{0} &espawns particles based on an event.'
style-invalid: '&cThe style &b{0} &cdoes not exist! Use &b/pp styles &cfor a list of styles you can use.'
style-list: '&eYou can use the following styles: &b{0}'