Anti-Save-Scumming and Randomness Persistence
Probability Director can support both ordinary save/load behavior and games that want random decisions to remain stable across reloads.
The important distinction is that Probability Director owns random-state persistence, while your game or save framework owns save slots, files, autosave timing, backup policy, and the gameplay state being saved.
This document explains when to use PersistState, RotateOnSave, or Keyed decisions with a host save workflow. SaveGame Director integration is optional and uses the canonical DirectorBridge Probability.SaveGame adapter.
1. What “Anti-Save-Scumming” Means Here
Section titled “1. What “Anti-Save-Scumming” Means Here”A common save-scumming pattern is:
- Save immediately before a random event.
- Trigger the event.
- Receive an unwanted result.
- Reload the save.
- Trigger the event again.
- Repeat until a preferred result appears.
Probability Director can prevent the simple reload-to-reroll version of this problem by preserving the deterministic identity or progression of the random decision.
It is important to understand what Probability Director does not do:
- It does not control how many save slots the game keeps.
- It does not delete old saves.
- It does not prevent players from copying or restoring older save files.
- It does not encrypt save files.
- It does not provide anti-tamper or DRM.
- It does not automatically save the gameplay consequence of a random result.
If a game allows the player to restore arbitrary older save files, no RNG persistence policy by itself can make that historical rollback impossible. Slot retention, autosave policy, cloud backups, checkpoints, and similar rules belong to the host save system.
2. The Three Persistence Choices
Section titled “2. The Three Persistence Choices”| Persistence choice | Use it when | What happens across save/load |
|---|---|---|
| Ephemeral | The randomness does not need to survive save/load | Stream state is not part of Probability Director persistence |
| PersistState | Normal save/load should continue the same random sequence or deterministic campaign identity | Persistent random state is restored so future decisions continue from the saved state |
| RotateOnSave | A successful save should also create a new committed random-generation boundary | The save transaction prepares the next generation, and that generation becomes authoritative only after the save succeeds |
For most gameplay systems, PersistState is the normal choice.
Use RotateOnSave deliberately. It is a save-boundary policy, not a replacement for saving normal gameplay state.
3. Normal Saving with PersistState
Section titled “3. Normal Saving with PersistState”3.1 When to use PersistState
Section titled “3.1 When to use PersistState”Use PersistState when you want normal save/load behavior:
- combat rolls should continue from the saved point;
- loot generation should not silently restart its stream;
- procedural choices should remain part of the same campaign;
- a Fixed stream should resume its deterministic sequence;
- a Secure stream should retain the protected campaign identity required to continue correctly.
For a Sequential stream, Probability Director preserves the state needed to continue that stream from the saved point.
Conceptually:
Play -> RNG state advances -> Save -> gameplay state is saved -> Probability Director state is saved -> continue playing
Later:
Load -> gameplay state is restored -> Probability Director state is restored -> next random operation continues from the saved pointIf the player saved immediately before a roll and then reloads that same save, the stream returns to the same saved state. Repeating the same sequence of operations therefore produces the same deterministic continuation rather than receiving a fresh reroll merely because the game was reloaded.
That makes ordinary PersistState useful against the simplest form of save-scumming while still behaving exactly like a conventional saved RNG state.
3.2 Sequential streams and call order
Section titled “3.2 Sequential streams and call order”A Sequential stream means:
Give me the next result from this stream.
Its future results therefore depend on what has already consumed that stream.
For save-sensitive gameplay, keep unrelated systems on separate streams.
Good:
Combat.DamageCombat.CriticalLoot.DropTableWorld.EncountersCosmetics.VariationPoor:
Gameplay.Random -> combat -> loot -> audio -> particles -> encountersWith the second design, an unrelated cosmetic draw could change a later gameplay result.
PersistState restores the stream correctly, but it cannot protect a design in which unrelated gameplay systems intentionally share one Sequential sequence.
4. Keyed Decisions for Stable Gameplay Outcomes
Section titled “4. Keyed Decisions for Stable Gameplay Outcomes”For some anti-save-scumming problems, a Keyed decision is more appropriate than relying on Sequential position.
A Keyed operation means:
Give me the deterministic result for this exact semantic decision.
A Keyed decision uses stable identity such as:
Namespace: LootStable Subject ID: Chest.0042Decision: ContentsOccurrence: 0If the persistent campaign identity and the same Decision Key are restored, the same semantic decision resolves consistently. It does not depend on unrelated Sequential calls.
This is particularly useful for:
- the contents of a specific chest;
- a procedural property assigned to a specific NPC;
- a world-generation decision tied to a stable location;
- a one-time reward roll tied to a stable gameplay object;
- any decision that should remain stable even if unrelated random calls are added elsewhere.
Important rule
Section titled “Important rule”Do not use transient UObject names, pointer addresses, PIE suffixes, or unstable iteration order as a Stable Subject ID.
The key should describe the gameplay decision, not the temporary object instance that happened to request it.
5. RotateOnSave
Section titled “5. RotateOnSave”5.1 What RotateOnSave does
Section titled “5.1 What RotateOnSave does”RotateOnSave establishes a new random-generation boundary when a save is successfully committed.
Probability Director does this transactionally.
Conceptually:
Current committed generation | +--> Prepare Save | +--> build candidate persistence state +--> live committed RNG state is not changed yet | +--> Save succeeds | | | +--> Commit candidate | | | +--> candidate generation becomes authoritative | +--> Save fails/cancels | +--> Discard candidate +--> current committed RNG state remains authoritativeThe important property is that preparing a save is not enough to rotate live random state.
The new generation is committed only after successful storage. If the save fails, the candidate is discarded rather than leaving gameplay RNG in a state that was never successfully saved.
5.2 When to use RotateOnSave
Section titled “5.2 When to use RotateOnSave”Use RotateOnSave for systems where the save boundary itself should be meaningful to the random state.
Examples include:
- checkpoint-driven games where successful checkpoints define a new random-generation boundary;
- roguelike or ironman-style save policies;
- autosave-driven progression where future random state should be tied to the committed save generation;
- systems where you explicitly want successful save commits to separate one persisted random generation from the next.
Do not use it automatically for every stream just because it sounds more secure.
For ordinary gameplay continuation, PersistState is simpler and usually easier to reason about.
5.3 What RotateOnSave does not mean
Section titled “5.3 What RotateOnSave does not mean”RotateOnSave does not mean:
Reloading the same save gives a new random result every time.
That would make persistence nondeterministic and would itself invite rerolling.
A committed save contains the committed Probability Director state. Reloading that same save restores that state.
RotateOnSave also does not make an old copied save disappear. If your game lets the player restore a save from an earlier generation, that older save still represents its earlier committed state.
If preventing rollback to older saves is part of the game design, combine Probability Director with a host save policy such as:
- controlled checkpoint slots;
- limited save history;
- ironman/single-slot rules;
- authoritative server or cloud state;
- application-specific rollback detection.
Those policies belong outside Probability Director.
6. Why Normal Saving and Anti-Save-Scumming Can Coexist
Section titled “6. Why Normal Saving and Anti-Save-Scumming Can Coexist”Probability Director separates two responsibilities:
Probability Director
- knows the deterministic/random state;
- knows how that state must be captured and restored;
- knows how persistent stream policies behave;
- keeps Secure internals protected;
- validates persistence compatibility.
Your save system
- decides when saving happens;
- decides where the save is stored;
- decides how many saves exist;
- stores the rest of the game’s state;
- decides autosave/checkpoint/ironman policy.
Because those responsibilities are separate, one game can mix persistence policies.
Example:
Combat.Damage PersistStateCombat.Critical PersistStateLoot.UniqueChest Keyed + persistent campaign identityWorld.Encounters RotateOnSaveCosmetics.Variation EphemeralThere is no requirement for every stream in a campaign to use the same gameplay purpose.
7. Fixed and Secure Streams
Section titled “7. Fixed and Secure Streams”Both Fixed and Secure streams can participate in persistent gameplay, but they serve different purposes.
Use Fixed when you want a developer-visible or user-visible reproducible seed.
Typical uses:
- debugging;
- deterministic test scenarios;
- reproducible procedural generation;
- seeded challenge modes.
A Fixed Sequential stream is reproducible, but it is still call-order dependent.
Secure
Section titled “Secure”Use Secure when you want fresh runtime unpredictability and do not want gameplay code to handle secret seed material.
For persistent Secure streams, Probability Director preserves the protected campaign material required by its persistence contract. The secret state remains internal rather than becoming ordinary Blueprint or diagnostic data.
Important security boundary
Section titled “Important security boundary”A Secure stream does not encrypt the save file.
Plain Unreal SaveGame serialization is not confidentiality. If save-file encryption, signing, anti-tamper, or protected cloud storage is required, the host storage system must provide it.
8. Save the Gameplay Result Too
Section titled “8. Save the Gameplay Result Too”Probability Director persistence preserves the state used to make future random decisions.
It should not be used as a replacement for saving gameplay state that has already been decided.
Example: a chest has already generated these items:
Iron Sword32 GoldHealth PotionThe normal save should store that chest’s resolved contents or resolved gameplay state.
Do not load the save and regenerate the chest merely because Probability Director could produce a deterministic result again.
A useful rule is:
Once randomness has become gameplay state, save the gameplay state.
Probability Director then preserves the random state for decisions that have not happened yet.
This is especially important when integrating with SaveGame Director, World State, inventory systems, quest systems, or other persistent gameplay systems.
9. Native Unreal SaveGame Integration
Section titled “9. Native Unreal SaveGame Integration”Probability Director does not own slot I/O.
If you use your own USaveGame or custom save framework, the host workflow is responsible for coordinating Probability Director persistence with the rest of the save.
Normal PersistState save
Section titled “Normal PersistState save”Conceptually:
1. Capture normal gameplay state.2. Capture Probability Director persistence state.3. Write both as one coherent save.4. Report save success/failure normally.On load:
1. Recreate/register the persistent Probability Director streams expected by the save.2. Restore the Probability Director persistence state.3. Restore the associated gameplay state according to your game's load ordering.4. Continue gameplay only after the load is accepted.Probability Director validates persistent stream configuration and persistence compatibility during restore rather than silently accepting a different stream contract.
RotateOnSave save
Section titled “RotateOnSave save”RotateOnSave requires transaction semantics.
Conceptually:
1. Prepare the Probability Director save candidate.2. Put that candidate into the save being written.3. Write the save through the host storage system.4. If storage succeeds: Commit the prepared Probability Director candidate.5. If storage fails or is abandoned: Discard the candidate.Do not commit the rotated state before the save file has actually succeeded.
Otherwise the running game could move to random state that the failed save does not contain.
The Prepare/Commit/Discard persistence operations are intentionally native persistence operations rather than ordinary Blueprint random-state editing tools.
10. SaveGame Director Integration
Section titled “10. SaveGame Director Integration”Probability Director remains standalone. DirectorBridge is an optional adapter for projects that also use SaveGame Director. Its integration identity is Probability.SaveGame.
The owners retain these responsibilities:
- Probability Director owns RNG algorithms, streams, decision keys, stream state, probability mechanics, and persistence payload semantics for its state.
- SaveGame Director owns save/restore orchestration, contributor lifecycle, and required/optional contributor policy.
- DirectorBridge owns registration and adaptation between the owners.
The adapter uses Probability Director persistence APIs and does not duplicate Probability serialization or stream-state logic. The canonical contributor participates in SaveGame Director operations while the owner APIs continue to define capture and restore semantics.
For projects without SaveGame Director, use the native Probability Director persistence APIs with the host’s save system, or leave streams Ephemeral. Neither SaveGame Director nor DirectorBridge is required by the Probability core.
See SaveGame Director Integration and Migration.
11. Practical Patterns
Section titled “11. Practical Patterns”Pattern A — Ordinary RPG save/load
Section titled “Pattern A — Ordinary RPG save/load”Use:
Sequential gameplay streams -> PersistStateStable one-time decisions -> Keyed where appropriateCosmetic throwaway RNG -> EphemeralWhy:
The game resumes exactly from its saved random state without turning every save into a special random-generation boundary.
Pattern B — A chest must not reroll on reload
Section titled “Pattern B — A chest must not reroll on reload”Prefer:
Keyed streamNamespace: LootStable Subject ID: persistent chest IDDecision: ContentsOccurrence: 0Then save the resulting chest contents as normal gameplay state once they have been generated.
Why:
The chest’s identity determines the decision, so unrelated random calls do not change it.
Pattern C — Sequential combat that should resume normally
Section titled “Pattern C — Sequential combat that should resume normally”Use:
Combat.Damage -> Sequential + PersistStateCombat.Crit -> Sequential + PersistStateWhy:
The save restores each stream’s progression. Keeping damage and critical rolls separate prevents unrelated consumption from shifting one another.
Pattern D — Checkpoint or ironman-style save boundary
Section titled “Pattern D — Checkpoint or ironman-style save boundary”Use RotateOnSave for the streams whose random generation should move to a new committed generation when a checkpoint/save succeeds.
Combine it with the game’s checkpoint or slot-retention policy.
Why:
Probability Director supplies safe transactional rotation; the host save system supplies the actual anti-rollback rules.
Pattern E — Cosmetic randomness
Section titled “Pattern E — Cosmetic randomness”Use Ephemeral when the random state is irrelevant after loading.
Examples:
- temporary particle variation;
- nonpersistent menu flourish;
- throwaway preview randomness.
Do not spend persistence complexity on randomness whose continuity has no gameplay meaning.
12. Save As and New Campaign
Section titled “12. Save As and New Campaign”These operations have different meanings.
Save As
Section titled “Save As”A Save As operation should preserve the same campaign/random identity.
Changing the destination slot is not a request for a new random universe.
Conceptually:
Campaign A / Slot 1 -> Save AsCampaign A / Slot 2Both saves still represent Campaign A.
New Campaign
Section titled “New Campaign”A New Campaign is an explicit reset of campaign identity and random state.
Use it when starting a genuinely new playthrough rather than when copying or renaming a save.
13. Common Mistakes
Section titled “13. Common Mistakes”Using Ephemeral for save-sensitive gameplay
Section titled “Using Ephemeral for save-sensitive gameplay”The stream is not intended to preserve its state through save/load.
Use a persistent policy instead.
Regenerating already-resolved gameplay on load
Section titled “Regenerating already-resolved gameplay on load”Save the resolved gameplay state.
Do not treat the RNG as a substitute for your inventory, quest, world-state, or encounter save data.
Sharing one Sequential stream across unrelated systems
Section titled “Sharing one Sequential stream across unrelated systems”An extra call in one system changes the future sequence for every other system sharing that stream.
Split unrelated responsibilities into separate streams.
Assuming Fixed means call-order independent
Section titled “Assuming Fixed means call-order independent”Fixed makes a sequence reproducible. It does not make Sequential calls independent of order.
Use Keyed decisions where semantic call-order independence is required.
Assuming Secure prevents save-file editing
Section titled “Assuming Secure prevents save-file editing”Secure protects the RNG design and secret-state exposure boundary. It does not encrypt or sign your save file.
Assuming RotateOnSave prevents restoration of old files
Section titled “Assuming RotateOnSave prevents restoration of old files”It does not own save history. Your host save policy must decide whether older saves are retained or loadable.
Committing RotateOnSave before storage success
Section titled “Committing RotateOnSave before storage success”Do not do this.
A failed save must not leave the running game on a random generation that was never successfully written.
14. Recommended Decision Guide
Section titled “14. Recommended Decision Guide”| Gameplay requirement | Recommended approach |
|---|---|
| Resume ordinary random progression after load | PersistState |
| Same semantic decision should stay stable regardless of unrelated call order | Keyed decision + stable Decision Key |
| Successful save/checkpoint should create a new committed random-generation boundary | RotateOnSave |
| Randomness has no persistence value | Ephemeral |
| Human-visible reproducible seed is required | Fixed |
| Fresh unpredictable runtime randomness is required | Secure |
| Prevent arbitrary rollback to manually copied older saves | Host save/storage policy; Probability Director alone cannot enforce this |
| Preserve an already-generated loot/reward/quest outcome | Save that gameplay result through the normal save system |
15. Recommended Save-Sensitive Workflow
Section titled “15. Recommended Save-Sensitive Workflow”For a save-sensitive random event:
1. Decide whether the event is Sequential or Keyed.2. Give it its own stable Stream ID.3. Choose Fixed or Secure according to reproducibility requirements.4. Choose PersistState for normal continuation, or RotateOnSave when successful save boundaries must rotate the random generation.5. Perform the random decision.6. Once the result becomes gameplay state, store that gameplay state normally.7. Save Probability Director persistence with the same save transaction.8. On load, restore both the gameplay state and Probability Director state.9. If old-save rollback must be restricted, enforce that in the host SaveGame/SaveGame Director storage policy.This keeps randomness, gameplay persistence, and save-file policy separate while allowing them to work together predictably.