-
Notifications
You must be signed in to change notification settings - Fork 2
Source Generators
ExpandedLib.Generators is a Roslyn source-generator project (netstandard2.0) that removes two
kinds of boilerplate at compile time: typed accessors for config classes, and typed members for a
block's JSON attributes. Both run automatically on build - there is nothing to invoke, you just tag
classes and mark them partial.
Triggers on: a class tagged [ExConfigRegister(fileName, modId)] (see Config System).
Emits: a static partial class (default name = your type name with Config -> Values, override
with AccessorName) containing:
-
public const string ConfigFileName- the file name you passed. - A private
ExConfigRegister<T>backing store, withLegacyFileNamesinitialised and a staticMigrationsmember forwarded if your config type declares one. -
public static void Load(ICoreAPI api)- calls the store'sLoad; ifManageable = true, also registers withExConfigProfiles. -
public static void Edit(Action<T> mutate)andpublic static void Save(). - One
public staticread-only getter per public, non-static, readable property (exceptConfigVersion), forwarding to the live config.
So this:
[ExConfigRegister("ppex_values.json", "ppex", LegacyFileNames = ["ppex.json"], Manageable = true)]
public class PpexConfig : IExVersionedConfig
{
public string? ConfigVersion { get; set; }
public static readonly ExConfigMigration[] Migrations = [ /* ... */ ];
public float LitresPerPipe { get; set; } = 30f;
}generates roughly:
public static partial class PpexValues
{
public const string ConfigFileName = "ppex_values.json";
private static readonly ExConfigRegister<PpexConfig> _store =
new(ConfigFileName, "ppex", PpexConfig.Migrations) { LegacyFileNames = ["ppex.json"] };
private static PpexConfig _config => _store.Config;
public static void Load(ICoreAPI api) { _store.Load(api); ExConfigProfiles.Register(_store); }
public static void Edit(Action<PpexConfig> mutate) { mutate(_store.Config); _store.Save(); }
public static void Save() => _store.Save();
public static float LitresPerPipe => _config.LitresPerPipe;
}Triggers on: a class tagged [BlockRegister] or [ItemRegister] and declared partial.
Inputs: the matching block-type / item-type JSON in blocktypes/ or itemtypes/.
Emits: members on the partial class that source the JSON attributes, so you read
BurstPressure instead of Attributes?["burstPressure"].AsFloat(0f):
-
constmembers for values identical across every matching file (noattributesByTypeoverride):const bool/const string/const int/const float. -
Instance properties for values that vary, for objects/arrays, or for
attributesByTypeentries:-
bool->public bool Foo => Attributes?["foo"].AsBool(false) ?? false; -
string->public string? Foo => Attributes?["foo"]?.AsString(); - number ->
public float Foo => Attributes?["foo"].AsFloat(0f) ?? 0f; - object/array ->
public JsonObject? Foo => Attributes?["foo"];(caller does.AsObject<T>())
-
JSON keys are PascalCased for the member name ("burstPressure" -> BurstPressure). Members
inherited unchanged from a [BlockRegister] ancestor are skipped; differing ones get the new
keyword to avoid CS0108 hiding warnings; keys that would collide with an existing member are
skipped with a comment.
[BlockRegister]
public partial class BlockPipe : BlockNetworkNode { } // <- partial + tagged// generated (given the matching blocktype JSON):
public partial class BlockPipe
{
public const string Material = "iron"; // identical across all variants
public float BurstPressure => Attributes?["burstPressure"].AsFloat(0f) ?? 0f; // varies by variant
public JsonObject? CustomData => Attributes?["customData"];
}This is also how IFillerHost.FillerOffsets gets implemented for free - the generator surfaces the
fillerOffsets attribute as a member, satisfying the interface (see
Multiblock Structures).
- Generated code is re-emitted every build, so adding a config property or a JSON attribute is instant - no boilerplate to duplicate or keep in sync.
- The generator targets
netstandard2.0(a Roslyn requirement); if you fork it, mind the usual netstandard2.0 source-generator constraints (no newer BCL APIs).
-
Config System - the runtime side of
[ExConfigRegister]. -
Registries -
[BlockRegister]/[ItemRegister]registration.
Expanded Library · framework mod for Vintage Story
exlib - Blocks
exlib - Registration
exlib - Utilities
exlib.testing