Skip to content

UltiKits/UltiTools-Reborn

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

469 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation



UltiTools-API

The Modern Paper Plugin Framework

Build Minecraft plugins the way you'd build a Spring Boot app — annotations, dependency injection, ORM, and scheduled tasks. Works with any Bukkit/Paper plugin. Java 21+, Minecraft 1.21+.


GitHub License GitHub release (latest by date) Maven Central GitHub Repo stars Minecraft Version Java Version Spigot Rating bStats Players bStats Servers CodeFactor

English | 中文文档

discord Join our Discord! 加入官方 QQ 群! discord

Why UltiTools?

Writing Minecraft plugins often means pages of boilerplate — registering commands manually, wiring up listeners, building raw SQL, scheduling BukkitRunnables everywhere. UltiTools-API eliminates all of that.

What you get:

Feature Traditional Bukkit With UltiTools-API
Commands onCommand() switch/case @CmdMapping(format = "pay <player> <amount>")
Listeners Manual registration in onEnable() @EventListener auto-discovered
Dependency injection Static singletons everywhere @Autowired like Spring
Data storage Raw JDBC or custom file I/O @Table + @Column ORM, works with MySQL/SQLite/JSON
Config getConfig().getString(...) @ConfigEntry on typed fields with validation
Scheduled tasks new BukkitRunnable() boilerplate @Scheduled(period = 6000) on any method
Feature toggles Manual if checks @ConditionalOnConfig skips entire components
Transactions Manual try/catch/rollback @Transactional AOP proxy
Inter-module events Custom event buses or static coupling @ModuleEventHandler pub/sub EventBus

Getting Started

Requirements

  • Java: 21 or higher
  • Minecraft: 1.21+ (Paper)
  • UltiTools Plugin: Install on your server

Add the Dependency

Maven

<dependency>
    <groupId>com.ultikits</groupId>
    <artifactId>UltiTools-API</artifactId>
    <version>6.2.3</version>
</dependency>

Gradle

implementation 'com.ultikits:UltiTools-API:6.2.3'

Option A: Write an UltiTools Module (Recommended)

Building an UltiTools module is the best way to develop with UltiTools-API. Modules are lightweight plugins managed by the UltiTools framework, giving you everything you'd get from a standalone plugin plus:

  • One-click updates — Server admins can update your module directly from the UltiTools panel, no manual JAR swapping
  • Built-in marketplace — Publish to UltiCloud and let users discover, install, and manage your module from a central catalog
  • Hot reload — Modules can be loaded and unloaded at runtime without restarting the server
  • Shared infrastructure — Database connections, config management, and i18n are handled by the framework; your module stays small and focused
  • Inter-module communication — Publish and subscribe to events across modules with the built-in EventBus

Quickest start — scaffold with the CLI:

npm install -g @ultikits/cli
ultikits create
# Answer the prompts, then:
cd MyModule && mvn compile

This generates a complete, compilable project with pom.xml, main class, plugin.yml, language files, and tests directory. See the CLI documentation for details.

Or set up manually — create plugin.yml:

name: MyPlugin
version: '${project.version}'
main: com.example.myplugin.MyPlugin
api-version: 620
authors: [ YourName ]

Write your main class:

@UltiToolsModule(scanBasePackages = {"com.example.myplugin"})
public class MyPlugin extends UltiToolsPlugin {
    @Override
    public boolean registerSelf() {
        return true;  // Commands, listeners, services are all auto-registered!
    }

    @Override
    public void unregisterSelf() { }
}

Add @CmdExecutor on command classes, @EventListener on listener classes, and @Service on services. The framework discovers and wires everything automatically.

For detailed guides, see the Developer Documentation or Project Wiki.

Option B: Use from Any Bukkit Plugin

Already have a plugin? You can integrate UltiTools-API into any existing Bukkit/Paper plugin without rewriting it as a module. You get the full framework — DI, ORM, scheduled tasks, EventBus — with a single line of code.

Add depend: [UltiTools] to your plugin.yml and call UltiToolsAPI.connect(this):

public class MyBukkitPlugin extends JavaPlugin {

    @Override
    public void onEnable() {
        // One line — UltiTools scans your plugin for @Service, @CmdExecutor, @EventListener, etc.
        UltiToolsAPI.connect(this);
    }

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

After connect(), all annotated classes in your plugin's package are auto-discovered and managed by the UltiTools IoC container — everything a native module gets, while keeping your plugin as a standard Bukkit plugin.

See the External Plugin API Guide for full details, or check out the complete working example.


Quick Look

A complete UltiTools module in under 50 lines:

// Main class — just one annotation to enable auto-scanning
@UltiToolsModule(scanBasePackages = {"com.example.myplugin"})
public class MyPlugin extends UltiToolsPlugin {
    @Override
    public boolean registerSelf() { return true; }

    @Override
    public void unregisterSelf() { }
}
// Define your data — works with MySQL, SQLite, and JSON automatically
@Data @Builder @NoArgsConstructor @AllArgsConstructor
@EqualsAndHashCode(callSuper = true)
@Table("homes")
public class HomeEntity extends BaseDataEntity<String> {
    @Column("player_id") private String playerId;
    @Column("name")      private String name;
    @Column("world")     private String world;
    @Column("x")         private double x;
    @Column("y")         private double y;
    @Column("z")         private double z;
}
// Write commands like controllers — auto-registered, auto-completed
@CmdTarget(CmdTarget.CmdTargetType.PLAYER)
@CmdExecutor(alias = {"home"}, permission = "myplugin.home")
public class HomeCommand extends AbstractCommandExecutor {

    @Autowired
    private HomeService homeService;

    @CmdMapping(format = "tp <name>")
    public void teleport(@CmdSender Player player, @CmdParam("name") String name) {
        homeService.teleport(player, name);
    }

    @CmdMapping(format = "set <name>")
    public void setHome(@CmdSender Player player, @CmdParam("name") String name) {
        homeService.setHome(player, name, player.getLocation());
        player.sendMessage("Home set!");
    }

    @CmdMapping(format = "list")
    public void listHomes(@CmdSender Player player) {
        homeService.getHomes(player).forEach(h ->
            player.sendMessage(" - " + h.getName())
        );
    }
}
// Service with DI, query DSL, and scheduled tasks
@Service
public class HomeService {

    @Autowired
    private UltiToolsPlugin plugin;

    public void teleport(Player player, String name) {
        HomeEntity home = plugin.getDataOperator(HomeEntity.class)
            .query()
            .where("playerId").eq(player.getUniqueId().toString())
            .and("name").eq(name)
            .first();

        if (home != null) {
            Location loc = new Location(
                Bukkit.getWorld(home.getWorld()),
                home.getX(), home.getY(), home.getZ()
            );
            player.teleport(loc);
        }
    }

    public List<HomeEntity> getHomes(Player player) {
        return plugin.getDataOperator(HomeEntity.class)
            .query()
            .where("playerId").eq(player.getUniqueId().toString())
            .orderBy("name")
            .list();
    }

    @Scheduled(period = 72000, async = true) // Cleanup every hour
    public void cleanupExpiredHomes() {
        // Automatically called by the framework — no BukkitRunnable needed
    }
}

That's it. No onCommand() switches, no manual listener registration, no new BukkitRunnable() boilerplate, no raw SQL.


Features

Annotation-Driven Commands

Map subcommands directly to methods. Parameters are parsed and type-converted automatically.

@CmdMapping(format = "pay <player> <amount>")
public void pay(@CmdSender Player sender, @CmdParam("player") String target, @CmdParam("amount") double amount) {
    // 'amount' is already a double — no manual parsing
}

IoC Container with @Autowired

Spring-like dependency injection with three-level cache for circular dependency resolution.

@Service
public class EconomyService {
    @Autowired
    private TransactionLogger logger;  // Auto-injected

    @Transactional
    public void transfer(UUID from, UUID to, double amount) {
        // Automatic rollback on exception
    }
}

Fluent Query DSL

Chain conditions, ordering, and pagination — works across MySQL, SQLite, and JSON storage.

List<AccountEntity> richPlayers = plugin.getDataOperator(AccountEntity.class)
    .query()
    .where("balance").gte(10000)
    .orderByDesc("balance")
    .limit(10)
    .list();

boolean exists = plugin.getDataOperator(AccountEntity.class)
    .query()
    .where("owner").eq(uuid)
    .and("name").eq("savings")
    .exists();

@Scheduled Tasks

Declare scheduled methods — the framework handles BukkitRunnable creation, registration, and cleanup.

@Service
public class InterestService {
    @Scheduled(delay = 100, period = 36000, async = true) // 30min cycle, async
    public void distributeInterest() {
        // Called automatically. Cancelled when plugin unloads.
    }

    @Scheduled(delay = 20) // One-shot: runs once after 1 second
    public void onStartup() {
        // Initialization logic
    }
}

Config Validation

Invalid config values are automatically caught and reset to safe defaults on load and reload.

@Getter @Setter
@ConfigEntity("config/config.yml")
public class EcoConfig extends AbstractConfigEntity {

    @ConfigEntry(path = "interestRate", comment = "Interest rate per cycle")
    @Range(min = 0.0, max = 1.0)
    private double interestRate = 0.0003;

    @ConfigEntry(path = "currency_name", comment = "Currency display name")
    @NotEmpty
    private String currencyName = "Gold Coin";

    @ConfigEntry(path = "motd", comment = "Server message of the day")
    @Size(min = 1, max = 256)
    private String motd = "Welcome!";
}

@ConditionalOnConfig

Toggle entire features on/off from config — no if checks scattered through code.

@Service
@ConditionalOnConfig(value = "config/config.yml", path = "enableInterest")
public class InterestService {
    // This bean is never created if enableInterest: false
}

@CmdExecutor(alias = {"warp"}, permission = "myplugin.warp")
@ConditionalOnConfig(value = "config/config.yml", path = "enableWarp")
public class WarpCommands extends AbstractCommandExecutor {
    // Command doesn't exist if enableWarp: false
}

AOP Proxies

@Transactional and @ExceptionCatch via CGLIB runtime proxies — no boilerplate try/catch.

@Service
public class BankService {
    @Transactional(propagation = Propagation.REQUIRED)
    public void transfer(UUID from, UUID to, double amount) {
        withdraw(from, amount);
        deposit(to, amount); // Rolls back both if this throws
    }

    @ExceptionCatch(value = IOException.class, silent = true)
    public String readExternalData() {
        // Returns null on IOException instead of crashing
    }
}

Module EventBus

Decoupled pub/sub communication between modules — no direct dependencies needed.

// Define a custom event
public class BalanceChangeEvent extends ModuleEvent {
    @Getter private final UUID player;
    @Getter private final double amount;
    public BalanceChangeEvent(UUID player, double amount) {
        this.player = player;
        this.amount = amount;
    }
}

// Subscribe via annotation — auto-discovered by the framework
@Service
public class AuditService {
    @ModuleEventHandler(priority = EventPriority.NORMAL)
    public void onBalanceChange(BalanceChangeEvent event) {
        log(event.getSourceModule(), event.getPlayer(), event.getAmount());
    }
}

// Publish from anywhere
eventBus.publish(new BalanceChangeEvent(player, 100.0));

More Built-in

  • GUI Framework — Inventory GUIs with pagination via ObliviateInvs, plus a declarative reactive GUI system
  • i18n — Built-in internationalization with i18n("key")
  • Hot Module Loading — Load/unload plugin modules without server restart
  • WebSocket + UltiPanel — Remote server management (commands, files, monitoring, logs)
  • Plugin Sandbox — Class blacklisting, path traversal protection, command blocklists

Documentation

Document Description
Developer Docs Full documentation site
Project Wiki Complete project documentation
Architecture Guide System architecture overview
IoC Container Dependency injection guide
Command System Command development guide
Data Storage Database operations guide
Quick Start Tutorial First module tutorial
API Reference API quick reference

Project Structure

UltiTools-Reborn/
├── src/main/java/com/ultikits/ultitools/
│   ├── UltiTools.java           # Main plugin entry
│   ├── api/                     # External Plugin API (UltiToolsAPI, ExternalPluginAdapter)
│   ├── abstracts/               # Base classes (UltiToolsPlugin, AbstractCommandExecutor, etc.)
│   ├── annotations/             # Framework annotations (@Service, @CmdExecutor, @Scheduled, etc.)
│   │   └── config/              # Validation annotations (@Range, @NotEmpty, @Size, @Pattern)
│   ├── aop/                     # AOP system (CglibProxyFactory, TransactionInterceptor)
│   ├── context/                 # IoC container (SimpleContainer, ComponentScanner)
│   ├── events/                  # Module EventBus (EventBus, ModuleEvent, EventPriority, Cancellable)
│   ├── manager/                 # Core managers (Command, Listener, Config, Plugin, Task)
│   ├── interfaces/              # Core interfaces (DataOperator, Query, DataStore) + impl/
│   ├── websocket/               # UltiPanel WebSocket client + handlers
│   └── utils/                   # SecurityPolicy, CloudAuthManager, ApiRateLimiter
├── docs/wiki/                   # Project documentation
└── pom.xml

Contributing

Submit an Issue for bugs and suggestions.

Main Contributors

Contributor Role
@wisdommen Founder, UltiKits Author
@qianmo2233 Developer, Documentation Maintainer
@Shpries Developer
@JueChenChen Issue Feedback & Suggestions
拾柒 Graphic Designer

License

MIT License — see LICENSE for details.


WakaTime Statistics

Development Timeline wakatime timeline

wakatime week

Acknowledgments

Java Development language
GitHub Actions CI/CD
wakatime IDE
Claude AI-assisted development
wakatime Build tool

Packages

 
 
 

Contributors

Languages