How-To: Route UI Requests

Goal: send every player-initiated inventory action from the client through the routing component, receive the outcome, and customize who may touch which inventory.

Prerequisites
Quick Start step 5 (routing component on the PlayerController) and How-To: Get the Building Blocks.

1. Know the client entry points

Each function below is BlueprintCallable on UCrimsonInventoryRoutingComponent, runs on the owning client, and forwards to a reliable Server_* RPC with a _Validate that rejects null targets, zero counts, and out-of-range values before the body runs.

Client callServer RPCManager call on success
MoveItem(Target, EntryId, T)Server_MoveItemMoveItemByEntry
TransferItem(From, To, EntryId, Count)Server_TransferItemTransferEntryTo with no hint
TransferItemTo(From, To, EntryId, Count, Hint)Server_TransferItemToTransferEntryTo
SplitStack(Target, EntryId, Amount)Server_SplitStackSplitStackByEntry
SortInventory(Target, Request)Server_SortInventoryRequestAsyncSort
DropItem(Target, EntryId, Count, DropLocation)Server_DropItemDropItemByEntry
LockEntry(Target, EntryId, bLocked)Server_LockEntrySetEntryLockFlag with User
OpenContainer(Container)Server_RequestOpenContainerResolves the scoped inventory, opens a session, fires Client_OnContainerOpened
CloseContainer()Server_CloseContainerEnds the session, fires Client_OnContainerClosed
TakePickupSlot(PickupActor, SlotType, SlotIndex, RequestedQuantity, Destination)Server_TakePickupSlotACrimsonPickupActorBase::TakeSlot
TakePickupAll(PickupActor, Destination)Server_TakePickupAllACrimsonPickupActorBase::TakeAll

2. Send a request and read the result

After every routed request the server broadcasts FCrimsonInventoryOperationResult on Crimson.Inventory.Message.OperationResult to the requesting client (via Client_NotifyOperationResult) and locally on the server. This is the reliable "your request finished" signal: it reaches you even when the mutated inventory is a chest with no owning connection.

FieldMeaning
OperationECrimsonInventoryOperation: Move, Transfer, Split, Drop, Lock, OpenContainer, TakePickupSlot, TakePickupAll, Sort.
bSuccessWhether the server applied it.
FailureReasonLocalized text from the layout, constraint, or access check. Empty on success.
EntryIdThe entry the request targeted, when applicable.

Bind Event to On Operation Result on the routing component in your inventory screen's Event Construct. In the event, branch on bSuccess and show Failure Reason. Then call Split Stack (Target, Entry Id, Amount = 2) from the split dialog.

Verify
Play As Client. Split a stack of 1: the client gets Operation = Split, bSuccess = false with a reason; split a stack of 5: bSuccess = true and Crimson.Inventory.Message.StackChanged fires for both the source and the new entry.

3. Customize the access policy

CanAccessInventory runs on the server before every routed operation. The default allows: the player's own chain (PlayerController, its PlayerState, its pawn), the player's personal containers (FindPersonalContainer), and any inventory with an open container session - with MaxContainerInteractionDistance enforced between the pawn and the container actor. CanOpenContainer gates OpenContainer separately.

Create a Blueprint child of CrimsonInventoryRoutingComponent and override Can Access Inventory (call the parent first, then add your rule) and, if you use party or map-instance scoped containers, Resolve Party Key / Resolve Map Instance Key. Use that child on the PlayerController.

Verify
From a client, call Move Item against another player's inventory: the result is bSuccess = false and the server logs the access denial. Your override's extra rule admits the case you added.

4. Personal containers

GetOrCreatePersonalContainer(ContainerTag, Layout) and FindPersonalContainer(ContainerTag) on the routing component create a per-player manager that lives on the PlayerController (a bank stash, a quest-item pouch). PerPlayer scoped containers resolve to these. They serialize through FCrimsonInventoryRoutingSaveData.

These two functions are C++ only today: PerPlayer containers call them for you (see How-To: Build a World Container), and Get Save Data / Apply Save Data on the routing component round-trip them.

Verify
Dev Tools -> Routing lists the personal container under the PlayerController with its tag.

See also

  • How-To: Build a World Container
  • How-To: Drop and Pick Up Items
  • How-To: React to Inventory Messages
  • Concept: Routing and Access