Table of Contents

Plugins Abstraction

Namespaces: RisingV.Shared.Plugins, RisingV.Shared.Config, RisingV.Shared.Logging
Assemblies: RisingV.Shared

The Plugins abstraction is the entry‑point layer that lets you build modular, self‑contained feature packs for RisingV.
It wraps the raw BepInEx BasePlugin base class with:

  • A uniform 6‑stage lifecycle shared by every RisingV subsystem.
  • First‑class configuration binding via BaseConfig.
  • Automatic manager wiring (Engines, Databases, Systems, …).
  • Tight integration with the Logging & EventBus frameworks.

1. Core Contracts

Contract Purpose
IPlugin Read‑only metadata + lifecycle signatures.
BasePlugin Convenience abstract base: derives from BasePlugin, auto‑registers with PluginManager, exposes virtual hooks.
PluginConfig Structured config holder (inherits BaseConfig).
PluginManager Central registry & orchestrator (TypeMapManager<IPlugin, PluginManager>).

IPlugin

public interface IPlugin
{
    string Guid     { get; }
    string Name     { get; }
    Version Version { get; }
    string Author   { get; }

    bool IsInitialized { get; }
    bool IsLoaded      { get; }
    bool IsReady       { get; }

    void Initialize();
    void Load();
    void Ready();
    void Reload(ReloadReason reason);
    void Unload();
    void Terminate();
}

Note – The three boolean properties are idempotent guards; your override must call base.*() to keep them in sync.

BasePlugin

Adds:

  • protected Logger Log created from plugin GUID.
  • public PluginConfig Config auto‑instantiated; override CreateConfig() to supply a custom subclass.
  • Helper accessors for common managers (Engines, Databases, Systems, …).
  • Shorthand Publish & Subscribe wrappers for EventBridge.

2. Lifecycle Flow

┌──────────┐     ┌──────────┐     ┌──────────┐
│Initialize│ ──► │   Load   │ ──► │  Ready   │
└──────────┘     └──────────┘     └──────────┘
      ▲               │               │
      │               ▼               ▼
┌──────────┐     ┌──────────┐     ┌──────────┐
│Terminate │ ◄── │  Unload  │ ◄── │  Reload  │
└──────────┘     └──────────┘     └──────────┘
Stage Typical Responsibilities
Initialize Create & register managers, bind config entries, seed data.
Load Connect to external systems (DB, network), allocate heavy objects.
Ready Fire game events; safe to interact with other plugins (all loaded).
Reload Hot‑apply modified configs or resources.
Unload Dispose large caches, stop threads, detach from events.
Terminate Final cleanup; called on server shutdown.

3. Quick‑Start Skeleton

using BepInEx;
using RisingV.Shared.Plugins;
using RisingV.Shared.Engines;
using RisingV.Core.Engines;

public class MyCoolPluginConfig() : PluginConfig(MyPluginInfo.PLUGIN_GUID, MyPluginInfo.PLUGIN_GUID)
{
    public ConfigEntry<float> AttackModifier { get; set; } =
        new("Gameplay", "AttackModifier", 1f, "Modifier for attack damage");
}

public class MyCoolPluginContext() : PluginContext(typeof(MyPluginInfo), new MyCoolPluginConfig());

[BepInPlugin(MyPluginInfo.PLUGIN_GUID, MyPluginInfo.PLUGIN_NAME, MyPluginInfo.PLUGIN_VERSION)]
[BepInDependency("RisingV.Core")]
[BepInDependency("RisingV.Scripting")]
public class Plugin : RisingPlugin<MyCoolPluginContext>
{
    protected override void OnLoad()
    {
        // Plugin startup logic (pre-initialization)
    }

    protected override bool OnUnload()
    {
        return true;
    }

    protected override void RegisterEngines(EngineManager engineManager)
    {
        engineManager.AddEngine<ScriptingEngine>(this);
        engineManager.AddEngine<DamageEngine>(this);
        engineManager.AddEngine<DeathEngine>(this);
    }

    // You can also override other methods to register systems, processors, databases, etc.
}

Tip – For most plugins you can skip overriding anything except Ready because BasePlugin already calls Load & Ready on all registered managers.


4. PluginConfig Pattern

public sealed class LootTweaksConfig : PluginConfig
{
    public ConfigEntry<float>? Multiplier { get; private set; }

    public override void Load()
    {
        base.Load();
        Multiplier = Bind("General", "Multiplier", 2f,
            "Multiply boss loot counts by this factor.");
    }
}

PluginConfig inherits BaseConfig, so you get the same queue‑based binding, Enabled entry, and hot‑reload hooks.


5. PluginManager

  • Located in RisingV.Shared.Plugins.
  • Registers all plugins found via reflection ([Plugin] attribute) and those instantiated manually in tests.
  • Sorts by dependency graph (plugins may declare [PluginDependency("Guid")]).
  • Provides ReloadAll, UnloadAll, TerminateAll helpers—called by the RisingV bootstrap on server restart.

6. Best Practices

  • One feature per plugin – keeps dependencies obvious.
  • Put long‑running threads behind SystemManager or EngineManager.
  • Use ReloadReason.IsFull to trigger heavy re‑initialisations only on manual reloadall commands.
  • Expose user‑configurable toggles via ConfigEntry<bool> so server admins can disable sub‑features without removing DLLs.

7. Troubleshooting

Symptom Likely Cause Fix
Plugin stuck at “Loaded” Forgot to call Ready() on a manager Ensure base.Ready() call or manual ready propagation.
NullReferenceException on Config Accessing bindings in constructor Move Bind calls to Load().
Circular plugin dependency [PluginDependency] loop Break loop or make one dependency optional.

TL;DR

  • BasePlugin gives you structured metadata, config, logging, and a uniform lifecycle.
  • Register managers in Initialize, heavy‑load in Load, interact in Ready.
  • Hot‑reload without server restarts via Reload.
    This abstraction keeps RisingV mods consistent, robust, and hot‑swappable.