SaveLoad using managed lifecycle
This commit is contained in:
committed by
Michal Pikulski
parent
3e835ed3b8
commit
b932be2232
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
@@ -62,10 +62,6 @@ namespace Core.SaveLoad
|
||||
{
|
||||
Logging.Debug("[SaveLoadManager] Initialized");
|
||||
|
||||
#if UNITY_EDITOR
|
||||
DiscoverInactiveSaveables("RestoreInEditor");
|
||||
#endif
|
||||
|
||||
// Load save data if save system is enabled (depends on settings from GameManager)
|
||||
if (DeveloperSettingsProvider.Instance.GetSettings<DebugSettings>().useSaveLoadSystem)
|
||||
{
|
||||
@@ -75,16 +71,20 @@ namespace Core.SaveLoad
|
||||
|
||||
protected override void OnSceneReady()
|
||||
{
|
||||
// Discover and register inactive SaveableInteractables in the newly loaded scene
|
||||
string sceneName = UnityEngine.SceneManagement.SceneManager.GetActiveScene().name;
|
||||
DiscoverInactiveSaveables(sceneName);
|
||||
// SaveableInteractables now auto-register via ManagedBehaviour lifecycle
|
||||
// No need to discover and register them manually
|
||||
}
|
||||
|
||||
protected override void OnSaveRequested()
|
||||
protected override string OnSceneSaveRequested()
|
||||
{
|
||||
// Scene is about to unload - this is now handled by SceneManagerService
|
||||
// which calls Save() globally before scene transitions
|
||||
Logging.Debug($"[SaveLoadManager] OnSaveRequested called");
|
||||
// SaveLoadManager orchestrates saves, doesn't participate in them
|
||||
return null;
|
||||
}
|
||||
|
||||
protected override string OnGlobalSaveRequested()
|
||||
{
|
||||
// SaveLoadManager orchestrates saves, doesn't participate in them
|
||||
return null;
|
||||
}
|
||||
|
||||
private void OnApplicationQuit()
|
||||
@@ -180,40 +180,6 @@ namespace Core.SaveLoad
|
||||
return participant;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Scene Lifecycle
|
||||
|
||||
/// <summary>
|
||||
/// Discovers and registers inactive SaveableInteractables in the scene.
|
||||
/// Active SaveableInteractables register themselves via their Start() method.
|
||||
/// </summary>
|
||||
private void DiscoverInactiveSaveables(string sceneName)
|
||||
{
|
||||
Logging.Debug($"[SaveLoadManager] Scene '{sceneName}' loaded. Discovering inactive SaveableInteractables...");
|
||||
|
||||
// Find ONLY INACTIVE SaveableInteractables (active ones will register themselves via Start())
|
||||
var inactiveSaveables = FindObjectsByType(
|
||||
typeof(Interactions.SaveableInteractable),
|
||||
FindObjectsInactive.Include,
|
||||
FindObjectsSortMode.None
|
||||
);
|
||||
|
||||
int registeredCount = 0;
|
||||
foreach (var obj in inactiveSaveables)
|
||||
{
|
||||
var saveable = obj as Interactions.SaveableInteractable;
|
||||
if (saveable != null && !saveable.gameObject.activeInHierarchy)
|
||||
{
|
||||
// Only register if it's actually inactive
|
||||
RegisterParticipant(saveable);
|
||||
registeredCount++;
|
||||
}
|
||||
}
|
||||
|
||||
Logging.Debug($"[SaveLoadManager] Discovered and registered {registeredCount} inactive SaveableInteractables");
|
||||
}
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -260,8 +226,31 @@ namespace Core.SaveLoad
|
||||
return;
|
||||
|
||||
IsRestoringState = true;
|
||||
int restoredCount = 0;
|
||||
|
||||
// Build dictionary for efficient lookup
|
||||
var saveDataDict = new Dictionary<string, string>();
|
||||
foreach (var entry in currentSaveData.participantStates)
|
||||
{
|
||||
saveDataDict[entry.saveId] = entry.serializedState;
|
||||
}
|
||||
|
||||
// NEW: Restore GLOBAL data via LifecycleManager (called ONCE on boot)
|
||||
if (Lifecycle.LifecycleManager.Instance != null)
|
||||
{
|
||||
Lifecycle.LifecycleManager.Instance.BroadcastGlobalRestoreRequested(saveDataDict);
|
||||
Logging.Debug($"[SaveLoadManager] Broadcast GLOBAL restore to LifecycleManager");
|
||||
}
|
||||
|
||||
// NEW: Restore SCENE data via LifecycleManager (for currently loaded scenes)
|
||||
if (Lifecycle.LifecycleManager.Instance != null)
|
||||
{
|
||||
Lifecycle.LifecycleManager.Instance.BroadcastSceneRestoreRequested(saveDataDict);
|
||||
Logging.Debug($"[SaveLoadManager] Broadcast SCENE restore to LifecycleManager");
|
||||
}
|
||||
|
||||
// EXISTING: Restore ISaveParticipants (backward compatibility)
|
||||
int restoredCount = 0;
|
||||
|
||||
// Clear pending queue at the start
|
||||
pendingParticipants.Clear();
|
||||
|
||||
@@ -272,19 +261,17 @@ namespace Core.SaveLoad
|
||||
string saveId = kvp.Key;
|
||||
ISaveParticipant participant = kvp.Value;
|
||||
|
||||
// Find the participant state in the list
|
||||
var entry = currentSaveData.participantStates.Find(e => e.saveId == saveId);
|
||||
if (entry != null && !string.IsNullOrEmpty(entry.serializedState))
|
||||
if (saveDataDict.TryGetValue(saveId, out string serializedState))
|
||||
{
|
||||
try
|
||||
{
|
||||
participant.RestoreState(entry.serializedState);
|
||||
participant.RestoreState(serializedState);
|
||||
restoredCount++;
|
||||
Logging.Debug($"[SaveLoadManager] Restored state for participant: {saveId}");
|
||||
Logging.Debug($"[SaveLoadManager] Restored ISaveParticipant: {saveId}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.Warning($"[SaveLoadManager] Exception while restoring state for '{saveId}': {ex}");
|
||||
Logging.Warning($"[SaveLoadManager] Exception while restoring '{saveId}': {ex}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -330,7 +317,7 @@ namespace Core.SaveLoad
|
||||
pendingParticipants.Clear();
|
||||
IsRestoringState = false;
|
||||
|
||||
Logging.Debug($"[SaveLoadManager] Restored state for {restoredCount} participants + {totalPendingRestored} pending participants");
|
||||
Logging.Debug($"[SaveLoadManager] Restored {restoredCount} ISaveParticipants + {totalPendingRestored} pending participants");
|
||||
OnParticipantStatesRestored?.Invoke();
|
||||
}
|
||||
|
||||
@@ -341,6 +328,76 @@ namespace Core.SaveLoad
|
||||
return Path.Combine(DefaultSaveFolder, $"save_{slot}.json");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves scene-specific data during scene transitions.
|
||||
/// This updates the in-memory save data but does NOT write to disk.
|
||||
/// Call Save() to persist to disk.
|
||||
/// </summary>
|
||||
public void SaveSceneData()
|
||||
{
|
||||
if (currentSaveData == null)
|
||||
{
|
||||
Logging.Warning("[SaveLoadManager] Cannot save scene data - no save data loaded");
|
||||
return;
|
||||
}
|
||||
|
||||
Logging.Debug("[SaveLoadManager] Saving scene-specific data...");
|
||||
|
||||
// Collect scene data from LifecycleManager
|
||||
if (Lifecycle.LifecycleManager.Instance != null)
|
||||
{
|
||||
var sceneData = Lifecycle.LifecycleManager.Instance.BroadcastSceneSaveRequested();
|
||||
|
||||
// Remove old scene data and add new
|
||||
if (currentSaveData.participantStates != null)
|
||||
{
|
||||
// Remove existing entries for these SaveIds (to avoid duplicates)
|
||||
currentSaveData.participantStates.RemoveAll(entry => sceneData.ContainsKey(entry.saveId));
|
||||
|
||||
// Add new scene data
|
||||
foreach (var kvp in sceneData)
|
||||
{
|
||||
currentSaveData.participantStates.Add(new ParticipantStateEntry
|
||||
{
|
||||
saveId = kvp.Key,
|
||||
serializedState = kvp.Value
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Logging.Debug($"[SaveLoadManager] Updated {sceneData.Count} scene data entries in memory");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Restores scene-specific data after scene load.
|
||||
/// Distributes data to components in the newly loaded scene.
|
||||
/// </summary>
|
||||
public void RestoreSceneData()
|
||||
{
|
||||
if (currentSaveData == null || currentSaveData.participantStates == null)
|
||||
{
|
||||
Logging.Debug("[SaveLoadManager] No scene data to restore");
|
||||
return;
|
||||
}
|
||||
|
||||
Logging.Debug("[SaveLoadManager] Restoring scene-specific data...");
|
||||
|
||||
// Build dictionary for efficient lookup
|
||||
var saveDataDict = new Dictionary<string, string>();
|
||||
foreach (var entry in currentSaveData.participantStates)
|
||||
{
|
||||
saveDataDict[entry.saveId] = entry.serializedState;
|
||||
}
|
||||
|
||||
// Restore scene data via LifecycleManager
|
||||
if (Lifecycle.LifecycleManager.Instance != null)
|
||||
{
|
||||
Lifecycle.LifecycleManager.Instance.BroadcastSceneRestoreRequested(saveDataDict);
|
||||
Logging.Debug($"[SaveLoadManager] Broadcast scene restore to LifecycleManager");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Entry point to save to a named slot. Starts an async coroutine that writes to disk.
|
||||
/// Fires OnSaveCompleted when finished.
|
||||
@@ -397,14 +454,44 @@ namespace Core.SaveLoad
|
||||
{
|
||||
currentSaveData.participantStates = new List<ParticipantStateEntry>();
|
||||
}
|
||||
else
|
||||
// NOTE: We do NOT clear participantStates here!
|
||||
// We preserve data from all scenes and update/add as needed.
|
||||
// This allows Level A data to persist when saving from Level B.
|
||||
|
||||
int savedCount = 0;
|
||||
|
||||
// NEW: Broadcast global save started event (ONCE)
|
||||
if (Lifecycle.LifecycleManager.Instance != null)
|
||||
{
|
||||
currentSaveData.participantStates.Clear();
|
||||
Lifecycle.LifecycleManager.Instance.BroadcastGlobalSaveStarted();
|
||||
}
|
||||
|
||||
// Capture state from all registered participants directly into the list
|
||||
// Create a snapshot to avoid collection modification during iteration
|
||||
int savedCount = 0;
|
||||
// Build a dictionary of all new data to save
|
||||
var allNewData = new Dictionary<string, string>();
|
||||
|
||||
// NEW: Collect GLOBAL data from ManagedBehaviours via LifecycleManager
|
||||
if (Lifecycle.LifecycleManager.Instance != null)
|
||||
{
|
||||
var globalData = Lifecycle.LifecycleManager.Instance.BroadcastGlobalSaveRequested();
|
||||
foreach (var kvp in globalData)
|
||||
{
|
||||
allNewData[kvp.Key] = kvp.Value;
|
||||
}
|
||||
Logging.Debug($"[SaveLoadManager] Collected {globalData.Count} GLOBAL save states");
|
||||
}
|
||||
|
||||
// NEW: Collect SCENE data from all loaded scenes
|
||||
if (Lifecycle.LifecycleManager.Instance != null)
|
||||
{
|
||||
var sceneData = Lifecycle.LifecycleManager.Instance.BroadcastSceneSaveRequested();
|
||||
foreach (var kvp in sceneData)
|
||||
{
|
||||
allNewData[kvp.Key] = kvp.Value;
|
||||
}
|
||||
Logging.Debug($"[SaveLoadManager] Collected {sceneData.Count} SCENE save states");
|
||||
}
|
||||
|
||||
// EXISTING: Collect data from ISaveParticipants (backward compatibility)
|
||||
foreach (var kvp in participants.ToList())
|
||||
{
|
||||
string saveId = kvp.Key;
|
||||
@@ -413,13 +500,8 @@ namespace Core.SaveLoad
|
||||
try
|
||||
{
|
||||
string serializedState = participant.SerializeState();
|
||||
currentSaveData.participantStates.Add(new ParticipantStateEntry
|
||||
{
|
||||
saveId = saveId,
|
||||
serializedState = serializedState
|
||||
});
|
||||
savedCount++;
|
||||
Logging.Debug($"[SaveLoadManager] Captured state for participant: {saveId}");
|
||||
allNewData[saveId] = serializedState;
|
||||
Logging.Debug($"[SaveLoadManager] Captured state for ISaveParticipant: {saveId}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -427,7 +509,28 @@ namespace Core.SaveLoad
|
||||
}
|
||||
}
|
||||
|
||||
Logging.Debug($"[SaveLoadManager] Captured state from {savedCount} participants");
|
||||
// Update existing entries or add new ones (preserves data from unloaded scenes)
|
||||
foreach (var kvp in allNewData)
|
||||
{
|
||||
var existingEntry = currentSaveData.participantStates.Find(e => e.saveId == kvp.Key);
|
||||
if (existingEntry != null)
|
||||
{
|
||||
// Update existing entry
|
||||
existingEntry.serializedState = kvp.Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Add new entry
|
||||
currentSaveData.participantStates.Add(new ParticipantStateEntry
|
||||
{
|
||||
saveId = kvp.Key,
|
||||
serializedState = kvp.Value
|
||||
});
|
||||
}
|
||||
savedCount++;
|
||||
}
|
||||
|
||||
Logging.Debug($"[SaveLoadManager] Captured state from {savedCount} total participants");
|
||||
|
||||
|
||||
json = JsonUtility.ToJson(currentSaveData, true);
|
||||
@@ -546,6 +649,12 @@ namespace Core.SaveLoad
|
||||
// Restore state for any already-registered participants
|
||||
RestoreAllParticipantStates();
|
||||
|
||||
// NEW: Broadcast global load completed event (ONCE, on boot)
|
||||
if (Lifecycle.LifecycleManager.Instance != null)
|
||||
{
|
||||
Lifecycle.LifecycleManager.Instance.BroadcastGlobalLoadCompleted();
|
||||
}
|
||||
|
||||
OnLoadCompleted?.Invoke(slot);
|
||||
Logging.Debug($"[SaveLoadManager] Load completed for slot '{slot}'");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user