using System; using System.Collections; using System.Collections.Generic; using System.IO; using FairyGUI; using RedHotRoast; using SGModule.Common; using SGModule.NetKit; using UnityEngine; using UnityEngine.Events; using UnityEngine.Networking; using UnityEngine.Video; using Object = UnityEngine.Object; public class TextureHelper { public static void GetNTexture(string path, string name, UnityAction action) { if (!nTexturesDic.TryGetValue(path + name, out var spr)) try { LoadKit.Instance.LoadSprite(path, name, sprite => { if (sprite != null) { spr = new NTexture(sprite); action?.Invoke(spr); nTexturesDic.TryAdd(path + name, spr); } }); } catch (Exception e) { Debug.LogError(e); } else action?.Invoke(spr); } public static void GetItemIcon(int itemId, UnityAction action = null) { var itemIconPath = "Atlas.Item"; var name = itemId.ToString(); if (!true) if (itemId is 101 or 111) name = "102"; if (itemId is 102 or 106) name += "_normal"; GetNTexture(itemIconPath, name, action); } public static IEnumerator GetGalleryFromNet(int imageID, Action action = null) { if (imageID == 0) { action?.Invoke(true); } else { var picInfoUrl = $"{LoginKit.Instance.LoginModel.CdnURL}gallery/{imageID}.jpg"; yield return GetTextureFromNet("Gallery", imageID, picInfoUrl, action); } } public static string GetBankIconUrl(string icon_name) { return string.Format("ui://G002_main/{0}", icon_name); } public static void GetGalleryLocal(int imageID, Action action) { if (!galleryNTexturesDic.TryGetValue(imageID, out var spr)) { var path = $"Gallery/{imageID}"; var asset = Resources.Load($"Atlas/{path}"); if (asset) { } else { CrazyAsyKit.StartCoroutine(GetGalleryFromNet(imageID, isSuccess => { if (isSuccess) CrazyAsyKit.StartCoroutine(GetLocalTexture($"{path}.jpg", texture => { if (texture == null) { action?.Invoke(null); return; } var spr1 = new NTexture(texture); galleryNTexturesDic.TryAdd(imageID, spr1); action?.Invoke(spr1); })); else action?.Invoke(null); })); } } else { action?.Invoke(spr); } } #region 缓存 private static readonly Dictionary nTexturesDic = new(); private static readonly Dictionary avatarNTexturesDic = new(); private static readonly Dictionary galleryNTexturesDic = new(); #endregion #region 加载Texture private static IEnumerator GetLocalTexture(string imagePath, Action action) { var file = $"{Application.persistentDataPath}/{imagePath}"; var fileInfo = new FileInfo(file); if (!fileInfo.Exists) { action?.Invoke(null); yield break; } var avatarPath = $"file://{file}"; var uwr = UnityWebRequestTexture.GetTexture(avatarPath); yield return uwr.SendWebRequest(); if (uwr.result is UnityWebRequest.Result.ConnectionError or UnityWebRequest.Result.ProtocolError) { action?.Invoke(null); yield break; } var texture2D = DownloadHandlerTexture.GetContent(uwr); action?.Invoke(texture2D); } private class DownloadTask { public Action Callback; public string FileName; public string Folder; public GLoader Loader; public string LocalFolder; public bool IsGameBg; } public static string imgUrl = ""; // 普通队列 private static readonly Queue normalQueue = new(); private static Coroutine normalCoroutine; // 优先队列 private static readonly Queue priorityQueue = new(); private static Coroutine priorityCoroutine; // ====================== 并发配置 ====================== private static int maxPriorityConcurrent = 3; // ✅ 可配置最大并发数 private static int priorityRunning = 0; // 当前正在运行的任务数 #region 材质池 private static class MaterialPool { private static readonly Stack blurMats = new(); private static readonly Stack normalMats = new(); public static Material GetBlur() { if (blurMats.Count > 0) return blurMats.Pop(); var shader = Resources.Load("RedHotRoastAssets/Shader/Blur"); if (!shader) return null; return new Material(shader); } public static Material GetNormal() { if (normalMats.Count > 0) return normalMats.Pop(); var shader = Resources.Load("RedHotRoastAssets/Shader/FairyGUI/FairyGUI-Image"); if (!shader) return null; return new Material(shader); } public static void Return(Material mat) { if (!mat) return; var name = mat.shader.name; if (name.Contains("Blur")) blurMats.Push(mat); else normalMats.Push(mat); } public static void Clear() { foreach (var mat in blurMats) Object.Destroy(mat); blurMats.Clear(); foreach (var mat in normalMats) Object.Destroy(mat); normalMats.Clear(); } } #endregion #region 图片加载接口 public static string getResPath() { // 删除旧资源 if (!DataMgr.curResVersion.Value.IsNullOrWhiteSpace() && DataMgr.curResVersion.Value != ConfigSystem.GetCommonConf().ResVersion) { var oldFolder = Path.Combine(Application.persistentDataPath, DataMgr.curResVersion.Value); if (Directory.Exists(oldFolder)) { Directory.Delete(oldFolder, true); } DataMgr.curResVersion.Value = ConfigSystem.GetCommonConf().ResVersion; } else if (DataMgr.curResVersion.Value.IsNullOrWhiteSpace()) { DataMgr.curResVersion.Value = ConfigSystem.GetCommonConf().ResVersion; } if (ConfigSystem.GetCommonConf().ResVersion.IsNullOrWhiteSpace()) { Debug.LogError("获取资源路径失败"); return Application.persistentDataPath; } string curFolder = Path.Combine(Application.persistentDataPath, ConfigSystem.GetCommonConf().ResVersion); return curFolder; } public static void SetImgLoader(GLoader loader, string fileName, Action callback, string folder, string localFolder, bool priority = true, bool isLoad = false, bool isGameBg = false) { var localPath = Path.Combine(getResPath(), localFolder, fileName + ".jpg"); if (File.Exists(localPath)) { Debug.Log($"[SetImgLoader] 本地存在,直接加载 {fileName}"); CrazyAsyKit.StartCoroutine(LoadTexture(fileName, loader, callback, localFolder, isGameBg)); return; } if (isLoad) { Debug.Log($"[SetImgLoader] 兼容老逻辑,直接加载 {fileName}"); return; } var task = new DownloadTask { Loader = loader, FileName = fileName, Folder = folder, Callback = callback, LocalFolder = localFolder, IsGameBg = isGameBg }; if (priority) EnqueuePriorityTask(task, isBatch: false); // 👈 单任务标记 else EnqueueNormalTask(loader, fileName, callback, folder, localFolder); } /// /// 批量加载任务(优先队列,串行) /// public static void SetImgLoaders( List<(GLoader loader, string fileName, Action callback, string folder, string localFolder)> tasks) { Debug.Log($"[BatchSetImgLoader] 收到 {tasks.Count} 个任务"); // 清空旧任务(批量任务才清空) if (priorityCoroutine != null) { CrazyAsyKit.StopCoroutine(priorityCoroutine); priorityCoroutine = null; } priorityQueue.Clear(); foreach (var task in tasks) { var downloadTask = new DownloadTask { Loader = task.loader, FileName = task.fileName, Folder = task.folder, Callback = task.callback, LocalFolder = task.localFolder, IsGameBg = false }; EnqueuePriorityTask(downloadTask, isBatch: true); // 👈 批量标记 } if (priorityCoroutine == null) priorityCoroutine = CrazyAsyKit.StartCoroutine(ProcessPriorityQueue()); } private static void EnqueuePriorityTask(DownloadTask task, bool isBatch) { // 去重:如果已在队列中就跳过 foreach (var queued in priorityQueue) { if (queued.FileName == task.FileName) { Debug.Log($"[PriorityQueue] 跳过重复任务 {task.FileName}"); return; } } if (isBatch) Debug.Log($"[PriorityQueue] 批量添加任务 {task.FileName}"); else Debug.Log($"[PriorityQueue] 单任务添加 {task.FileName}"); priorityQueue.Enqueue(task); if (priorityCoroutine == null) priorityCoroutine = CrazyAsyKit.StartCoroutine(ProcessPriorityQueue()); } #endregion #region 普通队列逻辑 private static void EnqueueNormalTask(GLoader loader, string fileName, Action callback, string folder, string localFolder) { normalQueue.Enqueue(new DownloadTask { Loader = loader, FileName = fileName, Folder = folder, Callback = callback, LocalFolder = localFolder }); if (normalCoroutine == null) normalCoroutine = CrazyAsyKit.StartCoroutine(ProcessNormalQueue()); } private static IEnumerator ProcessNormalQueue() { while (normalQueue.Count > 0) { var task = normalQueue.Dequeue(); yield return DownloadCoroutine(task); } normalCoroutine = null; } private static IEnumerator ProcessPriorityQueue() { while (priorityQueue.Count > 0 || priorityRunning > 0) { // 如果没达到最大并发,就从队列里拉一个任务出来 while (priorityRunning < maxPriorityConcurrent && priorityQueue.Count > 0) { var task = priorityQueue.Dequeue(); priorityRunning++; Debug.Log($"[PriorityQueue] 开始下载 {task.FileName}"); // 启动一个子协程跑下载 CrazyAsyKit.StartCoroutine(RunPriorityTask(task)); } yield return null; // 每帧检查一次 } priorityCoroutine = null; Debug.Log("[PriorityQueue] 批量任务完成"); } private static IEnumerator RunPriorityTask(DownloadTask task) { yield return DownloadCoroutine(task); priorityRunning--; Debug.Log($"[PriorityQueue] 完成 {task.FileName}"); } private static IEnumerator DownloadCoroutine(DownloadTask task) { var localPath = Path.Combine(getResPath(), task.LocalFolder, task.FileName + ".jpg"); var tempPath = localPath + ".tmp"; var url = imgUrl + task.Folder + task.FileName + ".jpg"; Debug.Log($"[Download] 下载 tempPath=0== {tempPath} \n url==={url}"); if (File.Exists(localPath)) { Debug.Log($"[SetImgLoader] 本地存在,直接加载 {task.FileName}"); yield return LoadTexture(task.FileName, task.Loader, task.Callback, task.LocalFolder, task.IsGameBg); yield break; } // ⚠️ 确保遗留的临时文件被清理掉 Debug.Log($"[Download] tempPath=1== {tempPath}"); if (File.Exists(tempPath)) { Debug.Log($"[Download] 删除遗留临时文件 {tempPath}"); File.Delete(tempPath); } // 确保目录存在 string tempDir = Path.GetDirectoryName(tempPath); if (!Directory.Exists(tempDir)) { Directory.CreateDirectory(tempDir); } var attempt = 0; while (attempt < 3) { Debug.Log($"[Download] 正在下载 {task.FileName} 次数:{attempt}"); attempt++; using (var www = UnityWebRequest.Get(url)) { yield return www.SendWebRequest(); if (www.result == UnityWebRequest.Result.Success) { try { Debug.Log($"[Download] 下载成功,保存临时文件 {tempPath}"); File.WriteAllBytes(tempPath, www.downloadHandler.data); if (File.Exists(localPath)) File.Delete(localPath); File.Move(tempPath, localPath); // 解密文件 // Debug.Log($"[Download] 开始解密文件 {task.FileName}"); // Rescrypt.DecryptFile(tempPath, localPath, ConfigManager.GameConfig.packageName); // Debug.Log($"[Download] 解密完成,保存路径:{localPath}"); } catch (Exception e) { Debug.LogError($"[Download] 解密或保存失败:{e}"); yield break; } if (task.Loader != null) yield return LoadTexture(task.FileName, task.Loader, task.Callback, task.LocalFolder, task.IsGameBg); yield break; } yield return new WaitForSeconds(1f); } } task.Callback?.Invoke(null); } // 用字典记录每个 loader 的当前请求 private static readonly Dictionary loaderRequests = new(); public static IEnumerator LoadTexture(string fileName, GLoader loader, Action callback = null, string folder = "", bool isGameBg = false) { string encryptedPath = Path.Combine(getResPath(), folder, fileName + ".jpg"); if (!File.Exists(encryptedPath)) { Debug.LogWarning($"[LoadEncryptedTexture] 加密文件不存在: {encryptedPath}"); callback?.Invoke(null); yield break; } Debug.Log($"[LoadTexture] 开始加载本地图片 {fileName}"); // 如果这个 loader 已经有旧请求,先取消掉 if (loader != null && loaderRequests.TryGetValue(loader, out var oldReq)) { if (!oldReq.isDone) { Debug.Log($"[LoadTexture] 取消上一次未完成的加载 {fileName}"); oldReq.Abort(); } loaderRequests.Remove(loader); } using (var www = UnityWebRequestTexture.GetTexture("file://" + GetDecryptedImagePath(fileName, folder))) { // 记录当前请求 if (loader != null) loaderRequests[loader] = www; yield return www.SendWebRequest(); // 如果这个 loader 的请求已经被替换,就直接丢弃结果 if (loader != null && (!loaderRequests.ContainsKey(loader) || loaderRequests[loader] != www)) { Debug.Log($"[LoadTexture] 请求已被新任务替换,丢弃结果 {fileName}"); yield break; } if (loader != null) loaderRequests.Remove(loader); if (www.result == UnityWebRequest.Result.Success) { var tex = DownloadHandlerTexture.GetContent(www); var nTex = new NTexture(tex) { destroyMethod = DestroyMethod.Destroy }; tex.name = fileName; // 清理旧纹理 if (loader != null && loader.texture != null) { loader.texture.Dispose(); if (loader.texture.nativeTexture != null) { UnityEngine.Object.Destroy(loader.texture.nativeTexture); } loader.texture = null; } if (loader != null && !loader.isDisposed) { loader.texture = nTex; Debug.Log($"[LoadTexture] 图片已设置到 Loader {fileName}"); callback?.Invoke(nTex); } else { Debug.Log($"[LoadTexture] Loader 不存在或已销毁 {fileName}"); } if (isGameBg) { callback?.Invoke(nTex); } } else { Debug.LogError($"[LoadTexture] 加载失败 {www.error}"); callback?.Invoke(null); } } if (loader != null && !loader.isDisposed) loader.visible = true; } public static string GetDecryptedImagePath(string fileName, string localFolder) { string tempPath = Path.Combine(Application.temporaryCachePath, FolderNames.GameCache, localFolder, fileName + "_temp.jpg"); // Debug.Log($"获取解密路径: {tempPath}"); if (!File.Exists(tempPath)) { Debug.Log($"!!!!!!!!!!!!临时文件不存在: {fileName}"); // 确保目录存在 Directory.CreateDirectory(Path.GetDirectoryName(tempPath)); string encPath = Path.Combine(getResPath(), localFolder, fileName + ".jpg"); Debug.Log(encPath); if (!File.Exists(encPath)) return null; Debug.Log($"!!!!!!!!!!!!写入一张: {fileName}"); byte[] decryptedBytes = Rescrypt.DecryptFileToBytes(encPath); File.WriteAllBytes(tempPath, decryptedBytes); } return tempPath; } public static IEnumerator WriteTempBeforeOpenCoroutine_() { System.Diagnostics.Stopwatch stopwatch = System.Diagnostics.Stopwatch.StartNew(); var levelUnlocks = ConfigSystem.GetLevelUnlockConfig(); var FreeImageLibrary_ = ConfigSystem.GetFreeImageConfig(); var ADImageLibrary_ = ConfigSystem.GetADImageConfig(); var SpecialImageLibrary_ = ConfigSystem.GetSpecialImageConfig(); var VIPImageLibrary_ = ConfigSystem.GetVIPImageConfig(); for (int i = 0; i < GameHelper.GetCommonModel().MultiModal - 1; i++) { GetDecryptedImagePath(levelUnlocks[i].Name, FolderNames.AlbumName); yield return null; } var LevelUnlockListNew = DataMgr.LevelUnlockListNew.Value; for (int i = 0; i // { // if (success) // { // GameHelper.ShowTips("save_successed", true); // onSuccess?.Invoke(); // } // else // { // Debug.LogWarning("保存到相册失败"); // } // // // 可选:清理临时文件 // if (File.Exists(tempPath)) // File.Delete(tempPath); // }); } catch (Exception ex) { Debug.LogError($"保存加密图片到相册失败: {ex.Message}"); } } public static void SaveVideoToAlbum(string fileName, Action onSuccess = null) { try { string localPath = Path.Combine(getResPath(), FolderNames.VideoName, fileName + ".mp4"); BrigdeIOS.SaveVideoWithCustomDate(localPath); // File.SetCreationTime(localPath, DateTime.Now); // File.SetCreationTimeUtc(localPath, DateTime.Now); // File.SetLastWriteTime(localPath, DateTime.Now); // File.SetLastWriteTimeUtc(localPath, DateTime.Now); // // CrazyAsyKit.StartCoroutine(GetSaveVideo(fileName, (isSuccess) => // // { // // // })); // NativeGallery.SaveVideoToGallery(localPath, "MyAlbum", "my_video.mp4", (bool success, string path) => // { // if (success) // { // GameHelper.ShowTips("save_successed", true); // onSuccess?.Invoke(); // } // }); } catch (Exception ex) { Debug.LogError($"保存加密视频到相册失败: {ex.Message}"); } } public static void SetVideoLoader(VideoPlayer player, GLoader loader, string fileName, Action action = null, bool play = true) { VideoLoadScheduler.EnqueueLoad(player, fileName, loader, videoPlayer => { Debug.Log("RendererList------视频加载成功!-" + fileName); Debug.Log("[绑定播放器 EnqueueLoad]=========1=== "); if (videoPlayer != null) { Debug.Log("视频加载成功!"); action?.Invoke(videoPlayer); } else { Debug.Log("视频加载失败!"); VideoPlayerPool.Instance.ReturnPlayer(player); action?.Invoke(null); } }, play); } public static void SetImageMosaic(GLoader loader, int num = 80) { var shader = Resources.Load("RedHotRoastAssets/Shader/Mosaic"); if (shader == null) { Debug.LogError("找不到 Shader:Custom/Mosaic。请确保 shader 名称正确并已包含到构建中。"); return; } var material = new Material(shader); material.SetFloat("_BlockSize", num); // 假设你 shader 中的 blockSize 参数是这个 loader.material = material; } #region 材质处理 public static void SetImageBlur(GLoader loader) { if (loader == null || loader.isDisposed || loader.texture == null || loader.material.name == "Custom/Blur") return; if (loader.material != null) MaterialPool.Return(loader.material); loader.material = MaterialPool.GetBlur(); } public static void CancelImageBlur(GLoader loader) { if (loader == null || loader.isDisposed || loader.texture == null || loader.material == null || loader.material.name != "Custom/Blur") return; if (loader.material != null) MaterialPool.Return(loader.material); loader.material = MaterialPool.GetNormal(); } #endregion /// /// 彻底清理材质池(切场景/退出游戏时调用) /// public static void ClearMaterialPool() { MaterialPool.Clear(); } private static Sprite _currentBackgroundSprite; private static SpriteRenderer _spriteRenderer; private static NTexture _currentNTexture; // 新增:缓存 NTexture public static void setGamebg(string fileName) { // 只获取一次组件 if (_spriteRenderer == null) { var go = GameObject.Find("game_bg"); if (go == null) { Debug.LogError("找不到名为 'game_bg' 的 GameObject!"); return; } _spriteRenderer = go.GetComponent(); if (_spriteRenderer == null) { Debug.LogError("'game_bg' 上没有找到 SpriteRenderer 组件!"); return; } } if (_spriteRenderer && _spriteRenderer.sprite && _spriteRenderer.sprite.texture.name == fileName) { return; } // 调用统一的 SetImgLoader,不需要再自己写下载逻辑 SetImgLoader(null, fileName, nTex => { if (nTex != null && nTex.nativeTexture != null) { Debug.Log("背景图片加载成功!"); var tex2D = nTex.nativeTexture as Texture2D; if (tex2D == null) { Debug.LogError("NTexture 转换失败!"); return; } tex2D.name = fileName; // 创建 Sprite var sprite = Sprite.Create( tex2D, new Rect(0, 0, tex2D.width, tex2D.height), new Vector2(0.5f, 0.5f) ); _spriteRenderer.sprite = sprite; if (_currentNTexture != null) { _currentNTexture.Dispose(); _currentNTexture = null; } if (_currentBackgroundSprite != null) { Object.Destroy(_currentBackgroundSprite); _currentBackgroundSprite = null; } _currentNTexture = nTex; _currentBackgroundSprite = sprite; var currentWidth = _spriteRenderer.sprite.bounds.size.x; var scaleFactor = 64f / currentWidth; // 等比缩放 _spriteRenderer.transform.localScale = new Vector3(scaleFactor, scaleFactor, 1); } else { Debug.LogError("背景图片加载失败!"); } }, "LevelAlbums/", FolderNames.AlbumName, true, false, true); // ✅ 背景一般是立刻需要的,走优先下载 } public static void GetCardIcon(bool isLogo = false, UnityAction action = null) { var cardIconPath = "logo_default"; // var paymentTypeVo = GameHelper.GetPaymentTypeVO(); // if (isLogo) // { // cardIconPath = paymentTypeVo.logo_id; // } // else // { // cardIconPath = paymentTypeVo.card_name; // } GetNTexture("Atlas.Pay", cardIconPath, action); } private static IEnumerator GetTextureFromNet(string type, int imgId, string imgUrl, Action action = null) { var imagePath = $"{CommonHelper.GetAppSavePath()}/{type}/{imgId}.jpg"; var fileInfo = new FileInfo(imagePath); if (fileInfo.Exists) { action?.Invoke(true); yield break; } var avatarRequest = UnityWebRequest.Get(imgUrl); yield return avatarRequest.SendWebRequest(); if (avatarRequest.result == UnityWebRequest.Result.ConnectionError || avatarRequest.result == UnityWebRequest.Result.ProtocolError) { action?.Invoke(false); } else { var avatarData = avatarRequest.downloadHandler.data; var dirPath = Path.GetDirectoryName(imagePath); if (!Directory.Exists(dirPath)) Directory.CreateDirectory(dirPath); if (fileInfo.Exists) yield break; Stream stream = fileInfo.Create(); stream.Write(avatarData, 0, avatarData.Length); stream.Close(); stream.Dispose(); action?.Invoke(true); } } #endregion #region 头像相关 public static void SetAvatarToLoader(int avatarId, GLoader _GLoader, bool IsNeedDefAvatar = true) { if (!avatarNTexturesDic.TryGetValue(avatarId, out var spr)) try { Sprite sprite = null; if (avatarId == 0) { if (!PlayerPrefsKit.ReadBool("IsLogin")) { avatarId = 1; SetAvatarToLoader(avatarId, _GLoader); return; } if (PlayerPrefsKit.ReadString("AvatarUrl").IsNullOrWhiteSpace()) { _GLoader.url = "ui://pmf3wbjicxrg4"; return; } GetSelfFaceBookAvatar(texture => { if (_GLoader != null) { if (texture != null) _GLoader.texture = texture; else _GLoader.url = "ui://pmf3wbjicxrg4"; } }); return; } if (avatarId > 100) { if (IsNeedDefAvatar) _GLoader.url = "ui://pmf3wbjicxrg4"; GetFaceBookAvatar(avatarId, e => { if (_GLoader != null) { if (e == null) _GLoader.url = "ui://pmf3wbjicxrg4"; else _GLoader.texture = e; } }); return; } LoadKit.Instance.LoadSprite("Atlas.Avatar", avatarId.ToString(), spr => { sprite = spr; var spr1 = new NTexture(sprite); _GLoader.texture = spr1; avatarNTexturesDic.Add(avatarId, spr1); }); } catch (Exception e) { Debug.LogError(e); } else _GLoader.texture = spr; } private static IEnumerator GetAvatarLocal(int avatarId, Action action) { yield return GetLocalTexture($"Avatar/{avatarId}.jpg", action); } #endregion #region FaceBook头像相关 public static void GetFaceBookAvatar(int avatarId, Action action) { if (!avatarNTexturesDic.TryGetValue(avatarId, out var spr)) HallManager.Instance.StartCoroutine(GetAvatarLocal(avatarId, texture => { if (texture == null) { action?.Invoke(null); return; } spr = new NTexture(texture); if (avatarNTexturesDic.ContainsKey(avatarId)) avatarNTexturesDic.Add(avatarId, spr); action?.Invoke(spr); })); else action?.Invoke(spr); } public static void GetSelfFaceBookAvatar(Action action) { if (!avatarNTexturesDic.TryGetValue(0, out var spr)) { } else { action?.Invoke(spr); } } #endregion }