# Plugin Development

## Plugin Structure

A Hytale plugin consists of:

1. A main class extending `JavaPlugin`
2. A `manifest.json` file with plugin metadata
3. Optional asset packs for client-side resources

## The Plugin Manifest

The `manifest.json` file must be placed at the root of your JAR:

```json
{
    "Group": "com.example",
    "Name": "MyPlugin",
    "Version": "1.0.0",
    "Description": "A description of what this plugin does",
    "Authors": ["Author1", "Author2"],
    "Website": "https://example.com",
    "Main": "com.example.MyPlugin",
    "ServerVersion": ">=1.0.0",
    "Dependencies": {
        "com.other:OtherPlugin": ">=2.0.0"
    },
    "OptionalDependencies": {
        "com.soft:SoftDep": ">=1.0.0"
    },
    "LoadBefore": ["com.load:BeforeThis"],
    "DisabledByDefault": false,
    "IncludesAssetPack": true
}
```

### Manifest Fields

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `Group` | String | Yes | Maven-style group ID (e.g., `com.example`) |
| `Name` | String | Yes | Plugin name (unique identifier within group) |
| `Version` | String | Yes | Semantic version (e.g., `1.0.0`) |
| `Description` | String | No | Brief description of the plugin |
| `Authors` | String[] | No | List of author names |
| `Website` | String | No | Plugin website or repository URL |
| `Main` | String | Yes | Fully qualified main class name |
| `ServerVersion` | String | No | Required server version range |
| `Dependencies` | Object | No | Required plugin dependencies with version ranges |
| `OptionalDependencies` | Object | No | Optional plugin dependencies |
| `LoadBefore` | String[] | No | Plugins that should load after this one |
| `DisabledByDefault` | Boolean | No | If true, plugin must be explicitly enabled |
| `IncludesAssetPack` | Boolean | No | If true, plugin includes client assets |

### Plugin Identifier

Plugins are identified by `Group:Name` format (e.g., `com.example:MyPlugin`). This identifier is used for:
- Dependency resolution
- Permission namespacing (`com.example.myplugin.*`)
- Configuration keys

## The JavaPlugin Class

```java
package com.example;

import com.hypixel.hytale.server.core.plugin.JavaPlugin;
import com.hypixel.hytale.server.core.plugin.java.JavaPluginInit;

public class MyPlugin extends JavaPlugin {
    
    private Config<MyConfig> config;
    
    public MyPlugin(JavaPluginInit init) {
        super(init);
        // Initialize config BEFORE setup() is called
        this.config = withConfig(MyConfig.CODEC);
    }
    
    @Override
    protected void setup() {
        // Register all components during setup
        registerCommands();
        registerEvents();
        registerEntities();
    }
    
    @Override
    protected void start() {
        // Plugin is now active
        // Access other plugins, start services, etc.
    }
    
    @Override
    protected void shutdown() {
        // Clean up resources
        // Save data, close connections, etc.
    }
    
    private void registerCommands() {
        getCommandRegistry().registerCommand(new MyCommand());
    }
    
    private void registerEvents() {
        getEventRegistry().register(PlayerConnectEvent.class, this::onPlayerConnect);
    }
    
    private void registerEntities() {
        getEntityRegistry().register("customEntity", CustomEntity.class, CustomEntity::new);
    }
    
    private void onPlayerConnect(PlayerConnectEvent event) {
        getLogger().info("Player connected: " + event.getPlayer().getName());
    }
}
```

## Plugin Lifecycle States

```
NONE -> SETUP -> START -> ENABLED -> SHUTDOWN -> DISABLED
```

| State | Description |
|-------|-------------|
| `NONE` | Initial state before any lifecycle methods |
| `SETUP` | `setup()` is executing; register components here |
| `START` | `start()` is executing; plugin becoming active |
| `ENABLED` | Plugin is fully operational and handling events |
| `SHUTDOWN` | `shutdown()` is executing; cleanup in progress |
| `DISABLED` | Plugin is fully disabled and unloaded |

## Configuration

### Defining a Configuration Class

```java
public class MyConfig {
    public static final Codec<MyConfig> CODEC = BuilderCodec.of(MyConfig::new)
        .with("enabled", Codec.BOOLEAN, c -> c.enabled, true)
        .with("maxPlayers", Codec.INTEGER, c -> c.maxPlayers, 100)
        .with("welcomeMessage", Codec.STRING, c -> c.welcomeMessage, "Welcome!")
        .build();
    
    public final boolean enabled;
    public final int maxPlayers;
    public final String welcomeMessage;
    
    private MyConfig(boolean enabled, int maxPlayers, String welcomeMessage) {
        this.enabled = enabled;
        this.maxPlayers = maxPlayers;
        this.welcomeMessage = welcomeMessage;
    }
}
```

### Using Configuration

```java
public class MyPlugin extends JavaPlugin {
    private Config<MyConfig> config;
    
    public MyPlugin(JavaPluginInit init) {
        super(init);
        this.config = withConfig(MyConfig.CODEC);
    }
    
    @Override
    protected void setup() {
        MyConfig cfg = config.get();
        if (cfg.enabled) {
            getLogger().info("Max players: " + cfg.maxPlayers);
        }
    }
}
```

Configuration is stored in the server's config under your plugin identifier.

## Available Registries

### CommandRegistry

Register custom commands:

```java
getCommandRegistry().registerCommand(new MyCommand());
```

See [Command System](04-command-system.md) for details.

### EventRegistry

Register event listeners:

```java
// Simple registration
getEventRegistry().register(PlayerConnectEvent.class, this::onConnect);

// With priority
getEventRegistry().register(EventPriority.EARLY, PlayerConnectEvent.class, this::onConnect);

// Global listener (all events of type)
getEventRegistry().registerGlobal(EntityEvent.class, this::onAnyEntity);

// Async event
getEventRegistry().registerAsync(AsyncEvent.class, this::onAsync);
```

See [Event System](03-event-system.md) for details.

### EntityRegistry

Register custom entity types:

```java
getEntityRegistry().register("myEntity", MyEntity.class, MyEntity::new);

// With serialization codec
getEntityRegistry().register("myEntity", MyEntity.class, MyEntity::new, MyEntity.CODEC);
```

### BlockStateRegistry

Register custom block states:

```java
getBlockStateRegistry().register("myBlock", MyBlockState.class);
```

### TaskRegistry

Schedule recurring tasks:

```java
getTaskRegistry().register(new MyTask());
```

### AssetRegistry

Register custom asset types:

```java
getAssetRegistry().register(MyAsset.class, MyAsset.CODEC);
```

### ClientFeatureRegistry

Register client-side features:

```java
getClientFeatureRegistry().register("myFeature", MyFeature.class);
```

## Permissions

Plugins automatically receive a base permission derived from their identifier:

```
{group}.{name} -> com.example.myplugin
```

Commands registered by your plugin will have permissions under:

```
{basePermission}.command.{commandName}
```

For example, if your plugin is `com.example:MyPlugin` and you register a command `spawn`:
- Base permission: `com.example.myplugin`
- Command permission: `com.example.myplugin.command.spawn`

## Logging

Use the built-in logger:

```java
getLogger().info("Information message");
getLogger().warn("Warning message");
getLogger().error("Error message");
getLogger().debug("Debug message");
```

## Dependencies

### Hard Dependencies

Hard dependencies must be present for your plugin to load:

```json
{
    "Dependencies": {
        "com.example:RequiredPlugin": ">=1.0.0"
    }
}
```

### Optional Dependencies

Optional dependencies are loaded if present but not required:

```json
{
    "OptionalDependencies": {
        "com.example:OptionalPlugin": ">=1.0.0"
    }
}
```

Check if optional dependency is loaded:

```java
@Override
protected void setup() {
    if (getPluginManager().isPluginLoaded("com.example:OptionalPlugin")) {
        // Optional plugin is available
    }
}
```

### Load Order

Use `LoadBefore` to ensure specific plugins load after yours:

```json
{
    "LoadBefore": ["com.example:LoadAfterMe"]
}
```

## Asset Packs

If your plugin includes client-side assets, set `IncludesAssetPack` to `true`:

```json
{
    "IncludesAssetPack": true
}
```

Place assets in your JAR under the `assets/` directory following Hytale's asset structure.

## Best Practices

1. **Register everything in `setup()`** - Commands, events, entities should all be registered during setup
2. **Use `start()` for initialization logic** - Access other plugins, connect to databases, etc.
3. **Clean up in `shutdown()`** - Close connections, save data, cancel tasks
4. **Use configuration** - Make your plugin configurable instead of hardcoding values
5. **Handle errors gracefully** - Log errors but don't crash the server
6. **Respect the lifecycle** - Don't access other plugins during setup, wait for start()
7. **Use meaningful permissions** - Follow the automatic permission naming convention
