Fixes two bugs related to commands

- Fixes commands not showing up in their own dedicated section in /help
- Fixes duplicate/disorganized commands in the HTTPD help page by overhauling it
This commit is contained in:
Video 2022-04-15 05:28:32 -06:00
parent d4f44e988c
commit b656925e4f
3 changed files with 103 additions and 79 deletions

View File

@ -13,19 +13,15 @@ import me.totalfreedom.totalfreedommod.TotalFreedomMod;
import me.totalfreedom.totalfreedommod.admin.Admin; import me.totalfreedom.totalfreedommod.admin.Admin;
import me.totalfreedom.totalfreedommod.player.PlayerData; import me.totalfreedom.totalfreedommod.player.PlayerData;
import me.totalfreedom.totalfreedommod.rank.Rank; import me.totalfreedom.totalfreedommod.rank.Rank;
import me.totalfreedom.totalfreedommod.util.FLog;
import me.totalfreedom.totalfreedommod.util.FUtil; import me.totalfreedom.totalfreedommod.util.FUtil;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.StringUtils;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.ChatColor; import org.bukkit.ChatColor;
import org.bukkit.Server; import org.bukkit.Server;
import org.bukkit.command.Command; import org.bukkit.command.*;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandMap;
import org.bukkit.command.CommandSender;
import org.bukkit.command.ConsoleCommandSender;
import org.bukkit.command.PluginCommand;
import org.bukkit.command.TabCompleter;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
import org.bukkit.util.StringUtil; import org.bukkit.util.StringUtil;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@ -40,7 +36,6 @@ public abstract class FreedomCommand implements CommandExecutor, TabCompleter
public static final String NO_PERMISSION = ChatColor.RED + "You do not have permission to execute this command."; public static final String NO_PERMISSION = ChatColor.RED + "You do not have permission to execute this command.";
public static final Timer timer = new Timer(); public static final Timer timer = new Timer();
public static final Map<CommandSender, FreedomCommand> COOLDOWN_TIMERS = new HashMap<>(); public static final Map<CommandSender, FreedomCommand> COOLDOWN_TIMERS = new HashMap<>();
private static CommandMap commandMap;
protected final TotalFreedomMod plugin = TotalFreedomMod.getPlugin(); protected final TotalFreedomMod plugin = TotalFreedomMod.getPlugin();
protected final Server server = plugin.getServer(); protected final Server server = plugin.getServer();
private final String name; private final String name;
@ -69,34 +64,22 @@ public abstract class FreedomCommand implements CommandExecutor, TabCompleter
this.cooldown = perms.cooldown(); this.cooldown = perms.cooldown();
} }
public static CommandMap getCommandMap()
{
if (commandMap == null)
{
try
{
final Field f = Bukkit.getServer().getPluginManager().getClass().getDeclaredField("commandMap");
f.setAccessible(true);
commandMap = (CommandMap)f.get(Bukkit.getServer().getPluginManager());
}
catch (Exception e)
{
e.printStackTrace();
}
}
return commandMap;
}
public static FreedomCommand getFrom(Command command) public static FreedomCommand getFrom(Command command)
{ {
try try
{ {
return (FreedomCommand)(((PluginCommand)command).getExecutor()); if (command instanceof FCommand)
{
return ((FCommand) command).getExecutor();
}
} }
catch (Exception ex) catch (Exception ex)
{ {
FLog.severe(ex);
return null; return null;
} }
return null;
} }
public static String getCommandPrefix() public static String getCommandPrefix()
@ -119,7 +102,7 @@ public abstract class FreedomCommand implements CommandExecutor, TabCompleter
{ {
cmd.setUsage(this.usage); cmd.setUsage(this.usage);
} }
getCommandMap().register("totalfreedommod", cmd); server.getCommandMap().register("totalfreedommod", cmd);
cmd.setExecutor(this); cmd.setExecutor(this);
} }
@ -316,7 +299,7 @@ public abstract class FreedomCommand implements CommandExecutor, TabCompleter
return perms; return perms;
} }
private final class FCommand extends Command public final class FCommand extends Command implements PluginIdentifiableCommand
{ {
private FreedomCommand cmd = null; private FreedomCommand cmd = null;
@ -325,6 +308,11 @@ public abstract class FreedomCommand implements CommandExecutor, TabCompleter
super(command); super(command);
} }
public final FreedomCommand getExecutor()
{
return cmd;
}
public void setExecutor(FreedomCommand cmd) public void setExecutor(FreedomCommand cmd)
{ {
this.cmd = cmd; this.cmd = cmd;
@ -427,5 +415,11 @@ public abstract class FreedomCommand implements CommandExecutor, TabCompleter
} }
return new ArrayList<>(); return new ArrayList<>();
} }
@Override
public @NotNull Plugin getPlugin()
{
return plugin;
}
} }
} }

View File

@ -11,9 +11,15 @@ public class HTMLGenerationTools
return "<p>" + escapeHtml4(data) + "</p>\r\n"; return "<p>" + escapeHtml4(data) + "</p>\r\n";
} }
public static String heading(String data, String id, int level)
{
return "<h" + level + (id != null ? " id=\"" + id + "\"" : "") + ">" + escapeHtml4(data)
+ "</h" + level + ">\r\n";
}
public static String heading(String data, int level) public static String heading(String data, int level)
{ {
return "<h" + level + ">" + escapeHtml4(data) + "</h" + level + ">\r\n"; return heading(data, null, level);
} }
public static <K, V> String list(Map<K, V> map) public static <K, V> String list(Map<K, V> map)

View File

@ -1,52 +1,67 @@
package me.totalfreedom.totalfreedommod.httpd.module; package me.totalfreedom.totalfreedommod.httpd.module;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Comparator; import java.util.Comparator;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects;
import me.totalfreedom.totalfreedommod.TotalFreedomMod;
import me.totalfreedom.totalfreedommod.command.FreedomCommand; import me.totalfreedom.totalfreedommod.command.FreedomCommand;
import me.totalfreedom.totalfreedommod.httpd.NanoHTTPD; import me.totalfreedom.totalfreedommod.httpd.NanoHTTPD;
import me.totalfreedom.totalfreedommod.rank.Displayable; import me.totalfreedom.totalfreedommod.rank.Rank;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.StringUtils;
import org.bukkit.Bukkit;
import org.bukkit.command.Command; import org.bukkit.command.Command;
import org.bukkit.command.CommandMap; import org.bukkit.command.CommandMap;
import org.bukkit.command.PluginIdentifiableCommand; import org.bukkit.command.PluginIdentifiableCommand;
import org.bukkit.command.SimpleCommandMap; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import static me.totalfreedom.totalfreedommod.httpd.HTMLGenerationTools.heading; import static me.totalfreedom.totalfreedommod.httpd.HTMLGenerationTools.heading;
import static me.totalfreedom.totalfreedommod.httpd.HTMLGenerationTools.paragraph; import static me.totalfreedom.totalfreedommod.httpd.HTMLGenerationTools.paragraph;
import static org.apache.commons.lang3.StringEscapeUtils.escapeHtml4; import static org.apache.commons.lang3.StringEscapeUtils.escapeHtml4;
public class Module_help extends HTTPDModule public class Module_help extends HTTPDModule
{ {
public Module_help(NanoHTTPD.HTTPSession session) public Module_help(NanoHTTPD.HTTPSession session)
{ {
super(session); super(session);
} }
private static String buildDescription(Command command) private static String buildDescription(@NotNull Command command)
{
return buildDescription(command.getName(), command.getDescription(), command.getUsage(), StringUtils.join(command.getAliases(), ", "));
}
private static String buildDescription(@NotNull FreedomCommand command)
{
return buildDescription(command.getName(), command.getDescription(), command.getUsage(), command.getAliases());
}
private static String buildDescription(@NotNull String name, @Nullable String description, @NotNull String usage, @NotNull String aliases)
{ {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
sb.append( sb.append(
"<li><span class=\"commandName\">{$CMD_NAME}</span> - Usage: <span class=\"commandUsage\">{$CMD_USAGE}</span>" "<li><span class=\"commandName\">{$CMD_NAME}</span> - Usage: <span class=\"commandUsage\">{$CMD_USAGE}</span>"
.replace("{$CMD_NAME}", escapeHtml4(command.getName().trim())) .replace("{$CMD_NAME}", escapeHtml4(name.trim()))
.replace("{$CMD_USAGE}", escapeHtml4(command.getUsage().trim()))); .replace("{$CMD_USAGE}", escapeHtml4(usage.trim())));
if (!command.getAliases().isEmpty()) if (!aliases.isEmpty())
{ {
sb.append( sb.append(
" - Aliases: <span class=\"commandAliases\">{$CMD_ALIASES}</span>" " - Aliases: <span class=\"commandAliases\">{$CMD_ALIASES}</span>"
.replace("{$CMD_ALIASES}", escapeHtml4(StringUtils.join(command.getAliases(), ", ")))); .replace("{$CMD_ALIASES}", escapeHtml4(aliases.trim())));
} }
sb.append( if (description != null)
"<br><span class=\"commandDescription\">{$CMD_DESC}</span></li>\r\n" {
.replace("{$CMD_DESC}", escapeHtml4(command.getDescription().trim()))); sb.append(
"<br><span class=\"commandDescription\">{$CMD_DESC}</span></li>\r\n"
.replace("{$CMD_DESC}", escapeHtml4(description.trim())));
}
return sb.toString(); return sb.toString();
} }
@ -54,11 +69,7 @@ public class Module_help extends HTTPDModule
@Override @Override
public String getBody() public String getBody()
{ {
final CommandMap map = FreedomCommand.getCommandMap(); final CommandMap map = Bukkit.getCommandMap();
if (!(map instanceof SimpleCommandMap))
{
return paragraph("Error loading commands.");
}
final StringBuilder responseBody = new StringBuilder() final StringBuilder responseBody = new StringBuilder()
.append(heading("Command Help", 1)) .append(heading("Command Help", 1))
@ -66,7 +77,7 @@ public class Module_help extends HTTPDModule
"This page is an automatically generated listing of all plugin commands that are currently live on the server. " "This page is an automatically generated listing of all plugin commands that are currently live on the server. "
+ "Please note that it does not include vanilla server commands.")); + "Please note that it does not include vanilla server commands."));
final Collection<Command> knownCommands = ((SimpleCommandMap)map).getCommands(); final Collection<Command> knownCommands = map.getKnownCommands().values();
final Map<String, List<Command>> commandsByPlugin = new HashMap<>(); final Map<String, List<Command>> commandsByPlugin = new HashMap<>();
for (Command command : knownCommands) for (Command command : knownCommands)
@ -79,34 +90,47 @@ public class Module_help extends HTTPDModule
List<Command> pluginCommands = commandsByPlugin.computeIfAbsent(pluginName, k -> Lists.newArrayList()); List<Command> pluginCommands = commandsByPlugin.computeIfAbsent(pluginName, k -> Lists.newArrayList());
pluginCommands.add(command); if (!pluginCommands.contains(command))
{
pluginCommands.add(command);
}
} }
final CommandComparator comparator = new CommandComparator();
// For every plugin...
for (Map.Entry<String, List<Command>> entry : commandsByPlugin.entrySet()) for (Map.Entry<String, List<Command>> entry : commandsByPlugin.entrySet())
{ {
final String pluginName = entry.getKey(); final String pluginName = entry.getKey();
final List<Command> commands = entry.getValue(); final List<Command> commands = entry.getValue();
commands.sort(new CommandComparator()); // Sort them alphabetically
commands.sort(comparator);
responseBody.append(heading(pluginName, 2)).append("<ul>\r\n"); responseBody.append(heading(pluginName, pluginName, 2)).append("<ul>\r\n");
Displayable lastTfmCommandLevel = null; if (!plugin.getName().equals(pluginName))
for (Command command : commands)
{ {
if (!TotalFreedomMod.pluginName.equals(pluginName)) commands.forEach((command) -> responseBody.append(buildDescription(command)));
{ }
responseBody.append(buildDescription(command)); else
continue; {
} Map<Rank, List<FreedomCommand>> freedomCommands = new HashMap<>();
Displayable tfmCommandLevel = Objects.requireNonNull(FreedomCommand.getFrom(command)).getPerms().level(); // Filters out non-TFM commands
if (lastTfmCommandLevel == null || lastTfmCommandLevel != tfmCommandLevel) commands.stream().filter((cmd) -> cmd instanceof FreedomCommand.FCommand).forEach((tfmCmd) -> {
{ Rank rank = FreedomCommand.getFrom(tfmCmd).getLevel();
responseBody.append("</ul>\r\n").append(heading(tfmCommandLevel.getName(), 3)).append("<ul>\r\n"); if (!freedomCommands.containsKey(rank))
} freedomCommands.put(rank, new ArrayList<>());
lastTfmCommandLevel = tfmCommandLevel; freedomCommands.get(rank).add(FreedomCommand.getFrom(tfmCmd));
responseBody.append(buildDescription(command)); });
// Finally dumps them to HTML
Arrays.stream(Rank.values()).filter(freedomCommands::containsKey)
.sorted(comparator::compare).forEach((rank -> {
responseBody.append("</ul>\r\n").append(heading(rank.getName(), 3)).append("<ul>\r\n");
freedomCommands.get(rank).stream().sorted(comparator::compare).forEach((command) -> responseBody.append(buildDescription(command)));
}));
} }
responseBody.append("</ul>\r\n"); responseBody.append("</ul>\r\n");
@ -118,7 +142,7 @@ public class Module_help extends HTTPDModule
@Override @Override
public String getTitle() public String getTitle()
{ {
return "TotalFreedomMod :: Command Help"; return plugin.getName() + " :: Command Help";
} }
@Override @Override
@ -129,23 +153,23 @@ public class Module_help extends HTTPDModule
public static class CommandComparator implements Comparator<Command> public static class CommandComparator implements Comparator<Command>
{ {
@Override @Override
public int compare(Command a, Command b) public int compare(Command a, Command b)
{ {
FreedomCommand ca = FreedomCommand.getFrom(a); return a.getName().compareTo(b.getName());
FreedomCommand cb = FreedomCommand.getFrom(b); }
if (ca == null public int compare(FreedomCommand a, FreedomCommand b)
|| cb == null {
|| ca.getPerms() == null return a.getName().compareTo(b.getName());
|| cb.getPerms() == null) }
{
return a.getName().compareTo(b.getName());
}
return ca.getPerms().level().getName().compareTo(cb.getPerms().level().getName()); public int compare(Rank a, Rank b)
{
Integer levelA = a.getLevel();
Integer levelB = b.getLevel();
return levelB.compareTo(levelA);
} }
} }
} }