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;
|
||||
|
||||
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.commands.disguise.DisguiseCommand;
|
||||
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.DisguisePermissions;
|
||||
import me.libraryaddict.disguise.utilities.parser.WatcherMethod;
|
||||
import me.libraryaddict.disguise.utilities.translations.LibsMsg;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
@ -27,12 +30,14 @@ import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* @author libraryaddict
|
||||
*/
|
||||
public abstract class DisguiseBaseCommand implements CommandExecutor {
|
||||
private static final Map<Class<? extends DisguiseBaseCommand>, String> disguiseCommands;
|
||||
private final Cache<UUID, Long> rateLimit = CacheBuilder.newBuilder().expireAfterWrite(500, TimeUnit.MILLISECONDS).build();
|
||||
|
||||
static {
|
||||
HashMap<Class<? extends DisguiseBaseCommand>, String> map = new HashMap<>();
|
||||
@ -49,6 +54,20 @@ public abstract class DisguiseBaseCommand implements CommandExecutor {
|
||||
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) {
|
||||
String requiredProtocolLib = DisguiseUtilities.getProtocolLibRequiredVersion();
|
||||
String version = ProtocolLibrary.getPlugin().getDescription().getVersion();
|
||||
@ -59,8 +78,7 @@ public abstract class DisguiseBaseCommand implements CommandExecutor {
|
||||
|
||||
if (sender instanceof Player && !sender.isOp() &&
|
||||
(!LibsPremium.isPremium() || LibsPremium.getPaidInformation() == LibsPremium.getPluginInformation())) {
|
||||
sender.sendMessage(ChatColor.RED +
|
||||
"This is the free version of Lib's Disguises, player commands are limited to console and " +
|
||||
sender.sendMessage(ChatColor.RED + "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!");
|
||||
return true;
|
||||
}
|
||||
@ -68,8 +86,7 @@ public abstract class DisguiseBaseCommand implements CommandExecutor {
|
||||
return false;
|
||||
}
|
||||
|
||||
protected List<String> getTabDisguiseTypes(CommandSender sender, DisguisePermissions perms, String[] allArgs,
|
||||
int startsAt, String currentArg) {
|
||||
protected List<String> getTabDisguiseTypes(CommandSender sender, DisguisePermissions perms, String[] allArgs, int startsAt, String currentArg) {
|
||||
// If not enough arguments to get current disguise type
|
||||
if (allArgs.length <= startsAt) {
|
||||
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
|
||||
// disguise
|
||||
if (allArgs.length == startsAt + 1 && disguiseType.getType() == DisguiseType.PLAYER &&
|
||||
!disguiseType.isCustomDisguise()) {
|
||||
if (allArgs.length == startsAt + 1 && disguiseType.getType() == DisguiseType.PLAYER && !disguiseType.isCustomDisguise()) {
|
||||
ArrayList<String> tabs = new ArrayList<>();
|
||||
|
||||
// Add all player names to tab list
|
||||
@ -103,8 +119,7 @@ public abstract class DisguiseBaseCommand implements CommandExecutor {
|
||||
return tabs;
|
||||
}
|
||||
|
||||
return getTabDisguiseOptions(sender, perms, disguiseType, allArgs, startsAt + (disguiseType.isPlayer() ? 2 : 1),
|
||||
currentArg);
|
||||
return getTabDisguiseOptions(sender, perms, disguiseType, allArgs, startsAt + (disguiseType.isPlayer() ? 2 : 1), currentArg);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -114,8 +129,8 @@ public abstract class DisguiseBaseCommand implements CommandExecutor {
|
||||
* @param startsAt What index this starts at
|
||||
* @return a list of viable disguise options
|
||||
*/
|
||||
protected List<String> getTabDisguiseOptions(CommandSender commandSender, DisguisePermissions perms,
|
||||
DisguisePerm disguisePerm, String[] allArgs, int startsAt, String currentArg) {
|
||||
protected List<String> getTabDisguiseOptions(CommandSender commandSender, DisguisePermissions perms, DisguisePerm disguisePerm, String[] allArgs,
|
||||
int startsAt, String currentArg) {
|
||||
ArrayList<String> usedOptions = new ArrayList<>();
|
||||
|
||||
WatcherMethod[] methods = ParamInfoManager.getDisguiseWatcherMethods(disguisePerm.getWatcherClass());
|
||||
@ -142,8 +157,8 @@ public abstract class DisguiseBaseCommand implements CommandExecutor {
|
||||
return getTabDisguiseSubOptions(commandSender, perms, disguisePerm, allArgs, startsAt, currentArg);
|
||||
}
|
||||
|
||||
protected List<String> getTabDisguiseSubOptions(CommandSender commandSender, DisguisePermissions perms,
|
||||
DisguisePerm disguisePerm, String[] allArgs, int startsAt, String currentArg) {
|
||||
protected List<String> getTabDisguiseSubOptions(CommandSender commandSender, DisguisePermissions perms, DisguisePerm disguisePerm, String[] allArgs,
|
||||
int startsAt, String currentArg) {
|
||||
boolean addMethods = true;
|
||||
List<String> tabs = new ArrayList<>();
|
||||
|
||||
@ -199,8 +214,9 @@ public abstract class DisguiseBaseCommand implements CommandExecutor {
|
||||
}
|
||||
|
||||
protected List<String> filterTabs(List<String> list, String[] origArgs) {
|
||||
if (origArgs.length == 0)
|
||||
if (origArgs.length == 0) {
|
||||
return list;
|
||||
}
|
||||
|
||||
Iterator<String> itel = list.iterator();
|
||||
String label = origArgs[origArgs.length - 1].toLowerCase(Locale.ENGLISH);
|
||||
@ -208,8 +224,9 @@ public abstract class DisguiseBaseCommand implements CommandExecutor {
|
||||
while (itel.hasNext()) {
|
||||
String name = itel.next();
|
||||
|
||||
if (name.toLowerCase(Locale.ENGLISH).startsWith(label))
|
||||
if (name.toLowerCase(Locale.ENGLISH).startsWith(label)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
itel.remove();
|
||||
}
|
||||
@ -231,8 +248,9 @@ public abstract class DisguiseBaseCommand implements CommandExecutor {
|
||||
ArrayList<String> allowedDisguises = new ArrayList<>();
|
||||
|
||||
for (DisguisePerm type : permissions.getAllowed()) {
|
||||
if (type.isUnknown())
|
||||
if (type.isUnknown()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
allowedDisguises.add(type.toReadable().replaceAll(" ", "_"));
|
||||
}
|
||||
@ -250,8 +268,9 @@ public abstract class DisguiseBaseCommand implements CommandExecutor {
|
||||
for (int i = 0; i < args.length - 1; i++) {
|
||||
String s = args[i];
|
||||
|
||||
if (s.trim().isEmpty())
|
||||
if (s.trim().isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
newArgs.add(s);
|
||||
}
|
||||
@ -289,8 +308,7 @@ public abstract class DisguiseBaseCommand implements CommandExecutor {
|
||||
try {
|
||||
Integer.parseInt(string);
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex) {
|
||||
} catch (Exception ex) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -37,20 +37,22 @@ public class DisguiseCommand extends DisguiseBaseCommand implements TabCompleter
|
||||
return true;
|
||||
}
|
||||
|
||||
if (hasHitRateLimit(sender)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
Disguise disguise;
|
||||
|
||||
try {
|
||||
disguise = DisguiseParser.parseDisguise(sender, (Entity) sender, getPermNode(),
|
||||
DisguiseUtilities.split(StringUtils.join(args, " ")), getPermissions(sender));
|
||||
}
|
||||
catch (DisguiseParseException ex) {
|
||||
disguise = DisguiseParser
|
||||
.parseDisguise(sender, (Entity) sender, getPermNode(), DisguiseUtilities.split(StringUtils.join(args, " ")), getPermissions(sender));
|
||||
} catch (DisguiseParseException ex) {
|
||||
if (ex.getMessage() != null) {
|
||||
DisguiseUtilities.sendMessage(sender, ex.getMessage());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
} catch (Throwable ex) {
|
||||
ex.printStackTrace();
|
||||
return true;
|
||||
}
|
||||
@ -69,10 +71,10 @@ public class DisguiseCommand extends DisguiseBaseCommand implements TabCompleter
|
||||
|
||||
if (!setViewDisguise(args)) {
|
||||
// They prefer to have the opposite of whatever the view disguises option is
|
||||
if (DisguiseAPI.hasSelfDisguisePreference(disguise.getEntity()) &&
|
||||
disguise.isSelfDisguiseVisible() == DisguiseConfig.isViewDisguises())
|
||||
if (DisguiseAPI.hasSelfDisguisePreference(disguise.getEntity()) && disguise.isSelfDisguiseVisible() == DisguiseConfig.isViewDisguises()) {
|
||||
disguise.setViewSelfDisguise(!disguise.isSelfDisguiseVisible());
|
||||
}
|
||||
}
|
||||
|
||||
if (!DisguiseAPI.isActionBarShown(disguise.getEntity())) {
|
||||
disguise.setNotifyBar(DisguiseConfig.NotifyBar.NONE);
|
||||
@ -91,8 +93,9 @@ public class DisguiseCommand extends DisguiseBaseCommand implements TabCompleter
|
||||
|
||||
private boolean setViewDisguise(String[] strings) {
|
||||
for (String string : strings) {
|
||||
if (!string.equalsIgnoreCase("setSelfDisguiseVisible"))
|
||||
if (!string.equalsIgnoreCase("setSelfDisguiseVisible")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -41,6 +41,10 @@ public class DisguiseEntityCommand extends DisguiseBaseCommand implements TabCom
|
||||
return true;
|
||||
}
|
||||
|
||||
if (hasHitRateLimit(sender)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
String[] disguiseArgs = DisguiseUtilities.split(StringUtils.join(args, " "));
|
||||
Disguise testDisguise;
|
||||
|
||||
|
@ -48,6 +48,10 @@ public class DisguisePlayerCommand extends DisguiseBaseCommand implements TabCom
|
||||
return true;
|
||||
}
|
||||
|
||||
if (hasHitRateLimit(sender)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
Entity entityTarget = Bukkit.getPlayer(args[0]);
|
||||
|
||||
if (entityTarget == null) {
|
||||
|
@ -66,6 +66,10 @@ public class DisguiseRadiusCommand extends DisguiseBaseCommand implements TabCom
|
||||
return true;
|
||||
}
|
||||
|
||||
if (hasHitRateLimit(sender)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (args[0].equalsIgnoreCase(TranslateType.DISGUISES.get("EntityType")) || args[0].equalsIgnoreCase(TranslateType.DISGUISES.get("EntityType") + "s")) {
|
||||
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_EXAMPLE(ChatColor.RED + "Example usage: /disguise %s"),
|
||||
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!"),
|
||||
MODS_LIST(ChatColor.DARK_GREEN + "%s has the mods:" + ChatColor.AQUA + " %s"),
|
||||
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
|
||||
libsdisguises.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:
|
||||
description: See all commands in tab-completion
|
||||
default: true
|
||||
|
Loading…
Reference in New Issue
Block a user