> For clean Markdown of any page, append .md to the page URL. > For a complete documentation index, see https://docs.forboc.ai/ue/npc/llms.txt. > For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.forboc.ai/_mcp/server. # NPC State And Protocol > Register NPCs, inspect slice state, and run the multi-turn protocol NPC state in the UE plugin lives in `NPCSlice`, not in a standalone immutable `FNPC` value. The slice mirrors the entity-adapter pattern from `@forbocai/core`. ## Primary Type ```cpp USTRUCT(BlueprintType) struct FNPCInternalState { UPROPERTY(BlueprintReadOnly) FString Id; UPROPERTY(BlueprintReadOnly) FString Persona; UPROPERTY(BlueprintReadOnly) FAgentState State; UPROPERTY(BlueprintReadOnly) TArray History; FAgentAction LastAction; bool bHasLastAction; UPROPERTY(BlueprintReadOnly) bool bIsBlocked; UPROPERTY(BlueprintReadOnly) FString BlockReason; UPROPERTY(BlueprintReadOnly) TArray StateLog; }; ``` The slice keeps an entity map plus `ActiveNpcId`. ## Register An NPC ```cpp #include "RuntimeStore.h" #include "NPC/NPCId.h" #include "NPC/NPCSlice.h" auto Store = createSDKStore(); FNPCInternalState Npc; Npc.Id = NPCId::GenerateNPCId(); Npc.Persona = TEXT("A careful quartermaster."); Npc.State = TypeFactory::AgentState(TEXT("{\"mood\":\"guarded\"}")); Store.dispatch(NPCSlice::Actions::SetNPCInfo(Npc)); ``` `SetNPCInfo` upserts the entity and also makes it active. ## Selectors * `NPCSlice::SelectNPCById(State, Id)` * `NPCSlice::SelectAllNPCs(State)` * `NPCSlice::SelectActiveNpcId(State)` * `NPCSlice::SelectActiveNPC(State)` ```cpp const auto Active = NPCSlice::SelectActiveNPC(Store.getState().NPCs); if (Active.hasValue) { UE_LOG(LogTemp, Display, TEXT("%s"), *Active.value.Persona); } ``` ## Update State Use slice actions for direct state changes. ```cpp Store.dispatch(NPCSlice::Actions::UpdateNPCState( Npc.Id, TypeFactory::AgentState(TEXT("{\"mood\":\"friendly\"}")))); ``` * `SetNPCState` replaces the full agent state * `UpdateNPCState` merges a JSON delta into the current state * `AddToHistory`, `SetHistory`, `SetLastAction`, `BlockAction`, and `ClearBlock` support the protocol runtime ## Run The Protocol For synchronous tooling, use `SDKOps::ProcessNpc(...)`. ```cpp #include "CLI/CliOperations.h" #include "Memory/MemoryThunks.h" #include "Cortex/CortexThunks.h" SDKOps::InitNodeMemory(Store, TEXT("/tmp/quartermaster.db")); SDKOps::InitCortex(Store, TEXT("smollm2-135m")); FAgentResponse Response = SDKOps::ProcessNpc(Store, Npc.Id, TEXT("What supplies are left?")); ``` For direct parity with `packages/core`, dispatch `rtk::processNPC(...)` yourself from `Protocol/ProtocolThunks.h`. ## Protocol Semantics * The API can return `IdentifyActor`, `QueryVector`, `ExecuteInference`, and `Finalize` instructions. * `QueryVector` requires local memory support in the runtime. * `ExecuteInference` requires local cortex support in the runtime. * On successful finalize, the runtime persists memory instructions, merges state deltas, records the last action, and appends user/assistant history. * On invalid finalize, the slice records `bIsBlocked` and `BlockReason`. ## Convenience Wrappers `SDKOps` exposes the same NPC lifecycle in synchronous form: * `SDKOps::CreateNpc(Store, Persona)` * `SDKOps::GetActiveNpc(Store)` * `SDKOps::UpdateNpc(Store, NpcId, Delta)` * `SDKOps::ProcessNpc(Store, NpcId, Text)` * `SDKOps::ListNpcs(Store)` > Register NPCs, inspect slice state, and run the multi-turn protocol