From f0da17b75cafa67ed45c7073c29c7cb4a869d415 Mon Sep 17 00:00:00 2001 From: MD <1917406+mdcfe@users.noreply.github.com> Date: Wed, 15 Sep 2021 22:23:31 +0100 Subject: [PATCH] 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> --- .../essentials/EssentialsPlayerListener.java | 43 +++++++++++-------- .../provider/CommandSendListenerProvider.java | 30 +++++++++++++ .../BukkitCommandSendListenerProvider.java | 25 +++++++++++ providers/PaperProvider/build.gradle | 1 + .../PaperCommandSendListenerProvider.java | 35 +++++++++++++++ settings.gradle.kts | 3 ++ 6 files changed, 120 insertions(+), 17 deletions(-) create mode 100644 providers/BaseProviders/src/main/java/net/ess3/provider/CommandSendListenerProvider.java create mode 100644 providers/BaseProviders/src/main/java/net/ess3/provider/providers/BukkitCommandSendListenerProvider.java create mode 100644 providers/PaperProvider/src/main/java/net/ess3/provider/providers/PaperCommandSendListenerProvider.java diff --git a/Essentials/src/main/java/com/earth2me/essentials/EssentialsPlayerListener.java b/Essentials/src/main/java/com/earth2me/essentials/EssentialsPlayerListener.java index 0a7d26956..b5aa2e64d 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/EssentialsPlayerListener.java +++ b/Essentials/src/main/java/com/earth2me/essentials/EssentialsPlayerListener.java @@ -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 apply(Player player) { + final User user = ess.getUser(player); final Set checked = new HashSet<>(); final Set 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)); } } diff --git a/providers/BaseProviders/src/main/java/net/ess3/provider/CommandSendListenerProvider.java b/providers/BaseProviders/src/main/java/net/ess3/provider/CommandSendListenerProvider.java new file mode 100644 index 000000000..5c69003e8 --- /dev/null +++ b/providers/BaseProviders/src/main/java/net/ess3/provider/CommandSendListenerProvider.java @@ -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. + *

+ * 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 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> { + } +} diff --git a/providers/BaseProviders/src/main/java/net/ess3/provider/providers/BukkitCommandSendListenerProvider.java b/providers/BaseProviders/src/main/java/net/ess3/provider/providers/BukkitCommandSendListenerProvider.java new file mode 100644 index 000000000..d3feffc68 --- /dev/null +++ b/providers/BaseProviders/src/main/java/net/ess3/provider/providers/BukkitCommandSendListenerProvider.java @@ -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 filter = filter(event.getPlayer()); + event.getCommands().removeIf(filter); + } + + @Override + public String getDescription() { + return "Bukkit synchronous command send listener"; + } +} diff --git a/providers/PaperProvider/build.gradle b/providers/PaperProvider/build.gradle index 49ced3f5a..1b1dba537 100644 --- a/providers/PaperProvider/build.gradle +++ b/providers/PaperProvider/build.gradle @@ -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 { diff --git a/providers/PaperProvider/src/main/java/net/ess3/provider/providers/PaperCommandSendListenerProvider.java b/providers/PaperProvider/src/main/java/net/ess3/provider/providers/PaperCommandSendListenerProvider.java new file mode 100644 index 000000000..1bc1a56e4 --- /dev/null +++ b/providers/PaperProvider/src/main/java/net/ess3/provider/providers/PaperCommandSendListenerProvider.java @@ -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> children = event.getCommandNode().getChildren(); + final Predicate filter = filter(event.getPlayer()); + + children.removeIf(node -> filter.test(node.getName())); + } + + @Override + public String getDescription() { + return "Paper async Brigadier command send listener"; + } +} diff --git a/settings.gradle.kts b/settings.gradle.kts index bd9fa6fc1..0ad3170fc 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -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") } }