Reusable UPrimaryDataAsset holding an action list (mirrors UCrimsonAbilitySet) - the designer-friendly authoring path. AppendTo(OutActions) for providers. Same loaded asset on client and server -> deterministic
UCrimsonContextAction_GrantAbility
Built-in: TryActivateAbilityByClass on the instigator's ASC
UCrimsonContextAction_DebugMessage
Test/debug action: logs Message at Display verbosity when executed server-side. Drop into any action list to verify the full menu round-trip without gameplay code
UCrimsonContextAction_FromInteractionOption
Transient wrapper letting ICrimsonInteractableTargets surface in the menu. Wire id derives from the option ability class; ValidateExecution enforces the crimson.ContextMenu.MaxExecuteDistance range CVar (default 1000 cm, <= 0 disables)
UCrimsonContextActionGatherer
THE single gather path (provider interface, else interactable actor + components, then registered contributors) + Hidden filtering + deterministic sort (destructive last, section, priority). Helpers: GatherActions, ResolveAction(Actions, ActionId, Occurrence), MakeActionHandle(Actions, Action, OutId, OutOccurrence)
ICrimsonContextActionProvider
GatherContextActions - must return a deterministic, identically-ordered list on client and server. Optional native ValidateContextActionRequest(Context) rejects wrong-instigator requests server-side (e.g. another player's item)
ICrimsonContextActionContributor
Inject actions into OTHER objects' menus (quests add "Turn In" to an NPC). GatherContextActionsForTarget inspects Context.Target and appends only when relevant. Same determinism contract as providers
UCrimsonContextActionContributorRegistry
UWorldSubsystem: RegisterContributor(Object, Priority) / UnregisterContributor. Consulted by the gatherer after the target's own gather, in deterministic (priority, class name) order. Register symmetrically on server and client or contributed actions cannot resolve
Execution context (instigator/target) / RPC payload ({ Provider, ActionId, Occurrence, Payload } - stable id + occurrence-among-same-id, plus an optional FInstancedStruct argument)
UCrimsonContextActionPlayerComponent
Player-controller component with the Server_ExecuteContextAction RPC (not auto-attached). Server flow: re-gather via the gatherer -> ValidateContextActionRequest -> resolve by (ActionId, Occurrence) -> GetAvailability == Enabled -> ValidateExecution -> execute. Stale requests drop gracefully
Perf-stat HUD display mode and the displayable stat set (FPS, frame/latency, packet)
FCrimsonAsyncMixin
Sequential async-load mixin (ported from Epic's AsyncMixin)
Editor tooling
Editor-only surfaces in the CrimsonCommonEditor module. Not available at runtime and not Blueprint-exposed.
MCP toolbar.CrimsonCommonEditor owns the single MCP button in the Level Editor play toolbar - a split control whose left half shows server status and starts/stops the server, and whose arrow opens the Crimson MCP menu. It lives here rather than in CrimsonEditorUtilities because everything it configures is suite-wide: the master switch is read by every Crimson plugin's editor module, so the button must exist for projects that do not install that optional plugin.
Type
Description
UCrimsonMCPSettings
Project Settings -> Crimson -> Crimson MCP. bEnableCrimsonMCP is the suite-wide master switch (off by default, read at module startup); bShowMCPToolbarButton shows/hides the toolbar button independently. Gate registration with IsEnabled().
SCrimsonMCPButton
The MCP split button widget: status + start/stop on the left, the Crimson MCP menu on the right.
FCrimsonMCPToolingUtility
Builds that menu - master switch, Epic toolset plugins (rewrites the .uproject), and per-toolset visibility via the Toolset Registry block list. CanShow() is false during PIE, when project edits are unsafe.
File groups. Lets a plugin teach an editor browse tool how to organize files it owns, without the tool depending on that plugin. A tool that finds no provider for its domain keeps its own default behavior, so registration presence is the whole detection mechanism - there is no setting and no plugin sniffing. The Save Game deleter is the first consumer: CrimsonSaveSystem stores its blobs flat, which a directory-based browser would show as one undifferentiated list, so CrimsonSaveSystemEditor registers a provider for the "SaveGames" domain and the blobs list under a heading per slot.
Type
Description
ICrimsonFileGroupProvider
Implement and register to organize a domain's files. GetDomain() names the domain; GetRootDirectories() reports where the files actually live; GroupFiles() sorts absolute paths into display groups. GroupFiles must account for every input path - put unrecognized files in a catch-all group rather than dropping them - or return false to decline, which makes the tool fall back.
FCrimsonFileGroupRegistry
Singleton. Register() from your editor module's StartupModule, Unregister(Domain) in ShutdownModule, FindProvider(Domain) from the tool.
FCrimsonFileGroup
One display group: a DisplayName and the absolute FilePaths in it.
This is the interface bridge, applied to tooling
Same shape as the runtime patterns in Concept: Decoupling Patterns - the contract lives in CrimsonCommon, the owner implements it, the consumer resolves it, and neither includes the other. Adding a public header here rebuilds every dependent editor module, so put a contract here only when more than one plugin genuinely needs it.