Skip to content

Core API

Tom_XV edited this page Sep 14, 2026 · 7 revisions

English | 日本語

The core plugin, DragNWash.ModFramework.dll, namespace DragNWash.ModFramework.

[BepInDependency(ModFramework.Guid, BepInDependency.DependencyFlags.HardDependency)]

ModFramework

Member What it does
const string Guid "com.tomxv.dragnwash.modframework", to depend on
const string Version The framework's version
bool IsReady True once the framework has finished starting
event Action Ready Raised once when the framework has started. A handler added later runs immediately
void Register(ModInfo info) Tells the Mods screen about your mod. Registering the same GUID again replaces it
void AddModsPage(ModsScreenPage page) Adds a custom page to your mod's details, opened with a button

ModInfo

Property Meaning
Guid Your BepInEx GUID. Required
DisplayName Name for players; may hold spaces and punctuation. Defaults to the BepInEx name
Description One or two sentences
Authors string[]
Website Project or download page
IsLibrary True for a prerequisite other mods build on. The Mods screen tags it Library, lists the mods that need it, and asks before it is switched off
Icon A Texture2D, ideally square. Create it in Awake (see Assets). Wins over IconPath
IconPath A PNG or JPG file loaded when you register. Register from Awake

ModsScreenPage

For settings that do not fit BepInEx config entries.

ModFramework.AddModsPage(new ModsScreenPage
{
    Guid = MyMod.Guid,
    Title = "Statistics",
    Build = panel =>
    {
        // panel is a RectTransform, cleared before each call.
        // Build it with UnityEngine.UI and TextMeshPro components.
    },
});

GameOptions

Rows in the game's own Options screen. They follow the game's flow: changing a row previews it and shows the game's Save button, Save keeps it, Back without saving returns to the saved value, and Set Default uses the default you give. Labels and choices are in English; translation mods translate them like any other UI text.

GameOptions.AddChoice(new OptionsChoice
{
    Id = "com.example.mymod.difficulty",   // unique; adding the same id again is ignored
    Label = "Difficulty",
    Choices = new[] { "Relaxed", "Normal", "Messy" },
    Section = OptionsSection.Gameplay,       // Gameplay (default), Audio or Graphics
    DefaultIndex = 1,                        // -1 leaves the row alone on Set Default
    GetSaved = () => _difficulty.Value,
    Save = index => _difficulty.Value = index,
    Preview = index => ApplyDifficulty(index), // optional, called for every shown change
});

GameOptions.AddToggle("com.example.mymod.fastmode", "Fast Mode",
    getSaved: () => _fastMode.Value,
    save: value => _fastMode.Value = value,
    preview: null, section: OptionsSection.Gameplay, defaultValue: false);

// After changing the value somewhere else (your own menu, a config file edit):
GameOptions.Refresh("com.example.mymod.fastmode");

AddChoice can be called at any time; the row appears once the game's settings exist. It throws ArgumentException without Id, Label, at least two Choices, GetSaved and Save.

Services

How a library offers an API to other mods without them reaching into its internals.

// In the library (publish IScoreService in its DLL):
Services.Register<IScoreService>(new ScoreService(), new Version(1, 2), MyLibrary.Guid);

// In a mod that has a BepInDependency on the library, from Awake:
var scores = Services.Get<IScoreService>();

// In a mod without a hard dependency, or needing a minimum version:
if (Services.TryGet<IScoreService>(out var s, new Version(1, 1))) { /* use s */ }
Services.WhenAvailable<IScoreService>(s => { /* runs now, or when it is registered */ });
Member What it does
bool Register<T>(T implementation, Version version = null, string ownerGuid = null) Registers the provider of T. A second provider for the same T is refused and logged
T Get<T>() The provider, or null
bool TryGet<T>(out T service, Version minimumVersion = null) The provider if it has at least that version
Version GetVersion<T>() The version the provider registered, or null
void WhenAvailable<T>(Action<T> callback) Calls back now or as soon as T is registered

GameHooks

Check that the game code you patch still exists on the running build, so a game update disables one feature instead of crashing the game. Failed checks are logged and shown on the Mods screen as Unavailable on this game build.

// "ScrubController" stands for the game type you patch.
if (GameHooks.Require(MyMod.Guid, "Faster scrubbing", "ScrubController", "Update"))
{
    harmony.PatchAll(typeof(ScrubPatches));
}

// A check you made yourself:
var method = AccessTools.Method("ScrubController:Update");
GameHooks.Require(MyMod.Guid, "Faster scrubbing", method != null, "ScrubController.Update");
Member What it does
bool Require(string ownerGuid, string feature, string typeName, string methodName = null) True when the type (and method) exists
bool Require(string ownerGuid, string feature, bool passed, string detail) Records your own check and returns passed
IReadOnlyList<string> UnavailableFeatures(string ownerGuid) Features of a mod that failed a check

GameInfo

Member Meaning
string UnityVersion For example 6000.3.14f1
GraphicsDeviceType GraphicsApi The graphics API in use
bool IsDirect3D12 True on Direct3D 12, where textures and fonts must be loaded at startup (see Assets)
OperatingSystemFamily OperatingSystem Windows, Linux (Steam Deck) or MacOSX

Also in the core

  • On/off switches from the Mods screen are applied by BepInEx/patchers/DragNWash.ModFramework.Preloader.dll before plugins load.
  • Conflict detection: after startup the core lists game methods patched by more than one mod in the log and marks those mods on the Mods screen. See Playing well with others.
  • Title screen: the version and the number of loaded BepInEx plugins above the build number.

Clone this wiki locally