# Early Plugin System (Class Transformation)

The Early Plugin System allows advanced mods to transform Java bytecode before classes are loaded. This is a powerful feature for core modifications that cannot be achieved through the standard plugin API.

## Overview

Early plugins are loaded before the main server initialization and can:

- Transform class bytecode
- Modify method implementations
- Add fields or methods to existing classes
- Inject custom behavior into core systems

**Warning**: This is an advanced feature. Incorrect use can break the server or cause incompatibilities with other mods.

## When to Use Early Plugins

Early plugins are appropriate when:

- You need to modify core server behavior not exposed through APIs
- You need to patch bugs or security issues
- You're implementing advanced hooks not available through events
- Standard plugin APIs don't provide sufficient access

**Prefer standard plugins** when possible. Early plugins:
- Are harder to maintain across server updates
- May conflict with other early plugins
- Can introduce hard-to-debug issues

## Creating an Early Plugin

### Project Structure

```
my-early-plugin/
├── src/
│   └── main/
│       ├── java/
│       │   └── com/example/
│       │       └── MyTransformer.java
│       └── resources/
│           └── META-INF/
│               └── services/
│                   └── com.hypixel.hytale.plugin.early.ClassTransformer
└── build.gradle
```

### Implementing ClassTransformer

```java
package com.example;

import com.hypixel.hytale.plugin.early.ClassTransformer;

public class MyTransformer implements ClassTransformer {
    
    @Override
    public int priority() {
        // Higher priority = loaded first
        // Use 0 for normal priority
        return 0;
    }
    
    @Override
    public byte[] transform(String className, String transformedName, byte[] classBytes) {
        // Return null to skip transformation
        // Return modified bytes to transform
        
        // Only transform specific classes
        if (!className.equals("com/hypixel/hytale/server/core/SomeClass")) {
            return null;
        }
        
        // Use ASM or similar library to transform bytecode
        return transformClass(classBytes);
    }
    
    private byte[] transformClass(byte[] classBytes) {
        // Bytecode transformation logic
        // Use ASM, Javassist, etc.
        return classBytes;
    }
}
```

### Service Registration

Create `META-INF/services/com.hypixel.hytale.plugin.early.ClassTransformer`:

```
com.example.MyTransformer
```

### Deployment

Place the compiled JAR in the server's `earlyplugins/` directory.

## Bytecode Transformation with ASM

### Basic ASM Example

```java
import org.objectweb.asm.*;

public class MyTransformer implements ClassTransformer {
    
    @Override
    public byte[] transform(String className, String transformedName, byte[] classBytes) {
        if (!className.equals("com/hypixel/hytale/target/TargetClass")) {
            return null;
        }
        
        ClassReader reader = new ClassReader(classBytes);
        ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
        ClassVisitor visitor = new MyClassVisitor(writer);
        
        reader.accept(visitor, 0);
        return writer.toByteArray();
    }
}

class MyClassVisitor extends ClassVisitor {
    
    public MyClassVisitor(ClassVisitor cv) {
        super(Opcodes.ASM9, cv);
    }
    
    @Override
    public MethodVisitor visitMethod(int access, String name, String descriptor,
                                     String signature, String[] exceptions) {
        MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);
        
        if (name.equals("targetMethod")) {
            return new MyMethodVisitor(mv);
        }
        
        return mv;
    }
}

class MyMethodVisitor extends MethodVisitor {
    
    public MyMethodVisitor(MethodVisitor mv) {
        super(Opcodes.ASM9, mv);
    }
    
    @Override
    public void visitCode() {
        super.visitCode();
        // Inject code at method start
        mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "out", 
                          "Ljava/io/PrintStream;");
        mv.visitLdcInsn("Method called!");
        mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println",
                           "(Ljava/lang/String;)V", false);
    }
}
```

### Adding Method Hooks

```java
class HookMethodVisitor extends MethodVisitor {
    private final String hookClass;
    private final String hookMethod;
    
    public HookMethodVisitor(MethodVisitor mv, String hookClass, String hookMethod) {
        super(Opcodes.ASM9, mv);
        this.hookClass = hookClass;
        this.hookMethod = hookMethod;
    }
    
    @Override
    public void visitCode() {
        super.visitCode();
        
        // Call hook at method start
        mv.visitMethodInsn(Opcodes.INVOKESTATIC, hookClass, hookMethod, "()V", false);
    }
    
    @Override
    public void visitInsn(int opcode) {
        // Inject before return
        if (opcode >= Opcodes.IRETURN && opcode <= Opcodes.RETURN) {
            mv.visitMethodInsn(Opcodes.INVOKESTATIC, hookClass, hookMethod + "End", "()V", false);
        }
        super.visitInsn(opcode);
    }
}
```

### Modifying Method Parameters

```java
class ParameterModifierVisitor extends MethodVisitor {
    
    public ParameterModifierVisitor(MethodVisitor mv) {
        super(Opcodes.ASM9, mv);
    }
    
    @Override
    public void visitCode() {
        super.visitCode();
        
        // Modify first parameter (index 1 for instance methods, 0 for static)
        // Load parameter, modify, store back
        mv.visitVarInsn(Opcodes.ALOAD, 1);  // Load first object parameter
        mv.visitMethodInsn(Opcodes.INVOKESTATIC, "com/example/Hooks", "modifyParam",
                           "(Ljava/lang/Object;)Ljava/lang/Object;", false);
        mv.visitVarInsn(Opcodes.ASTORE, 1);  // Store modified value
    }
}
```

## Priority System

Transformer priority determines load order:

```java
@Override
public int priority() {
    return 100;  // Higher = loaded first
}
```

| Priority | Use Case |
|----------|----------|
| 1000+ | Critical patches (security fixes) |
| 100-999 | Core modifications |
| 0 | Standard transformations |
| -100 to -1 | Post-processing |

## Compatibility Considerations

### Class Name Handling

```java
@Override
public byte[] transform(String className, String transformedName, byte[] classBytes) {
    // className: Internal name (com/example/MyClass)
    // transformedName: May differ if class was renamed
    
    // Always use transformedName for matching
    if (!transformedName.equals("com.hypixel.hytale.target.TargetClass")) {
        return null;
    }
    
    return transformBytes(classBytes);
}
```

### Version Checking

```java
public class MyTransformer implements ClassTransformer {
    
    private static final String TARGET_VERSION = "1.0.0";
    
    @Override
    public byte[] transform(String className, String transformedName, byte[] classBytes) {
        // Check server version compatibility
        if (!isCompatibleVersion()) {
            System.err.println("MyTransformer incompatible with this server version");
            return null;
        }
        
        return doTransform(classBytes);
    }
    
    private boolean isCompatibleVersion() {
        // Check against known compatible versions
        return true;
    }
}
```

### Chaining with Other Transformers

```java
@Override
public byte[] transform(String className, String transformedName, byte[] classBytes) {
    // Be careful not to break other transformers
    // Avoid removing methods/fields other transformers may depend on
    // Add, don't remove when possible
    
    return safeTransform(classBytes);
}
```

## Debugging Early Plugins

### Logging

```java
public class MyTransformer implements ClassTransformer {
    
    private static final boolean DEBUG = Boolean.getBoolean("mymod.debug");
    
    @Override
    public byte[] transform(String className, String transformedName, byte[] classBytes) {
        if (DEBUG) {
            System.out.println("[MyTransformer] Processing: " + transformedName);
        }
        
        byte[] result = doTransform(classBytes);
        
        if (DEBUG && result != null) {
            System.out.println("[MyTransformer] Transformed: " + transformedName);
        }
        
        return result;
    }
}
```

### Bytecode Verification

```java
private byte[] transformWithVerification(byte[] classBytes) {
    try {
        byte[] transformed = doTransform(classBytes);
        
        // Verify bytecode is valid
        ClassReader verifyReader = new ClassReader(transformed);
        ClassWriter verifyWriter = new ClassWriter(0);
        verifyReader.accept(new CheckClassAdapter(verifyWriter), 0);
        
        return transformed;
    } catch (Exception e) {
        System.err.println("Transformation produced invalid bytecode: " + e.getMessage());
        return null;  // Return null to skip transformation
    }
}
```

### Dumping Transformed Classes

```java
@Override
public byte[] transform(String className, String transformedName, byte[] classBytes) {
    byte[] result = doTransform(classBytes);
    
    if (result != null && Boolean.getBoolean("mymod.dumpClasses")) {
        try {
            Path dumpPath = Path.of("transformed", transformedName.replace('.', '/') + ".class");
            Files.createDirectories(dumpPath.getParent());
            Files.write(dumpPath, result);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    
    return result;
}
```

## Common Transformation Patterns

### Adding a Field

```java
class AddFieldVisitor extends ClassVisitor {
    
    public AddFieldVisitor(ClassVisitor cv) {
        super(Opcodes.ASM9, cv);
    }
    
    @Override
    public void visitEnd() {
        // Add field before class end
        FieldVisitor fv = cv.visitField(
            Opcodes.ACC_PUBLIC,
            "myCustomField",
            "Ljava/lang/Object;",
            null,
            null
        );
        if (fv != null) {
            fv.visitEnd();
        }
        
        super.visitEnd();
    }
}
```

### Redirecting Method Calls

```java
class RedirectMethodVisitor extends MethodVisitor {
    
    public RedirectMethodVisitor(MethodVisitor mv) {
        super(Opcodes.ASM9, mv);
    }
    
    @Override
    public void visitMethodInsn(int opcode, String owner, String name, 
                                String descriptor, boolean isInterface) {
        // Redirect specific method call
        if (owner.equals("com/hypixel/hytale/Original") && name.equals("oldMethod")) {
            super.visitMethodInsn(opcode, "com/example/Replacement", "newMethod",
                                  descriptor, isInterface);
        } else {
            super.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
        }
    }
}
```

## Best Practices

1. **Minimize transformations** - Only transform what's absolutely necessary
2. **Use priorities wisely** - Don't use high priority without good reason
3. **Handle errors gracefully** - Return null on failure, don't crash
4. **Log transformations** - Provide debug logging for troubleshooting
5. **Document changes** - Clearly document what your transformer modifies
6. **Test thoroughly** - Test with different server versions
7. **Check compatibility** - Verify compatibility with other known early plugins
8. **Version your transformer** - Track which server versions are supported
9. **Provide fallbacks** - If transformation fails, the mod should degrade gracefully
10. **Keep it simple** - Complex transformations are hard to maintain

## Security Considerations

- Early plugins have full access to server internals
- Malicious early plugins could compromise server security
- Only use early plugins from trusted sources
- Review transformer code before deployment
- Monitor for unexpected behavior after installing early plugins
