Skip to content

Missing public API to generate config entries procedurally #11

@ratijas

Description

@ratijas

Situation

My mod manages a list of things of type T, each of which has an associated config entry. The list is fixed at compile time, but the things are not bound to individual class fields, nor should they.

BepInEx and LethalConfig both support creation of config entries on the fly:

using BepInEx;
using BepInEx.Configuration;

class Thing {
    public string Name;
    public ConfigEntry<int> Weight;
}

Thing[] Things;
foreach (var t in Things)
{
    t.Weight = Config.Bind("Section", t.Name, 0);
}
using LethalConfig;
using LethalConfig.ConfigItems;
using LethalConfig.ConfigItems.Options;

foreach (var t in Things)
{
    var slider = new IntSliderConfigItem(t.Weight.Entry, new IntSliderOptions
    {
        RequiresRestart = false,
        CanModifyCallback = CanModifyWeightsNow,
    });
    LethalConfigManager.AddConfigItem(slider);
}

Problem

CSync doesn't provide an equivalent API. The only supported option is to write a class with field annotated with a certain attribute:

[PublicAPI]
public class SyncedConfig2<T> : ISyncedConfig where T : SyncedConfig2<T>
{
    // [...]
    private static Lazy<FieldInfo[]> SyncedEntryFields = new(
        () => AccessTools.GetDeclaredFields(typeof(T))
            .Where(field => field.GetCustomAttribute<SyncedEntryFieldAttribute>() is not null)
            .Where(field => typeof(SyncedEntryBase).IsAssignableFrom(field.FieldType))
            .ToArray()
    );

    internal void PopulateEntryContainer()
    {
        if (Interlocked.Exchange(ref _entryContainerPopulated, 1) != 0) return;
        foreach (var fieldInfo in SyncedEntryFields.Value)
        {
            var entryBase = (SyncedEntryBase)fieldInfo.GetValue(this);
            EntryContainer.Add(entryBase.BoxedEntry.ToSyncedEntryIdentifier(), entryBase);
        }
    }
    // [...]
}

This requires fields to be declared in advanced for every entry in the list, which is not the most maintainable solution.

Expected behavior

Provide public API for adding entries to the EntryContainer programmatically.

Workaround

Since we mods are patching the whole game by forcefully exposing their entire codebase, why mods themselves should be any different? After all, why not…

<PackageReference Include="Sigurd.BepInEx.CSync" Version="5.0.1" Publicize="true" />
foreach (var t in Things)
{
    // This is basically what ConfigFile.PopulateEntryContainer does
    SyncedEntryBase entryBase = t.Weight;
    EntryContainer.Add(entryBase.BoxedEntry.ToSyncedEntryIdentifier(), entryBase);
}

ConfigManager.Register(this);

oh well, can't have any privacy \s

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions