Asynchronously filter commands on Paper when possible (#4460)

Co-authored-by: MD <1917406+mdcfe@users.noreply.github.com>
Co-authored-by: Josh Roy <10731363+JRoy@users.noreply.github.com>
This commit is contained in:
MD 2021-09-15 22:23:31 +01:00 committed by GitHub
parent d86b471402
commit f0da17b75c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 120 additions and 17 deletions

View File

@ -13,6 +13,9 @@ import com.earth2me.essentials.utils.VersionUtil;
import io.papermc.lib.PaperLib;
import net.ess3.api.IEssentials;
import net.ess3.api.events.AfkStatusChangeEvent;
import net.ess3.provider.CommandSendListenerProvider;
import net.ess3.provider.providers.BukkitCommandSendListenerProvider;
import net.ess3.provider.providers.PaperCommandSendListenerProvider;
import net.essentialsx.api.v2.events.AsyncUserDataLoadEvent;
import org.bukkit.BanEntry;
import org.bukkit.BanList;
@ -37,7 +40,6 @@ import org.bukkit.event.player.AsyncPlayerChatEvent;
import org.bukkit.event.player.PlayerBucketEmptyEvent;
import org.bukkit.event.player.PlayerChangedWorldEvent;
import org.bukkit.event.player.PlayerCommandPreprocessEvent;
import org.bukkit.event.player.PlayerCommandSendEvent;
import org.bukkit.event.player.PlayerEggThrowEvent;
import org.bukkit.event.player.PlayerFishEvent;
import org.bukkit.event.player.PlayerInteractEvent;
@ -68,6 +70,7 @@ import java.util.Map.Entry;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Predicate;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Pattern;
@ -101,6 +104,15 @@ public class EssentialsPlayerListener implements Listener, FakeAccessor {
}
}
private static boolean isPaperCommandSendEvent() {
try {
Class.forName("com.destroystokyo.paper.event.brigadier.AsyncPlayerSendCommandsEvent");
return true;
} catch (final ClassNotFoundException ignored) {
return false;
}
}
private static boolean isArrowPickupEvent() {
try {
Class.forName("org.bukkit.event.player.PlayerPickupArrowEvent");
@ -123,8 +135,10 @@ public class EssentialsPlayerListener implements Listener, FakeAccessor {
ess.getServer().getPluginManager().registerEvents(new PickupListenerPre1_12(), ess);
}
if (isCommandSendEvent()) {
ess.getServer().getPluginManager().registerEvents(new CommandSendListener(), ess);
if (isPaperCommandSendEvent()) {
ess.getServer().getPluginManager().registerEvents(new PaperCommandSendListenerProvider(new CommandSendFilter()), ess);
} else if (isCommandSendEvent()) {
ess.getServer().getPluginManager().registerEvents(new BukkitCommandSendListenerProvider(new CommandSendFilter()), ess);
}
}
@ -975,15 +989,14 @@ public class EssentialsPlayerListener implements Listener, FakeAccessor {
}
}
private final class CommandSendListener implements Listener {
@EventHandler(priority = EventPriority.NORMAL)
public void onCommandSend(final PlayerCommandSendEvent event) {
final User user = ess.getUser(event.getPlayer());
private final class CommandSendFilter implements CommandSendListenerProvider.Filter {
@Override
public Predicate<String> apply(Player player) {
final User user = ess.getUser(player);
final Set<PluginCommand> checked = new HashSet<>();
final Set<PluginCommand> toRemove = new HashSet<>();
event.getCommands().removeIf(label -> {
return label -> {
if (isEssentialsCommand(label)) {
final PluginCommand command = ess.getServer().getPluginCommand(label);
if (!checked.contains(command)) {
@ -995,25 +1008,21 @@ public class EssentialsPlayerListener implements Listener, FakeAccessor {
return toRemove.contains(command);
}
return false;
});
if (ess.getSettings().isDebug()) {
ess.getLogger().info("Removed commands: " + toRemove.toString());
}
};
}
/**
* Returns true if all of the following are true:
* - The command is a plugin command
* - The plugin command is from a plugin in an essentials-controlled package
* - The plugin command is from an official EssentialsX plugin or addon
* - There is no known alternative OR the alternative is overridden by Essentials
*/
private boolean isEssentialsCommand(final String label) {
final PluginCommand command = ess.getServer().getPluginCommand(label);
return command != null
&& (command.getPlugin() == ess || command.getPlugin().getClass().getName().startsWith("com.earth2me.essentials"))
&& (ess.getSettings().isCommandOverridden(label) || (ess.getAlternativeCommandsHandler().getAlternative(label) == null));
&& (command.getPlugin() == ess || command.getPlugin().getClass().getName().startsWith("com.earth2me.essentials") || command.getPlugin().getClass().getName().startsWith("net.essentialsx"))
&& (ess.getSettings().isCommandOverridden(label) || (ess.getAlternativeCommandsHandler().getAlternative(label) == null));
}
}

View File

@ -0,0 +1,30 @@
package net.ess3.provider;
import org.bukkit.entity.Player;
import org.bukkit.event.Listener;
import java.util.function.Function;
import java.util.function.Predicate;
/**
* A provider for 1.13+ command send listeners.
* <p>
* Note to maintainers: this doesn't extend {@link ProviderListener} because it doesn't make sense here.
*/
public abstract class CommandSendListenerProvider implements Provider, Listener {
private final Filter commandFilter;
protected CommandSendListenerProvider(Filter commandFilter) {
this.commandFilter = commandFilter;
}
protected final Predicate<String> filter(final Player player) {
return commandFilter.apply(player);
}
/**
* A function that returns a predicate to test whether commands should be hidden from the given player.
*/
public interface Filter extends Function<Player, Predicate<String>> {
}
}

View File

@ -0,0 +1,25 @@
package net.ess3.provider.providers;
import net.ess3.provider.CommandSendListenerProvider;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.player.PlayerCommandSendEvent;
import java.util.function.Predicate;
public class BukkitCommandSendListenerProvider extends CommandSendListenerProvider {
public BukkitCommandSendListenerProvider(Filter commandFilter) {
super(commandFilter);
}
@EventHandler(priority = EventPriority.NORMAL)
public void onCommandSend(PlayerCommandSendEvent event) {
final Predicate<String> filter = filter(event.getPlayer());
event.getCommands().removeIf(filter);
}
@Override
public String getDescription() {
return "Bukkit synchronous command send listener";
}
}

View File

@ -5,6 +5,7 @@ plugins {
dependencies {
implementation project(':providers:BaseProviders')
compileOnly 'com.destroystokyo.paper:paper-api:1.16.5-R0.1-SNAPSHOT'
compileOnly 'com.destroystokyo.paper:paper-mojangapi:1.16.5-R0.1-SNAPSHOT'
}
essentials {

View File

@ -0,0 +1,35 @@
package net.ess3.provider.providers;
import com.destroystokyo.paper.event.brigadier.AsyncPlayerSendCommandsEvent;
import com.mojang.brigadier.tree.CommandNode;
import net.ess3.provider.CommandSendListenerProvider;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import java.util.Collection;
import java.util.function.Predicate;
public class PaperCommandSendListenerProvider extends CommandSendListenerProvider {
public PaperCommandSendListenerProvider(Filter commandFilter) {
super(commandFilter);
}
@EventHandler(priority = EventPriority.NORMAL)
public void onAsyncCommandSend(@SuppressWarnings("deprecation") AsyncPlayerSendCommandsEvent<?> event) {
if (!event.isAsynchronous() && event.hasFiredAsync()) {
// this has already fired once async
return;
}
final Collection<? extends CommandNode<?>> children = event.getCommandNode().getChildren();
final Predicate<String> filter = filter(event.getPlayer());
children.removeIf(node -> filter.test(node.getName()));
}
@Override
public String getDescription() {
return "Paper async Brigadier command send listener";
}
}

View File

@ -14,6 +14,9 @@ dependencyResolutionManagement {
maven("https://repo.extendedclip.com/content/repositories/placeholderapi/") {
content { includeGroup("me.clip") }
}
maven("https://libraries.minecraft.net/") {
content { includeGroup("com.mojang") }
}
mavenCentral {
content { includeGroup("net.kyori") }
}