mirror of
https://github.com/PaperMC/Paper.git
synced 2024-12-03 13:36:44 +01:00
Brigadier Command Support (#8235)
Adds the ability for plugins to register their own brigadier commands --------- Co-authored-by: Jake Potrebic <jake.m.potrebic@gmail.com> Co-authored-by: Jason Penilla <11360596+jpenilla@users.noreply.github.com> Co-authored-by: Bjarne Koll <lynxplay101@gmail.com>
This commit is contained in:
parent
447f9a1e16
commit
b98d20a8ac
30 changed files with 5195 additions and 515 deletions
|
@ -1,36 +0,0 @@
|
|||
plugins {
|
||||
`java-library`
|
||||
`maven-publish`
|
||||
}
|
||||
|
||||
java {
|
||||
withSourcesJar()
|
||||
withJavadocJar()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(project(":paper-api"))
|
||||
api("com.mojang:brigadier:1.0.18")
|
||||
|
||||
compileOnly("it.unimi.dsi:fastutil:8.5.6")
|
||||
compileOnly("org.jetbrains:annotations:23.0.0")
|
||||
|
||||
testImplementation("junit:junit:4.13.2")
|
||||
testImplementation("org.hamcrest:hamcrest-library:1.3")
|
||||
testImplementation("org.ow2.asm:asm-tree:9.7")
|
||||
}
|
||||
|
||||
configure<PublishingExtension> {
|
||||
publications.create<MavenPublication>("maven") {
|
||||
from(components["java"])
|
||||
}
|
||||
}
|
||||
|
||||
val scanJar = tasks.register("scanJarForBadCalls", io.papermc.paperweight.tasks.ScanJarForBadCalls::class) {
|
||||
badAnnotations.add("Lio/papermc/paper/annotation/DoNotUse;")
|
||||
jarToScan.set(tasks.jar.flatMap { it.archiveFile })
|
||||
classpath.from(configurations.compileClasspath)
|
||||
}
|
||||
tasks.check {
|
||||
dependsOn(scanJar)
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
package com.destroystokyo.paper.brigadier;
|
||||
|
||||
import com.mojang.brigadier.Command;
|
||||
import com.mojang.brigadier.suggestion.SuggestionProvider;
|
||||
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
* Brigadier {@link Command}, {@link SuggestionProvider}, and permission checker for Bukkit {@link Command}s.
|
||||
*
|
||||
* @param <S> command source type
|
||||
*/
|
||||
public interface BukkitBrigadierCommand <S extends BukkitBrigadierCommandSource> extends Command<S>, Predicate<S>, SuggestionProvider<S> {
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
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();
|
||||
}
|
|
@ -1,72 +0,0 @@
|
|||
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.ApiStatus;
|
||||
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.
|
||||
*
|
||||
* <p>This event may fire on login, world change, and permission rebuilds, by plugin request, and potentially future means.</p>
|
||||
*
|
||||
* <p>This event will fire before {@link org.bukkit.event.player.PlayerCommandSendEvent}, so no filtering has been done by
|
||||
* other plugins yet.</p>
|
||||
*
|
||||
* <p>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.</p>
|
||||
*
|
||||
* <p>Your logic should look like this:
|
||||
* {@code if (event.isAsynchronous() || !event.hasFiredAsync()) { // do stuff }}</p>
|
||||
*
|
||||
* <p>If your logic is not safe to run asynchronously, only react to the synchronous version.</p>
|
||||
*
|
||||
* <p>This is a draft/experimental API and is subject to change.</p>
|
||||
*/
|
||||
@ApiStatus.Experimental
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the full Root Command Node being sent to the client, which is mutable.
|
||||
*
|
||||
* @return the root command node
|
||||
*/
|
||||
public RootCommandNode<S> getCommandNode() {
|
||||
return node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets if this event has already fired asynchronously.
|
||||
*
|
||||
* @return whether this event has already fired asynchronously
|
||||
*/
|
||||
public boolean hasFiredAsync() {
|
||||
return hasFiredAsync;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public HandlerList getHandlers() {
|
||||
return handlers;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static HandlerList getHandlerList() {
|
||||
return handlers;
|
||||
}
|
||||
}
|
|
@ -1,84 +0,0 @@
|
|||
package com.destroystokyo.paper.event.brigadier;
|
||||
|
||||
import com.mojang.brigadier.suggestion.Suggestions;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.Cancellable;
|
||||
import org.bukkit.event.Event;
|
||||
import org.bukkit.event.HandlerList;
|
||||
import org.bukkit.event.player.PlayerEvent;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Called when sending {@link Suggestions} to the client. Will be called asynchronously if a plugin
|
||||
* marks the {@link com.destroystokyo.paper.event.server.AsyncTabCompleteEvent} event handled asynchronously,
|
||||
* otherwise called synchronously.
|
||||
*/
|
||||
public class AsyncPlayerSendSuggestionsEvent extends PlayerEvent implements Cancellable {
|
||||
|
||||
private static final HandlerList handlers = new HandlerList();
|
||||
private boolean cancelled = false;
|
||||
|
||||
private Suggestions suggestions;
|
||||
private final String buffer;
|
||||
|
||||
public AsyncPlayerSendSuggestionsEvent(Player player, Suggestions suggestions, String buffer) {
|
||||
super(player, !Bukkit.isPrimaryThread());
|
||||
this.suggestions = suggestions;
|
||||
this.buffer = buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the input buffer sent to request these suggestions.
|
||||
*
|
||||
* @return the input buffer
|
||||
*/
|
||||
public String getBuffer() {
|
||||
return buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the suggestions to be sent to client.
|
||||
*
|
||||
* @return the suggestions
|
||||
*/
|
||||
public Suggestions getSuggestions() {
|
||||
return suggestions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the suggestions to be sent to client.
|
||||
*
|
||||
* @param suggestions suggestions
|
||||
*/
|
||||
public void setSuggestions(Suggestions suggestions) {
|
||||
this.suggestions = suggestions;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public boolean isCancelled() {
|
||||
return this.cancelled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancels sending suggestions to the client.
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void setCancelled(boolean cancel) {
|
||||
this.cancelled = cancel;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public HandlerList getHandlers() {
|
||||
return handlers;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static HandlerList getHandlerList() {
|
||||
return handlers;
|
||||
}
|
||||
}
|
|
@ -1,168 +0,0 @@
|
|||
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.ApiStatus;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Fired anytime the server synchronizes Bukkit commands to Brigadier.
|
||||
*
|
||||
* <p>Allows a plugin to control the command node structure for its commands.
|
||||
* This is done at Plugin Enable time after commands have been registered, but may also
|
||||
* run at a later point in the server lifetime due to plugins, a server reload, etc.</p>
|
||||
*
|
||||
* <p>This is a draft/experimental API and is subject to change.</p>
|
||||
*/
|
||||
@ApiStatus.Experimental
|
||||
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 rawCommand = false;
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the command label of the {@link Command} being registered.
|
||||
*
|
||||
* @return the command label
|
||||
*/
|
||||
public String getCommandLabel() {
|
||||
return this.commandLabel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the {@link BukkitBrigadierCommand} for the {@link Command} being registered. This can be used
|
||||
* as the {@link com.mojang.brigadier.Command command executor} or
|
||||
* {@link com.mojang.brigadier.suggestion.SuggestionProvider} of a {@link com.mojang.brigadier.tree.CommandNode}
|
||||
* to delegate to the {@link Command} being registered.
|
||||
*
|
||||
* @return the {@link BukkitBrigadierCommand}
|
||||
*/
|
||||
public BukkitBrigadierCommand<S> getBrigadierCommand() {
|
||||
return this.brigadierCommand;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the {@link Command} being registered.
|
||||
*
|
||||
* @return the {@link Command}
|
||||
*/
|
||||
public Command getCommand() {
|
||||
return this.command;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the {@link RootCommandNode} which is being registered to.
|
||||
*
|
||||
* @return the {@link RootCommandNode}
|
||||
*/
|
||||
public RootCommandNode<S> getRoot() {
|
||||
return this.root;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the Bukkit APIs default arguments node (greedy string), for if
|
||||
* you wish to reuse it.
|
||||
*
|
||||
* @return default arguments node
|
||||
*/
|
||||
public ArgumentCommandNode<S, String> getDefaultArgs() {
|
||||
return this.defaultArgs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the {@link LiteralCommandNode} to be registered for the {@link Command}.
|
||||
*
|
||||
* @return the {@link LiteralCommandNode}
|
||||
*/
|
||||
public LiteralCommandNode<S> getLiteral() {
|
||||
return this.literal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link LiteralCommandNode} used to register this command. The default literal is mutable, so
|
||||
* this is primarily if you want to completely replace the object.
|
||||
*
|
||||
* @param literal new node
|
||||
*/
|
||||
public void setLiteral(LiteralCommandNode<S> literal) {
|
||||
this.literal = literal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets whether this command should is treated as "raw".
|
||||
*
|
||||
* @see #setRawCommand(boolean)
|
||||
* @return whether this command is treated as "raw"
|
||||
*/
|
||||
public boolean isRawCommand() {
|
||||
return this.rawCommand;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether this command should be treated as "raw".
|
||||
*
|
||||
* <p>A "raw" command will only use the node provided by this event for
|
||||
* sending the command tree to the client. For execution purposes, the default
|
||||
* greedy string execution of a standard Bukkit {@link Command} is used.</p>
|
||||
*
|
||||
* <p>On older versions of Paper, this was the default and only behavior of this
|
||||
* event.</p>
|
||||
*
|
||||
* @param rawCommand whether this command should be treated as "raw"
|
||||
*/
|
||||
public void setRawCommand(final boolean rawCommand) {
|
||||
this.rawCommand = rawCommand;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@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;
|
||||
}
|
||||
}
|
|
@ -1,42 +0,0 @@
|
|||
package io.papermc.paper.brigadier;
|
||||
|
||||
import com.mojang.brigadier.Message;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.ComponentLike;
|
||||
import net.kyori.adventure.text.TextComponent;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
|
||||
/**
|
||||
* Helper methods to bridge the gaps between Brigadier and Paper-MojangAPI.
|
||||
*/
|
||||
public final class PaperBrigadier {
|
||||
private PaperBrigadier() {
|
||||
throw new RuntimeException("PaperBrigadier is not to be instantiated!");
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new Brigadier {@link Message} from a {@link ComponentLike}.
|
||||
*
|
||||
* <p>Mostly useful for creating rich suggestion tooltips in combination with other Paper-MojangAPI APIs.</p>
|
||||
*
|
||||
* @param componentLike The {@link ComponentLike} to use for the {@link Message} contents
|
||||
* @return A new Brigadier {@link Message}
|
||||
*/
|
||||
public static @NonNull Message message(final @NonNull ComponentLike componentLike) {
|
||||
return PaperBrigadierProvider.instance().message(componentLike);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link Component} from a Brigadier {@link Message}.
|
||||
*
|
||||
* <p>If the {@link Message} was created from a {@link Component}, it will simply be
|
||||
* converted back, otherwise a new {@link TextComponent} will be created with the
|
||||
* content of {@link Message#getString()}</p>
|
||||
*
|
||||
* @param message The {@link Message} to create a {@link Component} from
|
||||
* @return The created {@link Component}
|
||||
*/
|
||||
public static @NonNull Component componentFromMessage(final @NonNull Message message) {
|
||||
return PaperBrigadierProvider.instance().componentFromMessage(message);
|
||||
}
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
package io.papermc.paper.brigadier;
|
||||
|
||||
import com.mojang.brigadier.Message;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.ComponentLike;
|
||||
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
interface PaperBrigadierProvider {
|
||||
final class Holder {
|
||||
private static @MonotonicNonNull PaperBrigadierProvider INSTANCE;
|
||||
}
|
||||
|
||||
static @NonNull PaperBrigadierProvider instance() {
|
||||
return requireNonNull(Holder.INSTANCE, "PaperBrigadierProvider has not yet been initialized!");
|
||||
}
|
||||
|
||||
static void initialize(final @NonNull PaperBrigadierProvider instance) {
|
||||
if (Holder.INSTANCE != null) {
|
||||
throw new IllegalStateException("PaperBrigadierProvider has already been initialized!");
|
||||
}
|
||||
Holder.INSTANCE = instance;
|
||||
}
|
||||
|
||||
@NonNull Message message(@NonNull ComponentLike componentLike);
|
||||
|
||||
@NonNull Component componentFromMessage(@NonNull Message message);
|
||||
}
|
|
@ -11,7 +11,7 @@ import kotlin.io.path.*
|
|||
plugins {
|
||||
java
|
||||
`maven-publish`
|
||||
id("io.papermc.paperweight.core") version "1.6.3"
|
||||
id("io.papermc.paperweight.core") version "1.7.1"
|
||||
}
|
||||
|
||||
allprojects {
|
||||
|
@ -109,7 +109,6 @@ paperweight {
|
|||
|
||||
tasks.generateDevelopmentBundle {
|
||||
apiCoordinates = "io.papermc.paper:paper-api"
|
||||
mojangApiCoordinates = "io.papermc.paper:paper-mojangapi"
|
||||
libraryRepositories.addAll(
|
||||
"https://repo.maven.apache.org/maven2/",
|
||||
paperMavenPublicUrl,
|
||||
|
|
1900
patches/api/0480-Brigadier-based-command-API.patch
Normal file
1900
patches/api/0480-Brigadier-based-command-API.patch
Normal file
File diff suppressed because it is too large
Load diff
|
@ -6,7 +6,7 @@ Subject: [PATCH] Plugin remapping
|
|||
Co-authored-by: Nassim Jahnke <nassim@njahnke.dev>
|
||||
|
||||
diff --git a/build.gradle.kts b/build.gradle.kts
|
||||
index 65fb16941fa7e3a9b300696fb6bd2b562bca48cd..5ffd1d7c130e01a4a7516b361e48bfaf41d4f321 100644
|
||||
index 66cdd81e4b65ce00973f86763cea566e43053722..2868eb8f9e577ce839d7ecf5ce8fed5bad957dbe 100644
|
||||
--- a/build.gradle.kts
|
||||
+++ b/build.gradle.kts
|
||||
@@ -46,6 +46,7 @@ dependencies {
|
||||
|
@ -1553,17 +1553,17 @@ index 0000000000000000000000000000000000000000..badff5d6ae6dd8d209c82bc7e8afe370
|
|||
+ }
|
||||
+}
|
||||
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
|
||||
index c25d80a1d5aa0f3cc2cbf1e9b94154c759aab36e..c836204a04ca050988057dcc92c7a1fbcc02ef34 100644
|
||||
index c25d80a1d5aa0f3cc2cbf1e9b94154c759aab36e..cbbd9aaeb0d87aae72edc7fb5aa10920834de8bf 100644
|
||||
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
|
||||
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
|
||||
@@ -637,6 +637,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
@@ -636,6 +636,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
}
|
||||
|
||||
this.server.enablePlugins(org.bukkit.plugin.PluginLoadOrder.POSTWORLD);
|
||||
this.server.getPluginManager().callEvent(new ServerLoadEvent(ServerLoadEvent.LoadType.STARTUP));
|
||||
+ if (io.papermc.paper.plugin.PluginInitializerManager.instance().pluginRemapper != null) io.papermc.paper.plugin.PluginInitializerManager.instance().pluginRemapper.pluginsEnabled(); // Paper - Remap plugins
|
||||
this.server.getPluginManager().callEvent(new ServerLoadEvent(ServerLoadEvent.LoadType.STARTUP));
|
||||
this.connection.acceptConnections();
|
||||
}
|
||||
|
||||
@@ -909,6 +910,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
this.server.disablePlugins();
|
||||
}
|
||||
|
@ -1904,14 +1904,14 @@ index 0000000000000000000000000000000000000000..73b20a92f330311e3fef8f03b51a0985
|
|||
+ }
|
||||
+}
|
||||
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
|
||||
index 45160b93a24dc74f6368441e2a4fe659ceaf5bf5..6573e72d041714ccc2bf0e3c8734bc212caf534e 100644
|
||||
index 45160b93a24dc74f6368441e2a4fe659ceaf5bf5..48be9bd462abba1f82200fe3425c36bf8ec91beb 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
|
||||
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
|
||||
@@ -966,6 +966,7 @@ public final class CraftServer implements Server {
|
||||
@@ -965,6 +965,7 @@ public final class CraftServer implements Server {
|
||||
this.loadPlugins();
|
||||
this.enablePlugins(PluginLoadOrder.STARTUP);
|
||||
this.enablePlugins(PluginLoadOrder.POSTWORLD);
|
||||
this.getPluginManager().callEvent(new ServerLoadEvent(ServerLoadEvent.LoadType.RELOAD));
|
||||
+ if (io.papermc.paper.plugin.PluginInitializerManager.instance().pluginRemapper != null) io.papermc.paper.plugin.PluginInitializerManager.instance().pluginRemapper.pluginsEnabled(); // Paper - Remap plugins
|
||||
this.getPluginManager().callEvent(new ServerLoadEvent(ServerLoadEvent.LoadType.RELOAD));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -18,7 +18,7 @@ index 9a1a961eabd4362c171da78c6be82c867f3696a4..1d0c473442b5c72245c356054440323e
|
|||
ComponentSerialization.TRUSTED_STREAM_CODEC.encode(buf, this.playerPrefix);
|
||||
ComponentSerialization.TRUSTED_STREAM_CODEC.encode(buf, this.playerSuffix);
|
||||
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
|
||||
index 4dc8fcd8e118a1c2f5fac0fc291b5555abeec124..53a344b3ee3813872f5f061aab660bf602b573a5 100644
|
||||
index 4712da0fcc454c1747e2677be821959c6f845f5e..6a5f4e8feb3c88662a78575590998efdf529bb51 100644
|
||||
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
|
||||
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
|
||||
@@ -636,6 +636,20 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
|
@ -40,8 +40,8 @@ index 4dc8fcd8e118a1c2f5fac0fc291b5555abeec124..53a344b3ee3813872f5f061aab660bf6
|
|||
+ // Paper end - Configurable player collision
|
||||
+
|
||||
this.server.enablePlugins(org.bukkit.plugin.PluginLoadOrder.POSTWORLD);
|
||||
this.server.getPluginManager().callEvent(new ServerLoadEvent(ServerLoadEvent.LoadType.STARTUP));
|
||||
if (io.papermc.paper.plugin.PluginInitializerManager.instance().pluginRemapper != null) io.papermc.paper.plugin.PluginInitializerManager.instance().pluginRemapper.pluginsEnabled(); // Paper - Remap plugins
|
||||
this.server.getPluginManager().callEvent(new ServerLoadEvent(ServerLoadEvent.LoadType.STARTUP));
|
||||
diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java
|
||||
index abb769cbbed17a82ee86a6c99e61a375045d9937..3fb300026e627313c65ea23b9c0a9f57a97fa310 100644
|
||||
--- a/src/main/java/net/minecraft/server/players/PlayerList.java
|
||||
|
|
|
@ -9,7 +9,7 @@ thread dumps at an interval until the point of crash.
|
|||
This will help diagnose what was going on in that time before the crash.
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
|
||||
index 478445e7ed67b17d7aff0e10e9aae0788605a4b9..077b2e1d06a4bb0c2ce10274dc6144ee76608d90 100644
|
||||
index 8f5eaecb0bda3c4d69bb077ca727458f3f190f44..5fe925b85a7783e3b972d24e01cfe4a4b878dd38 100644
|
||||
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
|
||||
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
|
||||
@@ -1104,6 +1104,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
|
@ -33,7 +33,7 @@ index 0ffa25a6e41cc56e78c79e0cee45e3b811794129..1b47e228ad7365b31d6ddd8c572d3bc5
|
|||
com.destroystokyo.paper.Metrics.PaperMetrics.startMetrics(); // Paper - start metrics
|
||||
com.destroystokyo.paper.VersionHistoryManager.INSTANCE.getClass(); // Paper - load version history now
|
||||
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
|
||||
index cbdfb4168f23bbbd37675da6dc88acb3191d88d6..9c673642b7c35bc3c443bd66fbcd278073eeccc2 100644
|
||||
index 77a9c54c77dd12df870eb60d15b381f12392e8d9..9ff04719f0b560c286d97c8ed99b04d3b32900e3 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
|
||||
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
|
||||
@@ -926,6 +926,7 @@ public final class CraftServer implements Server {
|
||||
|
@ -46,8 +46,8 @@ index cbdfb4168f23bbbd37675da6dc88acb3191d88d6..9c673642b7c35bc3c443bd66fbcd2780
|
|||
this.commandsConfiguration = YamlConfiguration.loadConfiguration(this.getCommandsConfigFile());
|
||||
@@ -1016,6 +1017,7 @@ public final class CraftServer implements Server {
|
||||
this.enablePlugins(PluginLoadOrder.POSTWORLD);
|
||||
this.getPluginManager().callEvent(new ServerLoadEvent(ServerLoadEvent.LoadType.RELOAD));
|
||||
if (io.papermc.paper.plugin.PluginInitializerManager.instance().pluginRemapper != null) io.papermc.paper.plugin.PluginInitializerManager.instance().pluginRemapper.pluginsEnabled(); // Paper - Remap plugins
|
||||
this.getPluginManager().callEvent(new ServerLoadEvent(ServerLoadEvent.LoadType.RELOAD));
|
||||
+ org.spigotmc.WatchdogThread.hasStarted = true; // Paper - Disable watchdog early timeout on reload
|
||||
}
|
||||
|
||||
|
|
|
@ -9,18 +9,6 @@ Adds AsyncPlayerSendCommandsEvent
|
|||
Adds CommandRegisteredEvent
|
||||
- Allows manipulating the CommandNode to add more children/metadata for the client
|
||||
|
||||
diff --git a/build.gradle.kts b/build.gradle.kts
|
||||
index e9498f78cb6c0973a820f093ff7a31bef44ba27f..db2d67c98c62aa90591fea82e8fb07270699d96c 100644
|
||||
--- a/build.gradle.kts
|
||||
+++ b/build.gradle.kts
|
||||
@@ -13,6 +13,7 @@ val alsoShade: Configuration by configurations.creating
|
||||
|
||||
dependencies {
|
||||
implementation(project(":paper-api"))
|
||||
+ implementation(project(":paper-mojangapi"))
|
||||
// Paper start
|
||||
implementation("org.jline:jline-terminal-jansi:3.21.0")
|
||||
implementation("net.minecrell:terminalconsoleappender:1.3.0")
|
||||
diff --git a/src/main/java/com/mojang/brigadier/exceptions/CommandSyntaxException.java b/src/main/java/com/mojang/brigadier/exceptions/CommandSyntaxException.java
|
||||
index 3370731ee064d2693b972a0765c13dd4fd69f66a..09d486a05179b9d878e1c33725b4e614c3544da9 100644
|
||||
--- a/src/main/java/com/mojang/brigadier/exceptions/CommandSyntaxException.java
|
||||
|
|
|
@ -5,10 +5,10 @@ Subject: [PATCH] Implement Mob Goal API
|
|||
|
||||
|
||||
diff --git a/build.gradle.kts b/build.gradle.kts
|
||||
index db2d67c98c62aa90591fea82e8fb07270699d96c..7a70c2c52dec44d6b6c7acc7140b2619e56646d0 100644
|
||||
index 158779a3590f089c4224b2b128c2e653aef42a94..4f8b8839f4d345f448d30de21ad9f3ad7422f98b 100644
|
||||
--- a/build.gradle.kts
|
||||
+++ b/build.gradle.kts
|
||||
@@ -41,6 +41,7 @@ dependencies {
|
||||
@@ -40,6 +40,7 @@ dependencies {
|
||||
runtimeOnly("org.apache.maven.resolver:maven-resolver-connector-basic:1.9.18")
|
||||
runtimeOnly("org.apache.maven.resolver:maven-resolver-transport-http:1.9.18")
|
||||
|
||||
|
@ -773,7 +773,7 @@ index 6667ecc4b7eded4e20a415cef1e1b1179e6710b8..16f9a98b8a939e5ca7e2dc04f87134a7
|
|||
LOOK,
|
||||
JUMP,
|
||||
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
|
||||
index c62936e7070d794aeac7e5175757aa045a6afcae..82d462fbc8936b70169ca3b55d488e1ef76aec40 100644
|
||||
index 821e50c456679f69a9b6ae058f35b601bf3c759b..bd4c982ba919f0196723b4c45be102b47054a70f 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
|
||||
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
|
||||
@@ -2907,5 +2907,11 @@ public final class CraftServer implements Server {
|
||||
|
|
|
@ -51,21 +51,19 @@ index 3728b051b9eb9e9e06bc765a9a2fae7f45daf6ff..779fee2f9b819124a01b9f8d2b7ed0d5
|
|||
}
|
||||
|
||||
diff --git a/src/main/java/org/bukkit/craftbukkit/command/VanillaCommandWrapper.java b/src/main/java/org/bukkit/craftbukkit/command/VanillaCommandWrapper.java
|
||||
index 5e6645e16b185aaa6f719055ddbf670b8741fead..bda9a0b99184adce28bb7851612ed7f4e324826d 100644
|
||||
index 5e6645e16b185aaa6f719055ddbf670b8741fead..98d314cd293d462ef109e952f3239e08e14dda59 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/command/VanillaCommandWrapper.java
|
||||
+++ b/src/main/java/org/bukkit/craftbukkit/command/VanillaCommandWrapper.java
|
||||
@@ -86,7 +86,23 @@ public final class VanillaCommandWrapper extends BukkitCommand {
|
||||
@@ -86,7 +86,21 @@ public final class VanillaCommandWrapper extends BukkitCommand {
|
||||
}
|
||||
|
||||
public static String getPermission(CommandNode<CommandSourceStack> vanillaCommand) {
|
||||
- return "minecraft.command." + ((vanillaCommand.getRedirect() == null) ? vanillaCommand.getName() : vanillaCommand.getRedirect().getName());
|
||||
+ // Paper start - Vanilla command permission fixes
|
||||
+ final String commandName;
|
||||
+ if (vanillaCommand.getRedirect() == null) {
|
||||
+ commandName = vanillaCommand.getName();
|
||||
+ } else {
|
||||
+ commandName = vanillaCommand.getRedirect().getName();
|
||||
+ while (vanillaCommand.getRedirect() != null) {
|
||||
+ vanillaCommand = vanillaCommand.getRedirect();
|
||||
+ }
|
||||
+ final String commandName = vanillaCommand.getName();
|
||||
+ return "minecraft.command." + stripDefaultNamespace(commandName);
|
||||
+ }
|
||||
+
|
||||
|
|
|
@ -5,10 +5,10 @@ Subject: [PATCH] Add support for Proxy Protocol
|
|||
|
||||
|
||||
diff --git a/build.gradle.kts b/build.gradle.kts
|
||||
index 7a70c2c52dec44d6b6c7acc7140b2619e56646d0..9b9e744d18cf66279f51f950b6ecce31415f9fa8 100644
|
||||
index 4f8b8839f4d345f448d30de21ad9f3ad7422f98b..1ed8d1ba75fa3a31469a6e8e3b661328b0debf12 100644
|
||||
--- a/build.gradle.kts
|
||||
+++ b/build.gradle.kts
|
||||
@@ -28,6 +28,7 @@ dependencies {
|
||||
@@ -27,6 +27,7 @@ dependencies {
|
||||
log4jPlugins.annotationProcessorConfigurationName("org.apache.logging.log4j:log4j-core:2.19.0") // Paper - Needed to generate meta for our Log4j plugins
|
||||
runtimeOnly(log4jPlugins.output)
|
||||
alsoShade(log4jPlugins.output)
|
||||
|
|
|
@ -5,10 +5,10 @@ Subject: [PATCH] Use Velocity compression and cipher natives
|
|||
|
||||
|
||||
diff --git a/build.gradle.kts b/build.gradle.kts
|
||||
index 9b9e744d18cf66279f51f950b6ecce31415f9fa8..5d448d8a7cf6626a11791f30ad52baf41a099272 100644
|
||||
index 1ed8d1ba75fa3a31469a6e8e3b661328b0debf12..87bb3fd9b97506f61734ae7f2e6860610ba794e7 100644
|
||||
--- a/build.gradle.kts
|
||||
+++ b/build.gradle.kts
|
||||
@@ -37,6 +37,11 @@ dependencies {
|
||||
@@ -36,6 +36,11 @@ dependencies {
|
||||
runtimeOnly("org.xerial:sqlite-jdbc:3.45.3.0")
|
||||
runtimeOnly("com.mysql:mysql-connector-j:8.3.0")
|
||||
runtimeOnly("com.lmax:disruptor:3.4.4") // Paper
|
||||
|
|
|
@ -226,13 +226,13 @@ index 0d9de4c61c7b26a6ff37c12fde629161fd0c3d5a..47355158e5e762540a10dc67b23092a0
|
|||
|
||||
private static int giveItem(CommandSourceStack source, ItemInput item, Collection<ServerPlayer> targets, int count) throws CommandSyntaxException {
|
||||
diff --git a/src/main/java/org/bukkit/craftbukkit/command/VanillaCommandWrapper.java b/src/main/java/org/bukkit/craftbukkit/command/VanillaCommandWrapper.java
|
||||
index 61115db85b81e627d11a0de21691a2ca69aafe2c..ba2a2ca0c36e61cb3cc00fafc7a5dd9f7050388f 100644
|
||||
index 2ee33c55890fa659f6d251e486264c85d9e89802..dd1507f65a7f1d84bc7f236f81a60ac1302a13b8 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/command/VanillaCommandWrapper.java
|
||||
+++ b/src/main/java/org/bukkit/craftbukkit/command/VanillaCommandWrapper.java
|
||||
@@ -98,6 +98,9 @@ public final class VanillaCommandWrapper extends BukkitCommand {
|
||||
} else {
|
||||
commandName = vanillaCommand.getRedirect().getName();
|
||||
@@ -96,6 +96,9 @@ public final class VanillaCommandWrapper extends BukkitCommand {
|
||||
vanillaCommand = vanillaCommand.getRedirect();
|
||||
}
|
||||
final String commandName = vanillaCommand.getName();
|
||||
+ if ("pgive".equals(stripDefaultNamespace(commandName))) {
|
||||
+ return "bukkit.command.paper.pgive";
|
||||
+ }
|
||||
|
|
2770
patches/server/1050-Brigadier-based-command-API.patch
Normal file
2770
patches/server/1050-Brigadier-based-command-API.patch
Normal file
File diff suppressed because it is too large
Load diff
|
@ -33,7 +33,7 @@ if (!file(".git").exists()) {
|
|||
|
||||
rootProject.name = "paper"
|
||||
|
||||
for (name in listOf("Paper-API", "Paper-Server", "Paper-MojangAPI")) {
|
||||
for (name in listOf("Paper-API", "Paper-Server")) {
|
||||
val projName = name.lowercase(Locale.ENGLISH)
|
||||
include(projName)
|
||||
findProject(":$projName")!!.projectDir = file(name)
|
||||
|
|
|
@ -2,7 +2,6 @@ version = "1.0.0-SNAPSHOT"
|
|||
|
||||
dependencies {
|
||||
compileOnly(project(":paper-api"))
|
||||
compileOnly(project(":paper-mojangapi"))
|
||||
}
|
||||
|
||||
tasks.processResources {
|
||||
|
|
|
@ -8,5 +8,8 @@ public final class TestPlugin extends JavaPlugin implements Listener {
|
|||
@Override
|
||||
public void onEnable() {
|
||||
this.getServer().getPluginManager().registerEvents(this, this);
|
||||
|
||||
// io.papermc.testplugin.brigtests.Registration.registerViaOnEnable(this);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ public class TestPluginBootstrap implements PluginBootstrap {
|
|||
|
||||
@Override
|
||||
public void bootstrap(@NotNull BootstrapContext context) {
|
||||
// io.papermc.testplugin.brigtests.Registration.registerViaBootstrap(context);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,166 @@
|
|||
package io.papermc.testplugin.brigtests;
|
||||
|
||||
import com.mojang.brigadier.Command;
|
||||
import io.papermc.paper.command.brigadier.BasicCommand;
|
||||
import io.papermc.paper.command.brigadier.CommandSourceStack;
|
||||
import io.papermc.paper.command.brigadier.Commands;
|
||||
import io.papermc.paper.command.brigadier.argument.ArgumentTypes;
|
||||
import io.papermc.paper.command.brigadier.argument.range.DoubleRangeProvider;
|
||||
import io.papermc.paper.plugin.bootstrap.BootstrapContext;
|
||||
import io.papermc.paper.plugin.lifecycle.event.LifecycleEventManager;
|
||||
import io.papermc.paper.plugin.lifecycle.event.types.LifecycleEvents;
|
||||
import io.papermc.testplugin.brigtests.example.ExampleAdminCommand;
|
||||
import io.papermc.testplugin.brigtests.example.MaterialArgumentType;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.command.defaults.BukkitCommand;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public final class Registration {
|
||||
|
||||
private Registration() {
|
||||
}
|
||||
|
||||
public static void registerViaOnEnable(final JavaPlugin plugin) {
|
||||
registerLegacyCommands(plugin);
|
||||
registerViaLifecycleEvents(plugin);
|
||||
}
|
||||
|
||||
private static void registerViaLifecycleEvents(final JavaPlugin plugin) {
|
||||
final LifecycleEventManager<Plugin> lifecycleManager = plugin.getLifecycleManager();
|
||||
lifecycleManager.registerEventHandler(LifecycleEvents.COMMANDS, event -> {
|
||||
final Commands commands = event.registrar();
|
||||
// ensure plugin commands override
|
||||
commands.register(Commands.literal("tag")
|
||||
.executes(ctx -> {
|
||||
ctx.getSource().getSender().sendPlainMessage("overriden command");
|
||||
return Command.SINGLE_SUCCESS;
|
||||
})
|
||||
.build(),
|
||||
null,
|
||||
Collections.emptyList()
|
||||
);
|
||||
});
|
||||
|
||||
lifecycleManager.registerEventHandler(LifecycleEvents.COMMANDS.newHandler(event -> {
|
||||
final Commands commands = event.registrar();
|
||||
commands.register(plugin.getPluginMeta(), Commands.literal("root_command")
|
||||
.then(Commands.literal("sub_command")
|
||||
.requires(source -> source.getSender().hasPermission("testplugin.test"))
|
||||
.executes(ctx -> {
|
||||
ctx.getSource().getSender().sendPlainMessage("root_command sub_command");
|
||||
return Command.SINGLE_SUCCESS;
|
||||
})).build(),
|
||||
null,
|
||||
Collections.emptyList()
|
||||
);
|
||||
|
||||
commands.register(plugin.getPluginMeta(), "example", "test", Collections.emptyList(), new BasicCommand() {
|
||||
@Override
|
||||
public void execute(@NotNull final CommandSourceStack commandSourceStack, final @NotNull String[] args) {
|
||||
System.out.println(Arrays.toString(args));
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Collection<String> suggest(final @NotNull CommandSourceStack commandSourceStack, final @NotNull String[] args) {
|
||||
System.out.println(Arrays.toString(args));
|
||||
return List.of("apple", "banana");
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
commands.register(plugin.getPluginMeta(), Commands.literal("water")
|
||||
.requires(source -> {
|
||||
System.out.println("isInWater check");
|
||||
return source.getExecutor().isInWater();
|
||||
})
|
||||
.executes(ctx -> {
|
||||
ctx.getSource().getExecutor().sendMessage("You are in water!");
|
||||
return Command.SINGLE_SUCCESS;
|
||||
}).then(Commands.literal("lava")
|
||||
.requires(source -> {
|
||||
System.out.println("isInLava check");
|
||||
if (source.getExecutor() != null) {
|
||||
return source.getExecutor().isInLava();
|
||||
}
|
||||
return true;
|
||||
})
|
||||
.executes(ctx -> {
|
||||
ctx.getSource().getExecutor().sendMessage("You are in lava!");
|
||||
return Command.SINGLE_SUCCESS;
|
||||
})).build(),
|
||||
null,
|
||||
Collections.emptyList());
|
||||
|
||||
|
||||
ExampleAdminCommand.register(plugin, commands);
|
||||
}).priority(10));
|
||||
}
|
||||
|
||||
private static void registerLegacyCommands(final JavaPlugin plugin) {
|
||||
plugin.getServer().getCommandMap().register("fallback", new BukkitCommand("hi", "cool hi command", "<>", List.of("hialias")) {
|
||||
@Override
|
||||
public boolean execute(@NotNull CommandSender sender, @NotNull String commandLabel, @NotNull String[] args) {
|
||||
sender.sendMessage("hi");
|
||||
return true;
|
||||
}
|
||||
});
|
||||
plugin.getServer().getCommandMap().register("fallback", new BukkitCommand("cooler-command", "cool hi command", "<>", List.of("cooler-command-alias")) {
|
||||
@Override
|
||||
public boolean execute(@NotNull CommandSender sender, @NotNull String commandLabel, @NotNull String[] args) {
|
||||
sender.sendMessage("hi");
|
||||
return true;
|
||||
}
|
||||
});
|
||||
plugin.getServer().getCommandMap().getKnownCommands().values().removeIf((command) -> {
|
||||
return command.getName().equals("hi");
|
||||
});
|
||||
}
|
||||
|
||||
public static void registerViaBootstrap(final BootstrapContext context) {
|
||||
final LifecycleEventManager<BootstrapContext> lifecycleManager = context.getLifecycleManager();
|
||||
lifecycleManager.registerEventHandler(LifecycleEvents.COMMANDS, event -> {
|
||||
final Commands commands = event.registrar();
|
||||
commands.register(Commands.literal("material")
|
||||
.then(Commands.literal("item")
|
||||
.then(Commands.argument("mat", MaterialArgumentType.item())
|
||||
.executes(ctx -> {
|
||||
ctx.getSource().getSender().sendPlainMessage(ctx.getArgument("mat", Material.class).name());
|
||||
return Command.SINGLE_SUCCESS;
|
||||
})
|
||||
)
|
||||
).then(Commands.literal("block")
|
||||
.then(Commands.argument("mat", MaterialArgumentType.block())
|
||||
.executes(ctx -> {
|
||||
ctx.getSource().getSender().sendPlainMessage(ctx.getArgument("mat", Material.class).name());
|
||||
return Command.SINGLE_SUCCESS;
|
||||
})
|
||||
)
|
||||
)
|
||||
.build(),
|
||||
null,
|
||||
Collections.emptyList()
|
||||
);
|
||||
});
|
||||
|
||||
lifecycleManager.registerEventHandler(LifecycleEvents.COMMANDS.newHandler(event -> {
|
||||
final Commands commands = event.registrar();
|
||||
commands.register(Commands.literal("heya")
|
||||
.then(Commands.argument("range", ArgumentTypes.doubleRange())
|
||||
.executes((ct) -> {
|
||||
ct.getSource().getSender().sendPlainMessage(ct.getArgument("range", DoubleRangeProvider.class).range().toString());
|
||||
return 1;
|
||||
})
|
||||
).build(),
|
||||
null,
|
||||
Collections.emptyList()
|
||||
);
|
||||
}).priority(10));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
package io.papermc.testplugin.brigtests.example;
|
||||
|
||||
import com.mojang.brigadier.ImmutableStringReader;
|
||||
import com.mojang.brigadier.Message;
|
||||
import com.mojang.brigadier.exceptions.CommandExceptionType;
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
import io.papermc.paper.command.brigadier.MessageComponentSerializer;
|
||||
import net.kyori.adventure.text.Component;
|
||||
|
||||
public class ComponentCommandExceptionType implements CommandExceptionType {
|
||||
|
||||
private final Message message;
|
||||
|
||||
public ComponentCommandExceptionType(final Component message) {
|
||||
this.message = MessageComponentSerializer.message().serialize(message);
|
||||
}
|
||||
|
||||
public CommandSyntaxException create() {
|
||||
return new CommandSyntaxException(this, this.message);
|
||||
}
|
||||
|
||||
public CommandSyntaxException createWithContext(final ImmutableStringReader reader) {
|
||||
return new CommandSyntaxException(this, this.message, reader.getString(), reader.getCursor());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,154 @@
|
|||
package io.papermc.testplugin.brigtests.example;
|
||||
|
||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||
import io.papermc.paper.command.brigadier.CommandSourceStack;
|
||||
import io.papermc.paper.command.brigadier.Commands;
|
||||
import io.papermc.paper.command.brigadier.argument.SignedMessageResolver;
|
||||
import io.papermc.paper.command.brigadier.argument.ArgumentTypes;
|
||||
import io.papermc.paper.command.brigadier.argument.resolvers.BlockPositionResolver;
|
||||
import io.papermc.paper.command.brigadier.argument.resolvers.selector.PlayerSelectorArgumentResolver;
|
||||
import io.papermc.paper.math.BlockPosition;
|
||||
import io.papermc.testplugin.TestPlugin;
|
||||
import net.kyori.adventure.chat.ChatType;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.block.BlockState;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class ExampleAdminCommand {
|
||||
|
||||
public static void register(JavaPlugin plugin, Commands commands) {
|
||||
final LiteralArgumentBuilder<CommandSourceStack> adminBuilder = Commands.literal("admin")
|
||||
.executes((ct) -> {
|
||||
ct.getSource().getSender().sendPlainMessage("root admin");
|
||||
return 1;
|
||||
})
|
||||
.then(
|
||||
Commands.literal("tp")
|
||||
.then(
|
||||
Commands.argument("player", ArgumentTypes.player()).executes((source) -> {
|
||||
CommandSourceStack sourceStack = source.getSource();
|
||||
Player resolved = source.getArgument("player", PlayerSelectorArgumentResolver.class).resolve(sourceStack).get(0);
|
||||
|
||||
if (resolved == source.getSource().getExecutor()) {
|
||||
source.getSource().getExecutor().sendMessage(Component.text("Can't teleport to self!"));
|
||||
return 0;
|
||||
}
|
||||
Entity entity = source.getSource().getExecutor();
|
||||
if (entity != null) {
|
||||
entity.teleport(resolved);
|
||||
}
|
||||
|
||||
return 1;
|
||||
})
|
||||
)
|
||||
)
|
||||
.then(
|
||||
Commands.literal("tp-self")
|
||||
.executes((cmd) -> {
|
||||
if (cmd.getSource().getSender() instanceof Player player) {
|
||||
player.teleport(cmd.getSource().getLocation());
|
||||
}
|
||||
|
||||
return com.mojang.brigadier.Command.SINGLE_SUCCESS;
|
||||
})
|
||||
)
|
||||
.then(
|
||||
Commands.literal("broadcast")
|
||||
.then(
|
||||
Commands.argument("message", ArgumentTypes.component()).executes((source) -> {
|
||||
Component message = source.getArgument("message", Component.class);
|
||||
Bukkit.broadcast(message);
|
||||
return 1;
|
||||
})
|
||||
)
|
||||
)
|
||||
.then(
|
||||
Commands.literal("ice_cream").then(
|
||||
Commands.argument("type", new IceCreamTypeArgument()).executes((context) -> {
|
||||
IceCreamType argumentResponse = context.getArgument("type", IceCreamType.class); // Gets the raw argument
|
||||
context.getSource().getSender().sendMessage(Component.text("You like: " + argumentResponse));
|
||||
return 1;
|
||||
})
|
||||
)
|
||||
)
|
||||
.then(
|
||||
Commands.literal("execute")
|
||||
.redirect(commands.getDispatcher().getRoot().getChild("execute"))
|
||||
)
|
||||
.then(
|
||||
Commands.literal("signed_message").then(
|
||||
Commands.argument("msg", ArgumentTypes.signedMessage()).executes((context) -> {
|
||||
SignedMessageResolver argumentResponse = context.getArgument("msg", SignedMessageResolver.class); // Gets the raw argument
|
||||
|
||||
// This is a better way of getting signed messages, includes the concept of "disguised" messages.
|
||||
argumentResponse.resolveSignedMessage("msg", context)
|
||||
.thenAccept((signedMsg) -> {
|
||||
context.getSource().getSender().sendMessage(signedMsg, ChatType.SAY_COMMAND.bind(Component.text("STATIC")));
|
||||
});
|
||||
|
||||
return 1;
|
||||
})
|
||||
)
|
||||
)
|
||||
.then(
|
||||
Commands.literal("setblock").then(
|
||||
Commands.argument("block", ArgumentTypes.blockState())
|
||||
.then(Commands.argument("pos", ArgumentTypes.blockPosition())
|
||||
.executes((context) -> {
|
||||
CommandSourceStack sourceStack = context.getSource();
|
||||
BlockPosition position = context.getArgument("pos", BlockPositionResolver.class).resolve(sourceStack);
|
||||
BlockState state = context.getArgument("block", BlockState.class);
|
||||
|
||||
// TODO: better block state api here? :thinking:
|
||||
Block block = context.getSource().getLocation().getWorld().getBlockAt(position.blockX(), position.blockY(), position.blockZ());
|
||||
block.setType(state.getType());
|
||||
block.setBlockData(state.getBlockData());
|
||||
|
||||
return 1;
|
||||
})
|
||||
)
|
||||
)
|
||||
);
|
||||
commands.register(plugin.getPluginMeta(), adminBuilder.build(), "Cool command showcasing what you can do!", List.of("alias_for_admin_that_you_shouldnt_use", "a"));
|
||||
|
||||
|
||||
Bukkit.getCommandMap().register(
|
||||
"legacy",
|
||||
new Command("legacy_command") {
|
||||
@Override
|
||||
public boolean execute(@NotNull CommandSender sender, @NotNull String commandLabel, @NotNull String[] args) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull List<String> tabComplete(@NotNull CommandSender sender, @NotNull String alias, @NotNull String[] args) throws IllegalArgumentException {
|
||||
return List.of(String.join(" ", args));
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
Bukkit.getCommandMap().register(
|
||||
"legacy",
|
||||
new Command("legacy_fail") {
|
||||
@Override
|
||||
public boolean execute(@NotNull CommandSender sender, @NotNull String commandLabel, @NotNull String[] args) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull List<String> tabComplete(@NotNull CommandSender sender, @NotNull String alias, @NotNull String[] args) throws IllegalArgumentException {
|
||||
return List.of(String.join(" ", args));
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
package io.papermc.testplugin.brigtests.example;
|
||||
|
||||
public enum IceCreamType {
|
||||
VANILLA,
|
||||
CHOCOLATE,
|
||||
BLUE_MOON,
|
||||
STRAWBERRY,
|
||||
WHOLE_MILK
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
package io.papermc.testplugin.brigtests.example;
|
||||
|
||||
import com.mojang.brigadier.Message;
|
||||
import com.mojang.brigadier.arguments.ArgumentType;
|
||||
import com.mojang.brigadier.arguments.StringArgumentType;
|
||||
import com.mojang.brigadier.context.CommandContext;
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
import com.mojang.brigadier.exceptions.SimpleCommandExceptionType;
|
||||
import com.mojang.brigadier.suggestion.Suggestions;
|
||||
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
|
||||
import io.papermc.paper.command.brigadier.MessageComponentSerializer;
|
||||
import io.papermc.paper.command.brigadier.argument.CustomArgumentType;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class IceCreamTypeArgument implements CustomArgumentType.Converted<IceCreamType, String> {
|
||||
|
||||
@Override
|
||||
public @NotNull IceCreamType convert(String nativeType) throws CommandSyntaxException {
|
||||
try {
|
||||
return IceCreamType.valueOf(nativeType.toUpperCase());
|
||||
} catch (Exception e) {
|
||||
Message message = MessageComponentSerializer.message().serialize(Component.text("Invalid species %s!".formatted(nativeType), NamedTextColor.RED));
|
||||
|
||||
throw new CommandSyntaxException(new SimpleCommandExceptionType(message), message);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ArgumentType<String> getNativeType() {
|
||||
return StringArgumentType.word();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <S> CompletableFuture<Suggestions> listSuggestions(CommandContext<S> context, SuggestionsBuilder builder) {
|
||||
for (IceCreamType species : IceCreamType.values()) {
|
||||
builder.suggest(species.name(), MessageComponentSerializer.message().serialize(Component.text("COOL! TOOLTIP!", NamedTextColor.GREEN)));
|
||||
}
|
||||
|
||||
return CompletableFuture.completedFuture(
|
||||
builder.build()
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,88 @@
|
|||
package io.papermc.testplugin.brigtests.example;
|
||||
|
||||
import com.mojang.brigadier.arguments.ArgumentType;
|
||||
import com.mojang.brigadier.context.CommandContext;
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
import com.mojang.brigadier.suggestion.Suggestions;
|
||||
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
|
||||
import io.papermc.paper.command.brigadier.argument.CustomArgumentType;
|
||||
import io.papermc.paper.command.brigadier.argument.ArgumentTypes;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Stream;
|
||||
import java.util.stream.StreamSupport;
|
||||
import org.bukkit.Keyed;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.NamespacedKey;
|
||||
import org.bukkit.Registry;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import static net.kyori.adventure.text.Component.translatable;
|
||||
|
||||
public class MaterialArgumentType implements CustomArgumentType.Converted<Material, NamespacedKey> {
|
||||
|
||||
private static final ComponentCommandExceptionType ERROR_INVALID = new ComponentCommandExceptionType(translatable("argument.id.invalid"));
|
||||
|
||||
private final Predicate<Material> check;
|
||||
|
||||
private MaterialArgumentType(Predicate<Material> check) {
|
||||
this.check = check;
|
||||
}
|
||||
|
||||
public static MaterialArgumentType item() {
|
||||
return new MaterialArgumentType(Material::isItem);
|
||||
}
|
||||
|
||||
public static MaterialArgumentType block() {
|
||||
return new MaterialArgumentType(Material::isBlock);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Material convert(final @NotNull NamespacedKey nativeType) throws CommandSyntaxException {
|
||||
final Material material = Registry.MATERIAL.get(nativeType);
|
||||
if (material == null) {
|
||||
throw ERROR_INVALID.create();
|
||||
}
|
||||
if (!this.check.test(material)) {
|
||||
throw ERROR_INVALID.create();
|
||||
}
|
||||
return material;
|
||||
}
|
||||
|
||||
static boolean matchesSubStr(String remaining, String candidate) {
|
||||
for(int i = 0; !candidate.startsWith(remaining, i); ++i) {
|
||||
i = candidate.indexOf('_', i);
|
||||
if (i < 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ArgumentType<NamespacedKey> getNativeType() {
|
||||
return ArgumentTypes.namespacedKey();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull <S> CompletableFuture<Suggestions> listSuggestions(final @NotNull CommandContext<S> context, final @NotNull SuggestionsBuilder builder) {
|
||||
final Stream<Material> stream = StreamSupport.stream(Registry.MATERIAL.spliterator(), false);
|
||||
final String remaining = builder.getRemaining();
|
||||
boolean containsColon = remaining.indexOf(':') > -1;
|
||||
stream.filter(this.check)
|
||||
.map(Keyed::key)
|
||||
.forEach(key -> {
|
||||
final String keyAsString = key.asString();
|
||||
if (containsColon) {
|
||||
if (matchesSubStr(remaining, keyAsString)) {
|
||||
builder.suggest(keyAsString);
|
||||
}
|
||||
} else if (matchesSubStr(remaining, key.namespace()) || "minecraft".equals(key.namespace()) && matchesSubStr(remaining, key.value())) {
|
||||
builder.suggest(keyAsString);
|
||||
}
|
||||
});
|
||||
return builder.buildFuture();
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in a new issue