MagmaLib Documentation v2.1
The professional solution for migrating Paper plugins to Folia with a modern API, type-safe composition and maximum performance.
Type-safe composition: thenApply(), thenAccept(), exceptionally() • Context propagation: named() for debugging • 100% backward compatible with v1.x
Installation
MagmaLib v2.1 is now available on JitPack. Add the dependency to your project and start building Folia-ready plugins today!
Paper 1.20+ or Folia 1.20+ (recommended). Java 17 or higher.
Maven
<repositories>
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>com.github.MagmaEnginers</groupId>
<artifactId>MagmaLib</artifactId>
<version>2.1.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
Gradle (Kotlin DSL)
repositories {
maven("https://jitpack.io")
}
dependencies {
compileOnly("com.github.MagmaEnginers:MagmaLib:2.1.0")
}
Gradle (Groovy)
repositories {
maven { url 'https://jitpack.io' }
}
dependencies {
compileOnly 'com.github.MagmaEnginers:MagmaLib:2.1.0'
}
JitPack may require authentication for rate limiting. If you encounter issues, add credentials to your ~/.m2/settings.xml or gradle.properties. See FAQ for details.
📁 Alternative: Copy Source
If you prefer not to use external dependencies:
- Download
MagmaLib.java - Copy it to your project:
src/main/java/your/package/MagmaLib.java - Done! No external dependencies required.
Quick Start
Integrate MagmaLib in 3 simple steps:
- Add the dependency or copy
MagmaLib.javainto your project - Initialize in
onEnable()withMagmaLib.init(this) - Replace
Bukkit.getScheduler()withMagmaLib.task()
// In your main JavaPlugin
@Override
public void onEnable() {
// 1. Initialize MagmaLib
MagmaLib.init(this);
// 2. Use Task Builder (standard API - safe)
MagmaLib.task(() -> {
player.sendMessage("Hello from MagmaLib!");
})
.at(player.getLocation())
.afterTicks(20)
.handleException(e -> getLogger().warning("Error: " + e.getMessage()))
.run();
// 3. Or type-safe composition (new in v2.1)
MagmaLib.<Integer>task(() -> calculateScore(player))
.thenApply(score -> score > 100 ? "Excellent!" : "Keep trying")
.thenAccept(msg -> player.sendMessage(msg))
.run();
}
Initialization
init() Standard
Initializes the library. Call in onEnable(). Throws IllegalStateException if plugin is null.
@Override
public void onEnable() {
MagmaLib.init(this);
}
isFolia()
Automatically detects if the server is Folia using cached reflection. Throws exception if init() was not called.
if (MagmaLib.isFolia()) {
// Code optimized for Folia
} else {
// Fallback for Paper/Spigot
}
Task Builder (Standard API)
Safe and readable API for most use cases. Includes automatic validations and error handling. 100% compatible with v1.x.
All your MagmaLib v1.x code works without changes in v2.1. The new composition features are optional.
task(Runnable) v1.x
Entry point for tasks without return value. Compatible with previous versions.
task(Supplier) v2.1
Entry point for type-safe composition with thenApply/thenAccept.
at()
Executes in the chunk region (Folia) or main thread (Paper).
with()
Binds the task to the entity's scheduler in Folia.
afterTicks()
Direct delay in Minecraft ticks (1 tick = 50ms).
everyTicks()
Repetitive interval in ticks for periodic tasks.
handleException()
Custom error handler for the task.
named() v2.1
Assigns a name for debugging and logs with rich context.
unsafe() Direct
⚠️ Disables validations. Only in hot paths with guaranteed parameters.
run()
Executes the task and returns a Task object for manual cancellation.
By default, .at() and .with() validate that the chunk is loaded and the entity is valid. Use .cancelIfUnloaded(false) to disable.
Type-Safe Composition v2.1
New functional API style like CompletableFuture with complete type-safety. Transform, consume and handle errors declaratively.
- ✅ Type-safety at every pipeline step (no casts)
- ✅ More readable and maintainable code
- ✅ Integrated error handling
- ✅ Thread-aware: executes on correct scheduler automatically
Composition Flow
thenApply() Transform
Transforms the result by applying a function. Changes the builder type for type-safe chaining.
// Integer → String
MagmaLib.<Integer>task(() -> player.getLevel())
.thenApply(level -> "Level: " + level)
.thenAccept(msg -> player.sendMessage(msg))
.run();
thenAccept() Consume
Consumes the result without returning a value. Ideal for side effects like sending messages.
// Consume final result
MagmaLib.task(() -> fetchUserData(player))
.thenApply(data -> data.username)
.thenAccept(username -> {
player.sendMessage("Welcome, " + username);
})
.run();
exceptionally() Fallback
Provides a recovery value when an exception occurs. Similar to try-catch but functional.
// Fallback on error
MagmaLib.task(() -> database.query(player.getUniqueId()))
.thenApply(result -> result.score)
.exceptionally(e -> {
getLogger().warning("Query failed: " + e.getMessage());
return 0; // Default value
})
.thenAccept(score -> updateUI(score))
.run();
named() Debugging
Assigns a name for logs with context. Includes task name, location and entity in error messages.
// More useful logs in production
MagmaLib.task(() -> processTransaction(player, amount))
.named("EconomyDeduction")
.at(player.getLocation())
.handleException(e -> {
// Log: "Error in MagmaLib task ['EconomyDeduction'] location=world@123,45,67..."
getLogger().severe("Transaction failed: " + e.getMessage());
})
.run();
Complete Example: Data Pipeline
// Complete type-safe pipeline
MagmaLib.<List<ItemStack>>task(() -> player.getInventory().getContents())
.named("InventoryProcessor")
.at(player.getLocation())
// Filter non-null items (List<ItemStack> → List<ItemStack>)
.thenApply(items -> items.stream()
.filter(Objects::nonNull)
.toList())
// Count items (List<ItemStack> → Integer)
.thenApply(items -> items.size())
// Format message (Integer → String)
.thenApply(count -> "You have " + count + " items")
// Send message to player (side effect)
.thenAccept(message -> player.sendMessage(message))
// Handle errors with fallback
.exceptionally(e -> {
getLogger().warning("Error processing inventory: " + e.getMessage());
return "Error loading items";
})
// Execute with automatic routing for Folia
.run();
thenApply() requires using MagmaLib.<T>task(Supplier<T>). If you use task(Runnable), it will throw IllegalStateException. The methods thenAccept() and exceptionally() also require Supplier mode.
Direct API (High Performance)
Optimized methods for hot paths. No builder, no validations, maximum throughput.
These methods omit validations. Use them ONLY when you manually guarantee: location.getWorld() != null, chunk loaded, and entity.isValid().
runDirect() ⚡
Immediate execution on global thread. ~25% faster than task().run().
runDirectAt() ⚡
Execution in chunk region. Ideal for massive block processing.
runDirectWith() ⚡
Execution on entity scheduler. For massive update loops. v2.1 Fix: 0L delay for immediate execution.
runDirectLater() ⚡
Direct delay in ticks. No TimeUnit conversion.
runDirectTimer() ⚡
Direct periodic task. No builder or extra allocations.
runTimerUntilFast() New
Optimized version without AtomicReference. ~15% faster.
Async & CompletableFuture
runAsync()
Executes asynchronously and returns a future for composition.
MagmaLib.runAsync(() -> {
return heavyComputation();
}).thenAccept(result -> {
MagmaLib.runSync(() -> player.sendMessage(result));
});
callSync()
Executes on main thread and returns value via CompletableFuture.
MagmaLib.callSync(() -> {
return player.getInventory().getContents();
}).thenAccept(items -> {
// Process on async thread
});
Advanced Utilities
runWithRetry() New
Executes with automatic retries. Compatible with Folia.
runTimerUntil() New
Executes periodically until a condition is met.
forAllPlayers()
Iterates players with safe error handling. Executes on main thread.
forAllLoadedChunks()
Processes loaded chunks (executes async by default).
ticksToMs() / msToTicks()
Optimized time conversion (multiplication by 50L).
executeIfLoaded()
Executes only if the location's chunk is loaded.
Entity Handling
Always use .with(entity) for entity operations in Folia. Guarantees execution on correct scheduler and prevents race conditions.
Example: Safe Update
MagmaLib.task(() -> {
if (!entity.isValid() || entity.isDead()) return;
// Safe operations with entity
entity.setCustomName("§aProcessed");
entity.setGlowing(true);
})
.with(entity) // Correct routing in Folia
.cancelIf(() -> !entity.isValid()) // Automatic cancellation
.handleException(e -> getLogger().warning("Error: " + e.getMessage()))
.run();
High Performance Mode (⚠️)
// Only if you guarantee entity.isValid() == true
for (Entity entity : entities) {
MagmaLib.task(() -> {
entity.setCustomName("Updated");
})
.with(entity)
.unsafe() // ⚠️ Omits isValid() check
.run();
}
Compatibility
| Feature | Paper/Spigot | Folia |
|---|---|---|
task().run() | ✅ Bukkit Scheduler | ✅ GlobalRegionScheduler |
task().at(location) | ✅ Main Thread | ✅ RegionScheduler |
task().with(entity) | ✅ Main Thread | ✅ EntityScheduler |
task().async() | ✅ Async Scheduler | ✅ AsyncScheduler |
thenApply/thenAccept v2.1 | ✅ Functional | ✅ Functional |
runDirectAt() | ✅ Main Thread | ✅ RegionScheduler |
runDirectWith() | ✅ Main Thread | ✅ EntityScheduler |
cancelIfUnloaded | ✅ Verification | ✅ Verification |
handleException | ✅ Try-catch | ✅ Try-catch |
named() v2.1 | ✅ Context logs | ✅ Context logs |
Note: MagmaLib automatically detects the server type and uses the appropriate scheduler. Your code works on both without modifications.
Performance Comparison
Benchmark: 10,000 iterations of simple tasks on test server (Paper 1.21.4, Java 21).
| Scenario | FoliaLib | MagmaLib v1.2 | MagmaLib v2.1 | Improvement |
|---|---|---|---|---|
| Simple global task | 1.2 ms | 1.0 ms | 0.9 ms | +25% |
| Regional task | 1.5 ms | 1.2 ms | 1.1 ms | +27% |
| Task with composition | N/A | 2.8 ms* | 2.1 ms | +25% |
| Loop with validations | 3.8 ms | 3.2 ms | 2.1 ms | +47% |
| Hot path (runDirect*) | 1.3 ms | 0.9 ms | 0.8 ms | +38% |
*v1.2 with manual CompletableFuture; v2.1 with integrated native composition
Conclusion: MagmaLib v2.1 is ~25-47% faster than alternatives in hot paths, with integrated type-safe composition and zero additional overhead.
Frequently Asked Questions
Will my v1.x code work in v2.1?
✅ Yes, 100% compatible. All v1.x APIs work without changes. The new composition features are optional and are activated by using task(Supplier<T>) instead of task(Runnable).
When to use thenApply vs thenAccept?
thenApply: When you need to transform the value and continue the pipeline (returns new type). thenAccept: When you want to consume the final value for a side effect (doesn't return value, ends the pipeline).
Is it safe to use .unsafe()?
Yes, if you follow these rules:
- ✅ Validate manually before calling
- ✅ Use only in code you fully control
- ✅ Document why it's safe in that context
- ❌ Never use with user inputs or external data
Does it work on Paper and Folia?
✅ Yes, 100% compatible. MagmaLib automatically detects the server type and uses the appropriate scheduler:
- Folia: RegionScheduler, EntityScheduler, GlobalRegionScheduler
- Paper/Spigot: Traditional Bukkit Scheduler
Your code needs no changes to work on both.
How to debug tasks with named()?
Error logs will automatically include:
- 🏷️ Task name:
['MyTask'] - 📍 Location:
location=world@x,y,z - 👤 Entity:
entity=Player[Notch] - 📋 Creation stack trace (if you enable debug)
How to install v2.1 now?
While JitPack is not available:
- Download
MagmaLib.javafrom the repository - Copy it to your plugin:
src/main/java/your/package/MagmaLib.java - Done! No external dependencies
Coming soon: jitpack.io/#MagmaEnginers/MagmaLib
Do I need authentication for JitPack?
JitPack may require authentication to avoid rate limiting. Follow these steps:
Maven: Add to ~/.m2/settings.xml:
<servers>
<server>
<id>jitpack.io</id>
<username>YOUR_GITHUB_USERNAME</username>
<password>ghp_YOUR_TOKEN</password>
</server>
</servers>
Gradle: Add to ~/.gradle/gradle.properties:
gpr.user=YOUR_GITHUB_USERNAME
gpr.key=ghp_YOUR_TOKEN
Create a token at GitHub Settings with scope read:packages.