mirror of
https://github.com/TotalFreedomMC/PlayerParticles.git
synced 2025-08-05 03:53:10 +00:00
441 lines
18 KiB
Java
441 lines
18 KiB
Java
package dev.esophose.playerparticles.manager;
|
|
|
|
import dev.esophose.playerparticles.PlayerParticles;
|
|
import dev.esophose.playerparticles.manager.ConfigurationManager.Setting;
|
|
import dev.esophose.playerparticles.nms.wrapper.ParticleHandler;
|
|
import dev.esophose.playerparticles.particles.ConsolePPlayer;
|
|
import dev.esophose.playerparticles.particles.FixedParticleEffect;
|
|
import dev.esophose.playerparticles.particles.ParticleEffectSettings;
|
|
import dev.esophose.playerparticles.particles.color.NoteColor;
|
|
import dev.esophose.playerparticles.particles.color.OrdinaryColor;
|
|
import dev.esophose.playerparticles.particles.PParticle;
|
|
import dev.esophose.playerparticles.particles.PPlayer;
|
|
import dev.esophose.playerparticles.particles.color.ParticleColor;
|
|
import dev.esophose.playerparticles.particles.ParticleEffect;
|
|
import dev.esophose.playerparticles.particles.ParticlePair;
|
|
import dev.esophose.playerparticles.particles.ParticleProperty;
|
|
import dev.esophose.playerparticles.particles.version.VersionMapping;
|
|
import dev.esophose.playerparticles.styles.DefaultStyles;
|
|
import dev.esophose.playerparticles.util.NMSUtil;
|
|
import java.awt.Color;
|
|
import java.util.ArrayList;
|
|
import java.util.Collection;
|
|
import java.util.Comparator;
|
|
import java.util.HashMap;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.Map.Entry;
|
|
import java.util.Random;
|
|
import java.util.UUID;
|
|
import java.util.concurrent.ConcurrentHashMap;
|
|
import java.util.stream.Collectors;
|
|
import java.util.stream.Stream;
|
|
import org.bukkit.Bukkit;
|
|
import org.bukkit.GameMode;
|
|
import org.bukkit.Location;
|
|
import org.bukkit.Particle.DustOptions;
|
|
import org.bukkit.World;
|
|
import org.bukkit.entity.Player;
|
|
import org.bukkit.event.EventHandler;
|
|
import org.bukkit.event.EventPriority;
|
|
import org.bukkit.event.Listener;
|
|
import org.bukkit.event.player.PlayerJoinEvent;
|
|
import org.bukkit.event.player.PlayerQuitEvent;
|
|
import org.bukkit.inventory.ItemStack;
|
|
import org.bukkit.material.MaterialData;
|
|
import org.bukkit.metadata.MetadataValue;
|
|
import org.bukkit.scheduler.BukkitTask;
|
|
|
|
public class ParticleManager extends Manager implements Listener, Runnable {
|
|
|
|
/**
|
|
* The map containing all the loaded PPlayer info
|
|
*/
|
|
private final Map<UUID, PPlayer> particlePlayers;
|
|
|
|
/**
|
|
* The task that spawns the particles
|
|
*/
|
|
private BukkitTask particleTask;
|
|
|
|
private ParticleHandler particleHandler;
|
|
private Map<ParticleEffect, ParticleEffectSettings> supportedParticleEffects;
|
|
|
|
/**
|
|
* Rainbow particle effect hue and note color used for rainbow colorable effects
|
|
*/
|
|
private int hue;
|
|
private int note;
|
|
private final Random random;
|
|
|
|
public ParticleManager(PlayerParticles playerParticles) {
|
|
super(playerParticles);
|
|
|
|
this.particlePlayers = new ConcurrentHashMap<>();
|
|
this.particleTask = null;
|
|
this.hue = 0;
|
|
this.note = 0;
|
|
this.random = new Random();
|
|
|
|
Bukkit.getPluginManager().registerEvents(this, this.playerParticles);
|
|
}
|
|
|
|
@Override
|
|
public void reload() {
|
|
if (this.particleTask != null)
|
|
this.particleTask.cancel();
|
|
|
|
int overrideVersion = Setting.OVERRIDE_PARTICLE_VERSION.getInt();
|
|
VersionMapping versionMapping = VersionMapping.getVersionMapping(overrideVersion != -1 ? overrideVersion : NMSUtil.getVersionNumber());
|
|
this.particleHandler = NMSUtil.getHandler(versionMapping);
|
|
|
|
this.supportedParticleEffects = new HashMap<>();
|
|
for (ParticleEffect particleEffect : versionMapping.getParticleEffectNameMapping().keySet())
|
|
this.supportedParticleEffects.put(particleEffect, new ParticleEffectSettings(particleEffect));
|
|
|
|
Bukkit.getScheduler().runTaskLater(this.playerParticles, () -> {
|
|
long ticks = Setting.TICKS_PER_PARTICLE.getLong();
|
|
this.particleTask = Bukkit.getScheduler().runTaskTimerAsynchronously(this.playerParticles, this, 5, ticks);
|
|
}, 1);
|
|
|
|
this.particlePlayers.clear();
|
|
DataManager dataManager = this.playerParticles.getManager(DataManager.class);
|
|
dataManager.loadFixedEffects();
|
|
for (Player player : Bukkit.getOnlinePlayers())
|
|
dataManager.getPPlayer(player.getUniqueId(), (pplayer) -> { }); // Loads the PPlayer from the database
|
|
dataManager.getPPlayer(ConsolePPlayer.getUUID(), (pplayer) -> { }); // Load the console PPlayer
|
|
}
|
|
|
|
@Override
|
|
public void disable() {
|
|
if (this.particleTask != null)
|
|
this.particleTask.cancel();
|
|
}
|
|
|
|
/**
|
|
* Adds the player to the array when they join
|
|
*
|
|
* @param e The event
|
|
*/
|
|
@EventHandler(priority = EventPriority.MONITOR)
|
|
public void onPlayerJoin(PlayerJoinEvent e) {
|
|
this.playerParticles.getManager(DataManager.class).getPPlayer(e.getPlayer().getUniqueId(), (pplayer) -> { }); // Loads the PPlayer from the database
|
|
}
|
|
|
|
/**
|
|
* Removes the player from the array when they log off
|
|
*
|
|
* @param e The event
|
|
*/
|
|
@EventHandler(priority = EventPriority.MONITOR)
|
|
public void onPlayerQuit(PlayerQuitEvent e) {
|
|
PPlayer pplayer = this.playerParticles.getManager(DataManager.class).getPPlayer(e.getPlayer().getUniqueId());
|
|
if (pplayer != null) {
|
|
pplayer.clearCachedPlayer();
|
|
if (pplayer.getFixedEffectIds().isEmpty())
|
|
this.particlePlayers.remove(pplayer.getUniqueId()); // Unload the PPlayer if they don't have any fixed effects
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Gets the PPlayers that are loaded
|
|
*
|
|
* @return The loaded PPlayers
|
|
*/
|
|
public Collection<PPlayer> getPPlayers() {
|
|
return this.particlePlayers.values();
|
|
}
|
|
|
|
/**
|
|
* Adds a PPlayer to the loaded map
|
|
*
|
|
* @param pplayer The PPlayer to add
|
|
*/
|
|
public void addPPlayer(PPlayer pplayer) {
|
|
this.particlePlayers.put(pplayer.getUniqueId(), pplayer);
|
|
}
|
|
|
|
/**
|
|
* The main loop to display all the particles
|
|
* Does not display particles if the world is disabled or if the player is in spectator mode
|
|
*/
|
|
@Override
|
|
public void run() {
|
|
this.playerParticles.getManager(ParticleStyleManager.class).updateTimers();
|
|
|
|
this.hue += Setting.RAINBOW_CYCLE_SPEED.getInt();
|
|
this.hue %= 360;
|
|
|
|
if (this.hue % 4 == 0) { // Only increment note by 5 notes per second
|
|
this.note++;
|
|
this.note %= 25;
|
|
}
|
|
|
|
PermissionManager permissionManager = this.playerParticles.getManager(PermissionManager.class);
|
|
|
|
// Spawn particles for each player
|
|
for (PPlayer pplayer : this.particlePlayers.values()) {
|
|
Player player = pplayer.getPlayer();
|
|
|
|
// Don't show their particles if they are in spectator mode
|
|
// Don't spawn particles if the world doesn't allow it
|
|
if (player != null && player.getGameMode() != GameMode.SPECTATOR && permissionManager.isWorldEnabled(player.getWorld().getName()))
|
|
for (ParticlePair particles : pplayer.getActiveParticles())
|
|
this.displayParticles(pplayer, 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 (effect.getLocation().getWorld() != null && permissionManager.isWorldEnabled(effect.getLocation().getWorld().getName()))
|
|
this.displayFixedParticleEffect(effect);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Displays particles at the given player location with their settings
|
|
*
|
|
* @param pplayer The PPlayer to spawn the particles for
|
|
* @param particle The ParticlePair to use for getting particle settings
|
|
* @param location The location to display at
|
|
*/
|
|
private void displayParticles(PPlayer pplayer, ParticlePair particle, Location location) {
|
|
if (!this.playerParticles.getManager(ParticleStyleManager.class).isEventHandled(particle.getStyle())) {
|
|
if (Setting.TOGGLE_ON_COMBAT.getBoolean() && pplayer.isInCombat())
|
|
return;
|
|
|
|
List<PParticle> particles;
|
|
if (Setting.TOGGLE_ON_MOVE.getBoolean() && particle.getStyle().canToggleWithMovement() && pplayer.isMoving()) {
|
|
particles = DefaultStyles.FEET.getParticles(particle, location);
|
|
} else {
|
|
particles = particle.getStyle().getParticles(particle, location);
|
|
}
|
|
|
|
for (PParticle pparticle : particles)
|
|
this.displayParticles(particle, pparticle, particle.getStyle().hasLongRangeVisibility(), pplayer.getPlayer());
|
|
}
|
|
}
|
|
|
|
/**
|
|
* An alternative method used for event styles
|
|
*
|
|
* @param source The player the particles are spawning from, nullable for special cases
|
|
* @param world The world the particles are spawning in
|
|
* @param particle The ParticlePair to use for getting particle settings
|
|
* @param particles The particles to display
|
|
* @param isLongRange If the particle can be viewed from long range
|
|
*/
|
|
public void displayParticles(Player source, World world, ParticlePair particle, List<PParticle> particles, boolean isLongRange) {
|
|
PermissionManager permissionManager = this.playerParticles.getManager(PermissionManager.class);
|
|
if ((source != null && source.getGameMode() == GameMode.SPECTATOR) || !permissionManager.isWorldEnabled(world.getName()))
|
|
return;
|
|
|
|
for (PParticle pparticle : particles)
|
|
this.displayParticles(particle, pparticle, isLongRange, source);
|
|
}
|
|
|
|
/**
|
|
* Displays particles at the given fixed effect location
|
|
*
|
|
* @param fixedEffect The fixed effect to display
|
|
*/
|
|
private void displayFixedParticleEffect(FixedParticleEffect fixedEffect) {
|
|
ParticlePair particle = fixedEffect.getParticlePair();
|
|
for (PParticle pparticle : particle.getStyle().getParticles(particle, fixedEffect.getLocation().clone().add(0, particle.getStyle().getFixedEffectOffset(), 0)))
|
|
this.displayParticles(particle, pparticle, true, null);
|
|
}
|
|
|
|
/**
|
|
* The main internal method for spawning particles
|
|
*
|
|
* @param particle The ParticlePair to spawn
|
|
* @param pparticle The particle data to spawn with
|
|
* @param isLongRange true if the particle is viewable at long distances, otherwise false
|
|
* @param owner The owner of the particle, nullable for no online owner
|
|
*/
|
|
@SuppressWarnings("deprecation")
|
|
private void displayParticles(ParticlePair particle, PParticle pparticle, boolean isLongRange, Player owner) {
|
|
ParticleEffect effect = particle.getEffect();
|
|
int count = pparticle.isDirectional() ? 0 : 1;
|
|
|
|
Object data = null;
|
|
Location center;
|
|
float offsetX, offsetY, offsetZ;
|
|
if (effect.hasProperty(ParticleProperty.REQUIRES_BLOCK_DATA)) {
|
|
if (NMSUtil.getVersionNumber() >= 13) {
|
|
data = particle.getSpawnMaterial().createBlockData();
|
|
} else {
|
|
data = new MaterialData(particle.getSpawnMaterial());
|
|
}
|
|
|
|
center = pparticle.getLocation(false);
|
|
offsetX = pparticle.getOffsetX();
|
|
offsetY = pparticle.getOffsetY();
|
|
offsetZ = pparticle.getOffsetZ();
|
|
} else if (effect.hasProperty(ParticleProperty.REQUIRES_ITEM_DATA)) {
|
|
data = new ItemStack(particle.getSpawnMaterial());
|
|
center = pparticle.getLocation(false);
|
|
offsetX = pparticle.getOffsetX();
|
|
offsetY = pparticle.getOffsetY();
|
|
offsetZ = pparticle.getOffsetZ();
|
|
} else if (effect.hasProperty(ParticleProperty.REQUIRES_COLOR_DATA)) {
|
|
center = pparticle.getLocation(true);
|
|
|
|
ParticleColor color = particle.getSpawnColor();
|
|
if (effect == ParticleEffect.DUST && NMSUtil.getVersionNumber() >= 13) {
|
|
OrdinaryColor dustColor = (OrdinaryColor) color;
|
|
data = new DustOptions(org.bukkit.Color.fromRGB(dustColor.getRed(), dustColor.getGreen(), dustColor.getBlue()), Setting.DUST_SIZE.getFloat());
|
|
offsetX = 0;
|
|
offsetY = 0;
|
|
offsetZ = 0;
|
|
} else {
|
|
offsetX = effect == ParticleEffect.DUST && color.getValueX() == 0 ? Float.MIN_VALUE : color.getValueX();
|
|
offsetY = color.getValueY();
|
|
offsetZ = color.getValueZ();
|
|
}
|
|
} else {
|
|
center = pparticle.getLocation(false);
|
|
offsetX = pparticle.getOffsetX();
|
|
offsetY = pparticle.getOffsetY();
|
|
offsetZ = pparticle.getOffsetZ();
|
|
}
|
|
|
|
this.particleHandler.spawnParticle(effect, this.getPlayersInRange(center, isLongRange, owner), center, count, offsetX, offsetY, offsetZ, pparticle.getSpeed(), data);
|
|
}
|
|
|
|
/**
|
|
* Gets a List of Players within the particle display range
|
|
*
|
|
* @param center The center of the radius to check around
|
|
* @param isLongRange If the particle can be viewed from long range
|
|
* @param owner The player that owns the particles
|
|
* @return A List of Players within the particle display range
|
|
*/
|
|
private List<Player> getPlayersInRange(Location center, boolean isLongRange, Player owner) {
|
|
List<Player> players = new ArrayList<>();
|
|
int range = !isLongRange ? Setting.PARTICLE_RENDER_RANGE_PLAYER.getInt() : Setting.PARTICLE_RENDER_RANGE_FIXED_EFFECT.getInt();
|
|
range *= range;
|
|
|
|
for (PPlayer pplayer : PlayerParticles.getInstance().getManager(ParticleManager.class).getPPlayers()) {
|
|
Player p = pplayer.getPlayer();
|
|
if (!this.canSee(p, owner))
|
|
continue;
|
|
|
|
if (p != null && pplayer.canSeeParticles() && p.getWorld().equals(center.getWorld()) && center.distanceSquared(p.getLocation()) <= range)
|
|
players.add(p);
|
|
}
|
|
|
|
return players;
|
|
}
|
|
|
|
/**
|
|
* Checks if a player can see another player
|
|
*
|
|
* @param player The player
|
|
* @param target The target
|
|
* @return True if player can see target, otherwise false
|
|
*/
|
|
private boolean canSee(Player player, Player target) {
|
|
if (player == null || target == null)
|
|
return true;
|
|
|
|
for (MetadataValue meta : target.getMetadata("vanished"))
|
|
if (meta.asBoolean())
|
|
return false;
|
|
|
|
return player.canSee(target);
|
|
}
|
|
|
|
/**
|
|
* @return A sorted List of all enabled ParticleEffects
|
|
*/
|
|
public List<ParticleEffect> getEnabledEffects() {
|
|
return this.getEnabledEffectsStream()
|
|
.sorted(Comparator.comparing(x -> x.getValue().getName()))
|
|
.map(Entry::getKey)
|
|
.collect(Collectors.toList());
|
|
}
|
|
|
|
/**
|
|
* Gets the settings for a ParticleEffect
|
|
*
|
|
* @param particleEffect The ParticleEffect to get the settings for
|
|
* @return The settings for a ParticleEffect
|
|
*/
|
|
public ParticleEffectSettings getEffectSettings(ParticleEffect particleEffect) {
|
|
return this.supportedParticleEffects.get(particleEffect);
|
|
}
|
|
|
|
/**
|
|
* Returns the ParticleEffect with the given name
|
|
*
|
|
* @param name Name of the ParticleEffect
|
|
* @return The ParticleEffect, or null if not found
|
|
*/
|
|
public ParticleEffect getEffectFromName(String name) {
|
|
return this.getEnabledEffectsStream()
|
|
.filter(x -> x.getValue().getName().equalsIgnoreCase(name))
|
|
.findFirst()
|
|
.map(Entry::getKey)
|
|
.orElse(null);
|
|
}
|
|
|
|
/**
|
|
* Returns the ParticleEffect with the given internal name
|
|
*
|
|
* @param internalName Internal name of the particle effect
|
|
* @return The ParticleEffect, or null if not found
|
|
*/
|
|
public ParticleEffect getEffectFromInternalName(String internalName) {
|
|
return this.getEnabledEffectsStream()
|
|
.filter(x -> x.getValue().getInternalName().equalsIgnoreCase(internalName))
|
|
.findFirst()
|
|
.map(Entry::getKey)
|
|
.orElse(null);
|
|
}
|
|
|
|
/**
|
|
* @return A stream of enabled ParticleEffects
|
|
*/
|
|
private Stream<Entry<ParticleEffect, ParticleEffectSettings>> getEnabledEffectsStream() {
|
|
return this.supportedParticleEffects.entrySet().stream().filter(x -> x.getValue().isEnabled());
|
|
}
|
|
|
|
/**
|
|
* Gets the rainbow OrdinaryColor for particle spawning with data 'rainbow'
|
|
*
|
|
* @return The rainbow OrdinaryColor for particle spawning with data 'rainbow'
|
|
*/
|
|
public OrdinaryColor getRainbowParticleColor() {
|
|
Color rgb = Color.getHSBColor(this.hue / 360F, 1.0F, 1.0F);
|
|
return new OrdinaryColor(rgb.getRed(), rgb.getGreen(), rgb.getBlue());
|
|
}
|
|
|
|
/**
|
|
* Gets the rainbow NoteColor for particle spawning with data 'rainbow'
|
|
*
|
|
* @return The rainbow NoteColor for particle spawning with data 'rainbow'
|
|
*/
|
|
public NoteColor getRainbowNoteParticleColor() {
|
|
return new NoteColor(this.note);
|
|
}
|
|
|
|
/**
|
|
* Gets a randomized OrdinaryColor for particle spawning with data 'random'
|
|
*
|
|
* @return A randomized OrdinaryColor for particle spawning with data 'random'
|
|
*/
|
|
public OrdinaryColor getRandomParticleColor() {
|
|
Color rgb = new Color(this.random.nextInt(256), this.random.nextInt(256), this.random.nextInt(256));
|
|
return new OrdinaryColor(rgb.getRed(), rgb.getGreen(), rgb.getBlue());
|
|
}
|
|
|
|
/**
|
|
* Gets a randomized NoteColor for particle spawning with data 'random'
|
|
*
|
|
* @return A randomized NoteColor for particle spawning with data 'random'
|
|
*/
|
|
public NoteColor getRandomNoteParticleColor() {
|
|
return new NoteColor(this.random.nextInt(25));
|
|
}
|
|
|
|
}
|