Zum Inhalt

Troubleshooting

Soluciones a problemas comunes en el desarrollo de plugins.

Problemas de Carga

Plugin no aparece en los logs

Síntomas: - No aparece mensaje [EarlyPlugin] Found: mi-plugin.jar - El servidor inicia sin errores

Causas: 1. JAR no está en earlyplugins/ 2. Nombre de directorio incorrecto

Solución:

# Verificar ubicación
ls earlyplugins/
# Debe mostrar: mi-plugin.jar

# Verificar que es el directorio correcto
pwd
# Debe estar en la raíz del servidor

Transformer no se registra

Síntomas: - JAR aparece en logs pero no el transformer - No se ejecuta la transformación

Causas: 1. Archivo de servicio faltante o mal ubicado 2. Nombre de clase incorrecto en el archivo de servicio

Solución:

# Verificar contenido del JAR
jar tf mi-plugin.jar | grep META-INF

# Debe mostrar:
# META-INF/services/com.hypixel.hytale.plugin.early.ClassTransformer

# Verificar contenido del archivo de servicio
unzip -p mi-plugin.jar META-INF/services/com.hypixel.hytale.plugin.early.ClassTransformer

# Debe mostrar el nombre completo de tu clase

Errores de Compilación

ClassNotFoundException durante compilación

Error:

error: cannot find symbol
class ClassTransformer

Solución:

// Agregar a build.gradle
dependencies {
    compileOnly files('libs/hytale-server.jar')
}

Dependencia no encontrada

Error:

Could not resolve: org.ow2.asm:asm:9.5

Solución:

repositories {
    mavenCentral()  // Asegurar que está presente
}

Errores de Ejecución

ClassNotFoundException en runtime

Error:

java.lang.ClassNotFoundException: org.objectweb.asm.ClassVisitor

Causa: Dependencias no incluidas en el JAR

Solución:

jar {
    from {
        configurations.runtimeClasspath.collect {
            it.isDirectory() ? it : zipTree(it)
        }
    }
    duplicatesStrategy = DuplicatesStrategy.EXCLUDE
}

VerifyError

Error:

java.lang.VerifyError: Bad instruction

Causa: Bytecode transformado es inválido

Solución:

// Usar COMPUTE_FRAMES
ClassWriter writer = new ClassWriter(
    reader,
    ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS
);

// Verificar bytecode antes de retornar
CheckClassAdapter.verify(
    new ClassReader(transformed),
    false,
    new PrintWriter(System.err)
);

NoSuchMethodError

Error:

java.lang.NoSuchMethodError: com.hypixel.hytale.server.Method()V

Causa: Incompatibilidad de versiones o método no existe

Solución: 1. Verificar versión de Hytale 2. Verificar que el método existe en la clase objetivo 3. Revisar firma del método (descriptor)

Problemas de Transformación

Transformación no tiene efecto

Síntomas: - El código de transformación se ejecuta - Pero no hay cambios visibles en el juego

Diagnóstico:

@Override
public byte[] transform(String className, String classPath, byte[] bytecode) {
    System.out.println("[Debug] Evaluando: " + className);

    if (shouldTransform(className)) {
        System.out.println("[Debug] Transformando: " + className);
        byte[] result = doTransform(bytecode);
        System.out.println("[Debug] Bytecode modificado: " + (result != bytecode));
        return result;
    }

    return null;
}

Soluciones: 1. Verificar que retornas el bytecode modificado (no null) 2. Verificar que la clase objetivo es correcta 3. Verificar que el método objetivo existe y tiene el descriptor correcto

Clase ya cargada antes de transformación

Síntomas: - El transformer se ejecuta - Pero la clase no se transforma

Causa: La clase se cargó antes de que el transformer estuviera activo

Solución: - Aumentar la prioridad del transformer - Verificar que no hay referencias tempranas a la clase

Problemas de Rendimiento

Servidor tarda mucho en iniciar

Causa: Transformación de muchas clases

Solución:

// Filtrar agresivamente
if (!className.startsWith("com.hypixel.hytale.")) {
    return null; // Ignorar clases externas
}

// Usar Set para búsqueda O(1)
private static final Set<String> TARGET_CLASSES = Set.of(
    "com.hypixel.hytale.server.core.HytaleServer",
    "com.hypixel.hytale.server.core.entity.LivingEntity"
);

if (!TARGET_CLASSES.contains(className)) {
    return null;
}

Alto uso de memoria

Causa: Muchos metadatos almacenados

Solución:

// Limpiar metadatos no usados
entity.removeMetaObject(TEMP_DATA);

// Usar datos temporales en lugar de persistentes
MetaKey<Object> TEMP = registry.registerMetaObject(
    e -> null  // No persistente
);

Problemas de Meta System

Datos no persisten

Síntomas: - Datos se pierden al reiniciar servidor

Causa: MetaKey no es persistente

Solución:

// Cambiar a persistente
MetaKey<Integer> SCORE = registry.registerMetaObject(
    p -> 0,
    true,              // ← Persistente
    "player_score",    // ← Nombre único
    Codec.INT          // ← Codec
);

NullPointerException al obtener metadato

Error:

java.lang.NullPointerException
    at MyPlugin.getData(MyPlugin.java:42)

Causa: Usar getIfPresentMetaObject sin verificar null

Solución:

// Opción 1: Usar getMetaObject (nunca null)
int value = entity.getMetaObject(MY_KEY);

// Opción 2: Verificar null con getIfPresentMetaObject
Integer value = entity.getIfPresentMetaObject(MY_KEY);
if (value != null) {
    // Usar value
}

Debugging Tips

Habilitar Logging Detallado

public class DebugTransformer implements ClassTransformer {
    private static final boolean DEBUG = true;

    private void log(String message) {
        if (DEBUG) {
            System.out.println("[Debug] " + message);
        }
    }

    @Override
    public byte[] transform(String className, String classPath, byte[] bytecode) {
        log("Evaluando: " + className);

        if (shouldTransform(className)) {
            log("Transformando: " + className);
            log("Bytecode size: " + bytecode.length);

            byte[] result = doTransform(bytecode);

            log("Transformación completa");
            log("New bytecode size: " + result.length);

            return result;
        }

        return null;
    }
}

Imprimir Bytecode Transformado

import org.objectweb.asm.util.TraceClassVisitor;
import java.io.PrintWriter;

byte[] transformed = transform(bytecode);

// Imprimir bytecode legible
ClassReader reader = new ClassReader(transformed);
TraceClassVisitor printer = new TraceClassVisitor(new PrintWriter(System.out));
reader.accept(printer, 0);

Verificar Bytecode Válido

import org.objectweb.asm.util.CheckClassAdapter;

byte[] transformed = transform(bytecode);

try {
    CheckClassAdapter.verify(
        new ClassReader(transformed),
        false,
        new PrintWriter(System.err)
    );
    System.out.println("[Debug] Bytecode válido");
} catch (Exception e) {
    System.err.println("[Error] Bytecode inválido: " + e.getMessage());
    return null; // Retornar original
}

return transformed;

Obtener Ayuda

Si los problemas persisten:

  1. Revisar logs completos del servidor
  2. Buscar el error específico en la documentación
  3. Crear caso de prueba mínimo que reproduzca el problema
  4. Revisar ejemplos en la documentación

Recursos Adicionales

  • FAQ: Preguntas frecuentes
  • Glosario: Términos técnicos
  • Ejemplos: Código de ejemplo funcional

¿Encontraste otro problema? Contribuye a esta documentación agregándolo.