Skip to content

Shading the CommandAPI in your plugins

"Shading" is the process of including the CommandAPI inside your plugin, rather than requiring the CommandAPI as an external plugin. In other words, if you shade the CommandAPI into your plugin, you don't need to include the CommandAPI.jar in your server's plugins folder.

Shading vs CommandAPI plugin

The CommandAPI plugin has a few slight differences with the shaded CommandAPI jar file. The CommandAPI plugin has the following extra features that aren’t present in the shaded version:

  • Command conversion via a config.yml file

Shading requirements

For the CommandAPI to function as normal, you must call the CommandAPI's initializers in the onLoad() and onEnable() methods of your plugin:

java
CommandAPI.onLoad(CommandAPIConfig config);
CommandAPI.onEnable();

If you want to handle reloading, the CommandAPI has minimal support for it with the onDisable() method, which can go in your plugin. This is optional and is not required if you don't plan on reloading the server.

Loading

The onLoad(CommandAPIConfig) method initializes the CommandAPI. This must be called before you access any other CommandAPI features. The CommandAPIConfig argument is used to configure how the CommandAPI works, similar to the config.yml. The CommandAPIConfig class follows a builder pattern, with methods for each option in the config.yml, which are listed and described here.

However, the CommandAPIConfig class is abstract and can’t be used to configure the CommandAPI directly. Instead, you must use a subclass of CommandAPIConfig that corresponds to the platform you’re developing for. For example, when developing for a Paper server, you should use the CommandAPIPaperConfig class.

java
public class CommandAPIPaperConfig {
    // Create a new config object
    CommandAPIPaperConfig(PluginMeta pluginMeta, LifecycleEventOwner lifecycleEventOwner);
    
    // General CommandAPI configuration
    CommandAPIPaperConfig verboseOutput(boolean value); // Enables verbose logging
    CommandAPIPaperConfig silentLogs(boolean value);    // Disables ALL logging (except errors)
    CommandAPIPaperConfig dispatcherFile(File file); // If not null, the CommandAPI will create a JSON file with Brigadier's command tree
    CommandAPIPaperConfig setNamespace(String namespace); // The namespace to use when the CommandAPI registers a command
    
    // General CommandAPI configuration for Bukkit-based servers
    CommandAPIPaperConfig fallbackToLatestNMS(boolean fallbackToLatestNMS); // Whether the CommandAPI should fall back to the latest NMS version if no implementation for the current version was found
    CommandAPIPaperConfig missingExecutorImplementationMessage(String value); // Set message to display when executor implementation is missing
    <T> CommandAPIPaperConfig initializeNBTAPI(Class<T> nbtContainerClass, Function<Object, T> nbtContainerConstructor); // Initializes hooks with an NBT API. See NBT arguments documentation page for more info
}

In order to create a CommandAPIPaperConfig object, you must give it a reference to a LifecycleEventOwner instance, meaning either a JavaPlugin or BootstrapContext instance. The CommandAPI always uses this to register commands and events, so it is required when loading the CommandAPI on Paper.

For example, to load the CommandAPI on Paper with all logging disabled, you can use the following:

java
CommandAPI.onLoad(new CommandAPIPaperConfig(plugin).silentLogs(true));

However, the CommandAPIConfig class is abstract and can’t be used to configure the CommandAPI directly. Instead, you must use a subclass of CommandAPIConfig that corresponds to the platform you’re developing for. For example, when developing for a Spigot server, you should use the CommandAPISpigotConfig class.

java
public class CommandAPISpigotConfig {
    // Create a new config object
    CommandAPISpigotConfig(JavaPlugin plugin);
    
    // General CommandAPI configuration
    CommandAPISpigotConfig verboseOutput(boolean value); // Enables verbose logging
    CommandAPISpigotConfig silentLogs(boolean value);    // Disables ALL logging (except errors)
    CommandAPISpigotConfig dispatcherFile(File file); // If not null, the CommandAPI will create a JSON file with Brigadier's command tree
    CommandAPISpigotConfig setNamespace(String namespace); // The namespace to use when the CommandAPI registers a command
    
    // General CommandAPI configuration for Bukkit-based servers
    CommandAPISpigotConfig fallbackToLatestNMS(boolean fallbackToLatestNMS); // Whether the CommandAPI should fall back to the latest NMS version if no implementation for the current version was found
    CommandAPISpigotConfig missingExecutorImplementationMessage(String value); // Set message to display when executor implementation is missing
    <T> CommandAPISpigotConfig initializeNBTAPI(Class<T> nbtContainerClass, Function<Object, T> nbtContainerConstructor); // Initializes hooks with an NBT API. See NBT arguments documentation page for more info
    
    // Spigot-specific configuration
    CommandAPISpigotConfig skipReloadDatapacks(boolean skip); // Whether the CommandAPI should reload datapacks on server load
}

In order to create a CommandAPISpigotConfig object, you must give it a reference to your JavaPlugin instance. The CommandAPI always uses this to register events, so it is required when loading the CommandAPI on Spigot.

For example, to load the CommandAPI on Spigot with all logging disabled, you can use the following:

java
CommandAPI.onLoad(new CommandAPISpigotConfig(plugin).silentLogs(true));

Enabling & Disabling

The onEnable() method initializes the CommandAPI's enabling sequence. Similar to the onLoad(CommandAPIConfig) method, this must be placed in your plugin's onEnable() method. This isn't as strict as the onLoad(CommandAPIConfig) method, and can be placed anywhere in your onEnable() method.

The onDisable() method disables the CommandAPI gracefully. This should be placed in your plugin's onDisable() method. This doesn't unregister commands, so commands may persist during reloads – this can be mitigated using the CommandAPI.unregister() method.

Example – Setting up the CommandAPI in your plugin

Example – Setting up the CommandAPI in your plugin

java
class MyPlugin extends JavaPlugin {
    @Override
    public void onLoad() {
        CommandAPI.onLoad(new CommandAPIPaperConfig(this).verboseOutput(true)); // Load with verbose output

        new CommandAPICommand("ping")
            .executes((sender, args) -> {
                sender.sendMessage("pong!");
            })
            .register();
    }

    @Override
    public void onEnable() {
        CommandAPI.onEnable();
        // Register commands, listeners etc.
    }

    @Override
    public void onDisable() {
        CommandAPI.onDisable();
    }
}
java
class MyPlugin extends JavaPlugin {
    @Override
    public void onLoad() {
        CommandAPI.onLoad(new CommandAPISpigotConfig(this).verboseOutput(true)); // Load with verbose output

        new CommandAPICommand("ping")
            .executes((sender, args) -> {
                sender.sendMessage("pong!");
            })
            .register();
    }

    @Override
    public void onEnable() {
        CommandAPI.onEnable();
        // Register commands, listeners etc.
    }

    @Override
    public void onDisable() {
        CommandAPI.onDisable();
    }
}

A note about relocating

By default, the CommandAPI is written in the dev.jorel.commandapi package. It is highly recommended to "relocate" the shaded copy of the CommandAPI to your own package instead to prevent package clashes with other projects that shade the CommandAPI:

dev.jorel.commandapirelocatemy.custom.package.commandapi

Shading with Build System

To shade the CommandAPI into a maven project, you'll need to use the commandapi-bukkit-shade dependency, which is optimized for shading and doesn't include plugin-specific files (such as plugin.yml). Here you have a choice between the Spigot-mapped version and the Mojang-mapped version. You do not need to use commandapi-bukkit-core if you are shading:

Add the CommandAPI shade dependency:

xml
<dependencies>
    <dependency>
        <groupId>dev.jorel</groupId>
        <artifactId>commandapi-spigot-shade</artifactId>
        <version>11.0.0</version>
    </dependency>
</dependencies>
xml
<dependencies>
    <dependency>
        <groupId>dev.jorel</groupId>
        <artifactId>commandapi-paper-shade</artifactId>
        <version>11.0.0</version>
    </dependency>
</dependencies>

You can shade the CommandAPI easily by adding the maven-shade-plugin to your build sequence:

xml
<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-shade-plugin</artifactId>
            <version>3.6.0</version>
            <executions>
                <execution>
                    <id>shade</id>
                    <phase>package</phase>
                    <goals>
                        <goal>shade</goal>
                    </goals>
                </execution>
            </executions>
            <configuration>
                <minimizeJar>true</minimizeJar>
                <relocations>
                    <relocation>
                        <pattern>dev.jorel.commandapi</pattern>
                        <!-- TODO: Change this to my own package name -->
                        <shadedPattern>my.custom.package.commandapi</shadedPattern>
                    </relocation>
                </relocations>
            </configuration>
        </plugin>
    </plugins>
</build>

To shade the CommandAPI into a Gradle project, we'll use the GradleUp Shadow Plugin. Add this to your list of plugins:

groovy
plugins {
    id 'java'
    id 'com.gradleup.shadow' version '8.3.3'
}
kotlin
plugins {
    java
    id("com.gradleup.shadow") version "8.3.3"
}

Add our repositories:

groovy
repositories {
    mavenCentral()

    // If you want to shade the NBT API as well
    maven { url = "https://repo.codemc.org/repository/maven-public/" }
}
kotlin
repositories {
    mavenCentral()

    // If you want to shade the NBT API as well
    maven(url = "https://repo.codemc.org/repository/maven-public/")
}

Next, we declare our dependencies:

groovy
dependencies {
    implementation "dev.jorel:commandapi-spigot-shade:11.0.0"
}
groovy
dependencies {
    implementation "dev.jorel:commandapi-paper-shade:11.0.0"
}
kotlin
dependencies {
    implementation("dev.jorel:commandapi-spigot-shade:11.0.0")
}
kotlin
dependencies {
    implementation("dev.jorel:commandapi-paper-shade:11.0.0")
}

Then you just need to relocate the CommandAPI to your desired location in the shadowJar task configuration:

groovy
shadowJar {
    // TODO: Change this to my own package name
    relocate("dev.jorel.commandapi", "my.custom.package.commandapi")
}
kotlin
tasks.withType<ShadowJar> {
    // TODO: Change this to my own package name
    relocate("dev.jorel.commandapi", "my.custom.package.commandapi")
}

Finally, we can build the shaded jar using the following command:

bash
gradlew build shadowJar

As we're shading the CommandAPI into your plugin, we don't need to add depend: [CommandAPI] to your plugin.yml file.