Replication
The framework is multiplayer-first, which means the interesting rules are about what a client is allowed to believe. This page is worth reading before you ship, because both mistakes below look like UI bugs and neither reproduces in standalone.
What replicates
A container replicates as a subobject of the component that owns it. Its whole state travels:
| Property | Why it must |
|---|---|
EntryArray | The contents — a FastArray, so changes are deltas rather than the whole list. |
ContainerType | Its identity. Every lookup in the framework goes through this tag. |
ContainerId | Which instance, when several share a type. |
MaxSlots | A client guessing capacity draws the wrong thing. |
Rules | So a client can grey out what will be refused, instead of finding out by trying. |
LayoutConfig | Its shape. Without it a grid cannot be drawn at all. |
Promoted UEDItemInstances replicate as subobjects too, registered and unregistered as entries come
and go.
All FastArray specifics are confined to FEDContainerEntryArray. Gameplay uses the container's entry
API and never the array, which is what lets the replication backend change — to Iris, for instance —
without touching gameplay code.
Authority
Containers are created on the authority. A client never builds one; it receives them.
Every mutation is server-side. The Try* calls return false off the authority and do nothing.
The Request* calls are the client's route in.
Events fire on every machine. The server broadcasts when it changes something; a client broadcasts when the change arrives. UI binds once and works on both.
The route
A Server RPC only travels an actor chain the calling client owns. Half the containers in a game are not on such an actor — a chest, a corpse, a vendor's stock all belong to the server — so a request made "on the chest" is silently dropped by the engine with nothing but a log line.
UEDContainer::RequestTransaction handles this: it finds a UEDInventoryRouteComponent on something
the client does own, usually its own pawn, and sends the request down that pipe instead. The
transaction payload names the real containers, so the pipe it travelled through does not matter.
You get this for free. It is worth knowing it exists, because "my chest works on the listen server and not on the client" is otherwise a long afternoon.
Trap 1 — a view bound before its container arrives
On a client the actor's containers arrive by replication a few frames after the HUD is built. A
view bound to the container pointer that exists at that moment is bound to null, and unless
something re-resolves it, that panel stays empty for the entire session while the inventory behind it
works perfectly.
Bind by role, not by pointer:
// Right: "this actor's hotbar" is true before the container exists.
View->SetSourceInventory(Inventory, HotbarTag);
// Only for a container you already hold, such as a chest being looted.
View->SetSource(ChestContainer);
SetSourceInventory follows the inventory: it attaches the moment the container arrives, and detaches
if it goes away.
The debugger collects on the server and sends the result to the client. It showing the containers correctly does not prove the client has them. Check the client's own view instead.
Trap 2 — the list arrives before the containers are recognisable
A container and the list holding it replicate as separate things. The list can arrive first and be searched while every container in it is still nameless, so a lookup by tag at that instant finds nothing.
Handled: the container's type announces itself on arrival and the inventory re-broadcasts
OnContainersChanged, so anything waiting for a container by tag gets another look. If you write
something that resolves containers by tag on a client, subscribe to that event rather than resolving
once.
Relevancy and bandwidth
The Networking module adds UEDInventoryRelevancyComponent and project settings for the policy a
container actor should use:
- owner-only relevancy — nobody needs to receive the contents of a chest across the map;
- cull distance;
- net update frequency;
- initial dormancy — a chest nobody has opened costs nothing.
Defaults are conservative. A hundred loot chests in a level is the case worth tuning for.
Testing it
Automation tests run in a single process, so replication cannot be exercised by them. That is not a gap you can close with more tests — it is a property of the harness. What the suite does assert is the thing that makes replication possible: that a container's identity, capacity, contents, rules and shape are all marked to replicate.
The rest wants a real session. Play in editor with 2 or 3 players and check the client window, not just the server's. When something misbehaves, the first question worth answering is "which window?" — client-only is replication, both is logic, and that one word halves the search.
Where next
- User interface — binding views correctly
- Troubleshooting — symptoms and their usual causes