Add simpler JavaPlugin command registration (#12142)

This commit is contained in:
Jake Potrebic
2025-02-17 15:21:29 -08:00
committed by GitHub
parent e494f2894e
commit b386a8f527

View File

@@ -2,6 +2,8 @@ package org.bukkit.plugin.java;
import com.google.common.base.Charsets;
import com.google.common.base.Preconditions;
import io.papermc.paper.command.brigadier.BasicCommand;
import io.papermc.paper.plugin.lifecycle.event.types.LifecycleEvents;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
@@ -11,6 +13,8 @@ import java.io.OutputStream;
import java.io.Reader;
import java.net.URL;
import java.net.URLConnection;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.logging.Level;
@@ -26,14 +30,16 @@ import org.bukkit.generator.ChunkGenerator;
import org.bukkit.plugin.PluginBase;
import org.bukkit.plugin.PluginDescriptionFile;
import org.bukkit.plugin.PluginLoader;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.ApiStatus;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;
/**
* Represents a Java plugin and its main class. It contains fundamental methods
* and fields for a plugin to be loaded and work properly. This is an indirect
* implementation of {@link org.bukkit.plugin.Plugin}.
*/
@NullMarked
public abstract class JavaPlugin extends PluginBase {
private boolean isEnabled = false;
private PluginLoader loader = null;
@@ -61,7 +67,7 @@ public abstract class JavaPlugin extends PluginBase {
}
@Deprecated(forRemoval = true)
protected JavaPlugin(@NotNull final JavaPluginLoader loader, @NotNull final PluginDescriptionFile description, @NotNull final File dataFolder, @NotNull final File file) {
protected JavaPlugin(final JavaPluginLoader loader, final PluginDescriptionFile description, final File dataFolder, final File file) {
final ClassLoader classLoader = this.getClass().getClassLoader();
if (classLoader instanceof PluginClassLoader) {
throw new IllegalStateException("Cannot use initialization constructor at runtime");
@@ -75,7 +81,6 @@ public abstract class JavaPlugin extends PluginBase {
*
* @return The folder.
*/
@NotNull
@Override
public final File getDataFolder() {
return dataFolder;
@@ -88,7 +93,6 @@ public abstract class JavaPlugin extends PluginBase {
* @deprecated Plugin loading now occurs at a point which makes it impossible to expose this
* behavior. This instance will only throw unsupported operation exceptions.
*/
@NotNull
@Override
@Deprecated(forRemoval = true)
public final PluginLoader getPluginLoader() {
@@ -100,7 +104,6 @@ public abstract class JavaPlugin extends PluginBase {
*
* @return Server running this plugin
*/
@NotNull
@Override
public final Server getServer() {
return server;
@@ -121,7 +124,6 @@ public abstract class JavaPlugin extends PluginBase {
*
* @return File containing this plugin
*/
@NotNull
protected File getFile() {
return file;
}
@@ -132,19 +134,16 @@ public abstract class JavaPlugin extends PluginBase {
* @return Contents of the plugin.yml file
* @deprecated No longer applicable to all types of plugins
*/
@NotNull
@Override
@Deprecated
public final PluginDescriptionFile getDescription() {
return description;
}
@NotNull
public final io.papermc.paper.plugin.configuration.PluginMeta getPluginMeta() {
return this.pluginMeta;
}
@NotNull
@Override
public FileConfiguration getConfig() {
if (newConfig == null) {
@@ -163,8 +162,7 @@ public abstract class JavaPlugin extends PluginBase {
* @throws IllegalArgumentException if file is null
* @see ClassLoader#getResourceAsStream(String)
*/
@Nullable
protected final Reader getTextResource(@NotNull String file) {
protected final @Nullable Reader getTextResource(String file) {
final InputStream in = getResource(file);
return in == null ? null : new InputStreamReader(in, Charsets.UTF_8);
@@ -199,7 +197,7 @@ public abstract class JavaPlugin extends PluginBase {
}
@Override
public void saveResource(@NotNull String resourcePath, boolean replace) {
public void saveResource(String resourcePath, boolean replace) {
if (resourcePath == null || resourcePath.equals("")) {
throw new IllegalArgumentException("ResourcePath cannot be null or empty");
}
@@ -236,9 +234,8 @@ public abstract class JavaPlugin extends PluginBase {
}
}
@Nullable
@Override
public InputStream getResource(@NotNull String filename) {
public @Nullable InputStream getResource(String filename) {
if (filename == null) {
throw new IllegalArgumentException("Filename cannot be null");
}
@@ -263,7 +260,6 @@ public abstract class JavaPlugin extends PluginBase {
*
* @return ClassLoader holding this plugin
*/
@NotNull
protected final ClassLoader getClassLoader() {
return classLoader;
}
@@ -296,11 +292,11 @@ public abstract class JavaPlugin extends PluginBase {
private static final PluginLoader INSTANCE = net.kyori.adventure.util.Services.service(PluginLoader.class)
.orElseThrow();
}
public final void init(@NotNull PluginLoader loader, @NotNull Server server, @NotNull PluginDescriptionFile description, @NotNull File dataFolder, @NotNull File file, @NotNull ClassLoader classLoader) {
public final void init(PluginLoader loader, Server server, PluginDescriptionFile description, File dataFolder, File file, ClassLoader classLoader) {
init(server, description, dataFolder, file, classLoader, description, com.destroystokyo.paper.utils.PaperPluginLogger.getLogger(description));
this.pluginMeta = description;
}
public final void init(@NotNull Server server, @NotNull PluginDescriptionFile description, @NotNull File dataFolder, @NotNull File file, @NotNull ClassLoader classLoader, @Nullable io.papermc.paper.plugin.configuration.PluginMeta configuration, @NotNull Logger logger) {
public final void init(Server server, PluginDescriptionFile description, File dataFolder, File file, ClassLoader classLoader, io.papermc.paper.plugin.configuration.@Nullable PluginMeta configuration, Logger logger) {
this.loader = DummyPluginLoaderImplHolder.INSTANCE;
this.server = server;
this.file = file;
@@ -316,7 +312,7 @@ public abstract class JavaPlugin extends PluginBase {
* {@inheritDoc}
*/
@Override
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String @NotNull [] args) {
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
return false;
}
@@ -324,8 +320,7 @@ public abstract class JavaPlugin extends PluginBase {
* {@inheritDoc}
*/
@Override
@Nullable
public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, @NotNull String @NotNull [] args) {
public @Nullable List<String> onTabComplete(CommandSender sender, Command command, String alias, String[] args) {
return null;
}
@@ -337,14 +332,15 @@ public abstract class JavaPlugin extends PluginBase {
* @param name name or alias of the command
* @return the plugin command if found, otherwise null
* @throws UnsupportedOperationException if this plugin is a paper plugin and the method is called in {@link #onEnable()}
* @see #registerCommand(String, String, Collection, BasicCommand)
*/
@Nullable
public PluginCommand getCommand(@NotNull String name) {
public @Nullable PluginCommand getCommand(String name) {
if (this.isBeingEnabled && !(pluginMeta instanceof PluginDescriptionFile)) {
throw new UnsupportedOperationException("""
You are trying to call JavaPlugin#getCommand on a Paper plugin during startup:
you are probably trying to get a command you tried to define in paper-plugin.yml.
Paper plugins do not support YAML-based command declarations!
You can use JavaPlugin#registerCommand to define commands in Paper plugins.
Please check the documentation for more information on how to define commands in Paper plugins: https://docs.papermc.io/paper/dev/getting-started/paper-plugins#commands
""");
}
@@ -362,6 +358,88 @@ public abstract class JavaPlugin extends PluginBase {
}
}
/**
* Registers a command for this plugin. Only valid to be called inside {@link #onEnable()}.
*
* <p>Commands have certain overriding behavior:
* <ul>
* <li>Aliases will not override already existing commands (excluding namespaced ones)</li>
* <li>Aliases are <b>not</b> Brigadier redirects, they just copy the command to a different label</li>
* <li>The main command/namespaced label will override already existing commands</li>
* </ul>
*
* @param label the label of the to-be-registered command
* @param basicCommand the basic command instance to register
* @see LifecycleEvents#COMMANDS
*/
@ApiStatus.Experimental
public void registerCommand(final String label, final BasicCommand basicCommand) {
this.registerCommand(label, null, Collections.emptyList(), basicCommand);
}
/**
* Registers a command for this plugin. Only valid to be called inside {@link #onEnable()}.
*
* <p>Commands have certain overriding behavior:
* <ul>
* <li>Aliases will not override already existing commands (excluding namespaced ones)</li>
* <li>Aliases are <b>not</b> Brigadier redirects, they just copy the command to a different label</li>
* <li>The main command/namespaced label will override already existing commands</li>
* </ul>
*
* @param label the label of the to-be-registered command
* @param description the help description for the root literal node
* @param basicCommand the basic command instance to register
* @see LifecycleEvents#COMMANDS
*/
@ApiStatus.Experimental
public void registerCommand(final String label, final @Nullable String description, final BasicCommand basicCommand) {
this.registerCommand(label, description, Collections.emptyList(), basicCommand);
}
/**
* Registers a command for this plugin. Only valid to be called inside {@link #onEnable()}.
*
* <p>Commands have certain overriding behavior:
* <ul>
* <li>Aliases will not override already existing commands (excluding namespaced ones)</li>
* <li>Aliases are <b>not</b> Brigadier redirects, they just copy the command to a different label</li>
* <li>The main command/namespaced label will override already existing commands</li>
* </ul>
*
* @param label the label of the to-be-registered command
* @param aliases a collection of aliases to register the basic command under.
* @param basicCommand the basic command instance to register
* @see LifecycleEvents#COMMANDS
*/
@ApiStatus.Experimental
public void registerCommand(final String label, final Collection<String> aliases, final BasicCommand basicCommand) {
this.registerCommand(label, null, aliases, basicCommand);
}
/**
* Registers a command for this plugin. Only valid to be called inside {@link #onEnable()}.
*
* <p>Commands have certain overriding behavior:
* <ul>
* <li>Aliases will not override already existing commands (excluding namespaced ones)</li>
* <li>Aliases are <b>not</b> Brigadier redirects, they just copy the command to a different label</li>
* <li>The main command/namespaced label will override already existing commands</li>
* </ul>
*
* @param label the label of the to-be-registered command
* @param description the help description for the root literal node
* @param aliases a collection of aliases to register the basic command under.
* @param basicCommand the basic command instance to register
* @see LifecycleEvents#COMMANDS
*/
@ApiStatus.Experimental
public void registerCommand(final String label, final @Nullable String description, final Collection<String> aliases, final BasicCommand basicCommand) {
this.getLifecycleManager().registerEventHandler(LifecycleEvents.COMMANDS, event -> {
event.registrar().register(label, description, aliases, basicCommand);
});
}
@Override
public void onLoad() {}
@@ -371,15 +449,13 @@ public abstract class JavaPlugin extends PluginBase {
@Override
public void onEnable() {}
@Nullable
@Override
public ChunkGenerator getDefaultWorldGenerator(@NotNull String worldName, @Nullable String id) {
public @Nullable ChunkGenerator getDefaultWorldGenerator(String worldName, @Nullable String id) {
return null;
}
@Nullable
@Override
public BiomeProvider getDefaultBiomeProvider(@NotNull String worldName, @Nullable String id) {
public @Nullable BiomeProvider getDefaultBiomeProvider(String worldName, @Nullable String id) {
return null;
}
@@ -393,13 +469,11 @@ public abstract class JavaPlugin extends PluginBase {
this.naggable = canNag;
}
@NotNull
@Override
public Logger getLogger() {
return logger;
}
@NotNull
@Override
public String toString() {
return description.getFullName();
@@ -428,8 +502,7 @@ public abstract class JavaPlugin extends PluginBase {
* @throws ClassCastException if plugin that provided the class does not
* extend the class
*/
@NotNull
public static <T extends JavaPlugin> T getPlugin(@NotNull Class<T> clazz) {
public static <T extends JavaPlugin> T getPlugin(Class<T> clazz) {
Preconditions.checkArgument(clazz != null, "Null class cannot have a plugin");
if (!JavaPlugin.class.isAssignableFrom(clazz)) {
throw new IllegalArgumentException(clazz + " does not extend " + JavaPlugin.class);
@@ -457,8 +530,7 @@ public abstract class JavaPlugin extends PluginBase {
* @throws IllegalStateException if called from the static initializer for
* given JavaPlugin
*/
@NotNull
public static JavaPlugin getProvidingPlugin(@NotNull Class<?> clazz) {
public static JavaPlugin getProvidingPlugin(Class<?> clazz) {
Preconditions.checkArgument(clazz != null, "Null class cannot have a plugin");
final ClassLoader cl = clazz.getClassLoader();
if (!(cl instanceof io.papermc.paper.plugin.provider.classloader.ConfiguredPluginClassLoader configuredPluginClassLoader)) {
@@ -472,7 +544,7 @@ public abstract class JavaPlugin extends PluginBase {
}
@Override
public final io.papermc.paper.plugin.lifecycle.event.@NotNull LifecycleEventManager<org.bukkit.plugin.Plugin> getLifecycleManager() {
public final io.papermc.paper.plugin.lifecycle.event.LifecycleEventManager<org.bukkit.plugin.Plugin> getLifecycleManager() {
return this.lifecycleEventManager;
}
}