Items and fragments
An item is not a class you subclass. It is a definition asset plus a list of fragments, and the fragments are the only thing that decides what it can do.
The two halves of an item
UEDItemDefinition is the immutable half — a data asset, loaded once and shared by every instance
made from it. It never holds runtime state.
FEDItemEntry is the mutable half — a plain struct living in a container. It holds a definition
id, a handle, a stack count, per-stack state, dynamic tags and a slot address. No UObject is
allocated for it.
DA_Sword (UEDItemDefinition) ← one asset, shared
▲ referenced by id
│
FEDItemEntry { Handle 41, Count 1 } ← one struct per stack, in a container
FEDItemEntry { Handle 42, Count 1 } ← a second sword: same asset, different handle
This is why an inventory of a thousand arrows costs a struct and not a thousand objects.
When an item needs to be an object
Some items genuinely need per-instance behaviour: a weapon with a tick, a container item with its own
logic. Set Instance Class on the definition and entries made from it are promoted to a
UEDItemInstance of that class, replicated as a subobject. Everything else stays a struct.
Promotion is opt-in and per definition. Nothing else in the framework changes; a promoted entry is still an entry.
Identity
An item is identified by its handle (FEDItemHandle) — an opaque 64-bit value allocated by the
server. Not by index, not by name, not by position.
That matters because everything else moves. An item transferred between containers keeps its handle, so a UI holding one, a quest watching one, or an ability granted by one all survive the move.
GetEntryAtIndex exists for iteration. The index of an entry changes whenever the array behind it
changes, and on a client the order is not even guaranteed to match the server's. Store the handle.
Fragments
A fragment is a plain USTRUCT deriving from FEDItemFragment, stored in the definition's
Fragments array as an FInstancedStruct. Each answers one orthogonal question.
if (const FEDStackFragment* Stack = Definition->FindFragment<FEDStackFragment>())
{
const int32 Max = Stack->GetMaxStackSize();
}
Get Fragment (Definition, wildcard out) ← Blueprint; pick the struct type on the output pin
Has Fragment Of Type (Definition, Struct)
An item with no Stack fragment does not stack. An item with no Size fragment is one cell. Absence is the default, so a rock is one fragment and a sword is eight without either paying for the other.
The rule that makes them work
Fragments have no virtual functions. A vtable inside an FInstancedStruct corrupts
serialization, so this is enforced by convention and by review, not by the compiler. Behaviour lives
in the systems that read fragments — layouts, rules, transaction executors, observer components.
If you find yourself wanting a virtual on a fragment, what you want is a registry: see Extending.
What ships
The full table is in the fragment reference. The ones you will use first:
| Fragment | Answers |
|---|---|
| Display | Name, description, icon, category label |
| Stack | Does it stack, and how high |
| Size | How many cells it covers, and in what shape |
| Rarity | Tier tag and accent colour |
| Container Placement | Where it prefers to go |
| Equipment | Which slot it can be worn in |
| Equipment | Which slot it is worn in, and how it looks and behaves per state |
| Nested Container | The container it brings with it |
| Weight, Value, Usable, Durability, Grants (GAS), Prerequisites, Stat Rolls, World Model | one each |
Per-stack state
Two swords from one asset can differ. Two mechanisms, and they are not interchangeable:
FEDItemStateContainer — tag-keyed numbers on the entry. Durability, ammo, charges. Replicated
with the entry.
DynamicTags — tags describing this stack: stolen, soulbound, quest-reserved. Also replicated.
Both gate stacking: two stacks with different state or different dynamic tags never merge. That is deliberate. Merging a stolen stack into a clean one would launder the mark, and merging a half-worn sword into a fresh one would invent durability.
Rolled stats
FEDRollFragment gives an item stats decided when it is created rather than when it is authored — a
sword rolling 12–18 damage. The roll happens once, on the server, and is stored in the entry's state,
so it replicates and saves like anything else.
Prerequisites
FEDPrerequisiteFragment holds conditions an item checks before a container will accept it: another
item required or forbidden, a GAS attribute above a threshold. They are evaluated at the same single
acceptance gate as container rules, so a dry run and the real thing always agree, and the failure
comes back as player-readable text rather than a boolean.
Where next
- Containers and layouts — where entries actually live.
- Fragment reference — every fragment and every field.
- Extending — writing a fragment of your own.