mirror of
https://github.com/NoCheatPlus/NoCheatPlus.git
synced 2025-01-30 03:11: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.Comparator;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
import fr.neatmonster.nocheatplus.hooks.APIUtils;
|
import fr.neatmonster.nocheatplus.hooks.APIUtils;
|
||||||
|
import fr.neatmonster.nocheatplus.utilities.FCFSComparator;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The class containg the violation history of a player.
|
* The class containg the violation history of a player.
|
||||||
@ -22,30 +27,29 @@ public class ViolationHistory {
|
|||||||
* (Comparable by time.)
|
* (Comparable by time.)
|
||||||
*/
|
*/
|
||||||
public static class ViolationLevel{
|
public static class ViolationLevel{
|
||||||
/**
|
|
||||||
* Descending sort.
|
/**
|
||||||
*/
|
* Descending sort by time.
|
||||||
public static Comparator<ViolationLevel> VLComparator = new Comparator<ViolationHistory.ViolationLevel>() {
|
*/
|
||||||
@Override
|
public static Comparator<ViolationLevel> VLComparator = new Comparator<ViolationHistory.ViolationLevel>() {
|
||||||
public int compare(final ViolationLevel vl1, final ViolationLevel vl2) {
|
@Override
|
||||||
if (vl1.time == vl2.time) return 0;
|
public int compare(final ViolationLevel vl1, final ViolationLevel vl2) {
|
||||||
else if (vl1.time < vl2.time) return 1;
|
return Long.compare(vl1.time, vl2.time);
|
||||||
else return -1;
|
}
|
||||||
}
|
};
|
||||||
};
|
|
||||||
|
|
||||||
/** The check. */
|
/** The check. */
|
||||||
public final String check;
|
public final String check;
|
||||||
|
|
||||||
/** The sum of violation levels added. */
|
/** The sum of violation levels added. */
|
||||||
public double sumVL;
|
public double sumVL;
|
||||||
|
|
||||||
/** Number of violations. */
|
/** Number of violations. */
|
||||||
public int nVL;
|
public int nVL;
|
||||||
|
|
||||||
/** Maximal violation level added. */
|
/** Maximal violation level added. */
|
||||||
public double maxVL;
|
public double maxVL;
|
||||||
|
|
||||||
|
|
||||||
/** The last VL time. */
|
/** The last VL time. */
|
||||||
public long time;
|
public long time;
|
||||||
@ -79,20 +83,128 @@ public class ViolationHistory {
|
|||||||
time = System.currentTimeMillis();
|
time = System.currentTimeMillis();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(final Object obj) {
|
public boolean equals(final Object obj) {
|
||||||
// Might add String.
|
// Might add String.
|
||||||
if (obj instanceof ViolationLevel)
|
if (obj instanceof ViolationLevel)
|
||||||
return this.check.equals(((ViolationLevel) obj).check);
|
return this.check.equals(((ViolationLevel) obj).check);
|
||||||
else return false;
|
else return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return check.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)*/
|
/** Map the check string names to check types (workaround, keep at default, set by Check)*/
|
||||||
static Map<String, CheckType> checkTypeMap = new HashMap<String, CheckType>();
|
static Map<String, CheckType> checkTypeMap = new HashMap<String, CheckType>();
|
||||||
|
|
||||||
@ -110,7 +222,7 @@ public class ViolationHistory {
|
|||||||
public static ViolationHistory getHistory(final Player player) {
|
public static ViolationHistory getHistory(final Player player) {
|
||||||
return getHistory(player.getName(), true);
|
return getHistory(player.getName(), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the history of a player, create if desired and not present.
|
* Get the history of a player, create if desired and not present.
|
||||||
* @param player
|
* @param player
|
||||||
@ -122,7 +234,7 @@ public class ViolationHistory {
|
|||||||
public static ViolationHistory getHistory(final Player player, final boolean create) {
|
public static ViolationHistory getHistory(final Player player, final boolean create) {
|
||||||
return getHistory(player.getName(), create);
|
return getHistory(player.getName(), create);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the history of a player by exact name.
|
* Gets the history of a player by exact name.
|
||||||
* @param playerName
|
* @param playerName
|
||||||
@ -132,43 +244,75 @@ public class ViolationHistory {
|
|||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public static ViolationHistory getHistory(final String playerName, final boolean create) {
|
public static ViolationHistory getHistory(final String playerName, final boolean create) {
|
||||||
final ViolationHistory hist = violationHistories.get(playerName);
|
final ViolationHistory hist = violationHistories.get(playerName);
|
||||||
if (hist != null)
|
if (hist != null)
|
||||||
return hist;
|
return hist;
|
||||||
else if (create){
|
else if (create){
|
||||||
final ViolationHistory newHist = new ViolationHistory();
|
final ViolationHistory newHist = new ViolationHistory();
|
||||||
violationHistories.put(playerName, newHist);
|
violationHistories.put(playerName, newHist);
|
||||||
return newHist;
|
return newHist;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
return null;
|
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){
|
public static ViolationHistory removeHistory(final String playerName){
|
||||||
return violationHistories.remove(playerName);
|
return violationHistories.remove(playerName);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void clear(final CheckType checkType){
|
public static void clear(final CheckType checkType){
|
||||||
for (ViolationHistory hist : violationHistories.values()){
|
for (ViolationHistory hist : violationHistories.values()){
|
||||||
hist.remove(checkType);
|
hist.remove(checkType);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** The violation levels for every check. */
|
/** The violation levels for every check. */
|
||||||
private final List<ViolationLevel> violationLevels = new ArrayList<ViolationLevel>();
|
private final List<ViolationLevel> violationLevels = new ArrayList<ViolationLevel>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the violation levels.
|
* Gets the violation levels. Sorted by time, descending.
|
||||||
*
|
*
|
||||||
* @return the violation levels
|
* @return the violation levels
|
||||||
*/
|
*/
|
||||||
public ViolationLevel[] getViolationLevels() {
|
public ViolationLevel[] getViolationLevels() {
|
||||||
final ViolationLevel[] sortedLevels = new ViolationLevel[violationLevels.size()];
|
final ViolationLevel[] sortedLevels = new ViolationLevel[violationLevels.size()];
|
||||||
violationLevels.toArray(sortedLevels);
|
violationLevels.toArray(sortedLevels);
|
||||||
Arrays.sort(sortedLevels, ViolationLevel.VLComparator); // Descending sort.;
|
Arrays.sort(sortedLevels, ViolationLevel.VLComparator); // Descending sort.;
|
||||||
return sortedLevels;
|
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.
|
* Log a VL.
|
||||||
*
|
*
|
||||||
@ -191,23 +335,23 @@ public class ViolationHistory {
|
|||||||
* @param checkType
|
* @param checkType
|
||||||
* @return If entries were removed.
|
* @return If entries were removed.
|
||||||
*/
|
*/
|
||||||
public boolean remove(final CheckType checkType) {
|
public boolean remove(final CheckType checkType) {
|
||||||
if (checkType == CheckType.ALL){
|
if (checkType == CheckType.ALL){
|
||||||
final boolean empty = violationLevels.isEmpty();
|
final boolean empty = violationLevels.isEmpty();
|
||||||
violationLevels.clear();
|
violationLevels.clear();
|
||||||
return !empty;
|
return !empty;
|
||||||
}
|
}
|
||||||
final Iterator<ViolationLevel> it = violationLevels.iterator();
|
final Iterator<ViolationLevel> it = violationLevels.iterator();
|
||||||
boolean found = false;
|
boolean found = false;
|
||||||
while (it.hasNext()){
|
while (it.hasNext()){
|
||||||
final ViolationLevel vl = it.next();
|
final ViolationLevel vl = it.next();
|
||||||
final CheckType refType = checkTypeMap.get(vl.check);
|
final CheckType refType = checkTypeMap.get(vl.check);
|
||||||
if (refType == null) continue;
|
if (refType == null) continue;
|
||||||
if (refType == checkType || APIUtils.isParent(checkType, refType)){
|
if (refType == checkType || APIUtils.isParent(checkType, refType)){
|
||||||
found = true;
|
found = true;
|
||||||
it.remove();
|
it.remove();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return found;
|
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.log.LogCommand;
|
||||||
import fr.neatmonster.nocheatplus.command.admin.notify.NotifyCommand;
|
import fr.neatmonster.nocheatplus.command.admin.notify.NotifyCommand;
|
||||||
import fr.neatmonster.nocheatplus.command.admin.reset.ResetCommand;
|
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.components.INotifyReload;
|
||||||
import fr.neatmonster.nocheatplus.config.ConfPaths;
|
import fr.neatmonster.nocheatplus.config.ConfPaths;
|
||||||
import fr.neatmonster.nocheatplus.config.ConfigFile;
|
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.
|
* Instantiates a new command handler.
|
||||||
@ -78,45 +79,46 @@ public class NoCheatPlusCommand extends BaseCommand{
|
|||||||
* the instance of NoCheatPlus
|
* the instance of NoCheatPlus
|
||||||
*/
|
*/
|
||||||
public NoCheatPlusCommand(final JavaPlugin plugin, final List<INotifyReload> notifyReload) {
|
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):
|
// Register sub commands (special order):
|
||||||
for (BaseCommand cmd : new BaseCommand[]{
|
for (BaseCommand cmd : new BaseCommand[]{
|
||||||
new BanCommand(plugin),
|
new BanCommand(plugin),
|
||||||
new CommandsCommand(plugin),
|
new CommandsCommand(plugin),
|
||||||
new DelayCommand(plugin),
|
new DelayCommand(plugin),
|
||||||
new ExemptCommand(plugin),
|
new ExemptCommand(plugin),
|
||||||
new ExemptionsCommand(plugin),
|
new ExemptionsCommand(plugin),
|
||||||
new InfoCommand(plugin),
|
new TopCommand(plugin),
|
||||||
new InspectCommand(plugin),
|
new InfoCommand(plugin),
|
||||||
new KickCommand(plugin),
|
new InspectCommand(plugin),
|
||||||
new KickListCommand(plugin),
|
new KickCommand(plugin),
|
||||||
new LagCommand(plugin),
|
new KickListCommand(plugin),
|
||||||
new VersionCommand(plugin),
|
new LagCommand(plugin),
|
||||||
new NotifyCommand(plugin),
|
new VersionCommand(plugin),
|
||||||
new ReloadCommand(plugin, notifyReload),
|
new NotifyCommand(plugin),
|
||||||
new RemovePlayerCommand(plugin),
|
new ReloadCommand(plugin, notifyReload),
|
||||||
new TellCommand(plugin),
|
new RemovePlayerCommand(plugin),
|
||||||
new DenyLoginCommand(plugin),
|
new TellCommand(plugin),
|
||||||
new UnexemptCommand(plugin),
|
new DenyLoginCommand(plugin),
|
||||||
new AllowLoginCommand(plugin),
|
new UnexemptCommand(plugin),
|
||||||
new LogCommand(plugin),
|
new AllowLoginCommand(plugin),
|
||||||
new ResetCommand(plugin),
|
new LogCommand(plugin),
|
||||||
|
new ResetCommand(plugin),
|
||||||
}){
|
}){
|
||||||
addSubCommands(cmd);
|
addSubCommands(cmd);
|
||||||
rootLabels.add(cmd.label);
|
rootLabels.add(cmd.label);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve a collection with all sub-command permissions.
|
* Retrieve a collection with all sub-command permissions.
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public Collection<String> getAllSubCommandPermissions(){
|
public Collection<String> getAllSubCommandPermissions(){
|
||||||
final Set<String> set = new LinkedHashSet<String>(rootLabels.size());
|
final Set<String> set = new LinkedHashSet<String>(rootLabels.size());
|
||||||
for (final String label : rootLabels){
|
for (final String label : rootLabels){
|
||||||
set.add(subCommands.get(label).permission);
|
set.add(subCommands.get(label).permission);
|
||||||
}
|
}
|
||||||
return set;
|
return set;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* (non-Javadoc)
|
/* (non-Javadoc)
|
||||||
@ -126,82 +128,88 @@ public class NoCheatPlusCommand extends BaseCommand{
|
|||||||
@Override
|
@Override
|
||||||
public boolean onCommand(final CommandSender sender, final Command command, final String commandLabel,
|
public boolean onCommand(final CommandSender sender, final Command command, final String commandLabel,
|
||||||
final String[] args) {
|
final String[] args) {
|
||||||
|
|
||||||
if (!command.getName().equalsIgnoreCase("nocheatplus")){
|
if (!command.getName().equalsIgnoreCase("nocheatplus")){
|
||||||
// Not our command, how did it get here?
|
// Not our command, how did it get here?
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sender.hasPermission(Permissions.FILTER_COMMAND_NOCHEATPLUS)){
|
if (sender.hasPermission(Permissions.FILTER_COMMAND_NOCHEATPLUS)){
|
||||||
// Check sub-commands.
|
// Check sub-commands.
|
||||||
if (args.length > 0){
|
if (args.length > 0){
|
||||||
AbstractCommand<?> subCommand = subCommands.get(args[0].trim().toLowerCase());
|
AbstractCommand<?> subCommand = subCommands.get(args[0].trim().toLowerCase());
|
||||||
if (subCommand != null && subCommand.testPermission(sender, command, commandLabel, args)){
|
if (subCommand != null && subCommand.testPermission(sender, command, commandLabel, args)){
|
||||||
// Sender has permission to run the command.
|
// Sender has permission to run the command.
|
||||||
return subCommand.onCommand(sender, command, commandLabel, args);
|
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.
|
// No sub command worked, print usage.
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
final ConfigFile config = ConfigManager.getConfigFile();
|
final ConfigFile config = ConfigManager.getConfigFile();
|
||||||
if (config.getBoolean(ConfPaths.PROTECT_PLUGINS_HIDE_ACTIVE)){
|
if (config.getBoolean(ConfPaths.PROTECT_PLUGINS_HIDE_ACTIVE)){
|
||||||
// Prevent the NCP usage printout:
|
// Prevent the NCP usage printout:
|
||||||
// TODO: GetColoredString
|
// TODO: GetColoredString
|
||||||
sender.sendMessage(ColorUtil.replaceColors(config.getString(ConfPaths.PROTECT_PLUGINS_HIDE_NOCOMMAND_MSG)));
|
sender.sendMessage(ColorUtil.replaceColors(config.getString(ConfPaths.PROTECT_PLUGINS_HIDE_NOCOMMAND_MSG)));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// /**
|
// /**
|
||||||
// * Check which of the choices starts with prefix
|
// * Check which of the choices starts with prefix
|
||||||
// * @param sender
|
// * @param sender
|
||||||
// * @param choices
|
// * @param choices
|
||||||
// * @return
|
// * @return
|
||||||
// */
|
// */
|
||||||
// protected List<String> getTabMatches(CommandSender sender, Collection<String> choices, String prefix){
|
// protected List<String> getTabMatches(CommandSender sender, Collection<String> choices, String prefix){
|
||||||
// final List<String> res = new ArrayList<String>(choices.size());
|
// final List<String> res = new ArrayList<String>(choices.size());
|
||||||
// final Set<BaseCommand> done = new HashSet<BaseCommand>();
|
// final Set<BaseCommand> done = new HashSet<BaseCommand>();
|
||||||
// for (final String label : choices){
|
// for (final String label : choices){
|
||||||
// if (!label.startsWith(prefix)) continue;
|
// if (!label.startsWith(prefix)) continue;
|
||||||
// final BaseCommand cmd = commands.get(label);
|
// final BaseCommand cmd = commands.get(label);
|
||||||
// if (done.contains(cmd)) continue;
|
// if (done.contains(cmd)) continue;
|
||||||
// done.add(cmd);
|
// done.add(cmd);
|
||||||
// if (sender.hasPermission(cmd.permission)) res.add(cmd.label);
|
// if (sender.hasPermission(cmd.permission)) res.add(cmd.label);
|
||||||
// }
|
// }
|
||||||
// if (!res.isEmpty()){
|
// if (!res.isEmpty()){
|
||||||
// Collections.sort(res);
|
// Collections.sort(res);
|
||||||
// return res;
|
// return res;
|
||||||
// }
|
// }
|
||||||
// return null;
|
// return null;
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// @Override
|
// @Override
|
||||||
// public List<String> onTabComplete(CommandSender sender, Command command, String alias, String[] args)
|
// public List<String> onTabComplete(CommandSender sender, Command command, String alias, String[] args)
|
||||||
// {
|
// {
|
||||||
// // TODO: TabComplete check ?
|
// // TODO: TabComplete check ?
|
||||||
// if (args.length == 0 || args.length == 1 && args[0].trim().isEmpty()){
|
// if (args.length == 0 || args.length == 1 && args[0].trim().isEmpty()){
|
||||||
// // Add labels without aliases.
|
// // Add labels without aliases.
|
||||||
// return getTabMatches(sender, rootLabels, "");
|
// return getTabMatches(sender, rootLabels, "");
|
||||||
// }
|
// }
|
||||||
// else {
|
// else {
|
||||||
// final String subLabel = args[0].trim().toLowerCase();
|
// final String subLabel = args[0].trim().toLowerCase();
|
||||||
// if (args.length == 1){
|
// if (args.length == 1){
|
||||||
// // Also check aliases for matches.
|
// // Also check aliases for matches.
|
||||||
// return getTabMatches(sender, commands.keySet(), subLabel);
|
// return getTabMatches(sender, commands.keySet(), subLabel);
|
||||||
// }
|
// }
|
||||||
// else{
|
// else{
|
||||||
// final NCPCommand cmd = commands.get(subLabel);
|
// final NCPCommand cmd = commands.get(subLabel);
|
||||||
// if (cmd.testPermission...){
|
// if (cmd.testPermission...){
|
||||||
// // Delegate the tab-completion.
|
// // Delegate the tab-completion.
|
||||||
// return cmd.onTabComplete(sender, command, alias, args);
|
// return cmd.onTabComplete(sender, command, alias, args);
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
// return null;
|
// 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_RELOAD = COMMAND + ".reload";
|
||||||
public static final String COMMAND_REMOVEPLAYER = COMMAND + ".removeplayer";
|
public static final String COMMAND_REMOVEPLAYER = COMMAND + ".removeplayer";
|
||||||
public static final String COMMAND_RESET = COMMAND + ".reset";
|
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_UNEXEMPT = COMMAND + ".unexempt";
|
||||||
public static final String COMMAND_VERSION = COMMAND + ".version";
|
public static final String COMMAND_VERSION = COMMAND + ".version";
|
||||||
|
|
||||||
|
@ -19,6 +19,7 @@ commands:
|
|||||||
# permissions: nocheatplus.admin.(...)
|
# permissions: nocheatplus.admin.(...)
|
||||||
usage: |
|
usage: |
|
||||||
Administrative commands overview:
|
Administrative commands overview:
|
||||||
|
/<command> top (entries) (check/s...) (sort by...) NEW.
|
||||||
/<command> info (player): Violation summary for a player.
|
/<command> info (player): Violation summary for a player.
|
||||||
/<command> inspect (player): Status info for a player.
|
/<command> inspect (player): Status info for a player.
|
||||||
/<command> notify on|off: In-game notifications per player.
|
/<command> notify on|off: In-game notifications per player.
|
||||||
@ -267,6 +268,8 @@ permissions:
|
|||||||
nocheatplus.notify: true
|
nocheatplus.notify: true
|
||||||
nocheatplus.command.reload:
|
nocheatplus.command.reload:
|
||||||
description: Allow the player to reload NoCheatPlus configuration.
|
description: Allow the player to reload NoCheatPlus configuration.
|
||||||
|
nocheatplus.command.top:
|
||||||
|
description: Allow to search violation history for top violations.
|
||||||
nocheatplus.command.info:
|
nocheatplus.command.info:
|
||||||
description: Allow to see violation info about a player.
|
description: Allow to see violation info about a player.
|
||||||
nocheatplus.command.inspect:
|
nocheatplus.command.inspect:
|
||||||
@ -328,6 +331,7 @@ permissions:
|
|||||||
description: Info commands about players.
|
description: Info commands about players.
|
||||||
children:
|
children:
|
||||||
nocheatplus.command.notify: true
|
nocheatplus.command.notify: true
|
||||||
|
nocheatplus.command.top: true
|
||||||
nocheatplus.command.info: true
|
nocheatplus.command.info: true
|
||||||
nocheatplus.command.exemptions: true
|
nocheatplus.command.exemptions: true
|
||||||
nocheatplus.command.kicklist: true
|
nocheatplus.command.kicklist: true
|
||||||
|
Loading…
Reference in New Issue
Block a user