Commit 47b99886 authored by ElderLich's avatar ElderLich

Bug Fix: Fix MyCard watch avatar refresh on recycled UI rows

Prevents MissingReferenceException spam when MyCard watch-list items are destroyed or recycled during async avatar loading. Avatar downloads now respect row lifetime/cancellation and only update RawImage targets that still exist.
parent f320ee58
...@@ -7,6 +7,7 @@ using System.IO; ...@@ -7,6 +7,7 @@ using System.IO;
using System.Linq; using System.Linq;
using System.Net; using System.Net;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using UnityEngine; using UnityEngine;
using UnityEngine.Networking; using UnityEngine.Networking;
...@@ -224,16 +225,19 @@ namespace MDPro3 ...@@ -224,16 +225,19 @@ namespace MDPro3
#region Online #region Online
public static async Task<Texture2D> DownloadImageAsync(string url) public static async Task<Texture2D> DownloadImageAsync(string url, CancellationToken cancellationToken = default)
{ {
using var request = UnityWebRequestTexture.GetTexture(url); using var request = UnityWebRequestTexture.GetTexture(url);
request.SetRequestHeader("User-Agent", "MDPro3/" + Application.version + " (" + System.Environment.OSVersion.ToString() + "); Unity/" + Application.unityVersion); request.SetRequestHeader("User-Agent", "MDPro3/" + Application.version + " (" + System.Environment.OSVersion.ToString() + "); Unity/" + Application.unityVersion);
var send = request.SendWebRequest(); var send = request.SendWebRequest();
await TaskUtility.WaitUntil(() => send.isDone); while (!send.isDone)
await TaskUtility.WaitOneFrame(cancellationToken);
if (!Application.isPlaying) if (!Application.isPlaying)
return null; return null;
cancellationToken.ThrowIfCancellationRequested();
if (request.result == UnityWebRequest.Result.Success) if (request.result == UnityWebRequest.Result.Success)
{ {
return DownloadHandlerTexture.GetContent(request); return DownloadHandlerTexture.GetContent(request);
......
using System; using System;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using UnityEngine; using UnityEngine;
using UnityEngine.Networking; using UnityEngine.Networking;
...@@ -113,8 +114,10 @@ namespace MDPro3.Net ...@@ -113,8 +114,10 @@ namespace MDPro3.Net
} }
} }
public static async UniTask<Texture2D> GetAvatarAsync(string userName) public static async UniTask<Texture2D> GetAvatarAsync(string userName, CancellationToken cancellationToken = default)
{ {
cancellationToken.ThrowIfCancellationRequested();
if(!Directory.Exists(avatarSavePath)) if(!Directory.Exists(avatarSavePath))
Directory.CreateDirectory(avatarSavePath); Directory.CreateDirectory(avatarSavePath);
...@@ -137,6 +140,7 @@ namespace MDPro3.Net ...@@ -137,6 +140,7 @@ namespace MDPro3.Net
return cachedAvatars[avatarName]; return cachedAvatars[avatarName];
var pic = await TextureManager.LoadPicFromFileAsync(fullPath); var pic = await TextureManager.LoadPicFromFileAsync(fullPath);
cancellationToken.ThrowIfCancellationRequested();
lock (cachedAvatars) lock (cachedAvatars)
if (!cachedAvatars.ContainsKey(avatarName)) if (!cachedAvatars.ContainsKey(avatarName))
...@@ -148,7 +152,7 @@ namespace MDPro3.Net ...@@ -148,7 +152,7 @@ namespace MDPro3.Net
using(var request = UnityWebRequest.Get(userUrl.Replace("{username}", userName))) using(var request = UnityWebRequest.Get(userUrl.Replace("{username}", userName)))
{ {
await request.SendWebRequest(); await request.SendWebRequest().WithCancellation(cancellationToken);
if (request.result == UnityWebRequest.Result.Success) if (request.result == UnityWebRequest.Result.Success)
{ {
avatarAddress = JsonUtility.FromJson<MyCardRoomUserInfo>(request.downloadHandler.text).user.avatar; avatarAddress = JsonUtility.FromJson<MyCardRoomUserInfo>(request.downloadHandler.text).user.avatar;
...@@ -160,8 +164,11 @@ namespace MDPro3.Net ...@@ -160,8 +164,11 @@ namespace MDPro3.Net
} }
} }
var requestAvatar = Tools.DownloadImageAsync(avatarAddress); cancellationToken.ThrowIfCancellationRequested();
var requestAvatar = Tools.DownloadImageAsync(avatarAddress, cancellationToken);
await requestAvatar; await requestAvatar;
cancellationToken.ThrowIfCancellationRequested();
Texture2D downloadImage = requestAvatar.Result; Texture2D downloadImage = requestAvatar.Result;
if (downloadImage == null) if (downloadImage == null)
return null; return null;
......
...@@ -2,9 +2,11 @@ using MDPro3.Net; ...@@ -2,9 +2,11 @@ using MDPro3.Net;
using TMPro; using TMPro;
using UnityEngine; using UnityEngine;
using UnityEngine.UI; using UnityEngine.UI;
using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text; using System.Text;
using System.Threading;
using UnityEngine.EventSystems; using UnityEngine.EventSystems;
using MDPro3.Servant; using MDPro3.Servant;
using MDPro3.UI.ServantUI; using MDPro3.UI.ServantUI;
...@@ -62,15 +64,34 @@ namespace MDPro3.UI ...@@ -62,15 +64,34 @@ namespace MDPro3.UI
protected override async UniTask RefreshAsync() protected override async UniTask RefreshAsync()
{ {
refreshed = false; refreshed = false;
var cancellationToken = cts?.Token ?? destroyCancellationToken;
var face0 = Manager.GetElement<RawImage>("Face0");
var face1 = Manager.GetElement<RawImage>("Face1");
Manager.GetElement<RawImage>("Face0").texture = Appearance.defaultFace0.texture; if (face0 != null)
Manager.GetElement<RawImage>("Face1").texture = Appearance.defaultFace1.texture; face0.texture = Appearance.defaultFace0.texture;
if (face1 != null)
face1.texture = Appearance.defaultFace1.texture;
Manager.GetElement<RawImage>("Face0").texture = await MyCard.GetAvatarAsync(player0Name); try
Manager.GetElement<RawImage>("Face1").texture = await MyCard.GetAvatarAsync(player1Name); {
var avatar0 = await MyCard.GetAvatarAsync(player0Name, cancellationToken);
if (!cancellationToken.IsCancellationRequested && face0 != null)
face0.texture = avatar0;
var avatar1 = await MyCard.GetAvatarAsync(player1Name, cancellationToken);
if (!cancellationToken.IsCancellationRequested && face1 != null)
face1.texture = avatar1;
refreshed = true; refreshed = true;
} }
catch (OperationCanceledException)
{
}
catch (MissingReferenceException)
{
}
}
protected override void CallToggleOnEvent() protected override void CallToggleOnEvent()
{ {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment