Skip to content

External Plugin API

Since v6.2.2

Available in UltiTools-API 6.2.2 and later.

The External Plugin API allows any regular Bukkit/Spigot plugin to use UltiTools framework features without extending UltiToolsPlugin. Your plugin stays a normal JavaPlugin — just call UltiToolsAPI.connect(this) and the framework scans your package for @Service, @CmdExecutor, @EventListener, @Scheduled, and more.

Quick Start

1. Add Dependency

Maven

xml
<dependency>
    <groupId>com.ultikits</groupId>
    <artifactId>UltiTools-API</artifactId>
    <version>6.2.2</version>
    <scope>provided</scope>
</dependency>

2. Declare Dependency in plugin.yml

yaml
name: MyExternalPlugin
version: '1.0.0'
main: com.example.myplugin.MyExternalPlugin
depend: [UltiTools]

WARNING

depend: [UltiTools] is required. UltiTools must be loaded before your plugin so the framework is ready when connect() is called.

3. Connect and Disconnect

java
package com.example.myplugin;

import org.bukkit.plugin.java.JavaPlugin;
import com.ultikits.ultitools.api.UltiToolsAPI;

public class MyExternalPlugin extends JavaPlugin {

    @Override
    public void onEnable() {
        UltiToolsAPI.connect(this);
        // Your plugin is now wired into UltiTools!
    }

    @Override
    public void onDisable() {
        UltiToolsAPI.disconnect(this);
    }
}

That's it. The framework automatically scans com.example.myplugin (derived from your main class) for annotated classes.

What Gets Scanned

When connect() is called, the framework scans your plugin's base package and registers:

AnnotationWhat it does
@ServiceRegistered as a bean in the IoC container
@CmdExecutorCommand class auto-registered with Bukkit
@EventListenerListener auto-registered with Bukkit
@ScheduledMethods scheduled as Bukkit tasks
@PlayerCacheFields tracked per-player with auto save/load
@ModuleEventHandlerSubscribed to the UltiTools EventBus

Writing Services

Services work exactly like in UltiTools modules — use @Service and @Autowired:

java
@Service
public class GreetingService {

    @Autowired
    private StatsService statsService; // Injected by the IoC container

    public void greetPlayer(Player player) {
        player.sendMessage("Welcome! You have " + statsService.getVisits(player) + " visits.");
    }
}

Writing Commands

Use @CmdExecutor and @CmdMapping just like a UltiTools module:

java
@CmdExecutor(alias = {"greet"}, permission = "myplugin.greet", description = "Greet command")
@CmdTarget(CmdTarget.CmdTargetType.PLAYER)
public class GreetCommand extends AbstractCommandExecutor {

    @Autowired
    private GreetingService greetingService;

    @CmdMapping(format = "")
    public void greet(@CmdSender Player player) {
        greetingService.greetPlayer(player);
    }
}

TIP

Command descriptions use the raw description attribute from @CmdExecutor. The i18n() method is not available for external plugins — use plain strings.

Writing Event Listeners

java
@EventListener
public class JoinListener implements Listener {

    @Autowired
    private GreetingService greetingService;

    @EventHandler
    public void onPlayerJoin(PlayerJoinEvent event) {
        greetingService.greetPlayer(event.getPlayer());
    }
}

Data Storage

Use UltiToolsAPI.getDataOperator() to get a DataOperator for your data entities. Data is stored in your plugin's own data folder, not UltiTools' folder.

java
@Data @Builder @NoArgsConstructor @AllArgsConstructor
@EqualsAndHashCode(callSuper = true)
@Table("player_stats")
public class StatsEntity extends BaseDataEntity<String> {
    @Column("player_id") private String playerId;
    @Column("visits") private int visits;
}
java
@Service
public class StatsService {

    private final JavaPlugin plugin;
    private final DataOperator<StatsEntity> dataOp;

    public StatsService(JavaPlugin plugin) {
        this.plugin = plugin;
        this.dataOp = UltiToolsAPI.getDataOperator(plugin, StatsEntity.class);
    }

    public int getVisits(Player player) {
        StatsEntity stats = dataOp.query()
            .where("playerId").eq(player.getUniqueId().toString())
            .first();
        return stats != null ? stats.getVisits() : 0;
    }
}

TIP

The storage backend (JSON, SQLite, or MySQL) is determined by the UltiTools server configuration, not your plugin. Your code works the same regardless.

EventBus

Use UltiToolsAPI.getEventBus() for inter-plugin communication:

java
// Publish an event
UltiToolsAPI.getEventBus().publish(new MyCustomEvent(data));

// Subscribe via annotation in a @Service class
@ModuleEventHandler
public void onCustomEvent(MyCustomEvent event) {
    // Handle event from any module or external plugin
}

API Reference

MethodDescription
UltiToolsAPI.connect(JavaPlugin)Connect plugin to UltiTools framework
UltiToolsAPI.disconnect(JavaPlugin)Disconnect plugin from framework
UltiToolsAPI.isConnected(JavaPlugin)Check if a plugin is connected
UltiToolsAPI.getDataOperator(JavaPlugin, Class)Get a DataOperator scoped to the plugin's data folder
UltiToolsAPI.getEventBus()Get the shared EventBus instance

Lifecycle

  • Auto-disconnect: If your plugin is disabled (server stop, /reload), UltiTools automatically disconnects it via a PluginDisableEvent listener. You don't strictly need disconnect() in onDisable(), but it's good practice.
  • UltiTools shutdown: When UltiTools itself shuts down, all external plugins are disconnected automatically via disconnectAll().
  • Cleanup: On disconnect, all commands, listeners, scheduled tasks, EventBus subscriptions, and PlayerCache beans registered by your plugin are removed.

Differences from UltiTools Modules

FeatureUltiTools ModuleExternal Plugin
Base classextends UltiToolsPluginextends JavaPlugin
Registration@UltiToolsModule + registerSelf()UltiToolsAPI.connect(this)
i18nplugin.i18n("key") availableNot available — use plain strings
Config entitiesFull support (@ConfigEntity)Not available — use Bukkit getConfig()
Data storageplugin.getDataOperator(Class)UltiToolsAPI.getDataOperator(plugin, Class)
Hot reloadSupported via ul reloadNot supported — requires server restart
plugin.ymlapi-version: 620depend: [UltiTools]

Complete Working Example

Check out the UltiTools-External-Example repository for a fully working example that demonstrates @Service, @CmdExecutor, @EventListener, @Autowired, and DataOperator CRUD in a standard Bukkit plugin.

Contributors

The avatar of contributor named as Ling Bao Ling Bao
The avatar of contributor named as Claude Opus 4.6 Claude Opus 4.6

Changelog

Released under the MIT License.