/** * ProtocolLib - Bukkit server library that allows access to the Minecraft protocol. * Copyright (C) 2012 Kristian S. Stangeland * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; * if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA * 02111-1307 USA */ package com.comphenix.protocol; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.io.PrintWriter; import java.text.SimpleDateFormat; import java.util.Date; import java.util.HashSet; import java.util.Set; import java.util.concurrent.TimeUnit; import java.util.logging.Level; import org.bukkit.ChatColor; import org.bukkit.command.CommandSender; import org.bukkit.plugin.Plugin; import org.bukkit.plugin.PluginDescriptionFile; import com.comphenix.protocol.error.DetailedErrorReporter; import com.comphenix.protocol.error.ErrorReporter; import com.comphenix.protocol.events.PacketListener; import com.comphenix.protocol.timing.TimedListenerManager; import com.comphenix.protocol.timing.TimingReportGenerator; import com.comphenix.protocol.updater.Updater; import com.comphenix.protocol.updater.Updater.UpdateType; import com.comphenix.protocol.utility.Closer; /** * Handles the "protocol" administration command. * * @author Kristian */ class CommandProtocol extends CommandBase { /** * Name of this command. */ public static final String NAME = "protocol"; private Plugin plugin; private Updater updater; private ProtocolConfig config; public CommandProtocol(ErrorReporter reporter, Plugin plugin, Updater updater, ProtocolConfig config) { super(reporter, CommandBase.PERMISSION_ADMIN, NAME, 1); this.plugin = plugin; this.updater = updater; this.config = config; } @Override protected boolean handleCommand(CommandSender sender, String[] args) { String subCommand = args[0]; // Only return TRUE if we executed the correct command if (subCommand.equalsIgnoreCase("config") || subCommand.equalsIgnoreCase("reload")) { reloadConfiguration(sender); } else if (subCommand.equalsIgnoreCase("check")) { checkVersion(sender, true); } else if (subCommand.equalsIgnoreCase("update")) { updateVersion(sender, true); } else if (subCommand.equalsIgnoreCase("timings")) { toggleTimings(sender, args); } else if (subCommand.equalsIgnoreCase("listeners")) { printListeners(sender); } else if (subCommand.equalsIgnoreCase("version")) { printVersion(sender); } else if (subCommand.equalsIgnoreCase("dump")) { dump(sender); } else { return false; } return true; } public void checkVersion(final CommandSender sender, boolean command) { performUpdate(sender, UpdateType.NO_DOWNLOAD, command); } public void updateVersion(final CommandSender sender, boolean command) { performUpdate(sender, UpdateType.DEFAULT, command); } // Display every listener on the server private void printListeners(final CommandSender sender) { ProtocolManager manager = ProtocolLibrary.getProtocolManager(); sender.sendMessage(ChatColor.GOLD + "Packet listeners:"); for (PacketListener listener : manager.getPacketListeners()) { sender.sendMessage(ChatColor.GOLD + " - " + listener); } // Along with every asynchronous listener sender.sendMessage(ChatColor.GOLD + "Asynchronous listeners:"); for (PacketListener listener : manager.getAsynchronousManager().getAsyncHandlers()) { sender.sendMessage(ChatColor.GOLD + " - " + listener); } } private void performUpdate(final CommandSender sender, UpdateType type, final boolean command) { if (updater.isChecking()) { sender.sendMessage(ChatColor.RED + "Already checking for an update."); return; } // Perform on an async thread Runnable notify = new Runnable() { @Override public void run() { if (command) { sender.sendMessage(ChatColor.YELLOW + "[ProtocolLib] " + updater.getResult()); String remoteVersion = updater.getRemoteVersion(); if (remoteVersion != null) { sender.sendMessage(ChatColor.YELLOW + "Remote version: " + remoteVersion); sender.sendMessage(ChatColor.YELLOW + "Current version: " + plugin.getDescription().getVersion()); } } else if (updater.shouldNotify() || config.isDebug()) { sender.sendMessage(ChatColor.YELLOW + "[ProtocolLib] " + updater.getResult()); } updater.removeListener(this); updateFinished(); } }; updater.start(type); updater.addListener(notify); } private void toggleTimings(CommandSender sender, String[] args) { TimedListenerManager manager = TimedListenerManager.getInstance(); boolean state = !manager.isTiming(); // toggle // Parse the boolean parameter if (args.length == 2) { Boolean parsed = parseBoolean(toQueue(args, 2), "start"); if (parsed != null) { state = parsed; } else { sender.sendMessage(ChatColor.RED + "Specify a state: ON or OFF."); return; } } else if (args.length > 2) { sender.sendMessage(ChatColor.RED + "Too many parameters."); return; } // Now change the state if (state) { if (manager.startTiming()) sender.sendMessage(ChatColor.GOLD + "Started timing packet listeners."); else sender.sendMessage(ChatColor.RED + "Packet timing already started."); } else { if (manager.stopTiming()) { saveTimings(manager); sender.sendMessage(ChatColor.GOLD + "Stopped and saved result in plugin folder."); } else { sender.sendMessage(ChatColor.RED + "Packet timing already stopped."); } } } private void saveTimings(TimedListenerManager manager) { try { File destination = new File(plugin.getDataFolder(), "Timings - " + System.currentTimeMillis() + ".txt"); TimingReportGenerator generator = new TimingReportGenerator(); // Print to a text file generator.saveTo(destination, manager); manager.clear(); } catch (IOException e) { reporter.reportMinimal(plugin, "saveTimings()", e); } } /** * Prevent further automatic updates until the next delay. */ public void updateFinished() { long currentTime = TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis()); config.setAutoLastTime(currentTime); config.saveAll(); } public void reloadConfiguration(CommandSender sender) { plugin.reloadConfig(); sender.sendMessage(ChatColor.YELLOW + "Reloaded configuration!"); } private void printVersion(CommandSender sender) { PluginDescriptionFile desc = plugin.getDescription(); sender.sendMessage(ChatColor.GREEN + desc.getName() + ChatColor.WHITE + " v" + ChatColor.GREEN + desc.getVersion()); sender.sendMessage(ChatColor.WHITE + "Authors: " + ChatColor.GREEN + "dmulloy2" + ChatColor.WHITE + " and " + ChatColor.GREEN + "Comphenix"); sender.sendMessage(ChatColor.WHITE + "Issues: " + ChatColor.GREEN + "https://github.com/dmulloy2/ProtocolLib/issues"); } private static SimpleDateFormat FILE_FORMAT; private static SimpleDateFormat TIMESTAMP_FORMAT; private void dump(CommandSender sender) { if (FILE_FORMAT == null) FILE_FORMAT = new SimpleDateFormat("yyyy-MM-dd_HH.mm.ss"); if (TIMESTAMP_FORMAT == null) TIMESTAMP_FORMAT = new SimpleDateFormat("MM/dd/yy HH:mm:ss"); try (Closer closer = Closer.create()) { Date date = new Date(); File file = new File(plugin.getDataFolder(), "dump-" + FILE_FORMAT.format(date) + ".txt"); if (file.exists()) { file.delete(); } file.createNewFile(); FileWriter fw = closer.register(new FileWriter(file)); PrintWriter pw = closer.register(new PrintWriter(fw)); pw.println("ProtocolLib Dump"); pw.println("Timestamp: " + TIMESTAMP_FORMAT.format(date)); pw.println(); pw.println("ProtocolLib Version: " + plugin.toString()); pw.println("Bukkit Version: " + plugin.getServer().getBukkitVersion()); pw.println("Server Version: " + plugin.getServer().getVersion()); pw.println("Java Version: " + System.getProperty("java.version")); pw.println(); ProtocolManager manager = ProtocolLibrary.getProtocolManager(); pw.println("ProtocolLib: " + DetailedErrorReporter.getStringDescription(plugin)); pw.println("Manager: " + DetailedErrorReporter.getStringDescription(manager)); pw.println(); Set listeners = manager.getPacketListeners(); Set plugins = new HashSet<>(); if (!listeners.isEmpty()) { pw.println("Listeners:"); for (PacketListener listener : listeners) { pw.println(DetailedErrorReporter.getStringDescription(listener)); Plugin plugin = listener.getPlugin(); if (plugin != null) { plugins.add(plugin); } else { pw.println("(Missing plugin!)"); } } pw.println(); } else { pw.println("No listeners"); } if (!plugins.isEmpty()) { pw.println("Plugins Using ProtocolLib:"); for (Plugin plugin : plugins) { pw.println(plugin.getName() + " by " + plugin.getDescription().getAuthors()); } } sender.sendMessage("Data dump written to " + file.getAbsolutePath()); } catch (IOException ex) { ProtocolLogger.log(Level.SEVERE, "Failed to create dump:", ex); sender.sendMessage(ChatColor.RED + "Failed to create dump! Check console!"); } } }