From 7985ff34338c24206b7f3e6a4b845a8a52c1471f Mon Sep 17 00:00:00 2001 From: 502647092 Date: Wed, 6 Jul 2016 20:51:25 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E8=83=BD=E8=80=97?= =?UTF-8?q?=E7=9B=91=E6=8E=A7=E7=B3=BB=E7=BB=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: 502647092 --- src/main/java/pw/yumc/Yum/Yum.java | 14 ++ src/main/java/pw/yumc/Yum/api/YumAPI.java | 56 ++++++-- .../pw/yumc/Yum/commands/FileCommand.java | 7 +- .../pw/yumc/Yum/commands/MonitorCommand.java | 129 ++++++++++++++++++ .../java/pw/yumc/Yum/commands/YumCommand.java | 50 +++---- .../pw/yumc/Yum/inject/CommandInjector.java | 108 +++++++++++++++ .../pw/yumc/Yum/inject/ListenerInjector.java | 61 +++++++++ .../java/pw/yumc/Yum/inject/TaskInjector.java | 64 +++++++++ .../pw/yumc/Yum/listeners/PluginListener.java | 32 +++++ .../Yum/listeners/ThreadSafetyListener.java | 7 +- .../java/pw/yumc/Yum/models/PluginInfo.java | 17 ++- src/main/resources/monitor.yml | 0 src/main/resources/plugin.yml | 6 + 13 files changed, 497 insertions(+), 54 deletions(-) create mode 100644 src/main/java/pw/yumc/Yum/commands/MonitorCommand.java create mode 100644 src/main/java/pw/yumc/Yum/inject/CommandInjector.java create mode 100644 src/main/java/pw/yumc/Yum/inject/ListenerInjector.java create mode 100644 src/main/java/pw/yumc/Yum/inject/TaskInjector.java create mode 100644 src/main/java/pw/yumc/Yum/listeners/PluginListener.java create mode 100644 src/main/resources/monitor.yml diff --git a/src/main/java/pw/yumc/Yum/Yum.java b/src/main/java/pw/yumc/Yum/Yum.java index 3428d8c..4db0a6b 100644 --- a/src/main/java/pw/yumc/Yum/Yum.java +++ b/src/main/java/pw/yumc/Yum/Yum.java @@ -8,6 +8,7 @@ import java.util.TimerTask; import org.bukkit.Bukkit; import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.plugin.Plugin; import org.bukkit.plugin.java.JavaPlugin; import cn.citycraft.CommonData.UpdatePlugin; @@ -15,6 +16,7 @@ import cn.citycraft.PluginHelper.kit.PluginKit; import cn.citycraft.PluginHelper.utils.VersionChecker; import pw.yumc.Yum.api.YumAPI; import pw.yumc.Yum.commands.FileCommand; +import pw.yumc.Yum.commands.MonitorCommand; import pw.yumc.Yum.commands.NetCommand; import pw.yumc.Yum.commands.YumCommand; import pw.yumc.Yum.listeners.PluginNetworkListener; @@ -43,6 +45,9 @@ public class Yum extends JavaPlugin { @Override public void onDisable() { NetworkManager.unregister(); + for (final Plugin plugin : Bukkit.getPluginManager().getPlugins()) { + YumAPI.uninject(plugin); + } } @Override @@ -54,6 +59,7 @@ public class Yum extends JavaPlugin { initCommands(); initListeners(); initRunnable(); + initInject(); new VersionChecker(this); YumAPI.updaterepo(Bukkit.getConsoleSender()); YumAPI.updatecheck(Bukkit.getConsoleSender()); @@ -76,6 +82,14 @@ public class Yum extends JavaPlugin { new YumCommand(this); new NetCommand(this); new FileCommand(this); + new MonitorCommand(this); + } + + private void initInject() { + for (final Plugin plugin : Bukkit.getPluginManager().getPlugins()) { + YumAPI.inject(plugin); + } + PluginKit.scp("§a性能监控系统已启用..."); } /** diff --git a/src/main/java/pw/yumc/Yum/api/YumAPI.java b/src/main/java/pw/yumc/Yum/api/YumAPI.java index 6e60e78..b6a46a5 100644 --- a/src/main/java/pw/yumc/Yum/api/YumAPI.java +++ b/src/main/java/pw/yumc/Yum/api/YumAPI.java @@ -12,6 +12,7 @@ import org.bukkit.plugin.Plugin; import cn.citycraft.CommonData.UpdatePlugin; import cn.citycraft.PluginHelper.kit.PKit; +import pw.yumc.Yum.inject.CommandInjector; import pw.yumc.Yum.managers.ConfigManager; import pw.yumc.Yum.managers.DownloadManager; import pw.yumc.Yum.managers.PluginsManager; @@ -31,6 +32,20 @@ public class YumAPI { private static RepositoryManager repo; private static boolean runlock = false; + /** + * 初始化Yum管理中心 + * + * @param plugin + * 插件实体 + */ + public YumAPI() { + YumAPI.main = PKit.instance; + plugman = new PluginsManager(main); + download = new DownloadManager(main); + repo = new RepositoryManager(main); + plugman.addIgnore(ConfigManager.i().getIgnoreList()); + } + /** * 删除插件 * @@ -81,6 +96,18 @@ public class YumAPI { return ulist; } + /** + * 注入性能监控器 + * + * @param plugin + * 插件 + */ + public static void inject(final Plugin plugin) { + CommandInjector.inject(plugin); + // ListenerInjector.inject(plugin); + // TaskInjector.inject(plugin); + } + /** * 安装新插件 * @@ -175,6 +202,18 @@ public class YumAPI { plugman.reload(plugin); } + /** + * 取消注入 + * + * @param plugin + * 插件 + */ + public static void uninject(final Plugin plugin) { + CommandInjector.uninject(plugin); + // ListenerInjector.uninject(plugin); + // TaskInjector.uninject(plugin); + } + /** * 卸载插件 * @@ -239,8 +278,9 @@ public class YumAPI { sender.sendMessage("§d开始更新服务器可更新插件"); for (final Plugin updateplugin : ulist) { sender.sendMessage("§d一键更新: §a开始更新" + updateplugin.getName() + "!"); - if (!updatefromyum(sender, updateplugin, null, true)) + if (!updatefromyum(sender, updateplugin, null, true)) { failed++; + } } if (failed != 0) { sender.sendMessage("§d一键更新: §c升级过程中 §4" + failed + " §c个插件更新失败!"); @@ -354,18 +394,4 @@ public class YumAPI { public static void upgrade(final CommandSender sender, final Plugin plugin) { plugman.upgrade(sender, plugin); } - - /** - * 初始化Yum管理中心 - * - * @param plugin - * 插件实体 - */ - public YumAPI() { - YumAPI.main = PKit.instance; - plugman = new PluginsManager(main); - download = new DownloadManager(main); - repo = new RepositoryManager(main); - plugman.addIgnore(ConfigManager.i().getIgnoreList()); - } } diff --git a/src/main/java/pw/yumc/Yum/commands/FileCommand.java b/src/main/java/pw/yumc/Yum/commands/FileCommand.java index 5df87e8..073211d 100644 --- a/src/main/java/pw/yumc/Yum/commands/FileCommand.java +++ b/src/main/java/pw/yumc/Yum/commands/FileCommand.java @@ -12,7 +12,6 @@ import cn.citycraft.PluginHelper.commands.InvokeSubCommand; import cn.citycraft.PluginHelper.utils.FileUtil; import pw.yumc.Yum.Yum; import pw.yumc.Yum.api.YumAPI; -import pw.yumc.Yum.managers.DownloadManager; /** * File命令基类 @@ -22,11 +21,9 @@ import pw.yumc.Yum.managers.DownloadManager; */ public class FileCommand implements HandlerCommands { Yum plugin; - DownloadManager dl; public FileCommand(final Yum yum) { plugin = yum; - dl = YumAPI.getDownload(); final InvokeSubCommand cmdhandler = new InvokeSubCommand(yum, "file"); cmdhandler.setAllCommandOnlyConsole(yum.getConfig().getBoolean("onlyFileCommandConsole", true)); cmdhandler.registerCommands(this); @@ -64,9 +61,9 @@ public class FileCommand implements HandlerCommands { if (args.length == 2) { file = new File(args[1]); } else { - file = new File(Bukkit.getUpdateFolderFile(), dl.getFileName(urlstr)); + file = new File(Bukkit.getUpdateFolderFile(), YumAPI.getDownload().getFileName(urlstr)); } - dl.run(e.getSender(), urlstr, file); + YumAPI.getDownload().run(e.getSender(), urlstr, file); } @HandlerCommand(name = "ls", aliases = { "l" }, description = "列出当前目录(服务器JAR为根目录)", possibleArguments = "<相对目录>") diff --git a/src/main/java/pw/yumc/Yum/commands/MonitorCommand.java b/src/main/java/pw/yumc/Yum/commands/MonitorCommand.java new file mode 100644 index 0000000..6324ea5 --- /dev/null +++ b/src/main/java/pw/yumc/Yum/commands/MonitorCommand.java @@ -0,0 +1,129 @@ +package pw.yumc.Yum.commands; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import org.bukkit.Bukkit; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.command.PluginCommand; +import org.bukkit.command.SimpleCommandMap; +import org.bukkit.command.TabCompleter; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.PluginManager; +import org.bukkit.scheduler.BukkitTask; + +import cn.citycraft.PluginHelper.commands.HandlerCommand; +import cn.citycraft.PluginHelper.commands.HandlerCommands; +import cn.citycraft.PluginHelper.commands.HandlerTabComplete; +import cn.citycraft.PluginHelper.commands.InvokeCommandEvent; +import cn.citycraft.PluginHelper.commands.InvokeSubCommand; +import cn.citycraft.PluginHelper.ext.kit.Reflect; +import cn.citycraft.PluginHelper.utils.StrKit; +import pw.yumc.Yum.Yum; +import pw.yumc.Yum.api.YumAPI; +import pw.yumc.Yum.inject.CommandInjector; +import pw.yumc.Yum.inject.TaskInjector; + +/** + * + * @since 2016年7月6日 下午5:13:32 + * @author 喵♂呜 + */ +public class MonitorCommand implements HandlerCommands { + Yum main; + + public MonitorCommand(final Yum yum) { + main = yum; + final InvokeSubCommand cmdhandler = new InvokeSubCommand(yum, "monitor"); + cmdhandler.setAllCommandOnlyConsole(yum.getConfig().getBoolean("onlyFileCommandConsole", true)); + cmdhandler.registerCommands(this); + } + + @HandlerCommand(name = "a") + public void a(final InvokeCommandEvent e) { + + } + + @HandlerCommand(name = "cmd", minimumArguments = 1, possibleArguments = "插件名称") + public void cmd(final InvokeCommandEvent e) { + final String pname = e.getArgs()[0]; + final CommandSender sender = e.getSender(); + if (Bukkit.getPluginManager().getPlugin(pname) == null) { + sender.sendMessage("§c插件不存在!"); + return; + } + final PluginManager pluginManager = Bukkit.getPluginManager(); + final SimpleCommandMap commandMap = Reflect.on(pluginManager).get("commandMap"); + sender.sendMessage("§6插件 §b" + pname + " §6的命令能耗如下!"); + final Map temp = new HashMap<>(); + for (final Command command : commandMap.getCommands()) { + if (command instanceof PluginCommand) { + final PluginCommand pluginCommand = (PluginCommand) command; + final Plugin plugin = pluginCommand.getPlugin(); + if (plugin.getName().equalsIgnoreCase(pname)) { + temp.put(command.getName(), command); + } + } + } + for (final Entry command : temp.entrySet()) { + final CommandExecutor executor = Reflect.on(command.getValue()).get("executor"); + if (executor instanceof CommandInjector) { + final CommandInjector injected = (CommandInjector) executor; + final StringBuffer str = new StringBuffer(); + str.append("§6- §e" + command.getValue().getName() + " "); + str.append(String.format("§6总耗时: §a%.2f秒 ", injected.totalTime / 1000000.0)); + str.append("§6执行次数: §b" + injected.count + "次 "); + if (injected.count != 0) { + str.append(String.format("§6平均耗时: §d%.2f秒!", injected.totalTime / 1000000.0 / injected.count)); + } + e.getSender().sendMessage(str.toString()); + } else { + final TabCompleter completer = Reflect.on(command).get("completer"); + final CommandInjector commandInjector = new CommandInjector(executor, completer); + Reflect.on(command).set("executor", commandInjector); + Reflect.on(command).set("completer", commandInjector); + } + } + } + + @HandlerTabComplete() + public List listtab(final InvokeCommandEvent e) { + final String[] args = e.getArgs(); + return StrKit.copyPartialMatches(args[1], YumAPI.getPlugman().getPluginNames(false), new ArrayList()); + } + + @HandlerCommand(name = "task") + public void task(final InvokeCommandEvent e) { + final String pname = e.getArgs()[0]; + final CommandSender sender = e.getSender(); + final Plugin plugin = Bukkit.getPluginManager().getPlugin(pname); + if (plugin == null) { + sender.sendMessage("§c插件不存在!"); + return; + } + final List pendingTasks = Bukkit.getScheduler().getPendingTasks(); + sender.sendMessage("§6插件 §b" + pname + " §6的任务能耗如下!"); + for (final BukkitTask pendingTask : pendingTasks) { + final Runnable task = Reflect.on(pendingTask).get("task"); + if (task instanceof TaskInjector) { + final TaskInjector executor = (TaskInjector) task; + final StringBuffer str = new StringBuffer(); + str.append("§6- §e" + task.getClass().getSimpleName() + " "); + str.append(String.format("§6总耗时: §a%.2f秒 ", executor.totalTime / 1000000.0)); + str.append("§6执行次数: §b" + executor.count + "次 "); + if (executor.count != 0) { + str.append(String.format("§6平均耗时: §d%.2f秒!", executor.totalTime / 1000000.0 / executor.count)); + } + e.getSender().sendMessage(str.toString()); + } else { + final TaskInjector taskInjector = new TaskInjector(task); + Reflect.on(pendingTask).set("task", taskInjector); + } + } + } +} diff --git a/src/main/java/pw/yumc/Yum/commands/YumCommand.java b/src/main/java/pw/yumc/Yum/commands/YumCommand.java index c2aa2b9..fb6a023 100644 --- a/src/main/java/pw/yumc/Yum/commands/YumCommand.java +++ b/src/main/java/pw/yumc/Yum/commands/YumCommand.java @@ -27,8 +27,6 @@ import cn.citycraft.PluginHelper.utils.StrKit; import pw.yumc.Yum.Yum; import pw.yumc.Yum.api.YumAPI; import pw.yumc.Yum.managers.ConfigManager; -import pw.yumc.Yum.managers.PluginsManager; -import pw.yumc.Yum.managers.RepositoryManager; import pw.yumc.Yum.models.RepoSerialization.Repositories; /** @@ -39,13 +37,9 @@ import pw.yumc.Yum.models.RepoSerialization.Repositories; */ public class YumCommand implements HandlerCommands, Listener { Yum main; - RepositoryManager repo; - PluginsManager plugman; public YumCommand(final Yum yum) { main = yum; - repo = YumAPI.getRepo(); - plugman = YumAPI.getPlugman(); Bukkit.getPluginManager().registerEvents(this, yum); final InvokeSubCommand cmdhandler = new InvokeSubCommand(yum, "yum"); cmdhandler.setAllCommandOnlyConsole(yum.getConfig().getBoolean("onlyCommandConsole", false)); @@ -59,7 +53,7 @@ public class YumCommand implements HandlerCommands, Listener { final Plugin plugin = Bukkit.getServer().getPluginManager().getPlugin(pluginname); if (plugin != null) { final String version = StringUtils.substring(plugin.getDescription().getVersion(), 0, 15); - if (plugman.deletePlugin(sender, plugin)) { + if (YumAPI.getPlugman().deletePlugin(sender, plugin)) { sender.sendMessage("§c删除: §a插件 §b" + pluginname + " §a版本 §d" + version + " §a已从服务器卸载并删除!"); } else { sender.sendMessage("§c删除: §a插件 §b" + pluginname + " §c卸载或删除时发生错误 删除失败!"); @@ -91,7 +85,7 @@ public class YumCommand implements HandlerCommands, Listener { final Plugin plugin = Bukkit.getServer().getPluginManager().getPlugin(pluginname); if (plugin != null) { final String version = StringUtils.substring(plugin.getDescription().getVersion(), 0, 15); - if (plugman.fullDeletePlugin(sender, plugin)) { + if (YumAPI.getPlugman().fullDeletePlugin(sender, plugin)) { sender.sendMessage("§c删除: §a插件 §b" + pluginname + " §a版本 §d" + version + " §a已从服务器卸载并删除!"); } else { sender.sendMessage("§c删除: §c插件 §b" + pluginname + " §c卸载或删除时发生错误 删除失败!"); @@ -134,7 +128,7 @@ public class YumCommand implements HandlerCommands, Listener { sender.sendMessage("§6 - §a" + perm.getName() + "§6 - §e" + (perm.getDescription().isEmpty() ? "无描述" : perm.getDescription())); } } - sender.sendMessage("§6插件物理路径: §3" + plugman.getPluginFile(plugin).getAbsolutePath()); + sender.sendMessage("§6插件物理路径: §3" + YumAPI.getPlugman().getPluginFile(plugin).getAbsolutePath()); } else { sender.sendMessage(pnf(pluginname)); } @@ -167,7 +161,7 @@ public class YumCommand implements HandlerCommands, Listener { final CommandSender sender = e.getSender(); sender.sendMessage("§6[Yum仓库]§3服务器已安装插件: "); for (final Plugin plugin : Bukkit.getPluginManager().getPlugins()) { - sender.sendMessage("§6- " + plugman.getFormattedName(plugin, true)); + sender.sendMessage("§6- " + YumAPI.getPlugman().getFormattedName(plugin, true)); } } @@ -175,16 +169,16 @@ public class YumCommand implements HandlerCommands, Listener { public List listtab(final InvokeCommandEvent e) { final String[] args = e.getArgs(); if (args[0].equalsIgnoreCase("install") || args[0].equalsIgnoreCase("i")) { - return StrKit.copyPartialMatches(args[1], repo.getAllPluginName(), new ArrayList()); + return StrKit.copyPartialMatches(args[1], YumAPI.getRepo().getAllPluginName(), new ArrayList()); } else if (args[0].equalsIgnoreCase("repo")) { if (args.length == 2) { return StrKit.copyPartialMatches(args[1], Arrays.asList(new String[] { "add", "all", "list", "delall", "clean", "update", "del" }), new ArrayList()); } if (args.length == 3 && (args[1] == "add" || args[1] == "del")) { - return StrKit.copyPartialMatches(args[2], repo.getRepos().keySet(), new ArrayList()); + return StrKit.copyPartialMatches(args[2], YumAPI.getRepo().getRepos().keySet(), new ArrayList()); } } else { - return StrKit.copyPartialMatches(args[1], plugman.getPluginNames(false), new ArrayList()); + return StrKit.copyPartialMatches(args[1], YumAPI.getPlugman().getPluginNames(false), new ArrayList()); } return null; } @@ -195,7 +189,7 @@ public class YumCommand implements HandlerCommands, Listener { final String pluginname = e.getArgs()[0]; final Plugin plugin = Bukkit.getServer().getPluginManager().getPlugin(pluginname); if (plugin == null) { - plugman.load(sender, pluginname); + YumAPI.getPlugman().load(sender, pluginname); } else { sender.sendMessage("§c错误: §a插件 §b" + pluginname + " §c已加载到服务器!"); } @@ -218,12 +212,12 @@ public class YumCommand implements HandlerCommands, Listener { } final String pluginname = e.getArgs()[0]; if (pluginname.equalsIgnoreCase("all") || pluginname.equalsIgnoreCase("*")) { - plugman.reloadAll(sender); + YumAPI.getPlugman().reloadAll(sender); return; } final Plugin plugin = Bukkit.getServer().getPluginManager().getPlugin(pluginname); if (plugin != null) { - plugman.reload(sender, plugin); + YumAPI.getPlugman().reload(sender, plugin); } else { sender.sendMessage(pnf(pluginname)); } @@ -240,8 +234,8 @@ public class YumCommand implements HandlerCommands, Listener { switch (cmd) { case "add": if (args.length == 2) { - if (repo.addRepositories(sender, args[1])) { - final String reponame = repo.getRepoCache(args[1]).name; + if (YumAPI.getRepo().addRepositories(sender, args[1])) { + final String reponame = YumAPI.getRepo().getRepoCache(args[1]).name; sender.sendMessage("§6仓库: §a源仓库 §e" + reponame + " §a的插件信息已缓存!"); } else { sender.sendMessage("§6仓库: §c源地址未找到仓库信息或当前地址已缓存!"); @@ -252,9 +246,9 @@ public class YumCommand implements HandlerCommands, Listener { break; case "del": if (args.length == 2) { - final Repositories delrepo = repo.getRepoCache(args[1]); + final Repositories delrepo = YumAPI.getRepo().getRepoCache(args[1]); if (delrepo != null) { - repo.delRepositories(sender, args[1]); + YumAPI.getRepo().delRepositories(sender, args[1]); sender.sendMessage("§6仓库: §a源仓库 §e" + delrepo.name + " §c已删除 §a请使用 §b/yum repo update §a更新缓存!"); } else { sender.sendMessage("§6仓库: §c源地址未找到!"); @@ -264,23 +258,23 @@ public class YumCommand implements HandlerCommands, Listener { } break; case "delall": - repo.getRepoCache().getRepos().clear(); + YumAPI.getRepo().getRepoCache().getRepos().clear(); sender.sendMessage("§6仓库: §a缓存的仓库信息已清理!"); break; case "list": sender.sendMessage("§6仓库: §b缓存的插件信息如下 "); - StrKit.sendStringArray(sender, repo.getAllPluginsInfo()); + StrKit.sendStringArray(sender, YumAPI.getRepo().getAllPluginsInfo()); break; case "all": sender.sendMessage("§6仓库: §b缓存的仓库信息如下 "); - StrKit.sendStringArray(sender, repo.getRepoCache().getAllRepoInfo()); + StrKit.sendStringArray(sender, YumAPI.getRepo().getRepoCache().getAllRepoInfo()); break; case "clean": - repo.clean(); + YumAPI.getRepo().clean(); sender.sendMessage("§6仓库: §a缓存的插件信息已清理!"); break; case "update": - repo.updateRepositories(sender); + YumAPI.getRepo().updateRepositories(sender); sender.sendMessage("§6仓库: §a仓库缓存数据已更新!"); break; } @@ -335,7 +329,7 @@ public class YumCommand implements HandlerCommands, Listener { final CommandSender sender = e.getSender(); final Plugin plugin = Bukkit.getServer().getPluginManager().getPlugin(pluginname); if (plugin != null) { - plugman.unload(sender, plugin); + YumAPI.getPlugman().unload(sender, plugin); } else { sender.sendMessage(pnf(pluginname)); } @@ -347,7 +341,7 @@ public class YumCommand implements HandlerCommands, Listener { final CommandSender sender = e.getSender(); switch (args.length) { case 0: - repo.updateRepositories(sender); + YumAPI.getRepo().updateRepositories(sender); sender.sendMessage("§6仓库: §a仓库缓存数据已更新!"); break; case 1: @@ -393,7 +387,7 @@ public class YumCommand implements HandlerCommands, Listener { @Override public void run() { if (args.length == 0) { - plugman.upgrade(sender); + YumAPI.getPlugman().upgrade(sender); } else { final String pluginname = args[0]; final Plugin plugin = Bukkit.getServer().getPluginManager().getPlugin(pluginname); diff --git a/src/main/java/pw/yumc/Yum/inject/CommandInjector.java b/src/main/java/pw/yumc/Yum/inject/CommandInjector.java new file mode 100644 index 0000000..0db2ea8 --- /dev/null +++ b/src/main/java/pw/yumc/Yum/inject/CommandInjector.java @@ -0,0 +1,108 @@ +package pw.yumc.Yum.inject; + +import java.util.List; + +import org.bukkit.Bukkit; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.command.PluginCommand; +import org.bukkit.command.SimpleCommandMap; +import org.bukkit.command.TabCompleter; +import org.bukkit.command.TabExecutor; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.PluginManager; + +import cn.citycraft.PluginHelper.ext.kit.Reflect; +import cn.citycraft.PluginHelper.kit.PluginKit; + +public class CommandInjector implements TabExecutor { + + private final CommandExecutor originalExecutor; + private final TabCompleter originalCompleter; + + public long totalTime; + public long count; + + public CommandInjector(final CommandExecutor originalCommandExecutor, final TabCompleter originalTabCompleter) { + this.originalExecutor = originalCommandExecutor; + this.originalCompleter = originalTabCompleter; + } + + public static void inject(final Plugin toInjectPlugin) { + final PluginManager pluginManager = Bukkit.getPluginManager(); + final SimpleCommandMap commandMap = Reflect.on(pluginManager).get("commandMap"); + for (final Command command : commandMap.getCommands()) { + if (command instanceof PluginCommand) { + final PluginCommand pluginCommand = (PluginCommand) command; + final Plugin plugin = pluginCommand.getPlugin(); + if (plugin.equals(toInjectPlugin)) { + final CommandExecutor executor = Reflect.on(command).get("executor"); + if (executor instanceof CommandInjector) { + return; + } + final TabCompleter completer = Reflect.on(command).get("completer"); + final CommandInjector commandInjector = new CommandInjector(executor, completer); + Reflect.on(command).set("executor", commandInjector); + Reflect.on(command).set("completer", commandInjector); + } + } + } + PluginKit.scp("§a注入命令性能监控到 " + toInjectPlugin.getName()); + } + + public static void uninject(final Plugin toUninject) { + final PluginManager pluginManager = Bukkit.getPluginManager(); + final SimpleCommandMap commandMap = Reflect.on(pluginManager).get("commandMap"); + for (final Command command : commandMap.getCommands()) { + if (command instanceof PluginCommand) { + final PluginCommand pluginCommand = (PluginCommand) command; + final Plugin plugin = pluginCommand.getPlugin(); + if (plugin.equals(toUninject)) { + final CommandExecutor executor = Reflect.on(command).get("executor"); + if (executor instanceof CommandInjector) { + final CommandInjector injected = (CommandInjector) executor; + Reflect.on(command).set("executor", injected.getOriginalExecutor()); + } + final TabCompleter completer = Reflect.on(command).get("completer"); + if (completer instanceof CommandInjector) { + final CommandInjector injected = (CommandInjector) completer; + Reflect.on(command).set("completer", injected.getOriginalCompleter()); + } + } + } + } + } + + public TabCompleter getOriginalCompleter() { + return originalCompleter; + } + + public CommandExecutor getOriginalExecutor() { + return originalExecutor; + } + + @Override + public boolean onCommand(final CommandSender sender, final Command command, final String label, final String[] args) { + final long start = System.nanoTime(); + // todo add a more aggressive 10 ms cpu sample + final boolean result = originalExecutor.onCommand(sender, command, label, args); + final long end = System.nanoTime(); + + totalTime += end - start; + count++; + return result; + } + + @Override + public List onTabComplete(final CommandSender sender, final Command command, final String alias, final String[] args) { + final long start = System.nanoTime(); + // todo add a more aggressive 10 ms cpu sample + final List result = originalCompleter.onTabComplete(sender, command, alias, args); + final long end = System.nanoTime(); + + totalTime += end - start; + count++; + return result; + } +} diff --git a/src/main/java/pw/yumc/Yum/inject/ListenerInjector.java b/src/main/java/pw/yumc/Yum/inject/ListenerInjector.java new file mode 100644 index 0000000..98c6103 --- /dev/null +++ b/src/main/java/pw/yumc/Yum/inject/ListenerInjector.java @@ -0,0 +1,61 @@ +package pw.yumc.Yum.inject; + +import java.util.List; + +import org.bukkit.event.Event; +import org.bukkit.event.EventException; +import org.bukkit.event.HandlerList; +import org.bukkit.event.Listener; +import org.bukkit.plugin.EventExecutor; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.RegisteredListener; + +import cn.citycraft.PluginHelper.ext.kit.Reflect; + +public class ListenerInjector implements EventExecutor { + + private final EventExecutor originalExecutor; + + public long totalTime; + public long count; + + public ListenerInjector(final EventExecutor originalExecutor) { + this.originalExecutor = originalExecutor; + } + + public static void inject(final Plugin plugin) { + final List listeners = HandlerList.getRegisteredListeners(plugin); + for (final RegisteredListener listener : listeners) { + final EventExecutor originalExecutor = Reflect.on(listener).get("executor"); + final ListenerInjector listenerInjector = new ListenerInjector(originalExecutor); + Reflect.on(listener).set("executor", listenerInjector); + } + } + + public static void uninject(final Plugin plugin) { + final List listeners = HandlerList.getRegisteredListeners(plugin); + for (final RegisteredListener listener : listeners) { + final ListenerInjector executor = Reflect.on(listener).get("executor"); + Reflect.on(listener).set("executor", executor.getOriginalExecutor()); + } + } + + @Override + public void execute(final Listener listener, final Event event) throws EventException { + if (!event.isAsynchronous()) { + final long start = System.nanoTime(); + // todo add a more aggressive 10 ms cpu sample + originalExecutor.execute(listener, event); + final long end = System.nanoTime(); + + totalTime += end - start; + count++; + } else { + originalExecutor.execute(listener, event); + } + } + + public EventExecutor getOriginalExecutor() { + return originalExecutor; + } +} diff --git a/src/main/java/pw/yumc/Yum/inject/TaskInjector.java b/src/main/java/pw/yumc/Yum/inject/TaskInjector.java new file mode 100644 index 0000000..cdab525 --- /dev/null +++ b/src/main/java/pw/yumc/Yum/inject/TaskInjector.java @@ -0,0 +1,64 @@ +package pw.yumc.Yum.inject; + +import java.util.List; + +import org.bukkit.Bukkit; +import org.bukkit.plugin.Plugin; +import org.bukkit.scheduler.BukkitScheduler; +import org.bukkit.scheduler.BukkitTask; + +import cn.citycraft.PluginHelper.ext.kit.Reflect; + +public class TaskInjector implements Runnable { + + private final Runnable originalTask; + + public long totalTime; + public long count; + + public TaskInjector(final Runnable originalTask) { + this.originalTask = originalTask; + } + + // sadly it works only with interval tasks + // for single runs we would have to register a dynamic proxy + public static void inject(final Plugin plugin) { + final BukkitScheduler scheduler = Bukkit.getScheduler(); + final List pendingTasks = scheduler.getPendingTasks(); + for (final BukkitTask pendingTask : pendingTasks) { + // we could ignore async tasks for now + if (pendingTask.isSync() && pendingTask.getOwner().equals(plugin)) { + final Runnable originalTask = Reflect.on(pendingTask).get("task"); + final TaskInjector taskInjector = new TaskInjector(originalTask); + Reflect.on(pendingTask).set("task", taskInjector); + } + } + } + + public static void uninject(final Plugin plugin) { + final BukkitScheduler scheduler = Bukkit.getScheduler(); + final List pendingTasks = scheduler.getPendingTasks(); + for (final BukkitTask pendingTask : pendingTasks) { + // we could ignore async tasks for now + if (pendingTask.isSync() && pendingTask.getOwner().equals(plugin)) { + final TaskInjector originalTask = Reflect.on(pendingTask).get("task"); + Reflect.on(pendingTask).set("task", originalTask.getOriginalTask()); + } + } + } + + public Runnable getOriginalTask() { + return originalTask; + } + + @Override + public void run() { + final long start = System.nanoTime(); + // todo add a more aggressive 10 ms cpu sample + originalTask.run(); + final long end = System.nanoTime(); + + totalTime += end - start; + count++; + } +} diff --git a/src/main/java/pw/yumc/Yum/listeners/PluginListener.java b/src/main/java/pw/yumc/Yum/listeners/PluginListener.java new file mode 100644 index 0000000..7474521 --- /dev/null +++ b/src/main/java/pw/yumc/Yum/listeners/PluginListener.java @@ -0,0 +1,32 @@ +package pw.yumc.Yum.listeners; + +import org.bukkit.Bukkit; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.server.PluginDisableEvent; +import org.bukkit.event.server.PluginEnableEvent; + +import cn.citycraft.PluginHelper.kit.PKit; +import pw.yumc.Yum.api.YumAPI; + +/** + * + * @since 2016年7月6日 下午6:44:16 + * @author 喵♂呜 + */ +public class PluginListener implements Listener { + + public PluginListener() { + Bukkit.getPluginManager().registerEvents(this, PKit.i()); + } + + @EventHandler + public void onPluginDisable(final PluginDisableEvent e) { + YumAPI.uninject(e.getPlugin()); + } + + @EventHandler + public void onPluginEnable(final PluginEnableEvent e) { + YumAPI.inject(e.getPlugin()); + } +} diff --git a/src/main/java/pw/yumc/Yum/listeners/ThreadSafetyListener.java b/src/main/java/pw/yumc/Yum/listeners/ThreadSafetyListener.java index 35f6010..b107b7f 100644 --- a/src/main/java/pw/yumc/Yum/listeners/ThreadSafetyListener.java +++ b/src/main/java/pw/yumc/Yum/listeners/ThreadSafetyListener.java @@ -15,7 +15,9 @@ import org.bukkit.event.world.ChunkUnloadEvent; import org.bukkit.event.world.WorldLoadEvent; import org.bukkit.event.world.WorldSaveEvent; import org.bukkit.event.world.WorldUnloadEvent; +import org.bukkit.plugin.Plugin; +import cn.citycraft.PluginHelper.kit.PluginKit; import pw.yumc.Yum.Yum; /** @@ -87,7 +89,10 @@ public class ThreadSafetyListener implements Listener { private void checkSafety(final Event eventType) { if (Yum.mainThread != null && Thread.currentThread() != Yum.mainThread && !eventType.isAsynchronous()) { final String eventName = eventType.getEventName(); - throw new IllegalAccessError("[Yum 线程安全]: 请勿异步调用一个同步事件 " + eventName); + final Plugin plugin = PluginKit.getOperatePlugin(); + if (plugin != null) { + throw new IllegalAccessError("[Yum 线程安全]: 请勿异步调用一个同步事件 " + eventName + " 操作插件: " + plugin.getName()); + } } } } diff --git a/src/main/java/pw/yumc/Yum/models/PluginInfo.java b/src/main/java/pw/yumc/Yum/models/PluginInfo.java index cf453ed..7e77b6d 100644 --- a/src/main/java/pw/yumc/Yum/models/PluginInfo.java +++ b/src/main/java/pw/yumc/Yum/models/PluginInfo.java @@ -6,13 +6,23 @@ import org.bukkit.Bukkit; import org.bukkit.command.CommandSender; import cn.citycraft.PluginHelper.PluginHelperLogger; +import cn.citycraft.PluginHelper.kit.PKit; import cn.citycraft.PluginHelper.utils.IOUtil; import pw.yumc.Yum.models.RepoSerialization.Plugin; import pw.yumc.Yum.models.RepoSerialization.TagInfo; import pw.yumc.Yum.models.RepoSerialization.URLType; public class PluginInfo implements Serializable { - public static final String NMSVersion = Bukkit.getServer().getClass().getPackage().getName().split("\\.")[3]; + public static String NMSVersion; + + static { + try { + NMSVersion = Bukkit.getServer().getClass().getPackage().getName().split("\\.")[3]; + } catch (final Exception e) { + PKit.i().getLogger().warning("服务器NMS解析失败: " + Bukkit.getServer().getClass().getPackage().getName()); + NMSVersion = "NONMS"; + } + } public String branch; public String name; @@ -78,10 +88,7 @@ public class PluginInfo implements Serializable { * @return 更新地址 */ public String getMavenUrl(final String ver) { - return String.format(url + (url.endsWith("/") ? "" : "/") + "%1$s/%2$s/%3$s/%2$s-%3$s.jar", - plugin.groupId.replace(".", "/"), - plugin.artifactId, - (ver == null || ver.isEmpty()) ? plugin.version : ver); + return String.format(url + (url.endsWith("/") ? "" : "/") + "%1$s/%2$s/%3$s/%2$s-%3$s.jar", plugin.groupId.replace(".", "/"), plugin.artifactId, (ver == null || ver.isEmpty()) ? plugin.version : ver); } public String getUrl(final CommandSender sender, final String version) { diff --git a/src/main/resources/monitor.yml b/src/main/resources/monitor.yml new file mode 100644 index 0000000..e69de29 diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 62888ce..77a52fa 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -30,6 +30,12 @@ commands: usage: §6使用§a/security help§6查看帮助! permission: security.use permission-message: §c你没有 的权限来执行此命令! + monitor: + description: MC系统监控命令 + aliases: [m] + usage: §6使用§a/monitor help§6查看帮助! + permission: monitor.use + permission-message: §c你没有 的权限来执行此命令! permissions: yum.use: description: 插件管理系统使用权限!