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,3 +1,4 @@
using System;
using System.Threading;
using Cysharp.Threading.Tasks;
using UnityEngine;
@@ -7,6 +8,6 @@ namespace Darkmatter.Core.Contracts.Services.Capture
public interface ICaptureService
{
UniTask<byte[]> CapturePngAsync(GameObject captureObject, float scale,
CancellationToken cancellationToken = default);
Func<IDisposable> captureViewScopeFactory = null, CancellationToken cancellationToken = default);
}
}

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,7 +76,9 @@ namespace Darkmatter.Features.Artbook
private async UniTaskVoid LoadAndShowAsync(CancellationToken ct)
{
ClearOwnedAssets();
try
{
ClearVisibleAssets();
_entries.Clear();
_currentSpread = 0;
@@ -89,20 +92,25 @@ namespace Darkmatter.Features.Artbook
var progress = _progression.GetProgress(id);
if (progress is not { hasThumbnail: true }) continue;
var texture = await _progression.GetCachedThumbnailAsync(id);
if (ct.IsCancellationRequested) return;
if (texture == null) continue;
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}");
}
}
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,14 +199,33 @@ namespace Darkmatter.Features.Artbook
private async UniTaskVoid SaveToGalleryAsync(ArtbookEntry? entry)
{
if (!entry.HasValue || entry.Value.Thumbnail == null) return;
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;
await _gallery.SaveImageAsync(entry.Value.Thumbnail, entry.Value.Name, ct);
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());
private void HandleRightEditClicked() => OpenForEdit(GetRightEntry());
@@ -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()

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);

View File

@@ -1,3 +1,4 @@
using System;
using System.Collections.Generic;
using System.Threading;
using Cysharp.Threading.Tasks;
@@ -5,6 +6,7 @@ using Darkmatter.Core.Contracts.Services.Camera;
using Darkmatter.Core.Contracts.Services.Capture;
using UnityEngine;
using CameraType = Darkmatter.Core.Enums.Services.Camera.CameraType;
using Object = UnityEngine.Object;
namespace Darkmatter.Services.Capture
{
@@ -15,7 +17,7 @@ namespace Darkmatter.Services.Capture
public CaptureService(ICameraService cameraService) => _cameraService = cameraService;
public async UniTask<byte[]> CapturePngAsync(GameObject captureObject, float scale,
CancellationToken cancellationToken = default)
Func<IDisposable> captureViewScopeFactory = null, CancellationToken cancellationToken = default)
{
if (captureObject == null) return null;
var paperRT = captureObject.transform as RectTransform;
@@ -26,10 +28,6 @@ namespace Darkmatter.Services.Capture
int sw = Screen.width;
int sh = Screen.height;
Rect crop = ComputeCropRect(captureObject, sw, sh);
int cropW = Mathf.Max(1, (int)crop.width);
int cropH = Mathf.Max(1, (int)crop.height);
var prevFlags = cam.clearFlags;
var prevBg = cam.backgroundColor;
var prevTarget = cam.targetTexture;
@@ -41,15 +39,22 @@ namespace Darkmatter.Services.Capture
List<Canvas> disabledCanvases = null;
List<UnityEngine.UI.Graphic> disabledGraphics = null;
IDisposable captureViewScope = null;
try
{
await UniTask.WaitForEndOfFrame(cancellationToken);
captureViewScope = captureViewScopeFactory?.Invoke();
disabledCanvases = DisableOtherRootCanvases(paperCanvas);
disabledGraphics = HideNonPaperGraphics(paperRT);
HideBackdropGraphics(paperRT, disabledGraphics);
Rect crop = ComputeCropRect(captureObject, sw, sh);
int cropW = Mathf.Max(1, (int)crop.width);
int cropH = Mathf.Max(1, (int)crop.height);
cam.clearFlags = CameraClearFlags.SolidColor;
cam.backgroundColor = new Color(0f, 0f, 0f, 0f);
cam.targetTexture = rt;
@@ -67,6 +72,8 @@ namespace Darkmatter.Services.Capture
fullScreen.ReadPixels(new Rect(0, 0, sw, sh), 0, 0);
fullScreen.Apply();
RenderTexture.active = prevActive;
captureViewScope?.Dispose();
captureViewScope = null;
try
{
@@ -118,6 +125,7 @@ namespace Darkmatter.Services.Capture
foreach (var c in disabledCanvases)
if (c != null)
c.enabled = true;
captureViewScope?.Dispose();
}
}

File diff suppressed because one or more lines are too long

View File

@@ -2689,7 +2689,7 @@ MonoBehaviour:
m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.UI.Image
m_Material: {fileID: 0}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_RaycastTarget: 1
m_RaycastTarget: 0
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
m_Maskable: 1
m_OnCullStateChanged:

View File

@@ -1 +1 @@
<?xml version="1.0" encoding="utf-8"?><resources><string name="com.crashlytics.android.build_id" translatable="false">7f32d9eb-b712-4616-ad93-07615d514802</string></resources>
<?xml version="1.0" encoding="utf-8"?><resources><string name="com.crashlytics.android.build_id" translatable="false">8fc48ed6-41de-4dcf-a980-9aa3316fad11</string></resources>

View File

@@ -1 +1 @@
<?xml version="1.0" encoding="utf-8"?><resources><string name="com.google.firebase.crashlytics.unity_version" translatable="false">6000.4.5f1</string></resources>
<?xml version="1.0" encoding="utf-8"?><resources><string name="com.google.firebase.crashlytics.unity_version" translatable="false">6000.5.1f1</string></resources>

View File

@@ -18,12 +18,15 @@ MonoBehaviour:
m_PlatformId: b9b35072a6f44c2e863f17467ea3dc13
m_PlatformBuildProfile:
rid: 570818657416118487
m_ActivePlatformGuid:
m_AdditionalPlatformBuildSettings: []
m_OverrideGlobalSceneList: 0
m_Scenes: []
m_HasScriptingDefines: 0
m_ScriptingDefines: []
m_PlayerSettingsYaml:
m_Settings: []
requiredComponents: []
references:
version: 2
RefIds:
@@ -33,6 +36,7 @@ MonoBehaviour:
m_Development: 0
m_ConnectProfiler: 0
m_BuildWithDeepProfilingSupport: 0
m_BuildWithCodeCoverage: 0
m_AllowDebugging: 0
m_WaitForManagedDebugger: 0
m_ManagedDebuggerFixedPort: 0

View File

@@ -148,7 +148,7 @@ PlayerSettings:
loadStoreDebugModeEnabled: 0
visionOSBundleVersion: 1.0
tvOSBundleVersion: 1.0
bundleVersion: 1.8
bundleVersion: 1.9
preloadedAssets:
- {fileID: 11400000, guid: cc969dbecb228fa49b16da9273753a8f, type: 2}
- {fileID: -944628639613478452, guid: 2bcd2660ca9b64942af0de543d8d7100, type: 3}
@@ -180,7 +180,7 @@ PlayerSettings:
iPhone: 1
tvOS: 0
overrideDefaultApplicationIdentifier: 1
AndroidBundleVersionCode: 8
AndroidBundleVersionCode: 9
AndroidMinSdkVersion: 26
AndroidTargetSdkVersion: 0
AndroidPreferredInstallLocation: 1