killapp/Assets/Scripts/Base/NetworkCtrl.cs
“虞渠成” 471f4c8962 init
2025-11-18 09:18:48 +08:00

340 lines
12 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.Networking;
namespace Kill.Base
{
public class NetworkCtrl : MonoBehaviour
{
public static NetworkCtrl Instance;
public string localPath;
public string streamingPath;
void Awake()
{
Instance = this;
localPath = Application.persistentDataPath;
streamingPath = Application.streamingAssetsPath;
}
async Task Start()
{
nowResourceUrl = resourceUrls[nowResourceIndex];
#if UNITY_EDITOR_WIN
platform = "Windows";
#elif UNITY_EDITOR_OSX
platform = "Mac";
#elif UNITY_ANDROID
platform = "Android";
#elif UNITY_IOS
platform = "iOS";
#endif
await Init();
}
public async Task Init()
{
Debug.Log("初始化网络控制");
GetNowBundleList();
await GetBundleList();
await UpdateResources();
await LoadRes.Instance.Init();
}
#region
public string[] resourceUrls;
public int nowResourceIndex;
string nowResourceUrl;
public string resourceVersion;
string platform;
public AssetBundleInfoList nowBundleList;
public AssetBundleInfoList newBundleList;
public void GetNowBundleList()
{
if (File.Exists(localPath + "/list.json"))
{
nowBundleList = JsonUtility.FromJson<AssetBundleInfoList>(File.ReadAllText(localPath + "/list.json"));
}
else if (File.Exists(streamingPath + "/list.json"))
{
nowBundleList = JsonUtility.FromJson<AssetBundleInfoList>(File.ReadAllText(streamingPath + "/list.json"));
}
else
{
nowBundleList = null;
}
}
public async Task GetBundleList()
{
string url = nowResourceUrl + $"{resourceVersion}/{platform}/list.json";
using (UnityWebRequest webRequest = UnityWebRequest.Get(url))
{
var operation = webRequest.SendWebRequest();
while (!operation.isDone)
{
await Task.Yield();
}
if (webRequest.result == UnityWebRequest.Result.Success)
{
AssetBundleInfoList list = JsonUtility.FromJson<AssetBundleInfoList>(webRequest.downloadHandler.text);
newBundleList = list;
}
else
{
Debug.LogError(url + "获取资源列表失败" + webRequest.error);
ErrorTip.Instance.Init("获取资源列表失败", url, async (msg) =>
{
await GetBundleList();
});
}
}
}
public async Task UpdateResources()
{
// 获取更新列表
List<AssetBundleInfo> updateList = GetUpdateList();
if (updateList.Count > 0)
{
// 开始下载更新
bool success = await DownloadUpdateFiles(updateList, (downloaded, total) =>
{
float progress = (float)downloaded / total;
Debug.Log($"更新进度: {progress:P}");
});
if (success)
{
Debug.Log("资源更新完成");
}
else
{
Debug.LogError("资源更新失败");
}
}
else
{
Debug.Log("资源已是最新版本");
}
}
/// <summary>
/// 对比当前资源列表和新资源列表,找出需要更新的文件
/// </summary>
/// <returns>需要更新的资源信息列表</returns>
public List<AssetBundleInfo> GetUpdateList()
{
List<AssetBundleInfo> updateList = new List<AssetBundleInfo>();
// 如果没有当前列表,则全部都需要下载
if (nowBundleList == null || nowBundleList.assetbundles == null)
{
return newBundleList.assetbundles ?? new List<AssetBundleInfo>();
}
// 创建当前资源字典以便快速查找
Dictionary<string, AssetBundleInfo> nowBundleDict = new Dictionary<string, AssetBundleInfo>();
foreach (var bundle in nowBundleList.assetbundles)
{
nowBundleDict[bundle.name] = bundle;
}
// 对比新资源列表
foreach (var newBundle in newBundleList.assetbundles)
{
// 检查文件是否存在或MD5是否不匹配
if (!nowBundleDict.ContainsKey(newBundle.name) ||
nowBundleDict[newBundle.name].md5 != newBundle.md5)
{
updateList.Add(newBundle);
}
}
return updateList;
}
/// <summary>
/// 计算需要更新的总大小
/// </summary>
/// <param name="updateList">需要更新的资源列表</param>
/// <returns>总大小(字节)</returns>
public long CalculateUpdateSize(List<AssetBundleInfo> updateList)
{
long totalSize = 0;
foreach (var bundle in updateList)
{
totalSize += bundle.size;
}
return totalSize;
}
/// <summary>
/// 下载更新文件并实时返回进度
/// </summary>
/// <param name="updateList">需要更新的资源列表</param>
/// <param name="progressCallback">进度回调(已下载字节数, 总字节数)</param>
/// <returns>是否下载成功</returns>
public async Task<bool> DownloadUpdateFiles(List<AssetBundleInfo> updateList, Action<long, long> progressCallback)
{
long totalSize = CalculateUpdateSize(updateList);
long downloadedSize = 0;
// 初始化 nowBundleList如果为空
if (nowBundleList == null || nowBundleList.assetbundles == null)
{
nowBundleList = new AssetBundleInfoList(new List<AssetBundleInfo>());
}
// 创建当前资源字典以便快速查找和更新
Dictionary<string, AssetBundleInfo> nowBundleDict = new Dictionary<string, AssetBundleInfo>();
foreach (var bundle in nowBundleList.assetbundles)
{
nowBundleDict[bundle.name] = bundle;
}
foreach (var bundle in updateList)
{
string url = nowResourceUrl + $"{resourceVersion}/{platform}/{bundle.name}";
string savePath = Path.Combine(localPath, bundle.name);
// 确保目录存在
string directory = Path.GetDirectoryName(savePath);
if (!Directory.Exists(directory))
{
Directory.CreateDirectory(directory);
}
bool downloadSuccess = await DownloadSingleFile(url, savePath, bundle.md5);
if (!downloadSuccess)
{
Debug.LogError($"下载文件失败: {bundle.name}");
return false;
}
// 下载成功后,更新 nowBundleList
nowBundleDict[bundle.name] = bundle;
downloadedSize += bundle.size;
progressCallback?.Invoke(downloadedSize, totalSize);
// 简短延迟避免请求过于频繁
await Task.Yield();
}
// 更新完整的 nowBundleList
nowBundleList.assetbundles = new List<AssetBundleInfo>(nowBundleDict.Values);
// 下载完成后保存新的list.json
SaveNewBundleList();
return true;
}
/// <summary>
/// 下载单个文件并校验MD5
/// </summary>
/// <param name="url">文件URL</param>
/// <param name="savePath">保存路径</param>
/// <param name="expectedMd5">期望的MD5值</param>
/// <returns>是否下载并校验成功</returns>
private async Task<bool> DownloadSingleFile(string url, string savePath, string expectedMd5)
{
int maxRetries = 2;
for (int i = 0; i <= maxRetries; i++)
{
using (UnityWebRequest webRequest = UnityWebRequest.Get(url))
{
var operation = webRequest.SendWebRequest();
while (!operation.isDone)
{
await Task.Yield();
}
if (webRequest.result == UnityWebRequest.Result.Success)
{
// 保存文件
File.WriteAllBytes(savePath, webRequest.downloadHandler.data);
// 校验MD5
string actualMd5 = CalculateMD5(savePath);
if (actualMd5.ToLower() == expectedMd5.ToLower())
{
return true;
}
else
{
Debug.LogError($"MD5校验失败 {url}: 期望 {expectedMd5}, 实际 {actualMd5}");
// MD5校验失败时删除文件
if (File.Exists(savePath))
{
File.Delete(savePath);
}
}
}
else
{
Debug.LogError($"下载失败 {url}: {webRequest.error} (尝试 {i + 1}/{maxRetries + 1})");
}
// 如果还没达到最大重试次数,等待一段时间后重试
if (i < maxRetries)
{
await Task.Delay(1000 * (i + 1)); // 递增延迟时间
}
}
}
// 所有重试都失败后显示错误提示
ErrorTip.Instance.Init("下载资源包失败", url, async (msg) =>
{
// 错误处理回调 - 重新尝试下载更新资源流程
Debug.Log("用户选择重试下载资源包");
await UpdateResources();
});
return false;
}
/// <summary>
/// 计算文件的MD5值
/// </summary>
/// <param name="filePath">文件路径</param>
/// <returns>MD5值</returns>
private string CalculateMD5(string filePath)
{
using (var md5 = System.Security.Cryptography.MD5.Create())
{
using (var stream = File.OpenRead(filePath))
{
byte[] hash = md5.ComputeHash(stream);
return BitConverter.ToString(hash).Replace("-", "").ToLower();
}
}
}
/// <summary>
/// 保存新的资源列表到本地
/// </summary>
public void SaveNewBundleList()
{
if (newBundleList != null)
{
string json = JsonUtility.ToJson(newBundleList, true);
string savePath = Path.Combine(localPath, "list.json");
File.WriteAllText(savePath, json);
Debug.Log("新的资源列表已保存到本地");
}
}
#endregion
#region
#endregion
}
}