Creating Your First Mod

This example uses the same registration, version, lifecycle, and cleanup contracts exercised by FTK2.ModAPI.ExampleMod.

using System;
using BepInEx;
using FTK2.ModAPI;
using FTK2.ModAPI.Core;
using FTK2.ModAPI.Events;

[BepInPlugin("author.demo", "Demo Mod", "0.1.0")]
[BepInDependency(FTK2API.PluginId, FTK2API.VersionString)]
public sealed class DemoPlugin : BaseUnityPlugin
{
    private IModContext? mod;
    private IDisposable? readySubscription;

    private void Awake()
    {
        var metadata = new ModMetadata(
            "author.demo", "Demo Mod", "Your Name",
            new ApiVersion(0, 1, 0), new ApiVersion(0, 5, 9));

        if (!FTK2API.TryRegister(metadata, out mod, out var error) || mod == null)
        {
            Logger.LogError(error);
            return;
        }

        readySubscription = mod.Events.Subscribe<GameDataReady>(OnGameDataReady);
        mod.Log.Info("Startup", "Demo Mod registered.");
    }

    private void OnGameDataReady(GameDataReady ready)
    {
        mod!.Log.Info("Startup",
            $"FTK2 {ready.GameVersion}; generation {ready.Generation}; {ready.ItemCount} items");
    }

    private void OnApplicationQuit()
    {
        readySubscription?.Dispose();
        (mod as IModSession)?.Dispose();
    }
}
  1. BepInDependency loads ModAPI first.
  2. ModMetadata declares one stable owner and API requirement.
  3. FTK2API.TryRegister returns an isolated IModContext.
  4. GameDataReady tells you native templates are usable.
  5. Dispose the subscription and optional IModSession on the main thread during real shutdown.

Use lowercase dotted mod IDs such as author.demo. Owned content then uses IDs such as author.demo:ember_axe.

Important

Subscribe during Awake; events are not replayed. Keep runtime service calls and subscription disposal on Unity's main thread.

Next: create an item or follow the Flame Cleave tutorial.