# Extension Development Guide

### Overview

AsteroidAPI allows you to create powerful extensions that enhance Asteroid's functionality. With our new extension system, you can create both external and internal extensions with proper lifecycle management, dependency handling, and type-safe operations.

### Extension Types

**External Extensions**

If you're creating an external extension, you won't need to use any of our internal-specified load classes. For example, to build a simple knockback extension that enables punching fake players with knockback, follow the code below:

{% tabs %}
{% tab title="Main Extension" %}
{% code overflow="wrap" %}

```java
final class KnockbackExtension extends JavaPlugin {
    public static double knockbackMultiplication;
    public static double onGroundAdd;

    @Override
    public void onEnable() {
        Plugin plugin = getServer().getPluginManager().getPlugin("Asteroid");
        knockbackMultiplication = plugin.getConfig().getDouble("knockback.multiplication");
        onGroundAdd = plugin.getConfig().getDouble("knockback.on_ground_add");
        plugin.getServer().getPluginManager().registerEvents(new KnockbackEvent(), plugin);
    }

    @Override
    public void onDisable() {
        // Plugin shutdown logic
    }
}
```

{% endcode %}
{% endtab %}

{% tab title="Knockback Event" %}
{% code overflow="wrap" %}

```java
public class KnockbackEvent implements Listener {
    public final FakePlayerRegistry fakePlayerRegistry;

    public KnockbackEvent() {
        fakePlayerRegistry = new FakePlayerRegistry();
    }

    @EventHandler
    public void entityDamage(EntityDamageByEntityEvent event) {
        Entity attacker = event.getDamager();
        Entity victim = event.getEntity();
        double damage = event.getDamage();

        if (attacker instanceof Player && fakePlayerRegistry.isFakePlayer(victim)) {
            FakePlayer fakePlayer = fakePlayerRegistry.getFakePlayer(victim);
            
            Location attackerLoc = attacker.getLocation();
            Location victimLoc = victim.getLocation();
            
            double dX = victimLoc.getX() - attackerLoc.getX();
            double dZ = victimLoc.getZ() - attackerLoc.getZ();
            
            double distance = Math.sqrt(dX * dX + dZ * dZ);
            if (distance > 0) {
                dX = dX / distance;
                dZ = dZ / distance;
            }
            
            double knockbackForce = damage * KnockbackExtension.knockbackMultiplication;
            if (victim.isOnGround()) {
                knockbackForce += KnockbackExtension.onGroundAdd;
            }
            
            Vector velocity = new Vector(dX * knockbackForce, 0.4, dZ * knockbackForce);
            fakePlayer.setVelocity(velocity);
        }
    }
}
```

{% endcode %}
{% endtab %}
{% endtabs %}

This code demonstrates how to create a simple external extension.

#### Internal Extensions

Internal extensions run within Asteroid itself and are loaded through our extension system. They're perfect for adding behaviors to fake players or modifying core functionality.

{% tabs %}
{% tab title="Basic Extension" %}
Here's a simple extension that adds invisibility to fake players:

{% code overflow="wrap" %}

```java
@Version(MinecraftVersion.ALL)
public class Invis implements FakePlayerSpawn, ExtensionLifecycle {
    private final Random rand = new Random();
    private Plugin plugin;
    
    private final int duration = 5;
    private final int secondsUntilWearsOut = 5;
    private final int chance = 50;

    @Override
    public void onLoad() {
        this.plugin = Bukkit.getPluginManager().getPlugin("Asteroid");
        System.out.println("Invisibility Extension - Loaded");
    }

    @Override
    public void onEnable() {}

    @Override
    public void onDisable() {}

    @Override
    public void onSpawnFakePlayerNMS(FakePlayerEntity fakePlayerEntity, JavaPlugin instance) {
        Player fakePlayer = fakePlayerEntity.getEntityPlayer();

        int randomNumber = rand.nextInt(101);
        if (chance < randomNumber) return;

        Bukkit.getScheduler().runTask(instance, () -> {
            fakePlayer.addPotionEffect(new PotionEffect(
                PotionEffectType.INVISIBILITY,
                duration == -1 ? -1 : 20 * duration,
                0,
                false,
                false
            ));
            fakePlayer.setInvisible(true);
        });

        if (secondsUntilWearsOut != -1) {
            Bukkit.getScheduler().runTaskLater(instance, () -> {
                fakePlayer.setInvisible(false);
            }, 20L * secondsUntilWearsOut);
        }
    }

    @Override
    public void onSpawnFakePlayerAfterLoad(FakePlayerEntity fakePlayerEntity, JavaPlugin instance) {}
}
```

{% endcode %}
{% endtab %}

{% tab title="With Dependencies" %}
If your extension requires other extensions to work:

```java
@Version(MinecraftVersion.ALL)
@ExtensionDependency({"com.example.RequiredExtension"})
public class DependentExtension implements FakePlayerSpawn, ExtensionLifecycle {
    @Override
    public void onLoad() {
        // Will only load if RequiredExtension is present
    }
    
    // ... rest of implementation
}
```

{% endtab %}
{% endtabs %}

#### Extension Lifecycle

Every internal extension now follows a lifecycle:

1. `onLoad()`: Initial setup, resource loading
2. `onEnable()`: Start tasks, register listeners
3. `onDisable()`: Cleanup resources

#### Interfaces

The main interfaces you can implement:

* `ExtensionLifecycle`: Base interface for lifecycle management
* `FakePlayerSpawn`: Handle fake player spawn events
* `FakePlayerTick`: Handle fake player tick events
* `Version`: Annotation for version compatibility

#### 2. Choose Your Interfaces

Decide which interfaces your extension needs to implement based on what you want to do:

```java
public class MyExtension implements FakePlayerSpawn, FakePlayerTick, ExtensionLifecycle {
    // Implementation
}
```

#### 3. Implement Lifecycle Methods

```java
@Override
public void onLoad() {
    // Load resources, initialize variables
}

@Override
public void onEnable() {
    // Start tasks, register events
}

@Override
public void onDisable() {
    // Cleanup
}
```

#### 4. Add Version Compatibility

```java
@Version(MinecraftVersion.ALL)  // or specific versions
public class MyExtension {
    // Your extension code
}
```

### Extension Management

#### Loading Extensions

Extensions are loaded automatically from the `plugins/Asteroid/Extensions` folder. The system will:

1. Scan for .jar files
2. Load classes implementing our interfaces
3. Initialize them in proper order based on dependencies

### Best Practices

#### 1. Resource Management

Always clean up resources in onDisable():

```java
@Override
public void onDisable() {
    tasks.forEach(BukkitTask::cancel);
    handlers.clear();
    // etc...
}
```

#### 2. Error Handling

Use try-catch blocks for robust error handling:

```java
try {
    // Your code
} catch (Exception e) {
    plugin.getLogger().severe("Error in extension: " + e.getMessage());
    e.printStackTrace();
}
```

#### 3. Version Compatibility

Always specify version compatibility:

```java
@Version(MinecraftVersion.ALL)  // or specific versions
public class MyExtension {
    // Your code
}
```

#### 4. Dependency Management

If your extension requires other extensions:

```java
@ExtensionDependency({
    "com.example.RequiredExtension1",
    "com.example.RequiredExtension2"
})
public class MyExtension {
    // Your code
}
```

### Common Extension Types

#### 1. Behavior Extensions

Add new behaviors to fake players:

```java
public class BehaviorExtension implements FakePlayerTick {
    @Override
    public void individualPlayerTick(FakePlayerEntity player, JavaPlugin plugin) {
        // Add custom behavior, whatever you want that can be ticked
    }
}
```

#### 2. Spawn Effect Extensions

Add effects when fake players spawn:

```java
public class SpawnEffectExtension implements FakePlayerSpawn {
    @Override
    public void onSpawnFakePlayerNMS(FakePlayerEntity player, JavaPlugin plugin) {
        // Add spawn effects, similar as AsteroidInvisibility
    }
}
```

### Testing Extensions

1. Place your extension .jar in `plugins/Asteroid/Extensions`
2. Start the server
3. Check console for loading messages
4. Test functionality
5. Check logs for any errors

### Common Issues and Solutions

#### Extension Not Loading

* Check if jar is in correct folder
* Verify interfaces are implemented correctly
* Check version compatibility
* Look for errors in console

#### Dependency Issues

* Ensure required extensions are present
* Check dependency load order
* Verify version compatibility of dependencies


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.spoof.dev/asteroid/api/usage/extension-development-guide.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
