Burnsies HideoutDocsBurnsie.Common

Configuration — ConfigSchema and IniFile

Burnsie.Common.Configuration gives you a declarative INI schema: every key is defined once — with its section, default, clamping range and comment — and that single table drives three things:

  1. Reading values (typed, clamped, warn-and-default on bad input),
  2. Writing the default file on first run (fully commented),
  3. Generating user documentation as a Markdown table.

Because file, values and docs come from one source, config/README drift is structurally impossible.

Quick start

var schema = new ConfigSchema("plugins/LSPDFR/MyPlugin.ini", log);
schema.HeaderComments = new[] { "MyPlugin configuration", "Edit and save; reload in game." };

var cooldown = schema.Int ("Settings",    "CooldownSeconds", 30, 5, 300, "Seconds between uses.");
var volume   = schema.Float("Settings",   "Volume",          0.8f, 0f, 1f, "Playback volume.");
var enabled  = schema.Bool ("Settings",   "Enabled",         true, "Master switch.");
var name     = schema.Text ("Settings",   "UnitName",        "1-Adam-12", "Your callsign.");
var hotkey   = schema.Enum ("Keybindings","Key",             Keys.F11, "Activation key.");

schema.EnsureFileExists();   // writes the fully-commented default INI if none exists
schema.Load();               // reads every declared entry from disk

int seconds = cooldown.Value;    // typed access after Load()
Keys key    = hotkey.Value;

Entry types

MethodEntry typeValidation
Int(section, key, default, min, max, comment)IntEntryClamped to [min, max]; out-of-range values are clamped with a warning naming the key
Float(...)FloatEntrySame clamping behavior
Bool(section, key, default, comment)BoolEntryStandard INI boolean parsing
Text(section, key, default, comment)TextEntryNever null; missing key returns the default
Enum<TEnum>(section, key, default, comment)EnumEntry<TEnum>Parsed by name, case-insensitive (e.g. Keys.F11 from f11); invalid names warn and use the default

All entries expose .Value, which holds the default until Load() runs. Defining the same section+key twice throws immediately (InvalidOperationException) — duplicates are a programming error, caught at startup rather than shipped.

File generation

EnsureFileExists():

  • Does nothing when the file already exists (never overwrites user edits).
  • Otherwise creates the directory and writes a complete default file: header comments, every section, each key preceded by its comment (with the permitted range appended, e.g. ; Seconds between activations. (5–300)).
  • Never throws — on failure it warns and the plugin continues with built-in defaults.

BuildDefaultFileText() returns the same text as a string; the packaging script uses this to ship a pristine default INI in the release zip.

Documentation generation

string markdown = schema.ToMarkdown();

Produces a per-section reference table (| Key | Default | Range | Description |) ready to paste into your README. Regenerate it whenever you touch the schema and your docs can never lag the code.

IniFile — the raw layer

ConfigSchema.File exposes the underlying IniFile, a thin exception-safe wrapper over RPH's InitializationFile. Use it directly for things outside the schema, such as migration reads from an old config:

if (schema.File.HasKey("LegacySection", "OldKey"))
{
    int old = schema.File.ReadInt("LegacySection", "OldKey", 0);
    // migrate...
}

schema.File.WriteString("State", "LastRunVersion", myVersion.ToString());

Every read method (ReadString, ReadInt, ReadFloat, ReadBool, ReadEnum) returns the supplied default on absence or error, with a warning — a corrupt or hand-mangled INI yields defaults, never an exception. A plugin must not die on a user's typo.

Rules

  • Paths are relative to the GTA V root (that's how RPH's InitializationFile resolves them); the conventional location is plugins/LSPDFR/YourPlugin.ini.
  • Call EnsureFileExists() before Load() on your startup path.
  • Re-run Load() to pick up user edits (e.g. on duty start) — entries update in place.