mirror of
https://github.com/NoCheatPlus/NoCheatPlus.git
synced 2025-01-28 10:21:22 +01:00
Add the "ncp top" command, allowing to search all the violation history.
Original pull request: https://github.com/NoCheatPlus/NoCheatPlus/pull/24 This probably is not the final implementation, but it allows some minimal freedom: * Specify number of entries to show. * Specify check types (and groups!). * Specify what to sort by. There might be need for some merged view, combining several different check types somehow, or just shortcuts for specific selections, e.g. for fighting-related checks. ---- + Fix root command not showing sub commmand usage.
This commit is contained in:
parent
9b6c717fc0
commit
c2722abc19
@ -0,0 +1,37 @@
|
||||
package fr.neatmonster.nocheatplus.utilities;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Comparator;
|
||||
|
||||
/**
|
||||
* Allow to sort by multiple criteria, first come first serve.
|
||||
* @author dev1mc
|
||||
*
|
||||
*/
|
||||
public class FCFSComparator <T> implements Comparator<T> {
|
||||
|
||||
private final Comparator<T>[] comparators;
|
||||
private final boolean reverse;
|
||||
|
||||
public FCFSComparator(Collection<Comparator<T>> comparators) {
|
||||
this(comparators, false);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public FCFSComparator(Collection<Comparator<T>> comparators, boolean reverse) {
|
||||
this.comparators = (Comparator<T>[]) comparators.toArray();
|
||||
this.reverse = reverse;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compare(T o1, T o2) {
|
||||
for (int i = 0; i < comparators.length; i++) {
|
||||
final int res = comparators[i].compare(o1, o2);
|
||||
if (res != 0) {
|
||||
return reverse ? -res : res;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
@ -5,12 +5,17 @@ import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import fr.neatmonster.nocheatplus.hooks.APIUtils;
|
||||
import fr.neatmonster.nocheatplus.utilities.FCFSComparator;
|
||||
|
||||
/**
|
||||
* The class containg the violation history of a player.
|
||||
@ -22,30 +27,29 @@ public class ViolationHistory {
|
||||
* (Comparable by time.)
|
||||
*/
|
||||
public static class ViolationLevel{
|
||||
/**
|
||||
* Descending sort.
|
||||
*/
|
||||
public static Comparator<ViolationLevel> VLComparator = new Comparator<ViolationHistory.ViolationLevel>() {
|
||||
@Override
|
||||
public int compare(final ViolationLevel vl1, final ViolationLevel vl2) {
|
||||
if (vl1.time == vl2.time) return 0;
|
||||
else if (vl1.time < vl2.time) return 1;
|
||||
else return -1;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Descending sort by time.
|
||||
*/
|
||||
public static Comparator<ViolationLevel> VLComparator = new Comparator<ViolationHistory.ViolationLevel>() {
|
||||
@Override
|
||||
public int compare(final ViolationLevel vl1, final ViolationLevel vl2) {
|
||||
return Long.compare(vl1.time, vl2.time);
|
||||
}
|
||||
};
|
||||
|
||||
/** The check. */
|
||||
public final String check;
|
||||
|
||||
/** The sum of violation levels added. */
|
||||
public double sumVL;
|
||||
|
||||
|
||||
/** Number of violations. */
|
||||
public int nVL;
|
||||
|
||||
|
||||
/** Maximal violation level added. */
|
||||
public double maxVL;
|
||||
|
||||
|
||||
|
||||
/** The last VL time. */
|
||||
public long time;
|
||||
@ -79,20 +83,128 @@ public class ViolationHistory {
|
||||
time = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object obj) {
|
||||
// Might add String.
|
||||
if (obj instanceof ViolationLevel)
|
||||
return this.check.equals(((ViolationLevel) obj).check);
|
||||
else return false;
|
||||
}
|
||||
@Override
|
||||
public boolean equals(final Object obj) {
|
||||
// Might add String.
|
||||
if (obj instanceof ViolationLevel)
|
||||
return this.check.equals(((ViolationLevel) obj).check);
|
||||
else return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return check.hashCode();
|
||||
}
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return check.hashCode();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static class VLView {
|
||||
|
||||
public static final Comparator<VLView> CmpName = new Comparator<ViolationHistory.VLView>() {
|
||||
@Override
|
||||
public int compare(VLView o1, VLView o2) {
|
||||
return o1.name.compareToIgnoreCase(o2.name);
|
||||
}
|
||||
};
|
||||
|
||||
public static final Comparator<VLView> CmpCheck = new Comparator<ViolationHistory.VLView>() {
|
||||
@Override
|
||||
public int compare(VLView o1, VLView o2) {
|
||||
return o1.check.compareToIgnoreCase(o2.check);
|
||||
}
|
||||
};
|
||||
|
||||
public static final Comparator<VLView> CmpSumVL = new Comparator<ViolationHistory.VLView>() {
|
||||
@Override
|
||||
public int compare(VLView o1, VLView o2) {
|
||||
return Double.compare(o1.sumVL, o2.sumVL);
|
||||
}
|
||||
};
|
||||
|
||||
public static final Comparator<VLView> CmpnVL = new Comparator<ViolationHistory.VLView>() {
|
||||
@Override
|
||||
public int compare(VLView o1, VLView o2) {
|
||||
return Integer.compare(o1.nVL, o2.nVL);
|
||||
}
|
||||
};
|
||||
|
||||
public static final Comparator<VLView> CmpAvgVL = new Comparator<ViolationHistory.VLView>() {
|
||||
@Override
|
||||
public int compare(VLView o1, VLView o2) {
|
||||
return Double.compare(o1.sumVL / o1.nVL, o2.sumVL / o2.nVL);
|
||||
}
|
||||
};
|
||||
|
||||
public static final Comparator<VLView> CmpMaxVL = new Comparator<ViolationHistory.VLView>() {
|
||||
@Override
|
||||
public int compare(VLView o1, VLView o2) {
|
||||
return Double.compare(o1.maxVL, o2.maxVL);
|
||||
}
|
||||
};
|
||||
|
||||
public static final Comparator<VLView> CmpTime = new Comparator<ViolationHistory.VLView>() {
|
||||
@Override
|
||||
public int compare(VLView o1, VLView o2) {
|
||||
return Long.compare(o1.time, o2.time);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a mixed/fcfs comparator from parsing given args. Accepted are
|
||||
* @param args
|
||||
* @param startIndex
|
||||
* @return If none are found, null is returned, no errors will be thrown, duplicates are removed.
|
||||
*/
|
||||
public static Comparator<VLView> parseMixedComparator(String[] args, int startIndex) {
|
||||
final Set<Comparator<VLView>> comparators = new LinkedHashSet<Comparator<VLView>>();
|
||||
for (int i = startIndex; i < args.length; i ++) {
|
||||
String arg = args[i].toLowerCase();
|
||||
while (arg.startsWith("-")) {
|
||||
arg = arg.substring(1);
|
||||
}
|
||||
if (arg.matches("(name|player|playername)")) {
|
||||
comparators.add(CmpName);
|
||||
} else if (arg.matches("(check|type|checktype)")) {
|
||||
comparators.add(CmpCheck);
|
||||
} else if (arg.matches("(sum|sumvl|vl)")) {
|
||||
comparators.add(CmpSumVL);
|
||||
} else if (arg.matches("(n|num|number|nvl)")) {
|
||||
comparators.add(CmpnVL);
|
||||
} else if (arg.matches("(avg|av|average|averagevl|avgvl|avvl|avl)")) {
|
||||
comparators.add(CmpAvgVL);
|
||||
} else if (arg.matches("(max|maxvl|maximum|maximumvl)")) {
|
||||
comparators.add(CmpMaxVL);
|
||||
} else if (arg.matches("(time|t)")) {
|
||||
comparators.add(CmpTime);
|
||||
}
|
||||
}
|
||||
if (comparators.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return new FCFSComparator<ViolationHistory.VLView>(comparators, true);
|
||||
}
|
||||
|
||||
public final String name;
|
||||
public final String check;
|
||||
public final double sumVL;
|
||||
public final int nVL;
|
||||
public final double maxVL;
|
||||
public final long time;
|
||||
|
||||
public VLView(String name, ViolationLevel vl) {
|
||||
this(name, vl.check, vl.sumVL, vl.nVL, vl.maxVL, vl.time);
|
||||
}
|
||||
|
||||
public VLView(String name, String check, double sumVL, int nVL, double maxVL, long time) {
|
||||
this.name = name;
|
||||
this.check = check;
|
||||
this.sumVL = sumVL;
|
||||
this.nVL = nVL;
|
||||
this.maxVL = maxVL;
|
||||
this.time = time;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/** Map the check string names to check types (workaround, keep at default, set by Check)*/
|
||||
static Map<String, CheckType> checkTypeMap = new HashMap<String, CheckType>();
|
||||
|
||||
@ -110,7 +222,7 @@ public class ViolationHistory {
|
||||
public static ViolationHistory getHistory(final Player player) {
|
||||
return getHistory(player.getName(), true);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the history of a player, create if desired and not present.
|
||||
* @param player
|
||||
@ -122,7 +234,7 @@ public class ViolationHistory {
|
||||
public static ViolationHistory getHistory(final Player player, final boolean create) {
|
||||
return getHistory(player.getName(), create);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the history of a player by exact name.
|
||||
* @param playerName
|
||||
@ -132,43 +244,75 @@ public class ViolationHistory {
|
||||
* @return
|
||||
*/
|
||||
public static ViolationHistory getHistory(final String playerName, final boolean create) {
|
||||
final ViolationHistory hist = violationHistories.get(playerName);
|
||||
if (hist != null)
|
||||
return hist;
|
||||
else if (create){
|
||||
final ViolationHistory newHist = new ViolationHistory();
|
||||
violationHistories.put(playerName, newHist);
|
||||
return newHist;
|
||||
}
|
||||
else
|
||||
return null;
|
||||
final ViolationHistory hist = violationHistories.get(playerName);
|
||||
if (hist != null)
|
||||
return hist;
|
||||
else if (create){
|
||||
final ViolationHistory newHist = new ViolationHistory();
|
||||
violationHistories.put(playerName, newHist);
|
||||
return newHist;
|
||||
}
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get a list of VLView instances for direct check type matches (no inheritance checks).
|
||||
* @param checkType
|
||||
* @return Always returns a list.
|
||||
*/
|
||||
public static List<VLView> getView(final CheckType checkType) {
|
||||
final List<VLView> view = new LinkedList<VLView>();
|
||||
for (final Entry<String, ViolationHistory> entry: violationHistories.entrySet()) {
|
||||
final ViolationHistory hist = entry.getValue();
|
||||
final ViolationLevel vl = hist.getViolationLevel(checkType);
|
||||
if (vl != null) {
|
||||
view.add(new VLView(entry.getKey(), vl));
|
||||
}
|
||||
}
|
||||
return view;
|
||||
}
|
||||
|
||||
public static ViolationHistory removeHistory(final String playerName){
|
||||
return violationHistories.remove(playerName);
|
||||
return violationHistories.remove(playerName);
|
||||
}
|
||||
|
||||
|
||||
public static void clear(final CheckType checkType){
|
||||
for (ViolationHistory hist : violationHistories.values()){
|
||||
hist.remove(checkType);
|
||||
}
|
||||
for (ViolationHistory hist : violationHistories.values()){
|
||||
hist.remove(checkType);
|
||||
}
|
||||
}
|
||||
|
||||
/** The violation levels for every check. */
|
||||
private final List<ViolationLevel> violationLevels = new ArrayList<ViolationLevel>();
|
||||
|
||||
/**
|
||||
* Gets the violation levels.
|
||||
* Gets the violation levels. Sorted by time, descending.
|
||||
*
|
||||
* @return the violation levels
|
||||
*/
|
||||
public ViolationLevel[] getViolationLevels() {
|
||||
final ViolationLevel[] sortedLevels = new ViolationLevel[violationLevels.size()];
|
||||
violationLevels.toArray(sortedLevels);
|
||||
Arrays.sort(sortedLevels, ViolationLevel.VLComparator); // Descending sort.;
|
||||
final ViolationLevel[] sortedLevels = new ViolationLevel[violationLevels.size()];
|
||||
violationLevels.toArray(sortedLevels);
|
||||
Arrays.sort(sortedLevels, ViolationLevel.VLComparator); // Descending sort.;
|
||||
return sortedLevels;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return only direct matches, no inheritance checking.
|
||||
* @param type
|
||||
* @return ViolationLevel instance, if present. Otherwise null.
|
||||
*/
|
||||
public ViolationLevel getViolationLevel(final CheckType type) {
|
||||
for (int i = 0; i < violationLevels.size(); i++) {
|
||||
final ViolationLevel vl = violationLevels.get(i);
|
||||
if (checkTypeMap.get(vl.check) == type) {
|
||||
return vl;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Log a VL.
|
||||
*
|
||||
@ -191,23 +335,23 @@ public class ViolationHistory {
|
||||
* @param checkType
|
||||
* @return If entries were removed.
|
||||
*/
|
||||
public boolean remove(final CheckType checkType) {
|
||||
if (checkType == CheckType.ALL){
|
||||
final boolean empty = violationLevels.isEmpty();
|
||||
violationLevels.clear();
|
||||
return !empty;
|
||||
}
|
||||
final Iterator<ViolationLevel> it = violationLevels.iterator();
|
||||
boolean found = false;
|
||||
while (it.hasNext()){
|
||||
final ViolationLevel vl = it.next();
|
||||
final CheckType refType = checkTypeMap.get(vl.check);
|
||||
if (refType == null) continue;
|
||||
if (refType == checkType || APIUtils.isParent(checkType, refType)){
|
||||
found = true;
|
||||
it.remove();
|
||||
}
|
||||
}
|
||||
return found;
|
||||
}
|
||||
public boolean remove(final CheckType checkType) {
|
||||
if (checkType == CheckType.ALL){
|
||||
final boolean empty = violationLevels.isEmpty();
|
||||
violationLevels.clear();
|
||||
return !empty;
|
||||
}
|
||||
final Iterator<ViolationLevel> it = violationLevels.iterator();
|
||||
boolean found = false;
|
||||
while (it.hasNext()){
|
||||
final ViolationLevel vl = it.next();
|
||||
final CheckType refType = checkTypeMap.get(vl.check);
|
||||
if (refType == null) continue;
|
||||
if (refType == checkType || APIUtils.isParent(checkType, refType)){
|
||||
found = true;
|
||||
it.remove();
|
||||
}
|
||||
}
|
||||
return found;
|
||||
}
|
||||
}
|
||||
|
@ -31,6 +31,7 @@ import fr.neatmonster.nocheatplus.command.admin.exemption.UnexemptCommand;
|
||||
import fr.neatmonster.nocheatplus.command.admin.log.LogCommand;
|
||||
import fr.neatmonster.nocheatplus.command.admin.notify.NotifyCommand;
|
||||
import fr.neatmonster.nocheatplus.command.admin.reset.ResetCommand;
|
||||
import fr.neatmonster.nocheatplus.command.admin.top.TopCommand;
|
||||
import fr.neatmonster.nocheatplus.components.INotifyReload;
|
||||
import fr.neatmonster.nocheatplus.config.ConfPaths;
|
||||
import fr.neatmonster.nocheatplus.config.ConfigFile;
|
||||
@ -69,7 +70,7 @@ public class NoCheatPlusCommand extends BaseCommand{
|
||||
}
|
||||
}
|
||||
|
||||
private Set<String> rootLabels = new LinkedHashSet<String>();
|
||||
private Set<String> rootLabels = new LinkedHashSet<String>();
|
||||
|
||||
/**
|
||||
* Instantiates a new command handler.
|
||||
@ -78,45 +79,46 @@ public class NoCheatPlusCommand extends BaseCommand{
|
||||
* the instance of NoCheatPlus
|
||||
*/
|
||||
public NoCheatPlusCommand(final JavaPlugin plugin, final List<INotifyReload> notifyReload) {
|
||||
super(plugin, "nocheatplus", null, new String[]{"ncp"});
|
||||
super(plugin, "nocheatplus", null, new String[]{"ncp"});
|
||||
// Register sub commands (special order):
|
||||
for (BaseCommand cmd : new BaseCommand[]{
|
||||
new BanCommand(plugin),
|
||||
new CommandsCommand(plugin),
|
||||
new DelayCommand(plugin),
|
||||
new ExemptCommand(plugin),
|
||||
new ExemptionsCommand(plugin),
|
||||
new InfoCommand(plugin),
|
||||
new InspectCommand(plugin),
|
||||
new KickCommand(plugin),
|
||||
new KickListCommand(plugin),
|
||||
new LagCommand(plugin),
|
||||
new VersionCommand(plugin),
|
||||
new NotifyCommand(plugin),
|
||||
new ReloadCommand(plugin, notifyReload),
|
||||
new RemovePlayerCommand(plugin),
|
||||
new TellCommand(plugin),
|
||||
new DenyLoginCommand(plugin),
|
||||
new UnexemptCommand(plugin),
|
||||
new AllowLoginCommand(plugin),
|
||||
new LogCommand(plugin),
|
||||
new ResetCommand(plugin),
|
||||
new BanCommand(plugin),
|
||||
new CommandsCommand(plugin),
|
||||
new DelayCommand(plugin),
|
||||
new ExemptCommand(plugin),
|
||||
new ExemptionsCommand(plugin),
|
||||
new TopCommand(plugin),
|
||||
new InfoCommand(plugin),
|
||||
new InspectCommand(plugin),
|
||||
new KickCommand(plugin),
|
||||
new KickListCommand(plugin),
|
||||
new LagCommand(plugin),
|
||||
new VersionCommand(plugin),
|
||||
new NotifyCommand(plugin),
|
||||
new ReloadCommand(plugin, notifyReload),
|
||||
new RemovePlayerCommand(plugin),
|
||||
new TellCommand(plugin),
|
||||
new DenyLoginCommand(plugin),
|
||||
new UnexemptCommand(plugin),
|
||||
new AllowLoginCommand(plugin),
|
||||
new LogCommand(plugin),
|
||||
new ResetCommand(plugin),
|
||||
}){
|
||||
addSubCommands(cmd);
|
||||
rootLabels.add(cmd.label);
|
||||
addSubCommands(cmd);
|
||||
rootLabels.add(cmd.label);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieve a collection with all sub-command permissions.
|
||||
* @return
|
||||
*/
|
||||
public Collection<String> getAllSubCommandPermissions(){
|
||||
final Set<String> set = new LinkedHashSet<String>(rootLabels.size());
|
||||
for (final String label : rootLabels){
|
||||
set.add(subCommands.get(label).permission);
|
||||
}
|
||||
return set;
|
||||
final Set<String> set = new LinkedHashSet<String>(rootLabels.size());
|
||||
for (final String label : rootLabels){
|
||||
set.add(subCommands.get(label).permission);
|
||||
}
|
||||
return set;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
@ -126,82 +128,88 @@ public class NoCheatPlusCommand extends BaseCommand{
|
||||
@Override
|
||||
public boolean onCommand(final CommandSender sender, final Command command, final String commandLabel,
|
||||
final String[] args) {
|
||||
|
||||
|
||||
if (!command.getName().equalsIgnoreCase("nocheatplus")){
|
||||
// Not our command, how did it get here?
|
||||
return false;
|
||||
// Not our command, how did it get here?
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
if (sender.hasPermission(Permissions.FILTER_COMMAND_NOCHEATPLUS)){
|
||||
// Check sub-commands.
|
||||
// Check sub-commands.
|
||||
if (args.length > 0){
|
||||
AbstractCommand<?> subCommand = subCommands.get(args[0].trim().toLowerCase());
|
||||
if (subCommand != null && subCommand.testPermission(sender, command, commandLabel, args)){
|
||||
// Sender has permission to run the command.
|
||||
return subCommand.onCommand(sender, command, commandLabel, args);
|
||||
}
|
||||
AbstractCommand<?> subCommand = subCommands.get(args[0].trim().toLowerCase());
|
||||
if (subCommand != null && subCommand.testPermission(sender, command, commandLabel, args)){
|
||||
// Sender has permission to run the command.
|
||||
final boolean res = subCommand.onCommand(sender, command, commandLabel, args);
|
||||
if (!res && subCommand.usage != null) {
|
||||
sender.sendMessage(subCommand.usage);
|
||||
return true;
|
||||
} else {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
}
|
||||
// No sub command worked, print usage.
|
||||
return false;
|
||||
// No sub command worked, print usage.
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
final ConfigFile config = ConfigManager.getConfigFile();
|
||||
if (config.getBoolean(ConfPaths.PROTECT_PLUGINS_HIDE_ACTIVE)){
|
||||
// Prevent the NCP usage printout:
|
||||
// TODO: GetColoredString
|
||||
sender.sendMessage(ColorUtil.replaceColors(config.getString(ConfPaths.PROTECT_PLUGINS_HIDE_NOCOMMAND_MSG)));
|
||||
return true;
|
||||
// Prevent the NCP usage printout:
|
||||
// TODO: GetColoredString
|
||||
sender.sendMessage(ColorUtil.replaceColors(config.getString(ConfPaths.PROTECT_PLUGINS_HIDE_NOCOMMAND_MSG)));
|
||||
return true;
|
||||
}
|
||||
else{
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// /**
|
||||
// * Check which of the choices starts with prefix
|
||||
// * @param sender
|
||||
// * @param choices
|
||||
// * @return
|
||||
// */
|
||||
// protected List<String> getTabMatches(CommandSender sender, Collection<String> choices, String prefix){
|
||||
// final List<String> res = new ArrayList<String>(choices.size());
|
||||
// final Set<BaseCommand> done = new HashSet<BaseCommand>();
|
||||
// for (final String label : choices){
|
||||
// if (!label.startsWith(prefix)) continue;
|
||||
// final BaseCommand cmd = commands.get(label);
|
||||
// if (done.contains(cmd)) continue;
|
||||
// done.add(cmd);
|
||||
// if (sender.hasPermission(cmd.permission)) res.add(cmd.label);
|
||||
// }
|
||||
// if (!res.isEmpty()){
|
||||
// Collections.sort(res);
|
||||
// return res;
|
||||
// }
|
||||
// return null;
|
||||
// }
|
||||
// /**
|
||||
// * Check which of the choices starts with prefix
|
||||
// * @param sender
|
||||
// * @param choices
|
||||
// * @return
|
||||
// */
|
||||
// protected List<String> getTabMatches(CommandSender sender, Collection<String> choices, String prefix){
|
||||
// final List<String> res = new ArrayList<String>(choices.size());
|
||||
// final Set<BaseCommand> done = new HashSet<BaseCommand>();
|
||||
// for (final String label : choices){
|
||||
// if (!label.startsWith(prefix)) continue;
|
||||
// final BaseCommand cmd = commands.get(label);
|
||||
// if (done.contains(cmd)) continue;
|
||||
// done.add(cmd);
|
||||
// if (sender.hasPermission(cmd.permission)) res.add(cmd.label);
|
||||
// }
|
||||
// if (!res.isEmpty()){
|
||||
// Collections.sort(res);
|
||||
// return res;
|
||||
// }
|
||||
// return null;
|
||||
// }
|
||||
|
||||
// @Override
|
||||
// public List<String> onTabComplete(CommandSender sender, Command command, String alias, String[] args)
|
||||
// {
|
||||
// // TODO: TabComplete check ?
|
||||
// if (args.length == 0 || args.length == 1 && args[0].trim().isEmpty()){
|
||||
// // Add labels without aliases.
|
||||
// return getTabMatches(sender, rootLabels, "");
|
||||
// }
|
||||
// else {
|
||||
// final String subLabel = args[0].trim().toLowerCase();
|
||||
// if (args.length == 1){
|
||||
// // Also check aliases for matches.
|
||||
// return getTabMatches(sender, commands.keySet(), subLabel);
|
||||
// }
|
||||
// else{
|
||||
// final NCPCommand cmd = commands.get(subLabel);
|
||||
// if (cmd.testPermission...){
|
||||
// // Delegate the tab-completion.
|
||||
// return cmd.onTabComplete(sender, command, alias, args);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// return null;
|
||||
// }
|
||||
// @Override
|
||||
// public List<String> onTabComplete(CommandSender sender, Command command, String alias, String[] args)
|
||||
// {
|
||||
// // TODO: TabComplete check ?
|
||||
// if (args.length == 0 || args.length == 1 && args[0].trim().isEmpty()){
|
||||
// // Add labels without aliases.
|
||||
// return getTabMatches(sender, rootLabels, "");
|
||||
// }
|
||||
// else {
|
||||
// final String subLabel = args[0].trim().toLowerCase();
|
||||
// if (args.length == 1){
|
||||
// // Also check aliases for matches.
|
||||
// return getTabMatches(sender, commands.keySet(), subLabel);
|
||||
// }
|
||||
// else{
|
||||
// final NCPCommand cmd = commands.get(subLabel);
|
||||
// if (cmd.testPermission...){
|
||||
// // Delegate the tab-completion.
|
||||
// return cmd.onTabComplete(sender, command, alias, args);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// return null;
|
||||
// }
|
||||
}
|
||||
|
@ -0,0 +1,202 @@
|
||||
package fr.neatmonster.nocheatplus.command.admin.top;
|
||||
|
||||
import java.text.DecimalFormat;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
import fr.neatmonster.nocheatplus.checks.CheckType;
|
||||
import fr.neatmonster.nocheatplus.checks.ViolationHistory;
|
||||
import fr.neatmonster.nocheatplus.checks.ViolationHistory.VLView;
|
||||
import fr.neatmonster.nocheatplus.command.BaseCommand;
|
||||
import fr.neatmonster.nocheatplus.hooks.APIUtils;
|
||||
import fr.neatmonster.nocheatplus.permissions.Permissions;
|
||||
import fr.neatmonster.nocheatplus.utilities.FCFSComparator;
|
||||
|
||||
public class TopCommand extends BaseCommand{
|
||||
|
||||
protected static class PrimaryThreadWorker implements Runnable{
|
||||
private final Collection<CheckType> checkTypes;
|
||||
private final CommandSender sender;
|
||||
private final Comparator<VLView> comparator;
|
||||
private final int n;
|
||||
private final Plugin plugin;
|
||||
public PrimaryThreadWorker(CommandSender sender, Collection<CheckType> checkTypes, Comparator<VLView> comparator, int n, Plugin plugin) {
|
||||
this.checkTypes = new LinkedHashSet<CheckType>(checkTypes);
|
||||
this.sender = sender;
|
||||
this.comparator = comparator;
|
||||
this.n = n;
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
final Iterator<CheckType> it = checkTypes.iterator();
|
||||
List<VLView> views = null;
|
||||
CheckType type = null;
|
||||
while (it.hasNext()) {
|
||||
type = it.next();
|
||||
it.remove();
|
||||
views = ViolationHistory.getView(type);
|
||||
if (views.isEmpty()) {
|
||||
views = null;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (views == null) {
|
||||
sender.sendMessage("No more history to process.");
|
||||
} else {
|
||||
// Start sorting and result processing asynchronously.
|
||||
Bukkit.getScheduler().runTaskAsynchronously(plugin,
|
||||
new AsynchronousWorker(sender, type, views, checkTypes, comparator, n, plugin));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected static class AsynchronousWorker implements Runnable{
|
||||
private final CommandSender sender;
|
||||
private final CheckType checkType;
|
||||
private final List<VLView> views;
|
||||
private final Collection<CheckType> checkTypes;
|
||||
private final Comparator<VLView> comparator;
|
||||
private final int n;
|
||||
private final Plugin plugin;
|
||||
public AsynchronousWorker(CommandSender sender, CheckType checkType, List<VLView> views, Collection<CheckType> checkTypes, Comparator<VLView> comparator, int n, Plugin plugin) {
|
||||
this.sender = sender;
|
||||
this.checkType = checkType;
|
||||
this.views = views;
|
||||
this.checkTypes = checkTypes;
|
||||
this.comparator = comparator;
|
||||
this.n = n;
|
||||
this.plugin = plugin;
|
||||
}
|
||||
@Override
|
||||
public void run() {
|
||||
final DecimalFormat format = new DecimalFormat("#.#");
|
||||
// Sort
|
||||
Collections.sort(views, comparator);
|
||||
// Display.
|
||||
final StringBuilder builder = new StringBuilder(100 + 32 * views.size());
|
||||
builder.append(checkType.toString());
|
||||
builder.append(":");
|
||||
final String c1, c2;
|
||||
if (sender instanceof Player) {
|
||||
c1 = ChatColor.WHITE.toString();
|
||||
c2 = ChatColor.GRAY.toString();
|
||||
} else {
|
||||
c1 = c2 = "";
|
||||
}
|
||||
int done = 0;
|
||||
for (final VLView view : views) {
|
||||
builder.append(" " + c1);
|
||||
builder.append(view.name);
|
||||
// Details
|
||||
builder.append(c2 + "(");
|
||||
// sum
|
||||
builder.append("sum=");
|
||||
builder.append(format.format(view.sumVL));
|
||||
// n
|
||||
builder.append("/n=");
|
||||
builder.append(view.nVL);
|
||||
// avg
|
||||
builder.append("/avg=");
|
||||
builder.append(format.format(view.sumVL / view.nVL));
|
||||
// max
|
||||
builder.append("/max=");
|
||||
builder.append(format.format(view.maxVL));
|
||||
builder.append(")");
|
||||
if (done >= n) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (views.isEmpty()) {
|
||||
builder.append(c1 + "Nothing to display.");
|
||||
}
|
||||
final String message = builder.toString();
|
||||
Bukkit.getScheduler().scheduleSyncDelayedTask(plugin,
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
sender.sendMessage(message);
|
||||
}
|
||||
});
|
||||
if (!checkTypes.isEmpty()) {
|
||||
Bukkit.getScheduler().scheduleSyncDelayedTask(plugin,
|
||||
new PrimaryThreadWorker(sender, checkTypes, comparator, n, plugin));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public TopCommand(JavaPlugin plugin) {
|
||||
super(plugin, "top", Permissions.COMMAND_TOP);
|
||||
this.usage = "Optional: Specify number of entries to show (once).\nObligatory: Specify check types (multiple possible).\nOptional: Specify what to sort by (multiple possible: -sumvl, -avgvl, -maxvl, -nvl, -name, -time).\nThis is a heavy operation, use with care."; // -check
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCommand(CommandSender sender, Command command, String alias, String[] args) {
|
||||
if (args.length < 2) {
|
||||
return false;
|
||||
}
|
||||
int startIndex = 1;
|
||||
Integer n = 10;
|
||||
try {
|
||||
n = Integer.parseInt(args[1].trim());
|
||||
startIndex = 2;
|
||||
} catch (NumberFormatException e) {}
|
||||
if (n <= 0) {
|
||||
sender.sendMessage("Setting number of entries to 10");
|
||||
n = 1;
|
||||
} else if ((sender instanceof Player) && n > 300) {
|
||||
sender.sendMessage("Capping number of entries at 300.");
|
||||
n = 300;
|
||||
} else if (n > 10000) {
|
||||
sender.sendMessage("Capping number of entries at 10000.");
|
||||
n = 10000;
|
||||
}
|
||||
|
||||
Set<CheckType> checkTypes = new LinkedHashSet<CheckType>();
|
||||
for (int i = startIndex; i < args.length; i ++) {
|
||||
CheckType type = null;
|
||||
try {
|
||||
type = CheckType.valueOf(args[i].trim().toUpperCase().replace('-', '_').replace('.', '_'));
|
||||
} catch (Throwable t) {} // ...
|
||||
if (type != null) {
|
||||
checkTypes.addAll(APIUtils.getChildren(type)); // Includes type.
|
||||
}
|
||||
}
|
||||
if (checkTypes.isEmpty()) {
|
||||
sender.sendMessage("No check types specified!");
|
||||
return false;
|
||||
}
|
||||
|
||||
Comparator<VLView> comparator = VLView.parseMixedComparator(args, startIndex);
|
||||
if (comparator == null) {
|
||||
// TODO: Default comparator ?
|
||||
comparator = new FCFSComparator<ViolationHistory.VLView>(Arrays.asList(VLView.CmpnVL), true);
|
||||
}
|
||||
|
||||
// Run a worker task.
|
||||
Bukkit.getScheduler().scheduleSyncDelayedTask(access,
|
||||
new PrimaryThreadWorker(sender, checkTypes, comparator, n, access));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO: Tab completion (!).
|
||||
|
||||
}
|
@ -35,6 +35,7 @@ public class Permissions {
|
||||
public static final String COMMAND_RELOAD = COMMAND + ".reload";
|
||||
public static final String COMMAND_REMOVEPLAYER = COMMAND + ".removeplayer";
|
||||
public static final String COMMAND_RESET = COMMAND + ".reset";
|
||||
public static final String COMMAND_TOP = COMMAND + ".top";
|
||||
public static final String COMMAND_UNEXEMPT = COMMAND + ".unexempt";
|
||||
public static final String COMMAND_VERSION = COMMAND + ".version";
|
||||
|
||||
|
@ -19,6 +19,7 @@ commands:
|
||||
# permissions: nocheatplus.admin.(...)
|
||||
usage: |
|
||||
Administrative commands overview:
|
||||
/<command> top (entries) (check/s...) (sort by...) NEW.
|
||||
/<command> info (player): Violation summary for a player.
|
||||
/<command> inspect (player): Status info for a player.
|
||||
/<command> notify on|off: In-game notifications per player.
|
||||
@ -267,6 +268,8 @@ permissions:
|
||||
nocheatplus.notify: true
|
||||
nocheatplus.command.reload:
|
||||
description: Allow the player to reload NoCheatPlus configuration.
|
||||
nocheatplus.command.top:
|
||||
description: Allow to search violation history for top violations.
|
||||
nocheatplus.command.info:
|
||||
description: Allow to see violation info about a player.
|
||||
nocheatplus.command.inspect:
|
||||
@ -328,6 +331,7 @@ permissions:
|
||||
description: Info commands about players.
|
||||
children:
|
||||
nocheatplus.command.notify: true
|
||||
nocheatplus.command.top: true
|
||||
nocheatplus.command.info: true
|
||||
nocheatplus.command.exemptions: true
|
||||
nocheatplus.command.kicklist: true
|
||||
|
Loading…
Reference in New Issue
Block a user