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.Charsets;
import com.google.common.base.Preconditions; 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.File;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
@@ -11,6 +13,8 @@ import java.io.OutputStream;
import java.io.Reader; import java.io.Reader;
import java.net.URL; import java.net.URL;
import java.net.URLConnection; import java.net.URLConnection;
import java.util.Collection;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.logging.Level; import java.util.logging.Level;
@@ -26,14 +30,16 @@ import org.bukkit.generator.ChunkGenerator;
import org.bukkit.plugin.PluginBase; import org.bukkit.plugin.PluginBase;
import org.bukkit.plugin.PluginDescriptionFile; import org.bukkit.plugin.PluginDescriptionFile;
import org.bukkit.plugin.PluginLoader; import org.bukkit.plugin.PluginLoader;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable; import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;
/** /**
* Represents a Java plugin and its main class. It contains fundamental methods * 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 * and fields for a plugin to be loaded and work properly. This is an indirect
* implementation of {@link org.bukkit.plugin.Plugin}. * implementation of {@link org.bukkit.plugin.Plugin}.
*/ */
@NullMarked
public abstract class JavaPlugin extends PluginBase { public abstract class JavaPlugin extends PluginBase {
private boolean isEnabled = false; private boolean isEnabled = false;
private PluginLoader loader = null; private PluginLoader loader = null;
@@ -61,7 +67,7 @@ public abstract class JavaPlugin extends PluginBase {
} }
@Deprecated(forRemoval = true) @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(); final ClassLoader classLoader = this.getClass().getClassLoader();
if (classLoader instanceof PluginClassLoader) { if (classLoader instanceof PluginClassLoader) {
throw new IllegalStateException("Cannot use initialization constructor at runtime"); throw new IllegalStateException("Cannot use initialization constructor at runtime");
@@ -75,7 +81,6 @@ public abstract class JavaPlugin extends PluginBase {
* *
* @return The folder. * @return The folder.
*/ */
@NotNull
@Override @Override
public final File getDataFolder() { public final File getDataFolder() {
return dataFolder; 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 * @deprecated Plugin loading now occurs at a point which makes it impossible to expose this
* behavior. This instance will only throw unsupported operation exceptions. * behavior. This instance will only throw unsupported operation exceptions.
*/ */
@NotNull
@Override @Override
@Deprecated(forRemoval = true) @Deprecated(forRemoval = true)
public final PluginLoader getPluginLoader() { public final PluginLoader getPluginLoader() {
@@ -100,7 +104,6 @@ public abstract class JavaPlugin extends PluginBase {
* *
* @return Server running this plugin * @return Server running this plugin
*/ */
@NotNull
@Override @Override
public final Server getServer() { public final Server getServer() {
return server; return server;
@@ -121,7 +124,6 @@ public abstract class JavaPlugin extends PluginBase {
* *
* @return File containing this plugin * @return File containing this plugin
*/ */
@NotNull
protected File getFile() { protected File getFile() {
return file; return file;
} }
@@ -132,19 +134,16 @@ public abstract class JavaPlugin extends PluginBase {
* @return Contents of the plugin.yml file * @return Contents of the plugin.yml file
* @deprecated No longer applicable to all types of plugins * @deprecated No longer applicable to all types of plugins
*/ */
@NotNull
@Override @Override
@Deprecated @Deprecated
public final PluginDescriptionFile getDescription() { public final PluginDescriptionFile getDescription() {
return description; return description;
} }
@NotNull
public final io.papermc.paper.plugin.configuration.PluginMeta getPluginMeta() { public final io.papermc.paper.plugin.configuration.PluginMeta getPluginMeta() {
return this.pluginMeta; return this.pluginMeta;
} }
@NotNull
@Override @Override
public FileConfiguration getConfig() { public FileConfiguration getConfig() {
if (newConfig == null) { if (newConfig == null) {
@@ -163,8 +162,7 @@ public abstract class JavaPlugin extends PluginBase {
* @throws IllegalArgumentException if file is null * @throws IllegalArgumentException if file is null
* @see ClassLoader#getResourceAsStream(String) * @see ClassLoader#getResourceAsStream(String)
*/ */
@Nullable protected final @Nullable Reader getTextResource(String file) {
protected final Reader getTextResource(@NotNull String file) {
final InputStream in = getResource(file); final InputStream in = getResource(file);
return in == null ? null : new InputStreamReader(in, Charsets.UTF_8); return in == null ? null : new InputStreamReader(in, Charsets.UTF_8);
@@ -199,7 +197,7 @@ public abstract class JavaPlugin extends PluginBase {
} }
@Override @Override
public void saveResource(@NotNull String resourcePath, boolean replace) { public void saveResource(String resourcePath, boolean replace) {
if (resourcePath == null || resourcePath.equals("")) { if (resourcePath == null || resourcePath.equals("")) {
throw new IllegalArgumentException("ResourcePath cannot be null or empty"); throw new IllegalArgumentException("ResourcePath cannot be null or empty");
} }
@@ -236,9 +234,8 @@ public abstract class JavaPlugin extends PluginBase {
} }
} }
@Nullable
@Override @Override
public InputStream getResource(@NotNull String filename) { public @Nullable InputStream getResource(String filename) {
if (filename == null) { if (filename == null) {
throw new IllegalArgumentException("Filename cannot be null"); throw new IllegalArgumentException("Filename cannot be null");
} }
@@ -263,7 +260,6 @@ public abstract class JavaPlugin extends PluginBase {
* *
* @return ClassLoader holding this plugin * @return ClassLoader holding this plugin
*/ */
@NotNull
protected final ClassLoader getClassLoader() { protected final ClassLoader getClassLoader() {
return classLoader; 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) private static final PluginLoader INSTANCE = net.kyori.adventure.util.Services.service(PluginLoader.class)
.orElseThrow(); .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)); init(server, description, dataFolder, file, classLoader, description, com.destroystokyo.paper.utils.PaperPluginLogger.getLogger(description));
this.pluginMeta = 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.loader = DummyPluginLoaderImplHolder.INSTANCE;
this.server = server; this.server = server;
this.file = file; this.file = file;
@@ -316,7 +312,7 @@ public abstract class JavaPlugin extends PluginBase {
* {@inheritDoc} * {@inheritDoc}
*/ */
@Override @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; return false;
} }
@@ -324,8 +320,7 @@ public abstract class JavaPlugin extends PluginBase {
* {@inheritDoc} * {@inheritDoc}
*/ */
@Override @Override
@Nullable public @Nullable List<String> onTabComplete(CommandSender sender, Command command, String alias, String[] args) {
public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, @NotNull String @NotNull [] args) {
return null; return null;
} }
@@ -337,14 +332,15 @@ public abstract class JavaPlugin extends PluginBase {
* @param name name or alias of the command * @param name name or alias of the command
* @return the plugin command if found, otherwise null * @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()} * @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 @Nullable PluginCommand getCommand(String name) {
public PluginCommand getCommand(@NotNull String name) {
if (this.isBeingEnabled && !(pluginMeta instanceof PluginDescriptionFile)) { if (this.isBeingEnabled && !(pluginMeta instanceof PluginDescriptionFile)) {
throw new UnsupportedOperationException(""" throw new UnsupportedOperationException("""
You are trying to call JavaPlugin#getCommand on a Paper plugin during startup: 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. 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! 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 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 @Override
public void onLoad() {} public void onLoad() {}
@@ -371,15 +449,13 @@ public abstract class JavaPlugin extends PluginBase {
@Override @Override
public void onEnable() {} public void onEnable() {}
@Nullable
@Override @Override
public ChunkGenerator getDefaultWorldGenerator(@NotNull String worldName, @Nullable String id) { public @Nullable ChunkGenerator getDefaultWorldGenerator(String worldName, @Nullable String id) {
return null; return null;
} }
@Nullable
@Override @Override
public BiomeProvider getDefaultBiomeProvider(@NotNull String worldName, @Nullable String id) { public @Nullable BiomeProvider getDefaultBiomeProvider(String worldName, @Nullable String id) {
return null; return null;
} }
@@ -393,13 +469,11 @@ public abstract class JavaPlugin extends PluginBase {
this.naggable = canNag; this.naggable = canNag;
} }
@NotNull
@Override @Override
public Logger getLogger() { public Logger getLogger() {
return logger; return logger;
} }
@NotNull
@Override @Override
public String toString() { public String toString() {
return description.getFullName(); return description.getFullName();
@@ -428,8 +502,7 @@ public abstract class JavaPlugin extends PluginBase {
* @throws ClassCastException if plugin that provided the class does not * @throws ClassCastException if plugin that provided the class does not
* extend the class * extend the class
*/ */
@NotNull public static <T extends JavaPlugin> T getPlugin(Class<T> clazz) {
public static <T extends JavaPlugin> T getPlugin(@NotNull Class<T> clazz) {
Preconditions.checkArgument(clazz != null, "Null class cannot have a plugin"); Preconditions.checkArgument(clazz != null, "Null class cannot have a plugin");
if (!JavaPlugin.class.isAssignableFrom(clazz)) { if (!JavaPlugin.class.isAssignableFrom(clazz)) {
throw new IllegalArgumentException(clazz + " does not extend " + JavaPlugin.class); 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 * @throws IllegalStateException if called from the static initializer for
* given JavaPlugin * given JavaPlugin
*/ */
@NotNull public static JavaPlugin getProvidingPlugin(Class<?> clazz) {
public static JavaPlugin getProvidingPlugin(@NotNull Class<?> clazz) {
Preconditions.checkArgument(clazz != null, "Null class cannot have a plugin"); Preconditions.checkArgument(clazz != null, "Null class cannot have a plugin");
final ClassLoader cl = clazz.getClassLoader(); final ClassLoader cl = clazz.getClassLoader();
if (!(cl instanceof io.papermc.paper.plugin.provider.classloader.ConfiguredPluginClassLoader configuredPluginClassLoader)) { if (!(cl instanceof io.papermc.paper.plugin.provider.classloader.ConfiguredPluginClassLoader configuredPluginClassLoader)) {
@@ -472,7 +544,7 @@ public abstract class JavaPlugin extends PluginBase {
} }
@Override @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; return this.lifecycleEventManager;
} }
} }