LunarCore Fabric
LunarCore Fabric is a foundational SDK for Minecraft Fabric mod development. It provides essential utilities and abstractions that streamline common mod development tasks without introducing gameplay mechanics or forcing specific architectural decisions.
Philosophy
Minimal and Purpose-Driven
LunarCore Fabric deliberately avoids feature bloat. Every utility included serves a clear purpose and addresses a common pain point in Fabric mod development. The SDK does not include:
- Gameplay mechanics or content
- User interface systems
- Forced mixins or bytecode manipulation
- Opinionated frameworks that dictate architecture
Fabric-Native Design
Rather than creating abstractions that hide Fabric's APIs, LunarCore builds on top of them. This approach ensures:
- Compatibility with other Fabric mods
- No learning curve for developers familiar with Fabric
- Transparent behavior that doesn't obscure underlying mechanics
- Easy debugging and troubleshooting
Thread Safety
All utilities in LunarCore are designed with thread safety in mind. This is particularly important for:
- Task scheduling across game ticks
- Asynchronous operations like update checking
- Event handling that may occur from different contexts
Core Components
Lifecycle Management
The LifecycleManager provides a clean way to handle mod initialization and shutdown without implementing multiple interfaces or scattering logic across your codebase.
Registration
Register callbacks for different lifecycle phases:
LifecycleManager.onInitialize(() -> {
// Called during mod initialization
LunarLogger.info(MOD_ID, "Mod initialized");
});
LifecycleManager.onShutdown(() -> {
// Called when the server stops or client closes
saveConfig();
cleanup();
});
Use Cases
Lifecycle callbacks are useful for:
- Initializing configuration files
- Setting up database connections
- Registering dynamic content
- Performing cleanup operations
- Saving persistent data
Implementation Details
The lifecycle system hooks into Fabric's existing lifecycle events but provides a simpler interface. Callbacks are executed in registration order, and exceptions in one callback won't prevent others from executing.
Configuration System
The Config class provides JSON-based configuration management with automatic defaults and type-safe getters.
Basic Usage
Config config = new Config(MOD_ID);
config.load();
// Retrieve values with defaults
boolean featureEnabled = config.getBoolean("features.autoSave", true);
int tickDelay = config.getInt("timing.tickDelay", 20);
String serverName = config.getString("server.name", "Default Server");
File Location
Configuration files are stored in .minecraft/config/<modid>.json. The system automatically:
- Creates the config directory if it doesn't exist
- Generates a default config file on first run
- Preserves comments in JSON (where supported)
Nested Configuration
Support for nested configuration structures:
// Access nested values with dot notation
String dbHost = config.getString("database.host", "localhost");
int dbPort = config.getInt("database.port", 5432);
Configuration Validation
The config system includes basic validation:
- Type checking for numeric values
- Range validation for numbers
- Enum validation for string constants
Reloading Configuration
Configuration can be reloaded at runtime:
config.reload();
This is useful for:
- Applying changes without restarting
- Responding to file changes
- Testing different configurations
Logging System
LunarLogger wraps SLF4J to provide a simplified logging interface with per-mod context.
Log Levels
LunarLogger.info(MOD_ID, "Server started successfully");
LunarLogger.warn(MOD_ID, "Deprecated configuration option used");
LunarLogger.error(MOD_ID, "Failed to load resource: {}", resourceName);
LunarLogger.debug(MOD_ID, "Player position: x={}, y={}, z={}", x, y, z);
Structured Logging
Support for parameterized log messages to avoid string concatenation:
// Good - parameters are only evaluated if log level is enabled
LunarLogger.debug(MOD_ID, "Processing {} items", expensiveCalculation());
// Bad - string concatenation always occurs
LunarLogger.debug(MOD_ID, "Processing " + expensiveCalculation() + " items");
Log Categories
Logs are automatically categorized by mod ID, making it easy to filter output:
[INFO] [yourmod] Feature initialized
[WARN] [yourmod] Config value out of range
[ERROR] [yourmod] Critical operation failed
Exception Logging
Dedicated methods for exception logging:
try {
riskyOperation();
} catch (Exception e) {
LunarLogger.error(MOD_ID, "Operation failed", e);
}
Event Helper
EventHelper simplifies event registration with automatic error handling and a fluent interface.
Basic Registration
EventHelper.forMod(MOD_ID)
.on(ServerLifecycleEvents.SERVER_STARTED, server -> {
LunarLogger.info(MOD_ID, "Server started");
})
.on(ServerLifecycleEvents.SERVER_STOPPING, server -> {
LunarLogger.info(MOD_ID, "Server stopping");
})
.registerAll();
Error Handling
All event handlers are automatically wrapped in try-catch blocks. Exceptions are logged but don't crash the game:
EventHelper.forMod(MOD_ID)
.on(ServerTickEvents.START_SERVER_TICK, server -> {
// Even if this throws an exception, other mods' handlers will still run
potentiallyFailingOperation();
})
.registerAll();
Conditional Registration
Register events conditionally:
if (config.getBoolean("features.enableAutoSave", false)) {
EventHelper.forMod(MOD_ID)
.on(ServerTickEvents.END_SERVER_TICK, this::performAutoSave)
.registerAll();
}
Task Scheduler
TaskScheduler provides thread-safe task scheduling that respects Minecraft's tick cycle.
Delayed Tasks
Run a task after a specified delay:
// Run after 100 ticks (5 seconds)
TaskScheduler.runLater(MOD_ID, 100, () -> {
LunarLogger.info(MOD_ID, "Delayed task executed");
});
Repeating Tasks
Execute a task repeatedly at fixed intervals:
// Run every 20 ticks (1 second)
TaskScheduler.runRepeating(MOD_ID, 20, () -> {
performPeriodicCheck();
});
Cancellation
Tasks return a cancellation token:
var task = TaskScheduler.runRepeating(MOD_ID, 100, () -> {
updateStatus();
});
// Cancel later
task.cancel();
Thread Safety
All scheduled tasks execute on the main server thread, ensuring:
- Safe access to game state
- No concurrent modification exceptions
- Predictable execution order
Update Checker
The UpdateChecker performs asynchronous version checking against GitHub releases or custom endpoints.
GitHub Integration
UpdateChecker.checkGitHubRelease(
MOD_ID,
"your-username",
"your-repo",
"1.0.0", // Current version
result -> {
if (result.isUpdateAvailable()) {
LunarLogger.info(MOD_ID, "Update available: {}",
result.getLatestVersion());
notifyPlayers(result.getDownloadUrl());
}
}
);
Custom Endpoints
For self-hosted update servers:
UpdateChecker.checkCustomEndpoint(
MOD_ID,
"https://your-server.com/api/version",
"1.0.0",
result -> {
handleUpdateCheck(result);
}
);
Update Information
The result object provides:
- Latest version number
- Download URL
- Release notes
- Whether an update is available
- Comparison result (newer, same, older)
Rate Limiting
Update checks are automatically rate-limited to prevent excessive API calls:
- Maximum one check per hour per endpoint
- Cached results for repeated queries
- Configurable rate limit through API
Integration Guide
Adding LunarCore to Your Project
Gradle Configuration
Add the repository and dependency to your build.gradle:
repositories {
maven {
name = "GitHubPackages"
url = uri("https://maven.pkg.github.com/LunarBit-dev/LunarCore-fabric")
credentials {
username = project.findProperty("gpr.user") ?: System.getenv("GITHUB_USER")
password = project.findProperty("gpr.token") ?: System.getenv("GITHUB_TOKEN")
}
}
}
dependencies {
modImplementation "dev.lunarbit.lunarcore:lunarcore:1.0.0"
include "dev.lunarbit.lunarcore:lunarcore:1.0.0"
}
GitHub Authentication
Create a GitHub personal access token with read:packages permission, then add to ~/.gradle/gradle.properties:
gpr.user=your-github-username
gpr.token=your-github-token
Basic Mod Structure
public class YourMod implements ModInitializer {
public static final String MOD_ID = "yourmod";
private Config config;
@Override
public void onInitialize() {
LunarLogger.info(MOD_ID, "Initializing " + MOD_ID);
// Setup
LifecycleManager.onInitialize(this::setup);
LifecycleManager.onShutdown(this::cleanup);
// Register events
registerEvents();
// Check for updates
checkForUpdates();
}
private void setup() {
config = new Config(MOD_ID);
config.load();
// Schedule periodic tasks
if (config.getBoolean("features.autoSave", true)) {
int interval = config.getInt("autoSave.interval", 6000);
TaskScheduler.runRepeating(MOD_ID, interval, this::autoSave);
}
}
private void registerEvents() {
EventHelper.forMod(MOD_ID)
.on(ServerLifecycleEvents.SERVER_STARTED, this::onServerStart)
.on(ServerLifecycleEvents.SERVER_STOPPING, this::onServerStop)
.registerAll();
}
private void cleanup() {
config.save();
LunarLogger.info(MOD_ID, "Cleanup complete");
}
}
fabric.mod.json Configuration
Declare LunarCore as a dependency:
{
"schemaVersion": 1,
"id": "yourmod",
"version": "1.0.0",
"name": "Your Mod",
"entrypoints": {
"main": [
"com.example.yourmod.YourMod"
]
},
"depends": {
"fabricloader": ">=0.18.3",
"minecraft": "~1.21.11",
"fabric-api": "*",
"lunarcore": ">=1.0.0"
}
}
Best Practices
Configuration Management
- Provide sensible defaults for all configuration values
- Document configuration options in comments or separate files
- Validate configuration values during loading
- Save configuration only when values change
Logging
- Use appropriate log levels (DEBUG for detailed traces, INFO for general events, WARN for recoverable issues, ERROR for critical failures)
- Include contextual information in log messages
- Use parameterized logging to avoid unnecessary string operations
- Log exceptions with stack traces for debugging
Task Scheduling
- Keep scheduled tasks lightweight to avoid blocking the game thread
- Cancel repeating tasks when they're no longer needed
- Handle exceptions within task callbacks
- Use appropriate delays to balance responsiveness and performance
Event Handling
- Register events during initialization, not dynamically at runtime
- Keep event handlers focused and single-purpose
- Avoid heavy computation in event handlers
- Use conditional registration for optional features
Compatibility
Minecraft Versions
LunarCore Fabric targets Minecraft 1.21.11 but is designed to be version-agnostic where possible. The API surface uses stable Fabric APIs that rarely change between Minecraft versions.
Mod Compatibility
LunarCore does not:
- Modify vanilla behavior
- Register items, blocks, or entities
- Inject mixins
- Override game systems
This design ensures compatibility with virtually all other mods.
Performance Impact
The performance overhead of LunarCore is minimal:
- Event helpers add negligible overhead to event dispatch
- Task scheduler uses Minecraft's existing tick cycle
- Configuration loading is lazy and cached
- Update checking is asynchronous and rate-limited
Troubleshooting
Common Issues
LunarCore Not Found
Ensure your Gradle credentials are correctly configured and you've run ./gradlew --refresh-dependencies.
Events Not Firing
Verify that you've called .registerAll() on your EventHelper chain.
Tasks Not Executing
Check that the server is running and ticks are progressing. Tasks only execute during normal game ticks.
Configuration Not Saving
Ensure the config directory is writable and there's sufficient disk space.
Debug Logging
Enable debug logging to troubleshoot issues:
LunarLogger.setLevel(MOD_ID, LogLevel.DEBUG);
Advanced Topics
Custom Configuration Loaders
Extend the Config system to support alternative formats:
public class YamlConfig extends Config {
@Override
protected void loadFromFile(Path path) {
// Custom YAML loading logic
}
}
Distributed Task Execution
For server networks, tasks can be distributed across multiple servers by extending the TaskScheduler.
Custom Update Endpoints
Implement custom version checking logic:
public class CustomUpdateChecker {
public static void check(String modId, String currentVersion,
Consumer<UpdateResult> callback) {
// Custom update checking implementation
}
}
API Documentation
Complete API documentation is available in the project's JavaDoc. Key packages:
dev.lunarbit.lunarcore.api.lifecycle- Lifecycle managementdev.lunarbit.lunarcore.api.config- Configuration systemdev.lunarbit.lunarcore.api.log- Logging utilitiesdev.lunarbit.lunarcore.api.event- Event helpersdev.lunarbit.lunarcore.api.task- Task schedulingdev.lunarbit.lunarcore.api.update- Update checking
License
LunarCore Fabric is released under the MIT License, permitting free use, modification, and distribution with proper attribution.
Community and Support
For questions, bug reports, or feature requests, please use the GitHub issue tracker. Contributions are welcome and should follow the project's coding standards and design principles.