How-To: Build a World Container

Goal: place a chest in the level, decide who shares its contents, open it from the client, and hand the resolved inventory to your UI.

Prerequisites
How-To: Route UI Requests and How-To: Choose a Layout. Interaction prompts are optional: open a container by calling OpenContainer on the routing component from whatever the player uses (a key press, your interaction system). (CrimsonCore - requires all Crimson plugins) ACrimsonInteractableContainerActor adds an interact prompt and open conditions.

1. Subclass the container actor

PropertyMeaning
ContainerTagIdentity. Scoped containers with the same tag in a map share one instance per scope key.
ScopeShared (one inventory on the actor, everyone sees it), PerPlayer (personal container on each player's routing component), PerParty (one instance per ResolvePartyKey), PerMapInstance (one per ResolveMapInstanceKey).
DefaultLayoutLayout for the actor's own inventory and for every scoped instance.
DefaultIntConstraints / DefaultFloatConstraintsCapacity tags seeded into the inventory by ApplyInventoryDefaults (called from BeginPlay on the actor's own inventory and on each new scoped instance).
RuntimeConstraintsConstraint classes added to every inventory this container creates, one instance per class, replicated. Defaults to Constraint: Inventory Slots, which keeps the slot-usage counter the capacity meter reads. Applied by ApplyInventoryDefaults before any item lands.
CanPlayerOpenNative event: the access policy the routing component consults, with a player-facing reason. Default allows any valid player.
NotifyContainerOpenedServer hook called by the routing component when a player opens the container. (An interact prompt, OpenContainerUIAbility and InteractionText come with CrimsonCore's ACrimsonInteractableContainerActor.)

Blueprint Class -> `CrimsonContainerActorBase`, name it BP_Chest. Add a Static Mesh component for the visual. In Class Defaults set Container Tag, Scope, Default Layout, and one Default Int Constraints entry Crimson.Inventory.Constraint.Slots = 24.

Verify
Place BP_Chest and play. Dev Tools -> Containers lists it with its tag, scope, and (for Shared) an inventory with 24 slots.

2. Open it from the client

OpenContainer asks the server. The server checks CanOpenContainer (by default the container's own CanPlayerOpen policy, whose reason text reaches the client on refusal), resolves the inventory for the container's scope (creating an ACrimsonInstancedInventoryActor through UCrimsonInventoryInstanceSubsystem if needed, and adding this player as a viewer so it becomes net-relevant), records an open session, and answers with Client_OnContainerOpened. Bind OnContainerOpened to receive FCrimsonContainerOpenResult.

In your interaction handler: Find Routing Component -> Open Container (Container = the hit actor). Beforehand, bind On Container Opened on the routing component: on bSuccess, create your container screen and give it Inventory. Bind On Container Closed to remove the screen. Call Close Container when the player dismisses it.

Verify
Play As Client. Open the chest: OnContainerOpened fires on that client with a valid Inventory; Move Item into it succeeds. Walk beyond MaxContainerInteractionDistance without closing: the next move fails with an access denial.

3. Scoped containers

For PerPlayer, the resolved inventory is the player's personal container (FindPersonalContainer(ContainerTag)) - it lives on their PlayerController and saves with FCrimsonInventoryRoutingSaveData. For PerParty and PerMapInstance the server spawns one ACrimsonInstancedInventoryActor per FCrimsonContainerInstanceKey (ContainerTag + the scope key from ResolvePartyKey / ResolveMapInstanceKey); the actor is only relevant to its current viewers. OnScopedInstanceCreated fires on the container actor so you can seed loot.

Set Scope to Per Player. Override On Scoped Instance Created on BP_Chest to Add Item Definition into New Inventory the first time it is created. Override Resolve Party Key on your routing component child for Per Party.

Verify
Two clients open the same Per Player chest: each sees a different inventory. Switch to Shared: both see the same entries update live.

See also

  • How-To: Route UI Requests
  • How-To: Save and Restore
  • Concept: Routing and Access
  • Concept: Multiplayer