Skip to content

Instantly share code, notes, and snippets.

@ifshuaishuai
Last active April 4, 2019 03:47
Show Gist options
  • Save ifshuaishuai/74b616c5f282a2efb9decea44d3b6d2b to your computer and use it in GitHub Desktop.
Save ifshuaishuai/74b616c5f282a2efb9decea44d3b6d2b to your computer and use it in GitHub Desktop.
using System;
using System.Collections;
using System.Collections.Generic;
using RSG;
using UnityEngine;
using UnityEngine.Assertions;
using System.IO;
using System.Runtime.InteropServices;
using Object = UnityEngine.Object;
using System.Text;
public class NetworkTextureManager
{
public const int DEFAULT_LIST_SIZE = 32;
private static NetworkTextureManager _instance;
public static NetworkTextureManager Instance
{
get
{
if(_instance == null)
{
_instance = new NetworkTextureManager();
}
return _instance;
}
}
private NetworkTextureManager()
{ }
abstract class WWWResource
{
public Object asset;
public abstract void GenerateAsset(WWW www);
public abstract void Destroy();
}
class WWWTexture : WWWResource
{
public override void GenerateAsset(WWW www)
{
Assert.IsNotNull(www);
Assert.IsTrue(string.IsNullOrEmpty(www.error));
asset = www.texture;
}
public override void Destroy()
{
if (asset)
{
Object.Destroy(asset);
}
}
}
class WWWWrapper
{
class RefData
{
public object owner;
public List<int> internalRefList = new List<int>(1);
}
public string path;
public WWWResource asset;
public bool loadDone;
public List<Action> loadDoneAction;
private List<RefData> _referenceList = new List<RefData>(DEFAULT_LIST_SIZE);
public override string ToString()
{
var sb = new StringBuilder();
for(int i=0, length=_referenceList.Count ; i<length; ++i)
{
sb.Append(string.Format("\nowner: {0}, refCount: {1}", _referenceList[i].owner, _referenceList[i].internalRefList.Count));
}
return string.Format("path: {0} loadDone: {1}, RefList: {2}", path, loadDone, sb.ToString());
}
private RefData FindRefData(object owner)
{
for (int i = 0, length = _referenceList.Count; i < length; ++i)
{
RefData refData = _referenceList[i];
if (refData.owner.Equals(owner))
{
return refData;
}
}
return null;
}
public void DestroyAllReference()
{
_referenceList.Clear();
}
public void DestroyReference(object owner)
{
RefData refData = FindRefData(owner);
Assert.IsNotNull(refData);
Assert.IsTrue(refData.internalRefList.Count > 0);
refData.internalRefList.RemoveAt(0); // remove first internal id
if (refData.internalRefList.Count == 0)
{
bool removed = _referenceList.Remove(refData);
Assert.IsTrue(removed, string.Format("remove {0} is not exist", owner));
}
}
public void AddReference(object owner, int id)
{
RefData refData = FindRefData(owner);
if (refData == null)
{
refData = new RefData { owner = owner };
refData.internalRefList.Add(id);
_referenceList.Add(refData);
}
else
{
Assert.IsNotNull(refData.internalRefList);
Assert.IsFalse(refData.internalRefList.Contains(id));
refData.internalRefList.Add(id);
}
}
public bool IsAllDestroyed()
{
return _referenceList.Count == 0;
}
public bool IsContainTarget(object owner, int id)
{
RefData refData = FindRefData(owner);
return refData != null && refData.internalRefList.Contains(id);
}
public int GetReferenceCount()
{
int refCount = 0;
for (int i = 0, length = _referenceList.Count; i < length; ++i)
{
refCount += _referenceList[i].internalRefList.Count;
}
return refCount;
}
}
private Dictionary<string, WWWWrapper> _wwwWrappers = new Dictionary<string, WWWWrapper>();
public int GetWWWWrappersCount(string path)
{
WWWWrapper wrapper;
if (_wwwWrappers.TryGetValue(path, out wrapper))
{
return wrapper.GetReferenceCount();
}
else
{
return 0;
}
}
public void DestroyWWW(string path, object owner)
{
WWWWrapper wrapper;
if (_wwwWrappers.TryGetValue(path, out wrapper))
{
wrapper.DestroyReference(owner);
if (wrapper.IsAllDestroyed())
{
DestroyAllWWW(path);
}
}
else
{
throw new Exception(string.Format("DestroyWWW path: {0} owner: {1}", path, owner));
}
}
private void DestroyAllWWW(string path)
{
WWWWrapper wrapper = null;
if (_wwwWrappers.TryGetValue(path, out wrapper))
{
if (wrapper.loadDone)
{
wrapper.asset.Destroy();
_wwwWrappers.Remove(path);
}
else
{
wrapper.DestroyAllReference();
}
}
else
{
throw new Exception("WWW not found");
}
}
public IPromise<Texture> GetWWWTexture(string path, object owner)
{
WWWWrapper wwwWrapper = null;
int id = GetNextIDWWW();
if (_wwwWrappers.TryGetValue(path, out wwwWrapper))
{
wwwWrapper.AddReference(owner, id);
if (wwwWrapper.loadDone)
{
return RetriveWWW(wwwWrapper, path, owner, id);
}
else
{
return DelayRetriveWWW(wwwWrapper, path, owner, id);
}
}
else
{
return BrandNewLoadWWW(path, owner, id);
}
}
private IPromise<Texture> RetriveWWW(WWWWrapper wwwWrapper, string path, object owner, int id)
{
var promise = new Promise<Texture>();
var asset = wwwWrapper.asset.asset;
if (asset)
{
if (IsTargetDestroyed(wwwWrapper, owner, id))
{
promise.Reject(new TargetDestroyedException(null, owner));
}
else
{
Texture result = asset as Texture;
if (result)
{
promise.Resolve(result);
}
else
{
promise.Reject(new WWWAssetException(path));
}
}
}
else
{
promise.Reject(new UnknownException("WWW Asset dead: " + path));
}
return promise;
}
private void AddLoadDoneAction<T>(Promise<T> promise, WWWWrapper wwwWrapper, string path, object owner, int id) where T : Object
{
Action loadDoneAction = new Action(() =>
{
T asset = wwwWrapper.asset.asset as T;
if (wwwWrapper.IsAllDestroyed())
{
promise.Reject(new LoadDoneAndDestroyAllException());
}
else if (IsTargetDestroyed(wwwWrapper, owner, id))
{
promise.Reject(new TargetDestroyedException(null, owner));
}
else if (!wwwWrapper.IsContainTarget(owner, id))
{
promise.Reject(new LoadDoneAndDestroyMainException());
}
else
{
T result = asset;
if (result)
{
promise.Resolve(result);
}
else
{
promise.Reject(new WWWAssetException(path));
}
}
});
wwwWrapper.loadDoneAction.Add(loadDoneAction);
}
private IPromise<Texture> DelayRetriveWWW(WWWWrapper wwwWrapper, string path, object owner, int id)
{
var promise = new Promise<Texture>();
AddLoadDoneAction(promise, wwwWrapper, path, owner, id);
return promise;
}
private IPromise<Texture> BrandNewLoadWWW(string path, object owner, int id)
{
var promise = new Promise<Texture>();
var wwwWrapper = new WWWWrapper()
{
path = path,
asset = new WWWTexture(),
loadDone = false,
loadDoneAction = new List<Action>(DEFAULT_LIST_SIZE),
};
wwwWrapper.AddReference(owner, id);
AddLoadDoneAction(promise, wwwWrapper, path, owner, id);
_wwwWrappers.Add(path, wwwWrapper);
CoroutineStarter.Instance.StartCoroutine(GetWWWInternal(path));
return promise;
}
private IEnumerator GetWWWInternal(string path)
{
var www = new WWW(path);
while (!www.isDone)
{
yield return null;
}
if (string.IsNullOrEmpty(www.error))
{
var wrapper = _wwwWrappers[path];
Assert.IsNotNull(wrapper);
if (wrapper.IsAllDestroyed())
{
www.Dispose();
}
else
{
wrapper.asset.GenerateAsset(www);
www.Dispose();
}
wrapper.loadDone = true;
if (wrapper.loadDoneAction != null)
{
for (int i = 0, length = wrapper.loadDoneAction.Count; i < length; ++i)
{
wrapper.loadDoneAction[i]();
}
wrapper.loadDoneAction = null;
}
if (wrapper.IsAllDestroyed())
{
wrapper.asset.Destroy();
_wwwWrappers.Remove(path);
}
}
else
{
www.Dispose();
}
}
private static bool IsTargetDestroyed(WWWWrapper wrapper, object owner, int id)
{
return !wrapper.IsContainTarget(owner, id);
}
private static int _idWWW = 0;
private static int GetNextIDWWW()
{
_idWWW = _idWWW + 1;
return _idWWW;
}
public void DumpStatus()
{
foreach(var item in _wwwWrappers)
{
Debug.LogWarning(item.Value.ToString());
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment