Appearance
Durable entity-state persistence
Mass Forge provides a versioned, SaveGame-safe Mass Forge Entity State Snapshot for durable gameplay state that belongs to one entity. Projects still own the surrounding USaveGame, slot naming, player/profile identity, world population records, and entity recreation policy.
Format 4 scope
Format version 4 captures:
- every configured Vital, Combat, and Support attribute by stable domain/name identity;
- exact counted Gameplay Tags when the source archetype has the Gameplay Tag fragment;
- ability grant IDs when the source archetype has the Ability fragment;
- remaining individual cooldown seconds for each grant;
- raw stored charges and remaining time until the next charge recovery; and
- active shared/global cooldown group IDs and their remaining seconds;
- the unconsumed partial interval for every compiled regeneration/degeneration rule, identified by its stable target attribute and guarded by its authored tick interval; and
- active Persistent Effects, including remaining duration, periodic phase and earned backlog, stack identity/strength, completed-period count, snapshotted tags and resolved attribute contributions, reversible attribute bases, and portable application context; and
- one active cast/channel phase, including guarded ability identity/version, execution type, remaining phase time, earned channel backlog, completed pulse count, committed state, and portable target relationship.
The format never stores a destination Mass entity handle, Actor pointer, request ID, lifecycle ID, persistent-effect handle, hit result, or absolute UWorld timestamp. Restoring in another world rebuilds deadlines from saved remaining gameplay seconds and allocates fresh world-local Persistent Effect and Ability Lifecycle handles.
Format 4 does not serialize queued work, AI tickets, event history, representation, navigation, or project-owned state. AI tickets and async lifecycle observers are world-local orchestration; reacquire or rebind them after restoration.
Formats 1, 2, and 3 remain readable. Because format 1 predates regeneration timing, restoring it resets every destination regeneration remainder to zero rather than retaining unrelated live timing. Format 2 retains regeneration timing but has no Persistent Effect section. Formats 1 and 2 reject a destination with active Persistent Effects, and formats 1–3 reject a destination with an active cast/channel; a legacy payload never erases temporal state it cannot describe. A format-2-or-newer regeneration restore requires the same unique target set and tick intervals as the captured profile; a changed profile returns Regeneration Profile Mismatch before any state changes.
Released format matrix
| Entity-state format | Durable sections | Compatibility rule on load |
|---|---|---|
| 1 | Attributes, counted tags, grants, individual/shared cooldowns, charges and charge recovery | Migrate the nested attribute payload when its schema is old. Regeneration remainder becomes zero. Active destination Persistent Effects or an Ability Lifecycle reject the restore. |
| 2 | Format 1 plus regeneration remainder | The current regeneration target/interval profile must match. Active destination Persistent Effects or an Ability Lifecycle reject the restore. |
| 3 | Format 2 plus exact active Persistent Effects | Soft definitions and stable Effect IDs must still resolve and agree. An active destination Ability Lifecycle rejects the restore. |
| 4 | Format 3 plus one exact active cast/channel | Soft Ability identity, Save Compatibility Version, execution/timing guards, and any external target binding must agree. Existing supported destination state is replaced atomically. |
Do not edit or blindly replace FormatVersion after deserialization. It describes which fields the saved record was capable of representing and selects the compatibility rules above. New saves use the current format automatically. Attribute schema evolution is separate: migrate the nested Attributes member through project-authored migration assets, without changing the outer entity-state format.
Portable Persistent Effect context
Each active effect stores its persistent definition and periodic definition as soft asset references guarded by their stable Effect IDs. It also stores logical Source ID, Ability ID, optional origin, up to 64 finite named scalar parameters, and up to 32 application-time attribute captures. Actor pointers and hit results are deliberately excluded.
SourceEntity is rebound to the newly restored destination only when the captured effect was self-sourced. An external source entity belongs to the old world and is therefore left unset; its logical Source ID and captured numeric inputs remain available. TargetEntity always becomes the destination. Save files must never assume that an old Persistent Effect handle will remain valid after restore—enumerate the restored entity's active effects when a new handle is needed.
Portable Ability Lifecycle targets
A targetless cast/channel uses the ordinary capture and restore nodes. A lifecycle that targeted the saved entity itself records Saved Entity and rebinds automatically. A lifecycle targeting another entity records External Entity and requires an explicit project-owned relationship key:
- Call Capture Mass Forge Entity State with Lifecycle Target Reference and supply a stable key meaningful to the outer SaveGame, such as a party-member record ID—not a Mass index/serial.
- Save the returned snapshot and the project's relationship records.
- Recreate both entities in the destination world and resolve their new full-generation handles.
- Build Mass Forge Entity State Lifecycle Target Binding with the identical key and the recreated target handle.
- Call Restore Mass Forge Entity State with Lifecycle Target or its queued variant.
Missing keys, mismatched keys, unset targets, stale targets, and a binding that collapses an external relationship onto the restored source all fail before mutation. Mass Forge does not guess a target from spatial proximity, Actor representation, or a recycled entity index.
Every Ability asset exposes Save Compatibility Version, default 1. Increment it when a behavior change must invalidate previously saved active phases. Restore also guards the stable Ability ID, execution type, cast time, channel duration, and channel period. A mismatch fails closed rather than continuing old timing under changed authored behavior.
Project-owned SaveGame envelope
Mass Forge deliberately provides the durable value type, not a universal USaveGame class. The owning game must retain the information required to recreate its population and relationships. A minimal C++ record can follow this shape:
cpp
USTRUCT(BlueprintType)
struct FProjectMassForgeEntityRecord
{
GENERATED_BODY()
UPROPERTY(SaveGame)
FName EntityRecordId;
UPROPERTY(SaveGame)
FPrimaryAssetId EntityConfigId;
UPROPERTY(SaveGame)
FMFEntityStateSnapshot State;
};
UCLASS()
class UProjectWorldSaveGame : public USaveGame
{
GENERATED_BODY()
public:
UPROPERTY(SaveGame)
int32 ProjectSaveVersion = 1;
UPROPERTY(SaveGame)
TArray<FProjectMassForgeEntityRecord> Entities;
};EntityRecordId is a project identity, not an FMFEntityHandle. If an active lifecycle targets another saved entity, pass that target's record ID to Capture Mass Forge Entity State with Lifecycle Target Reference. Store whatever additional transform, spawning, ownership, faction, inventory, quest, or world-partition information is needed beside the Mass Forge state. Keep referenced Ability, Instant Effect, and Persistent Effect assets cooked and preserve their paths through Unreal redirectors when moving them.
Blueprint projects use the same envelope: create a project SaveGame Blueprint with a project version and an array of project entity records containing a stable record name, an Entity Config reference/ID, and Mass Forge Entity State Snapshot. The Mass Forge snapshot remains a normal Blueprint struct.
End-to-end load and migration sequence
Use this ordering for travel, slot loads, and released-save upgrades:
- Deserialize the project
USaveGamerecord. Treat a missing/corrupt project record as a project load failure; do not construct a default Mass Forge snapshot and call it restored state. - Upgrade the outer
ProjectSaveVersionfirst. Perform project-specific unit conversions, archetype substitutions, or record-shape changes here. - Leave every Mass Forge
State.FormatVersionunchanged. Reject formats outsideOldestSupportedFormatVersionthroughCurrentFormatVersionrather than guessing. - Compare
State.Attributes.SchemaVersionwith the current Mass Forge project setting. Apply the complete ordered chain of Mass Forge Attribute Snapshot Migration assets toState.Attributes; replace only the nested member after each successful step. Stop the load if any step fails. - Recreate every destination entity from the project record's Entity Config/archetype information before restoring cross-entity relationships. Build a map from each
EntityRecordIdto its new full-generationFMFEntityHandle. - For a targetless or self-targeted lifecycle, use the ordinary restore path. For External Entity, resolve
State.AbilityLifecycle.TargetReferencethrough that map and build Mass Forge Entity State Lifecycle Target Binding with the identical reference plus the recreated target handle. Never substitute an index match or nearest entity. - At a guaranteed Mass-idle point, restore directly. If loading can overlap Mass processing, submit the queued restore, retain the positive request ID, and wait for its one terminal completion. Any rejected zero-ID receipt is a load failure that will not later complete.
- Only after successful restoration, rebuild project-owned Actor representation, AI/state-machine bindings, targeting caches, UI observers, and persistence bookkeeping. Re-query active Persistent Effects and the active Ability Lifecycle because restored world-local handles are new.
- On the next successful save, capture the current format and current attribute schema. Do not rewrite the user's old slot until the upgraded world has restored and passed project validation.
For large populations, recreate all entities and resolve the relationship map in bounded batches, then use the bounded queued restore API. A single entity-state transaction is atomic for one source plus its supported temporal state; a project save containing many entities is not one global Mass Forge transaction. If record 37 fails after records 1–36 restore, the project decides whether to discard the new world, retry, or report a partial project-load failure.
Failure recovery
- Unsupported Snapshot Version: keep the slot unchanged and route it through an older supported build or an explicit project converter. Never relabel the format.
- Schema Version Mismatch: run the missing attribute migration chain and retry with its output; do not restore the unmigrated values by slot.
- Missing/Changed Persistent Effect or Ability definition: restore the referenced asset path/redirector or perform an explicit project upgrade. Do not silently drop the active state.
- Regeneration Profile Mismatch: recreate the entity with the matching released profile or intentionally reset/rebuild the record in a documented project migration.
- Missing Lifecycle Target Binding: finish population recreation and relationship resolution before retrying.
- Queue Full: retain the project record, wait for capacity, and resubmit. A zero request ID promises no completion.
- Invalid Entity: discard the stale handle, resolve the intended record to its current generation, and retry only if the project record still identifies the same logical entity.
Blueprint workflow
- At a Mass-idle checkpoint, call Capture Mass Forge Entity State with the source entity. Actor-owned graphs may use Capture Mass Forge Entity State from Actor. Use the target-reference variant when the active lifecycle points to another entity.
- Require
Result == Success, then place the returned struct in the project's SaveGame record beside its own stable entity/archetype identity. - On load, recreate or resolve the correct destination entity. Never save and replay the former index/serial handle.
- Upgrade
Snapshot.Attributesthrough the required Mass Forge Attribute Snapshot Migration assets when its schema version is older than the current project schema. - Call Restore Mass Forge Entity State only from a guaranteed Mass-idle point. Use Queue Mass Forge Entity State Restore when load orchestration can overlap Mass processing. Supply the explicit target-binding variant for an external active-lifecycle relationship.
- For a queued restore, retain the positive request ID and match it against On Entity State Restore Completed. A rejected receipt has request ID
0and produces no completion.
The represented-Actor restore nodes resolve the current entity generation at submission. A representation swap cannot redirect accepted queued work to another entity.
Exact restore semantics
Restore validates the complete payload before changing state. It then commits attributes, tags, grants, cooldowns, charges, regeneration timing, Persistent Effect ownership/scheduling, and the active Ability Lifecycle before publishing any change event. An attribute-change callback therefore observes complete restored tag, ability, regeneration, effect, and lifecycle state rather than a partial intermediate state. Restore itself does not emit synthetic effect-applied/removed or ability-lifecycle started/ended events; the whole-entity restore completion is the authoritative boundary. Re-query restored effect/lifecycle handles and rebind world-local observers after that boundary.
The Includes Gameplay Tags and Includes Abilities flags describe whether the captured archetype owned those fragments:
- when a section is included, the destination must have its matching fragment and the saved list replaces that section exactly;
- entries absent from an included list are removed;
- when a section was not present on the source, an optional matching fragment on the destination is cleared; and
- non-empty section data with its include flag disabled is rejected instead of being silently ignored.
Successful tag count changes publish the normal tag-change events in lexical tag order. Removed ability grants publish before newly added grants, each in lexical ID order. Cooldown/charge-only changes do not masquerade as grant changes.
Current attribute clamps remain authoritative. Ability definitions remain authoritative when a restored grant is later queried or activated: for example, charge queries clamp raw stored charges to the current definition's maximum. Stable Ability IDs must retain compatible meaning across releases or be upgraded by the project surrounding the Mass Forge snapshot.
Validation and bounded data
Restore rejects the complete transaction for:
- an unsupported entity-state or nested attribute format;
- an unmigrated attribute schema version;
- invalid, duplicate, retired, missing, or non-finite attribute values;
- invalid, duplicate, zero/overflowing, or over-capacity counted tags;
- empty or duplicate Ability IDs, over-capacity grants, or invalid cooldown/recovery times;
- invalid, duplicate, or over-capacity cooldown groups;
- malformed, duplicate, non-finite, over-interval, or over-capacity regeneration timing entries;
- a missing regeneration fragment or changed target/interval profile for a format-2-or-newer timing section;
- malformed, over-capacity, missing, or changed Persistent Effect definitions; invalid duration/period/backlog state; invalid portable context; missing resolved-modifier bases; or a saved effective value inconsistent with its reversible base and contributions;
- malformed lifecycle phase/timing/backlog/commit state, an absent or changed Ability definition/version, or an active lifecycle whose Ability is not in the saved grants;
- missing, mismatched, unset, stale, self-collapsed, or otherwise invalid external lifecycle-target bindings;
- missing destination fragments, a stale entity generation, Mass processing, lifecycle processing, or a nested transaction; and
- active casts/channels for formats 1–3. Formats 1 and 2 additionally reject active Persistent Effects because those formats cannot replace them exactly.
Capacities match the runtime fragments: 32 exact counted tags, 16 ability grants, 8 active cooldown groups, 8 regeneration rules, 16 active Persistent Effects, and 24 attributes with reversible Persistent Effect contributions. The attribute section remains bounded by the configured schema capacities.
Queued restore contract
The queue owns a deep copy of the snapshot and any lifecycle-target binding. Max Queued Entity State Restores Per Frame limits work per safe tick and Max Pending Queued Entity State Restores provides back-pressure. Entity-state restores run before narrower Attribute Snapshot Restores and before all ordinary gameplay mutation queues.
Submission validates format, schema, IDs, finite values, section consistency, capacity, and the supplied entity identity when safe access is available. Execution revalidates the complete generation, fragment compatibility, schema, and temporal-state preconditions. An accepted request receives exactly one terminal completion unless its world tears down first. Work enqueued from a completion callback waits for the next eligible phase.
Active Ability Lifecycle is expected only when a format-1/2/3 payload targets an active destination lifecycle. Active Persistent Effects is expected only for a format-1/2 payload targeting active effect state. A valid format-4 restore replaces destination effects and lifecycle state atomically with the saved sections.
Attribute-only alternative
Use the older Capture/Restore Mass Forge Attribute Snapshot nodes when the project intentionally wants to restore only attributes while leaving tags, grants, cooldowns, charges, regeneration, Persistent Effects, and active Ability Lifecycles unchanged. The detailed schema migration and partial-attribute rules remain in the attribute persistence guide.
Verified released-format scenarios
The packaged world regression serializes and deserializes the public struct through Unreal's tagged SaveGame archive before restoration. It covers format 1 plus a nested schema-2-to-3 Health rename, format 2 regeneration remainder, format 3 soft-referenced Persistent Effect state and exact timing, and format 4 soft-referenced externally targeted channel state with a project relationship remap. The strict release verifier executes those scenarios twice on UE 5.6.1, 5.7.1, and 5.8.2 together with Editor, Development Game, Shipping Game, package-integrity, and zero-warning gates.
This proves the Mass Forge payload and restore contract. Each customer project must still test its own USaveGame envelope, population recreation, migrations, relationship resolver, cooked assets, platform storage, and failure UI before release. The time, travel, and save contract defines the supported runtime boundaries. Multiplayer persistence remains outside the initial supported release.