Skip to content

Command unregistration

The CommandAPI allows you to remove commands completely from Minecraft's command list. This includes Vanilla commands, Bukkit commands, and plugin commands.

There are three methods you might use when unregistering commands:

java
CommandAPI.unregister(String commandName);
CommandAPI.unregister(String commandName, boolean unregisterNamespaces);
CommandAPIBukkit.unregister(String commandName, boolean unregisterNamespaces, boolean unregisterBukkit);

To understand when and how to use these methods, you need to know a little about how Paper loads and sets up commands. This is basically the order of events when a Paper server starts:

  1. Plugins are initialized
    • PluginBootstrap#boostrap(BootstrapContext) is called
  2. Vanilla commands are registered
  3. Lifecycle events for the bootstrap stage are executed
  4. Bukkit commands are placed in the Bukkit CommandMap
    • Given the bukkit namespace
  5. Plugins are loaded
    • onLoad() is called
  6. Plugins are enabled
    • If a plugin uses it, then
      • Plugin commands are read from the plugin.yml file and placed in the Bukkit CommandMap
      • Given the plugin's name as their namespace (e.g. luckperms:lp)
    • onEnable is called
    • Repeat for each plugin
  7. Bukkit's help command is registered
  8. Lifecycle events for the plugin stage are executed
  9. The server is done loading

To understand when and how to use these methods, you need to know a little about how Spigot loads and sets up commands. This is basically the order of events when a Spigot server starts:

  1. Vanilla commands are placed in the Vanilla CommandDispatcher
  2. Bukkit commands are placed in the Bukkit CommandMap
    • Given the bukkit namespace (e.g. bukkit:version)
  3. Plugins are loaded
    • onLoad is called
  4. Plugins are enabled
    • Plugin commands are read from the plugin.yml file and placed in the Bukkit CommandMap
    • Given the plugin's name as their namespace (e.g. luckperms:lp)
    • onEnable is called
    • Repeat for each plugin
  5. Bukkit's help command is registered
  6. Vanilla commands are added to the Bukkit CommandMap
    • Given the minecraft namespace (e.g. minecraft:gamemode)
  7. The server is done loading

Unregistering a command only works if it happens after the command is created. Bukkit's command system is special and has two locations where commands can exist -- either the Vanilla CommandDispatcher or the Bukkit CommandMap -- so you also need to know where your command is registered. With that in mind, here is what each of the unregister methods do:

java
CommandAPI.unregister(String commandName);

Unregisters a command from the Vanilla CommandDispatcher.

java
CommandAPI.unregister(String commandName, boolean unregisterNamespaces);

Unregisters a command from the Vanilla CommandDispatcher. If unregisterNamespaces is true, then any namespaced version of the command is also unregistered.

java
CommandAPIBukkit.unregister(String commandName, boolean unregisterNamespaces, boolean unregisterBukkit);

Unregisters a command from Bukkit. As before, if unregisterNamespaces is true, then any namespaced version of the command is also unregistered. If unregisterBukkit is true, then only Bukkit commands in the Bukkit CommandMap are unregistered. If unregisterBukkit is false, only commands from the Vanilla CommandDispatcher are unregistered.

To give a better idea of how and when to use these methods, the rest of this page documents how to unregister different types of commands.

Developer's Note:

But first, let's take a look at where the CommandAPI registers commands. While you are able to call the register() method basically anywhere (as mentioned on the registration page), CommandAPI commands are only actually registered in two places: step 3 and step 8 of the loading sequence above.

Step 3 applies if you register commands in a bootstrap context which may look something like this:

java
public class MyPluginBootstrap implements PluginBootstrap {

    @Override
    public void bootstrap(@NotNull BootstrapContext context) {
        CommandAPI.onLoad(new CommandAPIPaperConfig(context));

        new CommandAPICommand("inbootstrap")
            .executes((sender, args) -> {
                sender.sendMessage(Component.text("This command is registed in step 3!"));
            })
            .register();
    }
}

Note that the CommandAPI#onLoad(CommandAPIConfig) call is now in the bootstrap method.

Step 8 applies if you register commands in the JavaPlugin#onLoad() or the JavaPlugin#onEnable() methods.

Similarly, unregistrations are also handled in a lifecycle event which runs after the events that register commands.

Unregistering a Bukkit command - /version

Developer's Note:

Paper has recently converted some commands that were Bukkit commands before to Brigadier commands. This includes the /version command. For demonstration's sake we'll assume it is still a Bukkit command (which still does apply to some versions).

/version is a command provided by Bukkit. Looking at the sequence of events above, that means it is created during step 4, before plugins are loaded in step 5. Consequently, the command will exist when the unregistration event is ran after step 8. Here we'll unregister the command in onLoad, but the same would work in onEnable too.

/version is a command provided by Bukkit. Looking at the sequence of events above, that means it is created during step 2, before plugins are loaded in step 3. Consequently, the command will exist when our plugin's onLoad method is called, so we'll unregister it there. The same code will work in onEnable too, since step 4 is also after step 2.

Since this command exists in the Bukkit CommandMap, we'll need to use CommandAPIBukkit#unregister with unregisterBukkit set to true. We'll also remove the namespaced version -- /bukkit:version -- so unregisterNamespaces will be true. All together, the code looks like this:

java
@Override
public void onLoad() {
    CommandAPIBukkit.unregister("version", true, true);
}

With this plugin, executing /version or /bukkit:version will give the unknown command message. Note that aliases like /ver and its namespaced version /bukkit:ver will still work. To remove aliases as well, you need to unregister each as its own command. For, /ver, that would mean calling CommandAPIBukkit.unregister("ver", true, true).

Unregistering a Vanilla command - /gamemode

/gamemode is a command provided by Vanilla Minecraft. Like the previous example, Vanilla commands are created in step 2, before lifecycle events for the bootstrap stage are ran and plugins are loaded in step 5. For variety, we'll unregister the command in our plugin's onEnable -- step 6 -- but the same code would also work in onLoad.

/gamemode is a command provided by Vanilla Minecraft. Like the previous example, Vanilla commands are created in step 1, before plugins are loaded in step 3. For variety, we'll unregister the command in our plugin's onEnable -- step 4 -- but the same code would also work in onLoad.

Since this command exists in the Vanilla CommandDispatcher, we can use CommandAPI#unregister. That works the same as CommandAPIBukkit#unregister with unregisterBukkit set to false. We don't care about the namespace, so unregisterNamespaces will be false. That means we can use the simplest method, CommandAPI.unregister(String commandName), since it sets unregisterNamespaces to false by default. All together, the code looks like this:

java
@Override
public void onEnable() {
    CommandAPI.unregister("gamemode");
}

With this code, executing /gamemode will give the unknown command exception as expected. However, even though unregisterNamespaces was false, /minecraft:gamemode can also not be run. This happens because Vanilla commands are given their namespace in step 6, after our plugin has removed /gamemode.

When the server starts, /gamemode is created in step 2 inside the Vanilla CommandDispatcher. In step 4, our plugin is enabled and we remove the /gamemode command from that CommandDispatcher. After all the plugins enable, step 6 moves all commands in the Vanilla CommandDispatcher to the Bukkit CommandMap and gives them the minecraft namespace. Since /gamemode doesn't exist at this point, step 6 cannot create the /minecraft:gamemode command. So, even though unregisterNamespaces was false, /minecraft:gamemode doesn't exist anyway.

Example - Replacing Minecraft's `/gamemode` command

Example - Replacing Minecraft's /gamemode command

To replace a command, simply register a new implementation for that command. Any attempts to unregister first will result in the new implementation to also not be present.

java
@Override
public void onEnable() {
    // Register our new /gamemode, with survival, creative, adventure and spectator
    new CommandAPICommand("gamemode")
        .withArguments(new MultiLiteralArgument("gamemodes", "survival", "creative", "adventure", "spectator"))
        .executes((sender, args) -> {
            // Implementation of our /gamemode command
        })
        .register();
}

To replace a command, first unregister the original command, then register a new implementation for that command.

java
@Override
public void onEnable() {
    CommandAPI.unregister("gamemode");

    // Register our new /gamemode, with survival, creative, adventure and spectator
    new CommandAPICommand("gamemode")
        .withArguments(new MultiLiteralArgument("gamemodes", "survival", "creative", "adventure", "spectator"))
        .executes((sender, args) -> {
            // Implementation of our /gamemode command
        })
        .register();
}

Now, when /gamemode is executed, it will use the new implementation defined using the CommandAPI.

Unregistering a Plugin command - /luckperms:luckperms

The /luckperms command is provided by the Bukkit LuckPerms plugin. Plugin commands are created during step 6, immediately before calling the onEnable method of the respective plugin. Since unregistrations run after step 8, it does not matter where we unregister, however here we choose to do it in the onEnable. We also have to make sure that our plugin is loaded after LuckPerms. The best way to make sure that happens is to add LuckPerms as a depend or softdepend in our plugin's plugin.yml. You can read more about the different between depend and softdepend in Spigot's documentation, but that will look something like this:

The /luckperms command is provided by the Bukkit LuckPerms plugin. Plugin commands are created during step 4, immediately before calling the onEnable method of the respective plugin. In this case, unregistering the command in our own plugin's onLoad would not work, since the command wouldn't exist yet. We also have to make sure that our onEnable method is called after LuckPerm's. The best way to make sure that happens is to add LuckPerms as a depend or softdepend in our plugin's plugin.yml. You can read more about the different between depend and softdepend in Spigot's documentation, but that will look something like this:

yaml
name: MyPlugin
main: some.package.name.Main
version: 1.0
depend:
  - LuckPerms

Since plugin commands are stored in the Bukkit CommandMap, we need to use CommandAPIBukkit#unregister with unregisterBukkit set to true. For demonstration’s sake, we only want to unregister the namespaced version -- /luckperms:luckperms -- and leave /luckperms alone. To do this, give "luckperms:luckperms" as the commandName, and set unregisterNamespaces to false. All together, the code looks like this:

java
@Override
public void onEnable() {
    CommandAPIBukkit.unregister("luckperms:luckperms", false, true);
}

Executing /luckperms will work as normal, but /luckperms:luckperms will give the unknown command message.

Unregistering a CommandAPI command

Unregistering a command created by the CommandAPI is easy.

For our example, let's say we want to unregister the /break command created by the Bukkit Maven Example Project for the CommandAPI.

Unregistering a command created by the CommandAPI is similar to both unregistering a Vanilla command and a plugin command. Like a Vanilla command, CommandAPI commands are stored in the Vanilla CommandDispatcher, so they should be unregistered with unregisterBukkit set to false. Like plugin commands, they may be created in onEnable, so you need to make sure your plugin is enabled after the plugin that adds the command.

Unlike plugin commands, CommandAPI commands may be created in onLoad, as discussed in Command loading order. That just means you may also be able to unregister the command in you own plugin's onLoad. As always, simply make sure you unregister a command after it is created, and it will be removed properly.

For our example, let's say we want to unregister the /break command created by the Bukkit Maven Example Project for the CommandAPI. If you look at that plugin's code, you can see that it registers the /break command in it's onEnable method. Therefore, we can unregister the command in our own plugin's onEnable, making sure that our plugin will enable second by adding ExamplePlugin as a depend or softdepend.

yaml
name: MyPlugin
main: some.package.name.Main
version: 1.0
depend:
  - CommandAPI
  - ExamplePlugin

Developer's Note:

If you can't find the code where a CommandAPI command is registered or just don't have access to the code of a plugin, you can still figure out when a command is registered. If you set verbose-outputs to true in the CommandAPI's configuration, it will log command registration.

For the ExamplePlugin, setting verbose-outputs to true gives this:

log
[Server thread/INFO]: [ExamplePlugin] Enabling ExamplePlugin v0.0.1
[Server thread/INFO]: [CommandAPI] Registering command /break block<LocationArgument>
[Server thread/INFO]: [CommandAPI] Registering command /myeffect target<EntitySelectorArgument.OnePlayer> potion<PotionEffectArgument>
[Server thread/INFO]: [CommandAPI] Registering command /nbt nbt<NBTCompoundArgument>

You can see that the ExamplePlugin registers its commands when onEnable is called.

In summary, we will unregister the /break command in our plugin's onEnable. We added Example plugin to the depend list in our plugin.yml so that our onEnable method runs second. unregisterNamespaces and unregisterBukkit will be set to false, and those are the default values, so we can simply use CommandAPI.unregister(String commandName). All together, the code looks like this:

java
@Override
public void onEnable() {
    CommandAPI.unregister("break");
}

Now, when you try to execute /break, you will just get the unknown command message as if it never existed.

Special case: Unregistering Bukkit's /help

If you look at the sequence of events at the top of this page, you might notice that Bukkit's /help command gets its own place in step 5. Unlike the other Bukkit commands, /help is special and gets registered after plugins are loaded and enabled (don't ask, I don't know why 😛). That means unregistering /help in onLoad or onEnable does not work, since the command doesn't exist yet.

In order to run our unregister task after the server is enabled, we can use Bukkit's Scheduler API. There are many ways to set up and run a task, and this should work in whatever way you like. You can even give the task zero delay, since Bukkit only starts processing tasks after the server is enabled.

Since /help is in the Bukkit CommandMap, we need to use CommandAPIBukkit#unregister with unregisterBukkit set to true. We'll leave /bukkit:help alone, so unregisterNamespaces will be false. All together, we can unregister Bukkit's /help command with this code:

java
@Override
public void onEnable() {
    new BukkitRunnable() {
        @Override
        public void run() {
            CommandAPIBukkit.unregister("help", false, true);
        }
    }.runTaskLater(this, 0);
}

Funnily, if you try to execute /help, the server will still tell you: Unknown command. Type "/help" for help.. Luckily, unregisterNamespaces was false, so you can still use /bukkit:help to figure out your problem.

Special case: Unregistering only /minecraft:gamemode

In the earlier example for Unregistering /gamemode, even though unregisterNamespaces was false, the /minecraft:gamemode command was also not executable. As explained up there, this happens because the namespaced version of commands in the Vanilla CommandDispatcher are not created until after plugins are loaded and enabled. Since we unregistered /gamemode in onEnable, when the time came for the server to transfer Vanilla commands into the Bukkit CommandMap, it didn't know to create the minecraft:gamemode command. Consequently, this means we cannot normally remove only the /minecraft:gamemode command without also unregistering /gamemode.

Of course, it is still possible to only unregister /minecraft:gamemode and the namespaced versions of other Vanilla commands. As always, in order to unregister a command, you have to unregister after the command is created. So, we just need to unregister /minecraft:gamemode after the server is enabled. Like the previous special case, we can use Bukkit's Scheduler API to run our unregister task after the server is enabled.

While /minecraft:gamemode only exists in the Bukkit CommandMap, it is the namespaced version of the Vanilla /gamemode command, so it is considered a Vanilla command. That means unregisterBukkit should be false, which is what it defaults to when using CommandAPI#unregister. The CommandAPI understands that once the server is enabled Vanilla commands will have been copied to the CommandMap, so it will be able to find /minecraft:gamemode

Finally, unregisterNamespaces should be false, and since that's the default value we don't have to include it. All together, the code looks like this:

java
@Override
public void onEnable() {
    new BukkitRunnable() {
        @Override
        public void run() {
            CommandAPI.unregister("minecraft:gamemode");
        }
    }.runTaskLater(this, 0);
}

With this code, /gamemode will execute as normal, but /minecraft:gamemode will give the unknown command message.

Developer's Note:

Doing the opposite action here -- only unregistering /gamemode but keeping /minecraft:gamemode -- is not recommended. That would be the following code, where commandName is "gamemode" (or any command in the Vanilla CommandDispatcher), and unregisterNamespaces is false:

java
// NOT RECOMMENDED
@Override
public void onEnable() {
    new BukkitRunnable() {
        @Override
        public void run() {
            CommandAPI.unregister("gamemode");
        }
    }.runTaskLater(this, 0);
}

The expected outcome of this code is that /minecraft:gamemode would work as expected, and /gamemode would give the command not found message. However, that is only true for the player's commands. If you try to use /minecraft:gamemode in the console, it will not work properly. Specifically, while you can tab-complete the command's label, minecraft:gamemode the command's arguments will not have any suggestions. If you try to execute /minecraft:gamemode in the console, it will always tell you your command is unknown or incomplete.

The main point is that if you ever try to unregister a Vanilla command after the server is enabled, the namespaced version of that command will break for the console. To avoid this issue, always set unregisterNamespaces to true if unregisterBukkit is false when unregistering commands after the server is enabled.

java
@Override
public void onEnable() {
    new BukkitRunnable() {
        @Override
        public void run() {
            CommandAPI.unregister("gamemode", true);
        }
    }.runTaskLater(this, 0);
}