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.
| Event | Component | A | O | R | Machine phrase |
|---|---|---|---|---|---|
OnInteractionStarted | interactor | 1 | 1 | 0 | Authority and owning client. |
OnInteractionCompleted | interactor | 1 | 1 | 0 | Authority and owning client. |
OnInteractionCancelled | interactor | 1 | 1 | 0 | Authority and owning client. |
OnInteractionRejected | interactor | 1 | 1 | 0 | Authority and owning client. |
OnInteractionStarted | interactable | 1 | 0 | 0 | Authority only. |
OnInteractionCompleted | interactable | 1 | 0 | 0 | Authority only. |
OnInteractionCancelled | interactable | 1 | 0 | 0 | Authority only. |
OnInteractionRejected | interactable | 1 | 0 | 0 | Authority only. |
OnPhaseChanged | interactable | 1 | 1 | 1 | Every machine. |
OnBehaviorValueChanged | interactable | 1 | 1 | 1 | Every machine. |
OnInteractDenied | interactor | 0 | 1 | 0 | Local machine. |
OnFocusChanged | interactor | 0 | 1 | 0 | Local 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:
| Kind | First firing | Second firing |
|---|---|---|
Started | Source == Predicted (the press) | Source == Authority (the acceptance) |
Completed | Source == Authority (the ending) | Source == OwnerResolve (the answer) |
Cancelled | Source == Authority | Source == OwnerResolve |
Rejected | Source == 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.
| # | Field | Type | Default |
|---|---|---|---|
| 1 | Kind | EUKInteractionEventKind | Rejected |
| 2 | Source | EUKInteractionEventSource | Predicted |
| 3 | Interactable | TObjectPtr<UUKInteractableComponent> | nullptr |
| 4 | Pawn | TObjectPtr<APawn> | nullptr |
| 5 | PlayerState | TObjectPtr<APlayerState> | nullptr |
| 6 | Preset | TObjectPtr<UUKInteractionPreset> | nullptr |
| 7 | IdentityTag | FGameplayTag | None |
| 8 | RequestSerial | int32 | 0 |
| 9 | Reason | EUKInteractionReason | None |
| 10 | CustomReasonTag | FGameplayTag | None |
| 11 | Usability | EUKUsability | Usable |
| 12 | HoldSeconds | float | 0 |
| 13 | ElapsedSeconds | float | 0 |
| 14 | bLocal | bool | False |
Which fields are meaningful depends on Kind x Source. A cell marked - holds the
default above and carries no meaning:
| Field | Started/Authority | Started/Predicted | Completed/both | Rejected/Authority | Rejected/OwnerResolve | Cancelled/both |
|---|---|---|---|---|---|---|
Kind | yes | yes | yes | yes | yes | yes |
Source | yes | yes | yes | yes | yes | yes |
Interactable | yes | yes | yes | yes | yes | yes |
Pawn | yes | yes | yes | yes | yes | yes |
PlayerState | yes | yes | yes | yes | yes | yes |
Preset | yes | yes | yes | yes | yes | yes |
IdentityTag | yes | yes | yes | yes | yes | yes |
RequestSerial | yes | yes | yes | yes | yes | yes |
Reason | - | - | - | yes | yes | yes |
CustomReasonTag | - | - | - | yes | yes | yes |
Usability | - | - | - | yes | yes | yes |
HoldSeconds | yes | yes | yes | - | - | yes |
ElapsedSeconds | - | - | yes | - | yes | yes |
bLocal | - | - | - | - | yes | yes |
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.
| # | Reason | English source |
|---|---|---|
| 1 | None | (empty) |
| 2 | Throttled | Too many requests |
| 3 | NotReplicated | Not connected |
| 4 | AlreadyInteracting | Already interacting |
| 5 | NoBehavior | Nothing to do |
| 6 | StaleSerial | Out of date |
| 7 | InvalidTarget | Not available |
| 8 | NoPlayerState | No player |
| 9 | NotUsable | Someone is using this |
| 10 | OutOfRange | Too far away |
| 11 | NoLineOfSight | No line of sight |
| 12 | Locked | Locked |
| 13 | Queued | Please wait |
| 14 | AlreadyTaken | Already taken |
| 15 | Released | Released |
| 16 | Moved | Moved away |
| 17 | Disabled | Disabled |
| 18 | TargetLost | No longer there |
| 19 | OwnerLost | Lost control |
| 20 | Timeout | Timed out |
| 21 | Custom | Not 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.
| # | Entry | Machine | Source | What it is |
|---|---|---|---|---|
| 1 | OnPredictedInteractionStarted | O | - | raw |
| 2 | OnInteractionStarted{Predicted} | O | Predicted | simple, 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 |
| 3 | OnStateChanged(Idle->Engaged) | A | - | raw, inside ApplyState |
| 4 | OnServerInteractionBegan | A | - | raw, on acceptance |
| 5 | OnInteractionStarted{Authority} | A | Authority | simple, inside the protocol's guard |
| 6 | Ibl.OnInteractionStarted | A | Authority | the 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 |
| 7 | OnStateChanged(Engaged->Idle) | A | - | raw |
| 8 | OnServerInteractionEnded | A | - | raw |
| 9 | OnInteractionCompleted{Authority} | A | Authority | simple |
| 10 | Ibl.OnInteractionCompleted | A | Authority | the interactable's, iff still valid |
| 11 | OnPredictedInteractionEnded(ServerResolved) | O | - | raw; fires NO simple event |
| 12 | NotifyRequestResolved | O | - | raw. NOT A DELEGATE (UKInteractorComponent.cpp:2473) - no recorder can hold it. witnessed: true means its POSITION is asserted two-sidedly through UUKOutstandingStateProbe... |
| 13 | OnRequestResolved | O | - | raw |
| 14 | OnInteractionCompleted{OwnerResolve} | O | OwnerResolve | simple, 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:
| Member | Machine |
|---|---|
On Behavior Value Changed | Every machine. |
On Interaction Completed | Authority and owning client. (interactor) / Authority only. (interactable) |
On Interaction Started | Authority and owning client. (interactor) / Authority only. (interactable) |
On Phase Changed | Every machine. |
On Predicted Interaction Ended | Owning client only. |
On Predicted Interaction Started | Owning client only. |
On Request Resolved | Owning client only. |
On Server Interaction Began | Authority only. |
On Server Interaction Ended | Authority only. |
On State Changed | Every 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.
| Machine | Entries |
|---|---|
O | 1, 2, 11, 12, 13, 14 |
A | 3, 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:
| Entry | Machine | Lands | Why it has no index |
|---|---|---|---|
OnBehaviorValueChanged(0, 1) | O | after entry 2 | the coalesced client update; no fixed position relative to entries 11-14 |
OnStateChanged(Idle->Idle) | O | after entry 2 | the 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) | R | with the first bunch | the 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 derivedOn 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->Idlefor anInstant: the phase this machine last observed never moved, because the authority'sEngagedandIdlecollapsed 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 / topology | Interactor records | Interactable records |
|---|---|---|
5.8/listen | OnInteractionStarted{Predicted}, OnInteractionStarted{Authority}, OnInteractionCancelled{Authority}, OnInteractionCancelled{OwnerResolve} | Ibl.OnInteractionCancelled, Ibl.OnInteractionStarted |
5.8/dedicated | OnInteractionStarted{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 / topology | Interactor records | Interactable records |
|---|---|---|
5.8/listen | OnInteractionStarted{Predicted}, OnInteractionCancelled{Authority}, OnInteractionCancelled{OwnerResolve}, OnInteractionStarted{Authority} | Ibl.OnInteractionCancelled, Ibl.OnInteractionStarted |
5.8/dedicated | OnInteractionCancelled{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:
- 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 BlueprintEvent BeginPlay; and - 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
| Token | Rule |
|---|---|
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 |
kind | the EUKInteractionEventKind value name (Started |
reason | the EUKInteractionReason value NAME - never the localized text (a log line is not UI) |
owner_slash_preset | the Interactable's OWNING ACTOR name, '/', the preset asset name |
identity | id= then the IdentityTag; an unset tag prints none_tag |
elapsed | t= 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:
| Tier | Category | What is in it |
|---|---|---|
| Simple | UK Interaction | The day-one set - everything Docs/quick-start.md's quickstart-nodes block lists. 26 members, capped at 12 per class. |
| Advanced | UK Interaction|Advanced | The raw protocol delegates, the state writes, the seams, the accessors. Correct to use, easy to misuse. |
| Debug | UK Interaction|Debug | The 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
| Class | Member | Node | Machine | Documented at |
|---|---|---|---|---|
UKInteractorComponent | OnFocusChanged | On Focus Changed | Local machine. | api.md > On Focus Changed |
UKInteractorComponent | OnInteractDenied | On Interact Denied | Local machine. | api.md > On Interact Denied |
UKInteractorComponent | BeginInteract | Begin Interact | Owning client only. | api.md > Begin Interact |
UKInteractorComponent | EndInteract | End Interact | Owning client only. | api.md > End Interact |
UKInteractorComponent | GetFocus | Get Focus | Owning client only. | api.md > Get Focus |
UKInteractorComponent | GetInteractorState | Get Interactor State | Owning client only. | api.md > Get Interactor State |
UKInteractorComponent | GetHoldProgress | Get Hold Progress | Owning client only. | api.md > Get Hold Progress |
UKInteractorComponent | OnInteractionStarted | On Interaction Started | Authority and owning client. | events.md > On Interaction Started |
UKInteractorComponent | OnInteractionCompleted | On Interaction Completed | Authority and owning client. | events.md > On Interaction Completed |
UKInteractorComponent | OnInteractionRejected | On Interaction Rejected | Authority and owning client. | events.md > On Interaction Rejected |
UKInteractorComponent | OnInteractionCancelled | On Interaction Cancelled | Authority and owning client. | events.md > On Interaction Cancelled |
UKInteractableComponent | GetUsability | Get Usability | Every machine. | api.md > Get Usability |
UKInteractableComponent | GetPreset | Get Preset | Every machine. | api.md > Get Preset |
UKInteractableComponent | GetResolvedPromptText | Get Resolved Prompt Text | Every machine. | api.md > Get Resolved Prompt Text |
UKInteractableComponent | GetResolvedPriority | Get Resolved Priority | Every machine. | api.md > Get Resolved Priority |
UKInteractableComponent | GetCooldownRemaining | Get Cooldown Remaining | Every machine. | api.md > Get Cooldown Remaining |
UKInteractableComponent | GetState | Get State | Every machine. | api.md > Get State |
UKInteractableComponent | OnPhaseChanged | On Phase Changed | Every machine. | events.md > On Phase Changed |
UKInteractableComponent | OnBehaviorValueChanged | On Behavior Value Changed | Every machine. | events.md > On Behavior Value Changed |
UKInteractableComponent | OnInteractionStarted | On Interaction Started | Authority only. | events.md > On Interaction Started |
UKInteractableComponent | OnInteractionCompleted | On Interaction Completed | Authority only. | events.md > On Interaction Completed |
UKInteractableComponent | OnInteractionCancelled | On Interaction Cancelled | Authority only. | events.md > On Interaction Cancelled |
UKInteractableComponent | OnInteractionRejected | On Interaction Rejected | Authority only. | events.md > On Interaction Rejected |
UKInteractionFunctionLibrary | GetReasonText | Get Reason Text | Every machine. | api.md > Get Reason Text |
UKPromptSubsystem | GetPromptState | Get Prompt State | Owning client only. | api.md > Get Prompt State |
UKPromptSubsystem | OnPromptStateChanged | On Prompt State Changed | Owning 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
| Class | Members |
|---|---|
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:
| Member | Machine |
|---|---|
On Begin Interact Requested | Owning client only. |
On End Interact Requested | Owning client only. |
On Input Device Kind Changed | Owning client only. |
On Lock State Changed | Every machine. |
On Predicted Interaction Ended | Owning client only. |
On Predicted Interaction Started | Owning client only. |
On Prompt State Changed | Owning client only. |
On Request Accepted | Owning client only. |
On Request Resolved | Owning client only. |
On Server Interaction Began | Authority only. |
On Server Interaction Ended | Authority only. |
On State Changed | Every 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.
| What | Measured (median) | Budget |
|---|---|---|
| One authority-side simple event, bound | 0.7 us | 4.0 us |
| The same site, unbound | 0.4 us | 4.0 us |
The same site, unbound, with Verbose on | 2.3 us | (no budget - diagnostics) |
| One unbound simple event, isolated | 0.5 us | 2.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.
Verboseis the expensive setting, and that is the point of the guard: withVerboseactive the same site measures 2.3 us against 0.4 us atLog. 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.
| Gap | State |
|---|---|
| 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
| Lane | 5.6 | 5.7 | 5.8 |
|---|---|---|---|
Compile (UKInteraction, UKInteractionClient, UKInteractionTests) | yes | yes | yes |
| Structural gates (Python, engine-free) | n/a - engine-free | n/a | n/a |
| Logic specs (library, payload, memo, derived predicate) | yes | yes | yes |
| Reflection specs - flag halves | yes | yes | yes |
| Reflection specs - metadata halves (editor-only data) | editor lane | editor lane | editor lane |
In-process session specs (order, cancels, hand-off, UK-VAL-050) | pending | pending | yes |
perf lane (dispatch, unbound budget) | n/a | n/a | yes (advisory) |
Cooked -game reason-table resolution | n/a | n/a | yes |
The string table asset itself is authored and saved on the 5.6 workspace, which is what makes it loadable by all three engines.