Events - which one fires, where, and what it carries

Which event fires, on which machine, and what it carries.

Assumes you have finished quick-start.

Every table on this page is generated from the same data the kit's own tests assert against. Where a figure is a measurement, the page says so and says whether it is advisory.

Which machine?

A = the authority, O = the owning client, R = every other client. A listen host's own pawn is A and O, so it fires both rows - twice, told apart only by Source. Standalone is identical to a listen host.

EventComponentAORMachine phrase
OnInteractionStartedinteractor110Authority and owning client.
OnInteractionCompletedinteractor110Authority and owning client.
OnInteractionCancelledinteractor110Authority and owning client.
OnInteractionRejectedinteractor110Authority and owning client.
OnInteractionStartedinteractable100Authority only.
OnInteractionCompletedinteractable100Authority only.
OnInteractionCancelledinteractable100Authority only.
OnInteractionRejectedinteractable100Authority only.
OnPhaseChangedinteractable111Every machine.
OnBehaviorValueChangedinteractable111Every machine.
OnInteractDeniedinteractor010Local machine.
OnFocusChangedinteractor010Local machine.

The four On Interaction ... rows appear twice in that table - once for the interactor and once for the interactable - because they are different members with the same name and different answers. The sections below are the anchors the tooltips link to.

The rule the quick-start teaches, word for word as quick-start.md and demo.md state it:

On Behavior Value Changed fires on every machine — bind it for anything the world shows or does (meshes, lights, sounds, labels); every player sees the same result, including you. On Interaction Completed fires on the authority only — bind it for the once-per-interaction game logic that must never run twice (give the item, open the gate, add the score). If you change a mesh from On Interaction Completed, the other players see nothing.

On Interaction Started

Authority and owning client. (interactor) / Authority only. (interactable)

The interaction began. On the owning client it is the prediction (Source == Predicted); on the authority it is the acceptance (Source == Authority). A listen host sees both.

Where it is raised. On the owning client, at the press - the same statement that sends the request, so a use animation can start before anything crosses the wire. On the authority, at the acceptance, inside the protocol's guard and after the raw On Server Interaction Began. The interactable's copy follows the interactor's at the acceptance site and never fires anywhere else.

What the payload says. Request Serial is the same integer on both firings, which is how you pair them. Preset and Identity Tag are captured from the request record, so a pickup's identity is already correct here. Hold Seconds carries the hold length for a hold preset; Reason is meaningless and reads None.

The one trap. A Predicted start is a guess. It can be followed by a Rejected rather than a Completed - the authority is allowed to say no. Branch on Source == Predicted to drive the optimistic animation and undo it on rejection; do not treat a predicted start as a commitment.

Which machine? On Server Interaction Began — Authority only.

On Interaction Completed

Authority and owning client. (interactor) / Authority only. (interactable)

The interaction ran to completion. Reason is None - the one kind that carries no refusal.

Where it is raised. On the authority at the ending, after the raw On Server Interaction Ended, and on the owning client when the answer comes back (Source == OwnerResolve). A listen host sees both, in that order.

What the payload says. Elapsed Seconds is meaningful here and only here among the successful kinds - it is how long the interaction actually ran. Reason is None; Get Reason Text returns empty text for it, which is what a prompt should show.

The interactable's copy fires only if the component is still valid at the ending. A door destroyed by its own completion handler does not raise its own On Interaction Completed.

On Interaction Rejected

Authority and owning client. (interactor) / Authority only. (interactable)

The request was refused. On the authority this fires only for the checks that have a target; a refusal with no target context fires nothing there, and the owning client still learns of it from its own resolve. A local refusal (bLocal) has no preceding On Interaction Started at all.

Where it is raised. On the authority for a refusal that got far enough to resolve a target

  • range, line of sight, usability, the arbiter - and nowhere on the authority for a refusal made before that (throttling, a stale serial, no behavior): those have no target to raise the event on. On the owning client, every refusal arrives, either from the authority's answer (Source == OwnerResolve) or from the local pre-check.

A local refusal has no start at all. When the client refuses before sending, b Local is true, there is no preceding On Interaction Started, and nothing went on the wire. Is Local Rejection is the node that tells the two apart without reading the flag by hand.

What the payload says. Reason and, for Custom, Custom Reason Tag are the point of this event; Usability carries what the target looked like at the moment of refusal. Feed Reason to Get Reason Text for the player-facing sentence - see Reasons.

Which machine? On Interaction Started — Authority and owning client. (interactor) / Authority only. (interactable)

On Interaction Cancelled

Authority and owning client. (interactor) / Authority only. (interactable)

The interaction ended without completing - released, moved, disabled, or the target went away.

Where it is raised. The same site pair as On Interaction Completed - the authority's ending and the owning client's resolve. The kind is chosen by the outcome, so a given interaction raises Completed or Cancelled, never both.

Why it ended is in Reason. Released (the player let go of a hold), Moved (walked out of range), Disabled (the target was switched off), Target Lost (the target was destroyed or de-registered), Owner Lost, Timeout. The distinction between a destroyed target and a de-registered one is invisible to you on purpose - both read Target Lost.

Elapsed Seconds is meaningful, and for a hold it is how far the player got before letting go. Pair it with Hold Seconds to render a partial gauge without tracking time yourself.

Which machine? On Interaction Completed — Authority and owning client. (interactor) / Authority only. (interactable)

On Phase Changed

Every machine.

The interactable moved between Idle / Engaged / Cooldown / Disabled. Derived from the replicated state, so it fires on every machine - including remote clients, which never see the semantic events.

Where it is raised. Everywhere, from the state apply - on the authority when it writes the state, and on each client when the update arrives. It is not sent; it is derived, which is why it costs no bandwidth.

The initial pair. On the first tick after Begin Play every component raises On Phase Changed and On Behavior Value Changed once with its settled values, so a Blueprint that only listens still learns the current state without polling. A late joiner gets that pair too, followed by any real delta.

What you get. The old phase and the new one. The four phases are Idle, Engaged, Cooldown and Disabled. This is the event a client-side door animation should bind - see Binding on the wrong machine.

Which machine? On Behavior Value Changed — Every machine.

On Behavior Value Changed

Every machine.

The interactable's behavior value changed (a door counter, a toggle). Derived, every machine, and it coalesces exactly as the state does: a client that observes one update where the authority applied two sees one row.

Where it is raised. Everywhere, from the same state apply as On Phase Changed, and always after it when both changed in the same update.

Coalescing is the contract, not a bug. Replication sends the current state, not a log of changes. An Instant interaction that moves the authority Idle -> Engaged -> Idle and the value 0 -> 1 reaches a client as one update: the client raises one On Behavior Value Changed and no On Phase Changed at all, because the phase it last observed never moved. Counting these events to count interactions will undercount; read the value, not the number of notifications.

What you get. The old value and the new one, as the interactable's behavior defines them - a door counter, a toggle's on/off, a dial's step.

Which machine? On Phase Changed — Every machine.

The host and standalone

On a listen host's own pawn - and in a standalone game - the authority and the owning client are the same object, so every one of the four kinds fires twice on one interactor. The two firings are identical except for Source:

KindFirst firingSecond firing
StartedSource == Predicted (the press)Source == Authority (the acceptance)
CompletedSource == Authority (the ending)Source == OwnerResolve (the answer)
CancelledSource == AuthoritySource == OwnerResolve
RejectedSource == Authority (targeted checks only)Source == OwnerResolve

A use animation therefore branches on Source == Predicted - one node - instead of counting events. Both firings carry the same Request Serial.

The payload

One struct, Interaction Event, on every semantic event of both components. Every field is read-only.

#FieldTypeDefault
1KindEUKInteractionEventKindRejected
2SourceEUKInteractionEventSourcePredicted
3InteractableTObjectPtr<UUKInteractableComponent>nullptr
4PawnTObjectPtr<APawn>nullptr
5PlayerStateTObjectPtr<APlayerState>nullptr
6PresetTObjectPtr<UUKInteractionPreset>nullptr
7IdentityTagFGameplayTagNone
8RequestSerialint320
9ReasonEUKInteractionReasonNone
10CustomReasonTagFGameplayTagNone
11UsabilityEUKUsabilityUsable
12HoldSecondsfloat0
13ElapsedSecondsfloat0
14bLocalboolFalse

Which fields are meaningful depends on Kind x Source. A cell marked - holds the default above and carries no meaning:

FieldStarted/AuthorityStarted/PredictedCompleted/bothRejected/AuthorityRejected/OwnerResolveCancelled/both
Kindyesyesyesyesyesyes
Sourceyesyesyesyesyesyes
Interactableyesyesyesyesyesyes
Pawnyesyesyesyesyesyes
PlayerStateyesyesyesyesyesyes
Presetyesyesyesyesyesyes
IdentityTagyesyesyesyesyesyes
RequestSerialyesyesyesyesyesyes
Reason---yesyesyes
CustomReasonTag---yesyesyes
Usability---yesyesyes
HoldSecondsyesyesyes--yes
ElapsedSeconds--yes-yesyes
bLocal----yesyes

Preset and Identity Tag are captured from the request record at the site, never re-read from the live component - so a rejected or cancelled pickup still says which item, and a preset swapped mid-interaction does not change an event already in flight.

The payload is never on the wire. It is not a replicated property, not an RPC parameter and not a SaveGame field. What replicates is the interactable's state; the events are what your machine says about it.

Reasons

The same 21 rows feed the shipped string table /UKInteraction/Core/Text/ST_UKInteraction_Reasons and the compiled English fallback, so this table, the asset and the fallback agree.

None is the one reason with no text: it is what a completion carries, and a prompt shows nothing for it.

#ReasonEnglish source
1None(empty)
2ThrottledToo many requests
3NotReplicatedNot connected
4AlreadyInteractingAlready interacting
5NoBehaviorNothing to do
6StaleSerialOut of date
7InvalidTargetNot available
8NoPlayerStateNo player
9NotUsableSomeone is using this
10OutOfRangeToo far away
11NoLineOfSightNo line of sight
12LockedLocked
13QueuedPlease wait
14AlreadyTakenAlready taken
15ReleasedReleased
16MovedMoved away
17DisabledDisabled
18TargetLostNo longer there
19OwnerLostLost control
20TimeoutTimed out
21CustomNot allowed

Custom returns the last segment of the event's Custom Reason Tag when that tag is set; the row above is what it falls back to when the tag is empty.

One reason is re-worded by usability, and only one. A press that reaches the authority after a pickup has gone resolves NotUsable, for which the table above says Someone is using this - a lie about an item that is simply not there. The kit answers that one combination with InvalidTarget's wording, Not available (NotUsable x Disabled), read by the C++-only UK::Interaction::Events::GetUsabilityQualifiedText. It is not a UFUNCTION, it adds no reason and no string, and it redirects to InvalidTarget's row rather than carrying a literal - so the shipped string table, and any translation of it, answers the qualified call exactly as it answers the unqualified one. Engaged and Cooldown are deliberately absent and keep the unqualified wording. See Docs/presets.md › Pickup.

Order at every site

The tables below are generated from the recorded order the kit's session tests assert.

The rule, in one line: raw delegate(s) of the owning system < interactor simple event < interactable simple event at every site, and a derived row sits immediately before the On State Changed row for the same update. The kit binds its own derived broadcast in InitializeComponent; a Blueprint binds on the Blueprint path, which is later - so your handler for On Phase Changed runs before your handler for On State Changed on the same update.

Which machine? On Phase Changed — Every machine. Which machine? On State Changed — Every machine.

A listen host's own pawn - the whole sequence

Listen, 1, preset DA_Instant, the host pressing its own door. The host is the authority and the owning client on one object, so this is every row the kit can produce for one interaction: 14 numbered entries + 3 derived rows = 17.

#EntryMachineSourceWhat it is
1OnPredictedInteractionStartedO-raw
2OnInteractionStarted{Predicted}OPredictedsimple, after the raw one at the same site
(derived)OnPhaseChanged(Idle, Engaged)A-derived; fires immediately before OnStateChanged(Idle->Engaged). the kit's binding precedes the recorder's
3OnStateChanged(Idle->Engaged)A-raw, inside ApplyState
4OnServerInteractionBeganA-raw, on acceptance
5OnInteractionStarted{Authority}AAuthoritysimple, inside the protocol's guard
6Ibl.OnInteractionStartedAAuthoritythe interactable's, via NotifyInteractionEvent
(derived)OnPhaseChanged(Engaged, Idle)A-derived; fires immediately before OnStateChanged(Engaged->Idle).
(derived)OnBehaviorValueChanged(0, 1)A-derived; fires immediately before OnStateChanged(Engaged->Idle). phase row first
7OnStateChanged(Engaged->Idle)A-raw
8OnServerInteractionEndedA-raw
9OnInteractionCompleted{Authority}AAuthoritysimple
10Ibl.OnInteractionCompletedAAuthoritythe interactable's, iff still valid
11OnPredictedInteractionEnded(ServerResolved)O-raw; fires NO simple event
12NotifyRequestResolvedO-raw. NOT A DELEGATE (UKInteractorComponent.cpp:2473) - no recorder can hold it. witnessed: true means its POSITION is asserted two-sidedly through UUKOutstandingStateProbe...
13OnRequestResolvedO-raw
14OnInteractionCompleted{OwnerResolve}OOwnerResolvesimple, the owning client's resolve

Entry 12, NotifyRequestResolved, is not a delegate - it is an internal call on the owning client (UKInteractorComponent.cpp:2473), so no recorder and no Blueprint can hold it. Its position is asserted two-sidedly instead (the interactor is still Interacting inside entry 11 and no longer inside entry 13); it is listed here because a customer reading the sequence needs to know why entry 13 follows entry 11 with a gap in between.

Which machine? The delegates this section names, one row each:

MemberMachine
On Behavior Value ChangedEvery machine.
On Interaction CompletedAuthority and owning client. (interactor) / Authority only. (interactable)
On Interaction StartedAuthority and owning client. (interactor) / Authority only. (interactable)
On Phase ChangedEvery machine.
On Predicted Interaction EndedOwning client only.
On Predicted Interaction StartedOwning client only.
On Request ResolvedOwning client only.
On Server Interaction BeganAuthority only.
On Server Interaction EndedAuthority only.
On State ChangedEvery machine.

Dedicated - the same sequence, split by machine

Dedicated, 3. Nothing is added and nothing is reordered: the same entries land, each on the machine that owns it.

MachineEntries
O1, 2, 11, 12, 13, 14
A3, 4, 5, 6, 7, 8, 9, 10
R(none of the numbered entries)

The three derived rows fire on A.

Plus rows that have no fixed position - they land when the coalesced replication update arrives, after entry 2 and in no fixed order relative to entries 11-14. Asserting one of these at a fixed index is a wrong assertion, not a wrong build:

EntryMachineLandsWhy it has no index
OnBehaviorValueChanged(0, 1)Oafter entry 2the coalesced client update; no fixed position relative to entries 11-14
OnStateChanged(Idle->Idle)Oafter entry 2the SAME coalesced update's raw row on the owning client. It canonicalises to Idle->Idle (the phase this machine last observed never moved), matches no numbered row, and lands in no fixed position relative...
OnBehaviorValueChanged(0, 1)Rwith the first bunchthe remote client records ONLY this row - what the world says, not what the server said

Two consequences worth stating plainly:

  • A remote client (R) records exactly one row for this interaction - the derived On Behavior Value Changed. It is never told the interaction started, completed or was rejected: those are the authority's events. This is the whole reason the derived pair exists.
  • The owning client's own replica row canonicalises to Idle->Idle for an Instant: the phase this machine last observed never moved, because the authority's Engaged and Idle collapsed into one update before it was sent.

Which machine? On Behavior Value Changed — Every machine. Which machine? On State Changed — Every machine.

When a handler cancels from inside an event

Both branches below are measured and pinned by the kit's tests, per engine and topology. They are recorded rather than designed: the order is a consequence of where the site broadcasts from, and pinning it is how a later engine change becomes a red test instead of a surprise.

A handler on the interactable's On Interaction Started calls Set Enabled (false).

The cancel completes before the broadcast that triggered it returns, so the interactable records Cancelled and then Started.

Engine / topologyInteractor recordsInteractable records
5.8/listenOnInteractionStarted{Predicted}, OnInteractionStarted{Authority}, OnInteractionCancelled{Authority}, OnInteractionCancelled{OwnerResolve}Ibl.OnInteractionCancelled, Ibl.OnInteractionStarted
5.8/dedicatedOnInteractionStarted{Predicted}, OnInteractionCancelled{OwnerResolve}Ibl.OnInteractionCancelled, Ibl.OnInteractionStarted

A handler on the raw On Server Interaction Began calls Set Enabled (false).

The accept site fires the simple On Interaction Started{Authority} above its StillActive() guard, so the customer's Cancelled lands first and the kit's own Started after it. The site is deliberately not moved - moving it would break the raw < simple order every other rule on this page depends on. Both memos therefore end on Started{Authority}.

Engine / topologyInteractor recordsInteractable records
5.8/listenOnInteractionStarted{Predicted}, OnInteractionCancelled{Authority}, OnInteractionCancelled{OwnerResolve}, OnInteractionStarted{Authority}Ibl.OnInteractionCancelled, Ibl.OnInteractionStarted
5.8/dedicatedOnInteractionCancelled{Authority}, OnInteractionStarted{Authority}Ibl.OnInteractionCancelled, Ibl.OnInteractionStarted

The one guarantee the kit makes here is the protocol's: for a given handler the branch is deterministic, there is no acknowledgement, no extra tick and exactly one result.

Which machine? On Interaction Cancelled — Authority and owning client. (interactor) / Authority only. (interactable) Which machine? On Interaction Started — Authority and owning client. (interactor) / Authority only. (interactable) Which machine? On Server Interaction Began — Authority only.

Binding on the wrong machine (UK-VAL-050)

The four On Interaction ... events on UK Interactable Component fire on the authority only. Bind one of them in a door Blueprint, run as a client, and nothing happens - the classic silent no-op. The kit refuses to be silent about it:

UK-VAL-050: BP_MyDoor binds OnInteractionCompleted on UKInteractableComponent, which fires on
the authority only - on this client it never fires (bind OnPhaseChanged / OnBehaviorValueChanged
for replicated reactions)

Where it is checked - twice, and only twice. A non-authority component looks at its four authority-only delegates:

  1. on the first tick after BeginPlay, on the same one-shot that fires the initial derived pair - which catches a binding made from the owner's Blueprint Event BeginPlay; and
  2. inside the kit's own state handler on this machine's first observed update - which catches a binding made later: by a construction script's deferred graph, by a runtime Add Dynamic, or on a replica whose first bunch arrived before it began play at all.

Two looks per component per machine is the whole budget. A binding made after the second look is not diagnosed - which is why the message says what to bind instead rather than only that something is wrong.

Once per bound class per process, not once per actor. The latch is keyed on (bound object's class, delegate name). Fifty doors of one Blueprint class making one mistake produce one line, not fifty.

The authority never warns. A listen host owns ROLE_Authority for its map-placed actors, so a host binding these events is correct and silent; a standalone game is likewise the authority, and UK-VAL-050 says nothing there either. If you see the warning, you are on a machine where the binding genuinely does nothing.

The knob. Project Settings > UK Interaction > Events > Warn On Client Bound Authority Events (UUKInteractionSettings::Events.bWarnOnClientBoundAuthorityEvents, default true). Setting it to false suppresses the check - it does not make the binding work.

What to bind instead. On a machine that is not the authority the replicated facts are the derived events: On Phase Changed and On Behavior Value Changed fire on every machine (see Which machine?). A reaction that must run for every player - a door swinging, a light turning on - belongs there. The semantic events answer who did what, and only the authority knows that.

A sibling warning, UK-VAL-052, is logged once per process when Get Reason Text had to fall back to the compiled English default because the string table was missing or had no row for the key - see Reasons.

Which machine? On Behavior Value Changed — Every machine. Which machine? On Phase Changed — Every machine.

Describe lines

Every simple event is logged at Verbose on LogUKInteraction through the same formatter the Describe Event node exposes.

[<A|O>][P?] #<serial> <Kind> <Reason> <owner>/<preset> id=<tag> t=<elapsed 2dp>s

A reference line, exactly as the kit's tests expect it:

[A] #7 Completed None BP_Door/DA_Instant id=UK.Test.Item.Key t=0.25s
TokenRule
prefix[A] / [O] / [O][P] - a function of Source alone, never of which component raised the event
serial# immediately followed by the payload's RequestSerial, no padding
kindthe EUKInteractionEventKind value name (Started
reasonthe EUKInteractionReason value NAME - never the localized text (a log line is not UI)
owner_slash_presetthe Interactable's OWNING ACTOR name, '/', the preset asset name
identityid= then the IdentityTag; an unset tag prints none_tag
elapsedt= then ElapsedSeconds at TWO decimal places, then s

The prefix is the fastest way to read a host's log: a listen host produces [O][P] for the press, [A] for the acceptance and the ending, and [O] for the owning client's resolve - the same four rows the host and standalone table describes. An unset identity prints id=None.

The line is only built when Verbose is active. The kit tests the log verbosity before it formats, so a shipping game running at Log pays nothing for these lines - not the string build, not the preset-name lookup. Turn them on with:

Log LogUKInteraction Verbose

Describe Event itself is a UK Interaction|Debug node: it formats whatever payload you hand it, whatever the log verbosity is. The verbosity rule governs the kit's own automatic lines.

The Blueprint surface

The canonical list of every Blueprint-visible member of the kit. The kit's tests check it against the shipped modules in both directions: a Blueprint-visible member missing from the list, or a listed member that does not exist, fails the suite.

Three tiers, and the tier is the category you see in the node palette:

TierCategoryWhat is in it
SimpleUK InteractionThe day-one set - everything Docs/quick-start.md's quickstart-nodes block lists. 26 members, capped at 12 per class.
AdvancedUK Interaction|AdvancedThe raw protocol delegates, the state writes, the seams, the accessors. Correct to use, easy to misuse.
DebugUK Interaction|DebugThe 8 read-only diagnostic readers: Get Last Detection Stats, Get Protocol Stats, Get Last Interaction Event, Get Lock Stats, Get Pickup Stats, Describe Event, Get Input Stats, Get Prompt Stats.

The Simple tier, by class

ClassMemberNodeMachineDocumented at
UKInteractorComponentOnFocusChangedOn Focus ChangedLocal machine.api.md > On Focus Changed
UKInteractorComponentOnInteractDeniedOn Interact DeniedLocal machine.api.md > On Interact Denied
UKInteractorComponentBeginInteractBegin InteractOwning client only.api.md > Begin Interact
UKInteractorComponentEndInteractEnd InteractOwning client only.api.md > End Interact
UKInteractorComponentGetFocusGet FocusOwning client only.api.md > Get Focus
UKInteractorComponentGetInteractorStateGet Interactor StateOwning client only.api.md > Get Interactor State
UKInteractorComponentGetHoldProgressGet Hold ProgressOwning client only.api.md > Get Hold Progress
UKInteractorComponentOnInteractionStartedOn Interaction StartedAuthority and owning client.events.md > On Interaction Started
UKInteractorComponentOnInteractionCompletedOn Interaction CompletedAuthority and owning client.events.md > On Interaction Completed
UKInteractorComponentOnInteractionRejectedOn Interaction RejectedAuthority and owning client.events.md > On Interaction Rejected
UKInteractorComponentOnInteractionCancelledOn Interaction CancelledAuthority and owning client.events.md > On Interaction Cancelled
UKInteractableComponentGetUsabilityGet UsabilityEvery machine.api.md > Get Usability
UKInteractableComponentGetPresetGet PresetEvery machine.api.md > Get Preset
UKInteractableComponentGetResolvedPromptTextGet Resolved Prompt TextEvery machine.api.md > Get Resolved Prompt Text
UKInteractableComponentGetResolvedPriorityGet Resolved PriorityEvery machine.api.md > Get Resolved Priority
UKInteractableComponentGetCooldownRemainingGet Cooldown RemainingEvery machine.api.md > Get Cooldown Remaining
UKInteractableComponentGetStateGet StateEvery machine.api.md > Get State
UKInteractableComponentOnPhaseChangedOn Phase ChangedEvery machine.events.md > On Phase Changed
UKInteractableComponentOnBehaviorValueChangedOn Behavior Value ChangedEvery machine.events.md > On Behavior Value Changed
UKInteractableComponentOnInteractionStartedOn Interaction StartedAuthority only.events.md > On Interaction Started
UKInteractableComponentOnInteractionCompletedOn Interaction CompletedAuthority only.events.md > On Interaction Completed
UKInteractableComponentOnInteractionCancelledOn Interaction CancelledAuthority only.events.md > On Interaction Cancelled
UKInteractableComponentOnInteractionRejectedOn Interaction RejectedAuthority only.events.md > On Interaction Rejected
UKInteractionFunctionLibraryGetReasonTextGet Reason TextEvery machine.api.md > Get Reason Text
UKPromptSubsystemGetPromptStateGet Prompt StateOwning client only.api.md > Get Prompt State
UKPromptSubsystemOnPromptStateChangedOn Prompt State ChangedOwning client only.api.md > On Prompt State Changed

Per-class Simple counts, computed from the same list: UKInteractorComponent 11, UKInteractableComponent 12, UKInteractionFunctionLibrary 1, UKPromptSubsystem 2 - against a cap of 12. UKInteractableComponent is exactly at the cap, so a new Simple member there would have to retire an existing one.

UKInteractorInputSubsystem, UKPromptSubsystem, UKGlyphProvider, UKPromptWidgetBase live in the client-only module UKInteractionClient, so their nodes do not exist on a dedicated server.

Advanced and Debug

ClassMembers
UKInteractorComponent (Advanced)OnBeginInteractRequested, OnEndInteractRequested, GetInteractorVelocity, SetDetectionProfile, SetDetectionEnabled, OnPredictedInteractionStarted, OnPredictedInteractionEnded, OnRequestAccepted, OnRequestResolved, OnServerInteractionBegan, OnServerInteractionEnded, GetPendingRequest, GetActiveInteraction, GetLastPredictedCancelReason
UKInteractorComponent (Debug)GetLastDetectionStats, GetProtocolStats, GetLastInteractionEvent
UKInteractableComponent (Advanced)SetEnabled, CooldownRemaining, SanitizeRestoredState, GetInteractionPoint, ApplyState, BindAndInitialize, OnStateChanged
UKInteractableComponent (Debug)GetLastInteractionEvent
UKInteractionBehavior (Advanced)ServerBegin, ServerComplete, ServerCancel, TickHold, GetPredictedHoldSeconds
UKBehavior_Hold (Advanced)ShouldCancelHold
UKInteractionArbiter (Advanced)Arbitrate, OnInteractionEnded
UKInteractionLockComponent (Advanced)GetLockState, GetQueuePosition, IsReservedFor, GetReservationEndServerTime, GetEffectiveQueueLength, OnLockStateChanged, ClearQueue, RemoveFromQueue
UKInteractionLockComponent (Debug)GetLockStats
UKPresetFunctionLibrary (Advanced)IsDoorOpen, IsDoorLocked, GetLeverPosition, GetPickupTakeCount, IsPickupAvailable, GetPickupRespawnRemaining, SetDoorLocked
UKPickupSubsystem (Debug)GetPickupStats
UKInteractionFunctionLibrary (Advanced)IsLocalRejection
UKInteractionFunctionLibrary (Debug)DescribeEvent
UKInteractorInputSubsystem (Advanced)OnInputDeviceKindChanged
UKInteractorInputSubsystem (Debug)GetInputStats
UKPromptSubsystem (Advanced)SetPromptWidgetEnabled, SetGlyphProvider, GetGlyph
UKPromptSubsystem (Debug)GetPromptStats
UKGlyphProvider (Advanced)GetGlyph
UKPromptWidgetBase (Advanced)OnPromptStateChanged

Which machine? The delegates this section names, one row each:

MemberMachine
On Begin Interact RequestedOwning client only.
On End Interact RequestedOwning client only.
On Input Device Kind ChangedOwning client only.
On Lock State ChangedEvery machine.
On Predicted Interaction EndedOwning client only.
On Predicted Interaction StartedOwning client only.
On Prompt State ChangedOwning client only.
On Request AcceptedOwning client only.
On Request ResolvedOwning client only.
On Server Interaction BeganAuthority only.
On Server Interaction EndedAuthority only.
On State ChangedEvery machine.

OnServerRequestRejectedNative is deliberately not on this page: it is a C++-only native multicast with no Blueprint node, so it is not a row.

Dispatch cost

What the event layer costs, measured rather than estimated. Both figures are advisory: they come from a single performance run on one engine (5.8) and one test machine (measured 2026-09-08), so they describe that machine and not yours. The kit does not fail a build on them, and the budget is never widened to make a number fit.

WhatMeasured (median)Budget
One authority-side simple event, bound0.7 us4.0 us
The same site, unbound0.4 us4.0 us
The same site, unbound, with Verbose on2.3 us(no budget - diagnostics)
One unbound simple event, isolated0.5 us2.0 us

Read it this way:

  • Binding costs almost nothing. Bound minus unbound at the same site is a few tenths of a microsecond. The cost of the event layer is the payload fill and the memo write, not the broadcast.
  • Verbose is the expensive setting, and that is the point of the guard: with Verbose active the same site measures 2.3 us against 0.4 us at Log. Leave it off in a shipping game and you pay none of it.
  • The per-event cost the kit's performance model assumes for an unbound event is 0.4 us, taken from this measurement (the original design target was 0.3 us).

The bracket is a test-only clock stamp on the authority from the protocol's OnServerInteractionEnded to the interactable's On Interaction Completed. It contains the payload fill, the memo write, the guarded verbosity test and both broadcasts. It does not contain the three derived events (they fire earlier, from the state apply) and it does not contain the protocol's reply RPC (it is sent after this site).

Which machine? On Interaction Completed — Authority and owning client. (interactor) / Authority only. (interactable) Which machine? On Server Interaction Ended — Authority only.

Known gaps

Everything on this page is shipped and covered by tests unless it is listed here.

GapState
Engine coverage. In one line: "supported on 5.6–5.8; multiplayer end-to-end suite run on all three". Compile, logic and structural tests run on 5.6, 5.7 and 5.8, and so does the in-process session suite - which is where every order, cancel and hand-off behaviour on this page is asserted.Closed before the first Fab release
No mid-session join is asserted in-process. A late joiner's initial derived pair is correct by construction and is observed at session start on every client; the join case itself needs a multi-process test run.Open
No shared two-interactor session rig. The pickup hand-off on this page is tested with a second presser built inside that one test rather than by a shared rig.Open
UK-VAL-051 - the editor check that explains the seam contract before Play - runs in the kit's editor validator (validator.md), which the Lite edition does not include. UK-VAL-050 is a runtime line and runs in every edition; UK-VAL-052 is checked in both halves - before Play by the validator, and at runtime in every edition.Not in Lite
The Lite content set omits Core/Text/. A Lite package would fall back to the compiled English default and log UK-VAL-052 once. Whether Lite ships the string table is undecided.Open
Should Cancel Hold is not yet a member. One clause of the naming rule names a behavior member that the kit's basic behaviors do not have yet.Open
Entry 12 of the host sequence is not a delegate, so it cannot be recorded directly; its position is asserted two-sidedly instead.By design
The performance figures are advisory and single-engine (above).Advisory

Engine coverage - what was verified where

Lane5.65.75.8
Compile (UKInteraction, UKInteractionClient, UKInteractionTests)yesyesyes
Structural gates (Python, engine-free)n/a - engine-freen/an/a
Logic specs (library, payload, memo, derived predicate)yesyesyes
Reflection specs - flag halvesyesyesyes
Reflection specs - metadata halves (editor-only data)editor laneeditor laneeditor lane
In-process session specs (order, cancels, hand-off, UK-VAL-050)pendingpendingyes
perf lane (dispatch, unbound budget)n/an/ayes (advisory)
Cooked -game reason-table resolutionn/an/ayes

The string table asset itself is authored and saved on the 5.6 workspace, which is what makes it loadable by all three engines.