A powerful and comprehensive framework for Minecraft plugin development, providing a structured architecture with utilities, services, and management systems to accelerate plugin creation.
- 🏗️ Base Plugin Architecture - Extend
BasePluginfor automatic framework initialization - ⚙️ Configuration Management - Multi-file YAML configuration system with caching and reloading
- 🎮 Command System - Advanced command registry with sub-command support and tab completion
- 📡 Event Registry - Centralized event listener management with automatic cleanup
- 🔧 Service Manager - Lifecycle-managed services for modular plugin architecture
- 🔒 Permission Manager - Dynamic permission registration and validation system
- ⚡ Scheduler Utilities - Simplified synchronous and asynchronous task scheduling
- 💾 Cache Service - In-memory caching system with TTL support
- ⏱️ Cooldown Manager - Per-player cooldown system for commands and actions
- 🎨 Text Utilities - Legacy color codes and MiniMessage support
- 📝 Message Builder - Fluent API for building complex messages
- ✅ Validation Utilities - Input validation and error handling
- ⏰ Time Utilities - Duration formatting and tick conversion
- Java 17+
- Paper/Spigot 1.20.4
- Maven 3.6+
settings:
debug: false
locale: en_US
cache:
enabled: true
default-ttl: 3600000
example:
enabled: true
value: "default"The framework supports multiple configuration files:
ConfigurationManager configManager = getConfigManager();
FileConfiguration config = configManager.load("config.yml");
FileConfiguration messages = configManager.load("messages.yml");
FileConfiguration database = configManager.load("database.yml");
// Reload specific config
configManager.reload("config.yml");
// Reload all configs
configManager.reloadAll();Extend BasePlugin and implement the required methods:
public class MyPlugin extends BasePlugin {
@Override
protected void initialize() {
// Plugin initialization logic
getLogger().info("Plugin initialized!");
}
@Override
protected void registerCommands(CommandRegistry registry) {
registry.register("mycommand", new MyCommand());
}
@Override
protected void registerEvents(EventRegistry registry) {
registry.register(new MyListener());
}
@Override
protected void registerServices(ServiceManager manager) {
manager.register(new MyService());
}
@Override
protected void shutdown() {
// Cleanup logic
getLogger().info("Plugin disabled!");
}
}public class MyCommand implements CommandExecutor {
@Override
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
sender.sendMessage("Hello from framework!");
return true;
}
}public class FrameworkCommand extends BaseCommand {
public FrameworkCommand() {
registerSubCommand("reload", new CommandHandler() {
@Override
public boolean execute(CommandSender sender, String[] args) {
// Reload logic
return true;
}
@Override
public String getPermission() {
return "plugin.reload";
}
});
}
@Override
protected boolean executeDefault(CommandSender sender, String[] args) {
sender.sendMessage("Usage: /command <subcommand>");
return true;
}
}public class MyListener implements Listener {
@EventHandler
public void onPlayerJoin(PlayerJoinEvent event) {
Player player = event.getPlayer();
PlayerUtil.sendMessage(player, "&aWelcome to the server!");
}
}
// Register in BasePlugin
@Override
protected void registerEvents(EventRegistry registry) {
registry.register(new MyListener());
}Create custom services with lifecycle management:
public class DatabaseService implements Service {
@Override
public void start() {
// Initialize database connection
getLogger().info("Database service started");
}
@Override
public void stop() {
// Close database connection
getLogger().info("Database service stopped");
}
@Override
public boolean isRunning() {
return connection != null && connection.isValid();
}
}
// Register in BasePlugin
@Override
protected void registerServices(ServiceManager manager) {
manager.register(new DatabaseService());
}
// Access service
DatabaseService dbService = getServiceManager().get(DatabaseService.class);CacheService cache = getServiceManager().get(CacheService.class);
// Store with TTL (Time To Live)
cache.put("player:uuid", playerData, 3600000); // 1 hour
// Store permanently
cache.put("config:value", configValue);
// Retrieve
Optional<PlayerData> data = cache.get("player:uuid");
// Get or compute
PlayerData data = cache.getOrCompute("player:uuid", () -> {
// Fetch from database
return database.getPlayerData(uuid);
});
// Check existence
if (cache.contains("player:uuid")) {
// Cache hit
}
// Invalidate
cache.invalidate("player:uuid");
// Clear all
cache.clear();CooldownManager cooldown = getServiceManager().get(CooldownManager.class);
// Set cooldown for player
cooldown.set("command:teleport", player.getUniqueId(), Duration.ofSeconds(30));
// Check if player is on cooldown
if (cooldown.has("command:teleport", player.getUniqueId())) {
Duration remaining = cooldown.remaining("command:teleport", player.getUniqueId());
player.sendMessage("Please wait " + TimeUtil.format(remaining));
return;
}
// Remove cooldown
cooldown.remove("command:teleport", player.getUniqueId());
// Clear all cooldowns for a specific key
cooldown.clear("command:teleport");// Synchronous task
SchedulerUtil.sync(plugin, () -> {
// Runs on main thread
});
// Asynchronous task
SchedulerUtil.async(plugin, () -> {
// Runs on async thread
});
// Delayed task
SchedulerUtil.syncLater(plugin, () -> {
// Runs after 20 ticks (1 second)
}, 20);
// Repeating task
BukkitTask task = SchedulerUtil.syncRepeating(plugin, () -> {
// Runs every 20 ticks
}, 0, 20);
// Cancel task
SchedulerUtil.cancelTask(task);
// Supply async, consume sync
SchedulerUtil.supplyThenAccept(plugin,
() -> fetchDataFromDatabase(), // Async
(data) -> processData(data) // Sync
);
// CompletableFuture support
CompletableFuture<String> future = SchedulerUtil.supply(plugin, () -> {
return fetchStringAsync();
});
future.thenAccept(result -> {
// Process result
});// Legacy color codes
Component message = TextUtil.color("&aHello &cWorld");
// MiniMessage support
Component mmMessage = TextUtil.miniMessage("<green>Hello <red>World</red></green>");
// Strip colors
String plain = TextUtil.strip("&aHello");
// Convert to legacy
String legacy = TextUtil.toLegacy(component);
// Message Builder (Fluent API)
Component message = MessageBuilder.create()
.text("Hello", NamedTextColor.GREEN)
.space()
.bold("World")
.newline()
.colored("&7This is a description")
.build();// Get player by name
Optional<Player> player = PlayerUtil.getPlayer("PlayerName");
// Get player by UUID
Optional<Player> player = PlayerUtil.getPlayer(uuid);
// Send message
PlayerUtil.sendMessage(player, "&aHello!");
PlayerUtil.sendMessage(player, component);
// Broadcast
PlayerUtil.broadcast("&aServer restart in 10 minutes!");
PlayerUtil.broadcast(component);
// Check permission
if (PlayerUtil.hasPermission(player, "plugin.use")) {
// Has permission
}// Convert Duration to ticks
long ticks = TimeUtil.toTicks(Duration.ofSeconds(5)); // 100 ticks
// Convert TimeUnit to ticks
long ticks = TimeUtil.toTicks(5, TimeUnit.SECONDS);
// Convert ticks to Duration
Duration duration = TimeUtil.fromTicks(100);
// Format duration
String formatted = TimeUtil.format(Duration.ofSeconds(3661)); // "1h 1m 1s"
String compact = TimeUtil.formatCompact(Duration.ofSeconds(3661)); // "01:01:01"
// Parse duration string
Duration duration = TimeUtil.parse("1h30m15s");PermissionManager permManager = new PermissionManager(plugin);
// Register permission
permManager.register("plugin.use", PermissionDefault.OP);
permManager.register("plugin.admin", PermissionDefault.OP, "Admin permission");
// Check permission
if (permManager.has(sender, "plugin.use")) {
// Has permission
}
// Check any permission
if (permManager.hasAny(sender, "plugin.use", "plugin.admin")) {
// Has at least one
}
// Check all permissions
if (permManager.hasAll(sender, "plugin.use", "plugin.reload")) {
// Has all
}
// Unregister
permManager.unregister("plugin.use");
permManager.unregisterAll();// Null checks
String value = ValidationUtil.requireNonNull(obj, "object");
String text = ValidationUtil.requireNonEmpty(str, "string");
// Collection checks
List<String> list = ValidationUtil.requireNonEmpty(collection, "list");
// Boolean checks
ValidationUtil.requireTrue(condition, "Condition must be true");
ValidationUtil.requireFalse(condition, "Condition must be false");
// Range checks
ValidationUtil.requireInRange(value, 0, 100, "percentage");
ValidationUtil.requirePositive(value, "count");
ValidationUtil.requireNonNegative(value, "amount");// Info log
LoggerUtil.info(plugin, "Plugin loaded successfully");
// Warning log
LoggerUtil.warning(plugin, "Configuration missing, using defaults");
// Severe log
LoggerUtil.severe(plugin, "Critical error occurred");
// Error with exception
LoggerUtil.error(plugin, "Failed to connect to database", exception);
// Debug log (with debug mode check)
LoggerUtil.debug(plugin, "Player joined: " + player.getName(), debugMode);
// Get logger
Logger logger = LoggerUtil.getLogger(plugin);The framework provides built-in permission management. Permissions are registered dynamically:
PermissionManager permManager = new PermissionManager(plugin);
permManager.register("plugin.use", PermissionDefault.OP);
permManager.register("plugin.admin", PermissionDefault.OP);- JDK 17
- Maven 3.6+
# Clone repository
git clone https://github.com/m4trixdev/PluginFramework.git
cd PluginFramework
# Compile
mvn clean compile
# Package
mvn clean package
# Install to local repository
mvn clean installThe compiled .jar file will be in target/PluginFramework-1.0.0.jar
BasePlugin
├── ConfigurationManager
│ └── Multi-file YAML support
├── CommandRegistry
│ └── Command registration & sub-commands
├── EventRegistry
│ └── Listener management
└── ServiceManager
└── Lifecycle-managed services
- CacheService - In-memory cache with TTL
- CooldownManager - Player cooldown system
- SchedulerUtil - Task scheduling
- TaskUtil - Alternative task utilities
- LoggerUtil - Logging helpers
- PlayerUtil - Player operations
- TextUtil - Text formatting
- MessageBuilder - Fluent message building
- TimeUtil - Time & duration utilities
- ValidationUtil - Input validation
initialize()- Plugin initializationregisterCommands()- Register commandsregisterEvents()- Register event listenersregisterServices()- Register servicespostInitialize()- Post-initialization hook (optional)preShutdown()- Pre-shutdown hook (optional)shutdown()- Plugin shutdown
public interface Service {
void start();
void stop();
String getName();
boolean isRunning();
}public interface CommandHandler extends TabCompleter {
boolean execute(CommandSender sender, String[] args);
String getPermission();
}- Ensure your plugin extends
BasePlugin - Check that all abstract methods are implemented
- Verify
plugin.ymlhas correct main class - Check server logs for initialization errors
- Verify file exists in
src/main/resources/ - Check YAML syntax is valid
- Ensure
ConfigurationManager.load()is called - Verify file permissions
- Confirm command is declared in
plugin.yml - Check
CommandRegistry.register()is called - Verify command executor is set correctly
- Check for permission issues
- Ensure service implements
Serviceinterface - Verify
start()method is implemented correctly - Check service registration in
registerServices() - Review logs for service errors
- Initial release
- Base plugin architecture
- Configuration management system
- Command and event registries
- Service manager
- Permission manager
- Cache and cooldown services
- Comprehensive utility classes
- Text formatting utilities
- Message builder API
- Time and validation utilities
This project is licensed under the MIT License.
M4trixDev
- GitHub: @m4trixdev
Contributions are welcome! Feel free to:
- Report bugs
- Suggest new features
- Submit pull requests
- Improve documentation
- Issues: GitHub Issues
- Discussions: GitHub Discussions
Made with ❤️ for the Minecraft plugin development community