Merge pull request #92 from Rosewood-Development/particle-packs

Added particle packs and other v8 changes
This commit is contained in:
Esophose 2022-03-22 21:46:58 -06:00 committed by GitHub
commit befe6ef3e2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
60 changed files with 598 additions and 138 deletions

View file

@ -10,7 +10,7 @@ sourceCompatibility = 1.8
targetCompatibility = 1.8
compileJava.options.encoding = 'UTF-8'
group = 'dev.esophose'
version = '7.25'
version = '8.0-SNAPSHOT'
java {
withJavadocJar()
@ -27,15 +27,15 @@ repositories {
}
dependencies {
api 'org.slf4j:slf4j-api:1.7.25'
api 'org.slf4j:slf4j-nop:1.7.25'
compileOnly 'org.spigotmc:spigot-api:1.18.2-R0.1-SNAPSHOT'
compileOnly 'org.jetbrains:annotations:23.0.0'
compileOnly 'me.clip:placeholderapi:2.10.4'
compileOnly 'org.xerial:sqlite-jdbc:3.36.0.3'
api 'org.slf4j:slf4j-api:1.7.36'
api 'org.slf4j:slf4j-nop:1.7.36'
api 'com.zaxxer:HikariCP:3.2.0'
api 'org.bstats:bstats-bukkit-lite:1.7'
api 'org.codemc.worldguardwrapper:worldguardwrapper:1.2.0-SNAPSHOT'
compileOnly 'org.jetbrains:annotations:16.0.2'
compileOnly 'me.clip:placeholderapi:2.10.4'
compileOnly 'org.xerial:sqlite-jdbc:3.23.1'
compileOnly 'org.spigotmc:spigot-api:1.18.1-R0.1-SNAPSHOT'
}
shadowJar {
@ -60,33 +60,22 @@ publishing {
publications {
shadow(MavenPublication) { publication ->
project.shadow.component(publication)
}
mavenJava(MavenPublication) {
artifactId = 'playerparticles'
from components.java
versionMapping {
usage('java-api') {
fromResolutionOf('runtimeClasspath')
}
usage('java-runtime') {
fromResolutionResult()
}
}
pom {
name = 'playerparticles'
}
}
}
repositories {
if (project.hasProperty('mavenUsername') && project.hasProperty('mavenPassword')) {
if (project.hasProperty('mavenUser') && project.hasProperty('mavenPassword')) {
maven {
credentials {
username project.mavenUsername
username project.mavenUser
password project.mavenPassword
}
def releasesRepoUrl = 'https://repo.codemc.org/repository/maven-releases/'
def snapshotsRepoUrl = 'https://repo.codemc.org/repository/maven-snapshots/'
def releasesRepoUrl = 'https://repo.rosewooddev.io/repository/public-releases/'
def snapshotsRepoUrl = 'https://repo.rosewooddev.io/repository/public-snapshots/'
url = version.endsWith('SNAPSHOT') ? snapshotsRepoUrl : releasesRepoUrl
}
}

View file

@ -1,3 +1,9 @@
=== v8.0 ===
+ Added support for Particle Packs
+ Added setting toggle-on-combat-include-mobs
* Fixed particle effects now save the pitch/yaw of the player when they are created
* Fixed an error when doing /pp reload if a player has the GUI open
* The icosphere style now fades between dust colors when using the dust_color_transition effect
=== v7.24 ===
* Fixed configs not generating properly on newer versions of 1.18.1
=== v7.23 ===

View file

@ -19,6 +19,7 @@ import dev.esophose.playerparticles.manager.LocaleManager;
import dev.esophose.playerparticles.manager.Manager;
import dev.esophose.playerparticles.manager.ParticleGroupPresetManager;
import dev.esophose.playerparticles.manager.ParticleManager;
import dev.esophose.playerparticles.manager.ParticlePackManager;
import dev.esophose.playerparticles.manager.ParticleStyleManager;
import dev.esophose.playerparticles.manager.PermissionManager;
import dev.esophose.playerparticles.manager.PluginUpdateManager;
@ -70,9 +71,8 @@ public class PlayerParticles extends JavaPlugin {
pm.registerEvents(new PPlayerCombatListener(), this);
pm.registerEvents(new PlayerChatHook(), this);
if (Setting.SEND_METRICS.getBoolean())
if (NMSUtil.getVersionNumber() > 7)
new MetricsLite(this, 3531);
if (Setting.SEND_METRICS.getBoolean() && NMSUtil.getVersionNumber() > 7)
new MetricsLite(this, 3531);
if (PlaceholderAPIHook.enabled())
new ParticlePlaceholderExpansion(this).register();
@ -128,6 +128,7 @@ public class PlayerParticles extends JavaPlugin {
this.getManager(ConfigurationManager.class);
this.getManager(LocaleManager.class);
this.getManager(ParticlePackManager.class);
this.getManager(DataManager.class);
this.getManager(PermissionManager.class);
this.getManager(CommandManager.class);

View file

@ -865,7 +865,7 @@ public final class PlayerParticlesAPI {
if (fixedEffect == null)
return null;
fixedEffect.setCoordinates(location.getX(), location.getY(), location.getZ());
fixedEffect.setCoordinates(location);
dataManager.saveFixedEffect(fixedEffect);
return fixedEffect;
}

View file

@ -151,6 +151,9 @@ public class FixedCommandModule implements CommandModule {
}
if (player != null) {
location.setYaw(player.getLocation().getYaw());
location.setPitch(player.getLocation().getPitch());
double distanceFromEffect = player.getLocation().distance(location);
int maxCreationDistance = permissionManager.getMaxFixedEffectCreationDistance();
if (maxCreationDistance != 0 && distanceFromEffect > maxCreationDistance) {
@ -292,6 +295,9 @@ public class FixedCommandModule implements CommandModule {
}
if (player != null) {
location.setYaw(player.getLocation().getYaw());
location.setPitch(player.getLocation().getPitch());
double distanceFromEffect = player.getLocation().distance(location);
int maxCreationDistance = permissionManager.getMaxFixedEffectCreationDistance();
if (maxCreationDistance != 0 && distanceFromEffect > maxCreationDistance) {
@ -300,7 +306,7 @@ public class FixedCommandModule implements CommandModule {
}
}
fixedEffect.setCoordinates(location.getX(), location.getY(), location.getZ());
fixedEffect.setCoordinates(location);
break;
case "effect": {
ParticleEffect effect = inputParser.next(ParticleEffect.class);

View file

@ -40,28 +40,28 @@ public class GroupCommandModule implements CommandModule {
}
switch (args[0].toLowerCase()) {
case "save":
this.onSave(pplayer, args[1].toLowerCase());
break;
case "load":
this.onLoad(pplayer, args[1].toLowerCase());
break;
case "remove":
this.onRemove(pplayer, args[1].toLowerCase());
break;
case "info":
this.onInfo(pplayer, args[1].toLowerCase());
break;
case "list":
this.onList(pplayer);
break;
default:
localeManager.sendMessage(pplayer, "command-description-group-save");
localeManager.sendMessage(pplayer, "command-description-group-load");
localeManager.sendMessage(pplayer, "command-description-group-remove");
localeManager.sendMessage(pplayer, "command-description-group-info");
localeManager.sendMessage(pplayer, "command-description-group-list");
break;
case "save":
this.onSave(pplayer, args[1].toLowerCase());
break;
case "load":
this.onLoad(pplayer, args[1].toLowerCase());
break;
case "remove":
this.onRemove(pplayer, args[1].toLowerCase());
break;
case "info":
this.onInfo(pplayer, args[1].toLowerCase());
break;
case "list":
this.onList(pplayer);
break;
default:
localeManager.sendMessage(pplayer, "command-description-group-save");
localeManager.sendMessage(pplayer, "command-description-group-load");
localeManager.sendMessage(pplayer, "command-description-group-remove");
localeManager.sendMessage(pplayer, "command-description-group-info");
localeManager.sendMessage(pplayer, "command-description-group-list");
break;
}
}
@ -163,10 +163,11 @@ public class GroupCommandModule implements CommandModule {
// Update group and notify player
PlayerParticlesAPI.getInstance().savePlayerParticleGroup(pplayer.getPlayer(), activeGroup);
if (!isPreset)
if (!isPreset) {
localeManager.sendMessage(pplayer, "group-load-success", StringPlaceholders.builder("amount", activeGroup.getParticles().size()).addPlaceholder("name", groupName).build());
else
} else {
localeManager.sendMessage(pplayer, "group-load-preset-success", StringPlaceholders.builder("amount", activeGroup.getParticles().size()).addPlaceholder("name", groupName).build());
}
}
/**

View file

@ -0,0 +1,24 @@
package dev.esophose.playerparticles.database.migrations;
import dev.esophose.playerparticles.database.DataMigration;
import dev.esophose.playerparticles.database.DatabaseConnector;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
public class _4_Add_Fixed_Effect_Yaw_Pitch_Columns extends DataMigration {
public _4_Add_Fixed_Effect_Yaw_Pitch_Columns() {
super(4);
}
@Override
public void migrate(DatabaseConnector connector, Connection connection, String tablePrefix) throws SQLException {
try (Statement statement = connection.createStatement()) {
statement.executeUpdate("ALTER TABLE " + tablePrefix + "fixed ADD COLUMN yaw DOUBLE DEFAULT 0");
statement.executeUpdate("ALTER TABLE " + tablePrefix + "fixed ADD COLUMN pitch DOUBLE DEFAULT 0");
}
}
}

View file

@ -23,6 +23,7 @@ import dev.esophose.playerparticles.command.ToggleCommandModule;
import dev.esophose.playerparticles.command.UseCommandModule;
import dev.esophose.playerparticles.command.VersionCommandModule;
import dev.esophose.playerparticles.command.WorldsCommandModule;
import dev.esophose.playerparticles.hook.PlaceholderAPIHook;
import dev.esophose.playerparticles.particles.PPlayer;
import dev.esophose.playerparticles.util.ParticleUtils;
import java.util.ArrayList;
@ -178,7 +179,11 @@ public class CommandManager extends Manager implements CommandExecutor, TabCompl
return true;
} else if (cmd.getName().equalsIgnoreCase("ppo")) {
Bukkit.getScheduler().runTask(this.playerParticles, () -> this.ppoCommand.onCommandExecute(sender, args));
String[] replacedArgs = new String[args.length];
Player player = sender instanceof Player ? (Player) sender : null;
for (int i = 0; i < args.length; i++)
replacedArgs[i] = PlaceholderAPIHook.applyPlaceholders(player, args[i]);
Bukkit.getScheduler().runTask(this.playerParticles, () -> this.ppoCommand.onCommandExecute(sender, replacedArgs));
}
return true;

View file

@ -48,6 +48,7 @@ public class ConfigurationManager extends Manager {
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", "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"),
TOGGLE_ON_COMBAT_INCLUDE_MOBS("toggle-on-combat-include-mobs", false, "If true, mobs will be included in the combat check in addition to players"),
DISABLED_WORLDS("disabled-worlds", Collections.singletonList("disabled_world_name"), "A list of worlds that the plugin is disabled in"),
CHECK_PERMISSIONS_ON_LOGIN("check-permissions-on-login", false, "Should particles a player no longer has permission to use be removed on login?"),
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"),

View file

@ -103,7 +103,6 @@ public class DataManager extends Manager {
* @param callback The callback to execute with the found pplayer, or a newly generated one
*/
public void getPPlayer(UUID playerUUID, Consumer<PPlayer> callback) {
// Try to get them from cache first
PPlayer fromCache = this.getPPlayer(playerUUID);
if (fromCache != null) {
@ -201,7 +200,7 @@ public class DataManager extends Manager {
}
// Load fixed effects
String fixedQuery = "SELECT f.id AS f_id, f.world, f.xPos, f.yPos, f.zPos, p.id AS p_id, p.effect, p.style, p.item_material, p.block_material, p.note, p.r, p.g, p.b, p.r_end, p.g_end, p.b_end, p.duration FROM " + this.getTablePrefix() + "fixed f " +
String fixedQuery = "SELECT f.id AS f_id, f.world, f.xPos, f.yPos, f.zPos, f.yaw, f.pitch, p.id AS p_id, p.effect, p.style, p.item_material, p.block_material, p.note, p.r, p.g, p.b, p.r_end, p.g_end, p.b_end, p.duration FROM " + this.getTablePrefix() + "fixed f " +
"JOIN " + this.getTablePrefix() + "particle p ON f.particle_uuid = p.uuid " +
"WHERE f.owner_uuid = ?";
try (PreparedStatement statement = connection.prepareStatement(fixedQuery)) {
@ -214,6 +213,8 @@ public class DataManager extends Manager {
double xPos = result.getDouble("xPos");
double yPos = result.getDouble("yPos");
double zPos = result.getDouble("zPos");
double yaw = result.getDouble("yaw");
double pitch = result.getDouble("pitch");
World world = Bukkit.getWorld(result.getString("world"));
if (world == null) {
// World was deleted, remove the fixed effect as it is no longer valid
@ -241,7 +242,7 @@ public class DataManager extends Manager {
continue;
}
fixedParticles.put(fixedEffectId, new FixedParticleEffect(playerUUID, fixedEffectId, new Location(world, xPos, yPos, zPos), particle));
fixedParticles.put(fixedEffectId, new FixedParticleEffect(playerUUID, fixedEffectId, new Location(world, xPos, yPos, zPos, (float) yaw, (float) pitch), particle));
}
}
@ -492,7 +493,7 @@ public class DataManager extends Manager {
statement.executeUpdate();
}
String fixedEffectQuery = "INSERT INTO " + this.getTablePrefix() + "fixed (owner_uuid, id, particle_uuid, world, xPos, yPos, zPos) VALUES (?, ?, ?, ?, ?, ?, ?)";
String fixedEffectQuery = "INSERT INTO " + this.getTablePrefix() + "fixed (owner_uuid, id, particle_uuid, world, xPos, yPos, zPos, yaw, pitch) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)";
try (PreparedStatement statement = connection.prepareStatement(fixedEffectQuery)) {
statement.setString(1, fixedEffect.getOwnerUniqueId().toString());
statement.setInt(2, fixedEffect.getId());
@ -501,6 +502,8 @@ public class DataManager extends Manager {
statement.setDouble(5, fixedEffect.getLocation().getX());
statement.setDouble(6, fixedEffect.getLocation().getY());
statement.setDouble(7, fixedEffect.getLocation().getZ());
statement.setDouble(8, fixedEffect.getLocation().getYaw());
statement.setDouble(9, fixedEffect.getLocation().getPitch());
statement.executeUpdate();
}
}));
@ -514,13 +517,15 @@ public class DataManager extends Manager {
public void updateFixedEffect(FixedParticleEffect fixedEffect) {
this.async(() -> this.databaseConnector.connect((connection) -> {
// Update fixed effect
String fixedEffectQuery = "UPDATE " + this.getTablePrefix() + "fixed SET xPos = ?, yPos = ?, zPos = ? WHERE owner_uuid = ? AND id = ?";
String fixedEffectQuery = "UPDATE " + this.getTablePrefix() + "fixed SET xPos = ?, yPos = ?, zPos = ?, yaw = ?, pitch = ? WHERE owner_uuid = ? AND id = ?";
try (PreparedStatement statement = connection.prepareStatement(fixedEffectQuery)) {
statement.setDouble(1, fixedEffect.getLocation().getX());
statement.setDouble(2, fixedEffect.getLocation().getY());
statement.setDouble(3, fixedEffect.getLocation().getZ());
statement.setString(4, fixedEffect.getOwnerUniqueId().toString());
statement.setInt(5, fixedEffect.getId());
statement.setDouble(4, fixedEffect.getLocation().getYaw());
statement.setDouble(5, fixedEffect.getLocation().getPitch());
statement.setString(6, fixedEffect.getOwnerUniqueId().toString());
statement.setInt(7, fixedEffect.getId());
statement.executeUpdate();
}
@ -589,7 +594,7 @@ public class DataManager extends Manager {
}
/**
* Asynchronizes the callback with it's own thread unless it is already not on the main thread
* Asynchronizes the callback with its own thread unless it is already not on the main thread
*
* @param asyncCallback The callback to run on a separate thread
*/

View file

@ -7,6 +7,7 @@ import dev.esophose.playerparticles.database.SQLiteConnector;
import dev.esophose.playerparticles.database.migrations._1_InitialMigration;
import dev.esophose.playerparticles.database.migrations._2_Add_Data_Columns;
import dev.esophose.playerparticles.database.migrations._3_Add_Setting_Toggle_Self_Column;
import dev.esophose.playerparticles.database.migrations._4_Add_Fixed_Effect_Yaw_Pitch_Columns;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.Arrays;
@ -24,7 +25,8 @@ public class DataMigrationManager extends Manager {
this.migrations = Arrays.asList(
new _1_InitialMigration(),
new _2_Add_Data_Columns(),
new _3_Add_Setting_Toggle_Self_Column()
new _3_Add_Setting_Toggle_Self_Column(),
new _4_Add_Fixed_Effect_Yaw_Pitch_Columns()
);
}

View file

@ -94,15 +94,22 @@ public class GuiManager extends Manager implements Listener, Runnable {
* Used for when the plugin unloads so players can't take items from the GUI
*/
public void forceCloseAllOpenGUIs() {
List<Player> toClose = new ArrayList<>();
for (Player player : Bukkit.getOnlinePlayers()) {
for (GuiInventory inventory : this.guiInventories) {
if (inventory.getPPlayer().getUniqueId().equals(player.getUniqueId()) && inventory.getInventory().equals(player.getOpenInventory().getTopInventory())) {
player.closeInventory();
toClose.add(player);
break;
}
}
}
this.guiInventories.clear();
if (Bukkit.isPrimaryThread()) {
toClose.forEach(Player::closeInventory);
} else {
Bukkit.getScheduler().runTask(this.playerParticles, x -> toClose.forEach(Player::closeInventory));
}
}
/**

View file

@ -0,0 +1,242 @@
package dev.esophose.playerparticles.manager;
import dev.esophose.playerparticles.PlayerParticles;
import dev.esophose.playerparticles.pack.ParticlePack;
import dev.esophose.playerparticles.pack.ParticlePackDescription;
import dev.esophose.playerparticles.pack.ParticlePackInitializationException;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.jar.JarEntry;
import java.util.jar.JarInputStream;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration;
public class ParticlePackManager extends Manager {
public static final String PACK_DIRECTORY_NAME = "packs";
public static final String INFO_FILE_NAME = "particle_pack.yml";
private final Map<String, ParticlePack> loadedPacks;
public ParticlePackManager(PlayerParticles playerParticles) {
super(playerParticles);
this.loadedPacks = new HashMap<>();
}
@Override
public void reload() {
this.moveParticlePacksFromPluginsFolder();
File packDirectory = new File(this.playerParticles.getDataFolder(), PACK_DIRECTORY_NAME);
if (!packDirectory.exists())
packDirectory.mkdirs();
File[] files = packDirectory.listFiles();
if (files == null || files.length == 0)
return;
for (File file : files) {
if (!file.isFile() || !file.getName().endsWith(".jar") || !this.isParticlePack(file))
continue;
ParticlePack particlePack = this.load(file);
if (particlePack == null)
continue;
ParticlePackDescription description = particlePack.getDescription();
if (description.getName() == null || description.getName().isEmpty()) {
this.playerParticles.getLogger().warning("Particle pack '" + file.getName() + "' has no name, skipping!");
continue;
}
if (description.getVersion() == null || description.getVersion().isEmpty()) {
this.playerParticles.getLogger().warning("Particle pack '" + description.getName() + "' has no version, skipping!");
continue;
}
ParticlePack possibleDuplicate = this.getParticlePack(description.getName());
if (possibleDuplicate != null) {
this.playerParticles.getLogger().warning("Found duplicate particle pack '" + description.getName() + "', overwriting!");
this.loadedPacks.values().remove(possibleDuplicate);
}
this.loadedPacks.put(particlePack.getName(), particlePack);
this.playerParticles.getLogger().info("Loaded particle pack '" + particlePack.getName() + " v" + particlePack.getDescription().getVersion() + "' with " + particlePack.getNumberOfStyles() + " style" + (particlePack.getNumberOfStyles() > 1 ? "s" : ""));
}
int numStyles = this.loadedPacks.values().stream().mapToInt(ParticlePack::getNumberOfStyles).sum();
this.playerParticles.getLogger().info("Loaded " + this.loadedPacks.size() + " particle pack" + (this.loadedPacks.size() > 1 ? "s" : "") + " with " + numStyles + " style" + (numStyles > 1 ? "s" : ""));
}
@Override
public void disable() {
this.loadedPacks.values().forEach(particlePack -> {
try {
particlePack.getClassLoader().close();
} catch (IOException e) {
e.printStackTrace();
}
});
this.loadedPacks.clear();
}
/**
* Gets the particle pack with the given name, case-insensitive
*
* @param packName The name of the particle pack
* @return The particle pack with the given name, or null if not found
*/
public ParticlePack getParticlePack(String packName) {
return this.loadedPacks.entrySet().stream()
.filter(x -> x.getKey().equalsIgnoreCase(packName))
.map(Map.Entry::getValue)
.findFirst()
.orElse(null);
}
/**
* @return A collection of all loaded particle packs
*/
public Collection<ParticlePack> getLoadedParticlePacks() {
return this.loadedPacks.values();
}
/**
* Attempts to load a particle pack from the given file
*
* @param packJar The file to load the particle pack from
* @return The loaded particle pack, or null if the file is not a valid particle pack
*/
public ParticlePack load(File packJar) {
try {
URL jar = packJar.toURI().toURL();
URLClassLoader classLoader = new URLClassLoader(new URL[]{jar}, this.getClass().getClassLoader());
List<String> matches = new ArrayList<>();
List<Class<? extends ParticlePack>> classes = new ArrayList<>();
try (JarInputStream jarInputStream = new JarInputStream(jar.openStream())) {
JarEntry entry;
while ((entry = jarInputStream.getNextJarEntry()) != null) {
String name = entry.getName();
if (name.endsWith(".class"))
matches.add(name.substring(0, name.lastIndexOf('.')).replace('/', '.'));
}
}
for (String match : matches) {
try {
Class<?> clazz = classLoader.loadClass(match);
if (ParticlePack.class.isAssignableFrom(clazz))
classes.add(clazz.asSubclass(ParticlePack.class));
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
if (classes.isEmpty()) {
this.playerParticles.getLogger().warning("Failed to load particle pack " + packJar.getName() + ": No particle pack found");
classLoader.close();
return null;
}
if (classes.size() > 1) {
this.playerParticles.getLogger().warning("Failed to load particle pack " + packJar.getName() + ": Multiple particle packs found");
return null;
}
try (InputStream inputStream = new URL("jar:file:" + packJar.getAbsolutePath() + "!/" + INFO_FILE_NAME).openStream();
Reader fileReader = new InputStreamReader(inputStream)) {
FileConfiguration particlePackConfig = YamlConfiguration.loadConfiguration(fileReader);
ParticlePack particlePack = classes.get(0).getDeclaredConstructor().newInstance();
Method initMethod = ParticlePack.class.getDeclaredMethod("init", PlayerParticles.class, ParticlePackDescription.class, URLClassLoader.class);
initMethod.setAccessible(true);
initMethod.invoke(particlePack, this.playerParticles, new ParticlePackDescription(particlePackConfig), classLoader);
return particlePack;
}
} catch (Exception e) {
throw new ParticlePackInitializationException(packJar.getName(), e);
}
}
/**
* Attempts to unload a particle pack with the given name
*
* @param packName The name of the particle pack to unload
* @return True if the particle pack was unloaded, false if it was not found or something went wrong
*/
public boolean unload(String packName) {
ParticlePack particlePack = this.getParticlePack(packName);
if (particlePack == null)
return false;
try {
ParticleStyleManager particleStyleManager = this.playerParticles.getManager(ParticleStyleManager.class);
particlePack.getStyles().forEach(particleStyleManager::removeAllStyleReferences);
particlePack.getEventStyles().forEach(particleStyleManager::removeAllStyleReferences);
this.loadedPacks.remove(particlePack.getName());
particlePack.getClassLoader().close();
} catch (IOException e) {
e.printStackTrace();
return false;
}
return true;
}
/**
* Moves particle packs in the plugins directory to the packs directory
*/
private void moveParticlePacksFromPluginsFolder() {
File pluginsDirectory = this.playerParticles.getDataFolder().getParentFile();
File[] files = pluginsDirectory.listFiles();
if (files == null || files.length == 0)
return;
for (File file : files) {
if (!file.isFile() || !file.getName().endsWith(".jar"))
continue;
if (this.isParticlePack(file)) {
this.playerParticles.getLogger().warning("Found particle pack in plugins directory, attempting to move to packs directory: " + file.getName());
// Move the jar to the packs folder
File newFile = new File(this.playerParticles.getDataFolder(), PACK_DIRECTORY_NAME + File.separator + file.getName());
if (newFile.exists()) {
this.playerParticles.getLogger().warning("Found duplicate particle pack when moving to packs directory, deleting old version: " + file.getName());
newFile.delete();
}
file.renameTo(newFile);
}
}
}
/**
* Checks if the given file is a particle pack
*
* @param file The file to check
* @return True if the file is a particle pack, false otherwise
*/
private boolean isParticlePack(File file) {
try {
// Try to find a file named "particle_pack.yml" in the jar and immediately discard it
new URL("jar:file:" + file.getAbsolutePath() + "!/" + INFO_FILE_NAME).openStream().close();
return true;
} catch (IOException e) {
return false;
}
}
}

View file

@ -2,12 +2,14 @@ package dev.esophose.playerparticles.manager;
import dev.esophose.playerparticles.PlayerParticles;
import dev.esophose.playerparticles.event.ParticleStyleRegistrationEvent;
import dev.esophose.playerparticles.particles.PPlayer;
import dev.esophose.playerparticles.particles.ParticleGroup;
import dev.esophose.playerparticles.styles.ConfiguredParticleStyle;
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.LinkedHashMap;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@ -25,8 +27,8 @@ public class ParticleStyleManager extends Manager {
public ParticleStyleManager(PlayerParticles playerParticles) {
super(playerParticles);
this.stylesByName = new LinkedHashMap<>();
this.stylesByInternalName = new LinkedHashMap<>();
this.stylesByName = new HashMap<>();
this.stylesByInternalName = new HashMap<>();
this.eventStyles = new ArrayList<>();
DefaultStyles.initStyles();
@ -43,12 +45,18 @@ public class ParticleStyleManager extends Manager {
// 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();
// Register styles from particle packs
this.playerParticles.getManager(ParticlePackManager.class).getLoadedParticlePacks().forEach(pack -> {
pack.getStyles().forEach(event::registerStyle);
pack.getEventStyles().forEach(event::registerEventStyle);
});
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 {
@ -64,6 +72,9 @@ public class ParticleStyleManager extends Manager {
if (this.stylesByInternalName.containsKey(style.getInternalName().toLowerCase()))
throw new IllegalArgumentException("Tried to register two styles with the same internal name spelling: '" + style.getInternalName() + "'");
if (style instanceof ConfiguredParticleStyle)
((ConfiguredParticleStyle) style).loadSettings();
this.stylesByName.put(style.getName().toLowerCase(), style);
this.stylesByInternalName.put(style.getInternalName().toLowerCase(), style);
@ -81,6 +92,25 @@ public class ParticleStyleManager extends Manager {
}
/**
* Removes all references of a ParticleStyle from all PPlayers
*
* @param style The style to remove
*/
public void removeAllStyleReferences(ParticleStyle style) {
Collection<PPlayer> pplayers = this.playerParticles.getManager(ParticleManager.class).getPPlayers().values();
for (PPlayer pplayer : pplayers) {
// Remove all references to style from groups
pplayer.getParticleGroups().values().removeIf(group -> {
group.getParticles().values().removeIf(particle -> particle.getStyle().equals(style));
return group.getParticles().isEmpty() && !group.getName().equals(ParticleGroup.DEFAULT_NAME);
});
// Remove all references to style from fixed effects
pplayer.getFixedParticlesMap().values().removeIf(x -> x.getParticlePair().getStyle().equals(style));
}
}
/**
* Returns if a given style is customly handled
*
@ -92,10 +122,14 @@ public class ParticleStyleManager extends Manager {
}
/**
* @return A List of styles that are registered and enabled
* @return A List of styles that are registered, enabled, and sorted by name
*/
public Collection<ParticleStyle> getStyles() {
return this.stylesByName.values().stream().filter(ParticleStyle::isEnabled).collect(Collectors.toList());
public List<ParticleStyle> getStyles() {
return this.stylesByName.entrySet().stream()
.filter(x -> x.getValue().isEnabled())
.sorted(Map.Entry.comparingByKey())
.map(Map.Entry::getValue)
.collect(Collectors.toList());
}
/**

View file

@ -0,0 +1,83 @@
package dev.esophose.playerparticles.pack;
import dev.esophose.playerparticles.PlayerParticles;
import dev.esophose.playerparticles.manager.ParticlePackManager;
import dev.esophose.playerparticles.styles.ParticleStyle;
import java.io.File;
import java.net.URLClassLoader;
import java.util.List;
public abstract class ParticlePack {
protected PlayerParticles playerParticles;
private ParticlePackDescription description;
private URLClassLoader classLoader;
/**
* @return The list of styles registered by this particle pack
*/
public abstract List<ParticleStyle> getStyles();
/**
* @return The list of event-based styles registered by this particle pack
*/
public abstract List<ParticleStyle> getEventStyles();
/**
* Called when the pack is enabled, does nothing by default
*/
public void onEnable() {
}
/**
* @return The total number of styles returned by {@link #getStyles()} and {@link #getEventStyles()}
*/
public final int getNumberOfStyles() {
return this.getStyles().size() + this.getEventStyles().size();
}
/**
* @return The name of the pack, shorthand for {@link ParticlePackDescription#getName()}
*/
public final String getName() {
return this.description.getName();
}
/**
* @return The description of the pack
*/
public final ParticlePackDescription getDescription() {
return this.description;
}
/**
* @return The classloader of the pack
*/
public final URLClassLoader getClassLoader() {
return this.classLoader;
}
/**
* @return The directory of the pack's configuration files
*/
public final File getConfigDirectory() {
return new File(PlayerParticles.getInstance().getDataFolder(), ParticlePackManager.PACK_DIRECTORY_NAME + File.separator + this.getName());
}
/**
* Called reflectively from the ParticlePackManager when loaded
*
* @param playerParticles The plugin instance
* @param description The description of the pack
* @param classLoader The classloader of the pack
*/
private void init(PlayerParticles playerParticles, ParticlePackDescription description, URLClassLoader classLoader) {
this.playerParticles = playerParticles;
this.description = description;
this.classLoader = classLoader;
this.onEnable();
}
}

View file

@ -0,0 +1,29 @@
package dev.esophose.playerparticles.pack;
import org.bukkit.configuration.file.FileConfiguration;
public class ParticlePackDescription {
private final String name;
private final String version;
public ParticlePackDescription(FileConfiguration packFileConfig) {
this.name = packFileConfig.getString("name");
this.version = packFileConfig.getString("version");
}
/**
* @return the name of the pack
*/
public String getName() {
return this.name;
}
/**
* @return the version of the pack
*/
public String getVersion() {
return this.version;
}
}

View file

@ -0,0 +1,9 @@
package dev.esophose.playerparticles.pack;
public class ParticlePackInitializationException extends RuntimeException {
public ParticlePackInitializationException(String packName, Throwable cause) {
super("An error occurred initializing a particle pack: " + packName, cause);
}
}

View file

@ -90,4 +90,13 @@ public class FixedParticleEffect {
this.location.setZ(z);
}
/**
* Updates the coordinates of the FixedParticleEffect
*
* @param location The new Location
*/
public void setCoordinates(Location location) {
this.location = location;
}
}

View file

@ -21,7 +21,7 @@ import org.bukkit.event.entity.EntityDamageByEntityEvent;
public class PPlayerCombatListener implements Listener {
private static final int CHECK_INTERVAL = 20;
private Map<UUID, Integer> timeSinceCombat = new HashMap<>();
private final Map<UUID, Integer> timeSinceCombat = new HashMap<>();
public PPlayerCombatListener() {
DataManager dataManager = PlayerParticles.getInstance().getManager(DataManager.class);
@ -31,7 +31,6 @@ public class PPlayerCombatListener implements Listener {
return;
List<UUID> toRemove = new ArrayList<>();
for (UUID uuid : this.timeSinceCombat.keySet()) {
PPlayer pplayer = dataManager.getPPlayer(uuid);
if (pplayer == null) {
@ -50,13 +49,15 @@ public class PPlayerCombatListener implements Listener {
}
/**
* Used to detect if the player is moving
* Used to detect if the player is in combat
*
* @param event The event
*/
@EventHandler(priority = EventPriority.MONITOR)
public void onPlayerAttack(EntityDamageByEntityEvent event) {
if (event.getEntity().getType() != EntityType.PLAYER)
boolean attackedIsPlayer = event.getEntity().getType() == EntityType.PLAYER;
boolean includeMobs = Setting.TOGGLE_ON_COMBAT_INCLUDE_MOBS.getBoolean();
if (!attackedIsPlayer && !includeMobs)
return;
Player attacker;
@ -70,10 +71,10 @@ public class PPlayerCombatListener implements Listener {
attacker = (Player) event.getDamager();
} else return;
Player damaged = (Player) event.getEntity();
if (attackedIsPlayer)
this.markInCombat((Player) event.getEntity());
this.markInCombat(attacker);
this.markInCombat(damaged);
}
/**

View file

@ -3,22 +3,24 @@ package dev.esophose.playerparticles.styles;
import com.google.common.collect.ObjectArrays;
import dev.esophose.playerparticles.PlayerParticles;
import dev.esophose.playerparticles.config.CommentedFileConfiguration;
import dev.esophose.playerparticles.pack.ParticlePack;
import dev.esophose.playerparticles.util.ParticleUtils;
import java.io.File;
import java.util.List;
import org.bukkit.Material;
public abstract class DefaultParticleStyle implements ParticleStyle {
public abstract class ConfiguredParticleStyle implements ParticleStyle {
protected PlayerParticles playerParticles;
private CommentedFileConfiguration config;
private boolean changed;
private String internalStyleName;
private boolean canBeFixedByDefault;
private boolean canToggleWithMovementByDefault;
private boolean canToggleWithCombatByDefault;
private double fixedEffectOffsetByDefault;
private final ParticlePack owningPack;
private final String internalStyleName;
private final boolean canBeFixedByDefault;
private final boolean canToggleWithMovementByDefault;
private final boolean canToggleWithCombatByDefault;
private final double fixedEffectOffsetByDefault;
private String styleName;
private boolean enabled;
@ -28,23 +30,31 @@ public abstract class DefaultParticleStyle implements ParticleStyle {
private double fixedEffectOffset;
private Material guiIconMaterial;
protected DefaultParticleStyle(String internalStyleName, boolean canBeFixedByDefault, boolean canToggleWithMovementByDefault, double fixedEffectOffsetByDefault) {
protected ConfiguredParticleStyle(String internalStyleName, boolean canBeFixedByDefault, boolean canToggleWithMovementByDefault, double fixedEffectOffsetByDefault) {
this(null, internalStyleName, canBeFixedByDefault, canToggleWithMovementByDefault, fixedEffectOffsetByDefault);
}
protected ConfiguredParticleStyle(ParticlePack owningPack, String internalStyleName, boolean canBeFixedByDefault, boolean canToggleWithMovementByDefault, double fixedEffectOffsetByDefault) {
this.owningPack = owningPack;
this.internalStyleName = internalStyleName;
this.canBeFixedByDefault = canBeFixedByDefault;
this.canToggleWithMovementByDefault = canToggleWithMovementByDefault;
this.canToggleWithCombatByDefault = true;
this.fixedEffectOffsetByDefault = fixedEffectOffsetByDefault;
this.playerParticles = PlayerParticles.getInstance();
this.setDefaultSettings();
this.loadSettings(false);
}
/**
* Sets the default settings shared for each style then calls setDefaultSettings(CommentedFileConfiguration)
* Sets the default settings shared for each style then calls {@link #setDefaultSettings(CommentedFileConfiguration)}
*/
private void setDefaultSettings() {
File directory = new File(this.playerParticles.getDataFolder(), "styles");
File directory;
if (this.owningPack == null) {
directory = new File(this.playerParticles.getDataFolder(), "styles");
} else {
directory = this.owningPack.getConfigDirectory();
}
directory.mkdirs();
File file = new File(directory, this.internalStyleName + ".yml");
@ -66,13 +76,10 @@ public abstract class DefaultParticleStyle implements ParticleStyle {
}
/**
* Loads the settings shared for each style then calls loadSettings(CommentedFileConfiguration)
*
* @param reloadConfig If the settings should be reloaded or not
* Loads the settings shared for each style then calls {@link #loadSettings(CommentedFileConfiguration)}
*/
public final void loadSettings(boolean reloadConfig) {
if (reloadConfig)
this.setDefaultSettings();
public final void loadSettings() {
this.setDefaultSettings();
this.styleName = this.config.getString("style-name");
this.enabled = this.config.getBoolean("enabled");

View file

@ -124,8 +124,8 @@ public class DefaultStyles implements Listener {
*/
public static void reloadSettings(ParticleStyleManager particleStyleManager) {
for (ParticleStyle style : particleStyleManager.getStylesWithDisabled())
if (style instanceof DefaultParticleStyle)
((DefaultParticleStyle) style).loadSettings(true);
if (style instanceof ConfiguredParticleStyle)
((ConfiguredParticleStyle) style).loadSettings();
}
}

View file

@ -17,7 +17,7 @@ import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.entity.ProjectileLaunchEvent;
public class ParticleStyleArrows extends DefaultParticleStyle implements Listener {
public class ParticleStyleArrows extends ConfiguredParticleStyle implements Listener {
private List<Projectile> projectiles;

View file

@ -13,7 +13,7 @@ import org.bukkit.util.Vector;
/*
* Equations Source: https://www.desmos.com/calculator/cscx2zcrlf
*/
public class ParticleStyleBatman extends DefaultParticleStyle {
public class ParticleStyleBatman extends ConfiguredParticleStyle {
private int step = 0;

View file

@ -9,7 +9,7 @@ import java.util.Collections;
import java.util.List;
import org.bukkit.Location;
public class ParticleStyleBeam extends DefaultParticleStyle {
public class ParticleStyleBeam extends ConfiguredParticleStyle {
private int step = 0;
private boolean reversed = false;

View file

@ -17,7 +17,7 @@ import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.block.BlockBreakEvent;
public class ParticleStyleBlockBreak extends DefaultParticleStyle implements Listener {
public class ParticleStyleBlockBreak extends ConfiguredParticleStyle implements Listener {
private int particleAmount;
private double particleSpread;

View file

@ -17,7 +17,7 @@ import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.block.BlockPlaceEvent;
public class ParticleStyleBlockPlace extends DefaultParticleStyle implements Listener {
public class ParticleStyleBlockPlace extends ConfiguredParticleStyle implements Listener {
private int particleAmount;
private double particleSpread;

View file

@ -21,7 +21,7 @@ import org.bukkit.Location;
import org.bukkit.entity.Player;
import org.bukkit.scheduler.BukkitRunnable;
public class ParticleStyleCelebration extends DefaultParticleStyle {
public class ParticleStyleCelebration extends ConfiguredParticleStyle {
private int step = 0;

View file

@ -8,7 +8,7 @@ import java.util.Collections;
import java.util.List;
import org.bukkit.Location;
public class ParticleStyleChains extends DefaultParticleStyle {
public class ParticleStyleChains extends ConfiguredParticleStyle {
private int chainParticleAmount;

View file

@ -33,7 +33,7 @@ import java.util.List;
import org.bukkit.Location;
import org.bukkit.util.Vector;
public class ParticleStyleCompanion extends DefaultParticleStyle {
public class ParticleStyleCompanion extends ConfiguredParticleStyle {
private int step = 0;

View file

@ -38,7 +38,7 @@ import org.bukkit.util.Vector;
* The project this is from is called EffectLib and can be found here:
* https://github.com/Slikey/EffectLib
*/
public class ParticleStyleCube extends DefaultParticleStyle {
public class ParticleStyleCube extends ConfiguredParticleStyle {
private int step = 0;
private boolean skipNextStep = false; // Only spawn every 2 ticks

View file

@ -20,7 +20,7 @@ import org.bukkit.event.entity.EntityDamageEvent;
import org.bukkit.event.entity.PlayerDeathEvent;
import org.bukkit.scheduler.BukkitRunnable;
public class ParticleStyleDeath extends DefaultParticleStyle implements Listener {
public class ParticleStyleDeath extends ConfiguredParticleStyle implements Listener {
private String style;
private List<EntityDamageEvent.DamageCause> causes;

View file

@ -8,7 +8,7 @@ import java.util.Arrays;
import java.util.List;
import org.bukkit.Location;
public class ParticleStyleFeet extends DefaultParticleStyle {
public class ParticleStyleFeet extends ConfiguredParticleStyle {
private double feetOffset;
private double particleSpreadX, particleSpreadY, particleSpreadZ;

View file

@ -20,7 +20,7 @@ import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerFishEvent;
import org.bukkit.projectiles.ProjectileSource;
public class ParticleStyleFishing extends DefaultParticleStyle implements Listener {
public class ParticleStyleFishing extends ConfiguredParticleStyle 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;

View file

@ -9,7 +9,7 @@ import java.util.Arrays;
import java.util.List;
import org.bukkit.Location;
public class ParticleStyleHalo extends DefaultParticleStyle {
public class ParticleStyleHalo extends ConfiguredParticleStyle {
private boolean skipNextSpawn = false;

View file

@ -17,7 +17,7 @@ import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.entity.EntityDamageEvent;
public class ParticleStyleHurt extends DefaultParticleStyle implements Listener {
public class ParticleStyleHurt extends ConfiguredParticleStyle implements Listener {
private int thickMultiplier;

View file

@ -19,7 +19,7 @@ import java.util.Set;
import org.bukkit.Location;
import org.bukkit.util.Vector;
public class ParticleStyleIcosphere extends DefaultParticleStyle {
public class ParticleStyleIcosphere extends ConfiguredParticleStyle {
private int ticksPerSpawn;
private double radius;

View file

@ -10,7 +10,7 @@ import java.util.Arrays;
import java.util.List;
import org.bukkit.Location;
public class ParticleStyleInvocation extends DefaultParticleStyle {
public class ParticleStyleInvocation extends ConfiguredParticleStyle {
private double step = 0;
private int circleStep = 0;

View file

@ -17,7 +17,7 @@ import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerMoveEvent;
public class ParticleStyleMove extends DefaultParticleStyle implements Listener {
public class ParticleStyleMove extends ConfiguredParticleStyle implements Listener {
private final ParticleManager particleManager = PlayerParticles.getInstance().getManager(ParticleManager.class);
private final DataManager dataManager = PlayerParticles.getInstance().getManager(DataManager.class);

View file

@ -9,7 +9,7 @@ import java.util.Collections;
import java.util.List;
import org.bukkit.Location;
public class ParticleStyleNormal extends DefaultParticleStyle {
public class ParticleStyleNormal extends ConfiguredParticleStyle {
protected ParticleStyleNormal() {
super("normal", true, false, 0);

View file

@ -9,7 +9,7 @@ import java.util.Arrays;
import java.util.List;
import org.bukkit.Location;
public class ParticleStyleOrbit extends DefaultParticleStyle {
public class ParticleStyleOrbit extends ConfiguredParticleStyle {
private int step = 0;

View file

@ -15,7 +15,7 @@ import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.entity.Player;
public class ParticleStyleOutline extends DefaultParticleStyle {
public class ParticleStyleOutline extends ConfiguredParticleStyle {
private double particleDistance;
private int spawnDelay;

View file

@ -8,7 +8,7 @@ import java.util.Collections;
import java.util.List;
import org.bukkit.Location;
public class ParticleStyleOverhead extends DefaultParticleStyle {
public class ParticleStyleOverhead extends ConfiguredParticleStyle {
private double headOffset;
private double particleSpreadX, particleSpreadY, particleSpreadZ;

View file

@ -7,7 +7,7 @@ import java.util.Collections;
import java.util.List;
import org.bukkit.Location;
public class ParticleStylePoint extends DefaultParticleStyle {
public class ParticleStylePoint extends ConfiguredParticleStyle {
private double offset;

View file

@ -10,7 +10,7 @@ import java.util.List;
import org.bukkit.Location;
import org.bukkit.util.Vector;
public class ParticleStylePopper extends DefaultParticleStyle {
public class ParticleStylePopper extends ConfiguredParticleStyle {
private int step = 0;

View file

@ -10,7 +10,7 @@ import java.util.Arrays;
import java.util.List;
import org.bukkit.Location;
public class ParticleStylePulse extends DefaultParticleStyle {
public class ParticleStylePulse extends ConfiguredParticleStyle {
private double step = 0;

View file

@ -9,7 +9,7 @@ import java.util.Arrays;
import java.util.List;
import org.bukkit.Location;
public class ParticleStyleQuadhelix extends DefaultParticleStyle {
public class ParticleStyleQuadhelix extends ConfiguredParticleStyle {
private int stepX = 0;
private int stepY = 0;

View file

@ -9,7 +9,7 @@ import java.util.Arrays;
import java.util.List;
import org.bukkit.Location;
public class ParticleStyleRings extends DefaultParticleStyle {
public class ParticleStyleRings extends ConfiguredParticleStyle {
private int step = 0;

View file

@ -9,7 +9,7 @@ import java.util.Arrays;
import java.util.List;
import org.bukkit.Location;
public class ParticleStyleSphere extends DefaultParticleStyle {
public class ParticleStyleSphere extends ConfiguredParticleStyle {
private int density;
private double radius;

View file

@ -8,7 +8,7 @@ import java.util.Collections;
import java.util.List;
import org.bukkit.Location;
public class ParticleStyleSpin extends DefaultParticleStyle {
public class ParticleStyleSpin extends ConfiguredParticleStyle {
private int step = 0;

View file

@ -9,7 +9,7 @@ import java.util.Collections;
import java.util.List;
import org.bukkit.Location;
public class ParticleStyleSpiral extends DefaultParticleStyle {
public class ParticleStyleSpiral extends ConfiguredParticleStyle {
private int stepX = 0;

View file

@ -21,7 +21,7 @@ import org.bukkit.event.Listener;
import org.bukkit.event.entity.EntityDamageByEntityEvent;
import org.bukkit.inventory.ItemStack;
public class ParticleStyleSwords extends DefaultParticleStyle implements Listener {
public class ParticleStyleSwords extends ConfiguredParticleStyle implements Listener {
private static final List<String> DEFAULT_SWORD_NAMES;

View file

@ -19,7 +19,7 @@ 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 {
public class ParticleStyleTeleport extends ConfiguredParticleStyle implements Listener {
private boolean before;
private boolean after;

View file

@ -8,7 +8,7 @@ import java.util.Arrays;
import java.util.List;
import org.bukkit.Location;
public class ParticleStyleThick extends DefaultParticleStyle {
public class ParticleStyleThick extends ConfiguredParticleStyle {
private int multiplier;

View file

@ -16,7 +16,7 @@ import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerMoveEvent;
public class ParticleStyleTrail extends DefaultParticleStyle implements Listener {
public class ParticleStyleTrail extends ConfiguredParticleStyle implements Listener {
private final ParticleManager particleManager = PlayerParticles.getInstance().getManager(ParticleManager.class);
private final DataManager dataManager = PlayerParticles.getInstance().getManager(DataManager.class);

View file

@ -9,7 +9,7 @@ import java.util.Arrays;
import java.util.List;
import org.bukkit.Location;
public class ParticleStyleTwins extends DefaultParticleStyle {
public class ParticleStyleTwins extends ConfiguredParticleStyle {
private int stepX = 0;
private int stepY = 0;

View file

@ -33,7 +33,7 @@ import java.util.List;
import org.bukkit.Location;
import org.bukkit.util.Vector;
public class ParticleStyleVortex extends DefaultParticleStyle {
public class ParticleStyleVortex extends ConfiguredParticleStyle {
private int step = 0;

View file

@ -10,7 +10,7 @@ import java.util.Collections;
import java.util.List;
import org.bukkit.Location;
public class ParticleStyleWhirl extends DefaultParticleStyle {
public class ParticleStyleWhirl extends ConfiguredParticleStyle {
private double step = 0;

View file

@ -10,7 +10,7 @@ import java.util.Collections;
import java.util.List;
import org.bukkit.Location;
public class ParticleStyleWhirlwind extends DefaultParticleStyle {
public class ParticleStyleWhirlwind extends ConfiguredParticleStyle {
private double step = 0;

View file

@ -11,14 +11,13 @@ import java.util.List;
import org.bukkit.Location;
import org.bukkit.util.Vector;
public class ParticleStyleWings extends DefaultParticleStyle {
private int spawnTimer = 0; // Spawn particles every 3 ticks
public class ParticleStyleWings extends ConfiguredParticleStyle {
private int spawnTimer;
private int spawnDelay;
protected ParticleStyleWings() {
super("wings", false, true, 0);
super("wings", true, true, 0);
}
@Override

View file

@ -4,7 +4,7 @@ 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/
website: https://www.spigotmc.org/resources/40261/
softdepend: [PlaceholderAPI, WorldGuard, WorldEdit]
commands:
pp: