This guide provides step-by-step instructions for using the Fluxcord plugin template to create your own plugins.
- Template Overview
- Getting Started
- Template Structure
- Configuration System
- Feature Examples
- Customization Guide
- Building and Testing
- Deployment
- Troubleshooting
The plugin template provides a comprehensive starting point that demonstrates all core features of the Fluxcord framework:
- Plugin Lifecycle: Complete lifecycle management with all phases
- Configuration System: YAML-based configuration with feature toggles
- Command System: Example command registration and handling
- Event System: Discord and plugin event handling
- Permission System: Role-based permissions with namespacing
- Internationalization: Multi-language support (English and French)
- Data Storage: Persistent data storage with scoping
- Service Architecture: Organized code structure with services
- Unit Testing: Complete test examples with JUnit 5 and Mockito
✅ Modular Design: All examples can be disabled via configuration
✅ Best Practices: Follows recommended patterns and conventions
✅ Comprehensive: Covers all framework features
✅ Well Documented: Extensive comments and documentation
✅ Test Ready: Includes unit tests and test configuration
- Java 25 or newer
- Maven 3.6+ for building
- Fluxcord framework
- IDE with Java support (IntelliJ IDEA recommended)
# Copy the entire template directory
cp -r plugin-template my-awesome-plugin
cd my-awesome-pluginEdit pom.xml:
<artifactId>my-awesome-plugin</artifactId>
<name>My Awesome Plugin</name>
<description>Description of what your plugin does</description>Edit src/main/resources/plugin.yml:
name: MyAwesomePlugin
version: 1.0.0
main: com.yourcompany.plugin.MyAwesomePlugin
description: Your plugin description
author: YourName
website: https://github.com/yourusername/your-plugin-repo
dependencies: []-
Rename the package:
- From:
com.example.plugin - To:
com.yourcompany.plugin
- From:
-
Rename the main class:
- From:
TemplatePlugin.java - To:
MyAwesomePlugin.java
- From:
-
Update imports in all files to use your new package name
Edit src/main/resources/config.yml to enable/disable features:
# Enable or disable example features
features:
example_commands: true # Enable to see command examples
example_events: true # Enable to see event examples
example_storage: true # Enable to see storage examplesmy-awesome-plugin/
├── pom.xml # Maven configuration
├── CHANGELOG.md # Version history
├── README.md # Plugin documentation
├── src/
│ ├── main/
│ │ ├── java/com/example/plugin/
│ │ │ ├── TemplatePlugin.java # Main plugin class
│ │ │ ├── commands/
│ │ │ │ └── ExampleCommand.java # Command examples
│ │ │ ├── events/
│ │ │ │ └── ExampleEventListener.java # Event examples
│ │ │ └── services/
│ │ │ └── ExampleDataService.java # Service examples
│ │ └── resources/
│ │ ├── plugin.yml # Plugin metadata
│ │ ├── config.yml # Plugin configuration
│ │ └── lang/ # Language files
│ │ ├── en-US.yml # English translations
│ │ └── fr-FR.yml # French translations
│ └── test/
│ └── java/com/example/plugin/
│ └── TemplatePluginTest.java # Unit tests
The template uses a hierarchical YAML configuration system with feature toggles:
# Plugin features control
features:
example_commands: false # Disable to remove command examples
example_events: false # Disable to remove event examples
example_storage: false # Disable to remove storage examples
# Command system settings
commands:
enabled: true
prefix: "!"
cooldown: 3
# Permission settings
permissions:
default_permission: true
admin_nodes:
- "myplugin.admin"
- "myplugin.config"
# Storage settings
storage:
enabled: true
format: "json"
autosave_interval: 30
# Debug settings
debug:
log_messages: false
extended_debug: false- Use feature toggles to enable/disable functionality
- Provide sensible defaults for all settings
- Group related settings under sections
- Document configuration options in comments
- Validate configuration values in your plugin code
File: commands/ExampleCommand.java
public CommandResult execute(CommandContext context) {
// Check permissions
if (!plugin.getPermissions().hasPermission(
context.getUser().getId(), "template.use")) {
String message = plugin.getLanguage()
.getString("errors.no_permission");
context.reply(message);
return CommandResult.error("No permission");
}
// Get localized response
String response = plugin.getLanguage()
.getString("messages.example_message");
context.reply(response);
return CommandResult.success();
}Key Points:
- Permission checks before execution
- Localized error and success messages
- Proper result handling
File: events/ExampleEventListener.java
// ExampleEventListener extends ListenerAdapter: Discord events are JDA listener overrides,
// registered with addDiscordListeners(listener); @EventHandler is only for Fluxcord events.
@Override
public void onMessageReceived(MessageReceivedEvent event) {
// Skip bot messages
if (event.getAuthor().isBot()) {
return;
}
// Check if feature is enabled
if (!plugin.getConfiguration().getBoolean("events.enabled", true)) {
return;
}
// Process the event
plugin.logger.debug("Message received from {}",
event.getAuthor().getAsTag());
}Key Points:
- Proper event filtering (skip bots)
- Configuration-based enabling/disabling
- Appropriate logging levels
File: services/ExampleDataService.java
public void saveUserPreference(String userId, String key, Object value) {
try {
plugin.getStorage().getUserStorage(userId).set(key, value);
plugin.getStorage().saveAll();
plugin.logger.debug("Saved user preference: {} = {} for user {}",
key, value, userId);
} catch (Exception e) {
plugin.logger.error("Failed to save user preference", e);
}
}Key Points:
- Proper error handling
- Appropriate logging
- Different storage scopes (user, guild, global)
Language Files (lang/en-US.yml, lang/fr-FR.yml):
template:
messages:
welcome_user: "Welcome, {0}!"
example_message: "Hello from Template Plugin!"
errors:
no_permission: "You don't have permission to use this command"
commands:
help: "Show template plugin help"Usage in Code:
// Simple message
String message = getLanguage().getString("messages.example_message");
// Message with placeholders
String welcome = getLanguage().getString("messages.welcome_user", username);- Create command class in
commands/package - Implement command logic with permission checks
- Register command in main plugin class:
getCommands().registerCommand(builder -> {
builder.name("mycommand")
.description("My custom command")
.executor((context, cmd) -> myCommand.execute(context));
});- Create event listener class in
events/package - Add
ListenerAdapteroverrides for Discord events and@EventHandlermethods for Fluxcord events - Register listener in main plugin class:
eventManager.registerListener(myEventListener, this);- Create service class in
services/package - Initialize service in main plugin class
- Use dependency injection pattern for service access
getPermissions().registerPermission(new SimplePermission(
pluginPrefix("custom.permission"),
"Description of the permission",
PermissionDefault.FALSE
));- Add options to
config.yml - Access in code:
getConfiguration().getString("your.setting", "default") - Validate configuration values appropriately
# Compile and package
mvn clean package
# The JAR will be in target/ directory
ls target/*.jar# Run all tests
mvn test
# Run specific test
mvn test -Dtest=TemplatePluginTest
# Run tests with coverage
mvn test jacoco:reportThe template includes JUnit 5 and Mockito dependencies:
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
<scope>test</scope>
</dependency>@ExtendWith(MockitoExtension.class)
public class MyPluginTest {
@Mock
private PluginContext mockContext;
private MyPlugin plugin;
@BeforeEach
void setUp() {
plugin = new MyPlugin();
when(mockContext.getPluginName()).thenReturn("MyPlugin");
// Setup other mocks...
}
@Test
void testPluginEnables() {
plugin.onLoad(mockContext);
plugin.onEnable();
assertTrue(plugin.isEnabled());
}
}# Build with production profile (if configured)
mvn clean package -Pproduction
# Or standard build
mvn clean package# Copy JAR to plugins directory
cp target/my-awesome-plugin-1.0.0.jar /path/to/bot/plugins/
# Restart bot or reload plugins (if supported)- Copy default configuration if needed
- Customize settings for your environment
- Test functionality with example features enabled
Plugin Not Loading
- Check plugin.yml format and syntax
- Verify main class path is correct
- Check for missing dependencies
Configuration Errors
- Validate YAML syntax in config.yml
- Check for missing configuration values
- Review error logs for specific issues
Permission Issues
- Verify permissions are registered in onPreEnable()
- Check permission node names for typos
- Ensure permission defaults are appropriate
Language Issues
- Check language file format and structure
- Ensure language keys match code usage
Enable extended debugging in config.yml:
debug:
extended_debug: true
log_messages: true
log_plugin_events: trueUse appropriate logging levels:
logger.trace("Detailed execution flow"); // Very verbose
logger.debug("Debug information"); // Development info
logger.info("General information"); // Normal operation
logger.warn("Warning about potential issues"); // Warnings
logger.error("Error occurred", exception); // Errors- Review Core Features: Read core-features.md for complete API reference
- Explore Examples: Check examples-overview.md for specialized examples
- Join Community: Connect with other plugin developers
- Contribute: Share your plugin with the community
This guide covers the essential aspects of plugin development using the template. For advanced features and specific use cases, refer to the core documentation and API reference.