Saving
NihiloCore provides a debounced saving base built on top of the engine's USaveGame
system. Rather than writing to disk on every change, saves are deferred and coalesced —
only committing once activity has settled, with a hard deadline to guarantee the write eventually happens
regardless of how frequently data changes.
How It Works
UNihiloDebouncedSavingTask wraps a
Debounce Task internally. When
Save is called, the provided USaveGame
object is stored as pending and the debounce is triggered. If Save
is called again before the delay elapses, the pending data is replaced and the delay resets. The
actual disk write only happens once the delay completes uninterrupted.
The timeout acts as a guarantee. If saves keep arriving faster than the delay window —
for example, during a long sequence of rapid stat updates — the timeout ensures the write
happens anyway after a maximum wait, using whatever the most recent pending data was at that moment.
Both the delay and the timeout call the same internal ExecuteSave
path, so the behavior is identical either way.
On completion, OnSaveCompleted is broadcast with a success flag
and an optional error message string. Bind to this to update UI, retry logic, or telemetry.
Setup
The task is configured via FNihiloScheduledSavingProviderSettings,
which carries the slot name, delay time, timeout time, user index, and a debug logging flag.
Use the static CreateDefault factory for quick construction with
sensible defaults, or build the struct manually for full control.
Pass the settings to Initialize before calling anything else.
The debounce task is created internally at this point — calls to Save
before initialization are rejected with an error broadcast on OnSaveCompleted.
Call Deinitialize when the task is no longer needed to
cleanly tear down the internal debounce and clear pending data.
| Setting | Default | Description |
|---|---|---|
| Slot Name | SlotName | The save slot identifier passed to SaveGameToSlot and LoadGameFromSlot. |
| Delay Time | 0.5s | How long to wait after the last Save call before writing to disk. |
| Max Save Delay Time | 5.0s | Hard deadline from the first Save call in a burst. The write is forced after this regardless of ongoing activity. |
| User Index | 0 | Platform user index forwarded to the engine save system. Relevant for split-screen or multi-user platforms. |
| Enable Debug Logging | false | Emits log entries for save and load operations, including slot name and success state. |
Extending the Task
UNihiloDebouncedSavingTask is designed to be subclassed.
Three virtual hooks let derived classes participate in the save lifecycle without overriding
the core saving logic:
- GetSaveGameClass — return the USaveGame subclass your project uses. The base returns the plain USaveGame class, which is only useful as a placeholder.
- OnSaveDataLoaded — called after a successful load, with the deserialized save object. Use this to distribute loaded data to the rest of your systems.
- OnSaveDataCreated — called when no save file exists and a fresh one is created by Load. Use this to populate default values before the new file is written to disk.
A typical subclass overrides GetSaveGameClass to return its own
save type, and implements OnSaveDataLoaded to push the loaded
data into whatever provider or component owns it. Gateway's saving provider follows exactly this pattern.
Loading
Load is synchronous and returns the USaveGame
object directly. If a save file exists in the slot it is loaded and returned after calling
OnSaveDataLoaded. If no file exists yet,
CreateSaveFile is called — which creates a default save object
via CreateDefaultSaveData, calls OnSaveDataCreated,
writes it to disk immediately, and returns it. This means a first-time load always results in a valid
object with no extra handling needed at the call site.
SaveImmediate bypasses the debounce entirely by calling
Force on the internal debounce task, flushing any pending write
to disk right away. Use this for cases where a guaranteed synchronous commit is required —
application shutdown, returning to the main menu, or explicit user-triggered saves.