commit 3bad8031f0fc2753dfc21204812537e9d2422401 Author: Marc Baloup Date: Sat Nov 22 17:18:10 2014 +0100 First commit diff --git a/.classpath b/.classpath new file mode 100644 index 0000000..b5367d3 --- /dev/null +++ b/.classpath @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bc12b62 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +bin/* +jar_export/* +lib/* +*Thumbs.db diff --git a/.project b/.project new file mode 100644 index 0000000..123eedd --- /dev/null +++ b/.project @@ -0,0 +1,17 @@ + + + PandacraftUtils + + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + + diff --git a/.settings/org.eclipse.core.resources.prefs b/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 0000000..99f26c0 --- /dev/null +++ b/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,2 @@ +eclipse.preferences.version=1 +encoding/=UTF-8 diff --git a/.settings/org.eclipse.core.runtime.prefs b/.settings/org.eclipse.core.runtime.prefs new file mode 100644 index 0000000..deae05a --- /dev/null +++ b/.settings/org.eclipse.core.runtime.prefs @@ -0,0 +1,2 @@ +eclipse.preferences.version=1 +line.separator=\r\n diff --git a/README.md b/README.md new file mode 100644 index 0000000..e69de29 diff --git a/resources/plugin.yml b/resources/plugin.yml new file mode 100644 index 0000000..6fe3efb --- /dev/null +++ b/resources/plugin.yml @@ -0,0 +1,130 @@ +name: PandacraftUtils +main: net.mc_pandacraft.java.plugin.pandacraftutils.PandacraftUtils +version: 2.1 + + + + + + +commands: + list: + description: Affiche des détails de chaques joueurs connectés + usage: /players [gm|ip|ping|uuid|loc|afk|spam] + aliases: [players, who, playerlist, online, plist] + permission: pandacraft.players + afk: + description: Active ou désactive le mode AFK du joueur + usage: /afk + aliases : [away] + permission: pandacraft.afk + setblock: + description: Change de block à un endroit précis + usage: /setblock [world] + permission: pandacraft.setblock + system: + description: Affiche des infos système + usage: /system [worlds|world |threads|tps_graph] + aliases : [syst, mem, memory, lag, gc, uptime, tps] + permission: pandacraft.system + ping: + description: Affiche votre ping avec le serveur + usage: /ping + permission: pandacraft.ping + me: + description: Envoi de messages prédéfinis + usage: /me + aliases : [action, describe] + permission: pandacraft.me + speedmessage: + description: Envoi de messages prédéfinis + usage: /mr [ list | [all|pl ] ] + aliases : [messagerapide, mr, speedmsg] + permission: pandacraft.speedmessage + /selection: + description: Active / désactive l'affichage du cubo + usage: //selection + aliases : [/select] + permission: pandacraft.selection + + + + + +permissions: + + pandacraft.setblock: + description: Utiliser la commande setblock + default: op + + pandacraft.players: + description: Utiliser la commande players-list + default: true + + pandacraft.players.details: + description: Utiliser la commande players avec des détails + default: op + + pandacraft.system: + description: Utiliser la commande system-gc-mem + default: op + + pandacraft.ping: + description: Utiliser la commande ping + default: true + + pandacraft.speedmessage: + description: Utiliser la commande speedmessage + default: false + + pandacraft.selection: + description: Utiliser la commande selection + default: true + + pandacraft.me: + description: Utiliser la commande me + default: true + + pandacraft.afk: + description: Utiliser la commande afk + default: true + pandacraft.afk.other: + description: Utiliser la commande afk sur un autre joueur + default: op + pandacraft.afk.exempt.kick: + description: Ne pas être kické si AFK trop longtemps + default: op + pandacraft.afk.exempt.commandcheck: + description: Ne pas sortir de AFK en exécutant une commande + default: false + pandacraft.afk.exempt.autoafk: + description: Ne pas être auto AFK + default: false + + + pandacraft.antispam.exempt: + description: Ignoré par le système anti-spam + default: op + + + pandacraft.grade.default: + description: Représente le grade Visiteur + default: false + pandacraft.grade.users: + description: Représente le grade Membre + default: false + pandacraft.grade.premium: + description: Représente le grade Premium + default: false + pandacraft.grade.ultimate: + description: Représente le grade Premium plus + default: false + pandacraft.grade.modos: + description: Représente le grade Modos + default: false + pandacraft.grade.admins: + description: Représente le grade Admins + default: false + pandacraft.grade.not_default: + description: Représente tout les joueurs inscrits + default: false \ No newline at end of file diff --git a/src/net/mc_pandacraft/java/plugin/pandacraftutils/ConfigManager.java b/src/net/mc_pandacraft/java/plugin/pandacraftutils/ConfigManager.java new file mode 100644 index 0000000..3ece9d1 --- /dev/null +++ b/src/net/mc_pandacraft/java/plugin/pandacraftutils/ConfigManager.java @@ -0,0 +1,74 @@ +package net.mc_pandacraft.java.plugin.pandacraftutils; + +import java.io.File; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/* + * Configuration du plugin + */ +public class ConfigManager { + private static ConfigManager instance; + + public static ConfigManager getInstance() { return instance; } + + + private PandacraftUtils plugin; + + public ConfigManager(File f, PandacraftUtils pl) { + plugin = pl; + instance = this; + + + initCommandAlias(); + } + + + + public int AFK_timeoutAutoAfkMessage = 60*5; // 5 min + public int AFK_timeoutAutoAfkKick = 60*10; // 10 min + + + public long AntiSpam_timeBeforeResendSameMessage = 30000;// 30 sec + public long AntiSpam_timeBeforeResendSameCommand = 30000;// 30 sec + public long AntiSpam_timePerCaracterForNewMessage = 100;// 0.1 sec + public int AntiSpam_maxViolationLevel = 20; + public int AntiSpam_nbSecondForOneVLDown = 10; + + public List> CommandAlias_alias; + + private void initCommandAlias() { + CommandAlias_alias = new ArrayList>(); + Map alias; + + + alias = new HashMap(); + alias.put("initialCommand", "/day"); + alias.put("replaceWith", "/ptime day"); + alias.put("permissions", "pandacraft.grade.users"); // multiple permission separated with semicolumn + CommandAlias_alias.add(alias); + + alias = new HashMap(); + alias.put("initialCommand", "/night"); + alias.put("replaceWith", "/ptime night"); + alias.put("permissions", "pandacraft.grade.users"); + CommandAlias_alias.add(alias); + + alias = new HashMap(); + alias.put("initialCommand", "/time"); + alias.put("replaceWith", "/ptime"); + alias.put("permissions", "pandacraft.grade.users"); + CommandAlias_alias.add(alias); + /* + Map alias = new HashMap(); + alias.put("initialCommand", ""); + alias.put("replaceWith", ""); + alias.put("permissions", ""); + CommandAlias_alias.add(alias); + */ + } + + +} diff --git a/src/net/mc_pandacraft/java/plugin/pandacraftutils/PandacraftUtils.java b/src/net/mc_pandacraft/java/plugin/pandacraftutils/PandacraftUtils.java new file mode 100644 index 0000000..610f965 --- /dev/null +++ b/src/net/mc_pandacraft/java/plugin/pandacraftutils/PandacraftUtils.java @@ -0,0 +1,83 @@ +package net.mc_pandacraft.java.plugin.pandacraftutils; + + +import net.mc_pandacraft.java.plugin.pandacraftutils.afk.CommandAfk; +import net.mc_pandacraft.java.plugin.pandacraftutils.antispam.AntispamManager; +import net.mc_pandacraft.java.plugin.pandacraftutils.creativ_cheat.CreativCheatManager; +import net.mc_pandacraft.java.plugin.pandacraftutils.list.CommandList; +import net.mc_pandacraft.java.plugin.pandacraftutils.me.CommandMe; +import net.mc_pandacraft.java.plugin.pandacraftutils.nopvp_protect.NoPvpProtectManager; +import net.mc_pandacraft.java.plugin.pandacraftutils.ping.CommandPing; +import net.mc_pandacraft.java.plugin.pandacraftutils.player_count_list.PacketOutServerInfoListener; +import net.mc_pandacraft.java.plugin.pandacraftutils.setblock.CommandSetblock; +import net.mc_pandacraft.java.plugin.pandacraftutils.spawntime.SpawnTimeManager; +import net.mc_pandacraft.java.plugin.pandacraftutils.speed_message.CommandSpeedMessage; +import net.mc_pandacraft.java.plugin.pandacraftutils.survival_cuboid.CommandWandSelection; +import net.mc_pandacraft.java.plugin.pandacraftutils.system.CommandSystem; + +import org.bukkit.plugin.java.JavaPlugin; + +public class PandacraftUtils extends JavaPlugin { + + public CommandAfk commandAfk; + public CommandList commandPlayers; + public CommandSetblock commandSetblock; + public CommandSystem commandSystem; + public CommandPing commandPing; + public CommandMe commandMe; + public CommandSpeedMessage commandSpeedMessage; + public CommandWandSelection commandWandSelection; + + + public SpawnTimeManager spawnTimeManager; + public AntispamManager antispamManager; + public CreativCheatManager creativCheatManager; + public NoPvpProtectManager noPvpProtectManager; + + public PacketOutServerInfoListener serverPingListener; + + + @Override + public void onEnable(){ + + // initialisation de la configuration + new ConfigManager(null, this); + + commandPlayers = new CommandList(this); + commandSetblock = new CommandSetblock(this); + commandAfk = new CommandAfk(this); + commandSystem = new CommandSystem(this); + commandPing = new CommandPing(this); + commandMe = new CommandMe(this); + commandSpeedMessage = new CommandSpeedMessage(this); // messages rapides + commandWandSelection = new CommandWandSelection(this); + + spawnTimeManager = new SpawnTimeManager(this); + antispamManager = new AntispamManager(this); + creativCheatManager = new CreativCheatManager(this); + noPvpProtectManager = new NoPvpProtectManager(this); + + serverPingListener = new PacketOutServerInfoListener(this); + } + + + @Override + public void onDisable(){ + commandPlayers = null; + commandSetblock = null; + commandAfk = null; + commandSystem = null; + commandPing = null; + commandMe = null; + commandSpeedMessage = null; + + spawnTimeManager = null; + antispamManager = null; + creativCheatManager = null; + noPvpProtectManager = null; + + serverPingListener = null; + } + +} + diff --git a/src/net/mc_pandacraft/java/plugin/pandacraftutils/afk/AfkPlayer.java b/src/net/mc_pandacraft/java/plugin/pandacraftutils/afk/AfkPlayer.java new file mode 100644 index 0000000..23ff9bd --- /dev/null +++ b/src/net/mc_pandacraft/java/plugin/pandacraftutils/afk/AfkPlayer.java @@ -0,0 +1,64 @@ +package net.mc_pandacraft.java.plugin.pandacraftutils.afk; + +import org.bukkit.entity.Player; + +public class AfkPlayer { + private Player player; + private long timeLastAction; + private boolean afk; + + + public AfkPlayer(Player p) + { + player = p; + afk = false; + timeLastAction = System.currentTimeMillis(); + } + + + public Player getPlayer() { return player; } + + public synchronized boolean isAfk() { return afk; } + + public synchronized void setAfk(boolean a) + { + if (afk == a) + return; + afk = a; + String message; + if (afk) + message = player.getDisplayName()+"§r est désormais AFK."; + else + message = player.getDisplayName()+"§r n'est plus AFK."; + player.getServer().broadcastMessage(message); + } + + public void performAfkCommand() + { + + + setAfk(!isAfk()); + if (!isAfk()) + isDoingAction(); + } + + public void adminToggleAfk() + { + setAfk(!isAfk()); + if (!isAfk()) + isDoingAction(); + } + + + + public double getDurationSinceLastAction() + { + return (System.currentTimeMillis() - timeLastAction)/1000D; + } + + public void isDoingAction() + { + timeLastAction = System.currentTimeMillis(); + setAfk(false); + } +} diff --git a/src/net/mc_pandacraft/java/plugin/pandacraftutils/afk/CommandAfk.java b/src/net/mc_pandacraft/java/plugin/pandacraftutils/afk/CommandAfk.java new file mode 100644 index 0000000..a952c2c --- /dev/null +++ b/src/net/mc_pandacraft/java/plugin/pandacraftutils/afk/CommandAfk.java @@ -0,0 +1,372 @@ +package net.mc_pandacraft.java.plugin.pandacraftutils.afk; + + +import net.mc_pandacraft.java.plugin.pandacraftutils.ConfigManager; +import net.mc_pandacraft.java.plugin.pandacraftutils.PandacraftUtils; + +import org.bukkit.Location; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.player.AsyncPlayerChatEvent; +import org.bukkit.event.player.PlayerCommandPreprocessEvent; +import org.bukkit.event.player.PlayerDropItemEvent; +import org.bukkit.event.player.PlayerEggThrowEvent; +import org.bukkit.event.player.PlayerInteractEntityEvent; +import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.event.player.PlayerJoinEvent; +import org.bukkit.event.player.PlayerMoveEvent; +import org.bukkit.event.player.PlayerQuitEvent; +import org.bukkit.event.player.PlayerTeleportEvent; +import org.bukkit.event.player.PlayerToggleFlightEvent; +import org.bukkit.event.player.PlayerToggleSneakEvent; +import org.bukkit.event.player.PlayerToggleSprintEvent; +import org.bukkit.scheduler.BukkitRunnable; + +public class CommandAfk extends BukkitRunnable implements CommandExecutor, Listener { + private PandacraftUtils plugin; + private AfkPlayer[] players; + + private int timeoutAutoAfkMessage = ConfigManager.getInstance().AFK_timeoutAutoAfkMessage; + private int timeoutAutoAfkKick = ConfigManager.getInstance().AFK_timeoutAutoAfkKick; + + + public CommandAfk(PandacraftUtils pl) + { + plugin = pl; + players = new AfkPlayer[plugin.getServer().getMaxPlayers()]; + + // analyse des joueurs déjà en ligne (/reload) + for (Player p : plugin.getServer().getOnlinePlayers()) + addPlayer(p); + + + plugin.getCommand("afk").setExecutor(this); + plugin.getServer().getScheduler().runTaskTimer(plugin, this, 5*20L, 20L); + plugin.getServer().getPluginManager().registerEvents(this, plugin); + + + } + + + + + + + + // ---------------------------------------- + // ------------ Commande /AFK ------------- + // ---------------------------------------- + @Override + public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) + { + boolean execute_self = false; + boolean execute_other = false; + Player other_player = null; + + if(sender instanceof Player) + { + if (args.length > 0) + { + if (((Player)sender).hasPermission("pandacraft.afk.other")) + { + if (plugin.getServer().getPlayer(args[0]) != null) + { + other_player = plugin.getServer().getPlayer(args[0]); + execute_other = true; + } + else + sender.sendMessage("§cLe joueur n'existe pas."); + } + else + { + sender.sendMessage("§cVous n'avez pas la permission d'effectuer cette action sur un autre joueur."); + } + } + else + execute_self = true; + } + else + { + if (args.length > 0) + { + if (plugin.getServer().getPlayer(args[0]) != null) + { + other_player = plugin.getServer().getPlayer(args[0]); + execute_other = true; + } + else + sender.sendMessage("§cLe joueur n'existe pas."); + } + else + sender.sendMessage("§cVeuillez indiquer un joueur."); + + } + + + if (execute_self) + { + Player p = (Player)sender; + AfkPlayer ap = getAfkPlayer(p); + if (ap == null) + plugin.getLogger().severe("Cant find AfkPlayer with name "+p.getName()+" when performing command /afk"); + else + ap.performAfkCommand(); + return true; + } + else if (execute_other && other_player != null) + { + AfkPlayer ap = getAfkPlayer(other_player); + if (ap == null) + plugin.getLogger().severe("Cant find AfkPlayer with name "+other_player.getName()+" when performing command /afk "); + ap.adminToggleAfk(); + return true; + } + + + + + + return false; + } + + + + + + + + + + + + + + // ---------------------------------------- + // ----------- Analyse Auto-AFK ----------- + // ---------------------------------------- + + @Override + public void run() { + // methode ex�cut� toute les secondes + for (AfkPlayer ap : players) + { // parcours de tout les joueurs + if (ap == null) + continue; + + + if (ap.getPlayer().hasPermission("pandacraft.afk.exempt.autoafk")) + continue; // les joueurs exemptés de l'auto-afk sont ignorés + + Player p = ap.getPlayer(); + + double timeElapsed = ap.getDurationSinceLastAction(); + + if (!ap.isAfk() && timeElapsed > timeoutAutoAfkMessage) + ap.setAfk(true); // on indique le changement d'état AFK de la personne + else if (ap.isAfk() && timeElapsed > timeoutAutoAfkKick && !p.hasPermission("pandacraft.afk.exempt.kick")) + p.kickPlayer("Vous avez été inactif pendant plus de "+Math.floor(timeoutAutoAfkKick/60)+" minutes"); + // on kick le joueur restant trop longtemps, si il n'est pas exempt� du kick + + + } + } + + + + + + + + + // ---------------------------------------- + // -------------- Ev�nements -------------- + // ---------------------------------------- + @EventHandler + public void onPlayerJoin (PlayerJoinEvent event) + { + addPlayer(event.getPlayer()); + } + + @EventHandler + public void onPlayerQuit (PlayerQuitEvent event) + { + removePlayer(event.getPlayer()); + } + + @EventHandler + public void onPlayerMove (PlayerMoveEvent event) + { + Location from = event.getFrom(); + Location to = event.getTo(); + + if ( (from.getPitch() == to.getPitch() && from.getYaw() == to.getYaw()) + || (from.getX() == to.getX() && from.getY() == to.getY() && from.getZ() == to.getZ())) + return; + getAfkPlayer(event.getPlayer()).isDoingAction(); + } + + + @EventHandler + public void onPlayerCommandPreprocess (PlayerCommandPreprocessEvent event) + { + if (event.getPlayer().hasPermission("pandacraft.afk.exempt.commandcheck")) + return; + + String[] split = event.getMessage().split(" "); + if (split.length > 0 && + ( + split[0].equalsIgnoreCase("/afk") + || split[0].equalsIgnoreCase("/away") + || split[0].equalsIgnoreCase("/mail") + || split[0].equalsIgnoreCase("/email") + || split[0].equalsIgnoreCase("/m") + || split[0].equalsIgnoreCase("/msg") + || split[0].equalsIgnoreCase("/t") + || split[0].equalsIgnoreCase("/tell") + || split[0].equalsIgnoreCase("/w") + || split[0].equalsIgnoreCase("/whisper") + || split[0].equalsIgnoreCase("/r") + || split[0].equalsIgnoreCase("/reply") + ) + ) + return; + + + getAfkPlayer(event.getPlayer()).isDoingAction(); + } + + @EventHandler + public void onPlayerInteract (PlayerInteractEvent event) + { + try + { + getAfkPlayer(event.getPlayer()).isDoingAction(); + } + catch (NullPointerException e) { } + } + + @EventHandler + public void onPlayerInteractEntity (PlayerInteractEntityEvent event) + { + try + { + getAfkPlayer(event.getPlayer()).isDoingAction(); + } + catch (NullPointerException e) { } + } + + @EventHandler + public void onPlayerTeleport (PlayerTeleportEvent event) + { + try + { + getAfkPlayer(event.getPlayer()).isDoingAction(); + } + catch (NullPointerException e) { } + } + + @EventHandler + public void onPlayerToggleSprint (PlayerToggleSprintEvent event) + { + try + { + getAfkPlayer(event.getPlayer()).isDoingAction(); + } + catch (NullPointerException e) { } + } + @EventHandler + public void onPlayerToggleFlight (PlayerToggleFlightEvent event) + { + try + { + getAfkPlayer(event.getPlayer()).isDoingAction(); + } + catch (NullPointerException e) { } + } + @EventHandler + public void onPlayerToggleSneak (PlayerToggleSneakEvent event) + { + try + { + getAfkPlayer(event.getPlayer()).isDoingAction(); + } + catch (NullPointerException e) { } + } + + @EventHandler + public void onAsyncPlayerChat (AsyncPlayerChatEvent event) + { + try + { + getAfkPlayer(event.getPlayer()).isDoingAction(); + } + catch (NullPointerException e) { } + } + + @EventHandler + public void onPlayerDropItem (PlayerDropItemEvent event) + { + try + { + getAfkPlayer(event.getPlayer()).isDoingAction(); + } + catch (NullPointerException e) { } + } + + @EventHandler + public void onPlayerEggThrow (PlayerEggThrowEvent event) + { + try + { + getAfkPlayer(event.getPlayer()).isDoingAction(); + } + catch (NullPointerException e) { } + } + + + + + + // ----------------------------------------- + // ---------- Gestion des donn�es ---------- + // ----------------------------------------- + public AfkPlayer getAfkPlayer(Player p) + { + for (AfkPlayer ap : players) + { + if (ap == null) + continue; + if (ap.getPlayer() == p) + return ap; + } + return null; + } + + + private void addPlayer(Player p) + { + int i=0; + while (players[i] != null && i= max_violation_level) + Bukkit.getScheduler().runTaskLater(plugin, new Runnable() { + @Override + public void run() { + player.kickPlayer("Ralentissez la cadance, les spams sont interdits !"); + } + }, 1L); + + + + + } + + private void removeVL(int nb) + { + violation_level -= nb; + if (violation_level < 0) violation_level = 0; + } + + + public int getVL() { return violation_level; } + public int getMaxVL() { return max_violation_level; } + + + public Player getPlayer() { return player; } + + + + public void onAsyncPlayerChat(AsyncPlayerChatEvent event) + { + if (event.getPlayer() != player) + return; + if (event.getPlayer().hasPermission("pandacraft.antispam.exempt")) + return; + String message = event.getMessage(); + long time = System.currentTimeMillis(); + if (last_message != null) + { + if (last_message.equals(message) && time - last_message_time < time_before_resend_same_message) + { + event.getPlayer().sendMessage(ChatColor.RED+"Evitez de renvoyer le même message !"); + if (violation_level >= max_violation_level/2) + event.setCancelled(true); + addVL(5); + return; + } + else + { + long time_since_last_message = time - last_message_time; + long timeout_needed = message.length() * time_per_caracter_for_new_message; + if (time_since_last_message < timeout_needed) + { + + event.getPlayer().sendMessage(ChatColor.RED+"Vous parlez un peu trop vite, ralentissez x)"); + addVL(4); + return; + } + } + } + + message = analyseString(message); + + event.setMessage(message); + + removeVL(NumberConversions.floor(((time - last_message_time)/1000)/nb_second_for_1_vl_down)); + + + last_message = message; + last_message_time = time; + } + + public void onPlayerCommandPreprocess (PlayerCommandPreprocessEvent event) + { + if (event.getPlayer() != player) + return; + if (event.getPlayer().hasPermission("pandacraft.antispam.exempt")) + return; + String[] command_line = event.getMessage().split(" "); + long time = System.currentTimeMillis(); + String commande = command_line[0].substring(1); + if (commande.equalsIgnoreCase("afk") + || commande.equalsIgnoreCase("away") + || commande.equalsIgnoreCase("time") + || commande.equalsIgnoreCase("day") + || commande.equalsIgnoreCase("night") + || commande.equalsIgnoreCase("me") + || commande.equalsIgnoreCase("action") + || commande.equalsIgnoreCase("describe") + || commande.equalsIgnoreCase("tpa") + || commande.equalsIgnoreCase("call") + || commande.equalsIgnoreCase("tpask") + || commande.equalsIgnoreCase("r") + || commande.equalsIgnoreCase("reply") + || commande.equalsIgnoreCase("msg") + || commande.equalsIgnoreCase("m") + || commande.equalsIgnoreCase("tell") + || commande.equalsIgnoreCase("t") + || commande.equalsIgnoreCase("whisper") + || commande.equalsIgnoreCase("w") + || commande.equalsIgnoreCase("mail") + || commande.equalsIgnoreCase("email")) + { + if (last_command != null) + { + if (last_command.equals(event.getMessage()) && time - last_command_time < time_before_resend_same_command) + { + event.getPlayer().sendMessage(ChatColor.RED+"Patientez avant de renvoyer cette commande !"); + if (violation_level >= max_violation_level/2) + event.setCancelled(true); + addVL(5); + return; + } + } + removeVL(NumberConversions.floor(((time - last_command_time)/1000)/nb_second_for_1_vl_down)); + + last_command = event.getMessage(); + last_command_time = time; + + } + + } + + + + + private String analyseString(String s) + { + + // �vite les suites d'au moins 4 caract�res � la suite + char[] cs = s.toCharArray(); + String ret = s.substring(0, (s.length()>=3)?3:s.length()); + int nb_duplicated_char = 0; + for (int i=3; i 0) + { + addVL(nb_duplicated_char/4+1); + player.sendMessage(ChatColor.RED+"Evitez les répétitions de caractères !"); + + } + + s = ret; + char[] sChar = s.toCharArray(); + char[] minChar = s.toLowerCase().toCharArray(); + + + // vérification des majuscules + if (sChar.length > 10) + { + int nb_caps = 0; + for (int i=0; i 70 || nb_caps > 20) + { + // si plus de 70% des caractères sont majuscules + // ou plus de 20 majuscules + addVL(4); + ret = s.toLowerCase(); + player.sendMessage(ChatColor.RED+"Il y a trop de majuscules dans votre message, faites attention avant d'envoyer votre message ;)"); + } + } + + return ret; + + } + + + + + + + + + + + + + + + + + +} diff --git a/src/net/mc_pandacraft/java/plugin/pandacraftutils/command_alias/CommandAliasManager.java b/src/net/mc_pandacraft/java/plugin/pandacraftutils/command_alias/CommandAliasManager.java new file mode 100644 index 0000000..95e984d --- /dev/null +++ b/src/net/mc_pandacraft/java/plugin/pandacraftutils/command_alias/CommandAliasManager.java @@ -0,0 +1,60 @@ +package net.mc_pandacraft.java.plugin.pandacraftutils.command_alias; + +import java.util.List; +import java.util.Map; + +import net.mc_pandacraft.java.plugin.pandacraftutils.ConfigManager; +import net.mc_pandacraft.java.plugin.pandacraftutils.PandacraftUtils; + +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.PlayerCommandPreprocessEvent; + +public class CommandAliasManager implements Listener { + private PandacraftUtils plugin; + + + public CommandAliasManager(PandacraftUtils pl) { + plugin = pl; + plugin.getServer().getPluginManager().registerEvents(this, plugin); + + + + } + + @EventHandler(priority=EventPriority.HIGHEST) + public void onPlayerCommandPreprocess (PlayerCommandPreprocessEvent event) + { + Player p = event.getPlayer(); + + List> aliases = ConfigManager.getInstance().CommandAlias_alias; + for (Map alias : aliases) { + if (alias == null) + continue; + String perm = alias.get("permissions"); + String initialCmd = alias.get("initialCommand"); + String aliasCmd = alias.get("replaceWith"); + if (perm == null || initialCmd == null || aliasCmd == null) + continue; + + if (!p.hasPermission(perm)) + continue; + + String mess = event.getMessage(); + + if(mess.toLowerCase().startsWith(initialCmd)) + mess = mess.replaceFirst(initialCmd, aliasCmd); + else + continue; + + event.setMessage(mess); + return; + } + + } + + + +} diff --git a/src/net/mc_pandacraft/java/plugin/pandacraftutils/creativ_cheat/CreativCheatManager.java b/src/net/mc_pandacraft/java/plugin/pandacraftutils/creativ_cheat/CreativCheatManager.java new file mode 100644 index 0000000..4b1fb02 --- /dev/null +++ b/src/net/mc_pandacraft/java/plugin/pandacraftutils/creativ_cheat/CreativCheatManager.java @@ -0,0 +1,31 @@ +package net.mc_pandacraft.java.plugin.pandacraftutils.creativ_cheat; + +import net.mc_pandacraft.java.plugin.pandacraftutils.PandacraftUtils; + +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerMoveEvent; + +public class CreativCheatManager implements Listener { + private PandacraftUtils plugin; + + public CreativCheatManager(PandacraftUtils pl) + { + plugin = pl; + plugin.getServer().getPluginManager().registerEvents(this, plugin); + } + + @EventHandler + public void onPlayerMove (PlayerMoveEvent e) + { + if (e.getTo().getY() > -60.0) + return; + + e.getPlayer().setFallDistance(0); + e.getPlayer().setHealth(20); + e.getPlayer().teleport(e.getTo().getWorld().getSpawnLocation()); + + plugin.getLogger().info("§7"+e.getPlayer().getDisplayName()+"§r teleported to §7"+e.getTo().getWorld().getName()+"§r's spawn to avoid a bug exploit"); + e.getPlayer().sendMessage("§dVous avez été téléporté au spawn de cette map car vous alliez tomber dans le vide."); + } +} diff --git a/src/net/mc_pandacraft/java/plugin/pandacraftutils/list/CommandList.java b/src/net/mc_pandacraft/java/plugin/pandacraftutils/list/CommandList.java new file mode 100644 index 0000000..9cf905d --- /dev/null +++ b/src/net/mc_pandacraft/java/plugin/pandacraftutils/list/CommandList.java @@ -0,0 +1,284 @@ +package net.mc_pandacraft.java.plugin.pandacraftutils.list; + +import java.util.ArrayList; + +import net.mc_pandacraft.java.plugin.pandacraftutils.PandacraftUtils; +import net.mc_pandacraft.java.plugin.pandacraftutils.afk.AfkPlayer; +import net.mc_pandacraft.java.util.TimeUtil; + +import org.bukkit.GameMode; +import org.bukkit.Server; +import org.bukkit.World; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.craftbukkit.v1_7_R1.entity.CraftPlayer; +import org.bukkit.entity.Player; + +public class CommandList implements CommandExecutor { + private PandacraftUtils plugin; + + public CommandList(PandacraftUtils pl) + { + plugin = pl; + plugin.getCommand("list").setExecutor(this); + } + + public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) + { + boolean execute = false; + boolean details = false; + if(sender instanceof Player) + { + if (((Player)sender).hasPermission("pandacraft.players")) + execute = true; + if (((Player)sender).hasPermission("pandacraft.players.details")) + details = true; + } + else + { + execute = true; + details = true; + } + + if (execute) + { + Server s = plugin.getServer(); + Player[] pls = plugin.getServer().getOnlinePlayers(); + + + + String decoration_color = "§6"; + String header = ""; + if (details && args.length > 0) + { + String arg = args[0].toLowerCase(); + if (arg.equals("loc") || arg.equals("world")) + header = "Localisation des joueurs"; + else if (arg.equals("uuid")) + header = "Identifiants des joueurs"; + else if (arg.equals("ip") || arg.equals("ping")) + header = "IP et ping des joueurs"; + else if (arg.equals("afk")) + header = "Status AFK des joueurs"; + else if (arg.equals("gm") || arg.equals("gamemode")) + header = "Mode de jeu des joueurs"; + else if (arg.equals("spam")) + header = "Niveau de spam des joueurs"; + } + else + { + header = "Liste des joueurs"; + } + + header = header.concat(" (§7"+pls.length+decoration_color+"/§7"+s.getMaxPlayers()+decoration_color+")"); + char decoration_motif = '-'; + + ArrayList aff_list = new ArrayList(); + + if (details && args.length > 0 && (args[0].toLowerCase().equals("loc") || args[0].toLowerCase().equals("world"))) + { + + for (World w : plugin.getServer().getWorlds()) + { + if (w.getPlayers().size() == 0) + continue; + + + // filtrage pour éviter d'afficher un monde vide (qui contiennent que des joueurs hors ligne) + boolean can_continue = false; + for (Player p : w.getPlayers()) + if (p != null && p.isOnline()) + { + can_continue = true; + break; + } + if (!can_continue) + continue; + // --------- + + + + aff_list.add(decoration_color+"Monde §7"+w.getName()); + + for (Player p : w.getPlayers()) + { + // filtrage pour éviter d'afficher des joueurs hors ligne + if (p == null || !p.isOnline()) + continue; + int x = p.getLocation().getBlockX(); + int y = p.getLocation().getBlockY(); + int z = p.getLocation().getBlockZ(); + String name = p.getDisplayName(); + aff_list.add(decoration_color+"- §f"+name+"§r - §7x="+x+" ; y="+y+" ; z="+z); + } + + } + + } + else if (details && args.length > 0) + { + for (Player p : pls) + { + if (p == null) + continue; + + + + + + String name = p.getDisplayName(); + + if (args.length > 0 && args[0].toLowerCase().equals("uuid")) + { + String uuid = p.getUniqueId().toString(); + + aff_list.add("§f"+name+"§r - §7"+uuid); + } + else if (args.length > 0 && (args[0].toLowerCase().equals("ip") || args[0].toLowerCase().equals("ping"))) + { + String ip = p.getAddress().getAddress().getHostAddress(); + + int ping = ((CraftPlayer)p).getHandle().ping; + + aff_list.add("§f"+name+"§r - §7"+ip+"§r - ping : §7"+ping); + } + else if (args.length > 0 && (args[0].toLowerCase().equals("spam"))) + { + try + { + int vl = plugin.antispamManager.getAPlayer(p).getVL(); + int max_vl = plugin.antispamManager.getAPlayer(p).getMaxVL(); + aff_list.add("§f"+name+"§r - §7"+vl+"/"+max_vl); + + } + catch (NullPointerException e) + { + aff_list.add("§cImpossible de récupérer le niveau de spam du joueur."); + } + } + else if (args.length > 0 && args[0].toLowerCase().equals("afk")) + { + AfkPlayer ap = plugin.commandAfk.getAfkPlayer(p); + boolean afk = (ap != null && ap.isAfk()); + double afk_time = (ap != null)?ap.getDurationSinceLastAction():-1; + String afkTime; + if (afk_time > 20) + afkTime = TimeUtil.durationToString((long)(afk_time*1000)); + else + afkTime = "Actif"; + + aff_list.add("§f"+name+""+((afk)?" §c[Afk]§r":"")+" §7"+afkTime+"§r"); + } + else if (args.length > 0 && (args[0].toLowerCase().equals("gm") || args[0].toLowerCase().equals("gamemode"))) + { + String world = p.getWorld().getName(); + double health = Math.rint(p.getHealth()*100)/100; + double eat = Math.rint(p.getFoodLevel()*100)/100; + boolean flyMode = p.getAllowFlight(); + String gm = (p.getGameMode() == GameMode.SURVIVAL)?("§CSurvie§r;vie:§7"+health+"§r;faim:§7"+eat+"§r"+((flyMode)?";§7canFly":"")) + :(p.getGameMode() == GameMode.CREATIVE)?"§bCréa§r" + :(p.getGameMode() == GameMode.ADVENTURE)?("§8Aventure§r;vie:§7"+health+"§r;faim:§7"+eat+"§r"+((flyMode)?";§7canFly":"")) + :"§oInconnu"; + String afk = (plugin.commandAfk.getAfkPlayer(p) != null && plugin.commandAfk.getAfkPlayer(p).isAfk())?" §c(Afk)§r":""; + aff_list.add("§f"+name+"§r"+afk+" - §7"+world+"§r - "+gm); + } + else + { + return false; + } + + } + } + else + { + String joueurs = ""; + boolean first = true; + for (Player p : pls) + { + if (p == null) + continue; + if (!first) + joueurs = joueurs.concat("§r, "); + else + first = false; + + joueurs = joueurs.concat(p.getDisplayName()); + + + } + + if (pls.length > 0) + { + aff_list.add(joueurs); + } + else + aff_list.add("Aucun joueur connecté"); + + } + + + int max_length = (header.length()-2*char_count(header, '§')) + 2; + max_length += 2; // évite le décalage de l'entête si il n'y a aucun trait à gauche + for (String line : aff_list) + max_length = (line != null && (line.length()-2*char_count(line, '§')) > max_length) ? (line.length()-2*char_count(line, '§')) : max_length; + + if (max_length > 70) max_length = 70; + + + long header_left_aff = Math.round(Math.floor((max_length - (header.length()-2*char_count(header, '§')) - 2)/2D)); + if (header_left_aff < 0) header_left_aff = 0; + long header_right_aff = Math.round(Math.ceil((max_length - (header.length()-2*char_count(header, '§')) - 2)/2D)); + if (header_right_aff < 0) header_right_aff = 0; + + String final_header = decoration_color; + String final_footer = decoration_color; + + for (int j=0; j pls = block_loc.getWorld().getPlayers(); + boolean player_nearby = false; + String nearby_pl_aff = ""; + // on fait le tour de toutes les entit§s de la map + for (Player pl : pls) + { + // on ne compte pas le poseur (sinon, on y arrivera pas x) ) + if (pl == p || pl.getGameMode() == GameMode.CREATIVE) + continue; + + Location ent_loc = pl.getLocation(); + if (ent_loc.distance(block_loc) < distance) + { // un joueur autre que celui qui a pos§ est trop proche + player_nearby = true; + nearby_pl_aff = nearby_pl_aff + " " + pl.getDisplayName(); + } + } + + if (player_nearby) + { + p.sendMessage("§dDemandez aux joueurs proche de vous de s'éloigner si vous voulez poser "+mat_name_aff+" :"+nearby_pl_aff); + loggerInfo("§7"+p.getName()+"§r attempted to place lava block or fire block in world §7"+block_loc.getWorld().getName()+"§r near to player(s) :§7" + nearby_pl_aff); + event.setCancelled(true); + } + + } + } + + @EventHandler + public void onPlayerBucketEmpty(PlayerBucketEmptyEvent event) { + if (event.getBlockClicked().getWorld().getPVP()) + return; + + if (event.getBucket() != null && event.getBucket() == Material.LAVA_BUCKET) + { // on fait l'analyse pour le placement du bloc de lave + double distance = lava_distance; + + Player p = event.getPlayer(); + Location block_loc = event.getBlockClicked().getLocation(); + List pls = block_loc.getWorld().getPlayers(); + boolean player_nearby = false; + String nearby_pl_aff = ""; + // on fait le tour de toutes les entit§s de la map + for (Player pl : pls) + { + // on ne compte pas le poseur (sinon, on y arrivera pas x) ) + if (pl == p || pl.getGameMode() == GameMode.CREATIVE) + continue; + + + Location ent_loc = pl.getLocation(); + if (ent_loc.distance(block_loc) < distance) + { // un joueur autre que celui qui a pos§ est trop proche + player_nearby = true; + nearby_pl_aff = nearby_pl_aff + " " + pl.getDisplayName(); + } + } + + if (player_nearby) + { + p.sendMessage("§dDemandez aux joueurs proche de vous de s'éloigner si vous voulez poser de la lave :"+nearby_pl_aff); + loggerInfo("§7"+p.getName()+"§r attempted to place lava in world §7"+block_loc.getWorld().getName()+"§r near to player(s) :§7" + nearby_pl_aff); + event.setCancelled(true); + } + } + } + + @EventHandler + public void onBlockIgnit(BlockIgniteEvent event) { + if (event.getPlayer() == null) + return; + + if (event.getBlock().getWorld().getPVP()) + return; + + if (event.getCause() == IgniteCause.FIREBALL || event.getCause() == IgniteCause.FLINT_AND_STEEL) + { // on fait l'analyse pour l'allumage avec un briquet + double distance = fire_distance; + + Player p = event.getPlayer(); + Location block_loc = event.getBlock().getLocation(); + List pls = block_loc.getWorld().getPlayers(); + boolean player_nearby = false; + String nearby_pl_aff = ""; + // on fait le tour de tout les joueurs de la map + for (Player pl : pls) + { + // on ne compte pas le poseur (sinon, on y arrivera pas x) ) + if (pl == p || pl.getGameMode() == GameMode.CREATIVE) + continue; + + + Location ent_loc = pl.getLocation(); + if (ent_loc.distance(block_loc) < distance) + { // un joueur autre que celui qui a pos§ est trop proche + player_nearby = true; + nearby_pl_aff = nearby_pl_aff + " " + pl.getDisplayName(); + } + } + + if (player_nearby) + { + p.sendMessage("§dDemandez aux joueurs proche de vous de s'éloigner si vous voulez poser du feu :"+nearby_pl_aff); + loggerInfo("§7"+p.getName()+"§r attempted to place fire in world §7"+block_loc.getWorld().getName()+"§r near to player(s) :§7" + nearby_pl_aff); + event.setCancelled(true); + } + } + } + + @EventHandler + public void onBlockBreak(BlockBreakEvent event) + { + if (event.getBlock().getWorld().getPVP()) + return; + + Player p = event.getPlayer(); + Block b = event.getBlock(); + + List pls = b.getWorld().getPlayers(); + + boolean player_standing = false; + String nearby_pl_aff = ""; + // on fait le tour de tout les joueurs de la map + for (Player pl : pls) + { + // on ne compte pas le poseur (sinon, on y arrivera pas x) ) et on ignorent ceux qui fly + if (pl == p || pl.isFlying()) + continue; + + + Location pl_loc = pl.getLocation().getBlock().getLocation(); + Location pl_loc_under = new Location(pl_loc.getWorld(), pl_loc.getX(), pl_loc.getY()-1, pl_loc.getZ()); + Location pl_loc_over = new Location(pl_loc.getWorld(), pl_loc.getX(), pl_loc.getY()+1, pl_loc.getZ()); + if (pl_loc.equals(b.getLocation()) + || pl_loc_under.equals(b.getLocation()) + || pl_loc_over.equals(b.getLocation())) + { // un joueur autre que celui qui a pos§ est trop proche + player_standing = true; + nearby_pl_aff = nearby_pl_aff + " " + pl.getDisplayName(); + } + } + + if (player_standing) + { + p.sendMessage("§dDemandez à ces joueurs de se déplacer si vous voulez casser ce bloc :"+nearby_pl_aff); + loggerInfo("§7"+p.getName()+"§r attempted to brake block in world §7"+b.getWorld().getName()+"§r under player(s) :§7" + nearby_pl_aff); + event.setCancelled(true); + } + } + + + @EventHandler + public void onBlockFromTo(BlockFromToEvent event) + { + if (event.getBlock().getWorld().getPVP()) + return; + + Block bf = event.getBlock(); + Block bt = event.getToBlock(); + Location loc_bt = bt.getLocation(); + + List pls = bf.getWorld().getPlayers(); + + boolean player_standing = false; + String nearby_pl_aff = ""; + // on fait le tour de tout les joueurs de la map + for (Player pl : pls) + { + // on ignore ceux en cr§atif + if (pl.getGameMode() == GameMode.CREATIVE) + continue; + + + Location pl_loc = pl.getLocation().getBlock().getLocation(); + + + + // si la distance par rapport au joueur courant est > 5 bloc, on ignore + // c'est pour optimiser car les calculs qui suits semblent onéreuses en temps de calcul x) + if (pl_loc.distanceSquared(loc_bt) > 5*5) + continue; + + + + + + + Location pl_loc_floor = new Location(pl_loc.getWorld(), Math.floor(pl.getLocation().getX()), Math.floor(pl.getLocation().getY()), Math.floor(pl.getLocation().getZ())); + + Location pl_loc_inner_block = pl.getLocation().subtract(pl_loc_floor); + + + int x_min = (int)Math.round(pl_loc.getX())-((pl_loc_inner_block.getX()<=0.3)?1:0); + int y_min = (int)Math.round(pl_loc.getY())-1; + int z_min = (int)Math.round(pl_loc.getZ())-((pl_loc_inner_block.getZ()<=0.3)?1:0); + + int x_max = (int)Math.round(pl_loc.getX())+((pl_loc_inner_block.getX()>=0.7)?1:0); + int y_max = (int)Math.round(pl_loc.getY())+((pl_loc_inner_block.getY()>=0.2)?2:1); + int z_max = (int)Math.round(pl_loc.getZ())+((pl_loc_inner_block.getZ()>=0.7)?1:0); + for (int x = x_min; x <= x_max; x++) + for (int y = y_min; y <= y_max; y++) + for (int z = z_min; z <= z_max; z++) + if ((new Location(pl_loc.getWorld(), x, y, z)).equals(loc_bt)) + { + player_standing = true; + nearby_pl_aff = nearby_pl_aff + " " + pl.getDisplayName(); + } + + } + + if (player_standing) + { + event.setCancelled(true); + } + } +} + diff --git a/src/net/mc_pandacraft/java/plugin/pandacraftutils/ping/CommandPing.java b/src/net/mc_pandacraft/java/plugin/pandacraftutils/ping/CommandPing.java new file mode 100644 index 0000000..2b53daf --- /dev/null +++ b/src/net/mc_pandacraft/java/plugin/pandacraftutils/ping/CommandPing.java @@ -0,0 +1,57 @@ +package net.mc_pandacraft.java.plugin.pandacraftutils.ping; + + +import net.mc_pandacraft.java.plugin.pandacraftutils.PandacraftUtils; + +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.command.ConsoleCommandSender; +import org.bukkit.craftbukkit.v1_7_R1.entity.CraftPlayer; +import org.bukkit.entity.Player; + +public class CommandPing implements CommandExecutor { + + private PandacraftUtils plugin; + + public CommandPing(PandacraftUtils pl) + { + plugin = pl; + + plugin.getCommand("ping").setExecutor(this); + } + + @Override + public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) + { + + if (sender instanceof Player) + { + try + { + int ping = ((CraftPlayer)(Player)sender).getHandle().ping; + sender.sendMessage(ChatColor.LIGHT_PURPLE+"Votre ping est de §7"+ping+" ms"+ChatColor.LIGHT_PURPLE+"."); + } + catch (Exception e) + { + sender.sendMessage(ChatColor.RED+"Impossible de récupérer votre ping"); + e.printStackTrace(); + } + return true; + } + else if (sender instanceof ConsoleCommandSender) + { + plugin.getServer().dispatchCommand(sender, "list ping"); + } + + + + + + + + return true; + } + +} diff --git a/src/net/mc_pandacraft/java/plugin/pandacraftutils/player_count_list/PacketOutServerInfoListener.java b/src/net/mc_pandacraft/java/plugin/pandacraftutils/player_count_list/PacketOutServerInfoListener.java new file mode 100644 index 0000000..5e2276b --- /dev/null +++ b/src/net/mc_pandacraft/java/plugin/pandacraftutils/player_count_list/PacketOutServerInfoListener.java @@ -0,0 +1,105 @@ +package net.mc_pandacraft.java.plugin.pandacraftutils.player_count_list; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.bukkit.entity.Player; + +import com.comphenix.protocol.PacketType; +import com.comphenix.protocol.ProtocolLibrary; +import com.comphenix.protocol.events.ListenerOptions; +import com.comphenix.protocol.events.ListenerPriority; +import com.comphenix.protocol.events.PacketAdapter; +import com.comphenix.protocol.events.PacketEvent; +import com.comphenix.protocol.wrappers.WrappedGameProfile; + +import net.mc_pandacraft.java.plugin.pandacraftutils.PandacraftUtils; + +public class PacketOutServerInfoListener { + private PandacraftUtils plugin; + + + public PacketOutServerInfoListener(PandacraftUtils pl) { + plugin = pl; + + ProtocolLibrary + .getProtocolManager() + .addPacketListener( + new PacketAdapter( + plugin, + ListenerPriority.NORMAL, + Arrays.asList(new PacketType[] { PacketType.Status.Server.OUT_SERVER_INFO }), + new ListenerOptions[] { ListenerOptions.ASYNC }) + { + + @Override + public void onPacketSending(PacketEvent event) + { + Player[] pl_list = PacketOutServerInfoListener.this.plugin.getServer().getOnlinePlayers(); + + + List plAdmin = new ArrayList(); + List plModo = new ArrayList(); + List plPlayerUltimate = new ArrayList(); + List plPlayerPremium = new ArrayList(); + List plPlayer = new ArrayList(); + + for (Player p : pl_list) + { + if (p != null && p.isOnline()) + { + if (p.hasPermission("pandacraft.grade.admins")) + plAdmin.add(p); + else if (p.hasPermission("pandacraft.grade.modos")) + plModo.add(p); + else if (p.hasPermission("pandacraft.grade.ultimate")) + plPlayerUltimate.add(p); + else if (p.hasPermission("pandacraft.grade.premium")) + plPlayerPremium.add(p); + else + plPlayer.add(p); + } + + } + + List list = new ArrayList(); + + if (!plAdmin.isEmpty() || !plModo.isEmpty()) + { + list.add(new WrappedGameProfile("", "§cStaff connecté :")); + for (Player p : plAdmin) + { + list.add(new WrappedGameProfile("", " - "+p.getDisplayName())); + } + for (Player p : plModo) + { + list.add(new WrappedGameProfile("", " - "+p.getDisplayName())); + } + } + if (!plPlayer.isEmpty() || !plPlayerPremium.isEmpty() || !plPlayerUltimate.isEmpty()) + { + list.add(new WrappedGameProfile("", "§6Joueurs connectés :")); + + for (Player p : plPlayerUltimate) + { + list.add(new WrappedGameProfile("", " - "+p.getDisplayName())); + } + for (Player p : plPlayerPremium) + { + list.add(new WrappedGameProfile("", " - "+p.getDisplayName())); + } + for (Player p : plPlayer) + { + list.add(new WrappedGameProfile("", " - "+p.getDisplayName())); + } + } + + + event.getPacket().getServerPings().read(0).setPlayers(list); + } + } + ); + + } +} diff --git a/src/net/mc_pandacraft/java/plugin/pandacraftutils/protocol/ParticleEffect.java b/src/net/mc_pandacraft/java/plugin/pandacraftutils/protocol/ParticleEffect.java new file mode 100644 index 0000000..234ab27 --- /dev/null +++ b/src/net/mc_pandacraft/java/plugin/pandacraftutils/protocol/ParticleEffect.java @@ -0,0 +1,793 @@ +package net.mc_pandacraft.java.plugin.pandacraftutils.protocol; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.entity.Player; + +/** + * ParticleEffect Library + *

+ * This library was created by @DarkBlade12 based on content related to particles of @microgeek (names and packet parameters), it allows you to display all Minecraft particle effects on a Bukkit server + *

+ * You are welcome to use it, modify it and redistribute it under the following conditions: + *

    + *
  • Don't claim this class as your own + *
  • Don't remove this disclaimer + *
+ *

+ * It would be nice if you provide credit to me if you use this class in a published project + * + * @author DarkBlade12 + * @version 1.5 + */ +public enum ParticleEffect { + /** + * A particle effect which is displayed by exploding tnt and creepers: + *

    + *
  • It looks like a crowd of gray balls which are fading away + *
  • The speed value has no influence on this particle effect + *
+ */ + HUGE_EXPLOSION("hugeexplosion"), + /** + * A particle effect which is displayed by exploding ghast fireballs and wither skulls: + *
    + *
  • It looks like a gray ball which is fading away + *
  • The speed value slightly influences the size of this particle effect + *
+ */ + LARGE_EXPLODE("largeexplode"), + /** + * A particle effect which is displayed by launching fireworks: + *
    + *
  • It looks like a white star which is sparkling + *
  • The speed value influences the velocity at which the particle flies off + *
+ */ + FIREWORKS_SPARK("fireworksSpark"), + /** + * A particle effect which is displayed by swimming entities and arrows in water: + *
    + *
  • It looks like a bubble + *
  • The speed value influences the velocity at which the particle flies off + *
+ */ + BUBBLE("bubble", true), + /** + * A particle effect which is displayed by water: + *
    + *
  • It looks like a tiny blue square + *
  • The speed value has no influence on this particle effect + *
+ */ + SUSPEND("suspend", true), + /** + * A particle effect which is displayed by air when close to bedrock and the in the void: + *
    + *
  • It looks like a tiny gray square + *
  • The speed value has no influence on this particle effect + *
+ */ + DEPTH_SUSPEND("depthSuspend"), + /** + * A particle effect which is displayed by mycelium: + *
    + *
  • It looks like a tiny gray square + *
  • The speed value has no influence on this particle effect + *
+ */ + TOWN_AURA("townaura"), + /** + * A particle effect which is displayed when landing a critical hit and by arrows: + *
    + *
  • It looks like a light brown cross + *
  • The speed value influences the velocity at which the particle flies off + *
+ */ + CRIT("crit"), + /** + * A particle effect which is displayed when landing a hit with an enchanted weapon: + *
    + *
  • It looks like a cyan star + *
  • The speed value influences the velocity at which the particle flies off + *
+ */ + MAGIC_CRIT("magicCrit"), + /** + * A particle effect which is displayed by primed tnt, torches, droppers, dispensers, end portals, brewing stands and monster spawners: + *
    + *
  • It looks like a little gray cloud + *
  • The speed value influences the velocity at which the particle flies off + *
+ */ + SMOKE("smoke"), + /** + * A particle effect which is displayed by entities with active potion effects: + *
    + *
  • It looks like a colored swirl + *
  • The speed value causes the particle to be colored black when set to 0 + *
+ */ + MOB_SPELL("mobSpell"), + /** + * A particle effect which is displayed by entities with active potion effects applied through a beacon: + *
    + *
  • It looks like a transparent colored swirl + *
  • The speed value causes the particle to be always colored black when set to 0 + *
+ */ + MOB_SPELL_AMBIENT("mobSpellAmbient"), + /** + * A particle effect which is displayed when splash potions or bottles o' enchanting hit something: + *
    + *
  • It looks like a white swirl + *
  • The speed value causes the particle to only move upwards when set to 0 + *
+ */ + SPELL("spell"), + /** + * A particle effect which is displayed when instant splash potions hit something: + *
    + *
  • It looks like a white cross + *
  • The speed value causes the particle to only move upwards when set to 0 + *
+ */ + INSTANT_SPELL("instantSpell"), + /** + * A particle effect which is displayed by witches: + *
    + *
  • It looks like a purple cross + *
  • The speed value causes the particle to only move upwards when set to 0 + *
+ */ + WITCH_MAGIC("witchMagic"), + /** + * A particle effect which is displayed by note blocks: + *
    + *
  • It looks like a colored note + *
  • The speed value causes the particle to be colored green when set to 0 + *
+ */ + NOTE("note"), + /** + * A particle effect which is displayed by nether portals, endermen, ender pearls, eyes of ender, ender chests and dragon eggs: + *
    + *
  • It looks like a purple cloud + *
  • The speed value influences the spread of this particle effect + *
+ */ + PORTAL("portal"), + /** + * A particle effect which is displayed by enchantment tables which are nearby bookshelves: + *
    + *
  • It looks like a cryptic white letter + *
  • The speed value influences the spread of this particle effect + *
+ */ + ENCHANTMENT_TABLE("enchantmenttable"), + /** + * A particle effect which is displayed by exploding tnt and creepers: + *
    + *
  • It looks like a white cloud + *
  • The speed value influences the velocity at which the particle flies off + *
+ */ + EXPLODE("explode"), + /** + * A particle effect which is displayed by torches, active furnaces, magma cubes and monster spawners: + *
    + *
  • It looks like a tiny flame + *
  • The speed value influences the velocity at which the particle flies off + *
+ */ + FLAME("flame"), + /** + * A particle effect which is displayed by lava: + *
    + *
  • It looks like a spark + *
  • The speed value has no influence on this particle effect + *
+ */ + LAVA("lava"), + /** + * A particle effect which is currently unused: + *
    + *
  • It looks like a transparent gray square + *
  • The speed value has no influence on this particle effect + *
+ */ + FOOTSTEP("footstep"), + /** + * A particle effect which is displayed by swimming entities, rain dropping on the ground and shaking wolves: + *
    + *
  • It looks like a blue drop + *
  • The speed value has no influence on this particle effect + *
+ */ + SPLASH("splash"), + /** + * A particle effect which is displayed on water when fishing: + *
    + *
  • It looks like a blue droplet + *
  • The speed value influences the velocity at which the particle flies off + *
+ */ + WAKE("wake"), + /** + * A particle effect which is displayed by fire, minecarts with furnace and blazes: + *
    + *
  • It looks like a large gray cloud + *
  • The speed value influences the velocity at which the particle flies off + *
+ */ + LARGE_SMOKE("largesmoke"), + /** + * A particle effect which is displayed when a mob dies: + *
    + *
  • It looks like a large white cloud + *
  • The speed value influences the velocity at which the particle flies off + *
+ */ + CLOUD("cloud"), + /** + * A particle effect which is displayed by redstone ore, powered redstone, redstone torches and redstone repeaters: + *
    + *
  • It looks like a tiny colored cloud + *
  • The speed value causes the particle to be colored red when set to 0 + *
+ */ + RED_DUST("reddust"), + /** + * A particle effect which is displayed when snowballs or eggs hit something: + *
    + *
  • It looks like a tiny part of the snowball icon + *
  • The speed value has no influence on this particle effect + *
+ */ + SNOWBALL_POOF("snowballpoof"), + /** + * A particle effect which is displayed by blocks beneath a water source: + *
    + *
  • It looks like a blue drip + *
  • The speed value has no influence on this particle effect + *
+ */ + DRIP_WATER("dripWater"), + /** + * A particle effect which is displayed by blocks beneath a lava source: + *
    + *
  • It looks like an orange drip + *
  • The speed value has no influence on this particle effect + *
+ */ + DRIP_LAVA("dripLava"), + /** + * A particle effect which is currently unused: + *
    + *
  • It looks like a tiny white cloud + *
  • The speed value influences the velocity at which the particle flies off + *
+ */ + SNOW_SHOVEL("snowshovel"), + /** + * A particle effect which is displayed by slimes: + *
    + *
  • It looks like a tiny part of the slimeball icon + *
  • The speed value has no influence on this particle effect + *
+ */ + SLIME("slime"), + /** + * A particle effect which is displayed when breeding and taming animals: + *
    + *
  • It looks like a red heart + *
  • The speed value has no influence on this particle effect + *
+ */ + HEART("heart"), + /** + * A particle effect which is displayed when attacking a villager in a village: + *
    + *
  • It looks like a cracked gray heart + *
  • The speed value has no influence on this particle effect + *
+ */ + ANGRY_VILLAGER("angryVillager"), + /** + * A particle effect which is displayed when using bone meal and trading with a villager in a village: + *
    + *
  • It looks like a green star + *
  • The speed value has no influence on this particle effect + *
+ */ + HAPPY_VILLAGER("happyVillager"); + + private static final Map NAME_MAP = new HashMap(); + private final String name; + private final boolean requiresWater; + + // Initialize map for quick name lookup + static { + for (ParticleEffect effect : values()) { + NAME_MAP.put(effect.name, effect); + } + } + + /** + * Construct a new particle effect + * + * @param name Name of this particle effect + * @param requiresWater Indicates whether water is required for this particle effect to display properly + */ + private ParticleEffect(String name, boolean requiresWater) { + this.name = name; + this.requiresWater = requiresWater; + } + + /** + * Construct a new particle effect with {@link #requiresWater} set to false + * + * @param name Name of this particle effect + * @see #ParticleEffect(String, boolean) + */ + private ParticleEffect(String name) { + this(name, false); + } + + /** + * Returns the name of this particle effect + * + * @return The name + */ + public String getName() { + return name; + } + + /** + * Determine if water is required for this particle effect to display properly + * + * @return Whether water is required or not + */ + public boolean getRequiresWater() { + return requiresWater; + } + + /** + * Returns the particle effect with the given name + * + * @param name Name of the particle effect + * @return The particle effect + */ + public static ParticleEffect fromName(String name) { + for (Entry entry : NAME_MAP.entrySet()) { + if (!entry.getKey().equalsIgnoreCase(name)) { + continue; + } + return entry.getValue(); + } + return null; + } + + /** + * Determine if water is at a certain location + * + * @param location Location to check + * @return Whether water is at this location or not + */ + private static boolean isWater(Location location) { + Material material = location.getBlock().getType(); + return material == Material.WATER || material == Material.STATIONARY_WATER; + } + + /** + * Determine if an id is a block id + * + * @param id Id to check + * @return Whether id is a block or not + */ + @SuppressWarnings("deprecation") + private static boolean isBlock(int id) { + Material material = Material.getMaterial(id); + return material != null && material.isBlock(); + } + + /** + * Displays a particle effect which is only visible for all players within a certain range in the world of @param center + * + * @param offsetX Maximum distance particles can fly away from the center on the x-axis + * @param offsetY Maximum distance particles can fly away from the center on the y-axis + * @param offsetZ Maximum distance particles can fly away from the center on the z-axis + * @param speed Display speed of the particles + * @param amount Amount of particles + * @param center Center location of the effect + * @param range Range of the visibility (Maximum range for particles is usually 16, but it can differ for some types) + * @throws IllegalArgumentException If the particle effect requires water and none is at the center location + * @see ParticleEffectPacket + * @see ParticleEffectPacket#sendTo(Location, double) + */ + public void display(float offsetX, float offsetY, float offsetZ, float speed, int amount, Location center, double range) throws IllegalArgumentException { + if (requiresWater && !isWater(center)) { + throw new IllegalArgumentException("There is no water at the center location"); + } + new ParticleEffectPacket(name, offsetX, offsetY, offsetZ, speed, amount).sendTo(center, range); + } + + /** + * Displays a particle effect which is only visible for the specified players + * + * @param offsetX Maximum distance particles can fly away from the center on the x-axis + * @param offsetY Maximum distance particles can fly away from the center on the y-axis + * @param offsetZ Maximum distance particles can fly away from the center on the z-axis + * @param speed Display speed of the particles + * @param amount Amount of particles + * @param center Center location of the effect + * @param players Receivers of the effect + * @throws IllegalArgumentException If the particle effect requires water and none is at the center location + * @see ParticleEffectPacket + * @see ParticleEffectPacket#sendTo(Location, List) + */ + public void display(float offsetX, float offsetY, float offsetZ, float speed, int amount, Location center, List players) throws IllegalArgumentException { + if (requiresWater && !isWater(center)) { + throw new IllegalArgumentException("There is no water at the center location"); + } + new ParticleEffectPacket(name, offsetX, offsetY, offsetZ, speed, amount).sendTo(center, players); + } + + /** + * Displays an icon crack (item break) particle effect which is only visible for all players within a certain range in the world of @param center + * + * @param id Id of the icon + * @param data Data value + * @param offsetX Maximum distance particles can fly away from the center on the x-axis + * @param offsetY Maximum distance particles can fly away from the center on the y-axis + * @param offsetZ Maximum distance particles can fly away from the center on the z-axis + * @param speed Display speed of the particles + * @param amount Amount of particles + * @param center Center location of the effect + * @param range Range of the visibility (Maximum range for particles is usually 16, but it can differ for some types) + * @see ParticleEffectPacket + * @see ParticleEffectPacket#sendTo(Location, double) + */ + public static void displayIconCrack(int id, byte data, float offsetX, float offsetY, float offsetZ, float speed, int amount, Location center, double range) { + new ParticleEffectPacket("iconcrack_" + id + "_" + data, offsetX, offsetY, offsetZ, speed, amount).sendTo(center, range); + } + + /** + * Displays an icon crack (item break) particle effect which is only visible for the specified players + * + * @param id Id of the icon + * @param data Data value + * @param offsetX Maximum distance particles can fly away from the center on the x-axis + * @param offsetY Maximum distance particles can fly away from the center on the y-axis + * @param offsetZ Maximum distance particles can fly away from the center on the z-axis + * @param speed Display speed of the particles + * @param amount Amount of particles + * @param center Center location of the effect + * @param players Receivers of the effect + * @see ParticleEffectPacket + * @see ParticleEffectPacket#sendTo(Location, List) + */ + public static void displayIconCrack(int id, byte data, float offsetX, float offsetY, float offsetZ, float speed, int amount, Location center, List players) { + new ParticleEffectPacket("iconcrack_" + id + "_" + data, offsetX, offsetY, offsetZ, speed, amount).sendTo(center, players); + } + + /** + * Displays a block crack (block break) particle effect which is only visible for all players within a certain range in the world of @param center + * + * @param id Id of the block + * @param data Data value + * @param offsetX Maximum distance particles can fly away from the center on the x-axis + * @param offsetY Maximum distance particles can fly away from the center on the y-axis + * @param offsetZ Maximum distance particles can fly away from the center on the z-axis + * @param amount Amount of particles + * @param center Center location of the effect + * @param range Range of the visibility (Maximum range for particles is usually 16, but it can differ for some types) + * @throws IllegalArgumentException If the specified id is not a block id + * @see ParticleEffectPacket + * @see ParticleEffectPacket#sendTo(Location, double) + */ + public static void displayBlockCrack(int id, byte data, float offsetX, float offsetY, float offsetZ, int amount, Location center, double range) throws IllegalArgumentException { + if (!isBlock(id)) { + throw new IllegalArgumentException("Invalid block id"); + } + new ParticleEffectPacket("blockcrack_" + id + "_" + data, offsetX, offsetY, offsetZ, 0, amount).sendTo(center, range); + } + + /** + * Displays a block crack (block break) particle effect which is only visible for the specified players + * + * @param id Id of the block + * @param data Data value + * @param offsetX Maximum distance particles can fly away from the center on the x-axis + * @param offsetY Maximum distance particles can fly away from the center on the y-axis + * @param offsetZ Maximum distance particles can fly away from the center on the z-axis + * @param amount Amount of particles + * @param center Center location of the effect + * @param players Receivers of the effect + * @throws IllegalArgumentException If the specified id is not a block id + * @see ParticleEffectPacket + * @see ParticleEffectPacket#sendTo(Location, List) + */ + public static void displayBlockCrack(int id, byte data, float offsetX, float offsetY, float offsetZ, int amount, Location center, List players) throws IllegalArgumentException { + if (!isBlock(id)) { + throw new IllegalArgumentException("Invalid block id"); + } + new ParticleEffectPacket("blockcrack_" + id + "_" + data, offsetX, offsetY, offsetZ, 0, amount).sendTo(center, players); + } + + /** + * Displays a block dust particle effect which is only visible for all players within a certain range in the world of @param center + * + * @param id Id of the block + * @param data Data value + * @param offsetX Maximum distance particles can fly away from the center on the x-axis + * @param offsetY Maximum distance particles can fly away from the center on the y-axis + * @param offsetZ Maximum distance particles can fly away from the center on the z-axis + * @param speed Display speed of the particles + * @param amount Amount of particles + * @param center Center location of the effect + * @param range Range of the visibility (Maximum range for particles is usually 16, but it can differ for some types) + * @throws IllegalArgumentException If the specified id is not a block id + * @see ParticleEffectPacket + * @see ParticleEffectPacket#sendTo(Location, double) + */ + public static void displayBlockDust(int id, byte data, float offsetX, float offsetY, float offsetZ, float speed, int amount, Location center, double range) throws IllegalArgumentException { + if (!isBlock(id)) { + throw new IllegalArgumentException("Invalid block id"); + } + new ParticleEffectPacket("blockdust_" + id + "_" + data, offsetX, offsetY, offsetZ, speed, amount).sendTo(center, range); + } + + /** + * Displays a block dust particle effect which is only visible for the specified players + * + * @param id Id of the block + * @param data Data value + * @param offsetX Maximum distance particles can fly away from the center on the x-axis + * @param offsetY Maximum distance particles can fly away from the center on the y-axis + * @param offsetZ Maximum distance particles can fly away from the center on the z-axis + * @param speed Display speed of the particles + * @param amount Amount of particles + * @param center Center location of the effect + * @param players Receivers of the effect + * @throws IllegalArgumentException If the specified id is not a block id + * @see ParticleEffectPacket + * @see ParticleEffectPacket#sendTo(Location, List) + */ + public static void displayBlockDust(int id, byte data, float offsetX, float offsetY, float offsetZ, float speed, int amount, Location center, List players) throws IllegalArgumentException { + if (!isBlock(id)) { + throw new IllegalArgumentException("Invalid block id"); + } + new ParticleEffectPacket("blockdust_" + id + "_" + data, offsetX, offsetY, offsetZ, speed, amount).sendTo(center, players); + } + + /** + * Represents a particle effect packet with all attributes which is used for sending packets to the players + *

+ * This class is part of the ParticleEffect Library and follows the same usage conditions + * + * @author DarkBlade12 + * @since 1.5 + */ + public static final class ParticleEffectPacket { + private static Constructor packetConstructor; + private static Method getHandle; + private static Field playerConnection; + private static Method sendPacket; + private static boolean initialized; + private final String name; + private final float offsetX; + private final float offsetY; + private final float offsetZ; + private final float speed; + private final int amount; + private Object packet; + + /** + * Construct a new particle effect packet + * + * @param name Name of the effect + * @param offsetX Maximum distance particles can fly away from the center on the x-axis + * @param offsetY Maximum distance particles can fly away from the center on the y-axis + * @param offsetZ Maximum distance particles can fly away from the center on the z-axis + * @param speed Display speed of the particles + * @param amount Amount of particles + * @throws IllegalArgumentException If the speed is lower than 0 or the amount is lower than 1 + * @see #initialize() + */ + public ParticleEffectPacket(String name, float offsetX, float offsetY, float offsetZ, float speed, int amount) throws IllegalArgumentException { + initialize(); + if (speed < 0) { + throw new IllegalArgumentException("The speed is lower than 0"); + } + if (amount < 1) { + throw new IllegalArgumentException("The amount is lower than 1"); + } + this.name = name; + this.offsetX = offsetX; + this.offsetY = offsetY; + this.offsetZ = offsetZ; + this.speed = speed; + this.amount = amount; + } + + /** + * Initializes {@link #packetConstructor}, {@link #getHandle}, {@link #playerConnection} and {@link #sendPacket} and sets {@link #initialized} to true if it succeeds + *

+ * Note: These fields only have to be initialized once, so it will return if {@link #initialized} is already set to true + * + * @throws VersionIncompatibleException if accessed packets, fields or methods differ in your bukkit version + */ + public static void initialize() throws VersionIncompatibleException { + if (initialized) { + return; + } + try { + int version = Integer.parseInt(Character.toString(ReflectionUtils.PackageType.getServerVersion().charAt(3))); + Class packetClass = ReflectionUtils.PackageType.MINECRAFT_SERVER.getClass(version < 7 ? "Packet63WorldParticles" : ReflectionUtils.PacketType.PLAY_OUT_WORLD_PARTICLES.getName()); + packetConstructor = ReflectionUtils.getConstructor(packetClass); + getHandle = ReflectionUtils.getMethod("CraftPlayer", ReflectionUtils.PackageType.CRAFTBUKKIT_ENTITY, "getHandle"); + playerConnection = ReflectionUtils.getField("EntityPlayer", ReflectionUtils.PackageType.MINECRAFT_SERVER, false, "playerConnection"); + sendPacket = ReflectionUtils.getMethod(playerConnection.getType(), "sendPacket", ReflectionUtils.PackageType.MINECRAFT_SERVER.getClass("Packet")); + } catch (Exception exception) { + throw new VersionIncompatibleException("Your current bukkit version seems to be incompatible with this library", exception); + } + initialized = true; + } + + /** + * Determine if {@link #packetConstructor}, {@link #getHandle}, {@link #playerConnection} and {@link #sendPacket} are initialized + * + * @return Whether these fields are initialized or not + * @see #initialize() + */ + public static boolean isInitialized() { + return initialized; + } + + /** + * Sends the packet to a single player and caches it + * + * @param center Center location of the effect + * @param player Receiver of the packet + * @throws PacketInstantiationException if instantion fails due to an unknown error + * @throws PacketSendingException if sending fails due to an unknown error + */ + public void sendTo(Location center, Player player) throws PacketInstantiationException, PacketSendingException { + if (packet == null) { + try { + packet = packetConstructor.newInstance(); + ReflectionUtils.setValue(packet, true, "a", name); + ReflectionUtils.setValue(packet, true, "b", (float) center.getX()); + ReflectionUtils.setValue(packet, true, "c", (float) center.getY()); + ReflectionUtils.setValue(packet, true, "d", (float) center.getZ()); + ReflectionUtils.setValue(packet, true, "e", offsetX); + ReflectionUtils.setValue(packet, true, "f", offsetY); + ReflectionUtils.setValue(packet, true, "g", offsetZ); + ReflectionUtils.setValue(packet, true, "h", speed); + ReflectionUtils.setValue(packet, true, "i", amount); + } catch (Exception exception) { + throw new PacketInstantiationException("Packet instantiation failed", exception); + } + } + try { + sendPacket.invoke(playerConnection.get(getHandle.invoke(player)), packet); + } catch (Exception exception) { + throw new PacketSendingException("Failed to send the packet to player '" + player.getName() + "'", exception); + } + } + + /** + * Sends the packet to all players in the list + * + * @param center Center location of the effect + * @param players Receivers of the packet + * @throws IllegalArgumentException If the player list is empty + * @see #sendTo(Location center, Player player) + */ + public void sendTo(Location center, List players) throws IllegalArgumentException { + if (players.isEmpty()) { + throw new IllegalArgumentException("The player list is empty"); + } + for (Player player : players) { + sendTo(center, player); + } + } + + /** + * Sends the packet to all players in a certain range + * + * @param center Center location of the effect + * @param range Range in which players will receive the packet (Maximum range for particles is usually 16, but it can differ for some types) + * @throws IllegalArgumentException If the range is lower than 1 + * @see #sendTo(Location center, Player player) + */ + public void sendTo(Location center, double range) throws IllegalArgumentException { + if (range < 1) { + throw new IllegalArgumentException("The range is lower than 1"); + } + String worldName = center.getWorld().getName(); + double squared = range * range; + for (Player player : Bukkit.getOnlinePlayers()) { + if (!player.getWorld().getName().equals(worldName) || player.getLocation().distanceSquared(center) > squared) { + continue; + } + sendTo(center, player); + } + } + + /** + * Represents a runtime exception that is thrown if a bukkit version is not compatible with this library + *

+ * This class is part of the ParticleEffect Library and follows the same usage conditions + * + * @author DarkBlade12 + * @since 1.5 + */ + private static final class VersionIncompatibleException extends RuntimeException { + private static final long serialVersionUID = 3203085387160737484L; + + /** + * Construct a new version incompatible exception + * + * @param message Message that will be logged + * @param cause Cause of the exception + */ + public VersionIncompatibleException(String message, Throwable cause) { + super(message, cause); + } + } + + /** + * Represents a runtime exception that is thrown if packet instantiation fails + *

+ * This class is part of the ParticleEffect Library and follows the same usage conditions + * + * @author DarkBlade12 + * @since 1.4 + */ + private static final class PacketInstantiationException extends RuntimeException { + private static final long serialVersionUID = 3203085387160737484L; + + /** + * Construct a new packet instantiation exception + * + * @param message Message that will be logged + * @param cause Cause of the exception + */ + public PacketInstantiationException(String message, Throwable cause) { + super(message, cause); + } + } + + /** + * Represents a runtime exception that is thrown if packet sending fails + *

+ * This class is part of the ParticleEffect Library and follows the same usage conditions + * + * @author DarkBlade12 + * @since 1.4 + */ + private static final class PacketSendingException extends RuntimeException { + private static final long serialVersionUID = 3203085387160737484L; + + /** + * Construct a new packet sending exception + * + * @param message Message that will be logged + * @param cause Cause of the exception + */ + public PacketSendingException(String message, Throwable cause) { + super(message, cause); + } + } + } +} \ No newline at end of file diff --git a/src/net/mc_pandacraft/java/plugin/pandacraftutils/protocol/ReflectionUtils.java b/src/net/mc_pandacraft/java/plugin/pandacraftutils/protocol/ReflectionUtils.java new file mode 100644 index 0000000..a4ad83e --- /dev/null +++ b/src/net/mc_pandacraft/java/plugin/pandacraftutils/protocol/ReflectionUtils.java @@ -0,0 +1,758 @@ +package net.mc_pandacraft.java.plugin.pandacraftutils.protocol; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.Map; + +import org.bukkit.Bukkit; + +/** + * ReflectionUtils + *

+ * This class provides useful methods which makes dealing with reflection much easier, especially when working with Bukkit + *

+ * You are welcome to use it, modify it and redistribute it under the following conditions: + *

    + *
  • Don't claim this class as your own + *
  • Don't remove this disclaimer + *
+ *

+ * It would be nice if you provide credit to me if you use this class in a published project + * + * @author DarkBlade12 + * @version 1.1 + */ +public final class ReflectionUtils { + // Prevent accidential construction + private ReflectionUtils() {} + + /** + * Returns the constructor of a given class with the given parameter types + * + * @param clazz Target class + * @param parameterTypes Parameter types of the desired constructor + * @return The constructor of the target class with the specified parameter types + * @throws NoSuchMethodException If the desired constructor with the specified parameter types cannot be found + * @see DataType + * @see DataType#getPrimitive(Class[]) + * @see DataType#compare(Class[], Class[]) + */ + public static Constructor getConstructor(Class clazz, Class... parameterTypes) throws NoSuchMethodException { + Class[] primitiveTypes = DataType.getPrimitive(parameterTypes); + for (Constructor constructor : clazz.getConstructors()) { + if (!DataType.compare(DataType.getPrimitive(constructor.getParameterTypes()), primitiveTypes)) { + continue; + } + return constructor; + } + throw new NoSuchMethodException("There is no such constructor in this class with the specified parameter types"); + } + + /** + * Returns the constructor of a desired class with the given parameter types + * + * @param className Name of the desired target class + * @param packageType Package where the desired target class is located + * @param parameterTypes Parameter types of the desired constructor + * @return The constructor of the desired target class with the specified parameter types + * @throws NoSuchMethodException If the desired constructor with the specified parameter types cannot be found + * @throws ClassNotFoundException ClassNotFoundException If the desired target class with the specified name and package cannot be found + * @see #getClass(String, PackageType) + * @see #getConstructor(Class, Class...) + */ + public static Constructor getConstructor(String className, PackageType packageType, Class... parameterTypes) throws NoSuchMethodException, ClassNotFoundException { + return getConstructor(packageType.getClass(className), parameterTypes); + } + + /** + * Returns an instance of a class with the given arguments + * + * @param clazz Target class + * @param arguments Arguments which are used to construct an object of the target class + * @return The instance of the target class with the specified arguments + * @throws InstantiationException If you cannot create an instance of the target class due to certain circumstances + * @throws IllegalAccessException If the desired constructor cannot be accessed due to certain circumstances + * @throws IllegalArgumentException If the types of the arguments do not match the parameter types of the constructor (this should not occur since it searches for a constructor with the types of the arguments) + * @throws InvocationTargetException If the desired constructor cannot be invoked + * @throws NoSuchMethodException If the desired constructor with the specified arguments cannot be found + */ + public static Object instantiateObject(Class clazz, Object... arguments) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException { + return getConstructor(clazz, DataType.getPrimitive(arguments)).newInstance(arguments); + } + + /** + * Returns an instance of a desired class with the given arguments + * + * @param className Name of the desired target class + * @param packageType Package where the desired target class is located + * @param arguments Arguments which are used to construct an object of the desired target class + * @return The instance of the desired target class with the specified arguments + * @throws InstantiationException If you cannot create an instance of the desired target class due to certain circumstances + * @throws IllegalAccessException If the desired constructor cannot be accessed due to certain circumstances + * @throws IllegalArgumentException If the types of the arguments do not match the parameter types of the constructor (this should not occur since it searches for a constructor with the types of the arguments) + * @throws InvocationTargetException If the desired constructor cannot be invoked + * @throws NoSuchMethodException If the desired constructor with the specified arguments cannot be found + * @throws ClassNotFoundException If the desired target class with the specified name and package cannot be found + * @see #getClass(String, PackageType) + * @see #instantiateObject(Class, Object...) + */ + public static Object instantiateObject(String className, PackageType packageType, Object... arguments) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, ClassNotFoundException { + return instantiateObject(packageType.getClass(className), arguments); + } + + /** + * Returns a method of a class with the given parameter types + * + * @param clazz Target class + * @param methodName Name of the desired method + * @param parameterTypes Parameter types of the desired method + * @return The method of the target class with the specified name and parameter types + * @throws NoSuchMethodException If the desired method of the target class with the specified name and parameter types cannot be found + * @see DataType#getPrimitive(Class[]) + * @see DataType#compare(Class[], Class[]) + */ + public static Method getMethod(Class clazz, String methodName, Class... parameterTypes) throws NoSuchMethodException { + Class[] primitiveTypes = DataType.getPrimitive(parameterTypes); + for (Method method : clazz.getMethods()) { + if (!method.getName().equals(methodName) || !DataType.compare(DataType.getPrimitive(method.getParameterTypes()), primitiveTypes)) { + continue; + } + return method; + } + throw new NoSuchMethodException("There is no such method in this class with the specified name and parameter types"); + } + + /** + * Returns a method of a desired class with the given parameter types + * + * @param className Name of the desired target class + * @param packageType Package where the desired target class is located + * @param methodName Name of the desired method + * @param parameterTypes Parameter types of the desired method + * @return The method of the desired target class with the specified name and parameter types + * @throws NoSuchMethodException If the desired method of the desired target class with the specified name and parameter types cannot be found + * @throws ClassNotFoundException If the desired target class with the specified name and package cannot be found + * @see #getClass(String, PackageType) + * @see #getMethod(Class, String, Class...) + */ + public static Method getMethod(String className, PackageType packageType, String methodName, Class... parameterTypes) throws NoSuchMethodException, ClassNotFoundException { + return getMethod(packageType.getClass(className), methodName, parameterTypes); + } + + /** + * Invokes a method on an object with the given arguments + * + * @param instance Target object + * @param methodName Name of the desired method + * @param arguments Arguments which are used to invoke the desired method + * @return The result of invoking the desired method on the target object + * @throws IllegalAccessException If the desired method cannot be accessed due to certain circumstances + * @throws IllegalArgumentException If the types of the arguments do not match the parameter types of the method (this should not occur since it searches for a method with the types of the arguments) + * @throws InvocationTargetException If the desired method cannot be invoked on the target object + * @throws NoSuchMethodException If the desired method of the class of the target object with the specified name and arguments cannot be found + * @see #getMethod(Class, String, Class...) + * @see DataType#getPrimitive(Object[]) + */ + public static Object invokeMethod(Object instance, String methodName, Object... arguments) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException { + return getMethod(instance.getClass(), methodName, DataType.getPrimitive(arguments)).invoke(instance, arguments); + } + + /** + * Invokes a method of the target class on an object with the given arguments + * + * @param instance Target object + * @param clazz Target class + * @param methodName Name of the desired method + * @param arguments Arguments which are used to invoke the desired method + * @return The result of invoking the desired method on the target object + * @throws IllegalAccessException If the desired method cannot be accessed due to certain circumstances + * @throws IllegalArgumentException If the types of the arguments do not match the parameter types of the method (this should not occur since it searches for a method with the types of the arguments) + * @throws InvocationTargetException If the desired method cannot be invoked on the target object + * @throws NoSuchMethodException If the desired method of the target class with the specified name and arguments cannot be found + * @see #getMethod(Class, String, Class...) + * @see DataType#getPrimitive(Object[]) + */ + public static Object invokeMethod(Object instance, Class clazz, String methodName, Object... arguments) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException { + return getMethod(clazz, methodName, DataType.getPrimitive(arguments)).invoke(instance, arguments); + } + + /** + * Invokes a method of a desired class on an object with the given arguments + * + * @param instance Target object + * @param className Name of the desired target class + * @param packageType Package where the desired target class is located + * @param methodName Name of the desired method + * @param arguments Arguments which are used to invoke the desired method + * @return The result of invoking the desired method on the target object + * @throws IllegalAccessException If the desired method cannot be accessed due to certain circumstances + * @throws IllegalArgumentException If the types of the arguments do not match the parameter types of the method (this should not occur since it searches for a method with the types of the arguments) + * @throws InvocationTargetException If the desired method cannot be invoked on the target object + * @throws NoSuchMethodException If the desired method of the desired target class with the specified name and arguments cannot be found + * @throws ClassNotFoundException If the desired target class with the specified name and package cannot be found + * @see #getClass(String, PackageType) + * @see #invokeMethod(Object, Class, String, Object...) + */ + public static Object invokeMethod(Object instance, String className, PackageType packageType, String methodName, Object... arguments) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, ClassNotFoundException { + return invokeMethod(instance, packageType.getClass(className), methodName, arguments); + } + + /** + * Returns a field of the target class with the given name + * + * @param clazz Target class + * @param declared Whether the desired field is declared or not + * @param fieldName Name of the desired field + * @return The field of the target class with the specified name + * @throws NoSuchFieldException If the desired field of the given class cannot be found + * @throws SecurityException If the desired field cannot be made accessible + */ + public static Field getField(Class clazz, boolean declared, String fieldName) throws NoSuchFieldException, SecurityException { + Field field = declared ? clazz.getDeclaredField(fieldName) : clazz.getField(fieldName); + field.setAccessible(true); + return field; + } + + /** + * Returns a field of a desired class with the given name + * + * @param className Name of the desired target class + * @param packageType Package where the desired target class is located + * @param declared Whether the desired field is declared or not + * @param fieldName Name of the desired field + * @return The field of the desired target class with the specified name + * @throws NoSuchFieldException If the desired field of the desired class cannot be found + * @throws SecurityException If the desired field cannot be made accessible + * @throws ClassNotFoundException If the desired target class with the specified name and package cannot be found + * @see #getField(Class, boolean, String) + */ + public static Field getField(String className, PackageType packageType, boolean declared, String fieldName) throws NoSuchFieldException, SecurityException, ClassNotFoundException { + return getField(packageType.getClass(className), declared, fieldName); + } + + /** + * Returns the value of a field of the given class of an object + * + * @param instance Target object + * @param clazz Target class + * @param declared Whether the desired field is declared or not + * @param fieldName Name of the desired field + * @return The value of field of the target object + * @throws IllegalArgumentException If the target object does not feature the desired field + * @throws IllegalAccessException If the desired field cannot be accessed + * @throws NoSuchFieldException If the desired field of the target class cannot be found + * @throws SecurityException If the desired field cannot be made accessible + * @see #getField(Class, boolean, String) + */ + public static Object getValue(Object instance, Class clazz, boolean declared, String fieldName) throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException { + return getField(clazz, declared, fieldName).get(instance); + } + + /** + * Returns the value of a field of a desired class of an object + * + * @param instance Target object + * @param className Name of the desired target class + * @param packageType Package where the desired target class is located + * @param declared Whether the desired field is declared or not + * @param fieldName Name of the desired field + * @return The value of field of the target object + * @throws IllegalArgumentException If the target object does not feature the desired field + * @throws IllegalAccessException If the desired field cannot be accessed + * @throws NoSuchFieldException If the desired field of the desired class cannot be found + * @throws SecurityException If the desired field cannot be made accessible + * @throws ClassNotFoundException If the desired target class with the specified name and package cannot be found + * @see #getValue(Object, Class, boolean, String) + */ + public static Object getValue(Object instance, String className, PackageType packageType, boolean declared, String fieldName) throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException, ClassNotFoundException { + return getValue(instance, packageType.getClass(className), declared, fieldName); + } + + /** + * Returns the value of a field with the given name of an object + * + * @param instance Target object + * @param declared Whether the desired field is declared or not + * @param fieldName Name of the desired field + * @return The value of field of the target object + * @throws IllegalArgumentException If the target object does not feature the desired field (should not occur since it searches for a field with the given name in the class of the object) + * @throws IllegalAccessException If the desired field cannot be accessed + * @throws NoSuchFieldException If the desired field of the target object cannot be found + * @throws SecurityException If the desired field cannot be made accessible + * @see #getValue(Object, Class, boolean, String) + */ + public static Object getValue(Object instance, boolean declared, String fieldName) throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException { + return getValue(instance, instance.getClass(), declared, fieldName); + } + + /** + * Sets the value of a field of the given class of an object + * + * @param instance Target object + * @param clazz Target class + * @param declared Whether the desired field is declared or not + * @param fieldName Name of the desired field + * @param value New value + * @throws IllegalArgumentException If the type of the value does not match the type of the desired field + * @throws IllegalAccessException If the desired field cannot be accessed + * @throws NoSuchFieldException If the desired field of the target class cannot be found + * @throws SecurityException If the desired field cannot be made accessible + * @see #getField(Class, boolean, String) + */ + public static void setValue(Object instance, Class clazz, boolean declared, String fieldName, Object value) throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException { + getField(clazz, declared, fieldName).set(instance, value); + } + + /** + * Sets the value of a field of a desired class of an object + * + * @param instance Target object + * @param className Name of the desired target class + * @param packageType Package where the desired target class is located + * @param declared Whether the desired field is declared or not + * @param fieldName Name of the desired field + * @param value New value + * @throws IllegalArgumentException If the type of the value does not match the type of the desired field + * @throws IllegalAccessException If the desired field cannot be accessed + * @throws NoSuchFieldException If the desired field of the desired class cannot be found + * @throws SecurityException If the desired field cannot be made accessible + * @throws ClassNotFoundException If the desired target class with the specified name and package cannot be found + * @see #setValue(Object, Class, boolean, String, Object) + */ + public static void setValue(Object instance, String className, PackageType packageType, boolean declared, String fieldName, Object value) throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException, ClassNotFoundException { + setValue(instance, packageType.getClass(className), declared, fieldName, value); + } + + /** + * Sets the value of a field with the given name of an object + * + * @param instance Target object + * @param declared Whether the desired field is declared or not + * @param fieldName Name of the desired field + * @param value New value + * @throws IllegalArgumentException If the type of the value does not match the type of the desired field + * @throws IllegalAccessException If the desired field cannot be accessed + * @throws NoSuchFieldException If the desired field of the target object cannot be found + * @throws SecurityException If the desired field cannot be made accessible + * @see #setValue(Object, Class, boolean, String, Object) + */ + public static void setValue(Object instance, boolean declared, String fieldName, Object value) throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException { + setValue(instance, instance.getClass(), declared, fieldName, value); + } + + /** + * Represents an enumeration of dynamic packages of NMS and CraftBukkit + *

+ * This class is part of the ReflectionUtils and follows the same usage conditions + * + * @author DarkBlade12 + * @since 1.0 + */ + public enum PackageType { + MINECRAFT_SERVER("net.minecraft.server." + getServerVersion()), + CRAFTBUKKIT("org.bukkit.craftbukkit." + getServerVersion()), + CRAFTBUKKIT_BLOCK(CRAFTBUKKIT, "block"), + CRAFTBUKKIT_CHUNKIO(CRAFTBUKKIT, "chunkio"), + CRAFTBUKKIT_COMMAND(CRAFTBUKKIT, "command"), + CRAFTBUKKIT_CONVERSATIONS(CRAFTBUKKIT, "conversations"), + CRAFTBUKKIT_ENCHANTMENS(CRAFTBUKKIT, "enchantments"), + CRAFTBUKKIT_ENTITY(CRAFTBUKKIT, "entity"), + CRAFTBUKKIT_EVENT(CRAFTBUKKIT, "event"), + CRAFTBUKKIT_GENERATOR(CRAFTBUKKIT, "generator"), + CRAFTBUKKIT_HELP(CRAFTBUKKIT, "help"), + CRAFTBUKKIT_INVENTORY(CRAFTBUKKIT, "inventory"), + CRAFTBUKKIT_MAP(CRAFTBUKKIT, "map"), + CRAFTBUKKIT_METADATA(CRAFTBUKKIT, "metadata"), + CRAFTBUKKIT_POTION(CRAFTBUKKIT, "potion"), + CRAFTBUKKIT_PROJECTILES(CRAFTBUKKIT, "projectiles"), + CRAFTBUKKIT_SCHEDULER(CRAFTBUKKIT, "scheduler"), + CRAFTBUKKIT_SCOREBOARD(CRAFTBUKKIT, "scoreboard"), + CRAFTBUKKIT_UPDATER(CRAFTBUKKIT, "updater"), + CRAFTBUKKIT_UTIL(CRAFTBUKKIT, "util"); + + private final String path; + + /** + * Construct a new package type + * + * @param path Path of the package + */ + private PackageType(String path) { + this.path = path; + } + + /** + * Construct a new package type + * + * @param parent Parent package of the package + * @param path Path of the package + */ + private PackageType(PackageType parent, String path) { + this(parent + "." + path); + } + + /** + * Returns the path of this package type + * + * @return The path + */ + public String getPath() { + return path; + } + + /** + * Returns the class with the given name + * + * @param className Name of the desired class + * @return The class with the specified name + * @throws ClassNotFoundException If the desired class with the specified name and package cannot be found + */ + public Class getClass(String className) throws ClassNotFoundException { + return Class.forName(this + "." + className); + } + + // Override for convenience + @Override + public String toString() { + return path; + } + + /** + * Returns the version of your server + * + * @return The server version + */ + public static String getServerVersion() { + return Bukkit.getServer().getClass().getPackage().getName().substring(23); + } + } + + /** + * Represents an enumeration of Java data types with corresponding classes + *

+ * This class is part of the ReflectionUtils and follows the same usage conditions + * + * @author DarkBlade12 + * @since 1.0 + */ + public enum DataType { + BYTE(byte.class, Byte.class), + SHORT(short.class, Short.class), + INTEGER(int.class, Integer.class), + LONG(long.class, Long.class), + CHARACTER(char.class, Character.class), + FLOAT(float.class, Float.class), + DOUBLE(double.class, Double.class), + BOOLEAN(boolean.class, Boolean.class); + + private static final Map, DataType> CLASS_MAP = new HashMap, DataType>(); + private final Class primitive; + private final Class reference; + + // Initialize map for quick class lookup + static { + for (DataType type : values()) { + CLASS_MAP.put(type.primitive, type); + CLASS_MAP.put(type.reference, type); + } + } + + /** + * Construct a new data type + * + * @param primitive Primitive class of this data type + * @param reference Reference class of this data type + */ + private DataType(Class primitive, Class reference) { + this.primitive = primitive; + this.reference = reference; + } + + /** + * Returns the primitive class of this data type + * + * @return The primitive class + */ + public Class getPrimitive() { + return primitive; + } + + /** + * Returns the reference class of this data type + * + * @return The reference class + */ + public Class getReference() { + return reference; + } + + /** + * Returns the data type with the given primitive/reference class + * + * @param clazz Primitive/Reference class of the data type + * @return The data type + */ + public static DataType fromClass(Class clazz) { + return CLASS_MAP.get(clazz); + } + + /** + * Returns the primitive class of the data type with the given reference class + * + * @param clazz Reference class of the data type + * @return The primitive class + */ + public static Class getPrimitive(Class clazz) { + DataType type = fromClass(clazz); + return type == null ? clazz : type.getPrimitive(); + } + + /** + * Returns the reference class of the data type with the given primitive class + * + * @param clazz Primitive class of the data type + * @return The reference class + */ + public static Class getReference(Class clazz) { + DataType type = fromClass(clazz); + return type == null ? clazz : type.getReference(); + } + + /** + * Returns the primitive class array of the given class array + * + * @param classes Given class array + * @return The primitive class array + */ + public static Class[] getPrimitive(Class[] classes) { + int length = classes == null ? 0 : classes.length; + Class[] types = new Class[length]; + for (int index = 0; index < length; index++) { + types[index] = getPrimitive(classes[index]); + } + return types; + } + + /** + * Returns the reference class array of the given class array + * + * @param classes Given class array + * @return The reference class array + */ + public static Class[] getReference(Class[] classes) { + int length = classes == null ? 0 : classes.length; + Class[] types = new Class[length]; + for (int index = 0; index < length; index++) { + types[index] = getReference(classes[index]); + } + return types; + } + + /** + * Returns the primitive class array of the given object array + * + * @param object Given object array + * @return The primitive class array + */ + public static Class[] getPrimitive(Object[] objects) { + int length = objects == null ? 0 : objects.length; + Class[] types = new Class[length]; + for (int index = 0; index < length; index++) { + types[index] = getPrimitive(objects[index].getClass()); + } + return types; + } + + /** + * Returns the reference class array of the given object array + * + * @param object Given object array + * @return The reference class array + */ + public static Class[] getReference(Object[] objects) { + int length = objects == null ? 0 : objects.length; + Class[] types = new Class[length]; + for (int index = 0; index < length; index++) { + types[index] = getReference(objects[index].getClass()); + } + return types; + } + + /** + * Compares two class arrays on equivalence + * + * @param primary Primary class array + * @param secondary Class array which is compared to the primary array + * @return Whether these arrays are equal or not + */ + public static boolean compare(Class[] primary, Class[] secondary) { + if (primary == null || secondary == null || primary.length != secondary.length) { + return false; + } + for (int index = 0; index < primary.length; index++) { + Class primaryClass = primary[index]; + Class secondaryClass = secondary[index]; + if (primaryClass.equals(secondaryClass) || primaryClass.isAssignableFrom(secondaryClass)) { + continue; + } + return false; + } + return true; + } + } + + /** + * Represents an enumeration of all packet types that are featured in Minecraft 1.7.10 + *

+ * If this enumeration is no longer up-to-date, please let me know in my forum post + *

+ * This class is part of the ReflectionUtils and follows the same usage conditions + * + * @author DarkBlade12 + * @since 1.0 + */ + public enum PacketType { + HANDSHAKING_IN_SET_PROTOCOL("PacketHandshakingInSetProtocol"), + LOGIN_IN_ENCRYPTION_BEGIN("PacketLoginInEncryptionBegin"), + LOGIN_IN_START("PacketLoginInStart"), + LOGIN_OUT_DISCONNECT("PacketLoginOutDisconnect"), + LOGIN_OUT_ENCRYPTION_BEGIN("PacketLoginOutEncryptionBegin"), + LOGIN_OUT_SUCCESS("PacketLoginOutSuccess"), + PLAY_IN_ABILITIES("PacketPlayInAbilities"), + PLAY_IN_ARM_ANIMATION("PacketPlayInArmAnimation"), + PLAY_IN_BLOCK_DIG("PacketPlayInBlockDig"), + PLAY_IN_BLOCK_PLACE("PacketPlayInBlockPlace"), + PLAY_IN_CHAT("PacketPlayInChat"), + PLAY_IN_CLIENT_COMMAND("PacketPlayInClientCommand"), + PLAY_IN_CLOSE_WINDOW("PacketPlayInCloseWindow"), + PLAY_IN_CUSTOM_PAYLOAD("PacketPlayInCustomPayload"), + PLAY_IN_ENCHANT_ITEM("PacketPlayInEnchantItem"), + PLAY_IN_ENTITY_ACTION("PacketPlayInEntityAction"), + PLAY_IN_FLYING("PacketPlayInFlying"), + PLAY_IN_HELD_ITEM_SLOT("PacketPlayInHeldItemSlot"), + PLAY_IN_KEEP_ALIVE("PacketPlayInKeepAlive"), + PLAY_IN_LOOK("PacketPlayInLook"), + PLAY_IN_POSITION("PacketPlayInPosition"), + PLAY_IN_POSITION_LOOK("PacketPlayInPositionLook"), + PLAY_IN_SET_CREATIVE_SLOT("PacketPlayInSetCreativeSlot "), + PLAY_IN_SETTINGS("PacketPlayInSettings"), + PLAY_IN_STEER_VEHICLE("PacketPlayInSteerVehicle"), + PLAY_IN_TAB_COMPLETE("PacketPlayInTabComplete"), + PLAY_IN_TRANSACTION("PacketPlayInTransaction"), + PLAY_IN_UPDATE_SIGN("PacketPlayInUpdateSign"), + PLAY_IN_USE_ENTITY("PacketPlayInUseEntity"), + PLAY_IN_WINDOW_CLICK("PacketPlayInWindowClick"), + PLAY_OUT_ABILITIES("PacketPlayOutAbilities"), + PLAY_OUT_ANIMATION("PacketPlayOutAnimation"), + PLAY_OUT_ATTACH_ENTITY("PacketPlayOutAttachEntity"), + PLAY_OUT_BED("PacketPlayOutBed"), + PLAY_OUT_BLOCK_ACTION("PacketPlayOutBlockAction"), + PLAY_OUT_BLOCK_BREAK_ANIMATION("PacketPlayOutBlockBreakAnimation"), + PLAY_OUT_BLOCK_CHANGE("PacketPlayOutBlockChange"), + PLAY_OUT_CHAT("PacketPlayOutChat"), + PLAY_OUT_CLOSE_WINDOW("PacketPlayOutCloseWindow"), + PLAY_OUT_COLLECT("PacketPlayOutCollect"), + PLAY_OUT_CRAFT_PROGRESS_BAR("PacketPlayOutCraftProgressBar"), + PLAY_OUT_CUSTOM_PAYLOAD("PacketPlayOutCustomPayload"), + PLAY_OUT_ENTITY("PacketPlayOutEntity"), + PLAY_OUT_ENTITY_DESTROY("PacketPlayOutEntityDestroy"), + PLAY_OUT_ENTITY_EFFECT("PacketPlayOutEntityEffect"), + PLAY_OUT_ENTITY_EQUIPMENT("PacketPlayOutEntityEquipment"), + PLAY_OUT_ENTITY_HEAD_ROTATION("PacketPlayOutEntityHeadRotation"), + PLAY_OUT_ENTITY_LOOK("PacketPlayOutEntityLook"), + PLAY_OUT_ENTITY_METADATA("PacketPlayOutEntityMetadata"), + PLAY_OUT_ENTITY_STATUS("PacketPlayOutEntityStatus"), + PLAY_OUT_ENTITY_TELEPORT("PacketPlayOutEntityTeleport"), + PLAY_OUT_ENTITY_VELOCITY("PacketPlayOutEntityVelocity"), + PLAY_OUT_EXPERIENCE("PacketPlayOutExperience"), + PLAY_OUT_EXPLOSION("PacketPlayOutExplosion"), + PLAY_OUT_GAME_STATE_CHANGE("PacketPlayOutGameStateChange"), + PLAY_OUT_HELD_ITEM_SLOT("PacketPlayOutHeldItemSlot"), + PLAY_OUT_KEEP_ALIVE("PacketPlayOutKeepAlive"), + PLAY_OUT_KICK_DISCONNECT("PacketPlayOutKickDisconnect"), + PLAY_OUT_LOGIN("PacketPlayOutLogin"), + PLAY_OUT_MAP("PacketPlayOutMap"), + PLAY_OUT_MAP_CHUNK("PacketPlayOutMapChunk"), + PLAY_OUT_MAP_CHUNK_BULK("PacketPlayOutMapChunkBulk"), + PLAY_OUT_MULTI_BLOCK_CHANGE("PacketPlayOutMultiBlockChange"), + PLAY_OUT_NAMED_ENTITY_SPAWN("PacketPlayOutNamedEntitySpawn"), + PLAY_OUT_NAMED_SOUND_EFFECT("PacketPlayOutNamedSoundEffect"), + PLAY_OUT_OPEN_SIGN_EDITOR("PacketPlayOutOpenSignEditor"), + PLAY_OUT_OPEN_WINDOW("PacketPlayOutOpenWindow"), + PLAY_OUT_PLAYER_INFO("PacketPlayOutPlayerInfo"), + PLAY_OUT_POSITION("PacketPlayOutPosition"), + PLAY_OUT_REL_ENTITY_MOVE("PacketPlayOutRelEntityMove"), + PLAY_OUT_REL_ENTITY_MOVE_LOOK("PacketPlayOutRelEntityMoveLook"), + PLAY_OUT_REMOVE_ENTITY_EFFECT("PacketPlayOutRemoveEntityEffect"), + PLAY_OUT_RESPAWN("PacketPlayOutRespawn"), + PLAY_OUT_SCOREBOARD_DISPLAY_OBJECTIVE("PacketPlayOutScoreboardDisplayObjective"), + PLAY_OUT_SCOREBOARD_OBJECTIVE("PacketPlayOutScoreboardObjective"), + PLAY_OUT_SCOREBOARD_SCORE("PacketPlayOutScoreboardScore"), + PLAY_OUT_SCOREBOARD_TEAM("PacketPlayOutScoreboardTeam"), + PLAY_OUT_SET_SLOT("PacketPlayOutSetSlot"), + PLAY_OUT_SPAWN_ENTITY("PacketPlayOutSpawnEntity"), + PLAY_OUT_SPAWN_ENTITY_EXPERIENCE_ORB("PacketPlayOutSpawnEntityExperienceOrb"), + PLAY_OUT_SPAWN_ENTITY_LIVING("PacketPlayOutSpawnEntityLiving"), + PLAY_OUT_SPAWN_ENTITY_PAINTING("PacketPlayOutSpawnEntityPainting"), + PLAY_OUT_SPAWN_ENTITY_WEATHER("PacketPlayOutSpawnEntityWeather"), + PLAY_OUT_SPAWN_POSITION("PacketPlayOutSpawnPosition"), + PLAY_OUT_STATISTIC("PacketPlayOutStatistic"), + PLAY_OUT_TAB_COMPLETE("PacketPlayOutTabComplete"), + PLAY_OUT_TILE_ENTITY_DATA("PacketPlayOutTileEntityData"), + PLAY_OUT_TRANSACTION("PacketPlayOutTransaction"), + PLAY_OUT_UPDATE_ATTRIBUTES("PacketPlayOutUpdateAttributes"), + PLAY_OUT_UPDATE_HEALTH("PacketPlayOutUpdateHealth"), + PLAY_OUT_UPDATE_SIGN("PacketPlayOutUpdateSign"), + PLAY_OUT_UPDATE_TIME("PacketPlayOutUpdateTime"), + PLAY_OUT_WINDOW_ITEMS("PacketPlayOutWindowItems"), + PLAY_OUT_WORLD_EVENT("PacketPlayOutWorldEvent"), + PLAY_OUT_WORLD_PARTICLES("PacketPlayOutWorldParticles"), + STATUS_IN_PING("PacketStatusInPing"), + STATUS_IN_START("PacketStatusInStart"), + STATUS_OUT_PONG("PacketStatusOutPong"), + STATUS_OUT_SERVER_INFO("PacketStatusOutServerInfo"); + + private static final Map NAME_MAP = new HashMap(); + private final String name; + private Class packet; + + // Initialize map for quick name lookup + static { + for (PacketType type : values()) { + NAME_MAP.put(type.name, type); + } + } + + /** + * Construct a new packet type + * + * @param name Name of this packet + */ + private PacketType(String name) { + this.name = name; + } + + /** + * Returns the name of this packet type + * + * @return The name + */ + public String getName() { + return name; + } + + /** + * Returns the class of this packet + * + * @return The packet class + * @throws ClassNotFoundException If the packet class cannot be found (the name differs in your Bukkit version) + * + */ + public Class getPacket() throws ClassNotFoundException { + return packet == null ? (packet = PackageType.MINECRAFT_SERVER.getClass(name)) : packet; + } + } +} \ No newline at end of file diff --git a/src/net/mc_pandacraft/java/plugin/pandacraftutils/setblock/CommandSetblock.java b/src/net/mc_pandacraft/java/plugin/pandacraftutils/setblock/CommandSetblock.java new file mode 100644 index 0000000..191d43f --- /dev/null +++ b/src/net/mc_pandacraft/java/plugin/pandacraftutils/setblock/CommandSetblock.java @@ -0,0 +1,149 @@ +package net.mc_pandacraft.java.plugin.pandacraftutils.setblock; + +import net.mc_pandacraft.java.plugin.pandacraftutils.PandacraftUtils; + +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.command.BlockCommandSender; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.command.ConsoleCommandSender; +import org.bukkit.entity.Player; + +public class CommandSetblock implements CommandExecutor { + private PandacraftUtils plugin; + + public CommandSetblock(PandacraftUtils pl) + { + plugin = pl; + plugin.getCommand("setblock").setExecutor(this); + } + + + + @Override + public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) + { + if (sender instanceof Player) + { + if (sender.hasPermission("pandacraft.setblock")) + { + if (args.length >= 4) + { + int x, y, z; + try + { + x = Integer.parseInt(args[0]); + y = Integer.parseInt(args[1]); + z = Integer.parseInt(args[2]); + } + catch (NumberFormatException e) + { + sender.sendMessage("§cLes coordonnées doivent être des chiffres :"); + return false; + } + + Material mat = Material.matchMaterial(args[3]); + if (mat == null) + { + sender.sendMessage("§cLe type de bloc indiqué n'existe pas :"); + return false; + } + + World w = ((Player)sender).getWorld(); + if (args.length >= 5 && plugin.getServer().getWorld(args[4]) != null) + w = plugin.getServer().getWorld(args[4]); + + w.getBlockAt(x, y, z).setType(mat); + sender.sendMessage("Bloc changé en "+mat+" à l'endroit "+(new Location(w, x, y, z))); + } + else + { + sender.sendMessage("§cPas assez de paramètres :"); + return false; + } + } + else + sender.sendMessage("§cVous n'avez pas la permission"); + } + else if (sender instanceof BlockCommandSender) + { + if (args.length >= 4) + { + int x, y, z; + try + { + x = Integer.parseInt(args[0]); + y = Integer.parseInt(args[1]); + z = Integer.parseInt(args[2]); + } + catch (NumberFormatException e) + { + sender.sendMessage("§cLes coordonnées doivent être des chiffres"); + return false; + } + + Material mat = Material.matchMaterial(args[3]); + if (mat == null) + { + sender.sendMessage("§cLe type de bloc indiqué n'existe pas"); + return false; + } + + World w = ((BlockCommandSender)sender).getBlock().getWorld(); + if (args.length >= 5 && plugin.getServer().getWorld(args[4]) != null) + w = plugin.getServer().getWorld(args[4]); + + w.getBlockAt(x, y, z).setType(mat); + sender.sendMessage("Bloc changé en "+mat+" à l'endroit "+(new Location(w, x, y, z))); + } + else + { + sender.sendMessage("§cPas assez de paramètres"); + return false; + } + } + else if (sender instanceof ConsoleCommandSender) + { + if (args.length >= 5) + { + int x, y, z; + try + { + x = Integer.parseInt(args[0]); + y = Integer.parseInt(args[1]); + z = Integer.parseInt(args[2]); + } + catch (NumberFormatException e) + { + sender.sendMessage("§cLes coordonnées doivent être des chiffres :"); + return false; + } + + Material mat = Material.matchMaterial(args[3]); + if (mat == null) + { + sender.sendMessage("§cLe type de bloc indiqué n'existe pas :"); + return false; + } + World w = plugin.getServer().getWorld(args[4]); + if (w == null) + { + sender.sendMessage("§cLe monde indiqué n'existe pas :"); + return false; + } + + w.getBlockAt(x, y, z).setType(mat); + } + else + { + sender.sendMessage("§cPas assez de paramètres :"); + return false; + } + } + return true; + } + +} diff --git a/src/net/mc_pandacraft/java/plugin/pandacraftutils/spawntime/SpawnTimeManager.java b/src/net/mc_pandacraft/java/plugin/pandacraftutils/spawntime/SpawnTimeManager.java new file mode 100644 index 0000000..160b404 --- /dev/null +++ b/src/net/mc_pandacraft/java/plugin/pandacraftutils/spawntime/SpawnTimeManager.java @@ -0,0 +1,52 @@ +package net.mc_pandacraft.java.plugin.pandacraftutils.spawntime; + +import java.util.Calendar; +import java.util.Date; + +import net.mc_pandacraft.java.plugin.pandacraftutils.PandacraftUtils; + +import org.bukkit.scheduler.BukkitRunnable; + +public class SpawnTimeManager extends BukkitRunnable { + + private PandacraftUtils plugin; + + int timeTick = 0; + + private String initial_map_name = "spawn"; + + + public SpawnTimeManager(PandacraftUtils pl) { + plugin = pl; + run(); + plugin.getServer().getScheduler().runTaskTimer(plugin, this, 1L, 2L); + } + + + + + @Override + public void run() { + + Calendar calendar = Calendar.getInstance(); + calendar.setTime(new Date()); + int hours = calendar.get(Calendar.HOUR_OF_DAY); + int minutes = calendar.get(Calendar.MINUTE); + int seconds = calendar.get(Calendar.SECOND); + + timeTick = ((hours * 3600 + minutes * 60 + seconds) * 10 / 36) - 6000; + if (timeTick < 0) + timeTick += 24000; + try + { + plugin.getServer().getWorld(initial_map_name).setFullTime(timeTick); + } + catch (NullPointerException e) {} + + } + + + + + +} diff --git a/src/net/mc_pandacraft/java/plugin/pandacraftutils/speed_message/CommandSpeedMessage.java b/src/net/mc_pandacraft/java/plugin/pandacraftutils/speed_message/CommandSpeedMessage.java new file mode 100644 index 0000000..a24c3a6 --- /dev/null +++ b/src/net/mc_pandacraft/java/plugin/pandacraftutils/speed_message/CommandSpeedMessage.java @@ -0,0 +1,43 @@ +package net.mc_pandacraft.java.plugin.pandacraftutils.speed_message; + +import java.util.ArrayList; +import java.util.List; + +import net.mc_pandacraft.java.plugin.pandacraftutils.PandacraftUtils; + +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; + +public class CommandSpeedMessage implements CommandExecutor { + + + private PandacraftUtils plugin; + private List messages = new ArrayList(); + + public CommandSpeedMessage(PandacraftUtils pl) + { + plugin = pl; + + plugin.getCommand("speedmessage").setExecutor(this); + + + + messages.add(new Message("site", "Site internet du serveur : http://www.mc-pandacraft.net/", "pandacraft.grade.all")); + messages.add(new Message("inscription", "Page d'inscription : http://mc-pandacraft.net/?p=inscription", "pandacraft.grade.default")); + messages.add(new Message("ultimate", "Grade [Ultimate] : http://www.mc-pandacraft.net/?p=ultimate", "pandacraft.grade.not_default")); + messages.add(new Message("help", "Si vous avez besoin d'aides pour les commandes, faites &7/help", "pandacraft.grade.all")); + + + } + + @Override + public boolean onCommand(CommandSender sender, Command cmd, String alias, + String[] args) { + + // TODO voir le plugin.yml pour l'utilisation de la commande + + return false; + } + +} diff --git a/src/net/mc_pandacraft/java/plugin/pandacraftutils/speed_message/Message.java b/src/net/mc_pandacraft/java/plugin/pandacraftutils/speed_message/Message.java new file mode 100644 index 0000000..2664e68 --- /dev/null +++ b/src/net/mc_pandacraft/java/plugin/pandacraftutils/speed_message/Message.java @@ -0,0 +1,24 @@ +/** + * + */ +package net.mc_pandacraft.java.plugin.pandacraftutils.speed_message; + +/** + * @author marcbal + * + */ +public class Message { + private String name; + private String message; + private String permissionNode; + + public Message(String non, String mess, String perm) { + name = non; + message = mess; + permissionNode = perm; + } + + public String getName() { return name; } + public String getMessage() { return message; } + public String getPermissionNode() { return permissionNode; } +} diff --git a/src/net/mc_pandacraft/java/plugin/pandacraftutils/survival_cuboid/CommandWandSelection.java b/src/net/mc_pandacraft/java/plugin/pandacraftutils/survival_cuboid/CommandWandSelection.java new file mode 100644 index 0000000..e506974 --- /dev/null +++ b/src/net/mc_pandacraft/java/plugin/pandacraftutils/survival_cuboid/CommandWandSelection.java @@ -0,0 +1,175 @@ +package net.mc_pandacraft.java.plugin.pandacraftutils.survival_cuboid; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; + +import net.mc_pandacraft.java.plugin.pandacraftutils.PandacraftUtils; +import net.mc_pandacraft.java.plugin.pandacraftutils.protocol.ParticleEffect; + +import org.bukkit.ChatColor; +import org.bukkit.Location; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerJoinEvent; +import org.bukkit.event.player.PlayerQuitEvent; +import org.bukkit.scheduler.BukkitRunnable; + +import com.sk89q.worldedit.bukkit.WorldEditPlugin; +import com.sk89q.worldedit.bukkit.selections.CuboidSelection; +import com.sk89q.worldedit.bukkit.selections.Selection; + +public class CommandWandSelection extends BukkitRunnable implements CommandExecutor, Listener { + + private PandacraftUtils plugin; + + private List players = new LinkedList(); + + + + public CommandWandSelection(PandacraftUtils pl) + { + plugin = pl; + + plugin.getCommand("/selection").setExecutor(this); + plugin.getServer().getScheduler().runTaskTimer(plugin, this, 20L, 20L); + plugin.getServer().getPluginManager().registerEvents(this, plugin); + + players.addAll(Arrays.asList(plugin.getServer().getOnlinePlayers())); + } + + + /* + * Commande //selection + */ + + + + + + // permet d'activer / désactiver l'affichage du cubo + @Override + public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) { + + if (! (sender instanceof Player)) + { + sender.sendMessage(ChatColor.RED+"Seul un joueur en ligne peut effectuer cette commande"); + return false; + } + + if (plugin.getServer().getPluginManager().getPlugin("WorldEdit") == null) + { + sender.sendMessage(ChatColor.RED+"Plugin WorldEdit non installé"); + return false; + } + + Player p = (Player) sender; + if (players.contains(p)) + { + players.remove(p); + sender.sendMessage(ChatColor.GREEN+"Affichage de la sélection désactivé."); + } + else + { + players.add(p); + sender.sendMessage(ChatColor.GREEN+"Affichage de la sélection activé. Si vous ne voyez pas les particules, activez les dans vos options Minecraft."); + } + + return true; + } + + + // supprime automatiquement un joueur qui se déconnecte + @EventHandler + public void onPlayerQuit(PlayerQuitEvent event) + { + if (players.contains(event.getPlayer())) + players.remove(event.getPlayer()); + } + + + @EventHandler + public void onPlayerJoin(PlayerJoinEvent event) + { + if (!players.contains(event.getPlayer())) + players.add(event.getPlayer()); + } + + + + // pour mettre à jour l'affichage du cubo + @Override + public void run() { + + WorldEditPlugin wePlugin = (WorldEditPlugin) plugin.getServer().getPluginManager().getPlugin("WorldEdit"); + if (wePlugin == null) return; + + try + { + + for (Player p : players) + { + // on vérifie que le joueur soit en ligne + if (p == null || !p.isOnline()) + continue; + + Selection sel = wePlugin.getSelection(p); + // on garde que les cuboïdes (pas les autres formes) + if (!(sel instanceof CuboidSelection)) + continue; + CuboidSelection cubo = (CuboidSelection) sel; + + // le joueur doit être dans le même monde que sa propre sélection + if (cubo.getWorld() != p.getWorld()) + continue; + + drawCuboid(cubo, p); + } + } + catch (Exception e) { e.printStackTrace(); } + + + + + } + + + + private void drawCuboid(CuboidSelection cubo, Player p) + { + List pls = new ArrayList(1); + pls.add(p); + + Location p1 = cubo.getMinimumPoint(), + p2 = cubo.getMaximumPoint().add(1, 1, 1); + + + long x1 = Math.round(p1.getX()), + x2 = Math.round(p2.getX()), + y1 = Math.round(p1.getY()), + y2 = Math.round(p2.getY()), + z1 = Math.round(p1.getZ()), + z2 = Math.round(p2.getZ()); + + float offset = 0F; + float distance = 17; + for (long i=x1; i<=x2; i++) + for (long j=y1; j<=y2; j++) + for (long k=z1; k<=z2; k++) + { + // exclus les points qui ne sont pas en contact avec l'extérieur de la sélection + if (!(i == x1 || i == x2 || j == y1 || j == y2 || k == z1 || k == z2)) + continue; + Location l = new Location(p1.getWorld(), i, j, k); + if (l.distanceSquared(p.getLocation()) < distance*distance) + ParticleEffect.HAPPY_VILLAGER + .display(offset, offset, offset, 0F, 1, l, pls); + } + } + +} diff --git a/src/net/mc_pandacraft/java/plugin/pandacraftutils/system/CommandSystem.java b/src/net/mc_pandacraft/java/plugin/pandacraftutils/system/CommandSystem.java new file mode 100644 index 0000000..d98d427 --- /dev/null +++ b/src/net/mc_pandacraft/java/plugin/pandacraftutils/system/CommandSystem.java @@ -0,0 +1,206 @@ +package net.mc_pandacraft.java.plugin.pandacraftutils.system; + +import java.lang.management.ManagementFactory; +import java.lang.management.ThreadInfo; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.TreeMap; +import java.util.concurrent.atomic.AtomicInteger; + +import net.mc_pandacraft.java.plugin.pandacraftutils.PandacraftUtils; +import net.mc_pandacraft.java.util.MemoryUtil; +import net.mc_pandacraft.java.util.TimeUtil; +import net.mc_pandacraft.java.util.bukkit.TextProgressBar; + +import org.bukkit.ChatColor; +import org.bukkit.World; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.command.ConsoleCommandSender; +import org.bukkit.entity.Entity; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Player; + +public class CommandSystem implements CommandExecutor { + private PandacraftUtils plugin; + private TPSAnalyser tpsAnalyser; + + public CommandSystem(PandacraftUtils pl) + { + plugin = pl; + + plugin.getCommand("system").setExecutor(this); + + tpsAnalyser = new TPSAnalyser(plugin); + + } + + + @Override + public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) + { + String decoration_color = "§6"; + + + sender.sendMessage(decoration_color+"---- Information sur les performances -----"); + + + + + if (args.length > 0 && (args[0].equalsIgnoreCase("world") || args[0].equalsIgnoreCase("worlds"))) + { + + if (args.length > 1 && plugin.getServer().getWorld(args[1]) != null) + { + World w = plugin.getServer().getWorld(args[1]); + String name = w.getName(); + int nbChunk = w.getLoadedChunks().length; + int nbEntity = w.getEntities().size(); + sender.sendMessage("§7"+name+"§r : chunks=§e"+nbChunk+"§r ; entity=§e"+nbEntity); + + Map entity_count = new HashMap(); + for (Entity e : w.getEntities()) + { + EntityType type = e.getType(); + if (entity_count.containsKey(type)) + entity_count.get(type).incrementAndGet(); + else + entity_count.put(type, new AtomicInteger(1)); + } + // on trie les valeurs + Map entity_tree = new TreeMap(new Comparator() { + private Map base; + public Comparator init(Map b) + { + base = b; + return this; + } + @Override + public int compare(EntityType o1, EntityType o2) { + AtomicInteger v1 = base.get(o1); + AtomicInteger v2 = base.get(o2); + if (v1.get() < v2.get()) // tri par ordre d§croissant + return 1; + else + return -1; + } + }.init(entity_count) ); + entity_tree.putAll(entity_count); + for (Entry val : entity_tree.entrySet()) + { + String typeS = val.getKey().name().toLowerCase(); + int nbr = (val.getValue() != null) ? val.getValue().get() : -1; + sender.sendMessage("- §7"+typeS+"§r : "+nbr); + } + + + } + else + { + // r§sum§ de tout les mondes + sender.sendMessage(decoration_color+"------------- Mondes chargés --------------"); + List worlds = plugin.getServer().getWorlds(); + for (World w : worlds) + { + String name = w.getName(); + int nbChunk = w.getLoadedChunks().length; + int nbEntity = w.getEntities().size(); + sender.sendMessage("§7"+name+"§r : chunks=§e"+nbChunk+"§r ; entités=§e"+nbEntity); + + } + } + + } + else if (args.length > 0 && (args[0].equalsIgnoreCase("thread") || args[0].equalsIgnoreCase("threads"))) + { + sender.sendMessage(decoration_color+"------------- Threads chargés -------------"); + try + { + ThreadInfo[] threadsInfo = ManagementFactory.getThreadMXBean().dumpAllThreads(false, false); + + int count = 0; + for(ThreadInfo thrd : threadsInfo) + { + if (thrd == null) + continue; + count++; + + sender.sendMessage("#"+thrd.getThreadId()+" §7"+thrd.getThreadName()+"§r : "+thrd.getThreadState().toString().toLowerCase()); + + } + sender.sendMessage(decoration_color+"Total : §7"+count+" thread"+((count>1)?"s":"")); + + + } + catch(Exception e) + { + plugin.getLogger().warning("Erreur lors de l'exécution de la commande "+cmd+" : "+e.getMessage()); + sender.sendMessage(ChatColor.RED+"Erreur lors de l'exécution de la commande."); + } + + } + else if (args.length > 0 && args[0].equalsIgnoreCase("tps_graph")) + { + if (!(sender instanceof ConsoleCommandSender)) + sender.sendMessage("Graph envoyé sur la console"); + + + ConsoleCommandSender console = plugin.getServer().getConsoleSender(); + console.sendMessage(tpsAnalyser.getStringTPSHistory(20*20)); + + } + else + { + // ----- infos générales ----- + + // m§moire + long maxMem = Runtime.getRuntime().maxMemory(); + long allocMem = Runtime.getRuntime().totalMemory(); + long freeMem = Runtime.getRuntime().freeMemory(); + sender.sendMessage(decoration_color+"Mémoire : §cUtil:"+MemoryUtil.humanReadableSize(allocMem - freeMem)+ + " §eAllouée:"+MemoryUtil.humanReadableSize(allocMem)+ + " §rMaxi:"+MemoryUtil.humanReadableSize(maxMem)); + double[] values_bar = new double[2]; + values_bar[0] = allocMem - freeMem; + values_bar[1] = freeMem; + ChatColor[] colors_bar = new ChatColor[2]; + colors_bar[0] = ChatColor.RED; + colors_bar[1] = ChatColor.YELLOW; + sender.sendMessage(TextProgressBar.progressBar(values_bar, colors_bar, maxMem, ((sender instanceof Player)?155:43))); + + // tps + double tps = tpsAnalyser.getTPS(); + String val_tps; + if (Double.isNaN(tps)) + { + val_tps = "N/A"; + tps = 0; + } + else + val_tps = (Math.round(tps*10)/10D)+""; + ChatColor color_bar = (tps >= 18)?ChatColor.GREEN: + (tps >=13)?ChatColor.YELLOW: + (tps >= 8)?ChatColor.GOLD: + (tps >= 4)?ChatColor.RED: + ChatColor.DARK_RED; + sender.sendMessage(decoration_color+"Tick par seconde : "+color_bar+val_tps+"/20"); + sender.sendMessage(TextProgressBar.progressBar(tps, color_bar, 20, ((sender instanceof Player)?155:43))); + + // uptime + long uptime = ManagementFactory.getRuntimeMXBean().getUptime(); + sender.sendMessage(decoration_color+"Uptime : §7"+TimeUtil.durationToString(uptime)); + + + + + } + + + return true; + } + +} diff --git a/src/net/mc_pandacraft/java/plugin/pandacraftutils/system/TPSAnalyser.java b/src/net/mc_pandacraft/java/plugin/pandacraftutils/system/TPSAnalyser.java new file mode 100644 index 0000000..56a020b --- /dev/null +++ b/src/net/mc_pandacraft/java/plugin/pandacraftutils/system/TPSAnalyser.java @@ -0,0 +1,87 @@ +package net.mc_pandacraft.java.plugin.pandacraftutils.system; + +import java.util.LinkedList; + +import net.mc_pandacraft.java.plugin.pandacraftutils.PandacraftUtils; + +public class TPSAnalyser implements Runnable { + private static int nb_tick_history = 20 * 20; // 20 secondes; + + private PandacraftUtils plugin; + + private LinkedList tps_times = new LinkedList(); + + + public TPSAnalyser(PandacraftUtils pl) + { + plugin = pl; + plugin.getServer().getScheduler().runTaskTimer(plugin, this, 1L, 1L); + } + + + + // mise à jour automatique pour le calcul des TPS + @Override + public void run() { + tps_times.add(new Long(System.currentTimeMillis())); + + while (tps_times.size() > nb_tick_history + 1) + tps_times.poll(); + + } + + + public double getTPS() + { + return getTPS(nb_tick_history); + } + + public double getTPS(int nb_tps) + { + if (tps_times.size() < 2) + return Double.NaN; + + if (nb_tps >= tps_times.size()) nb_tps = tps_times.size()-1; + + long time = tps_times.get(nb_tps).longValue() - tps_times.get(0).longValue(); + + if (time == 0) + return Double.NaN; + + return nb_tps * 1000 / (double)time; + } + + + public double[] getTPSHistory(int nb_tick) + { + if (nb_tick >= tps_times.size()) nb_tick = tps_times.size()-1; + + double[] history = new double[nb_tick]; + + for (int i=0; i0) result = result.concat(j+"j "); + if (h>0) result = result.concat(h+"h "); + if (m>0) result = result.concat(m+"m "); + if (s>0 && !dec_seconde) result = result.concat(s+"s"); + else if (dec_seconde && (s>0 || msec > 0)) + { + msec += s*1000; + result = result.concat((msec/1000D)+"s"); + } + + return result; + } + + public static String durationToString (long msec_time) + { + return durationToString(msec_time, false); + } +} diff --git a/src/net/mc_pandacraft/java/util/bukkit/TextProgressBar.java b/src/net/mc_pandacraft/java/util/bukkit/TextProgressBar.java new file mode 100644 index 0000000..67a2413 --- /dev/null +++ b/src/net/mc_pandacraft/java/util/bukkit/TextProgressBar.java @@ -0,0 +1,73 @@ +package net.mc_pandacraft.java.util.bukkit; + +import org.bukkit.ChatColor; +import org.bukkit.util.NumberConversions; + +public class TextProgressBar { + private static String pattern_start = "["; + private static String pattern_end = "]"; + private static ChatColor color_empty = ChatColor.DARK_GRAY; + private static ChatColor color_decoration = ChatColor.GOLD; + private static ChatColor color_default = ChatColor.RESET; + private static String pattern_empty = "."; + private static String pattern_full = "|"; + + public static String progressBar(double[] values, ChatColor[] colors, double total, int nbCar) + { + int[] sizes = new int[values.length]; + + int max_size = nbCar - pattern_start.length() - pattern_end.length(); + + for (int i=0; i=0; j--) + sum_values_before += values[j]; + + int car_position = NumberConversions.round(max_size * sum_values_before / total); + + // évite les barre de progressions plus grandes que la taille demandée + if (car_position > max_size) car_position = max_size; + + int sum_sizes_before = 0; + for (int j = i-1 ; j>=0; j--) + sum_sizes_before += sizes[j]; + + sizes[i] = car_position - sum_sizes_before; + } + int sum_sizes = 0; + + + String bar = color_decoration+pattern_start; + for (int i=0; i