"BuildHash() overrides only compile on PanelComponent, not on a plain Component"
▸ SYMPTOM
A headless dotnet build fails with:
CS0115: 'MyLogicComponent.BuildHash()': no suitable method found to overrideon a component that is not a UI panel — even though the same BuildHash() => 0; line compiles fine on your panel classes.
▸ CAUSE
protected override int BuildHash() is a member of PanelComponent, not of a plain Component. A logic-only Component (state + per-frame drive, no render tree) has no BuildHash to override, so the override has nothing to bind to and the compiler rejects it with CS0115.
It's easy to hit when you split a UI feature into two components on one ScreenPanel — a logic Component that owns state and drives per frame, sitting beside its PanelComponent view — and then reflexively copy the "keep the tree stable" BuildHash() => 0; idiom (which every PanelComponent in the codebase uses) onto the logic component too.
▸ FIX
Delete the BuildHash override from the plain Component. It has no render tree to hash, so it doesn't need one — only the PanelComponent view does:
// logic Component — NO BuildHash override
public sealed class MyLogicComponent : Component
{
// state + per-frame drive only
}
// view — BuildHash lives HERE
public sealed class MyView : PanelComponent
{
protected override int BuildHash() => 0; // keep the tree stable
}▸ WHY IT WORKS
BuildHash exists so the panel system can decide whether a panel's render tree needs rebuilding — it's meaningful only where there is a tree, i.e. on PanelComponent. A logic component contributes no markup, so there is nothing for a hash to stabilize; the override isn't just unnecessary, there's no base method for it to override. Keeping BuildHash on the view and off the logic half of a split UI feature matches the type each responsibility actually lives on.
- Published