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; overrideCreateConfig()
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
becauseBasePlugin
already callsLoad
&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
orEngineManager
. - Use
ReloadReason.IsFull
to trigger heavy re‑initialisations only on manualreloadall
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 inLoad
, interact inReady
. - Hot‑reload without server restarts via
Reload
.
This abstraction keeps RisingV mods consistent, robust, and hot‑swappable.