# Creating an Addon

This guide shows how to create a minimal OMS addon and register its first feature.

The example addon will register a single feature named `HelloWorldFeature`.

***

### Goal

By the end of this guide, you will have:

* a Forge mod that depends on OMS
* an OMS addon implementation
* addon registration through the OMS lifecycle
* a first feature registered through the addon

***

### 1. Create your addon project

You have two options:

Option A - Use the official template (recommended)\
Clone the [OMS Addon template project](https://github.com/c0nnor263/OMS-Addon-Template-1.20.1) and start from a ready-to-use setup

After cloning, update:

* mod id
* package name
* addon and feature classes

Option B - Add OMS and OMS API to your existing project\
If you already have a Forge mod, you can integrate OMS manually by adding:

* OMS runtime dependency
* OMS API (`oms-api`)

There are multiple ways to add the dependency depending on your setup.

***

{% hint style="info" %}

#### Regarding runtime dependencies and remapping

**OMS** or **OMS API** should not be bundled into your addon jar and must be added as runtime-only mod dependencies.<br>

We use a custom `localRuntime` configuration to make them available in dev (runClient/runServer) without including them in the final mod or exposing them transitively.

When using NeoForge / Legacy NeoForge (FG5+), these dependencies also need to be remapped.<br>

**Use the following setup:**

```kts
val localRuntime by configurations.creating

configurations {
    runtimeClasspath {
        extendsFrom(localRuntime)
    }
}

obfuscation {
    createRemappingConfiguration(localRuntime)
}
```

{% endhint %}

#### Add OMS as a runtime dependency

```kts
repositories {
    // OMS API
    maven("https://c0nnor263.github.io/OperateMyServer/maven")

    // Choose one source for OMS runtime:
    
    // CurseForge
    maven {
        url = uri("https://cursemaven.com")
        content { includeGroup("curse.maven") }
    }

    // Modrinth
    maven("https://api.modrinth.com/maven")

    // OMS Maven (direct). 
    // OperateMyServer Maven provides both OMS and OMS API for convenience
    // maven("https://c0nnor263.github.io/OperateMyServer/maven")
}

dependencies {
    // Choose ONE of the following:

    // CurseForge
    // "modLocalRuntime"("curse.maven:operate-my-server-1341025:<FILE_ID>")

    // Modrinth
    // "modLocalRuntime"("maven.modrinth:operate-my-server:<VERSION>")

    // OMS Maven (direct)
    // "modLocalRuntime"("io.conboi.oms:oms:<VERSION>")

    // OMS API
    modCompileOnly("io.conboi.oms:oms-api:1.0.0")

    // KotlinForForge
    implementation("thedarkcolour:kotlinforforge:4.11.0")
}
```

{% hint style="info" %}
Choose one source for **OMS** (CurseForge, Modrinth, or OMS Maven) and uncomment the corresponding dependency.
{% endhint %}

***

### 2. Create the addon class

An OMS addon is defined by extending `OmsAddon`.

```kt
class HelloWorldAddon : OmsAddon(HelloWorldMod.MOD_ID) {

    override fun onRegisterFeatures(context: AddonContext) {
        context.featureManager.register(
            HelloWorldFeature {  }
        )
    }
}
```

The addon acts as a container and registration entry point.\
Runtime behavior is implemented by features.

***

### 3. Register the addon

Addons must be registered during the OMS lifecycle:

```kt
@Mod.EventBusSubscriber(modid = HelloWorldMod.MOD_ID)
internal object OMSLifecycleListener {

    @SubscribeEvent
    fun onAddonRegisterEvent(event: OMSLifecycle.Addon.RegisterEvent) {
        event.registry.register(HelloWorldAddon())
    }
}
```

***

### 4. Register features

Features are registered through the addon context:

```kotlin
context.featureManager.register(HelloWorldFeature {  })
// If multiple
listOf(
    HelloWorldFeature1 {
        // config...
    },
    HelloWorldFeature2 {
        // config...
    }
).forEach { feature ->
    context.featureManager.register(feature)
}
```

Each feature will then be managed and executed by the `FeatureManager`.

***

### 5. Optional: Customizing Addon Context

`OmsAddon` allows customization of its runtime context via `configureContext`.

```kotlin
override fun configureContext(spec: AddonContextSpec): AddonContextSpec {
    return spec
}
```

This can be used to customize:

* filesystem paths
* feature manager implementation

Most addons do not need to override this and can rely on the default OMS behavior.

See [Customizing Addon Context](/oms-wiki/developer-guide/implementation-guides/customizing-addon-context.md)

***

### 6. What happens next

During startup:

1. OMS emits `Addon.RegisterEvent`
2. Your addon is registered
3. OMS creates the addon context
4. `onRegisterFeatures(context)` is called
5. Features are registered
6. The `FeatureManager` takes control of execution

***

### Notes

* Addons are containers for features
* Addons must be registered during the OMS lifecycle
* Features are registered through `context.featureManager`
* Runtime logic belongs to features

***

### Next Step

Continue with: [**Creating a Feature**](/oms-wiki/developer-guide/implementation-guides/creating-a-feature.md)

You will implement `HelloWorldFeature` and make it print `hello world` to the console.


---

# 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://conboi.gitbook.io/oms-wiki/developer-guide/implementation-guides/creating-an-addon.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.
