Documentación de MagmaLib v2.0
La solución profesional para migrar plugins de Paper a Folia con una API moderna, type-safe composition y máximo rendimiento.
Composición type-safe: thenApply(), thenAccept(), exceptionally() • Context propagation: named() para debugging • 100% backward compatible con v1.x
Instalación
Paper 1.18+ o Folia (recomendado 1.20+). Java 17 o superior.
MagmaLib v2.0 estará disponible en JitPack muy pronto. Mientras tanto, puedes usar el código fuente directamente copiando MagmaLib.java en tu proyecto.
Maven
<!-- Próximamente en JitPack -->
Gradle (Kotlin DSL)
// Próximamente en JitPack
Gradle (Groovy)
// Próximamente en JitPack
Inicio Rápido
Integra MagmaLib en 3 pasos simples:
- Añade la dependencia o copia
MagmaLib.javaen tu proyecto - Inicializa en
onEnable()conMagmaLib.init(this) - Reemplaza
Bukkit.getScheduler()porMagmaLib.task()
// En tu JavaPlugin principal
@Override
public void onEnable() {
// 1. Inicializar MagmaLib
MagmaLib.init(this);
// 2. Usar Task Builder (API estándar - segura)
MagmaLib.task(() -> {
player.sendMessage("¡Hola desde MagmaLib!");
})
.at(player.getLocation())
.afterTicks(20)
.handleException(e -> getLogger().warning("Error: " + e.getMessage()))
.run();
// 3. O composición type-safe (nuevo en v2.0)
MagmaLib.<Integer>task(() -> calculateScore(player))
.thenApply(score -> score > 100 ? "¡Excelente!" : "Sigue")
.thenAccept(msg -> player.sendMessage(msg))
.run();
}
Inicialización
init() Estándar
Inicializa la librería. Llamar en onEnable(). Lanza IllegalStateException si plugin es null.
@Override
public void onEnable() {
MagmaLib.init(this);
}
isFolia()
Detecta automáticamente si el servidor es Folia usando reflexión con caché. Lanza excepción si no se llamó init().
if (MagmaLib.isFolia()) {
// Código optimizado para Folia
} else {
// Fallback para Paper/Spigot
}
Task Builder (API Estándar)
API segura y legible para la mayoría de casos de uso. Incluye validaciones automáticas y manejo de errores. 100% compatible con v1.x.
Todo tu código de MagmaLib v1.x funciona sin cambios en v2.0. Las nuevas features de composición son opcionales.
task(Runnable) v1.x
Punto de entrada para tareas sin retorno. Compatible con versiones anteriores.
task(Supplier) v2.0
Punto de entrada para composición type-safe con thenApply/thenAccept.
at()
Ejecuta en la región del chunk (Folia) o hilo principal (Paper).
with()
Vincula la tarea al scheduler de la entidad en Folia.
afterTicks()
Retraso directo en ticks de Minecraft (1 tick = 50ms).
everyTicks()
Intervalo repetitivo en ticks para tareas periódicas.
handleException()
Manejador personalizado de errores para la tarea.
named() v2.0
Asigna nombre para debugging y logs con contexto rico.
unsafe() Directo
⚠️ Desactiva validaciones. Solo en hot paths con parámetros garantizados.
run()
Ejecuta la tarea y devuelve objeto Task para cancelación manual.
Por defecto, .at() y .with() validan que el chunk esté cargado y la entidad sea válida. Usa .cancelIfUnloaded(false) para desactivar.
Composición Type-Safe v2.0
Nueva API funcional estilo CompletableFuture con type-safety completo. Transforma, consume y maneja errores de forma declarativa.
- ✅ Type-safety en cada paso del pipeline (sin casts)
- ✅ Código más legible y mantenible
- ✅ Manejo de errores integrado
- ✅ Thread-aware: ejecuta en el scheduler correcto automáticamente
Flujo de Composición
thenApply() Transformar
Transforma el resultado aplicando una función. Cambia el tipo del builder para encadenamiento type-safe.
// Integer → String
MagmaLib.<Integer>task(() -> player.getLevel())
.thenApply(level -> "Nivel: " + level)
.thenAccept(msg -> player.sendMessage(msg))
.run();
thenAccept() Consumir
Consume el resultado sin retornar valor. Ideal para efectos secundarios como enviar mensajes.
// Consumir resultado final
MagmaLib.task(() -> fetchUserData(player))
.thenApply(data -> data.username)
.thenAccept(username -> {
player.sendMessage("Bienvenido, " + username);
})
.run();
exceptionally() Fallback
Proporciona un valor de recuperación cuando ocurre una excepción. Similar a try-catch pero funcional.
// Fallback ante error
MagmaLib.task(() -> database.query(player.getUniqueId()))
.thenApply(result -> result.score)
.exceptionally(e -> {
getLogger().warning("Query falló: " + e.getMessage());
return 0; // Valor por defecto
})
.thenAccept(score -> updateUI(score))
.run();
named() Debugging
Asigna nombre para logs con contexto. Incluye task name, ubicación y entidad en mensajes de error.
// Logs más útiles en producción
MagmaLib.task(() -> processTransaction(player, amount))
.named("EconomyDeduction")
.at(player.getLocation())
.handleException(e -> {
// Log: "Error en tarea MagmaLib ['EconomyDeduction'] location=world@123,45,67..."
getLogger().severe("Transacción fallida: " + e.getMessage());
})
.run();
Ejemplo Completo: Pipeline de Datos
// Pipeline type-safe completo
MagmaLib.<List<ItemStack>>task(() -> player.getInventory().getContents())
.named("InventoryProcessor")
.at(player.getLocation())
// Filtrar items no null (List ItemStack → List ItemStack)
.thenApply(items -> items.stream()
.filter(Objects::nonNull)
.toList())
// Contar items (List ItemStack → Integer)
.thenApply(items -> items.size())
// Formatear mensaje (Integer → String)
.thenApply(count -> "Tienes " + count + " items")
// Enviar mensaje al jugador (efecto secundario)
.thenAccept(message -> player.sendMessage(message))
// Manejar errores con fallback
.exceptionally(e -> {
getLogger().warning("Error procesando inventario: " + e.getMessage());
return "Error al cargar items";
})
// Ejecutar con routing automático para Folia
.run();
thenApply() requiere usar MagmaLib.<T>task(Supplier<T>). Si usas task(Runnable), lanzará IllegalStateException. Los métodos thenAccept() y exceptionally() también requieren modo Supplier.
API Directa (Alto Rendimiento)
Métodos optimizados para hot paths. Sin builder, sin validaciones, máximo throughput.
Estos métodos omiten validaciones. Úsalos SOLO cuando garantices manualmente: location.getWorld() != null, chunk cargado, y entity.isValid().
runDirect() ⚡
Ejecución inmediata en hilo global. ~25% más rápido que task().run().
runDirectAt() ⚡
Ejecución en región de chunk. Ideal para procesamiento masivo de bloques.
runDirectWith() ⚡
Ejecución en scheduler de entidad. Para bucles de actualización masiva. Fix v2.0: delay 0L para ejecución inmediata.
runDirectLater() ⚡
Retraso directo en ticks. Sin conversión de TimeUnit.
runDirectTimer() ⚡
Tarea periódica directa. Sin builder ni allocations extra.
runTimerUntilFast() Nuevo
Versión optimizada sin AtomicReference. ~15% más rápido.
Async & CompletableFuture
runAsync()
Ejecuta asíncronamente y retorna futuro para composición.
MagmaLib.runAsync(() -> {
return heavyComputation();
}).thenAccept(result -> {
MagmaLib.runSync(() -> player.sendMessage(result));
});
callSync()
Ejecuta en hilo principal y retorna valor vía CompletableFuture.
MagmaLib.callSync(() -> {
return player.getInventory().getContents();
}).thenAccept(items -> {
// Procesar en hilo asíncrono
});
Utilidades Avanzadas
runWithRetry() Nuevo
Ejecuta con reintentos automáticos. Compatible con Folia.
runTimerUntil() Nuevo
Ejecuta periódicamente hasta que se cumpla una condición.
forAllPlayers()
Itera jugadores con manejo seguro de errores. Ejecuta en hilo principal.
forAllLoadedChunks()
Procesa chunks cargados (ejecuta asíncrono por defecto).
ticksToMs() / msToTicks()
Conversión de tiempo optimizada (multiplicación por 50L).
executeIfLoaded()
Ejecuta solo si el chunk de la ubicación está cargado.
Manejo de Entidades
Siempre usa .with(entity) para operaciones con entidades en Folia. Garantiza ejecución en el scheduler correcto y previene race conditions.
Ejemplo: Actualización Segura
MagmaLib.task(() -> {
if (!entity.isValid() || entity.isDead()) return;
// Operaciones seguras con la entidad
entity.setCustomName("§aProcesado");
entity.setGlowing(true);
})
.with(entity) // Routing correcto en Folia
.cancelIf(() -> !entity.isValid()) // Cancelación automática
.handleException(e -> getLogger().warning("Error: " + e.getMessage()))
.run();
Modo Alto Rendimiento (⚠️)
// Solo si garantizas entity.isValid() == true
for (Entity entity : entities) {
MagmaLib.task(() -> {
entity.setCustomName("Updated");
})
.with(entity)
.unsafe() // ⚠️ Omite isValid() check
.run();
}
Compatibilidad
| Característica | 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.0 | ✅ Funcional | ✅ Funcional |
runDirectAt() | ✅ Main Thread | ✅ RegionScheduler |
runDirectWith() | ✅ Main Thread | ✅ EntityScheduler |
cancelIfUnloaded | ✅ Verificación | ✅ Verificación |
handleException | ✅ Try-catch | ✅ Try-catch |
named() v2.0 | ✅ Logs con contexto | ✅ Logs con contexto |
Nota: MagmaLib detecta automáticamente el tipo de servidor y usa el scheduler apropiado. Tu código funciona en ambos sin modificaciones.
Rendimiento Comparado
Benchmark: 10,000 iteraciones de tareas simples en servidor de prueba (Paper 1.21.4, Java 21).
| Escenario | FoliaLib | MagmaLib v1.2 | MagmaLib v2.0 | Mejora |
|---|---|---|---|---|
| Tarea global simple | 1.2 ms | 1.0 ms | 0.9 ms | +25% |
| Tarea en región | 1.5 ms | 1.2 ms | 1.1 ms | +27% |
| Tarea con composición | N/A | 2.8 ms* | 2.1 ms | +25% |
| Bucle con validaciones | 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 con CompletableFuture manual; v2.0 con composición nativa integrada
Conclusión: MagmaLib v2.0 es ~25-47% más rápido que alternativas en hot paths, con composición type-safe integrada y cero overhead adicional.
Preguntas Frecuentes
¿Mi código de v1.x funcionará en v2.0?
✅ Sí, 100% compatible. Todas las APIs de v1.x funcionan sin cambios. Las nuevas features de composición son opcionales y se activan usando task(Supplier<T>) en lugar de task(Runnable).
¿Cuándo usar thenApply vs thenAccept?
thenApply: Cuando necesitas transformar el valor y continuar el pipeline (retorna nuevo tipo). thenAccept: Cuando quieres consumir el valor final para un efecto secundario (no retorna valor, finaliza el pipeline).
¿Es seguro usar .unsafe()?
Sí, si sigues estas reglas:
- ✅ Valida manualmente antes de llamar
- ✅ Úsalo solo en código que controlas totalmente
- ✅ Documenta por qué es seguro en ese contexto
- ❌ Nunca lo uses con inputs de usuario o datos externos
¿Funciona en Paper y Folia?
✅ Sí, 100% compatible. MagmaLib detecta automáticamente el tipo de servidor y usa el scheduler apropiado:
- Folia: RegionScheduler, EntityScheduler, GlobalRegionScheduler
- Paper/Spigot: Bukkit Scheduler tradicional
Tu código no necesita cambios para funcionar en ambos.
¿Cómo debuggear tareas con named()?
Los logs de error incluirán automáticamente:
- 🏷️ Nombre de la tarea:
['MiTarea'] - 📍 Ubicación:
location=world@x,y,z - 👤 Entidad:
entity=Player[Notch] - 📋 Stack trace de creación (si habilitas debug)
¿Cómo instalar v2.0 ahora?
Mientras JitPack no esté disponible:
- Descarga
MagmaLib.javadel repositorio - Cópialo en tu plugin:
src/main/java/tu/paquete/MagmaLib.java - ¡Listo! Sin dependencias externas
Próximamente: jitpack.io/#MagmaEnginers/MagmaLib