Add a 500ms rate limit to disguise commands to try prevent possible crashes. Fixes #551
This commit is contained in:
parent
135b0fdeaa
commit
54b9490604
@ -1,6 +1,8 @@
|
|||||||
package me.libraryaddict.disguise.commands;
|
package me.libraryaddict.disguise.commands;
|
||||||
|
|
||||||
import com.comphenix.protocol.ProtocolLibrary;
|
import com.comphenix.protocol.ProtocolLibrary;
|
||||||
|
import com.google.common.cache.Cache;
|
||||||
|
import com.google.common.cache.CacheBuilder;
|
||||||
import me.libraryaddict.disguise.DisguiseConfig;
|
import me.libraryaddict.disguise.DisguiseConfig;
|
||||||
import me.libraryaddict.disguise.commands.disguise.DisguiseCommand;
|
import me.libraryaddict.disguise.commands.disguise.DisguiseCommand;
|
||||||
import me.libraryaddict.disguise.commands.disguise.DisguiseEntityCommand;
|
import me.libraryaddict.disguise.commands.disguise.DisguiseEntityCommand;
|
||||||
@ -19,6 +21,7 @@ import me.libraryaddict.disguise.utilities.parser.DisguiseParser;
|
|||||||
import me.libraryaddict.disguise.utilities.parser.DisguisePerm;
|
import me.libraryaddict.disguise.utilities.parser.DisguisePerm;
|
||||||
import me.libraryaddict.disguise.utilities.parser.DisguisePermissions;
|
import me.libraryaddict.disguise.utilities.parser.DisguisePermissions;
|
||||||
import me.libraryaddict.disguise.utilities.parser.WatcherMethod;
|
import me.libraryaddict.disguise.utilities.parser.WatcherMethod;
|
||||||
|
import me.libraryaddict.disguise.utilities.translations.LibsMsg;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.ChatColor;
|
import org.bukkit.ChatColor;
|
||||||
import org.bukkit.command.CommandExecutor;
|
import org.bukkit.command.CommandExecutor;
|
||||||
@ -27,12 +30,14 @@ import org.bukkit.entity.Player;
|
|||||||
import org.bukkit.inventory.ItemStack;
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author libraryaddict
|
* @author libraryaddict
|
||||||
*/
|
*/
|
||||||
public abstract class DisguiseBaseCommand implements CommandExecutor {
|
public abstract class DisguiseBaseCommand implements CommandExecutor {
|
||||||
private static final Map<Class<? extends DisguiseBaseCommand>, String> disguiseCommands;
|
private static final Map<Class<? extends DisguiseBaseCommand>, String> disguiseCommands;
|
||||||
|
private final Cache<UUID, Long> rateLimit = CacheBuilder.newBuilder().expireAfterWrite(500, TimeUnit.MILLISECONDS).build();
|
||||||
|
|
||||||
static {
|
static {
|
||||||
HashMap<Class<? extends DisguiseBaseCommand>, String> map = new HashMap<>();
|
HashMap<Class<? extends DisguiseBaseCommand>, String> map = new HashMap<>();
|
||||||
@ -49,6 +54,20 @@ public abstract class DisguiseBaseCommand implements CommandExecutor {
|
|||||||
disguiseCommands = map;
|
disguiseCommands = map;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected boolean hasHitRateLimit(CommandSender sender) {
|
||||||
|
if (sender.isOp() || !(sender instanceof Player) || sender.hasPermission("libsdisguises.ratelimitbypass")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rateLimit.getIfPresent(((Player) sender).getUniqueId()) != null) {
|
||||||
|
LibsMsg.TOO_FAST.send(sender);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
rateLimit.put(((Player) sender).getUniqueId(), System.currentTimeMillis());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
protected boolean isNotPremium(CommandSender sender) {
|
protected boolean isNotPremium(CommandSender sender) {
|
||||||
String requiredProtocolLib = DisguiseUtilities.getProtocolLibRequiredVersion();
|
String requiredProtocolLib = DisguiseUtilities.getProtocolLibRequiredVersion();
|
||||||
String version = ProtocolLibrary.getPlugin().getDescription().getVersion();
|
String version = ProtocolLibrary.getPlugin().getDescription().getVersion();
|
||||||
@ -59,8 +78,7 @@ public abstract class DisguiseBaseCommand implements CommandExecutor {
|
|||||||
|
|
||||||
if (sender instanceof Player && !sender.isOp() &&
|
if (sender instanceof Player && !sender.isOp() &&
|
||||||
(!LibsPremium.isPremium() || LibsPremium.getPaidInformation() == LibsPremium.getPluginInformation())) {
|
(!LibsPremium.isPremium() || LibsPremium.getPaidInformation() == LibsPremium.getPluginInformation())) {
|
||||||
sender.sendMessage(ChatColor.RED +
|
sender.sendMessage(ChatColor.RED + "This is the free version of Lib's Disguises, player commands are limited to console and " +
|
||||||
"This is the free version of Lib's Disguises, player commands are limited to console and " +
|
|
||||||
"Operators only! Purchase the plugin for non-admin usage!");
|
"Operators only! Purchase the plugin for non-admin usage!");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -68,8 +86,7 @@ public abstract class DisguiseBaseCommand implements CommandExecutor {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected List<String> getTabDisguiseTypes(CommandSender sender, DisguisePermissions perms, String[] allArgs,
|
protected List<String> getTabDisguiseTypes(CommandSender sender, DisguisePermissions perms, String[] allArgs, int startsAt, String currentArg) {
|
||||||
int startsAt, String currentArg) {
|
|
||||||
// If not enough arguments to get current disguise type
|
// If not enough arguments to get current disguise type
|
||||||
if (allArgs.length <= startsAt) {
|
if (allArgs.length <= startsAt) {
|
||||||
return getAllowedDisguises(perms);
|
return getAllowedDisguises(perms);
|
||||||
@ -85,8 +102,7 @@ public abstract class DisguiseBaseCommand implements CommandExecutor {
|
|||||||
|
|
||||||
// If current argument is just after the disguise type, and disguise type is a player which is not a custom
|
// If current argument is just after the disguise type, and disguise type is a player which is not a custom
|
||||||
// disguise
|
// disguise
|
||||||
if (allArgs.length == startsAt + 1 && disguiseType.getType() == DisguiseType.PLAYER &&
|
if (allArgs.length == startsAt + 1 && disguiseType.getType() == DisguiseType.PLAYER && !disguiseType.isCustomDisguise()) {
|
||||||
!disguiseType.isCustomDisguise()) {
|
|
||||||
ArrayList<String> tabs = new ArrayList<>();
|
ArrayList<String> tabs = new ArrayList<>();
|
||||||
|
|
||||||
// Add all player names to tab list
|
// Add all player names to tab list
|
||||||
@ -103,8 +119,7 @@ public abstract class DisguiseBaseCommand implements CommandExecutor {
|
|||||||
return tabs;
|
return tabs;
|
||||||
}
|
}
|
||||||
|
|
||||||
return getTabDisguiseOptions(sender, perms, disguiseType, allArgs, startsAt + (disguiseType.isPlayer() ? 2 : 1),
|
return getTabDisguiseOptions(sender, perms, disguiseType, allArgs, startsAt + (disguiseType.isPlayer() ? 2 : 1), currentArg);
|
||||||
currentArg);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -114,8 +129,8 @@ public abstract class DisguiseBaseCommand implements CommandExecutor {
|
|||||||
* @param startsAt What index this starts at
|
* @param startsAt What index this starts at
|
||||||
* @return a list of viable disguise options
|
* @return a list of viable disguise options
|
||||||
*/
|
*/
|
||||||
protected List<String> getTabDisguiseOptions(CommandSender commandSender, DisguisePermissions perms,
|
protected List<String> getTabDisguiseOptions(CommandSender commandSender, DisguisePermissions perms, DisguisePerm disguisePerm, String[] allArgs,
|
||||||
DisguisePerm disguisePerm, String[] allArgs, int startsAt, String currentArg) {
|
int startsAt, String currentArg) {
|
||||||
ArrayList<String> usedOptions = new ArrayList<>();
|
ArrayList<String> usedOptions = new ArrayList<>();
|
||||||
|
|
||||||
WatcherMethod[] methods = ParamInfoManager.getDisguiseWatcherMethods(disguisePerm.getWatcherClass());
|
WatcherMethod[] methods = ParamInfoManager.getDisguiseWatcherMethods(disguisePerm.getWatcherClass());
|
||||||
@ -142,8 +157,8 @@ public abstract class DisguiseBaseCommand implements CommandExecutor {
|
|||||||
return getTabDisguiseSubOptions(commandSender, perms, disguisePerm, allArgs, startsAt, currentArg);
|
return getTabDisguiseSubOptions(commandSender, perms, disguisePerm, allArgs, startsAt, currentArg);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected List<String> getTabDisguiseSubOptions(CommandSender commandSender, DisguisePermissions perms,
|
protected List<String> getTabDisguiseSubOptions(CommandSender commandSender, DisguisePermissions perms, DisguisePerm disguisePerm, String[] allArgs,
|
||||||
DisguisePerm disguisePerm, String[] allArgs, int startsAt, String currentArg) {
|
int startsAt, String currentArg) {
|
||||||
boolean addMethods = true;
|
boolean addMethods = true;
|
||||||
List<String> tabs = new ArrayList<>();
|
List<String> tabs = new ArrayList<>();
|
||||||
|
|
||||||
@ -199,8 +214,9 @@ public abstract class DisguiseBaseCommand implements CommandExecutor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected List<String> filterTabs(List<String> list, String[] origArgs) {
|
protected List<String> filterTabs(List<String> list, String[] origArgs) {
|
||||||
if (origArgs.length == 0)
|
if (origArgs.length == 0) {
|
||||||
return list;
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
Iterator<String> itel = list.iterator();
|
Iterator<String> itel = list.iterator();
|
||||||
String label = origArgs[origArgs.length - 1].toLowerCase(Locale.ENGLISH);
|
String label = origArgs[origArgs.length - 1].toLowerCase(Locale.ENGLISH);
|
||||||
@ -208,8 +224,9 @@ public abstract class DisguiseBaseCommand implements CommandExecutor {
|
|||||||
while (itel.hasNext()) {
|
while (itel.hasNext()) {
|
||||||
String name = itel.next();
|
String name = itel.next();
|
||||||
|
|
||||||
if (name.toLowerCase(Locale.ENGLISH).startsWith(label))
|
if (name.toLowerCase(Locale.ENGLISH).startsWith(label)) {
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
itel.remove();
|
itel.remove();
|
||||||
}
|
}
|
||||||
@ -231,8 +248,9 @@ public abstract class DisguiseBaseCommand implements CommandExecutor {
|
|||||||
ArrayList<String> allowedDisguises = new ArrayList<>();
|
ArrayList<String> allowedDisguises = new ArrayList<>();
|
||||||
|
|
||||||
for (DisguisePerm type : permissions.getAllowed()) {
|
for (DisguisePerm type : permissions.getAllowed()) {
|
||||||
if (type.isUnknown())
|
if (type.isUnknown()) {
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
allowedDisguises.add(type.toReadable().replaceAll(" ", "_"));
|
allowedDisguises.add(type.toReadable().replaceAll(" ", "_"));
|
||||||
}
|
}
|
||||||
@ -250,8 +268,9 @@ public abstract class DisguiseBaseCommand implements CommandExecutor {
|
|||||||
for (int i = 0; i < args.length - 1; i++) {
|
for (int i = 0; i < args.length - 1; i++) {
|
||||||
String s = args[i];
|
String s = args[i];
|
||||||
|
|
||||||
if (s.trim().isEmpty())
|
if (s.trim().isEmpty()) {
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
newArgs.add(s);
|
newArgs.add(s);
|
||||||
}
|
}
|
||||||
@ -289,8 +308,7 @@ public abstract class DisguiseBaseCommand implements CommandExecutor {
|
|||||||
try {
|
try {
|
||||||
Integer.parseInt(string);
|
Integer.parseInt(string);
|
||||||
return true;
|
return true;
|
||||||
}
|
} catch (Exception ex) {
|
||||||
catch (Exception ex) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -37,20 +37,22 @@ public class DisguiseCommand extends DisguiseBaseCommand implements TabCompleter
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (hasHitRateLimit(sender)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
Disguise disguise;
|
Disguise disguise;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
disguise = DisguiseParser.parseDisguise(sender, (Entity) sender, getPermNode(),
|
disguise = DisguiseParser
|
||||||
DisguiseUtilities.split(StringUtils.join(args, " ")), getPermissions(sender));
|
.parseDisguise(sender, (Entity) sender, getPermNode(), DisguiseUtilities.split(StringUtils.join(args, " ")), getPermissions(sender));
|
||||||
}
|
} catch (DisguiseParseException ex) {
|
||||||
catch (DisguiseParseException ex) {
|
|
||||||
if (ex.getMessage() != null) {
|
if (ex.getMessage() != null) {
|
||||||
DisguiseUtilities.sendMessage(sender, ex.getMessage());
|
DisguiseUtilities.sendMessage(sender, ex.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
} catch (Throwable ex) {
|
||||||
catch (Throwable ex) {
|
|
||||||
ex.printStackTrace();
|
ex.printStackTrace();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -69,10 +71,10 @@ public class DisguiseCommand extends DisguiseBaseCommand implements TabCompleter
|
|||||||
|
|
||||||
if (!setViewDisguise(args)) {
|
if (!setViewDisguise(args)) {
|
||||||
// They prefer to have the opposite of whatever the view disguises option is
|
// They prefer to have the opposite of whatever the view disguises option is
|
||||||
if (DisguiseAPI.hasSelfDisguisePreference(disguise.getEntity()) &&
|
if (DisguiseAPI.hasSelfDisguisePreference(disguise.getEntity()) && disguise.isSelfDisguiseVisible() == DisguiseConfig.isViewDisguises()) {
|
||||||
disguise.isSelfDisguiseVisible() == DisguiseConfig.isViewDisguises())
|
|
||||||
disguise.setViewSelfDisguise(!disguise.isSelfDisguiseVisible());
|
disguise.setViewSelfDisguise(!disguise.isSelfDisguiseVisible());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!DisguiseAPI.isActionBarShown(disguise.getEntity())) {
|
if (!DisguiseAPI.isActionBarShown(disguise.getEntity())) {
|
||||||
disguise.setNotifyBar(DisguiseConfig.NotifyBar.NONE);
|
disguise.setNotifyBar(DisguiseConfig.NotifyBar.NONE);
|
||||||
@ -91,8 +93,9 @@ public class DisguiseCommand extends DisguiseBaseCommand implements TabCompleter
|
|||||||
|
|
||||||
private boolean setViewDisguise(String[] strings) {
|
private boolean setViewDisguise(String[] strings) {
|
||||||
for (String string : strings) {
|
for (String string : strings) {
|
||||||
if (!string.equalsIgnoreCase("setSelfDisguiseVisible"))
|
if (!string.equalsIgnoreCase("setSelfDisguiseVisible")) {
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -41,6 +41,10 @@ public class DisguiseEntityCommand extends DisguiseBaseCommand implements TabCom
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (hasHitRateLimit(sender)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
String[] disguiseArgs = DisguiseUtilities.split(StringUtils.join(args, " "));
|
String[] disguiseArgs = DisguiseUtilities.split(StringUtils.join(args, " "));
|
||||||
Disguise testDisguise;
|
Disguise testDisguise;
|
||||||
|
|
||||||
|
@ -48,6 +48,10 @@ public class DisguisePlayerCommand extends DisguiseBaseCommand implements TabCom
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (hasHitRateLimit(sender)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
Entity entityTarget = Bukkit.getPlayer(args[0]);
|
Entity entityTarget = Bukkit.getPlayer(args[0]);
|
||||||
|
|
||||||
if (entityTarget == null) {
|
if (entityTarget == null) {
|
||||||
|
@ -66,6 +66,10 @@ public class DisguiseRadiusCommand extends DisguiseBaseCommand implements TabCom
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (hasHitRateLimit(sender)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if (args[0].equalsIgnoreCase(TranslateType.DISGUISES.get("EntityType")) || args[0].equalsIgnoreCase(TranslateType.DISGUISES.get("EntityType") + "s")) {
|
if (args[0].equalsIgnoreCase(TranslateType.DISGUISES.get("EntityType")) || args[0].equalsIgnoreCase(TranslateType.DISGUISES.get("EntityType") + "s")) {
|
||||||
ArrayList<String> classes = new ArrayList<>();
|
ArrayList<String> classes = new ArrayList<>();
|
||||||
|
|
||||||
|
@ -125,6 +125,7 @@ public enum LibsMsg {
|
|||||||
MADE_REF(ChatColor.RED + "Constructed a %s disguise! Your reference is %s"),
|
MADE_REF(ChatColor.RED + "Constructed a %s disguise! Your reference is %s"),
|
||||||
MADE_REF_EXAMPLE(ChatColor.RED + "Example usage: /disguise %s"),
|
MADE_REF_EXAMPLE(ChatColor.RED + "Example usage: /disguise %s"),
|
||||||
NO_CONSOLE(ChatColor.RED + "You may not use this command from the console!"),
|
NO_CONSOLE(ChatColor.RED + "You may not use this command from the console!"),
|
||||||
|
TOO_FAST(ChatColor.RED + "You are using the disguise command too fast!"),
|
||||||
NO_MODS(ChatColor.RED + "%s is not using any mods!"),
|
NO_MODS(ChatColor.RED + "%s is not using any mods!"),
|
||||||
MODS_LIST(ChatColor.DARK_GREEN + "%s has the mods:" + ChatColor.AQUA + " %s"),
|
MODS_LIST(ChatColor.DARK_GREEN + "%s has the mods:" + ChatColor.AQUA + " %s"),
|
||||||
NO_PERM(ChatColor.RED + "You are forbidden to use this command."),
|
NO_PERM(ChatColor.RED + "You are forbidden to use this command."),
|
||||||
|
@ -137,6 +137,8 @@ permissions:
|
|||||||
description: Allows the command user to set names on different heights
|
description: Allows the command user to set names on different heights
|
||||||
libsdisguises.grabhead:
|
libsdisguises.grabhead:
|
||||||
description: Allows the command user to use /grabhead
|
description: Allows the command user to use /grabhead
|
||||||
|
libsdisguises.ratelimitbypass:
|
||||||
|
description: Allows a living player to bypass the 500ms rate limit on disguise commands, used to prevent crashes
|
||||||
libsdisguises.seecmd:
|
libsdisguises.seecmd:
|
||||||
description: See all commands in tab-completion
|
description: See all commands in tab-completion
|
||||||
default: true
|
default: true
|
||||||
|
Loading…
Reference in New Issue
Block a user