Skip to main content

Saving and loading

EDInventorySave serialises a container to bytes and reads it back, with versioning and migration so a save written by an older build still loads.

Saving

TArray<uint8> Blob;
EDInventorySave::SaveContainer(*Container, Blob);

That captures the entries, their stack counts, per-stack state, dynamic tags and slot addresses. Promoted item instances are saved too.

Loading

FEDDemoSubsystemAllocator Allocator(UEDInventorySubsystem::Get(this));
EDInventorySave::LoadContainer(*Container, Blob, Allocator);

Loading needs a handle allocator because handles are server-allocated identity. Passing the world subsystem's allocator means loaded items get handles that cannot collide with anything allocated since. Restoring raw handles instead is how you end up with two items that are the same item.

Loading replaces the container's contents and broadcasts a Reset change, so observers re-read rather than trying to diff.

Key blobs by container tag

Save one blob per container, keyed by the container's type tag:

for (const UEDContainer* Container : Containers)
{
if (Container->GetContainerType().IsValid())
{
FEDContainerBlob Blob;
EDInventorySave::SaveContainer(*Container, Blob.Bytes);
Save->ContainerBlobs.Add(Container->GetContainerType().GetTagName(), MoveTemp(Blob));
}
}

The tag is the container's identity. It survives renaming a component, re-ordering a list, and re-authoring an asset — none of which is true of a component name or an array index.

Walk the inventory, not the components

Iterating container components misses every container gained at runtime — including the grid inside a worn backpack. Ask the inventory for its containers instead, or a player's bag quietly fails to save and they lose whatever was in it.

Loading into a container that already has a type

LoadContainer restores the type stored in the blob. An older save may have none, and blanking a container's type would silently detach every filter and lookup keyed on it — so an empty saved tag leaves the configured one alone.

If you see containers losing their identity after a load, that is the case to check first.

Several containers of a kind

Once an actor can have two containers with the same tag — two quivers — a tag is no longer a unique key. Save the container id alongside the tag and restore by id, falling back to the tag for older saves.

Versioning

Blobs carry a version, and migration runs on load. Adding a field to an entry means bumping the version and handling the old shape; the header for EDInventorySave documents the current version and what changed.

Replicated formats — the transaction error enum, NetSerialize layouts, the FastArray entry shape — get the same scrutiny for the same reason: changing one breaks saves and network compatibility with running clients.

JSON export

There is a debug JSON exporter as well. It is for reading a container in a bug report, not for persistence — it is not versioned and not intended to be loaded back.

Where next