diff options
author | Aikar <[email protected]> | 2020-04-27 01:37:37 -0400 |
---|---|---|
committer | Aikar <[email protected]> | 2020-04-27 01:42:12 -0400 |
commit | fdf41b742d5e78e7e97659a829f43803c61efb8f (patch) | |
tree | 55dd747a00a208f1158449544737078485408f33 /Paper-MojangAPI | |
parent | e0ea2e0e147875b5b0eeab747d19fa00a5e19ef2 (diff) | |
download | Paper-fdf41b742d5e78e7e97659a829f43803c61efb8f.tar.gz Paper-fdf41b742d5e78e7e97659a829f43803c61efb8f.zip |
Implement Brigadier Mojang API
This is the start of a new module for Paper to add support for API's
that interface Mojang API's directly.
This allows us to version properly by MC version incase Mojang makes any major breaking changes.
It also lets us separate Mojang API's from Paper-API so our downstream friends at Glowstone
will not have to worry about Mojang code.
Adds AsyncPlayerSendCommandsEvent
- Allows modifying on a per command basis what command data they see.
Adds CommandRegisteredEvent
- Allows manipulating the CommandNode to add more children/metadata for the client
Diffstat (limited to 'Paper-MojangAPI')
5 files changed, 421 insertions, 0 deletions
diff --git a/Paper-MojangAPI/pom.xml b/Paper-MojangAPI/pom.xml new file mode 100644 index 0000000000..55482a6a92 --- /dev/null +++ b/Paper-MojangAPI/pom.xml @@ -0,0 +1,204 @@ + +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>com.destroystokyo.paper</groupId> + <artifactId>paper-parent</artifactId> + <version>dev-SNAPSHOT</version> + </parent> + + <groupId>com.destroystokyo.paper</groupId> + <artifactId>paper-mojangapi</artifactId> + <version>1.15.2-R0.1-SNAPSHOT</version> + <packaging>jar</packaging> + + <name>Paper-MojangAPI</name> + <url>https://github.com/PaperMC/Paper</url> + <description>API additions that utilize Mojang Specific API's</description> + + <properties> + <!-- <skipTests>true</skipTests> Paper - This [was] not going to end well --> + <maven.compiler.source>1.8</maven.compiler.source> + <maven.compiler.target>1.8</maven.compiler.target> + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> + </properties> + + <repositories> + <repository> + <id>spigotmc-public</id> + <url>https://hub.spigotmc.org/nexus/content/groups/public/</url> + </repository> + <repository> + <id>sonatype</id> + <url>https://oss.sonatype.org/content/groups/public/</url> + </repository> + <repository> + <id>minecraft-libraries</id> + <name>Minecraft Libraries</name> + <url>https://libraries.minecraft.net</url> + </repository> + </repositories> + + <pluginRepositories> + <pluginRepository> + <id>spigotmc-public</id> + <url>https://hub.spigotmc.org/nexus/content/groups/public/</url> + </pluginRepository> + </pluginRepositories> + + <dependencies> + <dependency> + <groupId>com.destroystokyo.paper</groupId> + <artifactId>paper-api</artifactId> + <version>${project.version}</version> + <scope>provided</scope> + </dependency> + <!-- mojang api --> + <dependency> + <groupId>com.mojang</groupId> + <artifactId>brigadier</artifactId> + <version>1.0.17</version> + <scope>provided</scope> + </dependency> + <!-- utils --> + <dependency> + <groupId>it.unimi.dsi</groupId> + <artifactId>fastutil</artifactId> + <version>8.2.2</version> + <scope>provided</scope> + </dependency> + <!-- annotations --> + <dependency> + <groupId>org.jetbrains</groupId> + <artifactId>annotations</artifactId> + <version>18.0.0</version> + <scope>provided</scope> + </dependency> + <!-- testing --> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <version>4.13</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.hamcrest</groupId> + <artifactId>hamcrest-library</artifactId> + <version>1.3</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.ow2.asm</groupId> + <artifactId>asm-tree</artifactId> + <version>7.3.1</version> + <scope>test</scope> + </dependency> + </dependencies> + + <build> + <defaultGoal>clean install</defaultGoal> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-compiler-plugin</artifactId> + <version>3.8.1</version> + <dependencies> + <!-- we need our custom version as it fixes some bugs on case sensitive file systems --> + <dependency> + <groupId>org.codehaus.plexus</groupId> + <artifactId>plexus-compiler-eclipse</artifactId> + <version>2.8.5-spigotmc</version> + </dependency> + </dependencies> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-jar-plugin</artifactId> + <version>2.4</version> + <configuration> + <archive> + <manifestEntries> + <Automatic-Module-Name>org.bukkit</Automatic-Module-Name> + </manifestEntries> + </archive> + </configuration> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-shade-plugin</artifactId> + <version>3.2.1</version> + <executions> + <execution> + <phase>package</phase> + <goals> + <goal>shade</goal> + </goals> + </execution> + </executions> + <configuration> + <dependencyReducedPomLocation>${project.build.directory}/dependency-reduced-pom.xml</dependencyReducedPomLocation> + <!-- when downloading via Maven we can pull depends individually --> + <shadedArtifactAttached>true</shadedArtifactAttached> + </configuration> + </plugin> + </plugins> + </build> + + <profiles> + <profile> + <id>development</id> + <properties> + <skipTests>false</skipTests> + </properties> + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-checkstyle-plugin</artifactId> + <version>3.1.0</version> + <executions> + <execution> + <phase>process-classes</phase> + <goals> + <goal>check</goal> + </goals> + </execution> + </executions> + <configuration> + <configLocation>checkstyle.xml</configLocation> + <includeTestSourceDirectory>true</includeTestSourceDirectory> + </configuration> + <dependencies> + <dependency> + <groupId>com.puppycrawl.tools</groupId> + <artifactId>checkstyle</artifactId> + <version>8.29</version> + </dependency> + </dependencies> + </plugin> + <plugin> + <groupId>org.codehaus.mojo</groupId> + <artifactId>animal-sniffer-maven-plugin</artifactId> + <version>1.18</version> + <executions> + <execution> + <phase>process-classes</phase> + <goals> + <goal>check</goal> + </goals> + </execution> + </executions> + <configuration> + <signature> + <groupId>org.codehaus.mojo.signature</groupId> + <artifactId>java18</artifactId> + <version>1.0</version> + </signature> + </configuration> + </plugin> + </plugins> + </build> + </profile> + </profiles> +</project> diff --git a/Paper-MojangAPI/src/main/java/com/destroystokyo/paper/brigadier/BukkitBrigadierCommand.java b/Paper-MojangAPI/src/main/java/com/destroystokyo/paper/brigadier/BukkitBrigadierCommand.java new file mode 100644 index 0000000000..3848933b67 --- /dev/null +++ b/Paper-MojangAPI/src/main/java/com/destroystokyo/paper/brigadier/BukkitBrigadierCommand.java @@ -0,0 +1,9 @@ +package com.destroystokyo.paper.brigadier; + +import com.mojang.brigadier.Command; +import com.mojang.brigadier.suggestion.SuggestionProvider; + +import java.util.function.Predicate; + +public interface BukkitBrigadierCommand <S extends BukkitBrigadierCommandSource> extends Command<S>, Predicate<S>, SuggestionProvider<S> { +} diff --git a/Paper-MojangAPI/src/main/java/com/destroystokyo/paper/brigadier/BukkitBrigadierCommandSource.java b/Paper-MojangAPI/src/main/java/com/destroystokyo/paper/brigadier/BukkitBrigadierCommandSource.java new file mode 100644 index 0000000000..7a0e81658c --- /dev/null +++ b/Paper-MojangAPI/src/main/java/com/destroystokyo/paper/brigadier/BukkitBrigadierCommandSource.java @@ -0,0 +1,21 @@ +package com.destroystokyo.paper.brigadier; + +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Entity; +import org.jetbrains.annotations.Nullable; + +public interface BukkitBrigadierCommandSource { + + @Nullable + Entity getBukkitEntity(); + + @Nullable + World getBukkitWorld(); + + @Nullable + Location getBukkitLocation(); + + CommandSender getBukkitSender(); +} diff --git a/Paper-MojangAPI/src/main/java/com/destroystokyo/paper/event/brigadier/AsyncPlayerSendCommandsEvent.java b/Paper-MojangAPI/src/main/java/com/destroystokyo/paper/event/brigadier/AsyncPlayerSendCommandsEvent.java new file mode 100644 index 0000000000..a275a85ad2 --- /dev/null +++ b/Paper-MojangAPI/src/main/java/com/destroystokyo/paper/event/brigadier/AsyncPlayerSendCommandsEvent.java @@ -0,0 +1,65 @@ +package com.destroystokyo.paper.event.brigadier; + +import com.destroystokyo.paper.brigadier.BukkitBrigadierCommandSource; +import com.mojang.brigadier.tree.RootCommandNode; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.event.HandlerList; +import org.bukkit.event.player.PlayerEvent; +import org.jetbrains.annotations.NotNull; + +/** + * Fired any time a Brigadier RootCommandNode is generated for a player to inform the client of commands. + * You may manipulate this CommandNode to change what the client sees. + * + * This event may fire on login, world change, and permission rebuilds, by plugin request, and potentially future means. + * + * This event will fire before {@link org.bukkit.event.player.PlayerCommandSendEvent}, so no filtering has been done by + * other plugins yet. + * + * WARNING: This event will potentially (and most likely) fire twice! Once for Async, and once again for Sync. + * It is important that you check event.isAsynchronous() and event.hasFiredAsync() to ensure you only act once. + * If for some reason we are unable to send this asynchronously in the future, only the sync method will fire. + * + * Your logic should look like this: + * if (event.isAsynchronous() || !event.hasFiredAsync()) { do stuff } + * + * If your logic is not safe to run asynchronously, only react to the synchronous version. + * @deprecated Draft API - Subject to change until confirmed solves desired use cases + */ +public class AsyncPlayerSendCommandsEvent <S extends BukkitBrigadierCommandSource> extends PlayerEvent { + + private static final HandlerList handlers = new HandlerList(); + private final RootCommandNode<S> node; + private final boolean hasFiredAsync; + + public AsyncPlayerSendCommandsEvent(Player player, RootCommandNode<S> node, boolean hasFiredAsync) { + super(player, !Bukkit.isPrimaryThread()); + this.node = node; + this.hasFiredAsync = hasFiredAsync; + } + + /** + * @return The full Root Command Node being sent to the client, which is mutable. + */ + public RootCommandNode<S> getCommandNode() { + return node; + } + + /** + * @return If this event has already fired asynchronously. + */ + public boolean hasFiredAsync() { + return hasFiredAsync; + } + + @NotNull + public HandlerList getHandlers() { + return handlers; + } + + @NotNull + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/Paper-MojangAPI/src/main/java/com/destroystokyo/paper/event/brigadier/CommandRegisteredEvent.java b/Paper-MojangAPI/src/main/java/com/destroystokyo/paper/event/brigadier/CommandRegisteredEvent.java new file mode 100644 index 0000000000..92924622b5 --- /dev/null +++ b/Paper-MojangAPI/src/main/java/com/destroystokyo/paper/event/brigadier/CommandRegisteredEvent.java @@ -0,0 +1,122 @@ +package com.destroystokyo.paper.event.brigadier; + +import com.destroystokyo.paper.brigadier.BukkitBrigadierCommand; +import com.destroystokyo.paper.brigadier.BukkitBrigadierCommandSource; +import com.mojang.brigadier.tree.ArgumentCommandNode; +import com.mojang.brigadier.tree.LiteralCommandNode; +import com.mojang.brigadier.tree.RootCommandNode; +import org.bukkit.command.Command; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; +import org.bukkit.event.server.ServerEvent; +import org.jetbrains.annotations.NotNull; + +/** + * Fired anytime the server synchronizes Bukkit CommandMap to Brigadier. + * + * Allows a plugin to control the Literal and Argument nodes for this command to be + * sent to the client. + * This is done at Plugin Enable time after commands have been registered, but some + * plugins may use reflection to retrigger this rebuild during runtime. + * + * @deprecated Draft API - Subject to change until confirmed solves desired use cases + */ +public class CommandRegisteredEvent <S extends BukkitBrigadierCommandSource> extends ServerEvent implements Cancellable { + + private static final HandlerList handlers = new HandlerList(); + private final String commandLabel; + private final Command command; + private final BukkitBrigadierCommand<S> brigadierCommand; + private final RootCommandNode<S> root; + private final ArgumentCommandNode<S, String> defaultArgs; + private LiteralCommandNode<S> literal; + private boolean cancelled = false; + + public CommandRegisteredEvent(String commandLabel, BukkitBrigadierCommand<S> brigadierCommand, Command command, RootCommandNode<S> root, LiteralCommandNode<S> literal, ArgumentCommandNode<S, String> defaultArgs) { + this.commandLabel = commandLabel; + this.brigadierCommand = brigadierCommand; + this.command = command; + this.root = root; + this.literal = literal; + this.defaultArgs = defaultArgs; + } + + /** + * @return The command name being registered + */ + public String getCommandLabel() { + return commandLabel; + } + + /** + * @return The Bukkit API Brigadier Wrapped Command Object to handle executions and suggestions + */ + public BukkitBrigadierCommand<S> getBrigadierCommand() { + return brigadierCommand; + } + + public Command getCommand() { + return command; + } + + /** + * @return Gets the root command node being used to register a command to. + */ + public RootCommandNode<S> getRoot() { + return root; + } + + /** + * Returns the Bukkit API's default handling of Arguments, if you wish to reuse it. + * @return + */ + public ArgumentCommandNode<S, String> getDefaultArgs() { + return defaultArgs; + } + + /** + * Returns the Bukkit API's default literal for this command, including the {@link #getDefaultArgs()} as a child already. + * @return + */ + public LiteralCommandNode<S> getLiteral() { + return literal; + } + + /** + * Changes the literal used to register this command. The previous literable is mutable, so this is primarily if + * you want to completely replace the object. + * @param literal + */ + public void setLiteral(LiteralCommandNode<S> literal) { + this.literal = literal; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isCancelled() { + return this.cancelled; + } + + /** + * Cancels registering this command to Brigadier, but will remain in Bukkit Command Map. Can be used to hide a + * command from all players. + * + * {@inheritDoc} + */ + @Override + public void setCancelled(boolean cancel) { + this.cancelled = cancel; + } + + @NotNull + public HandlerList getHandlers() { + return handlers; + } + + @NotNull + public static HandlerList getHandlerList() { + return handlers; + } +} |