Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add simpler javaplugin command registration #12142

Merged
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
140 changes: 106 additions & 34 deletions paper-api/src/main/java/org/bukkit/plugin/java/JavaPlugin.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

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;
Expand All @@ -11,6 +13,8 @@
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;
Expand All @@ -26,14 +30,16 @@
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;
Expand Down Expand Up @@ -61,7 +67,7 @@ public JavaPlugin() {
}

@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");
Expand All @@ -75,7 +81,6 @@ protected JavaPlugin(@NotNull final JavaPluginLoader loader, @NotNull final Plug
*
* @return The folder.
*/
@NotNull
@Override
public final File getDataFolder() {
return dataFolder;
Expand All @@ -88,7 +93,6 @@ public final File getDataFolder() {
* @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() {
Expand All @@ -100,7 +104,6 @@ public final PluginLoader getPluginLoader() {
*
* @return Server running this plugin
*/
@NotNull
@Override
public final Server getServer() {
return server;
Expand All @@ -121,7 +124,6 @@ public final boolean isEnabled() {
*
* @return File containing this plugin
*/
@NotNull
protected File getFile() {
return file;
}
Expand All @@ -132,19 +134,16 @@ protected File getFile() {
* @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) {
Expand All @@ -163,8 +162,7 @@ public FileConfiguration getConfig() {
* @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);
Expand Down Expand Up @@ -199,7 +197,7 @@ public void saveDefaultConfig() {
}

@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");
}
Expand Down Expand Up @@ -236,9 +234,8 @@ public void saveResource(@NotNull String resourcePath, boolean replace) {
}
}

@Nullable
@Override
public InputStream getResource(@NotNull String filename) {
public @Nullable InputStream getResource(String filename) {
if (filename == null) {
throw new IllegalArgumentException("Filename cannot be null");
}
Expand All @@ -263,7 +260,6 @@ public InputStream getResource(@NotNull String filename) {
*
* @return ClassLoader holding this plugin
*/
@NotNull
protected final ClassLoader getClassLoader() {
return classLoader;
}
Expand Down Expand Up @@ -296,11 +292,11 @@ private static class DummyPluginLoaderImplHolder {
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;
Expand All @@ -316,16 +312,15 @@ public final void init(@NotNull Server server, @NotNull PluginDescriptionFile de
* {@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;
}

/**
* {@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;
}

Expand All @@ -337,14 +332,15 @@ public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Comman
* @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
""");
}
Expand All @@ -362,6 +358,88 @@ public PluginCommand getCommand(@NotNull String name) {
}
}

/**
* 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() {}

Expand All @@ -371,15 +449,13 @@ public void onDisable() {}
@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;
}

Expand All @@ -393,13 +469,11 @@ public final void setNaggable(boolean canNag) {
this.naggable = canNag;
}

@NotNull
@Override
public Logger getLogger() {
return logger;
}

@NotNull
@Override
public String toString() {
return description.getFullName();
Expand Down Expand Up @@ -428,8 +502,7 @@ public String toString() {
* @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);
Expand Down Expand Up @@ -457,8 +530,7 @@ public static <T extends JavaPlugin> T getPlugin(@NotNull Class<T> clazz) {
* @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)) {
Expand All @@ -472,7 +544,7 @@ public static JavaPlugin getProvidingPlugin(@NotNull Class<?> clazz) {
}

@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;
}
}
Loading