s&box Source Generator: IUpdateSubscriber, WrappedPropertySet, and How [Sync]/[Rpc.*] Work at Compile Time
s&box Source Generator: How [CodeGenerator] Works for [Sync] and [Rpc.*]
The Sandbox.Generator Roslyn source generator transforms your C# code at compile time. Understanding this explains why [Sync] and [Rpc.] work without any runtime reflection overhead.
What the Generator Does
For every property with [CodeGenerator( CodeGeneratorFlags.WrapPropertySet, "CallbackName" )], the generator rewrites the setter to call CallbackName( new WrappedPropertySet<T> { ... } ) instead of the original setter body. The original setter body is moved into a lambda stored in the WrappedPropertySet.
For every method with [CodeGenerator( CodeGeneratorFlags.WrapMethod, "CallbackName" )], the generator rewrites the method to call CallbackName( new WrappedMethod { Resume = () => { original body } }, args... ).
IUpdateSubscriber, IFixedUpdateSubscriber, IPreRenderSubscriber
The generator also automatically adds subscriber interfaces to components that override specific methods:
| Method | Interface Added |
|---|---|
| OnUpdate() | Sandbox.Internal.IUpdateSubscriber |
| OnFixedUpdate() | Sandbox.Internal.IFixedUpdateSubscriber |
| OnPreRender() | Sandbox.Internal.IPreRenderSubscriber |
WrappedPropertySet and WrappedPropertyGet
These structs carry the property value, getter/setter delegates, member identity hash, and attribute array to the callback:
// What [Sync] generates for a property setter:
// Original: public float Health { get; set; }
// Generated setter calls:
__sync_SetValue( new WrappedPropertySet<float>
{
Value = value,
Setter = (v) => { field = v; },
Getter = () => Health,
MemberIdent = <hash of "MyComponent.Health">,
PropertyName = "Health",
TypeName = "MyComponent",
Attributes = __Health__Attrs
} );Attribute Caching
The generator creates a static PropertyNameAttrs field that caches all attributes on the property as an array. This avoids repeated reflection calls when the network system needs to check SyncFlags.
Multiple Wrappers on One Property
Multiple [CodeGenerator] attributes on the same property are applied in priority order (highest priority first). This allows [Sync] and [Change] to both wrap the same property setter without conflict.