Crash fixes

This commit is contained in:
Savya Bikram Shah
2026-06-26 18:18:49 +05:45
parent 21e5206626
commit b28e1f637d
11 changed files with 161 additions and 940 deletions

View File

@@ -1,7 +1,6 @@
using System;
using UnityEngine;
namespace Darkmatter.Features.Artbook
{
public record struct ArtbookEntry(string Id, string Name, Texture2D Thumbnail, DateTime UpdatedUtc);
public record struct ArtbookEntry(string Id, string Name, DateTime UpdatedUtc);
}

View File

@@ -26,12 +26,13 @@ namespace Darkmatter.Features.Artbook
private readonly IRewardedSaveGate _saveGate;
private readonly List<ArtbookEntry> _entries = new();
private readonly List<Sprite> _ownedSprites = new();
private readonly List<Texture2D> _ownedTextures = new();
private readonly List<Sprite> _visibleSprites = new();
private readonly List<Texture2D> _visibleTextures = new();
private Action _onClose;
private CancellationTokenSource _cts;
private IDisposable _openSubscription;
private int _currentSpread;
private int _renderVersion;
public ArtbookPresenter(
ArtbookView view,
@@ -75,33 +76,40 @@ namespace Darkmatter.Features.Artbook
private async UniTaskVoid LoadAndShowAsync(CancellationToken ct)
{
ClearOwnedAssets();
_entries.Clear();
_currentSpread = 0;
await _catalog.FetchAsync();
if (ct.IsCancellationRequested) return;
foreach (var id in _catalog.AllTemplateIds)
try
{
ClearVisibleAssets();
_entries.Clear();
_currentSpread = 0;
await _catalog.FetchAsync();
if (ct.IsCancellationRequested) return;
var progress = _progression.GetProgress(id);
if (progress is not { hasThumbnail: true }) continue;
foreach (var id in _catalog.AllTemplateIds)
{
if (ct.IsCancellationRequested) return;
var texture = await _progression.GetCachedThumbnailAsync(id);
if (ct.IsCancellationRequested) return;
if (texture == null) continue;
var progress = _progression.GetProgress(id);
if (progress is not { hasThumbnail: true }) continue;
var template = await _catalog.LoadAsync(id);
if (ct.IsCancellationRequested) return;
var template = await _catalog.LoadAsync(id);
if (ct.IsCancellationRequested) return;
if (template == null) continue;
_ownedTextures.Add(texture);
_entries.Add(new ArtbookEntry(id, template.DisplayName, texture, progress.Value.UpdatedUtc));
_entries.Add(new ArtbookEntry(id, template.DisplayName, progress.Value.UpdatedUtc));
}
_entries.Sort((a, b) => b.UpdatedUtc.CompareTo(a.UpdatedUtc));
RenderSpread();
}
catch (OperationCanceledException)
{
// Opening/closing the art book can cancel the load at any await point.
}
catch (Exception e)
{
Debug.LogError($"[Artbook] Failed to load entries: {e}");
}
_entries.Sort((a, b) => b.UpdatedUtc.CompareTo(a.UpdatedUtc));
RenderSpread();
}
private void HandlePrevSpreadClicked()
@@ -122,7 +130,7 @@ namespace Darkmatter.Features.Artbook
private void RenderSpread()
{
_view.SetSpread(GetLeftEntry(), SpriteFor(GetLeftEntry()), GetRightEntry(), SpriteFor(GetRightEntry()));
RenderSpreadAsync(++_renderVersion, _cts?.Token ?? CancellationToken.None).Forget();
_view.SetNavigation(_currentSpread > 0, _currentSpread < TotalSpreads - 1);
}
@@ -138,13 +146,51 @@ namespace Darkmatter.Features.Artbook
return idx < _entries.Count ? _entries[idx] : null;
}
private Sprite SpriteFor(ArtbookEntry? entry)
private async UniTaskVoid RenderSpreadAsync(int version, CancellationToken ct)
{
if (!entry.HasValue) return null;
var tex = entry.Value.Thumbnail;
var left = GetLeftEntry();
var right = GetRightEntry();
ClearVisibleAssets();
_view.SetSpread(left, null, right, null);
try
{
var leftSprite = await SpriteForAsync(left, version, ct);
var rightSprite = await SpriteForAsync(right, version, ct);
if (ct.IsCancellationRequested || version != _renderVersion)
{
return;
}
_view.SetSpread(left, leftSprite, right, rightSprite);
}
catch (OperationCanceledException)
{
if (version == _renderVersion) ClearVisibleAssets();
}
catch (Exception e)
{
if (version == _renderVersion) ClearVisibleAssets();
Debug.LogError($"[Artbook] Failed to render spread: {e}");
}
}
private async UniTask<Sprite> SpriteForAsync(ArtbookEntry? entry, int version, CancellationToken ct)
{
if (!entry.HasValue || ct.IsCancellationRequested || version != _renderVersion) return null;
var tex = await _progression.GetCachedThumbnailAsync(entry.Value.Id);
if (ct.IsCancellationRequested || version != _renderVersion)
{
if (tex != null) UnityEngine.Object.Destroy(tex);
return null;
}
if (tex == null) return null;
var sprite = Sprite.Create(tex, new Rect(0, 0, tex.width, tex.height), new Vector2(0.5f, 0.5f), 100f);
_ownedSprites.Add(sprite);
_visibleTextures.Add(tex);
_visibleSprites.Add(sprite);
return sprite;
}
@@ -153,13 +199,32 @@ namespace Darkmatter.Features.Artbook
private async UniTaskVoid SaveToGalleryAsync(ArtbookEntry? entry)
{
if (!entry.HasValue || entry.Value.Thumbnail == null) return;
var ct = _cts?.Token ?? CancellationToken.None;
// Same kid-friendly prompt + rewarded ad as the gameplay save button.
if (!await _saveGate.RequestSaveAsync(ct)) return;
await _gallery.SaveImageAsync(entry.Value.Thumbnail, entry.Value.Name, ct);
// The art book has no success popup of its own, so use the shared toast.
await _saveGate.ShowSavedAsync(ct);
Texture2D texture = null;
try
{
if (!entry.HasValue) return;
var ct = _cts?.Token ?? CancellationToken.None;
// Same kid-friendly prompt + rewarded ad as the gameplay save button.
if (!await _saveGate.RequestSaveAsync(ct)) return;
texture = await _progression.GetCachedThumbnailAsync(entry.Value.Id);
if (texture == null) return;
await _gallery.SaveImageAsync(texture, entry.Value.Name, ct);
// The art book has no success popup of its own, so use the shared toast.
await _saveGate.ShowSavedAsync(ct);
}
catch (OperationCanceledException)
{
// The art book was closed or replaced while saving.
}
catch (Exception e)
{
Debug.LogError($"[Artbook] Failed to save page: {e}");
}
finally
{
if (texture != null) UnityEngine.Object.Destroy(texture);
}
}
private void HandleLeftEditClicked() => OpenForEdit(GetLeftEntry());
@@ -169,27 +234,43 @@ namespace Darkmatter.Features.Artbook
{
if (!entry.HasValue) return;
_eventBus.Publish(new DrawingSelectedSignal(entry.Value.Id));
StopActiveWork();
_view.Hide();
}
private void HandleBackButtonClicked()
{
_onClose?.Invoke();
StopActiveWork();
_view.Hide();
}
private void HandleColorbookButtonClicked()
{
_eventBus.Publish(new OpenColorBookSignal());
StopActiveWork();
_view.Hide();
}
private void StopActiveWork()
{
_renderVersion++;
_cts?.Cancel();
_view.SetSpread(null, null, null, null);
ClearVisibleAssets();
}
private void ClearOwnedAssets()
{
foreach (var s in _ownedSprites) UnityEngine.Object.Destroy(s);
foreach (var t in _ownedTextures) UnityEngine.Object.Destroy(t);
_ownedSprites.Clear();
_ownedTextures.Clear();
ClearVisibleAssets();
}
private void ClearVisibleAssets()
{
foreach (var s in _visibleSprites) UnityEngine.Object.Destroy(s);
foreach (var t in _visibleTextures) UnityEngine.Object.Destroy(t);
_visibleSprites.Clear();
_visibleTextures.Clear();
}
public void Dispose()
@@ -208,4 +289,4 @@ namespace Darkmatter.Features.Artbook
ClearOwnedAssets();
}
}
}
}

View File

@@ -51,12 +51,11 @@ namespace Darkmatter.Features.Capture
// A null result keeps the existing thumbnail and skips the gallery write (both no-op on null).
if (_coloring.IsPlayingCompletionAnimation) return null;
byte[] png;
using (_coloring.UseNonZoomedView())
{
png = await _captureService.CapturePngAsync(_refs.PaperRoot.gameObject, _config.CaptureScale, ct);
}
var png = await _captureService.CapturePngAsync(
_refs.PaperRoot.gameObject,
_config.CaptureScale,
_coloring.UseNonZoomedView,
ct);
if (!saveToGallery || png == null || png.Length == 0) return png;
var tex = new Texture2D(2, 2, TextureFormat.RGBA32, mipChain: false);