How to create an internal and an external extension.
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:
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
}
}
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.
Here's a simple extension that adds invisibility to fake players:
@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) {}
}
If your extension requires other extensions to work:
@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
}
Extension Lifecycle
Every internal extension now follows a lifecycle:
onLoad(): Initial setup, resource loading
onEnable(): Start tasks, register listeners
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:
public class MyExtension implements FakePlayerSpawn, FakePlayerTick, ExtensionLifecycle {
// Implementation
}
3. Implement Lifecycle Methods
@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
@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:
Scan for .jar files
Load classes implementing our interfaces
Initialize them in proper order based on dependencies
Best Practices
1. Resource Management
Always clean up resources in onDisable():
@Override
public void onDisable() {
tasks.forEach(BukkitTask::cancel);
handlers.clear();
// etc...
}
2. Error Handling
Use try-catch blocks for robust error handling:
try {
// Your code
} catch (Exception e) {
plugin.getLogger().severe("Error in extension: " + e.getMessage());
e.printStackTrace();
}
3. Version Compatibility
Always specify version compatibility:
@Version(MinecraftVersion.ALL) // or specific versions
public class MyExtension {
// Your code
}
4. Dependency Management
If your extension requires other extensions:
@ExtensionDependency({
"com.example.RequiredExtension1",
"com.example.RequiredExtension2"
})
public class MyExtension {
// Your code
}
Common Extension Types
1. Behavior Extensions
Add new behaviors to fake players:
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:
public class SpawnEffectExtension implements FakePlayerSpawn {
@Override
public void onSpawnFakePlayerNMS(FakePlayerEntity player, JavaPlugin plugin) {
// Add spawn effects, similar as AsteroidInvisibility
}
}
Testing Extensions
Place your extension .jar in plugins/Asteroid/Extensions