Files
Zombie/Assets/Plugins/XPlugins/ResMod/ResManager.cs

506 lines
12 KiB
C#
Raw 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.
#define UPDATER_LOG //open log
using System;
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using JetBrains.Annotations;
using UnityEngine.SceneManagement;
using Object = UnityEngine.Object;
using Debug = UnityEngine.Debug;
namespace XPlugin.Update
{
[ExecuteInEditMode]
public class ResManager : MonoBehaviour
{
private static ResManager _ins;
public static ResManager Ins
{
private set { _ins = value; }
get
{
if (_ins == null)
{
var go = new GameObject("ResManager(AUTO)");
go.hideFlags = HideFlags.DontSave;
if (Application.isPlaying)
{
DontDestroyOnLoad(go);
}
_ins = go.AddComponent<ResManager>();
}
return _ins;
}
}
/// <summary>
/// 下载目录子目录
/// </summary>
internal const string DOWNLOAD_DIR_NAME = "/Update/";
/// <summary>
/// 完整的下载目录
/// </summary>
public static string FullDownloadDir
{
get { return Application.persistentDataPath + DOWNLOAD_DIR_NAME; }
}
/// <summary>
/// 路径中的目录分隔符
/// </summary>
public const string DIR_SPLIT = "_!_";
/// <summary>
/// 已下载的文件
/// key:文件名
/// value:文件FileInfo
/// </summary>
internal Dictionary<string, FileInfo> DownloadedFiles = new Dictionary<string, FileInfo>();
/// <summary>
/// 缓存的unity objects
/// 请求过的物体会在这里缓存起来
/// </summary>
internal Dictionary<string, Object> CachedObjects = new Dictionary<string, Object>();
internal Dictionary<string, AssetBundle> CachedSceneBundles = new Dictionary<string, AssetBundle>();
internal Dictionary<string, Queue<Action<Object>>> AsyncLoadingQueue = new Dictionary<string, Queue<Action<Object>>>();
internal Dictionary<string, Queue<Action<bool>>> AsyncLoadingSceneQueue = new Dictionary<string, Queue<Action<bool>>>();
private void Awake()
{
Ins = this;
ReadDownloadDir();
SceneManager.sceneLoaded += OnSceneLoaded;
}
void OnDestroy()
{
Ins = null;
SceneManager.sceneLoaded -= OnSceneLoaded;
}
void OnSceneLoaded(Scene scene, LoadSceneMode mode)
{
// UpdaterLog.Log("Clean LevelCached Start");
//在切换关卡的时候将保留的Bundle释放
foreach (var bundle in CachedSceneBundles.Values)
{
bundle.Unload(false);
}
StartCoroutine(ClearDelay());
}
IEnumerator ClearDelay()
{
yield return 0;
//释放资源物体的引用
CachedObjects.Clear();
//释放场景bundle的引用
CachedSceneBundles.Clear();
// UpdaterLog.Log("Clean LevelCached End");
}
/// <summary>
/// 读取下载目录,将下载目录中的文件缓存起来
/// </summary>
internal void ReadDownloadDir()
{
DownloadedFiles.Clear();
CachedObjects.Clear();
CachedSceneBundles.Clear();
AsyncLoadingQueue.Clear();
AsyncLoadingSceneQueue.Clear();
if (Directory.Exists(FullDownloadDir))
{
// 找到所有的已下载文件
var files = Directory.GetFiles(FullDownloadDir);
foreach (var file in files)
{
FileInfo info = new FileInfo(file);
// UpdaterLog.Log("find downloaded file:" + info);
var fileName = Path.GetFileName(file);
DownloadedFiles.Add(fileName, info);
}
}
else
{
// UpdaterLog.Log("create directory:" + FullDownloadDir);
Directory.CreateDirectory(FullDownloadDir);
}
}
#region Load
internal Object Load(string path, Type type)
{
Object obj = null;
if (CachedObjects.TryGetValue(path, out obj))
{// 尝试在缓存中查找
return obj;
}
else
{// 尝试从外部文件中加载asset bundle
var fileName = path.Replace("/", DIR_SPLIT);//替换目录分隔符
if (DownloadedFiles.ContainsKey(fileName))
{
FileInfo file = DownloadedFiles[fileName];
var bytes = File.ReadAllBytes(file.FullName);
var ab = AssetBundle.LoadFromMemory(bytes);
string objectName = Path.GetFileName(path);
obj = ab.LoadAsset(objectName, type);
ab.Unload(false);//一旦加载完毕立即释放assetbundle但不释放bundle中的物体,物体会在场景切换时释放
if (obj == null)
{
// UpdaterLog.LogError(string.Format("the resource {0} load from ab is null", path));
return null;
}
CachedObjects.Add(path, obj);
}
else
{
obj = Resources.Load(path, type);
}
}
return obj;
}
#endregion Load
#region LoadAsync
internal void LoadAsync(string path, Type type, Action<Object> onDone)
{
Object obj = null;
if (CachedObjects.TryGetValue(path, out obj))
{// 尝试在缓存中查找
if (onDone != null)
{
onDone(obj);
}
}
else
{
Queue<Action<Object>> onDoneQueue;
if (AsyncLoadingQueue.TryGetValue(path, out onDoneQueue))
{//如果该物体已经在加载中,则将回调直接入列,等待加载结束调用即可
onDoneQueue.Enqueue(onDone);
return;
}
else
{
onDoneQueue = new Queue<Action<Object>>();
AsyncLoadingQueue.Add(path, onDoneQueue);
onDoneQueue.Enqueue(onDone);
}
var fileName = path.Replace("/", DIR_SPLIT);//替换目录分隔符
if (DownloadedFiles.ContainsKey(fileName))
{// 尝试从外部文件中加载asset bundle
FileInfo file = DownloadedFiles[fileName];
var bytes = File.ReadAllBytes(file.FullName);
StartCoroutine(AsyncCreateABReqObj(path, type, bytes));
}
else
{
StartCoroutine(AsyncResourcesLoad(path, type));
}
}
}
IEnumerator AsyncResourcesLoad(string path, Type type)
{
var req = Resources.LoadAsync(path, type);
yield return req;
Object obj = req.asset;
Queue<Action<Object>> onDoneQueue = AsyncLoadingQueue[path];
if (onDoneQueue == null)
{
// UpdaterLog.LogException(new Exception("internal error , the AsyncLoadingQueue have no queue named :" + path));
yield break;
}
while (onDoneQueue.Count > 0)
{
onDoneQueue.Dequeue()(obj);
}
this.AsyncLoadingQueue.Remove(path);
}
IEnumerator AsyncCreateABReqObj(string path, Type type, byte[] bytes)
{
var abCreateReq = AssetBundle.LoadFromMemoryAsync(bytes);//异步创建ab
yield return abCreateReq;
string objectName = Path.GetFileName(path);
var abReq = abCreateReq.assetBundle.LoadAssetAsync(objectName, type);//异步读取物体
yield return abReq;
Object obj = abReq.asset;
abCreateReq.assetBundle.Unload(false);//一旦加载完毕立即释放assetbundle但不释放bundle中的物体,物体会在场景切换时释放
if (obj == null)
{
// UpdaterLog.LogError(string.Format("the resource {0} load async from ab is null", path));
}
else
{
// UpdaterLog.Log(string.Format("load async the resource {0} from ab", path));
if (!CachedObjects.ContainsKey(path))
{
CachedObjects.Add(path, obj);
}
}
Queue<Action<Object>> onDoneQueue = AsyncLoadingQueue[path];
if (onDoneQueue == null)
{
// UpdaterLog.LogException(new Exception("internal error , the AsyncLoadingQueue have no queue named :" + path));
yield break;
}
while (onDoneQueue.Count > 0)
{
onDoneQueue.Dequeue()(obj);
}
this.AsyncLoadingQueue.Remove(path);
}
#endregion LoadAsync
#region ReqScene
internal bool ReqScene(string path)
{
if (!CachedSceneBundles.ContainsKey(path))
{
if (DownloadedFiles.ContainsKey(path))
{
FileInfo file = DownloadedFiles[path];
var bytes = File.ReadAllBytes(file.FullName);
var ab = AssetBundle.LoadFromMemory(bytes);
if (ab != null)
{ //判断AB中是否有该场景
var scenePaths = ab.GetAllScenePaths();
foreach (var scenePath in scenePaths)
{
if (Path.GetFileNameWithoutExtension(scenePath) == path)
{
// UpdaterLog.Log("find scene in assetbundle:" + path);
CachedSceneBundles.Add(path, ab);
break;
}
}
}
}
}
return Application.CanStreamedLevelBeLoaded(path);
}
#endregion ReqScene
#region ReqSceneAsync
internal void ReqSceneAsync(string path, Action<bool> onDone)
{
if (CachedSceneBundles.ContainsKey(path))
{//已经在缓存列表中,则直接返回
onDone(Application.CanStreamedLevelBeLoaded(path));
}
else
{
if (DownloadedFiles.ContainsKey(path))
{
Queue<Action<bool>> onDoneQueue;
if (AsyncLoadingSceneQueue.TryGetValue(path, out onDoneQueue))
{
//如果该场景已经在加载中,则将回调直接入列,等待加载结束调用即可
onDoneQueue.Enqueue(onDone);
return;
}
else
{
onDoneQueue = new Queue<Action<bool>>();
AsyncLoadingSceneQueue.Add(path, onDoneQueue);
onDoneQueue.Enqueue(onDone);
}
FileInfo file = DownloadedFiles[path];
var bytes = File.ReadAllBytes(file.FullName);
StartCoroutine(AsyncCreateABReqScene(path, bytes));
}
else
{
if (onDone != null)
{
onDone(Application.CanStreamedLevelBeLoaded(path));
}
}
}
}
IEnumerator AsyncCreateABReqScene(string path, byte[] bytes)
{
var abCreateReq = AssetBundle.LoadFromMemoryAsync(bytes);
yield return abCreateReq;
var ab = abCreateReq.assetBundle;
if (ab != null)
{ //判断AB中是否有该场景
var scenePaths = ab.GetAllScenePaths();
foreach (var scenePath in scenePaths)
{
if (Path.GetFileNameWithoutExtension(scenePath) == path)
{
// UpdaterLog.Log("find scene in assetbundle:" + path);
if (!CachedSceneBundles.ContainsKey(path))
{
CachedSceneBundles.Add(path, ab);
}
break;
}
}
}
Queue<Action<bool>> onDoneQueue = AsyncLoadingSceneQueue[path];
if (onDoneQueue == null)
{
// UpdaterLog.LogException(new Exception("Internal error , the AsyncLoadingQueue have no queue named :" + path));
yield break;
}
bool result = Application.CanStreamedLevelBeLoaded(path);
while (onDoneQueue.Count > 0)
{
onDoneQueue.Dequeue()(result);
}
this.AsyncLoadingSceneQueue.Remove(path);
}
#endregion ReqSceneAsnc
#region LoadStreamingAsset
internal string GetUpdatedStreamingAssetPath(string path)
{
var fileName = Path.GetFileNameWithoutExtension(path);
if (DownloadedFiles.ContainsKey(fileName))
{
path = "file://" + DownloadedFiles[fileName].FullName;
// UpdaterLog.Log("find streaming asset at update path : " + path);
}
else
{
path = Path.Combine(Application.streamingAssetsPath, path);
if (!path.Contains("://"))
{
path = "file://" + path;
}
}
return path;
}
internal WWW LoadStreamingAsset(string path)
{
WWW www = new WWW(GetUpdatedStreamingAssetPath(path));
while (!www.isDone)
{
if (!string.IsNullOrEmpty(www.error))
{
break;
}
}
return www;
}
internal void LoadStreamingAssetAsync(string path, Action<WWW> onDone)
{
StartCoroutine(AsyncLoadStreamingAsset(GetUpdatedStreamingAssetPath(path), onDone));
}
IEnumerator AsyncLoadStreamingAsset(string path, Action<WWW> onDone)
{
WWW www = new WWW(path);
yield return www;
if (onDone != null)
{
onDone(www);
}
}
#endregion LoadStreamingAsset
#region LoadAsset
internal WWW LoadAsset(string dir, string url)
{
string path = dir + url.GetHashCode().ToString();
WWW www = new WWW(File.Exists(path) ? "file:///" + path : url);
while (!www.isDone)
{
if (!string.IsNullOrEmpty(www.error))
{
break;
}
}
return www;
}
internal void LoadAssetAsync(string dir, string url, Action<WWW> onDone)
{
StartCoroutine(AsyncLoadAsset(dir, url, onDone));
}
IEnumerator AsyncLoadAsset(string dir, string url, Action<WWW> onDone)
{
string path = dir + url.GetHashCode().ToString();
WWW www = new WWW(File.Exists(path) ? "file:///" + path : url);
yield return www;
if (onDone != null)
{
onDone(www);
}
}
internal bool SaveAsset(string dir, string url, WWW www)
{
string path = dir + url.GetHashCode().ToString();
if (!File.Exists(path))
{
try
{
FileInfo info = new FileInfo(path);
DirectoryInfo dirInfo = info.Directory;
if (!dirInfo.Exists)
{
dirInfo.Create();
}
File.WriteAllBytes(info.FullName, www.bytes);
return true;
}
catch (Exception e)
{
Debug.LogException(e);
return false;
}
}
return true;
}
internal bool RemoveAsset(string dir, string url)
{
string path = dir + url.GetHashCode().ToString();
if (!File.Exists(path))
{
return true;
}
try
{
File.Delete(path);
return true;
}
catch
{
return false;
}
}
#endregion LoadImage
}
}