Concept: Multiplayer
Every part of the plugin assumes a dedicated server with several clients. This page states the mechanisms so you can reason about what a client sees and when.
Ownership
An item instance has exactly one owner at a time: the manager that holds its entry, or the pickup actor that holds its FCrimsonPickupInstance. Moving an item between inventories (TransferEntryTo) re-outers the instance; it is never duplicated. The manager's bReplicateUsingRegisteredSubObjectList is on and every instance, state fragment, constraint, and currency handler is registered with AddReplicatedSubObject - there is no legacy ReplicateSubobjects path.
What replicates
| Data | Mechanism | Condition |
|---|---|---|
| Entries | FCrimsonInventoryList (FFastArraySerializer) | Everyone who sees the owner. |
| Item instances, state fragments | Registered subobjects | With the entry. |
| Capacity tags (current and max) | FCrimsonInventoryTagStackContainer / FCrimsonInventoryFloatTagStackContainer FastArrays with OnRep_ConstraintValues, which sweeps and announces OnConstraintValueChanged | COND_Dynamic, resolved per send in PreReplication: owner-only when the owning actor has a net owning player (a player's own inventory), everyone who sees the owner when it does not (a world container). |
LayoutAsset, ActiveConstraints (and each constraint's rule properties), RegisteredCurrencyDataTables, Currencies | Standard replicated properties and subobjects | Everyone who sees the owner. |
| Layout runtime state, groups, occupancy | Not replicated - rebuilt locally from entries | - |
| Pickup contents | FCrimsonInventoryPickup with OnRep_PickupInventory; instances as registered subobjects | Everyone who sees the pickup. |
| Scoped container instances | ACrimsonInstancedInventoryActor::IsNetRelevantFor limits to viewers | Viewers only. |
One broadcast per mutation
Every server mutator ends in FinalizeServerMutation: it marks the FastArray dirty, rebuilds the layout runtime, broadcasts InventoryUpdated once, and calls FlushNetDormancy + ForceNetUpdate on the owner. Containers stay DORM_Awake (a dormant-all chest dropped changes for remote clients); the flush matters for projects that make their own inventory owners dormant. Clients rebuild the layout runtime in the FastArray callbacks and OnRep_InventoryList (always before the message broadcast), so a batch that adds three entries produces three StackChanged messages and one InventoryUpdated.
Where things run
| Action | Server | Owning client | Other clients |
|---|---|---|---|
| Add / remove / move / drop | Mutates | Sees it by replication; gets OperationResult if it asked | Sees it by replication if relevant |
PreviewPlacement | Authoritative check | Same code on replicated data - identical answer | - |
| Constraint evaluation | Authoritative | Preview only (rule properties replicate for this) | - |
| Equip | Host mutates; manager sets the Equipped flag from the host's message | Sees the flag | Sees the flag |
| Save / load | GetSaveData / ApplySaveData | - | - |
Routed request lifecycle
- Client calls a routing function (
MoveItem). Server_MoveItem_Validaterejects null targets and nonsensical counts before the body runs.Server_MoveItemchecksCanAccessInventory, then callsMoveItemByEntry.FinalizeServerMutationreplicates the change;Client_NotifyOperationResultreports the outcome to the caller.
BlueprintAuthorityOnly and log a warning. A client that appears to 'work' in a listen-server PIE window is the server. Always test with Play As Client.