-
Notifications
You must be signed in to change notification settings - Fork 2
Description
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