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 Logcreated from plugin GUID.public PluginConfig Configauto‑instantiated; overrideCreateConfig()to supply a custom subclass.- Helper accessors for common managers (
Engines,Databases,Systems, …). - Shorthand
Publish&Subscribewrappers 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
ReadybecauseBasePluginalready callsLoad&Readyon 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,TerminateAllhelpers—called by the RisingV bootstrap on server restart.
6. Best Practices
- One feature per plugin – keeps dependencies obvious.
- Put long‑running threads behind
SystemManagerorEngineManager. - Use
ReloadReason.IsFullto trigger heavy re‑initialisations only on manualreloadallcommands. - 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 inLoad, interact inReady. - Hot‑reload without server restarts via
Reload.
This abstraction keeps RisingV mods consistent, robust, and hot‑swappable.