Add docs, cleanup structure

This commit is contained in:
Michal Pikulski
2025-11-27 14:19:01 +01:00
parent a57fca63bf
commit 3723857a14
22 changed files with 675 additions and 344 deletions

View File

@@ -2,27 +2,73 @@
using Core;
using Core.Lifecycle;
using Minigames.StatueDressup.Data;
using UnityEngine;
using UnityEngine.ResourceManagement.AsyncOperations;
using Utils;
namespace Minigames.StatueDressup.Controllers
{
/// <summary>
/// Singleton manager for decoration data loading and caching.
/// Loads all DecorationData assets once via Addressables and provides shared access.
/// Singleton manager for statue dressup data loading and caching.
/// Loads settings first, then decoration data via Addressables and provides shared access.
/// Used by both minigame and town map to avoid duplicate loading.
/// </summary>
public class DecorationDataManager : ManagedBehaviour
public class DecorationDataManager : ManagedBehaviour, IReadyNotifier
{
public static DecorationDataManager Instance { get; private set; }
// Static callback queue for when instance doesn't exist yet
private static readonly List<System.Action> PendingCallbacks = new List<System.Action>();
private AppleHills.Core.Settings.IStatueDressupSettings settings;
private Dictionary<string, DecorationData> decorationDataDict;
private AsyncOperationHandle<System.Collections.Generic.IList<DecorationData>> decorationDataHandle;
private bool isLoaded;
/// <summary>
/// Check if data is loaded and ready
/// Get the settings instance
/// </summary>
public AppleHills.Core.Settings.IStatueDressupSettings Settings => settings;
/// <summary>
/// Check if data is loaded and ready (implements IReadyNotifier)
/// </summary>
public bool IsReady => isLoaded;
/// <summary>
/// Event invoked when data is loaded and ready (implements IReadyNotifier)
/// </summary>
public event System.Action OnReady;
/// <summary>
/// Static method to register callbacks that will execute when manager is ready.
/// Can be called before instance exists - callbacks will be queued and executed when ready.
/// Handles null instance gracefully by queuing callbacks until instance is created and ready.
/// </summary>
public static void WhenReady(System.Action callback)
{
if (callback == null) return;
// If instance exists and is ready, execute immediately
if (Instance != null && Instance.IsReady)
{
callback.Invoke();
return;
}
// If instance exists but not ready, use instance method
if (Instance != null)
{
(Instance as IReadyNotifier).WhenReady(callback);
return;
}
// Instance doesn't exist yet - queue callback
PendingCallbacks.Add(callback);
}
/// <summary>
/// Legacy property - use IsReady instead
/// </summary>
[System.Obsolete("Use IsReady instead")]
public bool IsLoaded => isLoaded;
/// <summary>
@@ -32,8 +78,6 @@ namespace Minigames.StatueDressup.Controllers
internal override void OnManagedAwake()
{
base.OnManagedAwake();
// Singleton pattern
if (Instance != null && Instance != this)
{
@@ -44,12 +88,32 @@ namespace Minigames.StatueDressup.Controllers
Instance = this;
}
internal override async void OnManagedStart()
internal override void OnManagedStart()
{
base.OnManagedStart();
StartCoroutine(LoadSettingsAndData());
}
/// <summary>
/// Load settings first, then decoration data
/// </summary>
private System.Collections.IEnumerator LoadSettingsAndData()
{
Logging.Debug("[DecorationDataManager] Waiting for GameManager to be accessible...");
await LoadAllDecorationData();
// Wait until GameManager is accessible and settings can be retrieved
settings = GameManager.GetSettingsObject<AppleHills.Core.Settings.IStatueDressupSettings>();
Logging.Debug("[DecorationDataManager] Settings loaded successfully");
// Now load decoration data
var loadTask = LoadAllDecorationData();
yield return new WaitUntil(() => loadTask.IsCompleted);
if (loadTask.IsFaulted)
{
Logging.Error($"[DecorationDataManager] Failed to load decoration data: {loadTask.Exception}");
}
}
/// <summary>
@@ -63,7 +127,6 @@ namespace Minigames.StatueDressup.Controllers
return;
}
var settings = StatueDressupSettings.Instance?.Settings;
string label = settings?.DecorationDataLabel;
if (string.IsNullOrEmpty(label))
@@ -85,6 +148,20 @@ namespace Minigames.StatueDressup.Controllers
isLoaded = true;
Logging.Debug($"[DecorationDataManager] Loaded {decorationDataDict.Count} DecorationData assets");
// Subscribe all pending callbacks to OnReady event before invoking
if (PendingCallbacks.Count > 0)
{
Logging.Debug($"[DecorationDataManager] Subscribing {PendingCallbacks.Count} pending callbacks to OnReady");
foreach (var callback in PendingCallbacks)
{
OnReady += callback;
}
PendingCallbacks.Clear();
}
// Mark as ready and notify listeners (including pending callbacks)
OnReady?.Invoke();
}
/// <summary>
@@ -122,10 +199,8 @@ namespace Minigames.StatueDressup.Controllers
return decorationDataDict.TryGetValue(decorationId, out data);
}
internal override void OnManagedDestroy()
private void OnDestroy()
{
base.OnManagedDestroy();
// Release Addressables handle
AddressablesUtility.ReleaseHandle(decorationDataHandle);

View File

@@ -35,9 +35,6 @@ namespace Minigames.StatueDressup.Controllers
public int CurrentPage => currentPage;
public int TotalPages => totalPages;
/// <summary>
/// Early initialization - singleton setup
/// </summary>
internal override void OnManagedAwake()
{
base.OnManagedAwake();
@@ -66,7 +63,19 @@ namespace Minigames.StatueDressup.Controllers
{
base.OnManagedStart();
var settings = StatueDressupSettings.Instance?.Settings;
// Wait for data manager to be ready before initializing
DecorationDataManager.WhenReady(() =>
{
InitializeMenu();
});
}
/// <summary>
/// Initialize menu once data manager is ready
/// </summary>
private void InitializeMenu()
{
var settings = DecorationDataManager.Instance?.Settings;
if (settings == null)
{
@@ -75,7 +84,7 @@ namespace Minigames.StatueDressup.Controllers
}
var allDecorations = settings.AllDecorations;
int itemsPerPage = settings.ItemsPerPage;
int itemsPerPage = settings?.ItemsPerPage ?? StatueDressupConstants.DefaultMenuItemsPerPage;
Logging.Debug($"[DecorationMenuController] Initializing with {allDecorations?.Count ?? 0} decorations");
@@ -114,7 +123,7 @@ namespace Minigames.StatueDressup.Controllers
/// </summary>
private void PopulateCurrentPage()
{
var settings = StatueDressupSettings.Instance?.Settings;
var settings = DecorationDataManager.Instance?.Settings;
if (settings == null) return;
var allDecorations = settings.AllDecorations;
@@ -196,7 +205,7 @@ namespace Minigames.StatueDressup.Controllers
outlineRect,
StatueDecorationController.Instance.StatueParent,
StatueDecorationController.Instance,
StatueDressupSettings.Instance.Settings,
DecorationDataManager.Instance.Settings,
OnDraggableFinished,
ShowStatueOutline,
HideStatueOutline

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using Core;
using Core.Lifecycle;
@@ -44,9 +44,6 @@ namespace Minigames.StatueDressup.Controllers
public Transform StatueParent => statueParent;
public RectTransform StatueArea => statueArea;
/// <summary>
/// Early initialization - singleton setup
/// </summary>
internal override void OnManagedAwake()
{
base.OnManagedAwake();
@@ -71,30 +68,12 @@ namespace Minigames.StatueDressup.Controllers
Logging.Debug("[StatueDecorationController] Initializing minigame");
// DecorationDataManager exists (initialized in OnManagedAwake) but data loads async in OnManagedStart
// Wait for data to finish loading before initializing
if (!DecorationDataManager.Instance.IsLoaded)
// Wait for decoration data to be ready before initializing
DecorationDataManager.WhenReady(() =>
{
Logging.Debug("[StatueDecorationController] Waiting for DecorationData to load...");
StartCoroutine(WaitForDataAndInitialize());
return;
}
InitializeMinigame();
}
/// <summary>
/// Wait for data manager to finish loading data before initializing
/// </summary>
private System.Collections.IEnumerator WaitForDataAndInitialize()
{
while (!DecorationDataManager.Instance.IsLoaded)
{
yield return null;
}
Logging.Debug("[StatueDecorationController] DecorationData loaded, initializing minigame");
InitializeMinigame();
Logging.Debug("[StatueDecorationController] DecorationData ready, initializing minigame");
InitializeMinigame();
});
}
/// <summary>
@@ -342,7 +321,7 @@ namespace Minigames.StatueDressup.Controllers
/// </summary>
private void SaveStatueState()
{
var settings = StatueDressupSettings.Instance?.Settings;
var settings = DecorationDataManager.Instance?.Settings;
// Check if persistence is enabled
if (settings == null || !settings.EnableStatePersistence)
@@ -363,7 +342,7 @@ namespace Minigames.StatueDressup.Controllers
/// </summary>
private void LoadStatueState()
{
var settings = StatueDressupSettings.Instance?.Settings;
var settings = DecorationDataManager.Instance?.Settings;
// Check if persistence is enabled
if (settings == null || !settings.EnableStatePersistence)
@@ -435,7 +414,7 @@ namespace Minigames.StatueDressup.Controllers
var context = DecorationDragContext.CreateForPlaced(
decorationData,
this,
StatueDressupSettings.Instance.Settings,
DecorationDataManager.Instance.Settings,
statueArea,
canvasParent,
showOutlineCallback,
@@ -512,3 +491,4 @@ namespace Minigames.StatueDressup.Controllers
}
}

View File

@@ -1,59 +0,0 @@
using Core;
using Core.Lifecycle;
namespace Minigames.StatueDressup.Controllers
{
/// <summary>
/// Singleton manager for StatueDressup settings access.
/// Loads settings once and provides global access point.
/// </summary>
public class StatueDressupSettings : ManagedBehaviour
{
public static StatueDressupSettings Instance { get; private set; }
private AppleHills.Core.Settings.IStatueDressupSettings settings;
/// <summary>
/// Get the settings instance
/// </summary>
public AppleHills.Core.Settings.IStatueDressupSettings Settings => settings;
internal override void OnManagedAwake()
{
base.OnManagedAwake();
// Singleton pattern
if (Instance != null && Instance != this)
{
Logging.Warning("[StatueDressupSettings] Duplicate instance detected. Destroying duplicate.");
Destroy(gameObject);
return;
}
Instance = this;
// Load settings once
settings = GameManager.GetSettingsObject<AppleHills.Core.Settings.IStatueDressupSettings>();
if (settings == null)
{
Logging.Error("[StatueDressupSettings] Failed to load StatueDressupSettings!");
}
else
{
Logging.Debug("[StatueDressupSettings] Settings loaded successfully");
}
}
internal override void OnManagedDestroy()
{
base.OnManagedDestroy();
if (Instance == this)
{
Instance = null;
}
}
}
}

View File

@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: acf5624b19664ce5900f1a7c1328edbc
timeCreated: 1764240158

View File

@@ -1,4 +1,4 @@
using System.Collections;
using System.Collections;
using System.Collections.Generic;
using Core;
using Core.Lifecycle;
@@ -40,9 +40,9 @@ namespace Minigames.StatueDressup.Controllers
private bool isLoadingPage;
private PhotoEnlargeController enlargeController;
internal override void OnManagedStart()
internal override void OnManagedAwake()
{
base.OnManagedStart();
base.OnManagedAwake();
// Singleton pattern
if (Instance != null && Instance != this)
@@ -53,11 +53,29 @@ namespace Minigames.StatueDressup.Controllers
}
Instance = this;
}
internal override void OnManagedStart()
{
base.OnManagedStart();
var settings = StatueDressupSettings.Instance?.Settings;
// Wait for data manager to be ready before initializing
DecorationDataManager.WhenReady(() =>
{
InitializeGallery();
});
}
/// <summary>
/// Initialize gallery once data manager is ready
/// </summary>
private void InitializeGallery()
{
var settings = DecorationDataManager.Instance?.Settings;
// Initialize enlarge controller
enlargeController = new PhotoEnlargeController(backdrop, enlargedContainer, settings?.GalleryAnimationDuration ?? 0.3f);
enlargeController = new PhotoEnlargeController(backdrop, enlargedContainer,
settings?.GalleryAnimationDuration ?? StatueDressupConstants.DefaultAnimationDuration);
// Setup page navigation buttons
if (previousPageButton != null)
@@ -108,7 +126,7 @@ namespace Minigames.StatueDressup.Controllers
ClearGrid();
// Get photos for current page
int itemsPerPage = StatueDressupSettings.Instance?.Settings?.GalleryItemsPerPage ?? 20;
int itemsPerPage = DecorationDataManager.Instance?.Settings?.GalleryItemsPerPage ?? StatueDressupConstants.DefaultGalleryItemsPerPage;
List<string> pagePhotoIds = PhotoManager.GetPhotoIdsPage(CaptureType.StatueMinigame, currentPage, itemsPerPage);
Logging.Debug($"[StatuePhotoGalleryController] Displaying page {currentPage + 1}: {pagePhotoIds.Count} items");
@@ -134,7 +152,7 @@ namespace Minigames.StatueDressup.Controllers
/// </summary>
private void UpdatePageButtons()
{
int itemsPerPage = StatueDressupSettings.Instance?.Settings?.GalleryItemsPerPage ?? 20;
int itemsPerPage = DecorationDataManager.Instance?.Settings?.GalleryItemsPerPage ?? StatueDressupConstants.DefaultGalleryItemsPerPage;
int totalPages = Mathf.CeilToInt((float)allPhotoIds.Count / itemsPerPage);
// Enable/disable previous button
@@ -168,7 +186,7 @@ namespace Minigames.StatueDressup.Controllers
/// </summary>
private void OnNextPageClicked()
{
int itemsPerPage = StatueDressupSettings.Instance?.Settings?.GalleryItemsPerPage ?? 20;
int itemsPerPage = DecorationDataManager.Instance?.Settings?.GalleryItemsPerPage ?? StatueDressupConstants.DefaultGalleryItemsPerPage;
int totalPages = Mathf.CeilToInt((float)allPhotoIds.Count / itemsPerPage);
if (currentPage < totalPages - 1)
@@ -224,7 +242,7 @@ namespace Minigames.StatueDressup.Controllers
}
// Create thumbnail
int thumbSize = StatueDressupSettings.Instance?.Settings?.GalleryThumbnailSize ?? 256;
int thumbSize = DecorationDataManager.Instance?.Settings?.GalleryThumbnailSize ?? StatueDressupConstants.DefaultThumbnailSize;
Texture2D thumbnail = PhotoManager.CreateThumbnail(fullPhoto, thumbSize);
// Destroy full photo immediately (we only need thumbnail)
@@ -250,7 +268,7 @@ namespace Minigames.StatueDressup.Controllers
thumbnailCacheOrder.Enqueue(photoId);
// Evict oldest if over limit
int maxCached = StatueDressupSettings.Instance?.Settings?.GalleryMaxCachedThumbnails ?? 50;
int maxCached = DecorationDataManager.Instance?.Settings?.GalleryMaxCachedThumbnails ?? StatueDressupConstants.DefaultMaxCachedThumbnails;
while (thumbnailCache.Count > maxCached && thumbnailCacheOrder.Count > 0)
{
string oldestId = thumbnailCacheOrder.Dequeue();
@@ -284,7 +302,7 @@ namespace Minigames.StatueDressup.Controllers
Logging.Debug($"[StatuePhotoGalleryController] Enlarging photo: {photoId}");
float enlargedScale = StatueDressupSettings.Instance?.Settings?.GalleryEnlargedScale ?? 2.5f;
float enlargedScale = DecorationDataManager.Instance?.Settings?.GalleryEnlargedScale ?? StatueDressupConstants.DefaultEnlargedScale;
// Check cache first
if (fullPhotoCache.TryGetValue(photoId, out Texture2D fullPhoto))
@@ -404,3 +422,4 @@ namespace Minigames.StatueDressup.Controllers
}
}