2462 lines
94 KiB
C#
2462 lines
94 KiB
C#
//---加载模块---
|
||
//流程:本地加载(编辑器)=》基础系统ab加载(base)=》xid ab加载(xid)
|
||
//xid--DownloadServer--local
|
||
//XWorld资源(game.xworld.ren/60s/)和xid资源的路径问题
|
||
//回调加载
|
||
//LoadScene(Path, function OnFinish(gameObj){...})
|
||
//协程后台加载
|
||
// coLoadScene("res/scene/10011/scene_10011")
|
||
//GameObj obj = coLoadPrefab("res/model/actor/10101")
|
||
|
||
//Webgl小游戏不允许阻塞式加载,只能协程加载 :全部LoadFromCacheOrDownload直接加载资源?
|
||
//AddScene 和 LoadRes 1、Editor读本地资源;2、读更新目录资源;3、WebGL动态下载对应资源;4、app版(pc,安卓,ios)读取包内文件
|
||
|
||
//场景加载切换:StartCoroutine(Loading(ScenePath));在中间场景(LoadingScene永远存在)中过度(loading过程),删掉前一个场景(DelScene), 后台添加场景(AddScene),()
|
||
|
||
using System;
|
||
using UnityEngine;
|
||
using UnityEngine.Networking;
|
||
using XLua;
|
||
using UObject = UnityEngine.Object;
|
||
using System.IO;
|
||
using System.Collections;
|
||
using System.Collections.Generic;
|
||
using UnityEditor;
|
||
using UnityEngine.SceneManagement;
|
||
|
||
|
||
namespace XGame
|
||
{
|
||
public class AssetBundleInfo
|
||
{
|
||
public AssetBundle m_AssetBundle;
|
||
public int m_ReferencedCount;
|
||
|
||
public AssetBundleInfo(AssetBundle assetBundle)
|
||
{
|
||
m_AssetBundle = assetBundle;
|
||
m_ReferencedCount = 1;//默认创建的时候一定是1个引用了,减到0就是要删除
|
||
}
|
||
}
|
||
[LuaCallCSharp]
|
||
public static class XResLoader
|
||
{
|
||
class LoadAssetRequest
|
||
{
|
||
public Type assetType;
|
||
public string[] assetNames;
|
||
//public LuaFunction luaFunc;
|
||
public Action<UObject[]> sharpFunc;
|
||
public string orginPath;
|
||
}
|
||
|
||
static XWCoroutine m_Base;
|
||
public static string m_Postfix = ".unity3d";
|
||
#if UNITY_EDITOR
|
||
public static int m_ABMode = 0;//0 self res ; 1 ab res
|
||
#else
|
||
public static int m_ABMode = 1;//0 self res ; 1 ab res
|
||
#endif
|
||
|
||
static string m_Path;
|
||
static int m_UpdatePathLength = 0;
|
||
//static int m_XidResMidPathLength = 0;
|
||
|
||
//static string[] m_AllManifest = null;
|
||
static AssetBundleManifest m_AssetBundleManifest = null;
|
||
//static AssetBundleManifest m_tempManifest = null;
|
||
|
||
static UObject[] m_CurLoadedObjs;
|
||
|
||
//static AssetBundleManifest m_ABManifest3D = null;
|
||
|
||
//
|
||
//static Dictionary<string, string> m_dicFilelist = new Dictionary<string, string>();
|
||
static Dictionary<string, string> m_dicXWorldFilelist = new Dictionary<string, string>();
|
||
static Dictionary<string, string> m_dicTempFilelist = new Dictionary<string, string>();
|
||
|
||
static Dictionary<string, string> m_dicXidCDN = new Dictionary<string, string>();
|
||
static Dictionary<string, string> m_dicXidServer = new Dictionary<string, string>();
|
||
static Dictionary<string, Dictionary<string, string>> m_dicXidFileList = new Dictionary<string, Dictionary<string, string>>();
|
||
static Dictionary<string, AssetBundleManifest> m_dicXidManaifest = new Dictionary<string, AssetBundleManifest>();
|
||
static string m_sCurXid = "XWorld";
|
||
//static string m_sCurXidServerPath = "http://127.0.0.1:15081";
|
||
//Scene
|
||
static Dictionary<string, int> m_SceneLoaded = new Dictionary<string, int>();
|
||
|
||
static AsyncOperation m_Async;
|
||
public static int m_displayProgress = 0;
|
||
static int m_toProgress = 0;
|
||
|
||
static string m_dllMd5, m_filelistMd5;
|
||
//读取场景的进度,它的取值范围在0 - 1 之间。
|
||
//static int m_progress = 0;
|
||
|
||
//资源hash128
|
||
public static Dictionary<string, Hash128> m_ResHash = new Dictionary<string, Hash128>();
|
||
|
||
/// <summary>
|
||
/// 包依赖关系
|
||
/// </summary>
|
||
static Dictionary<string, string[]> m_Dependencies = new Dictionary<string, string[]>();
|
||
|
||
/// <summary>
|
||
/// 已加载的AB包信息
|
||
/// </summary>
|
||
static Dictionary<string, AssetBundleInfo> m_LoadedAssetBundles = new Dictionary<string, AssetBundleInfo>();
|
||
static HashSet<string> m_LoadingAssetBundles = new HashSet<string>();
|
||
|
||
|
||
/// <summary>
|
||
/// 包加载队列列表
|
||
/// </summary>
|
||
static Dictionary<string, List<LoadAssetRequest>> m_LoadRequests = new Dictionary<string, List<LoadAssetRequest>>();
|
||
static Dictionary<string, int> m_LoadDependenciesReq = new Dictionary<string, int>();
|
||
|
||
/// <summary>
|
||
/// 对象缓存
|
||
/// </summary>
|
||
static Dictionary<string, List<UObject>> objDic = new Dictionary<string, List<UObject>>();
|
||
|
||
static string m_sUpdatePath;
|
||
static string m_sLocalPath;
|
||
static string m_sProductPath;
|
||
static string m_sServerPath;
|
||
|
||
|
||
public delegate void LoadAssetCallback(UnityEngine.GameObject[] resultObjects);//Action<UObject[]> action
|
||
public delegate void LoadFileCallback(byte[] data);
|
||
|
||
public static string GetFileMd5(string xid, string filename)
|
||
{
|
||
if (m_dicXidFileList.ContainsKey(xid) == false)
|
||
return null;
|
||
m_dicXidFileList[xid].TryGetValue(filename, out string md5);
|
||
return md5;
|
||
}
|
||
public static void SetXIDCDN(string xid, string cdn)
|
||
{
|
||
m_dicXidCDN[xid] = NormalizeDownloadServer(cdn, GlobalData.CurCDN);
|
||
}
|
||
public static IEnumerator Init()
|
||
{
|
||
//获得当前的Game
|
||
GameObject game = GameObject.Find("Main");
|
||
if (game == null)
|
||
{
|
||
game = new GameObject("Main");
|
||
}
|
||
m_Base = XWCoroutine.Instance;//game.GetComponent<XWCoroutine>();
|
||
//if (m_Base == null)
|
||
// m_Base = game.AddComponent<XWCoroutine>();
|
||
#if !UNITY_WEBGL
|
||
m_sUpdatePath = Application.persistentDataPath + "/";
|
||
m_sProductPath = Application.dataPath + "/";
|
||
m_sServerPath = AppConst.UpdateServer;
|
||
#else
|
||
m_sUpdatePath = GlobalData.ProductionResBase; //生产 CDN 根(HTTPS,经 Caddy;不再用明文+内部端口 :15081)
|
||
m_sProductPath = m_sUpdatePath;//Application.streamingAssetsPath + "/";
|
||
|
||
#if UNITY_EDITOR
|
||
m_sServerPath = GlobalData.ProductionResBase;
|
||
#else
|
||
m_sServerPath = GlobalData.ProductionResBase;
|
||
#endif
|
||
|
||
#endif
|
||
|
||
m_sLocalPath = Application.streamingAssetsPath + "/";//
|
||
m_UpdatePathLength = m_sUpdatePath.Length;
|
||
GlobalData.CurCDN = NormalizeDownloadServer(GlobalData.CurCDN, "http://127.0.0.1:15081/");
|
||
Debug.Log($"UpdatePath = {m_sUpdatePath} (cdn:{GlobalData.CurCDN})");
|
||
|
||
//XWorld的服务器下载路径
|
||
//大厅资源 base:编辑器本地(CurCDN),其它 app 端生产;XWorld/<platform>/ 由 coLoadXIDFile/LoadManifest 路径拼
|
||
m_dicXidCDN["XWorld"] = GlobalData.bEditor ? GlobalData.CurCDN : GlobalData.ProductionResBase;
|
||
//基础资源editor 也资源用ab加载
|
||
yield return LoadManifest("XWorld");
|
||
|
||
|
||
//加载shader
|
||
|
||
//加载TextMeshPro设置
|
||
//LoadTMPSettings();
|
||
yield break;
|
||
}
|
||
|
||
static IEnumerator coLoadXIDFile(string xid, string filepath, Action<byte[]> action)
|
||
{
|
||
string cdn;
|
||
if (!m_dicXidCDN.TryGetValue(xid, out cdn))
|
||
{
|
||
action(null);
|
||
yield break;
|
||
}
|
||
if(m_dicXidFileList.ContainsKey(xid) == false)
|
||
{
|
||
action(null);
|
||
yield break;
|
||
}
|
||
string md5 = m_dicXidFileList[xid][filepath];
|
||
string path = $"{xid}/{GlobalData.CurPlatform}/{GetFileMd5Name(filepath, md5)}";
|
||
byte[] data = null;
|
||
yield return GetFileData(cdn, path, (uwr) =>
|
||
{
|
||
if (uwr != null)
|
||
{
|
||
data = uwr.downloadHandler.data;
|
||
}
|
||
});
|
||
action(data);
|
||
}
|
||
public static IEnumerator coLoadFile(string path, Action<byte[]> action)
|
||
{
|
||
byte[] ret = null;
|
||
yield return coLoadXIDFile("XWorld", path, (datafile) =>
|
||
{
|
||
ret = datafile;
|
||
});
|
||
if (ret != null)
|
||
{
|
||
action(ret);
|
||
yield break;
|
||
}
|
||
yield return coLoadXIDFile(m_sCurXid, path, (datafile) =>
|
||
{
|
||
ret = datafile;
|
||
});
|
||
if (ret != null)
|
||
{
|
||
action(ret);
|
||
yield break;
|
||
}
|
||
action(null);
|
||
}
|
||
public static IEnumerator GetFileDataOrigin(string xid, string path, Action<UnityWebRequest> onFinish)
|
||
{
|
||
string cdn;
|
||
if (!m_dicXidCDN.TryGetValue(xid, out cdn))
|
||
{
|
||
Debug.LogError($"No XID({m_sCurXid}) CDN : ");
|
||
yield break;
|
||
}
|
||
string url = $"{cdn}{path}";
|
||
string md5 = GetFileMd5(xid, path);
|
||
string finalPath;
|
||
if (xid.Equals("XWorld", StringComparison.OrdinalIgnoreCase))
|
||
finalPath = $"{GlobalData.CurPlatform}/{GetFileMd5Name(path, md5)}";
|
||
else
|
||
finalPath = $"{xid}/{GlobalData.CurPlatform}/{GetFileMd5Name(path, md5)}";
|
||
yield return GetFileData(cdn, finalPath, (uwr) =>
|
||
{
|
||
onFinish(uwr);
|
||
});
|
||
}
|
||
static IEnumerator GetFileData(string downloadServer, string path, Action<UnityWebRequest> onFinish)
|
||
{
|
||
string url = $"{downloadServer}{path}";
|
||
|
||
#if UNITY_WEBGL
|
||
using (UnityWebRequest uwr = UnityWebRequest.Get(url))
|
||
{
|
||
yield return uwr.SendWebRequest();
|
||
if (uwr.downloadHandler.isDone && uwr.result == UnityWebRequest.Result.Success)
|
||
{
|
||
onFinish(uwr);
|
||
}
|
||
else
|
||
{
|
||
onFinish(null);
|
||
}
|
||
}
|
||
#else
|
||
string fullpath = $"{m_sUpdatePath}{path}";
|
||
|
||
if (File.Exists(fullpath))
|
||
{
|
||
fullpath = "file://" + fullpath;
|
||
//先加载更新文件
|
||
using (UnityWebRequest uwr = UnityWebRequest.Get(fullpath))
|
||
{
|
||
yield return uwr.SendWebRequest();
|
||
if (uwr.downloadHandler.isDone && uwr.result == UnityWebRequest.Result.Success)
|
||
{
|
||
onFinish(uwr);
|
||
}
|
||
else
|
||
{
|
||
Debug.Log($"GetFileData m_sUpdatePath not exist {fullpath}");
|
||
onFinish(null);
|
||
}
|
||
}
|
||
yield break;
|
||
}
|
||
else
|
||
{ //下载
|
||
bool isLoad = false;
|
||
yield return DownloadFile(downloadServer, path, (uwr) => {
|
||
if (uwr != null)
|
||
{
|
||
onFinish(uwr);
|
||
isLoad = true;
|
||
}
|
||
else
|
||
{
|
||
Debug.Log($"DownloadFile not exist {downloadServer}{path}");
|
||
}
|
||
});
|
||
if (isLoad)
|
||
{
|
||
yield break;
|
||
}
|
||
//加载包内文件
|
||
string localPath = $"file://{Application.streamingAssetsPath}/{path}";
|
||
using (UnityWebRequest request = UnityWebRequest.Get(localPath))
|
||
{
|
||
yield return request.SendWebRequest();
|
||
if (request.result == UnityWebRequest.Result.Success)
|
||
{
|
||
isLoad = true;
|
||
onFinish(request);
|
||
}
|
||
}
|
||
}
|
||
#endif
|
||
}
|
||
static IEnumerator GetAssetsBundleData(string downloadServer, string path, Action<UnityWebRequest, bool> onFinish)
|
||
{
|
||
string url = $"{downloadServer}{path}";
|
||
#if UNITY_EDITOR
|
||
//editor下直接加载本地文件
|
||
string resPath;
|
||
if (!path.StartsWith("Assets/"))
|
||
resPath = $"Assets/{path}";
|
||
else
|
||
resPath = path;
|
||
if (File.Exists(resPath))
|
||
{
|
||
string filepath = "file://" + path;
|
||
using (UnityWebRequest uwr = UnityWebRequestAssetBundle.GetAssetBundle(filepath))
|
||
{
|
||
yield return uwr.SendWebRequest();
|
||
if (uwr.downloadHandler.isDone && uwr.result == UnityWebRequest.Result.Success)
|
||
{
|
||
onFinish(uwr, true);
|
||
yield break;
|
||
}
|
||
}
|
||
|
||
}
|
||
#endif
|
||
|
||
#if UNITY_WEBGL
|
||
using (UnityWebRequest uwr = UnityWebRequestAssetBundle.GetAssetBundle(url))
|
||
{
|
||
yield return uwr.SendWebRequest();
|
||
if (uwr.downloadHandler.isDone && uwr.result == UnityWebRequest.Result.Success)
|
||
{
|
||
onFinish(uwr, true);
|
||
}
|
||
else
|
||
{
|
||
onFinish(null,true);
|
||
}
|
||
}
|
||
#else
|
||
string fullpath = $"{m_sUpdatePath}{path}";
|
||
bool localExists = File.Exists(fullpath);
|
||
if (localExists)
|
||
{
|
||
string localUrl = new Uri(fullpath).AbsoluteUri;
|
||
using (UnityWebRequest uwr = UnityWebRequestAssetBundle.GetAssetBundle(localUrl))
|
||
{
|
||
yield return uwr.SendWebRequest();
|
||
if (uwr.downloadHandler.isDone && uwr.result == UnityWebRequest.Result.Success)
|
||
{
|
||
onFinish(uwr, true);
|
||
}
|
||
else
|
||
{
|
||
Debug.LogWarning($"GetAssetsBundleData local load failed path={path}, localUrl={localUrl}, error={uwr.error}, result={uwr.result}");
|
||
onFinish(null, true);
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{ //下载
|
||
yield return DownloadFile(downloadServer, path, (uwr) => {
|
||
onFinish(uwr, false);
|
||
});
|
||
}
|
||
#endif
|
||
}
|
||
|
||
public static IEnumerator EnterXid(string xid)
|
||
{
|
||
UnloadManifest(m_sCurXid);
|
||
m_sCurXid = xid;
|
||
LoadManifest(xid);
|
||
//string temp = $"Assets/game/"; //$"Assets/{xid}/{GlobalData.CurPlatform}/";
|
||
//m_XidResMidPathLength = 12;//
|
||
yield break;
|
||
}
|
||
//加载md5列表和依赖关系manifest
|
||
static IEnumerator LoadManifest(string xid)
|
||
{
|
||
//m_dicXidFileList m_dicXidManaifest
|
||
string path;
|
||
if (!m_dicXidFileList.ContainsKey(xid))
|
||
{
|
||
string md5 = null;
|
||
//加载 update.ver
|
||
string cdn;
|
||
if (!m_dicXidCDN.TryGetValue(xid, out cdn))
|
||
{
|
||
Debug.LogError($"No XID({m_sCurXid}) CDN : ");
|
||
//cdn = "";
|
||
yield break;
|
||
}
|
||
//统一 <CurCDN>/<xid>/<platform>/...,与 coLoadXIDFile/coLoadXIDAB 一致(XWorld 也走 XWorld/ 子目录)
|
||
path = $"{xid}/{GlobalData.CurPlatform}/updatever.txt?{DateTime.Now.Ticks}";
|
||
yield return DownloadFile(cdn, path, (uwr) =>
|
||
{
|
||
if(uwr != null)
|
||
md5 = uwr.downloadHandler.text;
|
||
});
|
||
if (md5 == null)
|
||
{
|
||
Debug.LogError($"updatever.txt not exist! ({cdn}{path})");
|
||
yield break;
|
||
}
|
||
string [] lines = md5.Split(',');//dllmd5, filelistmd5
|
||
m_dllMd5 = lines[0];
|
||
m_filelistMd5 = lines[1];
|
||
//加载 files.txt_
|
||
path = $"{xid}/{GlobalData.CurPlatform}/{GetFileMd5Name("files.txt", lines[1])}";
|
||
|
||
yield return GetFileData(cdn, path, (uwr) =>
|
||
{
|
||
if (uwr != null)
|
||
{
|
||
string txt = uwr.downloadHandler.text;
|
||
string[] lines = txt.Split('\n');
|
||
int lineCount = lines.Length;
|
||
m_dicTempFilelist = new Dictionary<string, string>();
|
||
for (int i = 0; i < lineCount; ++i)
|
||
{
|
||
string line = lines[i].Trim().Trim('\uFEFF').Trim();
|
||
if (line.Length < 3)
|
||
continue;
|
||
|
||
if (line[0] == '{' && line[line.Length - 1] == '}')
|
||
line = line.Substring(1, line.Length - 2);
|
||
|
||
string[] datas = line.Split(',');
|
||
if (datas.Length < 2)
|
||
{
|
||
Debug.LogWarning($"Invalid files.txt line: {lines[i]}");
|
||
continue;
|
||
}
|
||
string fileName = datas[0].Trim();
|
||
string fileMd5 = datas[1].Trim();
|
||
if (fileName.Length == 0 || fileMd5.Length == 0)
|
||
continue;
|
||
|
||
m_dicTempFilelist[fileName] = fileMd5;
|
||
//Debug.Log($"{datas[0]} => {datas[1]}");
|
||
}
|
||
m_dicXidFileList[xid] = m_dicTempFilelist;
|
||
Debug.Log($"Load {path} files.txt success! ({txt.Length} {m_dicTempFilelist.Count})");
|
||
}
|
||
else
|
||
{
|
||
Debug.LogError($"files.txt not exist! ({cdn}{path})");
|
||
}
|
||
});
|
||
//加载 StreamingAssets_
|
||
|
||
m_dicTempFilelist.TryGetValue("StreamingAssets.unity3d", out md5);
|
||
//md5 = m_dicTempFilelist["StreamingAssets.unity3d"];
|
||
if (md5 == null)
|
||
{
|
||
Debug.LogError($"StreamingAssets not exist! ({cdn}StreamingAssets.unity3d ) ({m_dicTempFilelist.Count})");
|
||
yield break;
|
||
}
|
||
path = $"{xid}/{GlobalData.CurPlatform}/{GetFileMd5Name("StreamingAssets.unity3d", md5)}";
|
||
yield return GetFileData(cdn, path, (uwr) =>
|
||
{
|
||
if (uwr != null)
|
||
{
|
||
byte[] assetData;
|
||
try
|
||
{
|
||
assetData = AESManager.Decrypt(uwr.downloadHandler.data);
|
||
}
|
||
catch (Exception e)
|
||
{
|
||
Debug.LogError($"Decrypt StreamingAssets manifest failed: {path}, {e}");
|
||
return;
|
||
}
|
||
|
||
AssetBundle manAB = AssetBundle.LoadFromMemory(assetData);//AESManager.Decrypt(assetData)
|
||
if (manAB == null)
|
||
{
|
||
Debug.LogError($"Load StreamingAssets manifest AssetBundle failed: {path}");
|
||
return;
|
||
}
|
||
|
||
m_AssetBundleManifest = manAB.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
|
||
m_dicXidManaifest[xid] = m_AssetBundleManifest;
|
||
manAB.Unload(false);
|
||
}
|
||
});
|
||
if(!m_dicXidManaifest.ContainsKey(xid))
|
||
{
|
||
Debug.LogError($"StreamingAssets not exist! ({cdn}/StreamingAssets_)");
|
||
yield break;
|
||
}
|
||
}
|
||
|
||
}
|
||
static void UnloadManifest(string xid)
|
||
{
|
||
m_dicXidFileList[xid] = null;
|
||
m_dicXidManaifest[xid] = null;
|
||
}
|
||
|
||
static IEnumerator DownloadFile(string downloadServer, string fileName, System.Action<UnityWebRequest> onFinish)
|
||
{
|
||
UnityWebRequest uwr;
|
||
downloadServer = NormalizeDownloadServer(downloadServer, GlobalData.CurCDN);
|
||
string url = $"{downloadServer}{fileName}";
|
||
int npos = fileName.LastIndexOf('?');
|
||
if (npos > 0) {
|
||
fileName = fileName.Substring(0, npos);
|
||
}
|
||
Debug.Log($"download ServerList {url}");
|
||
if (!Uri.TryCreate(url, UriKind.Absolute, out Uri uri) ||
|
||
(uri.Scheme != Uri.UriSchemeHttp &&
|
||
uri.Scheme != Uri.UriSchemeHttps &&
|
||
uri.Scheme != Uri.UriSchemeFile))
|
||
{
|
||
Debug.LogError($"Download url invalid: {url}");
|
||
onFinish(null);
|
||
yield break;
|
||
}
|
||
using (uwr = UnityWebRequest.Get(url))
|
||
{
|
||
// uwr.timeout = m_httpRequestTimeout;
|
||
yield return uwr.SendWebRequest();
|
||
if (uwr.downloadHandler.isDone && uwr.result == UnityWebRequest.Result.Success)
|
||
{
|
||
string outfile = m_sUpdatePath + fileName;
|
||
string dir = Path.GetDirectoryName(outfile);
|
||
//创建目录
|
||
Utility.CreateMulDirectory(dir);
|
||
if (File.Exists(outfile))
|
||
File.Delete(outfile);
|
||
if (uwr.downloadHandler.data == null)
|
||
{
|
||
Debug.LogError($"Data = null ({fileName})");
|
||
}
|
||
else
|
||
File.WriteAllBytes(outfile, uwr.downloadHandler.data);
|
||
onFinish(uwr);
|
||
}
|
||
else
|
||
{
|
||
Debug.LogError($"Download Failed {url}");
|
||
onFinish(null);
|
||
//if (RequestErrorCallback != null) { RequestErrorCallback("DownloadFile", uwr.error); }
|
||
}
|
||
}
|
||
}
|
||
|
||
static string NormalizeDownloadServer(string downloadServer, string fallback)
|
||
{
|
||
string server = string.IsNullOrWhiteSpace(downloadServer) ? fallback : downloadServer.Trim();
|
||
if (string.IsNullOrWhiteSpace(server))
|
||
{
|
||
server = "http://127.0.0.1:15081/";
|
||
}
|
||
if (server.StartsWith("http://file://", StringComparison.OrdinalIgnoreCase))
|
||
{
|
||
server = server.Substring("http://".Length);
|
||
}
|
||
else if (server.StartsWith("https://file://", StringComparison.OrdinalIgnoreCase))
|
||
{
|
||
server = server.Substring("https://".Length);
|
||
}
|
||
if (server.StartsWith("file://", StringComparison.OrdinalIgnoreCase))
|
||
{
|
||
server = NormalizeFileUrl(server);
|
||
}
|
||
if (!server.StartsWith("http://", StringComparison.OrdinalIgnoreCase) &&
|
||
!server.StartsWith("https://", StringComparison.OrdinalIgnoreCase) &&
|
||
!server.StartsWith("file://", StringComparison.OrdinalIgnoreCase))
|
||
{
|
||
server = "http://" + server;
|
||
}
|
||
if (!server.EndsWith("/"))
|
||
{
|
||
server += "/";
|
||
}
|
||
return server;
|
||
}
|
||
|
||
static string NormalizeFileUrl(string url)
|
||
{
|
||
string rawPath = url.Substring("file://".Length).Replace('\\', '/');
|
||
while (rawPath.StartsWith("/") && rawPath.Length > 2 && rawPath[2] == ':')
|
||
{
|
||
rawPath = rawPath.Substring(1);
|
||
}
|
||
|
||
string localPath = Uri.UnescapeDataString(rawPath).Replace('/', Path.DirectorySeparatorChar);
|
||
try
|
||
{
|
||
return new Uri(localPath).AbsoluteUri;
|
||
}
|
||
catch
|
||
{
|
||
return "file:///" + rawPath.Replace("#", "%23");
|
||
}
|
||
}
|
||
|
||
|
||
static string GetPathMd5Name(string path)
|
||
{
|
||
string ret, md5;
|
||
if (path.Contains("XWorld/"))
|
||
{//基础资源
|
||
int pos = path.LastIndexOf("/")+1;
|
||
ret = path.Substring(pos);
|
||
if (m_dicXidFileList.ContainsKey("XWorld"))
|
||
{
|
||
if (m_dicXidFileList["XWorld"].ContainsKey(ret))
|
||
{
|
||
md5 = m_dicXidFileList["XWorld"][ret];
|
||
ret = GetFileMd5Name(path, md5);//$"{path}_{md5}";
|
||
}
|
||
else
|
||
{
|
||
string rawRet = GetUnversionedAssetBundleName(ret);
|
||
if (m_dicXidFileList["XWorld"].ContainsKey(rawRet))
|
||
{
|
||
md5 = m_dicXidFileList["XWorld"][rawRet];
|
||
string rawPath = path.Substring(0, path.Length - ret.Length) + rawRet;
|
||
ret = GetFileMd5Name(rawPath, md5);
|
||
}
|
||
else
|
||
{
|
||
Debug.LogWarning($"File not found in XWorld: {ret} {m_dicXidFileList["XWorld"].Count}");
|
||
ret = $"{path}";
|
||
}
|
||
|
||
}
|
||
}
|
||
else
|
||
{
|
||
ret = $"{path}";
|
||
}
|
||
}
|
||
else
|
||
{//xid 资源 m_sCurXid
|
||
int pos = path.LastIndexOf("/")+1;
|
||
ret = path.Substring(pos);
|
||
if(m_dicXidFileList.ContainsKey(m_sCurXid))
|
||
{
|
||
if (m_dicXidFileList[m_sCurXid].ContainsKey(ret))
|
||
{
|
||
md5 = m_dicXidFileList[m_sCurXid][ret];
|
||
ret = GetFileMd5Name(path, md5);//$"{path}_{md5}";
|
||
}
|
||
else
|
||
{
|
||
string rawRet = GetUnversionedAssetBundleName(ret);
|
||
if (m_dicXidFileList[m_sCurXid].ContainsKey(rawRet))
|
||
{
|
||
md5 = m_dicXidFileList[m_sCurXid][rawRet];
|
||
string rawPath = path.Substring(0, path.Length - ret.Length) + rawRet;
|
||
ret = GetFileMd5Name(rawPath, md5);
|
||
}
|
||
else
|
||
{
|
||
Debug.LogWarning($"File not found in {m_sCurXid}: {ret}");
|
||
ret = $"{path}";
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
ret = $"{path}";
|
||
}
|
||
}
|
||
return ret;
|
||
}
|
||
public static string GetFileMd5Name(string filename, string md5)
|
||
{
|
||
if (string.IsNullOrEmpty(md5))
|
||
return filename;
|
||
|
||
int pos = filename.LastIndexOf('.');
|
||
if (pos != -1)
|
||
{
|
||
return $"{filename.Substring(0, pos)}_{md5}{filename.Substring(pos)}";
|
||
}
|
||
else
|
||
return $"{filename}_{md5}";
|
||
}
|
||
|
||
|
||
//1、Editor读本地资源;2、读更新目录资源;3、WebGL动态下载对应资源;4、app版(pc,安卓,ios)读取包内文件
|
||
static string StripXidPlatformPrefix(string path)
|
||
{
|
||
if (string.IsNullOrEmpty(path))
|
||
return path;
|
||
|
||
string normalized = path.Replace("\\", "/");
|
||
string prefix = $"XWorld/{GlobalData.CurPlatform}/";
|
||
if (normalized.StartsWith(prefix, StringComparison.OrdinalIgnoreCase))
|
||
return normalized.Substring(prefix.Length);
|
||
|
||
string curPrefix = $"{m_sCurXid}/{GlobalData.CurPlatform}/";
|
||
if (!string.IsNullOrEmpty(m_sCurXid) && normalized.StartsWith(curPrefix, StringComparison.OrdinalIgnoreCase))
|
||
return normalized.Substring(curPrefix.Length);
|
||
|
||
return normalized;
|
||
}
|
||
|
||
static string GetUnversionedAssetBundleName(string fileName)
|
||
{
|
||
if (string.IsNullOrEmpty(fileName))
|
||
return fileName;
|
||
|
||
string ext = Path.GetExtension(fileName);
|
||
if (!ext.Equals(m_Postfix, StringComparison.OrdinalIgnoreCase))
|
||
return fileName;
|
||
|
||
string withoutExt = fileName.Substring(0, fileName.Length - ext.Length);
|
||
int md5Pos = withoutExt.LastIndexOf('_');
|
||
if (md5Pos <= 0)
|
||
return fileName;
|
||
|
||
string md5 = withoutExt.Substring(md5Pos + 1);
|
||
if (md5.Length != 8)
|
||
return fileName;
|
||
for (int i = 0; i < md5.Length; i++)
|
||
{
|
||
if (!Uri.IsHexDigit(md5[i]))
|
||
return fileName;
|
||
}
|
||
|
||
return withoutExt.Substring(0, md5Pos) + ext;
|
||
}
|
||
|
||
static string ResolveLocalAssetBundlePath(string path)
|
||
{
|
||
if (string.IsNullOrEmpty(path))
|
||
return path;
|
||
|
||
if (Path.IsPathRooted(path) || path.StartsWith(m_sUpdatePath) || path.StartsWith(m_sLocalPath))
|
||
return path;
|
||
|
||
string updatePath = m_sUpdatePath + path;
|
||
if (File.Exists(updatePath))
|
||
return updatePath;
|
||
|
||
string bundleName = StripXidPlatformPrefix(path);
|
||
return m_sLocalPath + GetUnversionedAssetBundleName(bundleName);
|
||
}
|
||
|
||
static string GetAssetBundleCacheKey(string path)
|
||
{
|
||
if (string.IsNullOrEmpty(path))
|
||
return path;
|
||
|
||
string key = path.Replace("\\", "/");
|
||
if (!string.IsNullOrEmpty(m_sUpdatePath) && key.StartsWith(m_sUpdatePath, StringComparison.OrdinalIgnoreCase))
|
||
key = key.Substring(m_sUpdatePath.Length);
|
||
if (!string.IsNullOrEmpty(m_sLocalPath) && key.StartsWith(m_sLocalPath, StringComparison.OrdinalIgnoreCase))
|
||
key = key.Substring(m_sLocalPath.Length);
|
||
|
||
key = StripXidPlatformPrefix(key);
|
||
return GetUnversionedAssetBundleName(key);
|
||
}
|
||
|
||
static bool IsAssetBundleLoading(string path)
|
||
{
|
||
return m_LoadingAssetBundles.Contains(GetAssetBundleCacheKey(path));
|
||
}
|
||
|
||
static void BeginAssetBundleLoad(string path)
|
||
{
|
||
m_LoadingAssetBundles.Add(GetAssetBundleCacheKey(path));
|
||
}
|
||
|
||
static void EndAssetBundleLoad(string path)
|
||
{
|
||
m_LoadingAssetBundles.Remove(GetAssetBundleCacheKey(path));
|
||
}
|
||
|
||
public static void AddScene(string ScenePath, LoadAssetCallback Callback)
|
||
{
|
||
m_Base.StartCoroutine(OnLoadScene(ScenePath, Callback));
|
||
}
|
||
|
||
public static IEnumerator OnLoadScene(string ScenePath, LoadAssetCallback Callback)
|
||
{
|
||
//检测是否Editor
|
||
if (m_ABMode == 1)
|
||
{
|
||
//加载相关ab包
|
||
string res;
|
||
string abpath = GetABResPath(ScenePath, out res);
|
||
//var res_type = type;
|
||
LoadAssetRequest request = new LoadAssetRequest();
|
||
request.assetType = typeof(Scene);
|
||
int pos = res.LastIndexOf("/");
|
||
if (pos != -1)
|
||
{//保留最后的资源名
|
||
request.assetNames = new string[] { res.Substring(pos + 1) };
|
||
}
|
||
else
|
||
request.assetNames = new string[] { res };
|
||
request.orginPath = null;
|
||
request.sharpFunc = null;
|
||
|
||
List<LoadAssetRequest> requests = null;
|
||
if (!m_LoadRequests.TryGetValue(abpath, out requests))
|
||
{
|
||
AssetBundleInfo bundleInfo = GetLoadedAssetBundle(abpath);
|
||
if (bundleInfo == null)
|
||
{
|
||
requests = new List<LoadAssetRequest>();
|
||
requests.Add(request);
|
||
m_LoadRequests.Add(abpath, requests);
|
||
|
||
yield return OnLoadAssetBundle(abpath, typeof(Scene));
|
||
|
||
}
|
||
else
|
||
{
|
||
}
|
||
}
|
||
else
|
||
{
|
||
requests.Add(request);
|
||
}
|
||
}
|
||
//bool SceneLoaded() { return bFinish; };
|
||
//yield return new WaitUntil(SceneLoaded);
|
||
#if !UNITY_EDITOR
|
||
#else
|
||
//editor下的场景要加入到buildsetting中
|
||
#endif
|
||
string SceneName = Path.GetFileNameWithoutExtension(ScenePath);
|
||
m_Async = SceneManager.LoadSceneAsync(SceneName, LoadSceneMode.Additive);
|
||
//m_Async.allowSceneActivation = false;
|
||
if (m_Async == null)
|
||
{
|
||
Debug.Log($"LoadScene {ScenePath} Failed!");
|
||
Callback(null);
|
||
}
|
||
else
|
||
{
|
||
while (m_Async.progress < 0.98f)
|
||
{
|
||
m_toProgress = (int)m_Async.progress * 100;
|
||
if (m_displayProgress < m_toProgress)
|
||
{
|
||
++m_displayProgress;
|
||
//yield return null;//new WaitForEndOfFrame();
|
||
}
|
||
yield return null;
|
||
}
|
||
|
||
m_toProgress = 100;
|
||
while (m_displayProgress < m_toProgress)
|
||
{
|
||
++m_displayProgress;
|
||
|
||
yield return new WaitForEndOfFrame();
|
||
}
|
||
int count = SceneManager.sceneCount;
|
||
GameObject[] objs = null;
|
||
Scene scene = SceneManager.GetSceneByName(SceneName);//有同名问题?
|
||
if (!m_SceneLoaded.ContainsKey(ScenePath))
|
||
{
|
||
m_SceneLoaded[ScenePath] = count - 1;
|
||
objs = scene.GetRootGameObjects();
|
||
}
|
||
|
||
if (Callback != null)
|
||
Callback(objs);
|
||
//切换场景
|
||
//m_Async.allowSceneActivation = true;
|
||
}
|
||
}
|
||
public static void DelScene(string ScenePath)
|
||
{
|
||
if (m_SceneLoaded.ContainsKey(ScenePath))
|
||
{
|
||
string SceneName = Path.GetFileNameWithoutExtension(ScenePath);
|
||
string abName;
|
||
if (ScenePath.EndsWith(m_Postfix))
|
||
abName = ScenePath;
|
||
else
|
||
abName = $"{ScenePath}{m_Postfix}";
|
||
abName = GetDepFilePath(m_sUpdatePath, abName);
|
||
UnloadAssetBundle(abName);
|
||
SceneManager.UnloadSceneAsync(SceneName);
|
||
m_SceneLoaded.Remove(ScenePath);
|
||
}
|
||
//GC回收
|
||
}
|
||
|
||
public static string GetMd5ByResPath(string path, string xid = null)
|
||
{
|
||
string ret;
|
||
Dictionary<string, string> dic;
|
||
if(xid == null)
|
||
{
|
||
dic = m_dicXidFileList["XWorld"];
|
||
}
|
||
else
|
||
{
|
||
dic = m_dicXidFileList[xid];
|
||
}
|
||
if(dic.TryGetValue(path, out ret))
|
||
//if (dic.ContainsKey(path))
|
||
{
|
||
return ret;//dic[path];
|
||
}
|
||
else
|
||
return "";
|
||
}
|
||
|
||
//直接加载文件,相对于StreamingAssets目录的路径
|
||
public static void LoadPackageFile(string Path, LoadFileCallback Callback)
|
||
{
|
||
m_Base.StartCoroutine(OnLoadPackageFile(Path, Callback));
|
||
}
|
||
|
||
public static IEnumerator OnLoadPackageFile(string Path, LoadFileCallback Callback)
|
||
{
|
||
#if UNITY_EDITOR
|
||
Callback(null);
|
||
yield break;
|
||
#else
|
||
byte[] data = null;
|
||
//编辑器
|
||
string url = $"{Application.streamingAssetsPath}/{Path}";
|
||
//包内目录
|
||
using (UnityWebRequest uwr = UnityWebRequest.Get(url))
|
||
{
|
||
|
||
yield return uwr.SendWebRequest();
|
||
data = uwr.downloadHandler.data;
|
||
//AssetBundle ab = null;
|
||
if (!string.IsNullOrEmpty(uwr.error) /*|| bytes.Length == 0*/)
|
||
{
|
||
//url = url.Replace(relpath, "");
|
||
Debug.LogError(uwr.error + " | "+url);
|
||
}
|
||
}
|
||
|
||
|
||
Callback(data);
|
||
#endif
|
||
}
|
||
|
||
|
||
public static void AddAssetBundle(string fullPathName, AssetBundle assetBundle)
|
||
{
|
||
string cacheKey = GetAssetBundleCacheKey(fullPathName);
|
||
if (m_LoadedAssetBundles.ContainsKey(cacheKey))
|
||
return;
|
||
AssetBundleInfo abInfo = new AssetBundleInfo(assetBundle);
|
||
m_LoadedAssetBundles.Add(cacheKey, abInfo);
|
||
}
|
||
public static string[] GetAllDependencies(string assetBundleName)
|
||
{
|
||
string[] bundles = m_AssetBundleManifest.GetAllDependencies(assetBundleName);
|
||
//if (bundles.Length > 0)
|
||
return bundles;
|
||
//其他
|
||
;
|
||
//if(m_ABManifest3D != null)
|
||
//if (assetBundleName == null || assetBundleName == "")
|
||
//{
|
||
// Debug.Log("assetBundleName is null");
|
||
//}
|
||
//if (m_ABManifest3D == null)
|
||
//{
|
||
// Debug.Log("m_ABManifest3D is null");
|
||
// return null;
|
||
//}
|
||
//string[] bundles = m_ABManifest3D.GetAllDependencies(assetBundleName);
|
||
//return bundles;
|
||
}
|
||
/// <summary>
|
||
/// 加载TextMeshPro Setting
|
||
/// </summary>
|
||
static void LoadTMPSettings()
|
||
{
|
||
|
||
}
|
||
|
||
//获取ab路径
|
||
public static string GetABResPath(string path, out string resname, bool bExtReserve = false)
|
||
{
|
||
string abpath;
|
||
string abname = string.Empty;
|
||
string dir = string.Empty;
|
||
string xid;
|
||
string md5;
|
||
|
||
int pos;
|
||
//针对xid路径修改 path = $"{xid}/{GlobalData.CurPlatform}/{GetFileMd5Name("files.txt", md5)}";
|
||
if(path.StartsWith("Game/"))
|
||
{
|
||
xid = "XWorld";
|
||
}
|
||
else if (path.StartsWith("XWorld/"))
|
||
{
|
||
xid = "XWorld";
|
||
pos = 7;
|
||
path = path.Substring(pos);//去掉前面的路径
|
||
}
|
||
else
|
||
{
|
||
xid = m_sCurXid;
|
||
pos = path.IndexOf('/');
|
||
path = path.Substring(pos);//去掉前面的路径
|
||
}
|
||
//m_AssetBundleManifest = m_dicXidManaifest[xid];
|
||
|
||
path = path.Replace("\\", "/");
|
||
int npos = path.LastIndexOf('/');
|
||
int lastpos = path.LastIndexOf('.');
|
||
|
||
if ((pos = path.IndexOf(m_Postfix)) != -1)
|
||
{//其他3d资源读取,兼容原来的map,model,effect 读取 读取方式为直接ab名加资源名
|
||
//npos = path.IndexOf(m_Postfix);//+8;
|
||
if (path.Length <= pos + 8)
|
||
{
|
||
resname = path;
|
||
Debug.LogWarning("GetABResPath Error: " + path);
|
||
return path;
|
||
}
|
||
else
|
||
{
|
||
pos = pos + 8;
|
||
}
|
||
abname = path.Substring(0, pos).ToLower();
|
||
resname = path.Substring(pos + 1);
|
||
|
||
if (m_dicXidFileList.ContainsKey(xid))
|
||
md5 = m_dicXidFileList[xid][abname];//md5需要是文件名path而不是资源路径
|
||
else
|
||
md5 = "";
|
||
//修改为只返回相对路径
|
||
abpath = $"{xid}/{GlobalData.CurPlatform}/{GetFileMd5Name(abname, md5)}";
|
||
return abpath;
|
||
/*/这里需要先读取Update目录资源
|
||
abpath = m_sUpdatePath + abname;
|
||
if (!File.Exists(abpath))//update目录为可写物理目录,可用File判断
|
||
{
|
||
#if UNITY_IOS || UNITY_ANDROID
|
||
//Debug.Log("Local : Update Path :" + abpath);
|
||
abpath = m_sLocalPath + abname;//底包内部ab读取
|
||
#else
|
||
|
||
//标准资源位置加载
|
||
abpath = m_sLocalPath + abname;
|
||
if (!File.Exists(abpath))//PC 版也可以这样判定
|
||
{
|
||
abpath = m_Path + abname;//统一资源位置
|
||
}
|
||
#endif
|
||
}
|
||
return abpath;//*/
|
||
}
|
||
else
|
||
{//原始相对路径加载,转为ab + 资源名的形式
|
||
if (lastpos < npos) //目录中包含.字
|
||
{
|
||
lastpos = -1;
|
||
}
|
||
if (lastpos == -1)
|
||
{
|
||
lastpos = path.Length;
|
||
}
|
||
int beginpos = 0;
|
||
beginpos = path.IndexOf('/');
|
||
//beginpos = path.IndexOf('/', beginpos+1);//查找第二个子目录名
|
||
string sType = path.Substring(0, beginpos);
|
||
//if (sType == "res")
|
||
//{//res 目录下的路径,有子目录如res/effect/
|
||
// beginpos = path.IndexOf('/', beginpos + 1);
|
||
//}
|
||
if (beginpos == -1 || npos == -1)
|
||
{
|
||
Debug.LogError(" / beginpos == -1 || npos == -1 : " + path);
|
||
beginpos = 0;
|
||
}
|
||
if (npos - beginpos - 1 <= 0)
|
||
{
|
||
resname = path;
|
||
Debug.LogWarning("GetABResPath Error: " + path);
|
||
return path;
|
||
}
|
||
if (path.EndsWith(".spriteatlas")) //单文件打包的
|
||
{
|
||
npos = path.LastIndexOf('.');
|
||
abname = path.Substring(0, npos) + m_Postfix;//beginpos + 1
|
||
abname = abname.ToLower().Replace("/", "_");
|
||
npos = path.LastIndexOf('/');
|
||
}
|
||
else
|
||
{
|
||
if (!path.EndsWith(m_Postfix))
|
||
abname = (path.Substring(0, npos) + m_Postfix).Replace('/', '_');//beginpos + 1
|
||
else
|
||
abname = path.Substring(0, npos).Replace('/', '_');
|
||
|
||
//转小写
|
||
abname = abname.ToLower();
|
||
}
|
||
if (beginpos + 1 <= 0)
|
||
{
|
||
resname = path;
|
||
Debug.LogWarning("GetABResPath Error: " + path);
|
||
return path;
|
||
}
|
||
//dir = path.Substring(0, beginpos + 1);// "/" +
|
||
//dir = dir.ToLower();
|
||
dir = "";
|
||
}
|
||
//统一走 60S/XWorld/<platform>/<file>_<md5>.unity3d(md5 命名),md5 取自 XWorld 清单;缺失则空(不抛)
|
||
md5 = "";
|
||
if (m_dicXidFileList.ContainsKey(xid) && m_dicXidFileList[xid] != null)
|
||
m_dicXidFileList[xid].TryGetValue(abname, out md5);
|
||
#if UNITY_WEBGL
|
||
|
||
//WebGL直接从网站下载
|
||
if (!bExtReserve)
|
||
{
|
||
resname = path.Substring(npos + 1, lastpos - npos - 1); //去掉文件后缀名
|
||
}
|
||
else
|
||
{
|
||
resname = path.Substring(npos + 1, path.Length - npos - 1);
|
||
}
|
||
//修改为只返回相对路径
|
||
abpath = $"{xid}/{GlobalData.CurPlatform}/{GetFileMd5Name(abname, md5)}";
|
||
return abpath;
|
||
|
||
#endif
|
||
if (!bExtReserve)
|
||
{
|
||
resname = path.Substring(npos + 1, lastpos - npos - 1); //去掉文件后缀名
|
||
}
|
||
else
|
||
{
|
||
resname = path.Substring(npos + 1, path.Length - npos - 1);
|
||
}
|
||
//修改为只返回相对路径
|
||
abpath = $"{xid}/{GlobalData.CurPlatform}/{GetFileMd5Name(abname, md5)}";
|
||
return abpath;
|
||
/*/这里需要先读取Update目录资源 ud
|
||
abpath = m_sUpdatePath + dir + abname;//path.Substring(0, beginpos + 1) //.Replace('\\', '/')
|
||
|
||
if (!File.Exists(abpath))//update目录为可写物理目录,可用File判断
|
||
{
|
||
#if UNITY_IOS || UNITY_ANDROID
|
||
//Debug.Log("Local : Update Path :" + abpath);
|
||
abpath = m_sLocalPath + dir + abname;//底包内部ab读取
|
||
#else
|
||
//原始底包资源(LocalTest个人UIEditor打包资源)
|
||
abpath = Application.dataPath + dir + abname;
|
||
if (!File.Exists(abpath))
|
||
{
|
||
//标准资源位置加载
|
||
abpath = m_sLocalPath + dir + abname;
|
||
if (!File.Exists(abpath))//PC 版也可以这样判定
|
||
{
|
||
abpath = m_Path + abname;//统一资源位置
|
||
}
|
||
}
|
||
|
||
#endif
|
||
|
||
}
|
||
|
||
return abpath;//*/
|
||
}
|
||
|
||
public static void LoadRes(string[] paths, Type type, Action<UObject[]> action)
|
||
{
|
||
m_Base.StartCoroutine(coLoadAllRes(paths, type, action));
|
||
}
|
||
public static UObject[] GetCurLoadedObjs()
|
||
{
|
||
return m_CurLoadedObjs;
|
||
}
|
||
public static UObject GetCurLoadedObj()
|
||
{
|
||
if (m_CurLoadedObjs != null)
|
||
return m_CurLoadedObjs[0];
|
||
else
|
||
return null;
|
||
}
|
||
public static IEnumerator coLoadRes(string path, Type type = null, Action<UObject> action = null)
|
||
{
|
||
if (path.StartsWith("Assets/"))
|
||
{
|
||
path = path.Substring(7);
|
||
}
|
||
yield return coLoadAllRes(new string[] { path }, type, objs =>
|
||
{
|
||
if (objs != null && objs.Length > 0)
|
||
{
|
||
m_CurLoadedObjs = objs;
|
||
if (action != null)
|
||
action(objs[0]);
|
||
}
|
||
else if (action != null)
|
||
{
|
||
m_CurLoadedObjs = null;
|
||
action(null);
|
||
}
|
||
|
||
});
|
||
}
|
||
public static IEnumerator coLoadRess(string[] paths, Type type = null, Action<UObject> action = null)
|
||
{
|
||
for (int i = 0; i < paths.Length; ++i)
|
||
{
|
||
if (paths[i].StartsWith("Assets/"))
|
||
{
|
||
paths[i] = paths[i].Substring(7);
|
||
}
|
||
}
|
||
yield return coLoadAllRes(paths, type, objs =>
|
||
{
|
||
if (objs != null && objs[0] != null)
|
||
{
|
||
m_CurLoadedObjs = objs;
|
||
if (action != null)
|
||
action(objs[0]);
|
||
}
|
||
else if (action != null)
|
||
{
|
||
m_CurLoadedObjs = null;
|
||
action(null);
|
||
}
|
||
|
||
});
|
||
}
|
||
|
||
public static IEnumerator coLoadXIDAB(string xid, string abpath, Action<AssetBundle> action)
|
||
{
|
||
AssetBundle ab = null;
|
||
|
||
AssetBundleInfo bundleInfo = GetLoadedAssetBundle(abpath);
|
||
{
|
||
if (bundleInfo != null)
|
||
{
|
||
ab = bundleInfo.m_AssetBundle;
|
||
action(ab);
|
||
yield break;
|
||
}
|
||
}
|
||
if (IsAssetBundleLoading(abpath))
|
||
{
|
||
while (IsAssetBundleLoading(abpath))
|
||
{
|
||
yield return null;
|
||
}
|
||
|
||
bundleInfo = GetLoadedAssetBundle(abpath);
|
||
if (bundleInfo != null)
|
||
{
|
||
ab = bundleInfo.m_AssetBundle;
|
||
action(ab);
|
||
yield break;
|
||
}
|
||
}
|
||
BeginAssetBundleLoad(abpath);
|
||
string cdn;
|
||
if (!m_dicXidCDN.TryGetValue(xid, out cdn))
|
||
{
|
||
EndAssetBundleLoad(abpath);
|
||
yield break;
|
||
}
|
||
if (!m_dicXidFileList.TryGetValue(xid, out var fileList) || fileList == null
|
||
|| !fileList.TryGetValue(abpath, out var md5))
|
||
{
|
||
// 清单里没有该资源(例如未打包/未发布),优雅返回,让上层去尝试其它 xid 或按"未找到"处理
|
||
EndAssetBundleLoad(abpath);
|
||
yield break;
|
||
}
|
||
string path = $"{xid}/{GlobalData.CurPlatform}/{GetFileMd5Name(abpath, md5)}";
|
||
yield return GetAssetsBundleData(cdn, path, (uwr, isRaw) =>
|
||
{
|
||
if (uwr != null)
|
||
{
|
||
//下载期间可能已被并发请求加载;复查命中则复用,避免对同一内容二次 LoadFromMemory。
|
||
AssetBundleInfo loadedInfo = GetLoadedAssetBundle(abpath);
|
||
if (loadedInfo != null)
|
||
{
|
||
ab = loadedInfo.m_AssetBundle;
|
||
EndAssetBundleLoad(abpath);
|
||
return;
|
||
}
|
||
if(isRaw)
|
||
ab = DownloadHandlerAssetBundle.GetContent(uwr);
|
||
else
|
||
ab = AssetBundle.LoadFromMemory(uwr.downloadHandler.data);
|
||
//注册进全局缓存,使后续(含作为依赖的)加载能去重,避免重复 LoadFromMemory。
|
||
if (ab != null)
|
||
AddAssetBundle(abpath, ab);
|
||
}
|
||
EndAssetBundleLoad(abpath);
|
||
});
|
||
action(ab);
|
||
}
|
||
public static IEnumerator coLoadAB(string abpath, Action<AssetBundle> action)
|
||
{
|
||
AssetBundle ret = null;
|
||
yield return coLoadXIDAB("XWorld", abpath, (ab) =>
|
||
{
|
||
ret = ab;
|
||
});
|
||
if (ret != null)
|
||
{
|
||
action(ret);
|
||
yield break;
|
||
}
|
||
yield return coLoadXIDAB(m_sCurXid, abpath, (ab) =>
|
||
{
|
||
ret = ab;
|
||
});
|
||
if (ret != null)
|
||
{
|
||
action(ret);
|
||
yield break;
|
||
}
|
||
//yield return coLoadXIDAB("XWorld", abpath, (ab) =>
|
||
//{
|
||
// ret = ab;
|
||
//});
|
||
}
|
||
public static IEnumerator coLoadAllRes(string[] paths, Type type, Action<UObject[]> action)
|
||
{
|
||
string res;
|
||
List<UObject> result = null;
|
||
List<string> listPath = new List<string>();
|
||
if (type == null)
|
||
type = typeof(GameObject);
|
||
var res_type = type;
|
||
result = new List<UObject>();
|
||
foreach (string path in paths)
|
||
{
|
||
string abpath = GetABResPath(path, out res);
|
||
if (type == null)
|
||
type = typeof(GameObject);
|
||
int pos = res.LastIndexOf("/");
|
||
|
||
LoadAssetRequest request = new LoadAssetRequest();
|
||
request.assetType = type;
|
||
if (pos != -1)
|
||
{//保留最后的资源名
|
||
request.assetNames = new string[] { res.Substring(pos + 1) };
|
||
}
|
||
else
|
||
request.assetNames = new string[] { res };
|
||
request.sharpFunc = action;
|
||
request.orginPath = path;
|
||
List<LoadAssetRequest> requests = null;
|
||
if (!m_LoadRequests.TryGetValue(abpath, out requests))
|
||
{
|
||
requests = new List<LoadAssetRequest>();
|
||
requests.Add(request);
|
||
m_LoadRequests.Add(abpath, requests);
|
||
//Launcher.Instance.StartCoroutine(OnLoadAsset(abpath, res, path, res_type, null));
|
||
}
|
||
else
|
||
{
|
||
requests.Add(request);
|
||
}
|
||
|
||
UObject getAsset = null;
|
||
#if UNITY_EDITOR
|
||
if (m_ABMode == 0)
|
||
{
|
||
//编辑器读取本地资源
|
||
string resPath;
|
||
if (!path.StartsWith("Assets/"))
|
||
resPath = $"Assets/{path}";
|
||
else
|
||
resPath = path;
|
||
if (File.Exists(resPath))
|
||
{//有才加载
|
||
getAsset = UnityEditor.AssetDatabase.LoadAssetAtPath<UObject>(resPath);
|
||
if (getAsset != null)
|
||
{
|
||
result.Add(getAsset);
|
||
}
|
||
else
|
||
{
|
||
Debug.LogError($"Load {path} Failed!");
|
||
}
|
||
m_LoadRequests.Remove(abpath);
|
||
continue;
|
||
}
|
||
}
|
||
#endif
|
||
//Load AB from base
|
||
AssetBundleInfo bundleInfo = GetLoadedAssetBundle(abpath);
|
||
{
|
||
if (bundleInfo == null)
|
||
{
|
||
yield return OnLoadAssetBundle(abpath, type);
|
||
|
||
bundleInfo = GetLoadedAssetBundle(abpath);
|
||
if (bundleInfo == null)
|
||
{
|
||
m_LoadRequests.Remove(abpath);
|
||
Debug.LogError("OnLoadAsset Failed--->>>" + abpath);
|
||
continue;
|
||
}
|
||
if (bundleInfo.m_AssetBundle == null)
|
||
{
|
||
m_LoadRequests.Remove(abpath);
|
||
Debug.LogError("OnLoadAsset AssetBundle is null--->>>" + abpath);
|
||
continue;
|
||
}
|
||
}
|
||
}
|
||
//Load Asset —— 只加载并返回“本次请求自身”的资源。
|
||
//原实现会遍历 m_LoadRequests[abpath] 整个共享队列、把所有人的资源都加进自己的 result:
|
||
//并发加载同一 AB 内不同资源时(如 UI_GameLobby/UI_CharacterSwitch/UI_LobbyJoystick 同在 game_art_ui_prefab.unity3d)
|
||
//请求会互相串包,coLoadRes 取 objs[0] 便拿到别人的资源(表现为换角色面板/摇杆加载到错误 prefab → 按钮不可见、摇杆缺 Knob)。
|
||
List<UObject> resultLocal = null;
|
||
if (!objDic.TryGetValue(request.orginPath, out resultLocal))
|
||
{
|
||
resultLocal = new List<UObject>();
|
||
AssetBundle ab = bundleInfo.m_AssetBundle;
|
||
for (int j = 0; j < request.assetNames.Length; j++)
|
||
{
|
||
string assetPath = request.assetNames[j];
|
||
AssetBundleRequest req = ab.LoadAssetAsync(assetPath, request.assetType);
|
||
yield return req;
|
||
if (req.asset == null)
|
||
{
|
||
Debug.Log($"LoadAsset From AB Failed {assetPath} type :{request.assetType.Name} ");
|
||
}
|
||
else
|
||
{
|
||
Debug.Log($"LoadAsset {assetPath} OK {DateTime.Now}");
|
||
}
|
||
resultLocal.Add(req.asset);
|
||
}
|
||
objDic[request.orginPath] = resultLocal;//缓存对象(键=资源路径,值=该资源本身)
|
||
}
|
||
result.AddRange(resultLocal);
|
||
|
||
//从共享队列中移除“自己的”请求;队列空了再移除整个键(避免影响并发中的其它请求)
|
||
List<LoadAssetRequest> cur;
|
||
if (m_LoadRequests.TryGetValue(abpath, out cur))
|
||
{
|
||
cur.Remove(request);
|
||
if (cur.Count == 0)
|
||
{
|
||
m_LoadRequests.Remove(abpath);
|
||
}
|
||
}
|
||
if (bundleInfo != null)
|
||
bundleInfo.m_ReferencedCount++;
|
||
}
|
||
//完成
|
||
if (action != null)
|
||
{
|
||
action(result.ToArray());
|
||
}
|
||
yield break;
|
||
}
|
||
|
||
|
||
public static void LoadResAB(string path, Type type, Action<UObject[]> action)
|
||
{
|
||
string res;
|
||
if (path.StartsWith("Assets/"))
|
||
{
|
||
path = path.Substring(7);
|
||
}
|
||
string abpath = GetABResPath(path, out res);
|
||
//Debug.LogWarning($"LoadResAB {abpath} {res}");
|
||
if (type == null)
|
||
type = typeof(GameObject);
|
||
var res_type = type;
|
||
LoadAssetRequest request = new LoadAssetRequest();
|
||
request.assetType = type;
|
||
int pos = res.LastIndexOf("/");
|
||
if (pos != -1)
|
||
{//保留最后的资源名
|
||
request.assetNames = new string[] { res.Substring(pos + 1) };
|
||
}
|
||
else
|
||
request.assetNames = new string[] { res };
|
||
request.sharpFunc = action;
|
||
request.orginPath = path;
|
||
|
||
List<LoadAssetRequest> requests = null;
|
||
if (!m_LoadRequests.TryGetValue(abpath, out requests))
|
||
{
|
||
requests = new List<LoadAssetRequest>();
|
||
requests.Add(request);
|
||
m_LoadRequests.Add(abpath, requests);
|
||
m_Base.StartCoroutine(OnLoadAsset(abpath, res, path, res_type, null));
|
||
}
|
||
else
|
||
{
|
||
requests.Add(request);
|
||
}
|
||
|
||
}
|
||
|
||
/// <summary>
|
||
/// 同步加载AB
|
||
/// </summary>
|
||
/// <param name="path"></param>
|
||
/// <param name="type"></param>
|
||
/// <returns></returns>
|
||
[Obsolete("资源加载统一用异步:coLoadRes 或回调版 LoadRes(path,type,Action)。同步加载不从CDN下载热更资源,会读到旧APK底包。详见 Doc/Rule/UnityProject.md")]
|
||
public static UObject LoadResAB(string path, Type type)
|
||
{
|
||
//先取缓存
|
||
List<UObject> result = null;
|
||
if (objDic.TryGetValue(path, out result) && result.Count > 0)
|
||
{
|
||
return result[0];
|
||
}
|
||
if (type == null)
|
||
type = typeof(GameObject);
|
||
string res;
|
||
string abPath = GetABResPath(path, out res);
|
||
var res_type = type;
|
||
AssetBundleInfo bundleInfo = GetLoadedAssetBundle(abPath);
|
||
AssetBundle ab = null;
|
||
if (bundleInfo == null)
|
||
{
|
||
if (type != typeof(AssetBundleManifest))
|
||
{
|
||
//先加载依赖项
|
||
string[] dependencies;
|
||
if(!m_Dependencies.TryGetValue(abPath, out dependencies))
|
||
//if (m_Dependencies.ContainsKey(abPath))
|
||
//{
|
||
// dependencies = m_Dependencies[abPath];
|
||
//}
|
||
//else
|
||
{
|
||
//if (abPath.Contains("/ui/"))
|
||
//{
|
||
if (m_AssetBundleManifest == null)
|
||
{
|
||
dependencies = null;
|
||
Debug.LogError("m_AssetBundleManifest is null : " + abPath);
|
||
return null;
|
||
}
|
||
else
|
||
{
|
||
string abDep = abPath.Replace(m_sUpdatePath, "");//未考虑原始包文件
|
||
abDep = GetUnversionedAssetBundleName(StripXidPlatformPrefix(abDep));
|
||
dependencies = m_AssetBundleManifest.GetAllDependencies(abDep); //GetAllDependencies(Path.GetFileName(abPath));
|
||
}
|
||
//}
|
||
//else
|
||
//{
|
||
// int pos = abPath.IndexOf("base/");
|
||
// dependencies = GetAllDependencies(path.Substring(0, path.Length - res.Length - 1));
|
||
//}
|
||
m_Dependencies.Add(abPath, dependencies);
|
||
}
|
||
//将依赖的包也加载出来
|
||
if (dependencies.Length > 0)
|
||
{
|
||
for (int i = 0; i < dependencies.Length; i++)
|
||
{
|
||
string depName = dependencies[i];
|
||
AssetBundleInfo d_bundleInfo = null;
|
||
#if (UNITY_IOS || UNITY_ANDROID) && !UNITY_EDITOR
|
||
string abfile = GetDepFilePath(abPath, depName);// abPath.Replace(Path.GetFileName(abPath), depName);
|
||
#else
|
||
//ud 220527
|
||
string abfile = abPath.Replace(m_sUpdatePath, "");//未考虑原始包文件
|
||
abfile = GetUnversionedAssetBundleName(StripXidPlatformPrefix(abfile));
|
||
dependencies = m_AssetBundleManifest.GetAllDependencies(abfile);
|
||
|
||
#endif
|
||
d_bundleInfo = GetLoadedAssetBundle(abfile);
|
||
if (d_bundleInfo != null)
|
||
{
|
||
d_bundleInfo.m_ReferencedCount++;
|
||
}
|
||
else
|
||
{
|
||
//加载对应的依赖包
|
||
int count = 0;
|
||
//跟协程加载那里进行线程安全判定
|
||
if(m_LoadDependenciesReq.TryGetValue(abfile, out count))
|
||
//if (m_LoadDependenciesReq.ContainsKey(abfile))
|
||
{
|
||
m_LoadDependenciesReq[abfile] = count + 1;
|
||
}
|
||
else
|
||
{
|
||
d_bundleInfo = LoadAssetBundleSycn(abfile);
|
||
if (d_bundleInfo == null)
|
||
{
|
||
Debug.LogError("LoadResAB Load dep ab Failed--->>>" + abfile);
|
||
continue;
|
||
}
|
||
d_bundleInfo.m_ReferencedCount++;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
bundleInfo = LoadAssetBundleSycn(abPath);
|
||
if (bundleInfo == null)
|
||
{
|
||
Debug.LogError("LoadResAB LoadABSycn Failed--->>>" + abPath);
|
||
return null;
|
||
}
|
||
else
|
||
{
|
||
ab = bundleInfo.m_AssetBundle;
|
||
if (ab == null)
|
||
{
|
||
Debug.LogWarning("LoadResAB Load AB Error: " + abPath);
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
ab = bundleInfo.m_AssetBundle;
|
||
}
|
||
|
||
if (ab == null)
|
||
{
|
||
return null;
|
||
}
|
||
UObject uo = ab.LoadAsset(res, type);
|
||
objDic.Add(path, new List<UObject>() { uo }); //缓存对象
|
||
bundleInfo.m_ReferencedCount++;
|
||
return uo;
|
||
}
|
||
|
||
public static byte[] LoadBytesRes(string filename)
|
||
{
|
||
filename = filename.Replace("\\", "/").Replace(".", "/");
|
||
if (m_ABMode == 0)
|
||
{//本地加载
|
||
string fullPath = $"{Application.dataPath}/lua/{filename}.lua";
|
||
if (File.Exists(fullPath))
|
||
{
|
||
return File.ReadAllBytes(fullPath);
|
||
}
|
||
}
|
||
//ab加载
|
||
string abResName;
|
||
int pos = filename.IndexOf("/");
|
||
if (pos == -1)//根路径脚本路径特殊处理一下
|
||
abResName = $"luabytes/luabytes/{filename}.bytes";
|
||
else
|
||
abResName = $"luabytes/{filename}.bytes";
|
||
//luabytes/
|
||
return GetABBytesRes(abResName);
|
||
}
|
||
public static byte[] GetABBytesRes(string filename)
|
||
{
|
||
byte[] data;
|
||
//1.update ab读取 //目前方式和ui一样,以目录为单位做ab包
|
||
string name = filename;
|
||
string path = name;//android : Application.persistentDataPath + "/";m_sUpdatePath +
|
||
UnityEngine.Object obj = LoadLuaAB(path, typeof(TextAsset));//LoadRes(path, typeof(TextAsset));
|
||
if (obj != null)
|
||
{
|
||
data = (obj as TextAsset).bytes;// txt;//
|
||
if (data != null)
|
||
return data;
|
||
}
|
||
//2.包内资源读取ab 或 Resources.Load<TextAsset>(filename).bytes;
|
||
//path = m_sProductPath + "lua/" + name;
|
||
//obj = ResManager.LoadResAB(path, typeof(TextAsset));
|
||
//if (obj != null)
|
||
//{
|
||
// data = (obj as TextAsset).bytes;
|
||
// if (data != null)
|
||
// return data;
|
||
//}
|
||
return null;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取文本资源字符
|
||
/// </summary> Plot/1001.BT
|
||
/// <param name="filename"></param>
|
||
/// <returns></returns>
|
||
public static string GetTextAssetRes(string filename)
|
||
{
|
||
string content = string.Empty;
|
||
if (Application.platform == RuntimePlatform.Android || Application.platform == RuntimePlatform.IPhonePlayer)
|
||
{
|
||
TextAsset txt;
|
||
//1.update ab读取 //目前方式和ui一样,以目录为单位做ab包
|
||
string name = filename;
|
||
string path = "scripts/config/" + name;//android : Application.persistentDataPath + "/";m_sUpdatePath +
|
||
UnityEngine.Object obj = LoadLuaAB(path, typeof(TextAsset));//LoadRes(path, typeof(TextAsset));
|
||
if (obj != null)
|
||
{
|
||
txt = (obj as TextAsset);// txt;//
|
||
if (txt != null)
|
||
content = txt.text;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
string m_productPath = m_sProductPath;
|
||
string fullPath = $"{m_productPath}Config/Config/{filename}";
|
||
if (!File.Exists(fullPath))
|
||
{
|
||
fullPath = $"{m_productPath}Scripts_c/Base/Config/{filename}";
|
||
if (!File.Exists(fullPath))
|
||
{
|
||
fullPath = $"{Application.dataPath}/Scripts_c/Base/Config/{filename}";
|
||
}
|
||
}
|
||
try
|
||
{
|
||
if (File.Exists(fullPath))
|
||
{
|
||
content = File.ReadAllText(fullPath);
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Debug.LogError(fullPath + "文件不存在(" + ex.Message + ")\n" + ex.StackTrace);
|
||
}
|
||
}
|
||
return content;
|
||
}
|
||
|
||
public static UObject LoadLuaAB(string path, Type type)
|
||
{
|
||
//先取缓存
|
||
List<UObject> result = null;
|
||
if (objDic.TryGetValue(path, out result) && result.Count > 0)
|
||
{
|
||
return result[0];
|
||
}
|
||
string res;
|
||
string abPath = GetABResPath(path, out res, true);
|
||
var res_type = type;
|
||
|
||
AssetBundleInfo bundleInfo = GetLoadedAssetBundle(abPath);
|
||
AssetBundle ab = null;
|
||
if (bundleInfo == null)
|
||
{
|
||
bundleInfo = LoadAssetBundleSycn(abPath);
|
||
if (bundleInfo == null)
|
||
{
|
||
Debug.LogError("LoadLuaAB LoadABSync Failed--->>>" + abPath);
|
||
return null;
|
||
}
|
||
ab = bundleInfo.m_AssetBundle;
|
||
}
|
||
else
|
||
{
|
||
ab = bundleInfo.m_AssetBundle;
|
||
}
|
||
if (ab == null)
|
||
{
|
||
Debug.LogWarning("LoadLuaAB Load AB Error: " + abPath);
|
||
return null;
|
||
}
|
||
UObject uo = ab.LoadAsset(res, type);
|
||
|
||
objDic.Add(path, new List<UObject>() { uo }); //缓存对象
|
||
bundleInfo.m_ReferencedCount++;
|
||
return uo;
|
||
}
|
||
/// <summary>
|
||
/// 同步加载AB包
|
||
/// </summary>
|
||
/// <param name="path"></param>
|
||
/// <returns></returns>
|
||
static AssetBundleInfo LoadAssetBundleSycn(string path)
|
||
{
|
||
AssetBundleInfo abInfo = null;
|
||
// GetABResPath 现在返回相对路径(如 XWorld/android/xxx_md5.unity3d);
|
||
// 与异步加载一致解析为完整本地路径:优先 persistentDataPath(已下载更新),回退底包 streamingAssets。
|
||
// 幂等:已是完整路径(rooted 或已含 m_sUpdatePath/m_sLocalPath)则不再拼。
|
||
path = ResolveLocalAssetBundlePath(path);
|
||
abInfo = GetLoadedAssetBundle(path);
|
||
if (abInfo != null)
|
||
{
|
||
return abInfo;
|
||
}
|
||
AssetBundle ab = AssetBundle.LoadFromFile(path);
|
||
if (ab == null)
|
||
{
|
||
Debug.LogError($"LoadAssetBundleSycn failed: {path}");
|
||
return null;
|
||
}
|
||
string cacheKey = GetAssetBundleCacheKey(path);
|
||
abInfo = new AssetBundleInfo(ab);
|
||
m_LoadedAssetBundles.Add(cacheKey, abInfo);
|
||
return abInfo;
|
||
}
|
||
public static AssetBundle LoadAssetBundleAsync(string reltivePath, System.Action<string, AssetBundle> callback = null)
|
||
{
|
||
string fullPath;//这里需要先读取Update目录资源
|
||
fullPath = ResolveLocalAssetBundlePath(reltivePath);
|
||
AssetBundleInfo abInfo;
|
||
if (callback == null)
|
||
{
|
||
abInfo = LoadAssetBundleSycn(fullPath);
|
||
if (abInfo != null)
|
||
return abInfo.m_AssetBundle;
|
||
else
|
||
return null;
|
||
}
|
||
abInfo = GetLoadedAssetBundle(fullPath);
|
||
if (abInfo != null)
|
||
{
|
||
callback(fullPath, abInfo.m_AssetBundle);
|
||
return null;
|
||
}
|
||
m_Base.StartCoroutine(OnLoadAssetBundleAsync(fullPath, callback));
|
||
return null;
|
||
}
|
||
|
||
public static IEnumerator OnLoadAssetBundleAsync(string fullPath, System.Action<string, AssetBundle> callback)
|
||
{
|
||
yield return OnLoadAssetBundle(fullPath, typeof(AssetBundle));
|
||
AssetBundle ab = null;
|
||
AssetBundleInfo info;
|
||
info = GetLoadedAssetBundle(fullPath);
|
||
if (info != null)
|
||
ab = info.m_AssetBundle;
|
||
//var loadRequest = AssetBundle.LoadFromFileAsync(fullPath);
|
||
//yield return loadRequest;
|
||
//AssetBundleInfo abInfo = new AssetBundleInfo(loadRequest.assetBundle);
|
||
//m_LoadedAssetBundles.Add(fullPath, abInfo);
|
||
callback(fullPath, ab);
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
/// 同步方式加载资源
|
||
/// </summary>
|
||
/// <param name="path"></param>
|
||
/// <param name="type"></param>
|
||
/// <returns></returns>
|
||
[Obsolete("资源加载统一用异步:coLoadRes 或回调版 LoadRes(path,type,Action)。同步加载不从CDN下载热更资源,会读到旧APK底包。详见 Doc/Rule/UnityProject.md")]
|
||
public static UObject LoadRes(string path, Type type)
|
||
{
|
||
#pragma warning disable 618 // 该同步实现内部仍会调用已标记 [Obsolete] 的同步方法
|
||
//先取缓存
|
||
List<UObject> result = null;
|
||
if (objDic.TryGetValue(path, out result) && result.Count > 0)
|
||
{
|
||
return result[0];
|
||
}
|
||
//如果是AB包模式
|
||
if (m_ABMode == 1)
|
||
{
|
||
return LoadResAB(path, type);
|
||
}
|
||
#if UNITY_EDITOR
|
||
//编辑器环境直接读取本地资源
|
||
string finalpath;
|
||
if (path.StartsWith("Assets/"))
|
||
finalpath = path;
|
||
else
|
||
finalpath = "Assets/" + path;
|
||
|
||
int lastpos = path.LastIndexOf('.');
|
||
if (lastpos == -1)
|
||
{
|
||
finalpath += ".prefab";//暂时无后缀名的默认都是prefab
|
||
}
|
||
|
||
UObject asset = AssetDatabase.LoadAssetAtPath(finalpath, type);
|
||
if (asset != null)
|
||
{
|
||
return asset;
|
||
}
|
||
#endif
|
||
UObject o = LoadResAB(path, type);
|
||
//if (o == null)
|
||
//{
|
||
// Debug.LogError("Load res Failed: "+ path);
|
||
//}
|
||
return o;
|
||
#pragma warning restore 618
|
||
}
|
||
|
||
|
||
//1、Editor读本地资源;2、读更新目录资源;3、WebGL动态下载对应资源;4、app版(pc,安卓,ios)读取包内文件
|
||
public static void LoadRes(string path, Type type, Action<UObject[]> action = null)
|
||
{
|
||
//先取缓存
|
||
List<UObject> result = null;
|
||
if (objDic.TryGetValue(path, out result))
|
||
{
|
||
//220412 ud 增加对C#回调的支持
|
||
if (action != null)
|
||
{
|
||
action(result.ToArray());
|
||
action = null;
|
||
}
|
||
return;
|
||
}
|
||
|
||
//表示UI界面处于AB包调试模式
|
||
if (m_ABMode == 1)
|
||
{
|
||
LoadResAB(path, type, action);
|
||
return;
|
||
}
|
||
|
||
#if UNITY_EDITOR
|
||
//编辑器环境直接读取本地资源
|
||
result = new List<UObject>();
|
||
string finalpath = path;
|
||
if (path.StartsWith("Assets/"))
|
||
finalpath = path;
|
||
else
|
||
finalpath = "Assets/" + path;
|
||
|
||
int lastpos = path.LastIndexOf('.');
|
||
if (lastpos == -1)
|
||
{
|
||
finalpath += ".prefab";//暂时无后缀名的默认都是prefab
|
||
}
|
||
|
||
UObject asset = AssetDatabase.LoadAssetAtPath(finalpath, type);
|
||
if (asset)
|
||
{
|
||
result.Add(asset);
|
||
objDic.Add(path, result);
|
||
if (action != null)
|
||
{
|
||
action(result.ToArray());
|
||
}
|
||
return;
|
||
}
|
||
#endif
|
||
|
||
LoadResAB(path, type, action);
|
||
return;
|
||
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取图标
|
||
/// </summary>
|
||
/// <param name="spritePath"></param>
|
||
/// <returns></returns>
|
||
public static Sprite GetSpriteByName(string spritePath)
|
||
{
|
||
if (!spritePath.EndsWith(".png"))
|
||
{
|
||
spritePath += ".png";
|
||
}
|
||
// List<UObject> objs = null;
|
||
// if(objDic.TryGetValue(spritePath,out objs) && objs.Count>0)
|
||
// {
|
||
// return (Sprite)objs[0];
|
||
// }
|
||
#pragma warning disable 618 // XResLoader 内部同步辅助,暂保留
|
||
UObject spObj = LoadRes(spritePath, typeof(Sprite));
|
||
#pragma warning restore 618
|
||
return spObj ? (Sprite)spObj : null;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取图集Sprite序列
|
||
/// </summary>
|
||
/// <param name="atlasName"></param>
|
||
/// <param name="spriteName"></param>
|
||
/// <returns></returns>
|
||
public static Sprite[] GetSpritesByName(string atlasName, string spriteName)
|
||
{
|
||
string path = atlasName + "/" + spriteName;
|
||
List<Sprite> sprites = new List<Sprite>();
|
||
for (int i = 1; i <= 30; i++)
|
||
{
|
||
try
|
||
{
|
||
Sprite sp = GetSpriteByName(path + i.ToString("D2") + ".png");
|
||
if (sp)
|
||
{
|
||
sprites.Add(sp);
|
||
}
|
||
else break;
|
||
}
|
||
catch
|
||
{
|
||
}
|
||
}
|
||
return sprites.ToArray();
|
||
}
|
||
|
||
//读取资源//、读取本地;、读更新目录资源;、WebGL动态下载对应资源;、app版(pc,安卓,ios)读取包内文件
|
||
static IEnumerator OnLoadAsset(string abPath, string res, string orginPath, Type type, Action<UObject> action = null)
|
||
{
|
||
GameObject getAsset = null;
|
||
if (m_ABMode == 0)
|
||
{
|
||
#if UNITY_EDITOR
|
||
//编辑器读取本地资源
|
||
string artPath = res;
|
||
getAsset = UnityEditor.AssetDatabase.LoadAssetAtPath<GameObject>(orginPath);
|
||
if (getAsset != null)
|
||
{
|
||
m_LoadRequests.Remove(abPath);
|
||
yield break;
|
||
}
|
||
#endif
|
||
}
|
||
|
||
AssetBundleInfo bundleInfo = GetLoadedAssetBundle(abPath);
|
||
if (getAsset == null)
|
||
{
|
||
if (bundleInfo == null)
|
||
{
|
||
yield return OnLoadAssetBundle(abPath, type);
|
||
bundleInfo = GetLoadedAssetBundle(abPath);
|
||
if (bundleInfo == null)
|
||
{
|
||
m_LoadRequests.Remove(abPath);
|
||
Debug.LogError("OnLoadAsset Failed--->>>" + abPath);
|
||
yield break;
|
||
}
|
||
}
|
||
}
|
||
//相关ab包已经加载完了,可以真正读取资源
|
||
List<LoadAssetRequest> list = null;
|
||
if (!m_LoadRequests.TryGetValue(abPath, out list))
|
||
{
|
||
m_LoadRequests.Remove(abPath);
|
||
yield break;
|
||
}
|
||
for (int i = 0; i < list.Count; i++)
|
||
{
|
||
string[] assetNames = list[i].assetNames;
|
||
List<UObject> result = null;
|
||
|
||
if (!objDic.TryGetValue(list[i].orginPath, out result))
|
||
{
|
||
result = new List<UObject>();
|
||
if (getAsset == null)
|
||
{
|
||
AssetBundle ab = bundleInfo.m_AssetBundle;
|
||
for (int j = 0; j < assetNames.Length; j++)
|
||
{
|
||
string assetPath = assetNames[j];
|
||
AssetBundleRequest request = ab.LoadAssetAsync(assetPath, list[i].assetType);
|
||
yield return request;
|
||
result.Add(request.asset);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
result.Add(getAsset);
|
||
}
|
||
if (objDic.ContainsKey(list[i].orginPath))
|
||
{
|
||
objDic.Remove(list[i].orginPath);
|
||
}
|
||
objDic.Add(list[i].orginPath, result);//缓存对象
|
||
}
|
||
|
||
if (list[i].sharpFunc != null)
|
||
{
|
||
list[i].sharpFunc(result.ToArray());
|
||
list[i].sharpFunc = null;
|
||
}
|
||
if (bundleInfo != null)
|
||
bundleInfo.m_ReferencedCount++;
|
||
}
|
||
m_LoadRequests.Remove(abPath);
|
||
}
|
||
|
||
static string GetDepFilePath(string abFullPath, string depName)
|
||
{
|
||
//ud 220616 分别支持ui和其他资源的打包方式
|
||
string path;
|
||
|
||
if (abFullPath.Contains(m_sUpdatePath))
|
||
{//原ab路径是更新目录
|
||
path = $"{m_sUpdatePath}{depName}";
|
||
if (!File.Exists(path))
|
||
{//替换会本地目录
|
||
path = path.Replace(m_sUpdatePath, m_sLocalPath);
|
||
}
|
||
}
|
||
else //if(path.Contains(m_sLocalPath))
|
||
{ //原ab路径是本地目录
|
||
path = $"{m_sUpdatePath}{depName}";
|
||
if (!File.Exists(path))
|
||
{
|
||
return $"{m_sLocalPath}{depName}";
|
||
}
|
||
}
|
||
return path;
|
||
}
|
||
|
||
//读取ab//、读更新目录资源ab;、WebGL动态下载对应资源ab;、app版(pc,安卓,ios)读取包内ab
|
||
static IEnumerator OnLoadAssetBundle(string abPath, Type type)
|
||
{
|
||
AssetBundleInfo existingInfo = GetLoadedAssetBundle(abPath);
|
||
if (existingInfo != null)
|
||
{
|
||
existingInfo.m_ReferencedCount++;
|
||
yield break;
|
||
}
|
||
|
||
if (IsAssetBundleLoading(abPath))
|
||
{
|
||
while (IsAssetBundleLoading(abPath))
|
||
{
|
||
yield return null;
|
||
}
|
||
|
||
existingInfo = GetLoadedAssetBundle(abPath);
|
||
if (existingInfo != null)
|
||
{
|
||
existingInfo.m_ReferencedCount++;
|
||
yield break;
|
||
}
|
||
}
|
||
|
||
BeginAssetBundleLoad(abPath);
|
||
string url = abPath;
|
||
string prePath;
|
||
int n = abPath.LastIndexOf("/");
|
||
prePath = abPath.Substring(0, n+1);
|
||
|
||
if (type != typeof(AssetBundleManifest))
|
||
{
|
||
//先加载依赖项
|
||
string[] dependencies = null;
|
||
if(!m_Dependencies.TryGetValue(abPath, out dependencies))
|
||
//if (m_Dependencies.ContainsKey(abPath))
|
||
//{
|
||
// dependencies = m_Dependencies[abPath];
|
||
//}
|
||
//else
|
||
{
|
||
//if (abPath.Contains("/ui/"))//ui是一种依赖打包方式
|
||
//获得ab名
|
||
string depPath = abPath.Replace(m_sUpdatePath, "");
|
||
//int pos = depPath.LastIndexOf('_');
|
||
int beginpos = depPath.LastIndexOf('/')+1;
|
||
depPath = depPath.Substring(beginpos);
|
||
depPath = GetUnversionedAssetBundleName(depPath);
|
||
//depPath = abPath.Replace(m_sLocalPath, "");
|
||
//GetDepFilePath(abPath, depPath);
|
||
if (m_AssetBundleManifest != null)
|
||
{
|
||
dependencies = m_AssetBundleManifest.GetAllDependencies(depPath); //GetAllDependencies(Path.GetFileName(abPath));
|
||
//else
|
||
//{//暂时兼容之前的打包方式
|
||
// int pos = abPath.IndexOf("base/");
|
||
// dependencies = GetAllDependencies(abPath.Substring(pos));
|
||
//};
|
||
m_Dependencies.Add(abPath, dependencies);
|
||
}
|
||
}
|
||
|
||
if (dependencies != null && dependencies.Length > 0)
|
||
{
|
||
for (int i = 0; i < dependencies.Length; i++)
|
||
{
|
||
string depName = dependencies[i];
|
||
AssetBundleInfo bundleInfo = null;
|
||
//#if (UNITY_IOS || UNITY_ANDROID) && !UNITY_EDITOR
|
||
// string abfile = GetDepFilePath(abPath, depName);// abPath.Replace(Path.GetFileName(abPath), depName);
|
||
//#else
|
||
//ud 220527
|
||
string abfile;
|
||
|
||
abfile = $"{prePath}{depName}";//abPath.Replace(Path.GetFileName(abPath), depName);//{m_sUpdatePath}
|
||
//#endif
|
||
bundleInfo = GetLoadedAssetBundle(abfile);
|
||
if (bundleInfo != null)
|
||
{
|
||
bundleInfo.m_ReferencedCount++;
|
||
}
|
||
else if (!m_LoadRequests.ContainsKey(abfile) && !m_LoadDependenciesReq.ContainsKey(abfile))
|
||
{
|
||
//加载对应的依赖包
|
||
m_LoadDependenciesReq.Add(abfile, 1);
|
||
//此处需要判定update目录是否有更新文件,没有才从本地目录获取
|
||
yield return OnLoadAssetBundle(abfile, type);
|
||
bundleInfo = GetLoadedAssetBundle(abfile);
|
||
if (bundleInfo == null)
|
||
{
|
||
Debug.LogError("OnLoadAssetBundle Failed--->>>" + abfile);
|
||
continue;//yield break;
|
||
}
|
||
bundleInfo.m_ReferencedCount++;
|
||
}
|
||
else
|
||
{
|
||
int count = 0;
|
||
if (m_LoadDependenciesReq.TryGetValue(abfile, out count))
|
||
m_LoadDependenciesReq[abfile] = count + 1;//m_LoadDependenciesReq[abfile] + 1;
|
||
else
|
||
m_LoadDependenciesReq.Add(abfile, 1);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
AssetBundleInfo Info = GetLoadedAssetBundle(abPath);
|
||
if (Info != null)
|
||
{//中途有人加载了这个ab包,就不需要加载了
|
||
Info.m_ReferencedCount++;
|
||
EndAssetBundleLoad(abPath);
|
||
yield break;
|
||
}
|
||
//真正完整路径
|
||
string cdn;
|
||
if (!m_dicXidCDN.TryGetValue(m_sCurXid, out cdn))
|
||
{
|
||
Debug.LogError($"No XID({m_sCurXid}) CDN : ");
|
||
//cdn = "";
|
||
EndAssetBundleLoad(abPath);
|
||
yield break;
|
||
}
|
||
url = GetPathMd5Name(abPath);
|
||
yield return GetAssetsBundleData(cdn, url, (uwr, isRaw) =>
|
||
{
|
||
if (uwr != null)
|
||
{
|
||
//下载是异步的(可能耗时数秒),期间可能有并发请求把同一 AB 加载并注册了。
|
||
//若不在 LoadFromMemory 前复查,会对同一内容二次 LoadFromMemory,触发
|
||
//"another AssetBundle with the same files is already loaded" 并返回 null。
|
||
AssetBundleInfo loadedInfo = GetLoadedAssetBundle(abPath);
|
||
if (loadedInfo != null)
|
||
{
|
||
int waited;
|
||
if (m_LoadDependenciesReq.TryGetValue(abPath, out waited))
|
||
{
|
||
loadedInfo.m_ReferencedCount += waited;
|
||
m_LoadDependenciesReq.Remove(abPath);
|
||
}
|
||
else
|
||
{
|
||
loadedInfo.m_ReferencedCount++;
|
||
}
|
||
EndAssetBundleLoad(abPath);
|
||
return;
|
||
}
|
||
AssetBundle ab;
|
||
if (isRaw)
|
||
ab = DownloadHandlerAssetBundle.GetContent(uwr);
|
||
else
|
||
ab = AssetBundle.LoadFromMemory(uwr.downloadHandler.data);
|
||
if (ab == null)
|
||
{
|
||
Debug.LogError($"Load AB failed: {url}");
|
||
EndAssetBundleLoad(abPath);
|
||
return;
|
||
}
|
||
DateTime now = DateTime.Now;
|
||
//Debug.LogWarning($"Load AB ok: {abPath} {now}");
|
||
string cacheKey = GetAssetBundleCacheKey(abPath);
|
||
m_LoadedAssetBundles[cacheKey] = new AssetBundleInfo(ab);
|
||
int count;
|
||
if (m_LoadDependenciesReq.TryGetValue(abPath, out count))
|
||
//if (m_LoadDependenciesReq.ContainsKey(abPath))
|
||
{
|
||
AssetBundleInfo bundleInfo = GetLoadedAssetBundle(abPath);
|
||
if (bundleInfo != null)
|
||
{
|
||
bundleInfo.m_ReferencedCount += count - 1;//可能有隐患,如果当前加载不是由依赖引起的,又同时有其他作为依赖加载可能就有问题,虽然可能性很少
|
||
}
|
||
m_LoadDependenciesReq.Remove(abPath);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
Debug.LogWarning("Load AB Error: " + url);
|
||
}
|
||
EndAssetBundleLoad(abPath);
|
||
});
|
||
|
||
yield break;
|
||
|
||
}
|
||
|
||
static AssetBundleInfo GetLoadedAssetBundle(string abName)
|
||
{
|
||
AssetBundleInfo bundle = null;
|
||
if (!m_LoadedAssetBundles.TryGetValue(abName, out bundle))
|
||
m_LoadedAssetBundles.TryGetValue(GetAssetBundleCacheKey(abName), out bundle);
|
||
if (bundle == null)
|
||
return null;
|
||
return bundle;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 释放所有ab包缓存
|
||
/// </summary>
|
||
//*
|
||
static public void UnloadAllAssetBundle()
|
||
{
|
||
List<string> strList = new List<string>();
|
||
foreach (KeyValuePair<string, AssetBundleInfo> kvp in m_LoadedAssetBundles)
|
||
{
|
||
if (m_LoadRequests.ContainsKey(kvp.Key))
|
||
{
|
||
//Debug.Log("unload AB Error: " + kvp.Key);
|
||
--kvp.Value.m_ReferencedCount;
|
||
continue;//如果当前AB处于Async Loading过程中,卸载会崩溃,只减去引用计数即可
|
||
}
|
||
else
|
||
{
|
||
//kvp.Value.m_AssetBundle.Unload(true);
|
||
strList.Add(kvp.Key);
|
||
}
|
||
|
||
}
|
||
Debug.Log("strList count: " + strList.Count);
|
||
foreach (var name in strList)
|
||
{
|
||
if (name == "StreamingAssets.unity3d")//(name.IndexOf("StreamingAssets", StringComparison.OrdinalIgnoreCase) >= 0)
|
||
{
|
||
continue;
|
||
}
|
||
//Debug.Log("unload: " + name);
|
||
AssetBundleInfo bundle = GetLoadedAssetBundle(name);
|
||
if (bundle == null)
|
||
continue;
|
||
if (m_LoadRequests.ContainsKey(name))
|
||
{
|
||
--bundle.m_ReferencedCount;
|
||
continue; //如果当前AB处于Async Loading过程中,卸载会崩溃,只减去引用计数即可
|
||
}
|
||
if (bundle.m_AssetBundle != null)
|
||
{
|
||
bundle.m_AssetBundle.Unload(false);
|
||
}
|
||
|
||
m_LoadedAssetBundles.Remove(name);
|
||
}
|
||
objDic.Clear();
|
||
m_AssetBundleManifest = null;
|
||
m_Dependencies.Clear();
|
||
m_LoadedAssetBundles.Clear();
|
||
m_LoadingAssetBundles.Clear();
|
||
}//*/
|
||
|
||
static public void UnloadRes(string path, bool isThorough = false)
|
||
{
|
||
if (path.Length < 2)
|
||
return;
|
||
if (objDic.ContainsKey(path))
|
||
{
|
||
objDic.Remove(path);
|
||
}
|
||
#if UNITY_EDITOR
|
||
return;
|
||
#endif
|
||
//释放相关资源
|
||
/*int npos = path.LastIndexOf('/');
|
||
int lastpos = path.LastIndexOf('.');
|
||
if (lastpos == -1)
|
||
{
|
||
lastpos = 0;
|
||
}
|
||
|
||
string abname = (path.Substring(0, npos) + m_Postfix).Replace('/', '_');
|
||
|
||
string abpath = m_sProductPath + "/UI/" + abname;
|
||
if (!File.Exists(abpath))
|
||
{
|
||
abpath = Application.dataPath + "/UI/" + abname;
|
||
}//*/
|
||
|
||
string res;
|
||
string abpath = GetABResPath(path, out res);
|
||
UnloadAssetBundle(abpath, isThorough);
|
||
}
|
||
/// <summary>
|
||
/// 此函数交给外部卸载专用,自己调整是否需要彻底清除AB
|
||
/// </summary>
|
||
/// <param name="abName"></param>
|
||
/// <param name="isThorough"></param>
|
||
static public void UnloadAssetBundle(string abPath, bool isThorough = false)
|
||
{
|
||
//Debug.Log(m_LoadedAssetBundles.Count + " assetbundle(s) in memory before unloading " + abPath);
|
||
UnloadAssetBundleInternal(abPath, isThorough);
|
||
//依赖减少需要此资源彻底删除才删
|
||
//UnloadDependencies(abPath, isThorough);
|
||
//Debug.Log(m_LoadedAssetBundles.Count + " assetbundle(s) in memory after unloading " + abName);
|
||
}
|
||
|
||
static void UnloadDependencies(string abName, bool isThorough)
|
||
{
|
||
string[] dependencies = null;
|
||
if (!m_Dependencies.TryGetValue(abName, out dependencies))
|
||
return;
|
||
|
||
// Loop dependencies.
|
||
m_Dependencies.Remove(abName);//ud 先去掉依赖以免无限循环
|
||
foreach (var dependency in dependencies)
|
||
{
|
||
string deppath = abName.Replace(Path.GetFileName(abName), dependency);
|
||
UnloadAssetBundleInternal(deppath, isThorough);
|
||
}
|
||
}
|
||
|
||
static void UnloadAssetBundleInternal(string abName, bool isThorough)
|
||
{
|
||
AssetBundleInfo bundle = GetLoadedAssetBundle(abName);
|
||
if (bundle == null)
|
||
{
|
||
//Debug.Log("Free AB Failed: " + abName);
|
||
return;
|
||
}
|
||
|
||
if ((--bundle.m_ReferencedCount) <= 0)
|
||
{
|
||
if (m_LoadRequests.ContainsKey(abName))
|
||
{
|
||
return; //如果当前AB处于Async Loading过程中,卸载会崩溃,只减去引用计数即可
|
||
}
|
||
UnloadDependencies(abName, isThorough);
|
||
bundle.m_AssetBundle.Unload(isThorough);
|
||
m_LoadedAssetBundles.Remove(abName);
|
||
//Debug.Log(abName + " has been unloaded successfully");
|
||
}
|
||
}
|
||
|
||
public static void Clear()
|
||
{
|
||
UnloadAllAssetBundle();
|
||
}
|
||
}
|
||
}
|
||
|