mirror of
https://github.com/Alex-Rachel/TEngine.git
synced 2025-08-14 16:51:28 +00:00
把代码核心逻辑移入热更工程,热更工程生成编译后可以直接Copy dll 和 mdb文件到UnityAsset目录下
把代码核心逻辑移入热更工程,热更工程生成编译后可以直接Copy dll 和 mdb文件到UnityAsset目录下
This commit is contained in:
@@ -0,0 +1,130 @@
|
||||
using System.Collections.Generic;
|
||||
using TEngineCore.ListJson;
|
||||
using UnityEngine;
|
||||
|
||||
namespace TEngineCore
|
||||
{
|
||||
public abstract class BaseClientData
|
||||
{
|
||||
private string m_configName;
|
||||
|
||||
public void Init(string configName)
|
||||
{
|
||||
m_configName = configName;
|
||||
Load();
|
||||
}
|
||||
|
||||
public void Load()
|
||||
{
|
||||
string fullName = GetSaveUniqPrefix() + m_configName;
|
||||
var jsonString = PlayerPrefs.GetString(fullName);
|
||||
if (!string.IsNullOrEmpty(jsonString))
|
||||
{
|
||||
JsonData json = JsonHelper.Instance.Deserialize(jsonString);
|
||||
if (json != null)
|
||||
{
|
||||
Deserialize(json);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Save()
|
||||
{
|
||||
string fullName = GetSaveUniqPrefix() + m_configName;
|
||||
JsonData jsonData = new JsonData();
|
||||
Serialize(jsonData);
|
||||
|
||||
var jsonTex = JsonHelper.Instance.Serialize(jsonData);
|
||||
if (!string.IsNullOrEmpty(jsonTex))
|
||||
{
|
||||
PlayerPrefs.SetString(fullName, jsonTex);
|
||||
PlayerPrefs.Save();
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract void Serialize(JsonData json);
|
||||
|
||||
protected abstract void Deserialize(JsonData json);
|
||||
|
||||
private string GetSaveUniqPrefix()
|
||||
{
|
||||
string hashPath = UnityUtil.GetHashCodeByString(Application.dataPath).ToString();
|
||||
string uniqInstance = SystemInfo.deviceUniqueIdentifier;
|
||||
string uniqKey = hashPath + uniqInstance;
|
||||
return uniqKey;
|
||||
}
|
||||
}
|
||||
|
||||
public class SystemSaveData: BaseClientData
|
||||
{
|
||||
public int[] settingParams;
|
||||
public uint test;
|
||||
public float m_cameraDistance;
|
||||
|
||||
public SystemSaveData()
|
||||
{
|
||||
settingParams = new int[(int)SystemSaveType.Max];
|
||||
settingParams[(int) SystemSaveType.Lod] = 0;
|
||||
}
|
||||
|
||||
public enum SystemSaveType
|
||||
{
|
||||
Lod, // 同屏人数
|
||||
MusicOn, // 打开音乐
|
||||
SoundOn, // 打开音效
|
||||
Max,
|
||||
}
|
||||
|
||||
protected override void Serialize(JsonData json)
|
||||
{
|
||||
if (json == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected override void Deserialize(JsonData json)
|
||||
{
|
||||
if (json == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class ClientSaveData : TSingleton<ClientSaveData>
|
||||
{
|
||||
private Dictionary<string, BaseClientData> m_dictSaveData = new Dictionary<string, BaseClientData>();
|
||||
|
||||
public T GetSaveData<T>() where T : BaseClientData, new()
|
||||
{
|
||||
string typeName = typeof(T).Name;
|
||||
BaseClientData ret;
|
||||
if (!m_dictSaveData.TryGetValue(typeName, out ret))
|
||||
{
|
||||
ret = new T();
|
||||
ret.Init(typeName);
|
||||
m_dictSaveData.Add(typeName, ret);
|
||||
}
|
||||
return (T)ret;
|
||||
}
|
||||
|
||||
public void SaveAllData()
|
||||
{
|
||||
var enumerator = m_dictSaveData.GetEnumerator();
|
||||
while (enumerator.MoveNext())
|
||||
{
|
||||
enumerator.Current.Value.Save();
|
||||
}
|
||||
}
|
||||
|
||||
public SystemSaveData CurrentSystemSaveData
|
||||
{
|
||||
get
|
||||
{
|
||||
return GetSaveData<SystemSaveData>();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
117
TEngineHotUpdate/src/TEngineCore/Config/ResConfigUtil.cs
Normal file
117
TEngineHotUpdate/src/TEngineCore/Config/ResConfigUtil.cs
Normal file
@@ -0,0 +1,117 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using UnityEngine;
|
||||
|
||||
namespace TEngineCore
|
||||
{
|
||||
public class ResConfigUtil
|
||||
{
|
||||
private static StringBuilder m_strBuilder = new StringBuilder();
|
||||
private static readonly string m_split = "_";
|
||||
|
||||
#region 读取接口
|
||||
public static List<T> ReadConfigListRes<T>(string fileName)
|
||||
{
|
||||
string resPath = string.Format("Config/{0}.json",fileName);
|
||||
TextAsset jsonStr = TResources.Load<TextAsset>(resPath);
|
||||
if (jsonStr == null)
|
||||
{
|
||||
TLogger.LogError("读取Json配置数据失败:{0}", fileName);
|
||||
return null;
|
||||
}
|
||||
|
||||
List<T> list = new List<T>();
|
||||
var jsonData = JsonHelper.Instance.Deserialize<List<T>>(jsonStr.text);
|
||||
list = jsonData;
|
||||
return list;
|
||||
}
|
||||
|
||||
public static Dictionary<string, T> ReadConfigRes<T>(string fileName)
|
||||
{
|
||||
string resPath = string.Format("Config/{0}.json", fileName);
|
||||
TextAsset jsonStr = TResources.Load<TextAsset>(resPath);
|
||||
if (jsonStr == null)
|
||||
{
|
||||
TLogger.LogError("读取Json配置数据失败:{0}", fileName);
|
||||
return null;
|
||||
}
|
||||
|
||||
Dictionary<string, T> dic = new Dictionary<string, T>();
|
||||
var jsonData = JsonHelper.Instance.Deserialize<Dictionary<string, T>>(jsonStr.text);
|
||||
dic = jsonData;
|
||||
return dic;
|
||||
}
|
||||
|
||||
public static Dictionary<int, T> ReadConfigResIntKey<T>(string fileName)
|
||||
{
|
||||
string resPath = string.Format("Config/{0}.json", fileName);
|
||||
TextAsset jsonStr = TResources.Load<TextAsset>(resPath);
|
||||
if (jsonStr == null)
|
||||
{
|
||||
TLogger.LogError("读取Json配置数据失败:{0}", fileName);
|
||||
return null;
|
||||
}
|
||||
|
||||
Dictionary<int, T> dic = new Dictionary<int, T>();
|
||||
var jsonData = JsonHelper.Instance.Deserialize<Dictionary<int, T>>(jsonStr.text);
|
||||
dic = jsonData;
|
||||
return dic;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public static UInt64 Make64Key(uint key1, uint key2)
|
||||
{
|
||||
return (((UInt64)key1) << 32) | key2;
|
||||
}
|
||||
|
||||
public static string MakeStringKey(uint key1, uint key2, uint key3)
|
||||
{
|
||||
m_strBuilder.Length = 0;
|
||||
m_strBuilder.Append(key1);
|
||||
m_strBuilder.Append(m_split);
|
||||
m_strBuilder.Append(key2);
|
||||
m_strBuilder.Append(m_split);
|
||||
m_strBuilder.Append(key3);
|
||||
return m_strBuilder.ToString();
|
||||
}
|
||||
|
||||
public static string MakeStringKey(string key1, uint key2)
|
||||
{
|
||||
m_strBuilder.Length = 0;
|
||||
m_strBuilder.Append(key1);
|
||||
m_strBuilder.Append(m_split);
|
||||
m_strBuilder.Append(key2);
|
||||
return m_strBuilder.ToString();
|
||||
}
|
||||
|
||||
public static string MakeStringKey(string key1, string key2)
|
||||
{
|
||||
m_strBuilder.Length = 0;
|
||||
m_strBuilder.Append(key1);
|
||||
m_strBuilder.Append(m_split);
|
||||
m_strBuilder.Append(key2);
|
||||
return m_strBuilder.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
*
|
||||
===》 example 《===
|
||||
public class BufferMgr : Singleton<BufferMgr>
|
||||
{
|
||||
private Dictionary<string, BuffConfig> m_dictBaseConfig = new Dictionary<string, BuffConfig>();
|
||||
|
||||
public BufferMgr()
|
||||
{
|
||||
m_dictBaseConfig = ResConfigUtil.ReadConfigRes<BuffConfig>("BuffConfig");
|
||||
}
|
||||
|
||||
public Dictionary<string, BuffConfig> GetBuffConfig()
|
||||
{
|
||||
return m_dictBaseConfig;
|
||||
}
|
||||
}
|
||||
*
|
||||
*/
|
74
TEngineHotUpdate/src/TEngineCore/Core/BaseLogicSys.cs
Normal file
74
TEngineHotUpdate/src/TEngineCore/Core/BaseLogicSys.cs
Normal file
@@ -0,0 +1,74 @@
|
||||
namespace TEngineCore
|
||||
{
|
||||
/// <summary>
|
||||
/// 基础LogicSys,生命周期由TEngine实现,推荐给系统实现,
|
||||
/// 减少多余的Mono,保持系统层面只有一个Update
|
||||
/// 用TEngine的主Mono来驱动LogicSys的生命周期
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
public class BaseLogicSys<T> : ILogicSys where T : new()
|
||||
{
|
||||
private static T m_Instance;
|
||||
|
||||
public static bool HasInstance
|
||||
{
|
||||
get { return m_Instance != null; }
|
||||
}
|
||||
public static T Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (null == m_Instance)
|
||||
{
|
||||
m_Instance = new T();
|
||||
}
|
||||
|
||||
return m_Instance;
|
||||
}
|
||||
}
|
||||
|
||||
#region virtual fucntion
|
||||
public virtual bool OnInit()
|
||||
{
|
||||
if (null == m_Instance)
|
||||
{
|
||||
m_Instance = new T();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public virtual void OnStart()
|
||||
{
|
||||
}
|
||||
|
||||
public virtual void OnUpdate()
|
||||
{
|
||||
}
|
||||
|
||||
public virtual void OnLateUpdate()
|
||||
{
|
||||
}
|
||||
|
||||
public virtual void OnDestroy()
|
||||
{
|
||||
}
|
||||
|
||||
public virtual void OnPause()
|
||||
{
|
||||
}
|
||||
|
||||
public virtual void OnResume()
|
||||
{
|
||||
}
|
||||
|
||||
public virtual void OnDrawGizmos()
|
||||
{
|
||||
}
|
||||
|
||||
public virtual void OnMapChanged()
|
||||
{
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
}
|
20
TEngineHotUpdate/src/TEngineCore/Core/ILogicSys.cs
Normal file
20
TEngineHotUpdate/src/TEngineCore/Core/ILogicSys.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
namespace TEngineCore
|
||||
{
|
||||
public interface ILogicSys
|
||||
{
|
||||
bool OnInit();
|
||||
|
||||
void OnDestroy();
|
||||
|
||||
void OnStart();
|
||||
|
||||
void OnUpdate();
|
||||
|
||||
void OnLateUpdate();
|
||||
|
||||
void OnPause();
|
||||
|
||||
void OnResume();
|
||||
}
|
||||
|
||||
}
|
90
TEngineHotUpdate/src/TEngineCore/Core/ListPool.cs
Normal file
90
TEngineHotUpdate/src/TEngineCore/Core/ListPool.cs
Normal file
@@ -0,0 +1,90 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine.Events;
|
||||
|
||||
namespace TEngineCore
|
||||
{
|
||||
public static class ListPool<T>
|
||||
{
|
||||
private static readonly ObjectPool<List<T>> m_ListPool = new ObjectPool<List<T>>(null, Clear);
|
||||
|
||||
static void Clear(List<T> list)
|
||||
{
|
||||
list.Clear();
|
||||
}
|
||||
|
||||
public static List<T> Get()
|
||||
{
|
||||
return m_ListPool.Get();
|
||||
}
|
||||
|
||||
public static void Release(List<T> toRelease)
|
||||
{
|
||||
m_ListPool.Release(toRelease);
|
||||
}
|
||||
}
|
||||
|
||||
public class ObjectPool<T> where T : new()
|
||||
{
|
||||
private readonly Stack<T> m_Stack = new Stack<T>();
|
||||
private readonly UnityAction<T> m_ActionGet;
|
||||
private readonly UnityAction<T> m_ActionRelease;
|
||||
|
||||
public int CountAll { get; private set; }
|
||||
public int CountActive
|
||||
{
|
||||
get
|
||||
{
|
||||
return CountAll - CountInActive;
|
||||
}
|
||||
}
|
||||
|
||||
public int CountInActive
|
||||
{
|
||||
get
|
||||
{
|
||||
return m_Stack.Count;
|
||||
}
|
||||
}
|
||||
|
||||
public ObjectPool()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public ObjectPool(UnityAction<T> actionGet, UnityAction<T> actionRelease)
|
||||
{
|
||||
m_ActionGet = actionGet;
|
||||
m_ActionRelease = actionRelease;
|
||||
}
|
||||
|
||||
public T Get()
|
||||
{
|
||||
T element;
|
||||
if (m_Stack.Count <= 0)
|
||||
{
|
||||
element = new T();
|
||||
CountAll++;
|
||||
}
|
||||
else
|
||||
{
|
||||
element = m_Stack.Pop();
|
||||
}
|
||||
|
||||
if (m_ActionGet != null)
|
||||
{
|
||||
m_ActionGet.Invoke(element);
|
||||
}
|
||||
return element;
|
||||
}
|
||||
|
||||
public void Release(T element)
|
||||
{
|
||||
if (m_Stack.Count > 0 && ReferenceEquals(m_Stack.Peek(), element))
|
||||
{
|
||||
TLogger.LogError("Internal error. Trying to destroy object that is already released to pool.");
|
||||
}
|
||||
m_ActionRelease?.Invoke(element);
|
||||
m_Stack.Push(element);
|
||||
}
|
||||
}
|
||||
}
|
125
TEngineHotUpdate/src/TEngineCore/Core/MemPoolMgr.cs
Normal file
125
TEngineHotUpdate/src/TEngineCore/Core/MemPoolMgr.cs
Normal file
@@ -0,0 +1,125 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using Debug = UnityEngine.Debug;
|
||||
|
||||
namespace TEngineCore
|
||||
{
|
||||
public interface IMemPoolObject
|
||||
{
|
||||
void Init();
|
||||
void Destroy();
|
||||
}
|
||||
|
||||
public interface MemPoolBase
|
||||
{
|
||||
string GetName();
|
||||
int GetPoolItemCount();
|
||||
void ClearPool();
|
||||
}
|
||||
|
||||
public class MemPool
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
public class MemPoolMgr : TSingleton<MemPoolMgr>
|
||||
{
|
||||
List<MemPoolBase> m_listPool = new List<MemPoolBase>();
|
||||
|
||||
[Conditional("TEngine_DEBUG")]
|
||||
public void ShowCount()
|
||||
{
|
||||
int totalCnt = 0;
|
||||
for (int i = 0; i < m_listPool.Count; i++)
|
||||
{
|
||||
var pool = m_listPool[i];
|
||||
totalCnt += pool.GetPoolItemCount();
|
||||
TLogger.LogInfo("[pool][{0}] [{1}]", pool.GetName(), pool.GetPoolItemCount());
|
||||
}
|
||||
TLogger.LogInfo("-------------------------memory pool count: {0}", totalCnt);
|
||||
}
|
||||
|
||||
public void RegMemPool(MemPoolBase pool)
|
||||
{
|
||||
m_listPool.Add(pool);
|
||||
}
|
||||
|
||||
public void ClearAllPool()
|
||||
{
|
||||
for (int i = 0; i < m_listPool.Count; i++)
|
||||
{
|
||||
var pool = m_listPool[i];
|
||||
pool.ClearPool();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class GameMemPool<T> : TSingleton<GameMemPool<T>>, MemPoolBase where T : IMemPoolObject, new()
|
||||
{
|
||||
private List<T> m_objPool = new List<T>();
|
||||
|
||||
public static T Alloc()
|
||||
{
|
||||
return GameMemPool<T>.Instance.DoAlloc();
|
||||
}
|
||||
|
||||
public static void Free(T obj)
|
||||
{
|
||||
GameMemPool<T>.Instance.DoFree(obj);
|
||||
}
|
||||
|
||||
public GameMemPool()
|
||||
{
|
||||
MemPoolMgr.Instance.RegMemPool(this);
|
||||
}
|
||||
|
||||
private T DoAlloc()
|
||||
{
|
||||
T newObj;
|
||||
if (m_objPool.Count > 0)
|
||||
{
|
||||
var lastIndex = m_objPool.Count - 1;
|
||||
newObj = m_objPool[lastIndex];
|
||||
m_objPool.RemoveAt(lastIndex);
|
||||
}
|
||||
else
|
||||
{
|
||||
newObj = new T();
|
||||
}
|
||||
|
||||
newObj.Init();
|
||||
return newObj;
|
||||
}
|
||||
|
||||
private void DoFree(T obj)
|
||||
{
|
||||
if (obj == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
obj.Destroy();
|
||||
m_objPool.Add(obj);
|
||||
}
|
||||
|
||||
public void ClearPool()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
TLogger.LogInfo("clear memory[{0}] count[{1}]", GetName(), m_objPool.Count);
|
||||
#endif
|
||||
m_objPool.Clear();
|
||||
}
|
||||
|
||||
public string GetName()
|
||||
{
|
||||
return typeof(T).FullName;
|
||||
}
|
||||
|
||||
public int GetPoolItemCount()
|
||||
{
|
||||
return m_objPool.Count;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
28
TEngineHotUpdate/src/TEngineCore/Core/Singleton.cs
Normal file
28
TEngineHotUpdate/src/TEngineCore/Core/Singleton.cs
Normal file
@@ -0,0 +1,28 @@
|
||||
namespace TEngineCore
|
||||
{
|
||||
/// <summary>
|
||||
/// 通用单例,无需释放和销毁
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
public class Singleton<T> where T : new()
|
||||
{
|
||||
private static T _instance;
|
||||
|
||||
public static T Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_instance == null)
|
||||
{
|
||||
_instance = new T();
|
||||
}
|
||||
return _instance;
|
||||
}
|
||||
}
|
||||
|
||||
public static T Active()
|
||||
{
|
||||
return Instance;
|
||||
}
|
||||
}
|
||||
}
|
149
TEngineHotUpdate/src/TEngineCore/Core/TEngine.cs
Normal file
149
TEngineHotUpdate/src/TEngineCore/Core/TEngine.cs
Normal file
@@ -0,0 +1,149 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace TEngineCore
|
||||
{
|
||||
/// <summary>
|
||||
/// 游戏入口,派生TEngine,实现进入游戏虚函数(override StartGame)
|
||||
/// </summary>
|
||||
public class TEngine : UnitySingleton<TEngine>
|
||||
{
|
||||
public override void Awake()
|
||||
{
|
||||
base.Awake();
|
||||
|
||||
TLogger.LogInfo($"DevicePerformanceLevel 设备性能评级:{DevicePerformanceUtil.GetDevicePerformanceLevel()}");
|
||||
|
||||
InitLibImp();
|
||||
|
||||
RegisterAllSystem();
|
||||
|
||||
AfterAwake();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 注册实现库
|
||||
/// </summary>
|
||||
protected virtual void InitLibImp()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 注册系统(例如BaseLogic/TEngineObject/MonoManger)
|
||||
/// </summary>
|
||||
protected virtual void RegisterAllSystem()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
protected void SetTargetFrameRate(int targetFrameRate)
|
||||
{
|
||||
Application.targetFrameRate = targetFrameRate;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------系统注册--------------------------------------------------------//
|
||||
private List<ILogicSys> m_LogicMgrList = new List<ILogicSys>();
|
||||
|
||||
/// <summary>
|
||||
/// 系统注册
|
||||
/// </summary>
|
||||
/// <param name="logicSys"></param>
|
||||
/// <returns></returns>
|
||||
protected bool AddLogicSys(ILogicSys logicSys)
|
||||
{
|
||||
if (m_LogicMgrList.Contains(logicSys))
|
||||
{
|
||||
TLogger.LogInfo("Repeat add logic system: " + logicSys.GetType().Name);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!logicSys.OnInit())
|
||||
{
|
||||
TLogger.LogInfo(" Init failed " + logicSys.GetType().Name);
|
||||
return false;
|
||||
}
|
||||
|
||||
m_LogicMgrList.Add(logicSys);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#region 生命周期
|
||||
public void Start()
|
||||
{
|
||||
var listLogic = m_LogicMgrList;
|
||||
var logicCnt = listLogic.Count;
|
||||
for (int i = 0; i < logicCnt; i++)
|
||||
{
|
||||
var logic = listLogic[i];
|
||||
logic.OnStart();
|
||||
}
|
||||
TLogger.LogInfo("TEngine:StartGame");
|
||||
StartGame();
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
var listLogic = m_LogicMgrList;
|
||||
var logicCnt = listLogic.Count;
|
||||
for (int i = 0; i < logicCnt; i++)
|
||||
{
|
||||
var logic = listLogic[i];
|
||||
logic.OnUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
public void LateUpdate()
|
||||
{
|
||||
var listLogic = m_LogicMgrList;
|
||||
var logicCnt = listLogic.Count;
|
||||
for (int i = 0; i < logicCnt; i++)
|
||||
{
|
||||
var logic = listLogic[i];
|
||||
logic.OnLateUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
public void OnPause()
|
||||
{
|
||||
for (int i = 0; i < m_LogicMgrList.Count; i++)
|
||||
{
|
||||
var logicSys = m_LogicMgrList[i];
|
||||
logicSys.OnPause();
|
||||
}
|
||||
}
|
||||
|
||||
public void OnResume()
|
||||
{
|
||||
for (int i = 0; i < m_LogicMgrList.Count; i++)
|
||||
{
|
||||
var logicSys = m_LogicMgrList[i];
|
||||
logicSys.OnResume();
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnDestroy()
|
||||
{
|
||||
for (int i = 0; i < m_LogicMgrList.Count; i++)
|
||||
{
|
||||
var logicSys = m_LogicMgrList[i];
|
||||
logicSys.OnDestroy();
|
||||
}
|
||||
base.OnDestroy();
|
||||
SingletonMgr.Release();
|
||||
}
|
||||
|
||||
protected virtual void AfterAwake()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
protected virtual void StartGame()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
880
TEngineHotUpdate/src/TEngineCore/Core/TEngineRedux.cs
Normal file
880
TEngineHotUpdate/src/TEngineCore/Core/TEngineRedux.cs
Normal file
@@ -0,0 +1,880 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace TEngineCore
|
||||
{
|
||||
#region Architecture
|
||||
|
||||
public interface IArchitecture
|
||||
{
|
||||
void RegisterSystem<T>(T system) where T : ISystem;
|
||||
|
||||
void RegisterModel<T>(T model) where T : IModel;
|
||||
|
||||
void RegisterUtility<T>(T utility) where T : IUtility;
|
||||
|
||||
T GetSystem<T>() where T : class, ISystem;
|
||||
|
||||
T GetModel<T>() where T : class, IModel;
|
||||
|
||||
T GetUtility<T>() where T : class, IUtility;
|
||||
|
||||
void SendCommand<T>() where T : ICommand, new();
|
||||
|
||||
void SendCommand<T>(T command) where T : ICommand;
|
||||
|
||||
TResult SendQuery<TResult>(IQuery<TResult> query);
|
||||
|
||||
void SendEvent<T>() where T : new();
|
||||
|
||||
void SendEvent<T>(T e);
|
||||
|
||||
IUnRegister RegisterEvent<T>(Action<T> onEvent);
|
||||
|
||||
void UnRegisterEvent<T>(Action<T> onEvent);
|
||||
}
|
||||
|
||||
public abstract class Architecture<T> : IArchitecture where T : Architecture<T>, new()
|
||||
{
|
||||
private bool _Inited = false;
|
||||
|
||||
private List<ISystem> _Systems = new List<ISystem>();
|
||||
|
||||
private List<IModel> _Models = new List<IModel>();
|
||||
|
||||
public static Action<T> OnRegisterPatch = architecture => { };
|
||||
|
||||
private static T _Architecture;
|
||||
|
||||
public static IArchitecture Interface
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_Architecture == null)
|
||||
{
|
||||
MakeSureArchitecture();
|
||||
}
|
||||
|
||||
return _Architecture;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void MakeSureArchitecture()
|
||||
{
|
||||
if (_Architecture == null)
|
||||
{
|
||||
_Architecture = new T();
|
||||
_Architecture.Init();
|
||||
|
||||
OnRegisterPatch?.Invoke(_Architecture);
|
||||
|
||||
foreach (var architectureModel in _Architecture._Models)
|
||||
{
|
||||
architectureModel.Init();
|
||||
}
|
||||
|
||||
_Architecture._Models.Clear();
|
||||
|
||||
foreach (var architectureSystem in _Architecture._Systems)
|
||||
{
|
||||
architectureSystem.Init();
|
||||
}
|
||||
|
||||
_Architecture._Systems.Clear();
|
||||
|
||||
_Architecture._Inited = true;
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract void Init();
|
||||
|
||||
private TEngineContainer mContainer = new TEngineContainer();
|
||||
|
||||
public void RegisterSystem<TSystem>(TSystem system) where TSystem : ISystem
|
||||
{
|
||||
system.SetArchitecture(this);
|
||||
mContainer.Register<TSystem>(system);
|
||||
|
||||
if (!_Inited)
|
||||
{
|
||||
_Systems.Add(system);
|
||||
}
|
||||
else
|
||||
{
|
||||
system.Init();
|
||||
}
|
||||
}
|
||||
|
||||
public void RegisterModel<TModel>(TModel model) where TModel : IModel
|
||||
{
|
||||
model.SetArchitecture(this);
|
||||
mContainer.Register<TModel>(model);
|
||||
|
||||
if (!_Inited)
|
||||
{
|
||||
_Models.Add(model);
|
||||
}
|
||||
else
|
||||
{
|
||||
model.Init();
|
||||
}
|
||||
}
|
||||
|
||||
public void RegisterUtility<TUtility>(TUtility utility) where TUtility : IUtility
|
||||
{
|
||||
mContainer.Register<TUtility>(utility);
|
||||
}
|
||||
|
||||
public TSystem GetSystem<TSystem>() where TSystem : class, ISystem
|
||||
{
|
||||
return mContainer.Get<TSystem>();
|
||||
}
|
||||
|
||||
public TModel GetModel<TModel>() where TModel : class, IModel
|
||||
{
|
||||
return mContainer.Get<TModel>();
|
||||
}
|
||||
|
||||
public TUtility GetUtility<TUtility>() where TUtility : class, IUtility
|
||||
{
|
||||
return mContainer.Get<TUtility>();
|
||||
}
|
||||
|
||||
public void SendCommand<TCommand>() where TCommand : ICommand, new()
|
||||
{
|
||||
var command = new TCommand();
|
||||
command.SetArchitecture(this);
|
||||
command.Execute();
|
||||
}
|
||||
|
||||
public void SendCommand<TCommand>(TCommand command) where TCommand : ICommand
|
||||
{
|
||||
command.SetArchitecture(this);
|
||||
command.Execute();
|
||||
}
|
||||
|
||||
public TResult SendQuery<TResult>(IQuery<TResult> query)
|
||||
{
|
||||
query.SetArchitecture(this);
|
||||
return query.Do();
|
||||
}
|
||||
|
||||
private TypeEventSystem mTypeEventSystem = new TypeEventSystem();
|
||||
|
||||
public void SendEvent<TEvent>() where TEvent : new()
|
||||
{
|
||||
mTypeEventSystem.Send<TEvent>();
|
||||
}
|
||||
|
||||
public void SendEvent<TEvent>(TEvent e)
|
||||
{
|
||||
mTypeEventSystem.Send<TEvent>(e);
|
||||
}
|
||||
|
||||
public IUnRegister RegisterEvent<TEvent>(Action<TEvent> onEvent)
|
||||
{
|
||||
return mTypeEventSystem.Register<TEvent>(onEvent);
|
||||
}
|
||||
|
||||
public void UnRegisterEvent<TEvent>(Action<TEvent> onEvent)
|
||||
{
|
||||
mTypeEventSystem.UnRegister<TEvent>(onEvent);
|
||||
}
|
||||
}
|
||||
|
||||
public interface IOnEvent<T>
|
||||
{
|
||||
void OnEvent(T e);
|
||||
}
|
||||
|
||||
public static class OnGlobalEventExtension
|
||||
{
|
||||
public static IUnRegister RegisterEvent<T>(this IOnEvent<T> self) where T : struct
|
||||
{
|
||||
return TypeEventSystem.Global.Register<T>(self.OnEvent);
|
||||
}
|
||||
|
||||
public static void UnRegisterEvent<T>(this IOnEvent<T> self) where T : struct
|
||||
{
|
||||
TypeEventSystem.Global.UnRegister<T>(self.OnEvent);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Controller
|
||||
|
||||
public interface IController : IBelongToArchitecture, ICanSendCommand, ICanGetSystem, ICanGetModel,
|
||||
ICanRegisterEvent, ICanSendQuery
|
||||
{
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region System
|
||||
|
||||
public interface ISystem : IBelongToArchitecture, ICanSetArchitecture, ICanGetModel, ICanGetUtility,
|
||||
ICanRegisterEvent, ICanSendEvent, ICanGetSystem
|
||||
{
|
||||
void Init();
|
||||
}
|
||||
|
||||
public abstract class AbstractSystem : ISystem
|
||||
{
|
||||
private IArchitecture _Architecture;
|
||||
|
||||
IArchitecture IBelongToArchitecture.GetArchitecture()
|
||||
{
|
||||
return _Architecture;
|
||||
}
|
||||
|
||||
void ICanSetArchitecture.SetArchitecture(IArchitecture architecture)
|
||||
{
|
||||
_Architecture = architecture;
|
||||
}
|
||||
|
||||
void ISystem.Init()
|
||||
{
|
||||
OnInit();
|
||||
}
|
||||
|
||||
protected abstract void OnInit();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Model
|
||||
|
||||
public interface IModel : IBelongToArchitecture, ICanSetArchitecture, ICanGetUtility, ICanSendEvent
|
||||
{
|
||||
void Init();
|
||||
}
|
||||
|
||||
public abstract class AbstractModel : IModel
|
||||
{
|
||||
private IArchitecture _Architecturel;
|
||||
|
||||
IArchitecture IBelongToArchitecture.GetArchitecture()
|
||||
{
|
||||
return _Architecturel;
|
||||
}
|
||||
|
||||
void ICanSetArchitecture.SetArchitecture(IArchitecture architecture)
|
||||
{
|
||||
_Architecturel = architecture;
|
||||
}
|
||||
|
||||
void IModel.Init()
|
||||
{
|
||||
OnInit();
|
||||
}
|
||||
|
||||
protected abstract void OnInit();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Utility
|
||||
|
||||
public interface IUtility
|
||||
{
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Command
|
||||
|
||||
public interface ICommand : IBelongToArchitecture, ICanSetArchitecture, ICanGetSystem, ICanGetModel, ICanGetUtility,
|
||||
ICanSendEvent, ICanSendCommand, ICanSendQuery
|
||||
{
|
||||
void Execute();
|
||||
}
|
||||
|
||||
public abstract class AbstractCommand : ICommand
|
||||
{
|
||||
private IArchitecture _Architecture;
|
||||
|
||||
IArchitecture IBelongToArchitecture.GetArchitecture()
|
||||
{
|
||||
return _Architecture;
|
||||
}
|
||||
|
||||
void ICanSetArchitecture.SetArchitecture(IArchitecture architecture)
|
||||
{
|
||||
_Architecture = architecture;
|
||||
}
|
||||
|
||||
void ICommand.Execute()
|
||||
{
|
||||
OnExecute();
|
||||
}
|
||||
|
||||
protected abstract void OnExecute();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Query
|
||||
|
||||
public interface IQuery<TResult> : IBelongToArchitecture, ICanSetArchitecture, ICanGetModel, ICanGetSystem,
|
||||
ICanSendQuery
|
||||
{
|
||||
TResult Do();
|
||||
}
|
||||
|
||||
public abstract class AbstractQuery<T> : IQuery<T>
|
||||
{
|
||||
public T Do()
|
||||
{
|
||||
return OnDo();
|
||||
}
|
||||
|
||||
protected abstract T OnDo();
|
||||
|
||||
|
||||
private IArchitecture _Architecture;
|
||||
|
||||
public IArchitecture GetArchitecture()
|
||||
{
|
||||
return _Architecture;
|
||||
}
|
||||
|
||||
public void SetArchitecture(IArchitecture architecture)
|
||||
{
|
||||
_Architecture = architecture;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Rule
|
||||
|
||||
public interface IBelongToArchitecture
|
||||
{
|
||||
IArchitecture GetArchitecture();
|
||||
}
|
||||
|
||||
public interface ICanSetArchitecture
|
||||
{
|
||||
void SetArchitecture(IArchitecture architecture);
|
||||
}
|
||||
|
||||
public interface ICanGetModel : IBelongToArchitecture
|
||||
{
|
||||
}
|
||||
|
||||
public static class CanGetModelExtension
|
||||
{
|
||||
public static T GetModel<T>(this ICanGetModel self) where T : class, IModel
|
||||
{
|
||||
return self.GetArchitecture().GetModel<T>();
|
||||
}
|
||||
}
|
||||
|
||||
public interface ICanGetSystem : IBelongToArchitecture
|
||||
{
|
||||
}
|
||||
|
||||
public static class CanGetSystemExtension
|
||||
{
|
||||
public static T GetSystem<T>(this ICanGetSystem self) where T : class, ISystem
|
||||
{
|
||||
return self.GetArchitecture().GetSystem<T>();
|
||||
}
|
||||
}
|
||||
|
||||
public interface ICanGetUtility : IBelongToArchitecture
|
||||
{
|
||||
}
|
||||
|
||||
public static class CanGetUtilityExtension
|
||||
{
|
||||
public static T GetUtility<T>(this ICanGetUtility self) where T : class, IUtility
|
||||
{
|
||||
return self.GetArchitecture().GetUtility<T>();
|
||||
}
|
||||
}
|
||||
|
||||
public interface ICanRegisterEvent : IBelongToArchitecture
|
||||
{
|
||||
}
|
||||
|
||||
public static class CanRegisterEventExtension
|
||||
{
|
||||
public static IUnRegister RegisterEvent<T>(this ICanRegisterEvent self, Action<T> onEvent)
|
||||
{
|
||||
return self.GetArchitecture().RegisterEvent<T>(onEvent);
|
||||
}
|
||||
|
||||
public static void UnRegisterEvent<T>(this ICanRegisterEvent self, Action<T> onEvent)
|
||||
{
|
||||
self.GetArchitecture().UnRegisterEvent<T>(onEvent);
|
||||
}
|
||||
}
|
||||
|
||||
public interface ICanSendCommand : IBelongToArchitecture
|
||||
{
|
||||
}
|
||||
|
||||
public static class CanSendCommandExtension
|
||||
{
|
||||
public static void SendCommand<T>(this ICanSendCommand self) where T : ICommand, new()
|
||||
{
|
||||
self.GetArchitecture().SendCommand<T>();
|
||||
}
|
||||
|
||||
public static void SendCommand<T>(this ICanSendCommand self, T command) where T : ICommand
|
||||
{
|
||||
self.GetArchitecture().SendCommand<T>(command);
|
||||
}
|
||||
}
|
||||
|
||||
public interface ICanSendEvent : IBelongToArchitecture
|
||||
{
|
||||
}
|
||||
|
||||
public static class CanSendEventExtension
|
||||
{
|
||||
public static void SendEvent<T>(this ICanSendEvent self) where T : new()
|
||||
{
|
||||
self.GetArchitecture().SendEvent<T>();
|
||||
}
|
||||
|
||||
public static void SendEvent<T>(this ICanSendEvent self, T e)
|
||||
{
|
||||
self.GetArchitecture().SendEvent<T>(e);
|
||||
}
|
||||
}
|
||||
|
||||
public interface ICanSendQuery : IBelongToArchitecture
|
||||
{
|
||||
}
|
||||
|
||||
public static class CanSendQueryExtension
|
||||
{
|
||||
public static TResult SendQuery<TResult>(this ICanSendQuery self, IQuery<TResult> query)
|
||||
{
|
||||
return self.GetArchitecture().SendQuery(query);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region TypeEventSystem
|
||||
|
||||
public interface IUnRegister
|
||||
{
|
||||
void UnRegister();
|
||||
}
|
||||
|
||||
public interface IUnRegisterList
|
||||
{
|
||||
List<IUnRegister> UnregisterList { get; }
|
||||
}
|
||||
|
||||
public static class IUnRegisterListExtension
|
||||
{
|
||||
public static void AddToUnregisterList(this IUnRegister self, IUnRegisterList unRegisterList)
|
||||
{
|
||||
unRegisterList.UnregisterList.Add(self);
|
||||
}
|
||||
|
||||
public static void UnRegisterAll(this IUnRegisterList self)
|
||||
{
|
||||
foreach (var unRegister in self.UnregisterList)
|
||||
{
|
||||
unRegister.UnRegister();
|
||||
}
|
||||
|
||||
self.UnregisterList.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 自定义可注销的类
|
||||
/// </summary>
|
||||
public struct CustomUnRegister : IUnRegister
|
||||
{
|
||||
/// <summary>
|
||||
/// 委托对象
|
||||
/// </summary>
|
||||
private Action mOnUnRegister { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 带参构造函数
|
||||
/// </summary>
|
||||
/// <param name="onDispose"></param>
|
||||
public CustomUnRegister(Action onUnRegsiter)
|
||||
{
|
||||
mOnUnRegister = onUnRegsiter;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 资源释放
|
||||
/// </summary>
|
||||
public void UnRegister()
|
||||
{
|
||||
mOnUnRegister.Invoke();
|
||||
mOnUnRegister = null;
|
||||
}
|
||||
}
|
||||
|
||||
public class UnRegisterOnDestroyTrigger : MonoBehaviour
|
||||
{
|
||||
private readonly HashSet<IUnRegister> mUnRegisters = new HashSet<IUnRegister>();
|
||||
|
||||
public void AddUnRegister(IUnRegister unRegister)
|
||||
{
|
||||
mUnRegisters.Add(unRegister);
|
||||
}
|
||||
|
||||
public void RemoveUnRegister(IUnRegister unRegister)
|
||||
{
|
||||
mUnRegisters.Remove(unRegister);
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
foreach (var unRegister in mUnRegisters)
|
||||
{
|
||||
unRegister.UnRegister();
|
||||
}
|
||||
|
||||
mUnRegisters.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
public static class UnRegisterExtension
|
||||
{
|
||||
public static IUnRegister UnRegisterWhenGameObjectDestroyed(this IUnRegister unRegister, GameObject gameObject)
|
||||
{
|
||||
var trigger = gameObject.GetComponent<UnRegisterOnDestroyTrigger>();
|
||||
|
||||
if (!trigger)
|
||||
{
|
||||
trigger = gameObject.AddComponent<UnRegisterOnDestroyTrigger>();
|
||||
}
|
||||
|
||||
trigger.AddUnRegister(unRegister);
|
||||
|
||||
return unRegister;
|
||||
}
|
||||
}
|
||||
|
||||
public class TypeEventSystem
|
||||
{
|
||||
private readonly EasyEvents mEvents = new EasyEvents();
|
||||
|
||||
|
||||
public static readonly TypeEventSystem Global = new TypeEventSystem();
|
||||
|
||||
public void Send<T>() where T : new()
|
||||
{
|
||||
mEvents.GetEvent<EasyEvent<T>>()?.Trigger(new T());
|
||||
}
|
||||
|
||||
public void Send<T>(T e)
|
||||
{
|
||||
mEvents.GetEvent<EasyEvent<T>>()?.Trigger(e);
|
||||
}
|
||||
|
||||
public IUnRegister Register<T>(Action<T> onEvent)
|
||||
{
|
||||
var e = mEvents.GetOrAddEvent<EasyEvent<T>>();
|
||||
return e.Register(onEvent);
|
||||
}
|
||||
|
||||
public void UnRegister<T>(Action<T> onEvent)
|
||||
{
|
||||
var e = mEvents.GetEvent<EasyEvent<T>>();
|
||||
if (e != null)
|
||||
{
|
||||
e.UnRegister(onEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region TEngine
|
||||
|
||||
public class TEngineContainer
|
||||
{
|
||||
private Dictionary<Type, object> _Instances = new Dictionary<Type, object>();
|
||||
|
||||
public void Register<T>(T instance)
|
||||
{
|
||||
var key = typeof(T);
|
||||
|
||||
if (_Instances.ContainsKey(key))
|
||||
{
|
||||
_Instances[key] = instance;
|
||||
}
|
||||
else
|
||||
{
|
||||
_Instances.Add(key, instance);
|
||||
}
|
||||
}
|
||||
|
||||
public T Get<T>() where T : class
|
||||
{
|
||||
var key = typeof(T);
|
||||
|
||||
if (_Instances.TryGetValue(key, out var retInstance))
|
||||
{
|
||||
return retInstance as T;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region BindableProperty
|
||||
|
||||
public interface IBindableProperty<T> : IReadonlyBindableProperty<T>
|
||||
{
|
||||
new T Value { get; set; }
|
||||
void SetValueWithoutEvent(T newValue);
|
||||
}
|
||||
|
||||
public interface IReadonlyBindableProperty<T>
|
||||
{
|
||||
T Value { get; }
|
||||
|
||||
IUnRegister RegisterWithInitValue(Action<T> action);
|
||||
void UnRegister(Action<T> onValueChanged);
|
||||
IUnRegister Register(Action<T> onValueChanged);
|
||||
}
|
||||
|
||||
public class BindableProperty<T> : IBindableProperty<T>
|
||||
{
|
||||
public BindableProperty(T defaultValue = default)
|
||||
{
|
||||
mValue = defaultValue;
|
||||
}
|
||||
|
||||
protected T mValue;
|
||||
|
||||
public T Value
|
||||
{
|
||||
get => GetValue();
|
||||
set
|
||||
{
|
||||
if (value == null && mValue == null) return;
|
||||
if (value != null && value.Equals(mValue)) return;
|
||||
|
||||
SetValue(value);
|
||||
mOnValueChanged?.Invoke(value);
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void SetValue(T newValue)
|
||||
{
|
||||
mValue = newValue;
|
||||
}
|
||||
|
||||
protected virtual T GetValue()
|
||||
{
|
||||
return mValue;
|
||||
}
|
||||
|
||||
public void SetValueWithoutEvent(T newValue)
|
||||
{
|
||||
mValue = newValue;
|
||||
}
|
||||
|
||||
private Action<T> mOnValueChanged = (v) => { };
|
||||
|
||||
public IUnRegister Register(Action<T> onValueChanged)
|
||||
{
|
||||
mOnValueChanged += onValueChanged;
|
||||
return new BindablePropertyUnRegister<T>()
|
||||
{
|
||||
BindableProperty = this,
|
||||
OnValueChanged = onValueChanged
|
||||
};
|
||||
}
|
||||
|
||||
public IUnRegister RegisterWithInitValue(Action<T> onValueChanged)
|
||||
{
|
||||
onValueChanged(mValue);
|
||||
return Register(onValueChanged);
|
||||
}
|
||||
|
||||
public static implicit operator T(BindableProperty<T> property)
|
||||
{
|
||||
return property.Value;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Value.ToString();
|
||||
}
|
||||
|
||||
public void UnRegister(Action<T> onValueChanged)
|
||||
{
|
||||
mOnValueChanged -= onValueChanged;
|
||||
}
|
||||
}
|
||||
|
||||
public class BindablePropertyUnRegister<T> : IUnRegister
|
||||
{
|
||||
public BindableProperty<T> BindableProperty { get; set; }
|
||||
|
||||
public Action<T> OnValueChanged { get; set; }
|
||||
|
||||
public void UnRegister()
|
||||
{
|
||||
BindableProperty.UnRegister(OnValueChanged);
|
||||
|
||||
BindableProperty = null;
|
||||
OnValueChanged = null;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region EasyEvent
|
||||
|
||||
public interface IEasyEvent
|
||||
{
|
||||
}
|
||||
|
||||
public class EasyEvent : IEasyEvent
|
||||
{
|
||||
private Action mOnEvent = () => { };
|
||||
|
||||
public IUnRegister Register(Action onEvent)
|
||||
{
|
||||
mOnEvent += onEvent;
|
||||
return new CustomUnRegister(() => { UnRegister(onEvent); });
|
||||
}
|
||||
|
||||
public void UnRegister(Action onEvent)
|
||||
{
|
||||
mOnEvent -= onEvent;
|
||||
}
|
||||
|
||||
public void Trigger()
|
||||
{
|
||||
mOnEvent?.Invoke();
|
||||
}
|
||||
}
|
||||
|
||||
public class EasyEvent<T> : IEasyEvent
|
||||
{
|
||||
private Action<T> mOnEvent = e => { };
|
||||
|
||||
public IUnRegister Register(Action<T> onEvent)
|
||||
{
|
||||
mOnEvent += onEvent;
|
||||
return new CustomUnRegister(() => { UnRegister(onEvent); });
|
||||
}
|
||||
|
||||
public void UnRegister(Action<T> onEvent)
|
||||
{
|
||||
mOnEvent -= onEvent;
|
||||
}
|
||||
|
||||
public void Trigger(T t)
|
||||
{
|
||||
mOnEvent?.Invoke(t);
|
||||
}
|
||||
}
|
||||
|
||||
public class EasyEvent<T, K> : IEasyEvent
|
||||
{
|
||||
private Action<T, K> mOnEvent = (t, k) => { };
|
||||
|
||||
public IUnRegister Register(Action<T, K> onEvent)
|
||||
{
|
||||
mOnEvent += onEvent;
|
||||
return new CustomUnRegister(() => { UnRegister(onEvent); });
|
||||
}
|
||||
|
||||
public void UnRegister(Action<T, K> onEvent)
|
||||
{
|
||||
mOnEvent -= onEvent;
|
||||
}
|
||||
|
||||
public void Trigger(T t, K k)
|
||||
{
|
||||
mOnEvent?.Invoke(t, k);
|
||||
}
|
||||
}
|
||||
|
||||
public class EasyEvent<T, K, S> : IEasyEvent
|
||||
{
|
||||
private Action<T, K, S> mOnEvent = (t, k, s) => { };
|
||||
|
||||
public IUnRegister Register(Action<T, K, S> onEvent)
|
||||
{
|
||||
mOnEvent += onEvent;
|
||||
return new CustomUnRegister(() => { UnRegister(onEvent); });
|
||||
}
|
||||
|
||||
public void UnRegister(Action<T, K, S> onEvent)
|
||||
{
|
||||
mOnEvent -= onEvent;
|
||||
}
|
||||
|
||||
public void Trigger(T t, K k, S s)
|
||||
{
|
||||
mOnEvent?.Invoke(t, k, s);
|
||||
}
|
||||
}
|
||||
|
||||
public class EasyEvents
|
||||
{
|
||||
private static EasyEvents mGlobalEvents = new EasyEvents();
|
||||
|
||||
public static T Get<T>() where T : IEasyEvent
|
||||
{
|
||||
return mGlobalEvents.GetEvent<T>();
|
||||
}
|
||||
|
||||
|
||||
public static void Register<T>() where T : IEasyEvent, new()
|
||||
{
|
||||
mGlobalEvents.AddEvent<T>();
|
||||
}
|
||||
|
||||
private Dictionary<Type, IEasyEvent> mTypeEvents = new Dictionary<Type, IEasyEvent>();
|
||||
|
||||
public void AddEvent<T>() where T : IEasyEvent, new()
|
||||
{
|
||||
mTypeEvents.Add(typeof(T), new T());
|
||||
}
|
||||
|
||||
public T GetEvent<T>() where T : IEasyEvent
|
||||
{
|
||||
IEasyEvent e;
|
||||
|
||||
if (mTypeEvents.TryGetValue(typeof(T), out e))
|
||||
{
|
||||
return (T)e;
|
||||
}
|
||||
|
||||
return default;
|
||||
}
|
||||
|
||||
public T GetOrAddEvent<T>() where T : IEasyEvent, new()
|
||||
{
|
||||
var eType = typeof(T);
|
||||
if (mTypeEvents.TryGetValue(eType, out var e))
|
||||
{
|
||||
return (T)e;
|
||||
}
|
||||
|
||||
var t = new T();
|
||||
mTypeEvents.Add(eType, t);
|
||||
return t;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
341
TEngineHotUpdate/src/TEngineCore/Core/TLogger.cs
Normal file
341
TEngineHotUpdate/src/TEngineCore/Core/TLogger.cs
Normal file
@@ -0,0 +1,341 @@
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
using UnityEngine;
|
||||
using Debug = UnityEngine.Debug;
|
||||
|
||||
namespace TEngineCore
|
||||
{
|
||||
public static class ColorUtils
|
||||
{
|
||||
#region ColorStr
|
||||
|
||||
public const string White = "FFFFFF";
|
||||
public const string Black = "000000";
|
||||
public const string Red = "FF0000";
|
||||
public const string Green = "00FF18";
|
||||
public const string Oringe = "FF9400";
|
||||
public const string Exception = "FF00BD";
|
||||
#endregion
|
||||
|
||||
public static string ToColor(this string str, string color)
|
||||
{
|
||||
if (string.IsNullOrEmpty(str))
|
||||
{
|
||||
return str;
|
||||
}
|
||||
|
||||
return string.Format("<color=#{0}>{1}</color>", color, str);
|
||||
}
|
||||
}
|
||||
|
||||
public class TLogger : TSingleton<TLogger>
|
||||
{
|
||||
protected override void Init()
|
||||
{
|
||||
_outputType = OutputType.EDITOR;
|
||||
_logToFile = new LogToFile();
|
||||
_logToFile.Init();
|
||||
UnityEngine.Application.logMessageReceivedThreaded += OnLogMessageReceivedThreaded;
|
||||
}
|
||||
|
||||
private LogToFile _logToFile;
|
||||
private void OnLogMessageReceivedThreaded(string condition, string stackTrace, LogType type)
|
||||
{
|
||||
if (!condition.StartsWith("TLogger]"))
|
||||
{
|
||||
_stringBuilder.Clear();
|
||||
|
||||
_stringBuilder.AppendFormat("[System][{0}][{1}] {2}", type.ToString(), System.DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss fff"), condition);
|
||||
|
||||
if (type == LogType.Warning || type == LogType.Error || type == LogType.Exception)
|
||||
{
|
||||
_stringBuilder.AppendLine(stackTrace);
|
||||
}
|
||||
|
||||
string strToWrite = _stringBuilder.ToString();
|
||||
|
||||
_logToFile.Write(strToWrite);
|
||||
}
|
||||
}
|
||||
|
||||
~TLogger()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public override void Release()
|
||||
{
|
||||
Application.logMessageReceivedThreaded -= OnLogMessageReceivedThreaded;
|
||||
_logToFile.DeInit();
|
||||
_logToFile = null;
|
||||
base.Release();
|
||||
}
|
||||
|
||||
|
||||
private void ChangeOutputChannel(OutputType type)
|
||||
{
|
||||
if (type != _outputType)
|
||||
{
|
||||
_outputType = type;
|
||||
}
|
||||
}
|
||||
|
||||
public static void SetFilterLevel(LogLevel filterLevel)
|
||||
{
|
||||
Instance._filterLevel = filterLevel;
|
||||
}
|
||||
|
||||
[Conditional("UNITY_EDITOR"), Conditional("_DEVELOPMENT_BUILD_"), Conditional("ENABLE_LOG_ASSERT")]
|
||||
public static void LogAssert(bool condition, string logStr)
|
||||
{
|
||||
if (!condition)
|
||||
Instance.Log(LogLevel.ASSERT, logStr);
|
||||
}
|
||||
|
||||
[Conditional("UNITY_EDITOR"), Conditional("_DEVELOPMENT_BUILD_"), Conditional("ENABLE_LOG_ASSERT")]
|
||||
public static void LogAssert(bool condition, string format, params System.Object[] args)
|
||||
{
|
||||
if (!condition)
|
||||
{
|
||||
string logStr = string.Format(format, args);
|
||||
Instance.Log(LogLevel.ASSERT, logStr);
|
||||
}
|
||||
}
|
||||
|
||||
[Conditional("UNITY_EDITOR"), Conditional("_DEVELOPMENT_BUILD_"), Conditional("ENABLE_LOG_INFO")]
|
||||
public static void LogInfo(string logStr)
|
||||
{
|
||||
Instance.Log(LogLevel.INFO, logStr);
|
||||
}
|
||||
|
||||
[Conditional("UNITY_EDITOR"), Conditional("_DEVELOPMENT_BUILD_"), Conditional("ENABLE_LOG_INFO")]
|
||||
public static void LogInfo(string format, params System.Object[] args)
|
||||
{
|
||||
string logStr = string.Format(format, args);
|
||||
Instance.Log(LogLevel.INFO, logStr);
|
||||
}
|
||||
|
||||
[Conditional("UNITY_EDITOR"), Conditional("_DEVELOPMENT_BUILD_"), Conditional("ENABLE_LOG_SUCCESS")]
|
||||
public static void LogInfoSuccessd(string logStr)
|
||||
{
|
||||
Instance.Log(LogLevel.Successd, logStr);
|
||||
}
|
||||
|
||||
[Conditional("UNITY_EDITOR"), Conditional("_DEVELOPMENT_BUILD_"), Conditional("ENABLE_LOG_SUCCESS")]
|
||||
public static void LogInfoSuccessd(string format, params System.Object[] args)
|
||||
{
|
||||
string logStr = string.Format(format, args);
|
||||
Instance.Log(LogLevel.Successd, logStr);
|
||||
}
|
||||
|
||||
[Conditional("UNITY_EDITOR"), Conditional("_DEVELOPMENT_BUILD_"), Conditional("ENABLE_LOG_WARNING")]
|
||||
public static void LogWarning(string logStr)
|
||||
{
|
||||
Instance.Log(LogLevel.WARNING, logStr);
|
||||
}
|
||||
|
||||
[Conditional("UNITY_EDITOR"), Conditional("_DEVELOPMENT_BUILD_"), Conditional("ENABLE_LOG_WARNING")]
|
||||
public static void LogWarning(string format, params System.Object[] args)
|
||||
{
|
||||
string logStr = string.Format(format, args);
|
||||
Instance.Log(LogLevel.WARNING, logStr);
|
||||
}
|
||||
|
||||
[Conditional("UNITY_EDITOR"), Conditional("_DEVELOPMENT_BUILD_"), Conditional("ENABLE_LOG_ERROR")]
|
||||
public static void LogError(string logStr)
|
||||
{
|
||||
Instance.Log(LogLevel.ERROR, logStr);
|
||||
}
|
||||
|
||||
[Conditional("UNITY_EDITOR"), Conditional("_DEVELOPMENT_BUILD_"), Conditional("ENABLE_LOG_ERROR")]
|
||||
public static void LogError(string format, params System.Object[] args)
|
||||
{
|
||||
string logStr = string.Format(format, args);
|
||||
Instance.Log(LogLevel.ERROR, logStr);
|
||||
}
|
||||
|
||||
[Conditional("UNITY_EDITOR"), Conditional("_DEVELOPMENT_BUILD_"), Conditional("ENABLE_LOG_EXCEPTION")]
|
||||
public static void LogException(string logStr)
|
||||
{
|
||||
Instance.Log(LogLevel.EXCEPTION, logStr);
|
||||
}
|
||||
|
||||
[Conditional("UNITY_EDITOR"), Conditional("_DEVELOPMENT_BUILD_"), Conditional("ENABLE_LOG_EXCEPTION")]
|
||||
public static void LogException(string format, params System.Object[] args)
|
||||
{
|
||||
string msg = string.Format(format, args);
|
||||
Instance.Log(LogLevel.EXCEPTION, msg);
|
||||
}
|
||||
|
||||
private StringBuilder GetFormatedString(LogLevel logLevel, string logString, bool bColor)
|
||||
{
|
||||
_stringBuilder.Clear();
|
||||
switch (logLevel)
|
||||
{
|
||||
case LogLevel.Successd:
|
||||
if (UseCustomColor)
|
||||
{
|
||||
_stringBuilder.AppendFormat("[TLogger][SUCCESSED][{0}] - <color=#{2}>{1}</color>",
|
||||
System.DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss fff"), logString, ColorUtils.Green);
|
||||
}
|
||||
else
|
||||
{
|
||||
_stringBuilder.AppendFormat(
|
||||
bColor ? "[TLogger][INFO][{0}] - <color=gray>{1}</color>" : "[TLogger][SUCCESSED][{0}] - {1}",
|
||||
System.DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss fff"), logString);
|
||||
}
|
||||
break;
|
||||
case LogLevel.INFO:
|
||||
if (UseCustomColor)
|
||||
{
|
||||
_stringBuilder.AppendFormat("[TLogger][INFO][{0}] - <color=#{2}>{1}</color>" ,
|
||||
System.DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss fff"), logString,ColorUtils.Black);
|
||||
}
|
||||
else
|
||||
{
|
||||
_stringBuilder.AppendFormat(
|
||||
bColor ? "[TLogger][INFO][{0}] - <color=gray>{1}</color>" : "[TLogger][INFO][{0}] - {1}",
|
||||
System.DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss fff"), logString);
|
||||
}
|
||||
break;
|
||||
case LogLevel.ASSERT:
|
||||
if (UseCustomColor)
|
||||
{
|
||||
_stringBuilder.AppendFormat("[TLogger][ASSERT][{0}] - <color=#{2}>{1}</color>",
|
||||
System.DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss fff"), logString,ColorUtils.Exception);
|
||||
}
|
||||
else
|
||||
{
|
||||
_stringBuilder.AppendFormat(
|
||||
bColor ? "[TLogger][ASSERT][{0}] - <color=green>{1}</color>" : "[TLogger][ASSERT][{0}] - {1}",
|
||||
System.DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss fff"), logString);
|
||||
}
|
||||
break;
|
||||
case LogLevel.WARNING:
|
||||
if (UseCustomColor)
|
||||
{
|
||||
_stringBuilder.AppendFormat("[TLogger][WARNING][{0}] - <color=#{2}>{1}</color>",
|
||||
System.DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss fff"), logString, ColorUtils.Oringe);
|
||||
}
|
||||
else
|
||||
{
|
||||
_stringBuilder.AppendFormat(
|
||||
bColor
|
||||
? "[TLogger][WARNING][{0}] - <color=yellow>{1}</color>"
|
||||
: "[TLogger][WARNING][{0}] - {1}", System.DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss fff"),
|
||||
logString);
|
||||
}
|
||||
break;
|
||||
case LogLevel.ERROR:
|
||||
if (UseCustomColor)
|
||||
{
|
||||
_stringBuilder.AppendFormat("[ERROR][WARNING][{0}] - <color=#{2}>{1}</color>",
|
||||
System.DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss fff"), logString, ColorUtils.Red);
|
||||
}
|
||||
else
|
||||
{
|
||||
_stringBuilder.AppendFormat(
|
||||
bColor ? "[TLogger][ERROR][{0}] - <color=red>{1}</color>" : "[TLogger][ERROR][{0}] - {1}",
|
||||
System.DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss fff"), logString);
|
||||
}
|
||||
break;
|
||||
case LogLevel.EXCEPTION:
|
||||
if (UseCustomColor)
|
||||
{
|
||||
_stringBuilder.AppendFormat("[ERROR][EXCEPTION][{0}] - <color=#{2}>{1}</color>",
|
||||
System.DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss fff"), logString, ColorUtils.Exception);
|
||||
}
|
||||
else
|
||||
{
|
||||
_stringBuilder.AppendFormat(
|
||||
bColor
|
||||
? "[TLogger][EXCEPTION][{0}] - <color=red>{1}</color>"
|
||||
: "[TLogger][EXCEPTION][{0}] - {1}",
|
||||
System.DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss fff"), logString);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return _stringBuilder;
|
||||
}
|
||||
|
||||
private void Log(LogLevel type, string logString)
|
||||
{
|
||||
if (_outputType == OutputType.NONE)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (type < _filterLevel)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
StringBuilder infoBuilder = GetFormatedString(type, logString, UseSystemColor);
|
||||
string logStr = infoBuilder.ToString();
|
||||
|
||||
//获取C#堆栈,Warning以上级别日志才获取堆栈
|
||||
if (type == LogLevel.ERROR || type == LogLevel.WARNING || type == LogLevel.EXCEPTION)
|
||||
{
|
||||
StackFrame[] sf = new StackTrace().GetFrames();
|
||||
for (int i = 0; i < sf.Length; i++)
|
||||
{
|
||||
StackFrame frame = sf[i];
|
||||
string declaringTypeName = frame.GetMethod().DeclaringType.FullName;
|
||||
string methodName = sf[i].GetMethod().Name;
|
||||
|
||||
infoBuilder.AppendFormat("[{0}::{1}\n", declaringTypeName, methodName);
|
||||
}
|
||||
}
|
||||
|
||||
if (type == LogLevel.INFO || type == LogLevel.Successd)
|
||||
{
|
||||
Debug.Log(logStr);
|
||||
}
|
||||
else if (type == LogLevel.WARNING)
|
||||
{
|
||||
Debug.LogWarning(logStr);
|
||||
}
|
||||
else if (type == LogLevel.ASSERT)
|
||||
{
|
||||
Debug.LogAssertion(logStr);
|
||||
}
|
||||
else if (type == LogLevel.ERROR)
|
||||
{
|
||||
Debug.LogError(logStr);
|
||||
}
|
||||
else if (type == LogLevel.EXCEPTION)
|
||||
{
|
||||
Debug.LogError(logStr);
|
||||
}
|
||||
}
|
||||
|
||||
#region Properties
|
||||
public bool UseCustomColor = false;
|
||||
public bool UseSystemColor = true;
|
||||
|
||||
public enum LogLevel
|
||||
{
|
||||
INFO,
|
||||
Successd,
|
||||
ASSERT,
|
||||
WARNING,
|
||||
ERROR,
|
||||
EXCEPTION,
|
||||
}
|
||||
|
||||
[System.Flags]
|
||||
public enum OutputType
|
||||
{
|
||||
NONE = 0,
|
||||
EDITOR = 0x1,
|
||||
GUI = 0x2,
|
||||
FILE = 0x4
|
||||
}
|
||||
|
||||
private LogLevel _filterLevel = LogLevel.INFO;
|
||||
private OutputType _outputType = OutputType.EDITOR;
|
||||
private StringBuilder _stringBuilder = new StringBuilder();
|
||||
#endregion
|
||||
}
|
||||
}
|
198
TEngineHotUpdate/src/TEngineCore/Core/TSingleton.cs
Normal file
198
TEngineHotUpdate/src/TEngineCore/Core/TSingleton.cs
Normal file
@@ -0,0 +1,198 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace TEngineCore
|
||||
{
|
||||
/// <summary>
|
||||
/// 单例接口
|
||||
/// </summary>
|
||||
public interface ISingleton
|
||||
{
|
||||
void Active();
|
||||
|
||||
void Release();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 单例管理器(统一化持久和释放)
|
||||
/// </summary>
|
||||
public static class SingletonMgr
|
||||
{
|
||||
private static List<ISingleton> _iSingletonList;
|
||||
private static Dictionary<string, GameObject> _gameObjects;
|
||||
private static GameObject _root;
|
||||
public static GameObject Root
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_root == null)
|
||||
{
|
||||
_root = GameObject.Find("TEngine");
|
||||
}
|
||||
|
||||
return _root;
|
||||
}
|
||||
}
|
||||
|
||||
public static void Retain(ISingleton go)
|
||||
{
|
||||
if (_iSingletonList == null)
|
||||
{
|
||||
_iSingletonList = new List<ISingleton>();
|
||||
}
|
||||
_iSingletonList.Add(go);
|
||||
}
|
||||
|
||||
public static void Retain(GameObject go)
|
||||
{
|
||||
if (_gameObjects == null)
|
||||
{
|
||||
_gameObjects = new Dictionary<string, GameObject>();
|
||||
}
|
||||
|
||||
if (!_gameObjects.ContainsKey(go.name))
|
||||
{
|
||||
_gameObjects.Add(go.name, go);
|
||||
if (Application.isPlaying)
|
||||
{
|
||||
UnityEngine.Object.DontDestroyOnLoad(go);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void Release(GameObject go)
|
||||
{
|
||||
if (_gameObjects != null && _gameObjects.ContainsKey(go.name))
|
||||
{
|
||||
_gameObjects.Remove(go.name);
|
||||
UnityEngine.Object.Destroy(go);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Release(ISingleton go)
|
||||
{
|
||||
if (_iSingletonList != null && _iSingletonList.Contains(go))
|
||||
{
|
||||
_iSingletonList.Remove(go);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Release()
|
||||
{
|
||||
if (_gameObjects != null)
|
||||
{
|
||||
foreach (var item in _gameObjects)
|
||||
{
|
||||
UnityEngine.Object.Destroy(item.Value);
|
||||
}
|
||||
|
||||
_gameObjects.Clear();
|
||||
}
|
||||
|
||||
if (_iSingletonList != null)
|
||||
{
|
||||
for (int i = 0; i < _iSingletonList.Count; ++i)
|
||||
{
|
||||
_iSingletonList[i].Release();
|
||||
}
|
||||
|
||||
_iSingletonList.Clear();
|
||||
}
|
||||
|
||||
Resources.UnloadUnusedAssets();
|
||||
}
|
||||
|
||||
public static GameObject GetGameObject(string name)
|
||||
{
|
||||
GameObject go = null;
|
||||
if (_gameObjects != null)
|
||||
{
|
||||
_gameObjects.TryGetValue(name, out go);
|
||||
}
|
||||
|
||||
return go;
|
||||
}
|
||||
|
||||
internal static bool CointansKey(string name)
|
||||
{
|
||||
if (_gameObjects != null)
|
||||
{
|
||||
return _gameObjects.ContainsKey(name);
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
internal static ISingleton GetSingleton(string name)
|
||||
{
|
||||
for (int i = 0; i < _iSingletonList.Count; ++i)
|
||||
{
|
||||
if (_iSingletonList[i].ToString() == name)
|
||||
{
|
||||
return _iSingletonList[i];
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 全局单例对象(非线程安全)
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
public abstract class TSingleton<T> : ISingleton where T : TSingleton<T>, new()
|
||||
{
|
||||
protected static T _instance = default(T);
|
||||
|
||||
public static T Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (null == _instance)
|
||||
{
|
||||
_instance = new T();
|
||||
_instance.Init();
|
||||
#if UNITY_EDITOR
|
||||
TLogger.LogInfo($"TSingleton Instance:{typeof(T).Name}");
|
||||
#endif
|
||||
SingletonMgr.Retain(_instance);
|
||||
}
|
||||
return _instance;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool IsValid
|
||||
{
|
||||
get
|
||||
{
|
||||
return _instance != null;
|
||||
}
|
||||
}
|
||||
|
||||
protected TSingleton()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
protected virtual void Init()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public virtual void Active()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public virtual void Release()
|
||||
{
|
||||
if (_instance != null)
|
||||
{
|
||||
SingletonMgr.Release(_instance);
|
||||
_instance = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
110
TEngineHotUpdate/src/TEngineCore/Core/UnitySingleton.cs
Normal file
110
TEngineHotUpdate/src/TEngineCore/Core/UnitySingleton.cs
Normal file
@@ -0,0 +1,110 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace TEngineCore
|
||||
{
|
||||
/// <summary>
|
||||
/// 具备Unity完整生命周期的单例
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
public class UnitySingleton<T> : MonoBehaviour where T : MonoBehaviour
|
||||
{
|
||||
|
||||
private static T _instance;
|
||||
|
||||
public static T Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_instance == null)
|
||||
{
|
||||
System.Type thisType = typeof(T);
|
||||
string instName = thisType.Name;
|
||||
GameObject go = SingletonMgr.GetGameObject(instName);
|
||||
if (go == null)
|
||||
{
|
||||
go = GameObject.Find($"/{instName}");
|
||||
if (go == null)
|
||||
{
|
||||
go = new GameObject(instName);
|
||||
go.transform.position = Vector3.zero;
|
||||
}
|
||||
SingletonMgr.Retain(go);
|
||||
}
|
||||
|
||||
if (go != null)
|
||||
{
|
||||
_instance = go.GetComponent<T>();
|
||||
if (_instance == null)
|
||||
{
|
||||
_instance = go.AddComponent<T>();
|
||||
}
|
||||
}
|
||||
|
||||
if (_instance == null)
|
||||
{
|
||||
TLogger.LogError($"Can't create UnitySingleton<{typeof(T)}>");
|
||||
}
|
||||
}
|
||||
return _instance;
|
||||
}
|
||||
}
|
||||
|
||||
public static T Active()
|
||||
{
|
||||
return Instance;
|
||||
}
|
||||
|
||||
public static bool IsValid
|
||||
{
|
||||
get
|
||||
{
|
||||
return _instance != null;
|
||||
}
|
||||
}
|
||||
|
||||
private bool CheckInstance()
|
||||
{
|
||||
if (this == Instance)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
GameObject.Destroy(gameObject);
|
||||
return false;
|
||||
}
|
||||
|
||||
protected virtual void OnLoad()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public virtual void Awake()
|
||||
{
|
||||
if (CheckInstance())
|
||||
{
|
||||
OnLoad();
|
||||
}
|
||||
#if UNITY_EDITOR
|
||||
TLogger.LogInfo($"UnitySingleton Instance:{typeof(T).Name}");
|
||||
GameObject tEngine = SingletonMgr.Root;
|
||||
if (tEngine != null)
|
||||
{
|
||||
this.gameObject.transform.SetParent(tEngine.transform);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
protected virtual void OnDestroy()
|
||||
{
|
||||
Release();
|
||||
}
|
||||
|
||||
public static void Release()
|
||||
{
|
||||
if (_instance != null)
|
||||
{
|
||||
SingletonMgr.Release(_instance.gameObject);
|
||||
_instance = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
113
TEngineHotUpdate/src/TEngineCore/ECS/ArrayPool.cs
Normal file
113
TEngineHotUpdate/src/TEngineCore/ECS/ArrayPool.cs
Normal file
@@ -0,0 +1,113 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace TEngineCore
|
||||
{
|
||||
public interface IIndex
|
||||
{
|
||||
int Index { get; set; }
|
||||
}
|
||||
|
||||
internal class HashSetDebugView<T> where T : IIndex
|
||||
{
|
||||
private readonly ArrayPool<T> m_Set;
|
||||
|
||||
public HashSetDebugView(ArrayPool<T> set)
|
||||
{
|
||||
m_Set = set ?? throw new ArgumentNullException(nameof(set));
|
||||
}
|
||||
|
||||
[DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
|
||||
public T[] Items
|
||||
{
|
||||
get
|
||||
{
|
||||
return m_Set.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[DebuggerTypeProxy(typeof(HashSetDebugView<>))]
|
||||
[DebuggerDisplay("Count = {Count}")]
|
||||
public class ArrayPool<T> where T:IIndex
|
||||
{
|
||||
internal T[] m_Items = new T[256];
|
||||
internal bool[] Buckets = new bool[256];
|
||||
private int m_Index;
|
||||
private int count;
|
||||
|
||||
public T this[int index]
|
||||
{
|
||||
get
|
||||
{
|
||||
return m_Items[index];
|
||||
}
|
||||
set
|
||||
{
|
||||
m_Items[index] = value;
|
||||
}
|
||||
}
|
||||
|
||||
public int Count
|
||||
{
|
||||
get
|
||||
{
|
||||
return count;
|
||||
}
|
||||
}
|
||||
|
||||
public T[] ToArray()
|
||||
{
|
||||
List<T> elements = new List<T>();
|
||||
for (int i = 0; i < m_Items.Length; i++)
|
||||
{
|
||||
if (Buckets[i])
|
||||
{
|
||||
elements.Add(m_Items[i]);
|
||||
}
|
||||
}
|
||||
return elements.ToArray();
|
||||
}
|
||||
|
||||
public void Remove(T item)
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
m_Items[item.Index] = default;
|
||||
Buckets[item.Index] = false;
|
||||
}
|
||||
}
|
||||
|
||||
public void Add(T item)
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
if (item.Index != -1)
|
||||
{
|
||||
if (!Buckets[item.Index])
|
||||
{
|
||||
m_Items[item.Index] = item;
|
||||
Buckets[item.Index] = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
m_Items[m_Index] = item;
|
||||
Buckets[m_Index] = true;
|
||||
item.Index = m_Index;
|
||||
m_Index++;
|
||||
if (m_Index >= m_Items.Length)
|
||||
{
|
||||
T[] newItems = new T[m_Items.Length * 2];
|
||||
bool[] newBuckets = new bool[m_Items.Length * 2];
|
||||
Array.Copy(m_Items,0,newItems,0,m_Items.Length);
|
||||
Array.Copy(Buckets, 0, newBuckets, 0, Buckets.Length);
|
||||
m_Items = newItems;
|
||||
Buckets = newBuckets;
|
||||
}
|
||||
count = m_Index;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
25
TEngineHotUpdate/src/TEngineCore/ECS/Demo/ECSDemoApp.cs
Normal file
25
TEngineHotUpdate/src/TEngineCore/ECS/Demo/ECSDemoApp.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
using TEngineCore;
|
||||
using UnityEngine;
|
||||
|
||||
public class ECSDemoApp : MonoBehaviour
|
||||
{
|
||||
public ECSGameSystem EcsGameSystem = new ECSGameSystem();
|
||||
public GameObject @object;
|
||||
|
||||
void Start()
|
||||
{
|
||||
var entity = EcsGameSystem.Create<Entity>();
|
||||
ECSActor actor = entity.AddComponent<ECSActor>();
|
||||
actor.Name = typeof(ECSActor).ToString();
|
||||
actor.gameObject = Instantiate(@object);
|
||||
actor.transform = actor.gameObject.GetComponent<Transform>();
|
||||
entity.AddComponent<ECSComponent>();
|
||||
|
||||
Debug.Log(entity.ToString());
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
EcsGameSystem.OnUpdate();
|
||||
}
|
||||
}
|
@@ -0,0 +1,9 @@
|
||||
using TEngineCore;
|
||||
|
||||
public class ECSGameSystem : ECSSystem
|
||||
{
|
||||
public void OnUpdate()
|
||||
{
|
||||
Update();
|
||||
}
|
||||
}
|
13
TEngineHotUpdate/src/TEngineCore/ECS/ECSActor.cs
Normal file
13
TEngineHotUpdate/src/TEngineCore/ECS/ECSActor.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
namespace TEngineCore
|
||||
{
|
||||
/// <summary>
|
||||
/// ECS Actor
|
||||
/// </summary>
|
||||
public class ECSActor : ECSComponent
|
||||
{
|
||||
public string Name;
|
||||
public UnityEngine.GameObject gameObject;
|
||||
public UnityEngine.Transform transform;
|
||||
public uint ActorID;
|
||||
}
|
||||
}
|
14
TEngineHotUpdate/src/TEngineCore/ECS/ECSComponent.cs
Normal file
14
TEngineHotUpdate/src/TEngineCore/ECS/ECSComponent.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
namespace TEngineCore
|
||||
{
|
||||
/// <summary>
|
||||
/// ECS构架可以将此组件从Entity上移除这个组件并丢入对象池,给其他此刻需要此组件的Entity使用,
|
||||
/// 因此可以节省大量的内存反复创建和释放, 这也是ECS的特性可以大量重复使用组件
|
||||
/// </summary>
|
||||
public class ECSComponent:ECSObject
|
||||
{
|
||||
#pragma warning disable IDE1006
|
||||
public Entity Entity { get; set; }
|
||||
#pragma warning restore IDE1006
|
||||
}
|
||||
}
|
||||
|
72
TEngineHotUpdate/src/TEngineCore/ECS/ECSDebugBehaviour.cs
Normal file
72
TEngineHotUpdate/src/TEngineCore/ECS/ECSDebugBehaviour.cs
Normal file
@@ -0,0 +1,72 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace TEngineCore
|
||||
{
|
||||
public enum ECSDebugType
|
||||
{
|
||||
Entity,
|
||||
System,
|
||||
Component
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class ECSCmptDebugKeyInfo
|
||||
{
|
||||
public string m_name;
|
||||
public string val;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class ECSCmptDebugInfo
|
||||
{
|
||||
public string m_name;
|
||||
public ECSDebugType Type = ECSDebugType.Component;
|
||||
public List<ECSCmptDebugKeyInfo> m_info = new List<ECSCmptDebugKeyInfo>();
|
||||
}
|
||||
|
||||
public class ECSDebugBehaviour : UnityEngine.MonoBehaviour
|
||||
{
|
||||
public List<ECSCmptDebugInfo> m_ECSInfo = new List<ECSCmptDebugInfo>();
|
||||
public ECSCmptDebugInfo AddDebugCmpt(string cmptName)
|
||||
{
|
||||
var cmptInfo = m_ECSInfo.Find((item) => { return item.m_name == cmptName; });
|
||||
if (cmptInfo == null)
|
||||
{
|
||||
cmptInfo = new ECSCmptDebugInfo();
|
||||
cmptInfo.m_name = cmptName;
|
||||
m_ECSInfo.Add(cmptInfo); ;
|
||||
}
|
||||
|
||||
return cmptInfo;
|
||||
}
|
||||
|
||||
public void RmvDebugCmpt(string cmptName)
|
||||
{
|
||||
m_ECSInfo.RemoveAll((item) => { return item.m_name == cmptName; });
|
||||
}
|
||||
|
||||
public void SetDebugInfo(string cmptName, string key, string val)
|
||||
{
|
||||
var cmptInfo = AddDebugCmpt(cmptName);
|
||||
var entry = cmptInfo.m_info.Find((t) => { return t.m_name == key; });
|
||||
if (entry == null)
|
||||
{
|
||||
entry = new ECSCmptDebugKeyInfo();
|
||||
entry.m_name = key;
|
||||
cmptInfo.m_info.Add(entry);
|
||||
}
|
||||
|
||||
entry.val = val;
|
||||
}
|
||||
|
||||
public void RemoveAllDebugInfo(string cmpName)
|
||||
{
|
||||
var cmpInfo = m_ECSInfo.Find((item) => { return item.m_name == cmpName; });
|
||||
if (cmpInfo != null)
|
||||
{
|
||||
cmpInfo.m_info.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
236
TEngineHotUpdate/src/TEngineCore/ECS/ECSEventCmpt.cs
Normal file
236
TEngineHotUpdate/src/TEngineCore/ECS/ECSEventCmpt.cs
Normal file
@@ -0,0 +1,236 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace TEngineCore
|
||||
{
|
||||
public class ECSEventCmpt : ECSComponent
|
||||
{
|
||||
private Dictionary<int, IEcsEcsEventInfo> m_eventDic = new Dictionary<int, IEcsEcsEventInfo>();
|
||||
|
||||
#region AddEventListener
|
||||
public void AddEventListener<T>(int eventid, Action<T> action)
|
||||
{
|
||||
if (m_eventDic.ContainsKey(eventid))
|
||||
{
|
||||
(m_eventDic[eventid] as EcsEventInfo<T>).actions += action;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_eventDic.Add(eventid, new EcsEventInfo<T>(action));
|
||||
}
|
||||
}
|
||||
|
||||
public void AddEventListener<T, U>(int eventid, Action<T, U> action)
|
||||
{
|
||||
if (m_eventDic.ContainsKey(eventid))
|
||||
{
|
||||
(m_eventDic[eventid] as EcsEventInfo<T, U>).actions += action;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_eventDic.Add(eventid, new EcsEventInfo<T, U>(action));
|
||||
}
|
||||
}
|
||||
|
||||
public void AddEventListener<T, U, W>(int eventid, Action<T, U, W> action)
|
||||
{
|
||||
if (m_eventDic.ContainsKey(eventid))
|
||||
{
|
||||
(m_eventDic[eventid] as EcsEventInfo<T, U, W>).actions += action;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_eventDic.Add(eventid, new EcsEventInfo<T, U, W>(action));
|
||||
}
|
||||
}
|
||||
|
||||
public void AddEventListener(int eventid, Action action)
|
||||
{
|
||||
if (m_eventDic.ContainsKey(eventid))
|
||||
{
|
||||
(m_eventDic[eventid] as EcsEventInfo).actions += action;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_eventDic.Add(eventid, new EcsEventInfo(action));
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region RemoveEventListener
|
||||
public void RemoveEventListener<T>(int eventid, Action<T> action)
|
||||
{
|
||||
if (action == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_eventDic.ContainsKey(eventid))
|
||||
{
|
||||
(m_eventDic[eventid] as EcsEventInfo<T>).actions -= action;
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveEventListener<T, U>(int eventid, Action<T, U> action)
|
||||
{
|
||||
if (action == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_eventDic.ContainsKey(eventid))
|
||||
{
|
||||
(m_eventDic[eventid] as EcsEventInfo<T, U>).actions -= action;
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveEventListener<T, U, W>(int eventid, Action<T, U, W> action)
|
||||
{
|
||||
if (action == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_eventDic.ContainsKey(eventid))
|
||||
{
|
||||
(m_eventDic[eventid] as EcsEventInfo<T, U, W>).actions -= action;
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveEventListener(int eventid, Action action)
|
||||
{
|
||||
if (action == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_eventDic.ContainsKey(eventid))
|
||||
{
|
||||
(m_eventDic[eventid] as EcsEventInfo).actions -= action;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Send
|
||||
public void Send<T>(int eventid, T info)
|
||||
{
|
||||
if (m_eventDic.ContainsKey(eventid))
|
||||
{
|
||||
var EcsEventInfo = (m_eventDic[eventid] as EcsEventInfo<T>);
|
||||
if (EcsEventInfo != null)
|
||||
{
|
||||
EcsEventInfo.actions.Invoke(info);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Send<T, U>(int eventid, T info, U info2)
|
||||
{
|
||||
if (m_eventDic.ContainsKey(eventid))
|
||||
{
|
||||
var EcsEventInfo = (m_eventDic[eventid] as EcsEventInfo<T, U>);
|
||||
if (EcsEventInfo != null)
|
||||
{
|
||||
EcsEventInfo.actions.Invoke(info, info2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Send<T, U, W>(int eventid, T info, U info2, W info3)
|
||||
{
|
||||
if (m_eventDic.ContainsKey(eventid))
|
||||
{
|
||||
var EcsEventInfo = (m_eventDic[eventid] as EcsEventInfo<T, U, W>);
|
||||
if (EcsEventInfo != null)
|
||||
{
|
||||
EcsEventInfo.actions.Invoke(info, info2, info3);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 事件触发 无参
|
||||
/// </summary>
|
||||
/// <param name="name"></param>
|
||||
public void Send(int eventid)
|
||||
{
|
||||
if (m_eventDic.ContainsKey(eventid))
|
||||
{
|
||||
var EcsEventInfo = (m_eventDic[eventid] as EcsEventInfo);
|
||||
if (EcsEventInfo != null)
|
||||
{
|
||||
EcsEventInfo.actions.Invoke();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Clear
|
||||
public void Clear()
|
||||
{
|
||||
m_eventDic.Clear();
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 生命周期
|
||||
public override void OnDestroy()
|
||||
{
|
||||
Clear();
|
||||
}
|
||||
|
||||
public override void Awake()
|
||||
{
|
||||
Entity.Event = this;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
#region EcsEventInfo
|
||||
internal interface IEcsEcsEventInfo
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public class EcsEventInfo : IEcsEcsEventInfo
|
||||
{
|
||||
public Action actions;
|
||||
|
||||
public EcsEventInfo(Action action)
|
||||
{
|
||||
actions += action;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class EcsEventInfo<T> : IEcsEcsEventInfo
|
||||
{
|
||||
public Action<T> actions;
|
||||
|
||||
public EcsEventInfo(Action<T> action)
|
||||
{
|
||||
actions += action;
|
||||
}
|
||||
}
|
||||
|
||||
public class EcsEventInfo<T, U> : IEcsEcsEventInfo
|
||||
{
|
||||
public Action<T, U> actions;
|
||||
|
||||
public EcsEventInfo(Action<T, U> action)
|
||||
{
|
||||
actions += action;
|
||||
}
|
||||
}
|
||||
|
||||
public class EcsEventInfo<T, U, W> : IEcsEcsEventInfo
|
||||
{
|
||||
public Action<T, U, W> actions;
|
||||
|
||||
public EcsEventInfo(Action<T, U, W> action)
|
||||
{
|
||||
actions += action;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
}
|
112
TEngineHotUpdate/src/TEngineCore/ECS/ECSObject.cs
Normal file
112
TEngineHotUpdate/src/TEngineCore/ECS/ECSObject.cs
Normal file
@@ -0,0 +1,112 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace TEngineCore
|
||||
{
|
||||
/// <summary>
|
||||
/// ECS架构基类Object
|
||||
/// </summary>
|
||||
public class ECSObject
|
||||
{
|
||||
internal int HashCode;
|
||||
|
||||
internal ECSSystem System;
|
||||
|
||||
public ECSObject()
|
||||
{
|
||||
HashCode = GetType().GetHashCode();
|
||||
}
|
||||
|
||||
public virtual void Awake() { }
|
||||
|
||||
public virtual void OnDestroy() { }
|
||||
|
||||
/// <summary>
|
||||
/// Remove The ECSEntity or Component And Throw the ECSObject to ArrayPool When AddComponent Or Create Can Use Again
|
||||
/// </summary>
|
||||
/// <param name="ecsObject"></param>
|
||||
/// <param name="reuse">此对象是否可以复用,复用会将对象丢入System对象池中 等待再次使用,如果是Entity对象,并且不复用的话,则把Entity所使用的组件也不复用</param>
|
||||
public static void Destroy(ECSObject ecsObject, bool reuse = true)
|
||||
{
|
||||
if (ecsObject is ECSComponent ecsComponent)
|
||||
{
|
||||
ecsComponent.Entity.Components.Remove(ecsComponent);
|
||||
if (ecsComponent is IUpdate update)
|
||||
{
|
||||
ecsComponent.Entity.Updates.Remove(update);
|
||||
}
|
||||
if (reuse)
|
||||
{
|
||||
ecsComponent.Entity.System.Push(ecsComponent);
|
||||
}
|
||||
ecsObject.OnDestroy();
|
||||
return;
|
||||
}
|
||||
else if (ecsObject is Entity entity)
|
||||
{
|
||||
entity.System.RemoveEntity(entity);
|
||||
entity.OnDestroy();
|
||||
while (entity.Components.Count > 0)
|
||||
{
|
||||
ECSComponent ecsComponentTemp = entity.Components[0];
|
||||
entity.Components.RemoveAt(0);
|
||||
ecsComponentTemp.OnDestroy();
|
||||
if (reuse)
|
||||
{
|
||||
entity.System.Push(ecsComponentTemp);
|
||||
}
|
||||
}
|
||||
entity.Updates.Clear();
|
||||
entity.CanUpdate = false;
|
||||
if (reuse)
|
||||
{
|
||||
entity.System.Push(entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public T FindObjectOfType<T>() where T : ECSObject
|
||||
{
|
||||
Type type = typeof(T);
|
||||
var elements = System.Entities.ToArray();
|
||||
for (int i = 0; i < elements.Length; i++)
|
||||
{
|
||||
if (elements[i].GetType() == type)
|
||||
{
|
||||
return elements[i] as T;
|
||||
}
|
||||
for (int n = 0; n < elements[i].Components.Count; n++)
|
||||
{
|
||||
if (elements[i].Components[n].GetType() == type)
|
||||
{
|
||||
return elements[i].Components[n] as T;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public T[] FindObjectsOfType<T>() where T : ECSObject
|
||||
{
|
||||
Type type = typeof(T);
|
||||
var items = System.Entities.ToArray();
|
||||
List<T> elements = new List<T>();
|
||||
for (int i = 0; i < items.Length; i++)
|
||||
{
|
||||
if (items[i].GetType() == type)
|
||||
{
|
||||
elements.Add(items[i] as T);
|
||||
}
|
||||
for (int n = 0; n < items[i].Components.Count; n++)
|
||||
{
|
||||
if (items[i].Components[n].GetType() == type)
|
||||
{
|
||||
elements.Add(items[i].Components[n] as T);
|
||||
}
|
||||
}
|
||||
}
|
||||
return elements.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
233
TEngineHotUpdate/src/TEngineCore/ECS/ECSSystem.cs
Normal file
233
TEngineHotUpdate/src/TEngineCore/ECS/ECSSystem.cs
Normal file
@@ -0,0 +1,233 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace TEngineCore
|
||||
{
|
||||
/// <summary>
|
||||
/// ECS系统 管理Entity、ECSComponent复用对象池
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class ECSSystem : IDisposable
|
||||
{
|
||||
private static ECSSystem instance = new ECSSystem();
|
||||
public static ECSSystem Instance => instance;
|
||||
private readonly Dictionary<int,Stack<ECSObject>> m_ObjectPool = new Dictionary<int, Stack<ECSObject>>();
|
||||
internal readonly ArrayPool<Entity> Entities = new ArrayPool<Entity>();
|
||||
private bool m_IsDispose = false;
|
||||
|
||||
public void AddEntity(Entity entity)
|
||||
{
|
||||
entity.System = this;
|
||||
entity.Awake();
|
||||
Entities.Add(entity);
|
||||
}
|
||||
|
||||
public void RemoveEntity(Entity entity)
|
||||
{
|
||||
Entities.Remove(entity);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get Object From ECSSystem
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <returns></returns>
|
||||
public T Get<T>() where T : ECSObject, new()
|
||||
{
|
||||
int type = typeof(T).GetHashCode();
|
||||
if (m_ObjectPool.TryGetValue(type,out Stack<ECSObject> stack))
|
||||
{
|
||||
if (stack.Count > 0)
|
||||
{
|
||||
return (T) stack.Pop();
|
||||
}
|
||||
goto Instantiate;
|
||||
}
|
||||
stack = new Stack<ECSObject>();
|
||||
m_ObjectPool.Add(type,stack);
|
||||
Instantiate: T ecsObject = new T();
|
||||
return ecsObject;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Push Object To ECSSystem
|
||||
/// </summary>
|
||||
public void Push(ECSObject ecsObject)
|
||||
{
|
||||
int type = ecsObject.HashCode;
|
||||
|
||||
if (m_ObjectPool.TryGetValue(type,out Stack<ECSObject> stack))
|
||||
{
|
||||
stack.Push(ecsObject);
|
||||
return;
|
||||
}
|
||||
stack = new Stack<ECSObject>();
|
||||
m_ObjectPool.Add(type,stack);
|
||||
stack.Push(ecsObject);
|
||||
}
|
||||
|
||||
public T Create<T>() where T : Entity, new()
|
||||
{
|
||||
T entity = Get<T>();
|
||||
AddEntity(entity);
|
||||
return entity;
|
||||
}
|
||||
|
||||
public T Create<T>(T entity) where T : Entity, new()
|
||||
{
|
||||
AddEntity(entity);
|
||||
return entity;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新ECS系统
|
||||
/// </summary>
|
||||
/// <param name="worker">线程池是否并行</param>
|
||||
public void Update(bool worker = false)
|
||||
{
|
||||
Run(worker);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新ECS物理系统
|
||||
/// </summary>
|
||||
/// <param name="worker">线程池是否并行</param>
|
||||
public void FixedUpdate(bool worker = false)
|
||||
{
|
||||
RunFixed(worker);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 运行ECS系统
|
||||
/// </summary>
|
||||
/// <param name="worker">线程池是否并行</param>
|
||||
public void Run(bool worker = false)
|
||||
{
|
||||
int count = Entities.Count;
|
||||
if (!worker)
|
||||
{
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
if (!Entities.Buckets[i])
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!Entities[i].CanUpdate)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
Entities[i].Execute();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Parallel.For(0, count, i =>
|
||||
{
|
||||
if (!Entities.Buckets[i])
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Entities[i].CanUpdate)
|
||||
{
|
||||
return;
|
||||
}
|
||||
Entities[i].Execute();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public void RunFixed(bool worker = false)
|
||||
{
|
||||
int count = Entities.Count;
|
||||
if (!worker)
|
||||
{
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
if (!Entities.Buckets[i])
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!Entities[i].CanFixedUpdate)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
Entities[i].FixedUpdate();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Parallel.For(0, count, i =>
|
||||
{
|
||||
if (!Entities.Buckets[i])
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Entities[i].CanFixedUpdate)
|
||||
{
|
||||
return;
|
||||
}
|
||||
Entities[i].FixedUpdate();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (m_IsDispose)
|
||||
{
|
||||
return;
|
||||
}
|
||||
m_IsDispose = true;
|
||||
}
|
||||
|
||||
public T FindObjectOfType<T>() where T : ECSObject
|
||||
{
|
||||
Type type = typeof(T);
|
||||
var elements = Entities.ToArray();
|
||||
for (int i = 0; i < elements.Length; i++)
|
||||
{
|
||||
if (elements[i].GetType() == type)
|
||||
{
|
||||
return elements[i] as T;
|
||||
}
|
||||
|
||||
for (int j = 0; j < elements[i].Components.Count; j++)
|
||||
{
|
||||
if (elements[i].Components[j].GetType() == type)
|
||||
{
|
||||
return elements[i].Components[j] as T;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public T[] FindObjectsOfType<T>() where T : ECSObject
|
||||
{
|
||||
Type type = typeof(T);
|
||||
var entities = Entities.ToArray();
|
||||
List<T> elements = new List<T>();
|
||||
for (int i = 0; i < entities.Length; i++)
|
||||
{
|
||||
if (entities[i].GetType() == type)
|
||||
{
|
||||
elements.Add(entities[i] as T);
|
||||
}
|
||||
for (int n = 0; n < entities[i].Components.Count; n++)
|
||||
{
|
||||
if (entities[i].Components[n].GetType() == type)
|
||||
{
|
||||
elements.Add(entities[i].Components[n] as T);
|
||||
}
|
||||
}
|
||||
}
|
||||
return elements.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
273
TEngineHotUpdate/src/TEngineCore/ECS/Entity.cs
Normal file
273
TEngineHotUpdate/src/TEngineCore/ECS/Entity.cs
Normal file
@@ -0,0 +1,273 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace TEngineCore
|
||||
{
|
||||
public class Entity : ECSObject, IIndex
|
||||
{
|
||||
[SerializeField]
|
||||
internal List<ECSComponent> Components = new List<ECSComponent>();
|
||||
internal List<IUpdate> Updates = new List<IUpdate>();
|
||||
internal List<IFixedUpdate> FixedUpdates = new List<IFixedUpdate>();
|
||||
internal bool InActive;
|
||||
internal bool CanUpdate;
|
||||
internal bool CanFixedUpdate;
|
||||
public int Index { get; set; } = -1;
|
||||
public ECSEventCmpt Event { get; set; }
|
||||
public Entity()
|
||||
{
|
||||
InActive = true;
|
||||
System = ECSSystem.Instance;
|
||||
}
|
||||
|
||||
~Entity()
|
||||
{
|
||||
InActive = false;
|
||||
}
|
||||
|
||||
internal void Execute()
|
||||
{
|
||||
for (int i = 0; i < Updates.Count; i++)
|
||||
{
|
||||
Updates[i].Update();
|
||||
}
|
||||
}
|
||||
|
||||
internal void FixedUpdate()
|
||||
{
|
||||
for (int i = 0; i < FixedUpdates.Count; i++)
|
||||
{
|
||||
FixedUpdates[i].FixedUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
public void RmvComponent<T>() where T : ECSComponent, new()
|
||||
{
|
||||
for (int i = 0; i < Components.Count; i++)
|
||||
{
|
||||
if (Components[i] is T component)
|
||||
{
|
||||
if (component is IUpdate update)
|
||||
{
|
||||
Updates.Remove(update);
|
||||
}
|
||||
else if (component is IFixedUpdate fixedUpdate)
|
||||
{
|
||||
FixedUpdates.Remove(fixedUpdate);
|
||||
}
|
||||
|
||||
System.Push(component);
|
||||
|
||||
CanUpdate = Updates.Count > 0;
|
||||
|
||||
CanFixedUpdate = FixedUpdates.Count > 0;
|
||||
}
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
CheckDebugInfo();
|
||||
#endif
|
||||
}
|
||||
|
||||
public void RmvComponent(Type componentType)
|
||||
{
|
||||
for (int i = 0; i < Components.Count; i++)
|
||||
{
|
||||
if (Components[i].GetType() == componentType)
|
||||
{
|
||||
|
||||
if (componentType is IUpdate update)
|
||||
{
|
||||
Updates.Remove(update);
|
||||
|
||||
CanUpdate = Updates.Count > 0;
|
||||
}
|
||||
else if (componentType is IFixedUpdate fixedUpdate)
|
||||
{
|
||||
FixedUpdates.Remove(fixedUpdate);
|
||||
|
||||
CanFixedUpdate = FixedUpdates.Count > 0;
|
||||
}
|
||||
//if (componentType is ECSComponent component)
|
||||
//{
|
||||
// System.Push(component);
|
||||
//}
|
||||
}
|
||||
}
|
||||
#if UNITY_EDITOR
|
||||
CheckDebugInfo();
|
||||
#endif
|
||||
}
|
||||
|
||||
public T AddComponent<T>() where T : ECSComponent, new()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
CheckDebugInfo();
|
||||
#endif
|
||||
T component = System.Get<T>();
|
||||
component.Entity = this;
|
||||
component.System = System;
|
||||
Components.Add(component);
|
||||
component.Awake();
|
||||
if (component is IUpdate update)
|
||||
{
|
||||
Updates.Add(update);
|
||||
}
|
||||
else if (component is IFixedUpdate fixedUpdate)
|
||||
{
|
||||
FixedUpdates.Add(fixedUpdate);
|
||||
}
|
||||
CanUpdate = Updates.Count > 0;
|
||||
CanFixedUpdate = FixedUpdates.Count > 0;
|
||||
return component;
|
||||
}
|
||||
|
||||
public ECSComponent AddComponent(ECSComponent component)
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
CheckDebugInfo();
|
||||
#endif
|
||||
component.Entity = this;
|
||||
component.System = System;
|
||||
Components.Add(component);
|
||||
component.Awake();
|
||||
if (component is IUpdate update)
|
||||
{
|
||||
Updates.Add(update);
|
||||
}
|
||||
else if (component is IFixedUpdate fixedUpdate)
|
||||
{
|
||||
FixedUpdates.Add(fixedUpdate);
|
||||
}
|
||||
CanUpdate = Updates.Count > 0;
|
||||
CanFixedUpdate = FixedUpdates.Count > 0;
|
||||
return component;
|
||||
}
|
||||
|
||||
public T GetComponent<T>() where T : ECSComponent
|
||||
{
|
||||
for (int i = 0; i < Components.Count; i++)
|
||||
{
|
||||
if (Components[i] is T type)
|
||||
{
|
||||
return type;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public ECSComponent GetComponent(Type componentType)
|
||||
{
|
||||
for (int i = 0; i < Components.Count; i++)
|
||||
{
|
||||
if (Components[i].GetType() == componentType)
|
||||
{
|
||||
return Components[i];
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public T[] GetComponents<T>() where T : ECSComponent
|
||||
{
|
||||
List<T> elements = new List<T>();
|
||||
for (int i = 0; i < Components.Count; i++)
|
||||
{
|
||||
if (Components[i] is T type)
|
||||
{
|
||||
elements.Add(type);
|
||||
}
|
||||
}
|
||||
return elements.ToArray();
|
||||
}
|
||||
|
||||
public List<T> GetComponentsList<T>() where T : ECSComponent
|
||||
{
|
||||
List<T> elements = new List<T>();
|
||||
for (int i = 0; i < Components.Count; i++)
|
||||
{
|
||||
if (Components[i] is T type)
|
||||
{
|
||||
elements.Add(type);
|
||||
}
|
||||
}
|
||||
return elements;
|
||||
}
|
||||
|
||||
public ECSComponent[] GetComponents(Type comType)
|
||||
{
|
||||
List<ECSComponent> elements = new List<ECSComponent>();
|
||||
for (int i = 0; i < Components.Count; i++)
|
||||
{
|
||||
{
|
||||
if (Components[i].GetType() == comType)
|
||||
{
|
||||
elements.Add(Components[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return elements.ToArray();
|
||||
}
|
||||
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
string str = "[";
|
||||
for (int i = 0; i < Components.Count; i++)
|
||||
{
|
||||
str += Components[i].GetType().Name + ",";
|
||||
}
|
||||
str = str.TrimEnd(',');
|
||||
str += "]";
|
||||
return $"{GetType().Name} Components: {str}";
|
||||
}
|
||||
|
||||
|
||||
public void CheckDebugInfo(GameObject gameObject)
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
if (gameObject == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var debugBehaviour = UnityUtil.AddMonoBehaviour<ECSDebugBehaviour>(gameObject);
|
||||
debugBehaviour.m_ECSInfo.Clear();
|
||||
for (int i = 0; i < this.Components.Count; i++)
|
||||
{
|
||||
var component = this.Components[i];
|
||||
var cmptName = component.GetType().Name;
|
||||
debugBehaviour.SetDebugInfo(cmptName, "", "");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
public void CheckDebugInfo()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
//var actorEntity = this as Entity;
|
||||
|
||||
//if (actorEntity == null)
|
||||
//{
|
||||
// return;
|
||||
//}
|
||||
|
||||
//if (actorEntity.gameObject == null)
|
||||
//{
|
||||
// return;
|
||||
//}
|
||||
|
||||
//var debugBehaviour = UnityUtil.AddMonoBehaviour<ECSDebugBehaviour>(actorEntity.gameObject);
|
||||
//debugBehaviour.m_ECSInfo.Clear();
|
||||
//for (int i = 0; i < this.Components.Count; i++)
|
||||
//{
|
||||
// var component = this.Components[i];
|
||||
// var cmptName = component.GetType().Name;
|
||||
// debugBehaviour.SetDebugInfo(cmptName, "", "");
|
||||
//}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
32
TEngineHotUpdate/src/TEngineCore/ECS/HotfixComponent.cs
Normal file
32
TEngineHotUpdate/src/TEngineCore/ECS/HotfixComponent.cs
Normal file
@@ -0,0 +1,32 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace TEngineCore
|
||||
{
|
||||
public class HotfixComponent : ECSComponent,IUpdate
|
||||
{
|
||||
public object[] Values;
|
||||
public Action OnAwake, OnUpdate, OnDestroyExt;
|
||||
|
||||
public override void Awake()
|
||||
{
|
||||
OnAwake?.Invoke();
|
||||
}
|
||||
|
||||
void IUpdate.Update()
|
||||
{
|
||||
OnUpdate?.Invoke();
|
||||
}
|
||||
|
||||
public override void OnDestroy()
|
||||
{
|
||||
OnDestroyExt?.Invoke();
|
||||
OnAwake = null;
|
||||
OnUpdate = null;
|
||||
OnDestroyExt = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
10
TEngineHotUpdate/src/TEngineCore/ECS/IFixedUpdate.cs
Normal file
10
TEngineHotUpdate/src/TEngineCore/ECS/IFixedUpdate.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
namespace TEngineCore
|
||||
{
|
||||
/// <summary>
|
||||
/// ECS组件更新接口(减少组件for循环开销)
|
||||
/// </summary>
|
||||
public interface IFixedUpdate
|
||||
{
|
||||
void FixedUpdate();
|
||||
}
|
||||
}
|
10
TEngineHotUpdate/src/TEngineCore/ECS/IUpdate.cs
Normal file
10
TEngineHotUpdate/src/TEngineCore/ECS/IUpdate.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
namespace TEngineCore
|
||||
{
|
||||
/// <summary>
|
||||
/// ECS组件更新接口(减少组件for循环开销)
|
||||
/// </summary>
|
||||
public interface IUpdate
|
||||
{
|
||||
void Update();
|
||||
}
|
||||
}
|
13
TEngineHotUpdate/src/TEngineCore/ECS/UpdateComponent.cs
Normal file
13
TEngineHotUpdate/src/TEngineCore/ECS/UpdateComponent.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
namespace TEngineCore
|
||||
{
|
||||
/// <summary>
|
||||
/// 热更层由此组件进行更新(ILRuntime不支持多继承, 接口继承)
|
||||
/// </summary>
|
||||
public class UpdateComponent :ECSComponent, IUpdate
|
||||
{
|
||||
public virtual void Update()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
392
TEngineHotUpdate/src/TEngineCore/Event/GameEventMgr.cs
Normal file
392
TEngineHotUpdate/src/TEngineCore/Event/GameEventMgr.cs
Normal file
@@ -0,0 +1,392 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using TEngineCore;
|
||||
|
||||
namespace TEngineCore
|
||||
{
|
||||
#region EventInfo
|
||||
internal interface IEventInfo
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public class EventInfo : IEventInfo
|
||||
{
|
||||
public Action actions;
|
||||
|
||||
public EventInfo(Action action)
|
||||
{
|
||||
actions += action;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class EventInfo<T> : IEventInfo
|
||||
{
|
||||
public Action<T> actions;
|
||||
|
||||
public EventInfo(Action<T> action)
|
||||
{
|
||||
actions += action;
|
||||
}
|
||||
}
|
||||
|
||||
public class EventInfo<T, U> : IEventInfo
|
||||
{
|
||||
public Action<T, U> actions;
|
||||
|
||||
public EventInfo(Action<T, U> action)
|
||||
{
|
||||
actions += action;
|
||||
}
|
||||
}
|
||||
|
||||
public class EventInfo<T, U, W> : IEventInfo
|
||||
{
|
||||
public Action<T, U, W> actions;
|
||||
|
||||
public EventInfo(Action<T, U, W> action)
|
||||
{
|
||||
actions += action;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
/// <summary>
|
||||
/// 总观察者 - 总事件中心系统
|
||||
/// </summary>
|
||||
public class GameEventMgr : TSingleton<GameEventMgr>
|
||||
{
|
||||
/// <summary>
|
||||
/// Dictionary<int, IEventInfo> Key->Int.32 Value->EventInfo,调用频率高建议使用int事件,减少字典内String的哈希碰撞
|
||||
/// </summary>
|
||||
private Dictionary<int, IEventInfo> m_eventDic = new Dictionary<int, IEventInfo>();
|
||||
|
||||
/// <summary>
|
||||
/// Dictionary<string, IEventInfo> Key->string Value->EventInfo,调用频率不高的时候可以使用
|
||||
/// </summary>
|
||||
private Dictionary<string, IEventInfo> m_eventStrDic = new Dictionary<string, IEventInfo>();
|
||||
|
||||
#region AddEventListener
|
||||
public void AddEventListener<T>(int eventid, Action<T> action)
|
||||
{
|
||||
if (m_eventDic.ContainsKey(eventid))
|
||||
{
|
||||
(m_eventDic[eventid] as EventInfo<T>).actions += action;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_eventDic.Add(eventid, new EventInfo<T>(action));
|
||||
}
|
||||
}
|
||||
|
||||
public void AddEventListener<T, U>(int eventid, Action<T, U> action)
|
||||
{
|
||||
if (m_eventDic.ContainsKey(eventid))
|
||||
{
|
||||
(m_eventDic[eventid] as EventInfo<T, U>).actions += action;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_eventDic.Add(eventid, new EventInfo<T, U>(action));
|
||||
}
|
||||
}
|
||||
|
||||
public void AddEventListener<T, U, W>(int eventid, Action<T, U, W> action)
|
||||
{
|
||||
if (m_eventDic.ContainsKey(eventid))
|
||||
{
|
||||
(m_eventDic[eventid] as EventInfo<T, U, W>).actions += action;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_eventDic.Add(eventid, new EventInfo<T, U, W>(action));
|
||||
}
|
||||
}
|
||||
|
||||
public void AddEventListener(int eventid, Action action)
|
||||
{
|
||||
if (m_eventDic.ContainsKey(eventid))
|
||||
{
|
||||
(m_eventDic[eventid] as EventInfo).actions += action;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_eventDic.Add(eventid, new EventInfo(action));
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region RemoveEventListener
|
||||
public void RemoveEventListener<T>(int eventid, Action<T> action)
|
||||
{
|
||||
if (action == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_eventDic.ContainsKey(eventid))
|
||||
{
|
||||
(m_eventDic[eventid] as EventInfo<T>).actions -= action;
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveEventListener<T, U>(int eventid, Action<T, U> action)
|
||||
{
|
||||
if (action == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_eventDic.ContainsKey(eventid))
|
||||
{
|
||||
(m_eventDic[eventid] as EventInfo<T, U>).actions -= action;
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveEventListener<T, U, W>(int eventid, Action<T, U, W> action)
|
||||
{
|
||||
if (action == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_eventDic.ContainsKey(eventid))
|
||||
{
|
||||
(m_eventDic[eventid] as EventInfo<T, U, W>).actions -= action;
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveEventListener(int eventid, Action action)
|
||||
{
|
||||
if (action == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_eventDic.ContainsKey(eventid))
|
||||
{
|
||||
(m_eventDic[eventid] as EventInfo).actions -= action;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Send
|
||||
public void Send<T>(int eventid, T info)
|
||||
{
|
||||
if (m_eventDic.ContainsKey(eventid))
|
||||
{
|
||||
var eventInfo = (m_eventDic[eventid] as EventInfo<T>);
|
||||
if (eventInfo != null)
|
||||
{
|
||||
eventInfo.actions.Invoke(info);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Send<T, U>(int eventid, T info, U info2)
|
||||
{
|
||||
if (m_eventDic.ContainsKey(eventid))
|
||||
{
|
||||
var eventInfo = (m_eventDic[eventid] as EventInfo<T, U>);
|
||||
if (eventInfo != null)
|
||||
{
|
||||
eventInfo.actions.Invoke(info, info2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Send<T, U, W>(int eventid, T info, U info2, W info3)
|
||||
{
|
||||
if (m_eventDic.ContainsKey(eventid))
|
||||
{
|
||||
var eventInfo = (m_eventDic[eventid] as EventInfo<T, U, W>);
|
||||
if (eventInfo != null)
|
||||
{
|
||||
eventInfo.actions.Invoke(info, info2, info3);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 事件触发 无参
|
||||
/// </summary>
|
||||
/// <param name="name"></param>
|
||||
public void Send(int eventid)
|
||||
{
|
||||
if (m_eventDic.ContainsKey(eventid))
|
||||
{
|
||||
var eventInfo = (m_eventDic[eventid] as EventInfo);
|
||||
if (eventInfo != null)
|
||||
{
|
||||
eventInfo.actions.Invoke();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region StringEvent
|
||||
#region AddEventListener
|
||||
public void AddEventListener<T>(string eventid, Action<T> action)
|
||||
{
|
||||
if (m_eventStrDic.ContainsKey(eventid))
|
||||
{
|
||||
(m_eventStrDic[eventid] as EventInfo<T>).actions += action;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_eventStrDic.Add(eventid, new EventInfo<T>(action));
|
||||
}
|
||||
}
|
||||
|
||||
public void AddEventListener<T, U>(string eventid, Action<T, U> action)
|
||||
{
|
||||
if (m_eventStrDic.ContainsKey(eventid))
|
||||
{
|
||||
(m_eventStrDic[eventid] as EventInfo<T, U>).actions += action;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_eventStrDic.Add(eventid, new EventInfo<T, U>(action));
|
||||
}
|
||||
}
|
||||
|
||||
public void AddEventListener<T, U, W>(string eventid, Action<T, U, W> action)
|
||||
{
|
||||
if (m_eventStrDic.ContainsKey(eventid))
|
||||
{
|
||||
(m_eventStrDic[eventid] as EventInfo<T, U, W>).actions += action;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_eventStrDic.Add(eventid, new EventInfo<T, U, W>(action));
|
||||
}
|
||||
}
|
||||
|
||||
public void AddEventListener(string eventid, Action action)
|
||||
{
|
||||
if (m_eventStrDic.ContainsKey(eventid))
|
||||
{
|
||||
(m_eventStrDic[eventid] as EventInfo).actions += action;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_eventStrDic.Add(eventid, new EventInfo(action));
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region RemoveEventListener
|
||||
public void RemoveEventListener<T>(string eventid, Action<T> action)
|
||||
{
|
||||
if (action == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_eventStrDic.ContainsKey(eventid))
|
||||
{
|
||||
(m_eventStrDic[eventid] as EventInfo<T>).actions -= action;
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveEventListener<T, U>(string eventid, Action<T, U> action)
|
||||
{
|
||||
if (action == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_eventStrDic.ContainsKey(eventid))
|
||||
{
|
||||
(m_eventStrDic[eventid] as EventInfo<T, U>).actions -= action;
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveEventListener<T, U, W>(string eventid, Action<T, U, W> action)
|
||||
{
|
||||
if (action == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_eventStrDic.ContainsKey(eventid))
|
||||
{
|
||||
(m_eventStrDic[eventid] as EventInfo<T, U, W>).actions -= action;
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveEventListener(string eventid, Action action)
|
||||
{
|
||||
if (action == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_eventStrDic.ContainsKey(eventid))
|
||||
{
|
||||
(m_eventStrDic[eventid] as EventInfo).actions -= action;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Send
|
||||
public void Send<T>(string eventid, T info)
|
||||
{
|
||||
if (m_eventStrDic.ContainsKey(eventid))
|
||||
{
|
||||
var eventInfo = (m_eventStrDic[eventid] as EventInfo<T>);
|
||||
if (eventInfo != null)
|
||||
{
|
||||
eventInfo.actions.Invoke(info);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Send<T, U>(string eventid, T info, U info2)
|
||||
{
|
||||
if (m_eventStrDic.ContainsKey(eventid))
|
||||
{
|
||||
var eventInfo = (m_eventStrDic[eventid] as EventInfo<T, U>);
|
||||
if (eventInfo != null)
|
||||
{
|
||||
eventInfo.actions.Invoke(info, info2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Send<T, U, W>(string eventid, T info, U info2, W info3)
|
||||
{
|
||||
if (m_eventStrDic.ContainsKey(eventid))
|
||||
{
|
||||
var eventInfo = (m_eventStrDic[eventid] as EventInfo<T, U, W>);
|
||||
if (eventInfo != null)
|
||||
{
|
||||
eventInfo.actions.Invoke(info, info2, info3);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Send(string eventid)
|
||||
{
|
||||
if (m_eventStrDic.ContainsKey(eventid))
|
||||
{
|
||||
var eventInfo = (m_eventStrDic[eventid] as EventInfo);
|
||||
if (eventInfo != null)
|
||||
{
|
||||
eventInfo.actions.Invoke();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
#endregion
|
||||
|
||||
#region Clear
|
||||
public void Clear()
|
||||
{
|
||||
m_eventDic.Clear();
|
||||
m_eventStrDic.Clear();
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
}
|
37
TEngineHotUpdate/src/TEngineCore/Event/StringId.cs
Normal file
37
TEngineHotUpdate/src/TEngineCore/Event/StringId.cs
Normal file
@@ -0,0 +1,37 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace TEngineCore
|
||||
{
|
||||
public class StringId
|
||||
{
|
||||
private static Dictionary<string, int> m_eventTypeHashMap = new Dictionary<string, int>();
|
||||
private static Dictionary<int, string> m_eventHashToStringMap = new Dictionary<int, string>();
|
||||
private static int m_currentId = 0;
|
||||
|
||||
public static int StringToHash(string val)
|
||||
{
|
||||
int hashId;
|
||||
if (m_eventTypeHashMap.TryGetValue(val, out hashId))
|
||||
{
|
||||
return hashId;
|
||||
}
|
||||
|
||||
hashId = ++m_currentId;
|
||||
m_eventTypeHashMap[val] = hashId;
|
||||
m_eventHashToStringMap[hashId] = val;
|
||||
|
||||
return hashId;
|
||||
}
|
||||
|
||||
public static string HashToString(int hash)
|
||||
{
|
||||
string value;
|
||||
if (m_eventHashToStringMap.TryGetValue(hash, out value))
|
||||
{
|
||||
return value;
|
||||
}
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
130
TEngineHotUpdate/src/TEngineCore/FileSystem/FileSystem.cs
Normal file
130
TEngineHotUpdate/src/TEngineCore/FileSystem/FileSystem.cs
Normal file
@@ -0,0 +1,130 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using UnityEngine;
|
||||
|
||||
namespace TEngineCore
|
||||
{
|
||||
public static class FileSystem
|
||||
{
|
||||
public const string ArtResourcePath = "Assets/ArtResources";
|
||||
public const string GameResourcePath = AssetConfig.AssetRootPath;
|
||||
internal static Dictionary<string, string> _fileFixList = new Dictionary<string, string>();
|
||||
private static string _persistentDataPath = null;
|
||||
private static string _resRootPath = null;
|
||||
private static string _resRootStreamAssetPath = null;
|
||||
public const string BuildPath = "Build";
|
||||
public const string AssetBundleBuildPath = BuildPath + "/AssetBundles";
|
||||
private const string AssetBundleTargetPath = "{0}/AssetBundles";
|
||||
/// <summary>
|
||||
/// 资源更新读取根目录
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static string ResourceRoot
|
||||
{
|
||||
get
|
||||
{
|
||||
if (string.IsNullOrEmpty(_resRootPath))
|
||||
{
|
||||
_resRootPath = Path.Combine(PersistentDataPath, "TEngine");
|
||||
}
|
||||
|
||||
if (!Directory.Exists(_resRootPath))
|
||||
{
|
||||
Directory.CreateDirectory(_resRootPath);
|
||||
}
|
||||
|
||||
return _resRootPath.FixPath();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 持久化数据存储路径
|
||||
/// </summary>
|
||||
public static string PersistentDataPath
|
||||
{
|
||||
get
|
||||
{
|
||||
if (string.IsNullOrEmpty(_persistentDataPath))
|
||||
{
|
||||
#if UNITY_EDITOR_WIN
|
||||
_persistentDataPath = Application.dataPath + "/../TEnginePersistentDataPath";
|
||||
if (!Directory.Exists(_persistentDataPath))
|
||||
{
|
||||
Directory.CreateDirectory(_persistentDataPath);
|
||||
}
|
||||
#else
|
||||
_persistentDataPath = Application.persistentDataPath;
|
||||
#endif
|
||||
}
|
||||
return _persistentDataPath.FixPath();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 资源更新读取StreamAsset根目录
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static string ResourceRootInStreamAsset
|
||||
{
|
||||
get
|
||||
{
|
||||
if (string.IsNullOrEmpty(_resRootStreamAssetPath))
|
||||
{
|
||||
_resRootStreamAssetPath = Path.Combine(Application.streamingAssetsPath, "TEngine");
|
||||
}
|
||||
return _resRootStreamAssetPath.FixPath();
|
||||
}
|
||||
}
|
||||
public static string GetAssetBundlePathInVersion(string bundlename)
|
||||
{
|
||||
//默认用外部目录
|
||||
string path = FilePath($"{ResourceRoot}/AssetBundles/{bundlename}");
|
||||
if (!File.Exists(path))
|
||||
{
|
||||
path = $"{ResourceRootInStreamAsset}/AssetBundles/{bundlename}";
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
public static string StreamAssetBundlePath
|
||||
{
|
||||
get { return string.Format(AssetBundleTargetPath, ResourceRootInStreamAsset); }
|
||||
}
|
||||
|
||||
internal static string FilePath(string path)
|
||||
{
|
||||
path = path.FixPath();
|
||||
#if UNITY_EDITOR_WIN
|
||||
path = path.Replace("Assets/../", "");
|
||||
#endif
|
||||
if (_fileFixList.ContainsKey(path))
|
||||
{
|
||||
return _fileFixList[path];
|
||||
}
|
||||
else
|
||||
{
|
||||
return path;
|
||||
}
|
||||
}
|
||||
|
||||
public static string FixPath(this string str)
|
||||
{
|
||||
str = str.Replace("\\", "/");
|
||||
return str;
|
||||
}
|
||||
|
||||
public static Stream OpenRead(string filePath)
|
||||
{
|
||||
#if UNITY_ANDROID && !UNITY_EDITOR
|
||||
byte[] bytes = ReadAllBytesFromOutOrInnerFolder(filePath);
|
||||
if (bytes != null)
|
||||
return new MemoryStream(bytes);
|
||||
else
|
||||
return null;
|
||||
#else
|
||||
return File.OpenRead(filePath);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
96
TEngineHotUpdate/src/TEngineCore/FileSystem/FileWriter.cs
Normal file
96
TEngineHotUpdate/src/TEngineCore/FileSystem/FileWriter.cs
Normal file
@@ -0,0 +1,96 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace TEngineCore
|
||||
{
|
||||
public class FileWriter
|
||||
{
|
||||
private FileStream _fStream;
|
||||
private StreamWriter _writer;
|
||||
|
||||
|
||||
public bool OpenFile(string path)
|
||||
{
|
||||
try
|
||||
{
|
||||
_fStream = File.Open(path, FileMode.Create, FileAccess.ReadWrite, FileShare.Read);
|
||||
_writer = new StreamWriter(_fStream);
|
||||
return true;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
TLogger.LogException(e.ToString());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void Shutdown()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_writer != null)
|
||||
{
|
||||
_writer.Close();
|
||||
}
|
||||
_writer = null;
|
||||
|
||||
if (_fStream != null)
|
||||
{
|
||||
_fStream.Close();
|
||||
}
|
||||
_fStream = null;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
TLogger.LogException(e.ToString());
|
||||
_writer = null;
|
||||
_fStream = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void Write(string msg)
|
||||
{
|
||||
if (_writer == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(msg))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
_writer.WriteLine(msg);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Shutdown();
|
||||
}
|
||||
|
||||
Flush();
|
||||
}
|
||||
|
||||
public void Flush()
|
||||
{
|
||||
if (_writer == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (_writer.BaseStream != null)
|
||||
{
|
||||
_writer.Flush();
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Shutdown();
|
||||
TLogger.LogException( e.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
106
TEngineHotUpdate/src/TEngineCore/FileSystem/LogToFile.cs
Normal file
106
TEngineHotUpdate/src/TEngineCore/FileSystem/LogToFile.cs
Normal file
@@ -0,0 +1,106 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
namespace TEngineCore
|
||||
{
|
||||
public class LogToFile
|
||||
{
|
||||
private FileWriter _fileWriter = new FileWriter();
|
||||
private static readonly string mLogRootPath = Path.Combine(FileSystem.PersistentDataPath, "Log").FixPath();
|
||||
private static int MaxLogDays = 3;
|
||||
private static string _currentLogName = string.Empty;
|
||||
|
||||
private string GetLogPath(DateTime dt)
|
||||
{
|
||||
string dataDir = dt.ToString("yyyy_MM_dd");
|
||||
string logPath = Path.Combine(mLogRootPath, dataDir);
|
||||
return logPath.FixPath();
|
||||
}
|
||||
|
||||
public void DeInit()
|
||||
{
|
||||
if (_fileWriter == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
_fileWriter.Flush();
|
||||
_fileWriter.Shutdown();
|
||||
}
|
||||
|
||||
public void Init()
|
||||
{
|
||||
DateTime currentTime = DateTime.Now;
|
||||
RemoveOldLogs(currentTime);
|
||||
string logDir = GetLogPath(currentTime);
|
||||
string logFileName = string.Format("Log_{0}.log", currentTime.ToString("yyyy_MM_dd-HH_mm_ss"));
|
||||
_currentLogName = logFileName;
|
||||
string fullPath = Path.Combine(logDir, logFileName).FixPath();
|
||||
try
|
||||
{
|
||||
if (!Directory.Exists(logDir))
|
||||
{
|
||||
Directory.CreateDirectory(logDir);
|
||||
}
|
||||
|
||||
if (File.Exists(fullPath))
|
||||
{
|
||||
File.Delete(fullPath);
|
||||
}
|
||||
|
||||
_fileWriter.OpenFile(fullPath);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
TLogger.LogException( e.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
public void Write(string msg)
|
||||
{
|
||||
if (_fileWriter == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
_fileWriter.Write(msg);
|
||||
}
|
||||
|
||||
private void RemoveOldLogs(DateTime now)
|
||||
{
|
||||
HashSet<string> foldersToKeep = new HashSet<string>();
|
||||
for (int i = 0; i < MaxLogDays; i++)
|
||||
{
|
||||
DateTime current = now.AddDays(-i);
|
||||
string folder = GetLogPath(current);
|
||||
foldersToKeep.Add(folder);
|
||||
}
|
||||
if (Directory.Exists(mLogRootPath))
|
||||
{
|
||||
try
|
||||
{
|
||||
string[] allLogDir = Directory.GetDirectories(mLogRootPath);
|
||||
foreach (string dir in allLogDir)
|
||||
{
|
||||
string fixedDir = dir.FixPath();
|
||||
if (!foldersToKeep.Contains(fixedDir))
|
||||
{
|
||||
try
|
||||
{
|
||||
Directory.Delete(fixedDir, true);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
TLogger.LogException(e.ToString());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
TLogger.LogException(e.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
30
TEngineHotUpdate/src/TEngineCore/Json/JsonHelper.cs
Normal file
30
TEngineHotUpdate/src/TEngineCore/Json/JsonHelper.cs
Normal file
@@ -0,0 +1,30 @@
|
||||
using UnityEngine;
|
||||
using TEngineCore;
|
||||
using TEngineCore.ListJson;
|
||||
|
||||
public class JsonHelper : Singleton<JsonHelper>
|
||||
{
|
||||
public JsonHelper()
|
||||
{
|
||||
}
|
||||
|
||||
public T Deserialize<T>(string json)
|
||||
{
|
||||
return JsonMapper.ToObject<T>(json);
|
||||
}
|
||||
|
||||
public JsonData Deserialize(string json)
|
||||
{
|
||||
return JsonMapper.ToObject(json);
|
||||
}
|
||||
|
||||
public string Serialize(JsonData jsonData)
|
||||
{
|
||||
return JsonMapper.ToJson(jsonData);
|
||||
}
|
||||
|
||||
public string Serialize(Object jsonData)
|
||||
{
|
||||
return JsonMapper.ToJson(jsonData);
|
||||
}
|
||||
}
|
@@ -0,0 +1,60 @@
|
||||
#region Header
|
||||
/**
|
||||
* IJsonWrapper.cs
|
||||
* Interface that represents a type capable of handling all kinds of JSON
|
||||
* data. This is mainly used when mapping objects through JsonMapper, and
|
||||
* it's implemented by JsonData.
|
||||
*
|
||||
* The authors disclaim copyright to this source code. For more details, see
|
||||
* the COPYING file included with this distribution.
|
||||
**/
|
||||
#endregion
|
||||
|
||||
|
||||
using System.Collections;
|
||||
using System.Collections.Specialized;
|
||||
|
||||
|
||||
namespace TEngineCore.ListJson
|
||||
{
|
||||
public enum JsonType
|
||||
{
|
||||
None,
|
||||
|
||||
Object,
|
||||
Array,
|
||||
String,
|
||||
Int,
|
||||
Long,
|
||||
Double,
|
||||
Boolean
|
||||
}
|
||||
|
||||
public interface IJsonWrapper : IList, IOrderedDictionary
|
||||
{
|
||||
bool IsArray { get; }
|
||||
bool IsBoolean { get; }
|
||||
bool IsDouble { get; }
|
||||
bool IsInt { get; }
|
||||
bool IsLong { get; }
|
||||
bool IsObject { get; }
|
||||
bool IsString { get; }
|
||||
|
||||
bool GetBoolean ();
|
||||
double GetDouble ();
|
||||
int GetInt ();
|
||||
JsonType GetJsonType ();
|
||||
long GetLong ();
|
||||
string GetString ();
|
||||
|
||||
void SetBoolean (bool val);
|
||||
void SetDouble (double val);
|
||||
void SetInt (int val);
|
||||
void SetJsonType (JsonType type);
|
||||
void SetLong (long val);
|
||||
void SetString (string val);
|
||||
|
||||
string ToJson ();
|
||||
void ToJson (JsonWriter writer);
|
||||
}
|
||||
}
|
1059
TEngineHotUpdate/src/TEngineCore/Json/LitJson/JsonData.cs
Normal file
1059
TEngineHotUpdate/src/TEngineCore/Json/LitJson/JsonData.cs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,65 @@
|
||||
#region Header
|
||||
/**
|
||||
* JsonException.cs
|
||||
* Base class throwed by LitJSON when a parsing error occurs.
|
||||
*
|
||||
* The authors disclaim copyright to this source code. For more details, see
|
||||
* the COPYING file included with this distribution.
|
||||
**/
|
||||
#endregion
|
||||
|
||||
|
||||
using System;
|
||||
|
||||
|
||||
namespace TEngineCore.ListJson
|
||||
{
|
||||
public class JsonException :
|
||||
#if NETSTANDARD1_5
|
||||
Exception
|
||||
#else
|
||||
ApplicationException
|
||||
#endif
|
||||
{
|
||||
public JsonException () : base ()
|
||||
{
|
||||
}
|
||||
|
||||
internal JsonException (ParserToken token) :
|
||||
base (String.Format (
|
||||
"Invalid token '{0}' in input string", token))
|
||||
{
|
||||
}
|
||||
|
||||
internal JsonException (ParserToken token,
|
||||
Exception inner_exception) :
|
||||
base (String.Format (
|
||||
"Invalid token '{0}' in input string", token),
|
||||
inner_exception)
|
||||
{
|
||||
}
|
||||
|
||||
internal JsonException (int c) :
|
||||
base (String.Format (
|
||||
"Invalid character '{0}' in input string", (char) c))
|
||||
{
|
||||
}
|
||||
|
||||
internal JsonException (int c, Exception inner_exception) :
|
||||
base (String.Format (
|
||||
"Invalid character '{0}' in input string", (char) c),
|
||||
inner_exception)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
public JsonException (string message) : base (message)
|
||||
{
|
||||
}
|
||||
|
||||
public JsonException (string message, Exception inner_exception) :
|
||||
base (message, inner_exception)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
987
TEngineHotUpdate/src/TEngineCore/Json/LitJson/JsonMapper.cs
Normal file
987
TEngineHotUpdate/src/TEngineCore/Json/LitJson/JsonMapper.cs
Normal file
@@ -0,0 +1,987 @@
|
||||
#region Header
|
||||
/**
|
||||
* JsonMapper.cs
|
||||
* JSON to .Net object and object to JSON conversions.
|
||||
*
|
||||
* The authors disclaim copyright to this source code. For more details, see
|
||||
* the COPYING file included with this distribution.
|
||||
**/
|
||||
#endregion
|
||||
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
|
||||
|
||||
namespace TEngineCore.ListJson
|
||||
{
|
||||
internal struct PropertyMetadata
|
||||
{
|
||||
public MemberInfo Info;
|
||||
public bool IsField;
|
||||
public Type Type;
|
||||
}
|
||||
|
||||
|
||||
internal struct ArrayMetadata
|
||||
{
|
||||
private Type element_type;
|
||||
private bool is_array;
|
||||
private bool is_list;
|
||||
|
||||
|
||||
public Type ElementType {
|
||||
get {
|
||||
if (element_type == null)
|
||||
return typeof (JsonData);
|
||||
|
||||
return element_type;
|
||||
}
|
||||
|
||||
set { element_type = value; }
|
||||
}
|
||||
|
||||
public bool IsArray {
|
||||
get { return is_array; }
|
||||
set { is_array = value; }
|
||||
}
|
||||
|
||||
public bool IsList {
|
||||
get { return is_list; }
|
||||
set { is_list = value; }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
internal struct ObjectMetadata
|
||||
{
|
||||
private Type element_type;
|
||||
private bool is_dictionary;
|
||||
|
||||
private IDictionary<string, PropertyMetadata> properties;
|
||||
|
||||
|
||||
public Type ElementType {
|
||||
get {
|
||||
if (element_type == null)
|
||||
return typeof (JsonData);
|
||||
|
||||
return element_type;
|
||||
}
|
||||
|
||||
set { element_type = value; }
|
||||
}
|
||||
|
||||
public bool IsDictionary {
|
||||
get { return is_dictionary; }
|
||||
set { is_dictionary = value; }
|
||||
}
|
||||
|
||||
public IDictionary<string, PropertyMetadata> Properties {
|
||||
get { return properties; }
|
||||
set { properties = value; }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
internal delegate void ExporterFunc (object obj, JsonWriter writer);
|
||||
public delegate void ExporterFunc<T> (T obj, JsonWriter writer);
|
||||
|
||||
internal delegate object ImporterFunc (object input);
|
||||
public delegate TValue ImporterFunc<TJson, TValue> (TJson input);
|
||||
|
||||
public delegate IJsonWrapper WrapperFactory ();
|
||||
|
||||
|
||||
public class JsonMapper
|
||||
{
|
||||
#region Fields
|
||||
private static readonly int max_nesting_depth;
|
||||
|
||||
private static readonly IFormatProvider datetime_format;
|
||||
|
||||
private static readonly IDictionary<Type, ExporterFunc> base_exporters_table;
|
||||
private static readonly IDictionary<Type, ExporterFunc> custom_exporters_table;
|
||||
|
||||
private static readonly IDictionary<Type,
|
||||
IDictionary<Type, ImporterFunc>> base_importers_table;
|
||||
private static readonly IDictionary<Type,
|
||||
IDictionary<Type, ImporterFunc>> custom_importers_table;
|
||||
|
||||
private static readonly IDictionary<Type, ArrayMetadata> array_metadata;
|
||||
private static readonly object array_metadata_lock = new Object ();
|
||||
|
||||
private static readonly IDictionary<Type,
|
||||
IDictionary<Type, MethodInfo>> conv_ops;
|
||||
private static readonly object conv_ops_lock = new Object ();
|
||||
|
||||
private static readonly IDictionary<Type, ObjectMetadata> object_metadata;
|
||||
private static readonly object object_metadata_lock = new Object ();
|
||||
|
||||
private static readonly IDictionary<Type,
|
||||
IList<PropertyMetadata>> type_properties;
|
||||
private static readonly object type_properties_lock = new Object ();
|
||||
|
||||
private static readonly JsonWriter static_writer;
|
||||
private static readonly object static_writer_lock = new Object ();
|
||||
#endregion
|
||||
|
||||
|
||||
#region Constructors
|
||||
static JsonMapper ()
|
||||
{
|
||||
max_nesting_depth = 100;
|
||||
|
||||
array_metadata = new Dictionary<Type, ArrayMetadata> ();
|
||||
conv_ops = new Dictionary<Type, IDictionary<Type, MethodInfo>> ();
|
||||
object_metadata = new Dictionary<Type, ObjectMetadata> ();
|
||||
type_properties = new Dictionary<Type,
|
||||
IList<PropertyMetadata>> ();
|
||||
|
||||
static_writer = new JsonWriter ();
|
||||
|
||||
datetime_format = DateTimeFormatInfo.InvariantInfo;
|
||||
|
||||
base_exporters_table = new Dictionary<Type, ExporterFunc> ();
|
||||
custom_exporters_table = new Dictionary<Type, ExporterFunc> ();
|
||||
|
||||
base_importers_table = new Dictionary<Type,
|
||||
IDictionary<Type, ImporterFunc>> ();
|
||||
custom_importers_table = new Dictionary<Type,
|
||||
IDictionary<Type, ImporterFunc>> ();
|
||||
|
||||
RegisterBaseExporters ();
|
||||
RegisterBaseImporters ();
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
#region Private Methods
|
||||
private static void AddArrayMetadata (Type type)
|
||||
{
|
||||
if (array_metadata.ContainsKey (type))
|
||||
return;
|
||||
|
||||
ArrayMetadata data = new ArrayMetadata ();
|
||||
|
||||
data.IsArray = type.IsArray;
|
||||
|
||||
if (type.GetInterface ("System.Collections.IList") != null)
|
||||
data.IsList = true;
|
||||
|
||||
foreach (PropertyInfo p_info in type.GetProperties ()) {
|
||||
if (p_info.Name != "Item")
|
||||
continue;
|
||||
|
||||
ParameterInfo[] parameters = p_info.GetIndexParameters ();
|
||||
|
||||
if (parameters.Length != 1)
|
||||
continue;
|
||||
|
||||
if (parameters[0].ParameterType == typeof (int))
|
||||
data.ElementType = p_info.PropertyType;
|
||||
}
|
||||
|
||||
lock (array_metadata_lock) {
|
||||
try {
|
||||
array_metadata.Add (type, data);
|
||||
} catch (ArgumentException) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void AddObjectMetadata (Type type)
|
||||
{
|
||||
if (object_metadata.ContainsKey (type))
|
||||
return;
|
||||
|
||||
ObjectMetadata data = new ObjectMetadata ();
|
||||
|
||||
if (type.GetInterface ("System.Collections.IDictionary") != null)
|
||||
data.IsDictionary = true;
|
||||
|
||||
data.Properties = new Dictionary<string, PropertyMetadata> ();
|
||||
|
||||
foreach (PropertyInfo p_info in type.GetProperties ()) {
|
||||
if (p_info.Name == "Item") {
|
||||
ParameterInfo[] parameters = p_info.GetIndexParameters ();
|
||||
|
||||
if (parameters.Length != 1)
|
||||
continue;
|
||||
|
||||
if (parameters[0].ParameterType == typeof (string))
|
||||
data.ElementType = p_info.PropertyType;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
PropertyMetadata p_data = new PropertyMetadata ();
|
||||
p_data.Info = p_info;
|
||||
p_data.Type = p_info.PropertyType;
|
||||
|
||||
data.Properties.Add (p_info.Name, p_data);
|
||||
}
|
||||
|
||||
foreach (FieldInfo f_info in type.GetFields ()) {
|
||||
PropertyMetadata p_data = new PropertyMetadata ();
|
||||
p_data.Info = f_info;
|
||||
p_data.IsField = true;
|
||||
p_data.Type = f_info.FieldType;
|
||||
|
||||
data.Properties.Add (f_info.Name, p_data);
|
||||
}
|
||||
|
||||
lock (object_metadata_lock) {
|
||||
try {
|
||||
object_metadata.Add (type, data);
|
||||
} catch (ArgumentException) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void AddTypeProperties (Type type)
|
||||
{
|
||||
if (type_properties.ContainsKey (type))
|
||||
return;
|
||||
|
||||
IList<PropertyMetadata> props = new List<PropertyMetadata> ();
|
||||
|
||||
foreach (PropertyInfo p_info in type.GetProperties ()) {
|
||||
if (p_info.Name == "Item")
|
||||
continue;
|
||||
|
||||
PropertyMetadata p_data = new PropertyMetadata ();
|
||||
p_data.Info = p_info;
|
||||
p_data.IsField = false;
|
||||
props.Add (p_data);
|
||||
}
|
||||
|
||||
foreach (FieldInfo f_info in type.GetFields ()) {
|
||||
PropertyMetadata p_data = new PropertyMetadata ();
|
||||
p_data.Info = f_info;
|
||||
p_data.IsField = true;
|
||||
|
||||
props.Add (p_data);
|
||||
}
|
||||
|
||||
lock (type_properties_lock) {
|
||||
try {
|
||||
type_properties.Add (type, props);
|
||||
} catch (ArgumentException) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static MethodInfo GetConvOp (Type t1, Type t2)
|
||||
{
|
||||
lock (conv_ops_lock) {
|
||||
if (! conv_ops.ContainsKey (t1))
|
||||
conv_ops.Add (t1, new Dictionary<Type, MethodInfo> ());
|
||||
}
|
||||
|
||||
if (conv_ops[t1].ContainsKey (t2))
|
||||
return conv_ops[t1][t2];
|
||||
|
||||
MethodInfo op = t1.GetMethod (
|
||||
"op_Implicit", new Type[] { t2 });
|
||||
|
||||
lock (conv_ops_lock) {
|
||||
try {
|
||||
conv_ops[t1].Add (t2, op);
|
||||
} catch (ArgumentException) {
|
||||
return conv_ops[t1][t2];
|
||||
}
|
||||
}
|
||||
|
||||
return op;
|
||||
}
|
||||
|
||||
private static object ReadValue (Type inst_type, JsonReader reader)
|
||||
{
|
||||
reader.Read ();
|
||||
|
||||
if (reader.Token == JsonToken.ArrayEnd)
|
||||
return null;
|
||||
|
||||
Type underlying_type = Nullable.GetUnderlyingType(inst_type);
|
||||
Type value_type = underlying_type ?? inst_type;
|
||||
|
||||
if (reader.Token == JsonToken.Null) {
|
||||
#if NETSTANDARD1_5
|
||||
if (inst_type.IsClass() || underlying_type != null) {
|
||||
return null;
|
||||
}
|
||||
#else
|
||||
if (inst_type.IsClass || underlying_type != null) {
|
||||
return null;
|
||||
}
|
||||
#endif
|
||||
|
||||
throw new JsonException (String.Format (
|
||||
"Can't assign null to an instance of type {0}",
|
||||
inst_type));
|
||||
}
|
||||
|
||||
if (reader.Token == JsonToken.Double ||
|
||||
reader.Token == JsonToken.Int ||
|
||||
reader.Token == JsonToken.Long ||
|
||||
reader.Token == JsonToken.String ||
|
||||
reader.Token == JsonToken.Boolean) {
|
||||
|
||||
Type json_type = reader.Value.GetType ();
|
||||
|
||||
if (value_type.IsAssignableFrom (json_type))
|
||||
return reader.Value;
|
||||
|
||||
// If there's a custom importer that fits, use it
|
||||
if (custom_importers_table.ContainsKey (json_type) &&
|
||||
custom_importers_table[json_type].ContainsKey (
|
||||
value_type)) {
|
||||
|
||||
ImporterFunc importer =
|
||||
custom_importers_table[json_type][value_type];
|
||||
|
||||
return importer (reader.Value);
|
||||
}
|
||||
|
||||
// Maybe there's a base importer that works
|
||||
if (base_importers_table.ContainsKey (json_type) &&
|
||||
base_importers_table[json_type].ContainsKey (
|
||||
value_type)) {
|
||||
|
||||
ImporterFunc importer =
|
||||
base_importers_table[json_type][value_type];
|
||||
|
||||
return importer (reader.Value);
|
||||
}
|
||||
|
||||
// Maybe it's an enum
|
||||
#if NETSTANDARD1_5
|
||||
if (value_type.IsEnum())
|
||||
return Enum.ToObject (value_type, reader.Value);
|
||||
#else
|
||||
if (value_type.IsEnum)
|
||||
return Enum.ToObject (value_type, reader.Value);
|
||||
#endif
|
||||
// Try using an implicit conversion operator
|
||||
MethodInfo conv_op = GetConvOp (value_type, json_type);
|
||||
|
||||
if (conv_op != null)
|
||||
return conv_op.Invoke (null,
|
||||
new object[] { reader.Value });
|
||||
|
||||
// No luck
|
||||
throw new JsonException (String.Format (
|
||||
"Can't assign value '{0}' (type {1}) to type {2}",
|
||||
reader.Value, json_type, inst_type));
|
||||
}
|
||||
|
||||
object instance = null;
|
||||
|
||||
if (reader.Token == JsonToken.ArrayStart) {
|
||||
|
||||
AddArrayMetadata (inst_type);
|
||||
ArrayMetadata t_data = array_metadata[inst_type];
|
||||
|
||||
if (! t_data.IsArray && ! t_data.IsList)
|
||||
throw new JsonException (String.Format (
|
||||
"Type {0} can't act as an array",
|
||||
inst_type));
|
||||
|
||||
IList list;
|
||||
Type elem_type;
|
||||
|
||||
if (! t_data.IsArray) {
|
||||
list = (IList) Activator.CreateInstance (inst_type);
|
||||
elem_type = t_data.ElementType;
|
||||
} else {
|
||||
list = new ArrayList ();
|
||||
elem_type = inst_type.GetElementType ();
|
||||
}
|
||||
|
||||
list.Clear();
|
||||
|
||||
while (true) {
|
||||
object item = ReadValue (elem_type, reader);
|
||||
if (item == null && reader.Token == JsonToken.ArrayEnd)
|
||||
break;
|
||||
|
||||
list.Add (item);
|
||||
}
|
||||
|
||||
if (t_data.IsArray) {
|
||||
int n = list.Count;
|
||||
instance = Array.CreateInstance (elem_type, n);
|
||||
|
||||
for (int i = 0; i < n; i++)
|
||||
((Array) instance).SetValue (list[i], i);
|
||||
} else
|
||||
instance = list;
|
||||
|
||||
} else if (reader.Token == JsonToken.ObjectStart) {
|
||||
AddObjectMetadata (value_type);
|
||||
ObjectMetadata t_data = object_metadata[value_type];
|
||||
|
||||
instance = Activator.CreateInstance (value_type);
|
||||
|
||||
while (true) {
|
||||
reader.Read ();
|
||||
|
||||
if (reader.Token == JsonToken.ObjectEnd)
|
||||
break;
|
||||
|
||||
string property = (string) reader.Value;
|
||||
|
||||
if (t_data.Properties.ContainsKey (property)) {
|
||||
PropertyMetadata prop_data =
|
||||
t_data.Properties[property];
|
||||
|
||||
if (prop_data.IsField) {
|
||||
((FieldInfo) prop_data.Info).SetValue (
|
||||
instance, ReadValue (prop_data.Type, reader));
|
||||
} else {
|
||||
PropertyInfo p_info =
|
||||
(PropertyInfo) prop_data.Info;
|
||||
|
||||
if (p_info.CanWrite)
|
||||
p_info.SetValue (
|
||||
instance,
|
||||
ReadValue (prop_data.Type, reader),
|
||||
null);
|
||||
else
|
||||
ReadValue (prop_data.Type, reader);
|
||||
}
|
||||
|
||||
} else {
|
||||
if (! t_data.IsDictionary) {
|
||||
|
||||
if (! reader.SkipNonMembers) {
|
||||
throw new JsonException (String.Format (
|
||||
"The type {0} doesn't have the " +
|
||||
"property '{1}'",
|
||||
inst_type, property));
|
||||
} else {
|
||||
ReadSkip (reader);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
((IDictionary) instance).Add (
|
||||
property, ReadValue (
|
||||
t_data.ElementType, reader));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
private static IJsonWrapper ReadValue (WrapperFactory factory,
|
||||
JsonReader reader)
|
||||
{
|
||||
reader.Read ();
|
||||
|
||||
if (reader.Token == JsonToken.ArrayEnd ||
|
||||
reader.Token == JsonToken.Null)
|
||||
return null;
|
||||
|
||||
IJsonWrapper instance = factory ();
|
||||
|
||||
if (reader.Token == JsonToken.String) {
|
||||
instance.SetString ((string) reader.Value);
|
||||
return instance;
|
||||
}
|
||||
|
||||
if (reader.Token == JsonToken.Double) {
|
||||
instance.SetDouble ((double) reader.Value);
|
||||
return instance;
|
||||
}
|
||||
|
||||
if (reader.Token == JsonToken.Int) {
|
||||
instance.SetInt ((int) reader.Value);
|
||||
return instance;
|
||||
}
|
||||
|
||||
if (reader.Token == JsonToken.Long) {
|
||||
instance.SetLong ((long) reader.Value);
|
||||
return instance;
|
||||
}
|
||||
|
||||
if (reader.Token == JsonToken.Boolean) {
|
||||
instance.SetBoolean ((bool) reader.Value);
|
||||
return instance;
|
||||
}
|
||||
|
||||
if (reader.Token == JsonToken.ArrayStart) {
|
||||
instance.SetJsonType (JsonType.Array);
|
||||
|
||||
while (true) {
|
||||
IJsonWrapper item = ReadValue (factory, reader);
|
||||
if (item == null && reader.Token == JsonToken.ArrayEnd)
|
||||
break;
|
||||
|
||||
((IList) instance).Add (item);
|
||||
}
|
||||
}
|
||||
else if (reader.Token == JsonToken.ObjectStart) {
|
||||
instance.SetJsonType (JsonType.Object);
|
||||
|
||||
while (true) {
|
||||
reader.Read ();
|
||||
|
||||
if (reader.Token == JsonToken.ObjectEnd)
|
||||
break;
|
||||
|
||||
string property = (string) reader.Value;
|
||||
|
||||
((IDictionary) instance)[property] = ReadValue (
|
||||
factory, reader);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
private static void ReadSkip (JsonReader reader)
|
||||
{
|
||||
ToWrapper (
|
||||
delegate { return new JsonMockWrapper (); }, reader);
|
||||
}
|
||||
|
||||
private static void RegisterBaseExporters ()
|
||||
{
|
||||
base_exporters_table[typeof (byte)] =
|
||||
delegate (object obj, JsonWriter writer) {
|
||||
writer.Write (Convert.ToInt32 ((byte) obj));
|
||||
};
|
||||
|
||||
base_exporters_table[typeof (char)] =
|
||||
delegate (object obj, JsonWriter writer) {
|
||||
writer.Write (Convert.ToString ((char) obj));
|
||||
};
|
||||
|
||||
base_exporters_table[typeof (DateTime)] =
|
||||
delegate (object obj, JsonWriter writer) {
|
||||
writer.Write (Convert.ToString ((DateTime) obj,
|
||||
datetime_format));
|
||||
};
|
||||
|
||||
base_exporters_table[typeof (decimal)] =
|
||||
delegate (object obj, JsonWriter writer) {
|
||||
writer.Write ((decimal) obj);
|
||||
};
|
||||
|
||||
base_exporters_table[typeof (sbyte)] =
|
||||
delegate (object obj, JsonWriter writer) {
|
||||
writer.Write (Convert.ToInt32 ((sbyte) obj));
|
||||
};
|
||||
|
||||
base_exporters_table[typeof (short)] =
|
||||
delegate (object obj, JsonWriter writer) {
|
||||
writer.Write (Convert.ToInt32 ((short) obj));
|
||||
};
|
||||
|
||||
base_exporters_table[typeof (ushort)] =
|
||||
delegate (object obj, JsonWriter writer) {
|
||||
writer.Write (Convert.ToInt32 ((ushort) obj));
|
||||
};
|
||||
|
||||
base_exporters_table[typeof (uint)] =
|
||||
delegate (object obj, JsonWriter writer) {
|
||||
writer.Write (Convert.ToUInt64 ((uint) obj));
|
||||
};
|
||||
|
||||
base_exporters_table[typeof (ulong)] =
|
||||
delegate (object obj, JsonWriter writer) {
|
||||
writer.Write ((ulong) obj);
|
||||
};
|
||||
|
||||
base_exporters_table[typeof(DateTimeOffset)] =
|
||||
delegate (object obj, JsonWriter writer) {
|
||||
writer.Write(((DateTimeOffset)obj).ToString("yyyy-MM-ddTHH:mm:ss.fffffffzzz", datetime_format));
|
||||
};
|
||||
}
|
||||
|
||||
private static void RegisterBaseImporters ()
|
||||
{
|
||||
ImporterFunc importer;
|
||||
|
||||
importer = delegate (object input) {
|
||||
return Convert.ToByte ((int) input);
|
||||
};
|
||||
RegisterImporter (base_importers_table, typeof (int),
|
||||
typeof (byte), importer);
|
||||
|
||||
importer = delegate (object input) {
|
||||
return Convert.ToUInt64 ((int) input);
|
||||
};
|
||||
RegisterImporter (base_importers_table, typeof (int),
|
||||
typeof (ulong), importer);
|
||||
|
||||
importer = delegate (object input) {
|
||||
return Convert.ToInt64((int)input);
|
||||
};
|
||||
RegisterImporter(base_importers_table, typeof(int),
|
||||
typeof(long), importer);
|
||||
|
||||
importer = delegate (object input) {
|
||||
return Convert.ToSByte ((int) input);
|
||||
};
|
||||
RegisterImporter (base_importers_table, typeof (int),
|
||||
typeof (sbyte), importer);
|
||||
|
||||
importer = delegate (object input) {
|
||||
return Convert.ToInt16 ((int) input);
|
||||
};
|
||||
RegisterImporter (base_importers_table, typeof (int),
|
||||
typeof (short), importer);
|
||||
|
||||
importer = delegate (object input) {
|
||||
return Convert.ToUInt16 ((int) input);
|
||||
};
|
||||
RegisterImporter (base_importers_table, typeof (int),
|
||||
typeof (ushort), importer);
|
||||
|
||||
importer = delegate (object input) {
|
||||
return Convert.ToUInt32 ((int) input);
|
||||
};
|
||||
RegisterImporter (base_importers_table, typeof (int),
|
||||
typeof (uint), importer);
|
||||
|
||||
importer = delegate (object input) {
|
||||
return Convert.ToSingle ((int) input);
|
||||
};
|
||||
RegisterImporter (base_importers_table, typeof (int),
|
||||
typeof (float), importer);
|
||||
|
||||
importer = delegate (object input) {
|
||||
return Convert.ToDouble ((int) input);
|
||||
};
|
||||
RegisterImporter (base_importers_table, typeof (int),
|
||||
typeof (double), importer);
|
||||
|
||||
importer = delegate (object input) {
|
||||
return Convert.ToDecimal ((double) input);
|
||||
};
|
||||
RegisterImporter (base_importers_table, typeof (double),
|
||||
typeof (decimal), importer);
|
||||
|
||||
importer = delegate (object input) {
|
||||
return Convert.ToSingle((double)input);
|
||||
};
|
||||
RegisterImporter(base_importers_table, typeof(double),
|
||||
typeof(float), importer);
|
||||
|
||||
importer = delegate (object input) {
|
||||
return Convert.ToUInt32 ((long) input);
|
||||
};
|
||||
RegisterImporter (base_importers_table, typeof (long),
|
||||
typeof (uint), importer);
|
||||
|
||||
importer = delegate (object input) {
|
||||
return Convert.ToChar ((string) input);
|
||||
};
|
||||
RegisterImporter (base_importers_table, typeof (string),
|
||||
typeof (char), importer);
|
||||
|
||||
importer = delegate (object input) {
|
||||
return Convert.ToDateTime ((string) input, datetime_format);
|
||||
};
|
||||
RegisterImporter (base_importers_table, typeof (string),
|
||||
typeof (DateTime), importer);
|
||||
|
||||
importer = delegate (object input) {
|
||||
return DateTimeOffset.Parse((string)input, datetime_format);
|
||||
};
|
||||
RegisterImporter(base_importers_table, typeof(string),
|
||||
typeof(DateTimeOffset), importer);
|
||||
}
|
||||
|
||||
private static void RegisterImporter (
|
||||
IDictionary<Type, IDictionary<Type, ImporterFunc>> table,
|
||||
Type json_type, Type value_type, ImporterFunc importer)
|
||||
{
|
||||
if (! table.ContainsKey (json_type))
|
||||
table.Add (json_type, new Dictionary<Type, ImporterFunc> ());
|
||||
|
||||
table[json_type][value_type] = importer;
|
||||
}
|
||||
|
||||
private static void WriteValue (object obj, JsonWriter writer,
|
||||
bool writer_is_private,
|
||||
int depth)
|
||||
{
|
||||
if (depth > max_nesting_depth)
|
||||
throw new JsonException (
|
||||
String.Format ("Max allowed object depth reached while " +
|
||||
"trying to export from type {0}",
|
||||
obj.GetType ()));
|
||||
|
||||
if (obj == null) {
|
||||
writer.Write (null);
|
||||
return;
|
||||
}
|
||||
|
||||
if (obj is IJsonWrapper) {
|
||||
if (writer_is_private)
|
||||
writer.TextWriter.Write (((IJsonWrapper) obj).ToJson ());
|
||||
else
|
||||
((IJsonWrapper) obj).ToJson (writer);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (obj is String) {
|
||||
writer.Write ((string) obj);
|
||||
return;
|
||||
}
|
||||
|
||||
if (obj is Double) {
|
||||
writer.Write ((double) obj);
|
||||
return;
|
||||
}
|
||||
|
||||
if (obj is Single)
|
||||
{
|
||||
writer.Write((float)obj);
|
||||
return;
|
||||
}
|
||||
|
||||
if (obj is Int32) {
|
||||
writer.Write ((int) obj);
|
||||
return;
|
||||
}
|
||||
|
||||
if (obj is Boolean) {
|
||||
writer.Write ((bool) obj);
|
||||
return;
|
||||
}
|
||||
|
||||
if (obj is Int64) {
|
||||
writer.Write ((long) obj);
|
||||
return;
|
||||
}
|
||||
|
||||
if (obj is Array) {
|
||||
writer.WriteArrayStart ();
|
||||
|
||||
foreach (object elem in (Array) obj)
|
||||
WriteValue (elem, writer, writer_is_private, depth + 1);
|
||||
|
||||
writer.WriteArrayEnd ();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (obj is IList) {
|
||||
writer.WriteArrayStart ();
|
||||
foreach (object elem in (IList) obj)
|
||||
WriteValue (elem, writer, writer_is_private, depth + 1);
|
||||
writer.WriteArrayEnd ();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (obj is IDictionary dictionary) {
|
||||
writer.WriteObjectStart ();
|
||||
foreach (DictionaryEntry entry in dictionary) {
|
||||
var propertyName = entry.Key is string key ?
|
||||
key
|
||||
: Convert.ToString(entry.Key, CultureInfo.InvariantCulture);
|
||||
writer.WritePropertyName (propertyName);
|
||||
WriteValue (entry.Value, writer, writer_is_private,
|
||||
depth + 1);
|
||||
}
|
||||
writer.WriteObjectEnd ();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Type obj_type = obj.GetType ();
|
||||
|
||||
// See if there's a custom exporter for the object
|
||||
if (custom_exporters_table.ContainsKey (obj_type)) {
|
||||
ExporterFunc exporter = custom_exporters_table[obj_type];
|
||||
exporter (obj, writer);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// If not, maybe there's a base exporter
|
||||
if (base_exporters_table.ContainsKey (obj_type)) {
|
||||
ExporterFunc exporter = base_exporters_table[obj_type];
|
||||
exporter (obj, writer);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Last option, let's see if it's an enum
|
||||
if (obj is Enum) {
|
||||
Type e_type = Enum.GetUnderlyingType (obj_type);
|
||||
|
||||
if (e_type == typeof (long))
|
||||
writer.Write ((long) obj);
|
||||
else if (e_type == typeof (uint))
|
||||
writer.Write ((uint) obj);
|
||||
else if (e_type == typeof (ulong))
|
||||
writer.Write ((ulong) obj);
|
||||
else if (e_type == typeof(ushort))
|
||||
writer.Write ((ushort)obj);
|
||||
else if (e_type == typeof(short))
|
||||
writer.Write ((short)obj);
|
||||
else if (e_type == typeof(byte))
|
||||
writer.Write ((byte)obj);
|
||||
else if (e_type == typeof(sbyte))
|
||||
writer.Write ((sbyte)obj);
|
||||
else
|
||||
writer.Write ((int) obj);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Okay, so it looks like the input should be exported as an
|
||||
// object
|
||||
AddTypeProperties (obj_type);
|
||||
IList<PropertyMetadata> props = type_properties[obj_type];
|
||||
|
||||
writer.WriteObjectStart ();
|
||||
foreach (PropertyMetadata p_data in props) {
|
||||
if (p_data.IsField) {
|
||||
writer.WritePropertyName (p_data.Info.Name);
|
||||
WriteValue (((FieldInfo) p_data.Info).GetValue (obj),
|
||||
writer, writer_is_private, depth + 1);
|
||||
}
|
||||
else {
|
||||
PropertyInfo p_info = (PropertyInfo) p_data.Info;
|
||||
|
||||
if (p_info.CanRead) {
|
||||
writer.WritePropertyName (p_data.Info.Name);
|
||||
WriteValue (p_info.GetValue (obj, null),
|
||||
writer, writer_is_private, depth + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
writer.WriteObjectEnd ();
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
public static string ToJson (object obj)
|
||||
{
|
||||
lock (static_writer_lock) {
|
||||
static_writer.Reset ();
|
||||
|
||||
WriteValue (obj, static_writer, true, 0);
|
||||
|
||||
return static_writer.ToString ();
|
||||
}
|
||||
}
|
||||
|
||||
public static void ToJson (object obj, JsonWriter writer)
|
||||
{
|
||||
WriteValue (obj, writer, false, 0);
|
||||
}
|
||||
|
||||
public static JsonData ToObject (JsonReader reader)
|
||||
{
|
||||
return (JsonData) ToWrapper (
|
||||
delegate { return new JsonData (); }, reader);
|
||||
}
|
||||
|
||||
public static JsonData ToObject (TextReader reader)
|
||||
{
|
||||
JsonReader json_reader = new JsonReader (reader);
|
||||
|
||||
return (JsonData) ToWrapper (
|
||||
delegate { return new JsonData (); }, json_reader);
|
||||
}
|
||||
|
||||
public static JsonData ToObject (string json)
|
||||
{
|
||||
return (JsonData) ToWrapper (
|
||||
delegate { return new JsonData (); }, json);
|
||||
}
|
||||
|
||||
public static T ToObject<T> (JsonReader reader)
|
||||
{
|
||||
return (T) ReadValue (typeof (T), reader);
|
||||
}
|
||||
|
||||
public static T ToObject<T> (TextReader reader)
|
||||
{
|
||||
JsonReader json_reader = new JsonReader (reader);
|
||||
|
||||
return (T) ReadValue (typeof (T), json_reader);
|
||||
}
|
||||
|
||||
public static T ToObject<T> (string json)
|
||||
{
|
||||
JsonReader reader = new JsonReader (json);
|
||||
|
||||
return (T) ReadValue (typeof (T), reader);
|
||||
}
|
||||
|
||||
public static object ToObject(string json, Type ConvertType )
|
||||
{
|
||||
JsonReader reader = new JsonReader(json);
|
||||
|
||||
return ReadValue(ConvertType, reader);
|
||||
}
|
||||
|
||||
public static IJsonWrapper ToWrapper (WrapperFactory factory,
|
||||
JsonReader reader)
|
||||
{
|
||||
return ReadValue (factory, reader);
|
||||
}
|
||||
|
||||
public static IJsonWrapper ToWrapper (WrapperFactory factory,
|
||||
string json)
|
||||
{
|
||||
JsonReader reader = new JsonReader (json);
|
||||
|
||||
return ReadValue (factory, reader);
|
||||
}
|
||||
|
||||
public static void RegisterExporter<T> (ExporterFunc<T> exporter)
|
||||
{
|
||||
ExporterFunc exporter_wrapper =
|
||||
delegate (object obj, JsonWriter writer) {
|
||||
exporter ((T) obj, writer);
|
||||
};
|
||||
|
||||
custom_exporters_table[typeof (T)] = exporter_wrapper;
|
||||
}
|
||||
|
||||
public static void RegisterImporter<TJson, TValue> (
|
||||
ImporterFunc<TJson, TValue> importer)
|
||||
{
|
||||
ImporterFunc importer_wrapper =
|
||||
delegate (object input) {
|
||||
return importer ((TJson) input);
|
||||
};
|
||||
|
||||
RegisterImporter (custom_importers_table, typeof (TJson),
|
||||
typeof (TValue), importer_wrapper);
|
||||
}
|
||||
|
||||
public static void UnregisterExporters ()
|
||||
{
|
||||
custom_exporters_table.Clear ();
|
||||
}
|
||||
|
||||
public static void UnregisterImporters ()
|
||||
{
|
||||
custom_importers_table.Clear ();
|
||||
}
|
||||
}
|
||||
}
|
105
TEngineHotUpdate/src/TEngineCore/Json/LitJson/JsonMockWrapper.cs
Normal file
105
TEngineHotUpdate/src/TEngineCore/Json/LitJson/JsonMockWrapper.cs
Normal file
@@ -0,0 +1,105 @@
|
||||
#region Header
|
||||
/**
|
||||
* JsonMockWrapper.cs
|
||||
* Mock object implementing IJsonWrapper, to facilitate actions like
|
||||
* skipping data more efficiently.
|
||||
*
|
||||
* The authors disclaim copyright to this source code. For more details, see
|
||||
* the COPYING file included with this distribution.
|
||||
**/
|
||||
#endregion
|
||||
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Specialized;
|
||||
|
||||
|
||||
namespace TEngineCore.ListJson
|
||||
{
|
||||
public class JsonMockWrapper : IJsonWrapper
|
||||
{
|
||||
public bool IsArray { get { return false; } }
|
||||
public bool IsBoolean { get { return false; } }
|
||||
public bool IsDouble { get { return false; } }
|
||||
public bool IsInt { get { return false; } }
|
||||
public bool IsLong { get { return false; } }
|
||||
public bool IsObject { get { return false; } }
|
||||
public bool IsString { get { return false; } }
|
||||
|
||||
public bool GetBoolean () { return false; }
|
||||
public double GetDouble () { return 0.0; }
|
||||
public int GetInt () { return 0; }
|
||||
public JsonType GetJsonType () { return JsonType.None; }
|
||||
public long GetLong () { return 0L; }
|
||||
public string GetString () { return ""; }
|
||||
|
||||
public void SetBoolean (bool val) {}
|
||||
public void SetDouble (double val) {}
|
||||
public void SetInt (int val) {}
|
||||
public void SetJsonType (JsonType type) {}
|
||||
public void SetLong (long val) {}
|
||||
public void SetString (string val) {}
|
||||
|
||||
public string ToJson () { return ""; }
|
||||
public void ToJson (JsonWriter writer) {}
|
||||
|
||||
|
||||
bool IList.IsFixedSize { get { return true; } }
|
||||
bool IList.IsReadOnly { get { return true; } }
|
||||
|
||||
object IList.this[int index] {
|
||||
get { return null; }
|
||||
set {}
|
||||
}
|
||||
|
||||
int IList.Add (object value) { return 0; }
|
||||
void IList.Clear () {}
|
||||
bool IList.Contains (object value) { return false; }
|
||||
int IList.IndexOf (object value) { return -1; }
|
||||
void IList.Insert (int i, object v) {}
|
||||
void IList.Remove (object value) {}
|
||||
void IList.RemoveAt (int index) {}
|
||||
|
||||
|
||||
int ICollection.Count { get { return 0; } }
|
||||
bool ICollection.IsSynchronized { get { return false; } }
|
||||
object ICollection.SyncRoot { get { return null; } }
|
||||
|
||||
void ICollection.CopyTo (Array array, int index) {}
|
||||
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator () { return null; }
|
||||
|
||||
|
||||
bool IDictionary.IsFixedSize { get { return true; } }
|
||||
bool IDictionary.IsReadOnly { get { return true; } }
|
||||
|
||||
ICollection IDictionary.Keys { get { return null; } }
|
||||
ICollection IDictionary.Values { get { return null; } }
|
||||
|
||||
object IDictionary.this[object key] {
|
||||
get { return null; }
|
||||
set {}
|
||||
}
|
||||
|
||||
void IDictionary.Add (object k, object v) {}
|
||||
void IDictionary.Clear () {}
|
||||
bool IDictionary.Contains (object key) { return false; }
|
||||
void IDictionary.Remove (object key) {}
|
||||
|
||||
IDictionaryEnumerator IDictionary.GetEnumerator () { return null; }
|
||||
|
||||
|
||||
object IOrderedDictionary.this[int idx] {
|
||||
get { return null; }
|
||||
set {}
|
||||
}
|
||||
|
||||
IDictionaryEnumerator IOrderedDictionary.GetEnumerator () {
|
||||
return null;
|
||||
}
|
||||
void IOrderedDictionary.Insert (int i, object k, object v) {}
|
||||
void IOrderedDictionary.RemoveAt (int i) {}
|
||||
}
|
||||
}
|
478
TEngineHotUpdate/src/TEngineCore/Json/LitJson/JsonReader.cs
Normal file
478
TEngineHotUpdate/src/TEngineCore/Json/LitJson/JsonReader.cs
Normal file
@@ -0,0 +1,478 @@
|
||||
#region Header
|
||||
/**
|
||||
* JsonReader.cs
|
||||
* Stream-like access to JSON text.
|
||||
*
|
||||
* The authors disclaim copyright to this source code. For more details, see
|
||||
* the COPYING file included with this distribution.
|
||||
**/
|
||||
#endregion
|
||||
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
|
||||
namespace TEngineCore.ListJson
|
||||
{
|
||||
public enum JsonToken
|
||||
{
|
||||
None,
|
||||
|
||||
ObjectStart,
|
||||
PropertyName,
|
||||
ObjectEnd,
|
||||
|
||||
ArrayStart,
|
||||
ArrayEnd,
|
||||
|
||||
Int,
|
||||
Long,
|
||||
Double,
|
||||
|
||||
String,
|
||||
|
||||
Boolean,
|
||||
Null
|
||||
}
|
||||
|
||||
|
||||
public class JsonReader
|
||||
{
|
||||
#region Fields
|
||||
private static readonly IDictionary<int, IDictionary<int, int[]>> parse_table;
|
||||
|
||||
private Stack<int> automaton_stack;
|
||||
private int current_input;
|
||||
private int current_symbol;
|
||||
private bool end_of_json;
|
||||
private bool end_of_input;
|
||||
private Lexer lexer;
|
||||
private bool parser_in_string;
|
||||
private bool parser_return;
|
||||
private bool read_started;
|
||||
private TextReader reader;
|
||||
private bool reader_is_owned;
|
||||
private bool skip_non_members;
|
||||
private object token_value;
|
||||
private JsonToken token;
|
||||
#endregion
|
||||
|
||||
|
||||
#region Public Properties
|
||||
public bool AllowComments {
|
||||
get { return lexer.AllowComments; }
|
||||
set { lexer.AllowComments = value; }
|
||||
}
|
||||
|
||||
public bool AllowSingleQuotedStrings {
|
||||
get { return lexer.AllowSingleQuotedStrings; }
|
||||
set { lexer.AllowSingleQuotedStrings = value; }
|
||||
}
|
||||
|
||||
public bool SkipNonMembers {
|
||||
get { return skip_non_members; }
|
||||
set { skip_non_members = value; }
|
||||
}
|
||||
|
||||
public bool EndOfInput {
|
||||
get { return end_of_input; }
|
||||
}
|
||||
|
||||
public bool EndOfJson {
|
||||
get { return end_of_json; }
|
||||
}
|
||||
|
||||
public JsonToken Token {
|
||||
get { return token; }
|
||||
}
|
||||
|
||||
public object Value {
|
||||
get { return token_value; }
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
#region Constructors
|
||||
static JsonReader ()
|
||||
{
|
||||
parse_table = PopulateParseTable ();
|
||||
}
|
||||
|
||||
public JsonReader (string json_text) :
|
||||
this (new StringReader (json_text), true)
|
||||
{
|
||||
}
|
||||
|
||||
public JsonReader (TextReader reader) :
|
||||
this (reader, false)
|
||||
{
|
||||
}
|
||||
|
||||
private JsonReader (TextReader reader, bool owned)
|
||||
{
|
||||
if (reader == null)
|
||||
throw new ArgumentNullException ("reader");
|
||||
|
||||
parser_in_string = false;
|
||||
parser_return = false;
|
||||
|
||||
read_started = false;
|
||||
automaton_stack = new Stack<int> ();
|
||||
automaton_stack.Push ((int) ParserToken.End);
|
||||
automaton_stack.Push ((int) ParserToken.Text);
|
||||
|
||||
lexer = new Lexer (reader);
|
||||
|
||||
end_of_input = false;
|
||||
end_of_json = false;
|
||||
|
||||
skip_non_members = true;
|
||||
|
||||
this.reader = reader;
|
||||
reader_is_owned = owned;
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
#region Static Methods
|
||||
private static IDictionary<int, IDictionary<int, int[]>> PopulateParseTable ()
|
||||
{
|
||||
// See section A.2. of the manual for details
|
||||
IDictionary<int, IDictionary<int, int[]>> parse_table = new Dictionary<int, IDictionary<int, int[]>> ();
|
||||
|
||||
TableAddRow (parse_table, ParserToken.Array);
|
||||
TableAddCol (parse_table, ParserToken.Array, '[',
|
||||
'[',
|
||||
(int) ParserToken.ArrayPrime);
|
||||
|
||||
TableAddRow (parse_table, ParserToken.ArrayPrime);
|
||||
TableAddCol (parse_table, ParserToken.ArrayPrime, '"',
|
||||
(int) ParserToken.Value,
|
||||
|
||||
(int) ParserToken.ValueRest,
|
||||
']');
|
||||
TableAddCol (parse_table, ParserToken.ArrayPrime, '[',
|
||||
(int) ParserToken.Value,
|
||||
(int) ParserToken.ValueRest,
|
||||
']');
|
||||
TableAddCol (parse_table, ParserToken.ArrayPrime, ']',
|
||||
']');
|
||||
TableAddCol (parse_table, ParserToken.ArrayPrime, '{',
|
||||
(int) ParserToken.Value,
|
||||
(int) ParserToken.ValueRest,
|
||||
']');
|
||||
TableAddCol (parse_table, ParserToken.ArrayPrime, (int) ParserToken.Number,
|
||||
(int) ParserToken.Value,
|
||||
(int) ParserToken.ValueRest,
|
||||
']');
|
||||
TableAddCol (parse_table, ParserToken.ArrayPrime, (int) ParserToken.True,
|
||||
(int) ParserToken.Value,
|
||||
(int) ParserToken.ValueRest,
|
||||
']');
|
||||
TableAddCol (parse_table, ParserToken.ArrayPrime, (int) ParserToken.False,
|
||||
(int) ParserToken.Value,
|
||||
(int) ParserToken.ValueRest,
|
||||
']');
|
||||
TableAddCol (parse_table, ParserToken.ArrayPrime, (int) ParserToken.Null,
|
||||
(int) ParserToken.Value,
|
||||
(int) ParserToken.ValueRest,
|
||||
']');
|
||||
|
||||
TableAddRow (parse_table, ParserToken.Object);
|
||||
TableAddCol (parse_table, ParserToken.Object, '{',
|
||||
'{',
|
||||
(int) ParserToken.ObjectPrime);
|
||||
|
||||
TableAddRow (parse_table, ParserToken.ObjectPrime);
|
||||
TableAddCol (parse_table, ParserToken.ObjectPrime, '"',
|
||||
(int) ParserToken.Pair,
|
||||
(int) ParserToken.PairRest,
|
||||
'}');
|
||||
TableAddCol (parse_table, ParserToken.ObjectPrime, '}',
|
||||
'}');
|
||||
|
||||
TableAddRow (parse_table, ParserToken.Pair);
|
||||
TableAddCol (parse_table, ParserToken.Pair, '"',
|
||||
(int) ParserToken.String,
|
||||
':',
|
||||
(int) ParserToken.Value);
|
||||
|
||||
TableAddRow (parse_table, ParserToken.PairRest);
|
||||
TableAddCol (parse_table, ParserToken.PairRest, ',',
|
||||
',',
|
||||
(int) ParserToken.Pair,
|
||||
(int) ParserToken.PairRest);
|
||||
TableAddCol (parse_table, ParserToken.PairRest, '}',
|
||||
(int) ParserToken.Epsilon);
|
||||
|
||||
TableAddRow (parse_table, ParserToken.String);
|
||||
TableAddCol (parse_table, ParserToken.String, '"',
|
||||
'"',
|
||||
(int) ParserToken.CharSeq,
|
||||
'"');
|
||||
|
||||
TableAddRow (parse_table, ParserToken.Text);
|
||||
TableAddCol (parse_table, ParserToken.Text, '[',
|
||||
(int) ParserToken.Array);
|
||||
TableAddCol (parse_table, ParserToken.Text, '{',
|
||||
(int) ParserToken.Object);
|
||||
|
||||
TableAddRow (parse_table, ParserToken.Value);
|
||||
TableAddCol (parse_table, ParserToken.Value, '"',
|
||||
(int) ParserToken.String);
|
||||
TableAddCol (parse_table, ParserToken.Value, '[',
|
||||
(int) ParserToken.Array);
|
||||
TableAddCol (parse_table, ParserToken.Value, '{',
|
||||
(int) ParserToken.Object);
|
||||
TableAddCol (parse_table, ParserToken.Value, (int) ParserToken.Number,
|
||||
(int) ParserToken.Number);
|
||||
TableAddCol (parse_table, ParserToken.Value, (int) ParserToken.True,
|
||||
(int) ParserToken.True);
|
||||
TableAddCol (parse_table, ParserToken.Value, (int) ParserToken.False,
|
||||
(int) ParserToken.False);
|
||||
TableAddCol (parse_table, ParserToken.Value, (int) ParserToken.Null,
|
||||
(int) ParserToken.Null);
|
||||
|
||||
TableAddRow (parse_table, ParserToken.ValueRest);
|
||||
TableAddCol (parse_table, ParserToken.ValueRest, ',',
|
||||
',',
|
||||
(int) ParserToken.Value,
|
||||
(int) ParserToken.ValueRest);
|
||||
TableAddCol (parse_table, ParserToken.ValueRest, ']',
|
||||
(int) ParserToken.Epsilon);
|
||||
|
||||
return parse_table;
|
||||
}
|
||||
|
||||
private static void TableAddCol (IDictionary<int, IDictionary<int, int[]>> parse_table, ParserToken row, int col,
|
||||
params int[] symbols)
|
||||
{
|
||||
parse_table[(int) row].Add (col, symbols);
|
||||
}
|
||||
|
||||
private static void TableAddRow (IDictionary<int, IDictionary<int, int[]>> parse_table, ParserToken rule)
|
||||
{
|
||||
parse_table.Add ((int) rule, new Dictionary<int, int[]> ());
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
#region Private Methods
|
||||
private void ProcessNumber (string number)
|
||||
{
|
||||
if (number.IndexOf ('.') != -1 ||
|
||||
number.IndexOf ('e') != -1 ||
|
||||
number.IndexOf ('E') != -1) {
|
||||
|
||||
double n_double;
|
||||
if (double.TryParse (number, NumberStyles.Any, CultureInfo.InvariantCulture, out n_double)) {
|
||||
token = JsonToken.Double;
|
||||
token_value = n_double;
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
int n_int32;
|
||||
if (int.TryParse (number, NumberStyles.Integer, CultureInfo.InvariantCulture, out n_int32)) {
|
||||
token = JsonToken.Int;
|
||||
token_value = n_int32;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
long n_int64;
|
||||
if (long.TryParse (number, NumberStyles.Integer, CultureInfo.InvariantCulture, out n_int64)) {
|
||||
token = JsonToken.Long;
|
||||
token_value = n_int64;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
ulong n_uint64;
|
||||
if (ulong.TryParse(number, NumberStyles.Integer, CultureInfo.InvariantCulture, out n_uint64))
|
||||
{
|
||||
token = JsonToken.Long;
|
||||
token_value = n_uint64;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Shouldn't happen, but just in case, return something
|
||||
token = JsonToken.Int;
|
||||
token_value = 0;
|
||||
}
|
||||
|
||||
private void ProcessSymbol ()
|
||||
{
|
||||
if (current_symbol == '[') {
|
||||
token = JsonToken.ArrayStart;
|
||||
parser_return = true;
|
||||
|
||||
} else if (current_symbol == ']') {
|
||||
token = JsonToken.ArrayEnd;
|
||||
parser_return = true;
|
||||
|
||||
} else if (current_symbol == '{') {
|
||||
token = JsonToken.ObjectStart;
|
||||
parser_return = true;
|
||||
|
||||
} else if (current_symbol == '}') {
|
||||
token = JsonToken.ObjectEnd;
|
||||
parser_return = true;
|
||||
|
||||
} else if (current_symbol == '"') {
|
||||
if (parser_in_string) {
|
||||
parser_in_string = false;
|
||||
|
||||
parser_return = true;
|
||||
|
||||
} else {
|
||||
if (token == JsonToken.None)
|
||||
token = JsonToken.String;
|
||||
|
||||
parser_in_string = true;
|
||||
}
|
||||
|
||||
} else if (current_symbol == (int) ParserToken.CharSeq) {
|
||||
token_value = lexer.StringValue;
|
||||
|
||||
} else if (current_symbol == (int) ParserToken.False) {
|
||||
token = JsonToken.Boolean;
|
||||
token_value = false;
|
||||
parser_return = true;
|
||||
|
||||
} else if (current_symbol == (int) ParserToken.Null) {
|
||||
token = JsonToken.Null;
|
||||
parser_return = true;
|
||||
|
||||
} else if (current_symbol == (int) ParserToken.Number) {
|
||||
ProcessNumber (lexer.StringValue);
|
||||
|
||||
parser_return = true;
|
||||
|
||||
} else if (current_symbol == (int) ParserToken.Pair) {
|
||||
token = JsonToken.PropertyName;
|
||||
|
||||
} else if (current_symbol == (int) ParserToken.True) {
|
||||
token = JsonToken.Boolean;
|
||||
token_value = true;
|
||||
parser_return = true;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private bool ReadToken ()
|
||||
{
|
||||
if (end_of_input)
|
||||
return false;
|
||||
|
||||
lexer.NextToken ();
|
||||
|
||||
if (lexer.EndOfInput) {
|
||||
Close ();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
current_input = lexer.Token;
|
||||
|
||||
return true;
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
public void Close ()
|
||||
{
|
||||
if (end_of_input)
|
||||
return;
|
||||
|
||||
end_of_input = true;
|
||||
end_of_json = true;
|
||||
|
||||
if (reader_is_owned)
|
||||
{
|
||||
using(reader){}
|
||||
}
|
||||
|
||||
reader = null;
|
||||
}
|
||||
|
||||
public bool Read ()
|
||||
{
|
||||
if (end_of_input)
|
||||
return false;
|
||||
|
||||
if (end_of_json) {
|
||||
end_of_json = false;
|
||||
automaton_stack.Clear ();
|
||||
automaton_stack.Push ((int) ParserToken.End);
|
||||
automaton_stack.Push ((int) ParserToken.Text);
|
||||
}
|
||||
|
||||
parser_in_string = false;
|
||||
parser_return = false;
|
||||
|
||||
token = JsonToken.None;
|
||||
token_value = null;
|
||||
|
||||
if (! read_started) {
|
||||
read_started = true;
|
||||
|
||||
if (! ReadToken ())
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
int[] entry_symbols;
|
||||
|
||||
while (true) {
|
||||
if (parser_return) {
|
||||
if (automaton_stack.Peek () == (int) ParserToken.End)
|
||||
end_of_json = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
current_symbol = automaton_stack.Pop ();
|
||||
|
||||
ProcessSymbol ();
|
||||
|
||||
if (current_symbol == current_input) {
|
||||
if (! ReadToken ()) {
|
||||
if (automaton_stack.Peek () != (int) ParserToken.End)
|
||||
throw new JsonException (
|
||||
"Input doesn't evaluate to proper JSON text");
|
||||
|
||||
if (parser_return)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
entry_symbols =
|
||||
parse_table[current_symbol][current_input];
|
||||
|
||||
} catch (KeyNotFoundException e) {
|
||||
throw new JsonException ((ParserToken) current_input, e);
|
||||
}
|
||||
|
||||
if (entry_symbols[0] == (int) ParserToken.Epsilon)
|
||||
continue;
|
||||
|
||||
for (int i = entry_symbols.Length - 1; i >= 0; i--)
|
||||
automaton_stack.Push (entry_symbols[i]);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
486
TEngineHotUpdate/src/TEngineCore/Json/LitJson/JsonWriter.cs
Normal file
486
TEngineHotUpdate/src/TEngineCore/Json/LitJson/JsonWriter.cs
Normal file
@@ -0,0 +1,486 @@
|
||||
#region Header
|
||||
/**
|
||||
* JsonWriter.cs
|
||||
* Stream-like facility to output JSON text.
|
||||
*
|
||||
* The authors disclaim copyright to this source code. For more details, see
|
||||
* the COPYING file included with this distribution.
|
||||
**/
|
||||
#endregion
|
||||
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
|
||||
namespace TEngineCore.ListJson
|
||||
{
|
||||
internal enum Condition
|
||||
{
|
||||
InArray,
|
||||
InObject,
|
||||
NotAProperty,
|
||||
Property,
|
||||
Value
|
||||
}
|
||||
|
||||
internal class WriterContext
|
||||
{
|
||||
public int Count;
|
||||
public bool InArray;
|
||||
public bool InObject;
|
||||
public bool ExpectingValue;
|
||||
public int Padding;
|
||||
}
|
||||
|
||||
public class JsonWriter
|
||||
{
|
||||
#region Fields
|
||||
private static readonly NumberFormatInfo number_format;
|
||||
|
||||
private WriterContext context;
|
||||
private Stack<WriterContext> ctx_stack;
|
||||
private bool has_reached_end;
|
||||
private char[] hex_seq;
|
||||
private int indentation;
|
||||
private int indent_value;
|
||||
private StringBuilder inst_string_builder;
|
||||
private bool pretty_print;
|
||||
private bool validate;
|
||||
private bool lower_case_properties;
|
||||
private TextWriter writer;
|
||||
#endregion
|
||||
|
||||
|
||||
#region Properties
|
||||
public int IndentValue {
|
||||
get { return indent_value; }
|
||||
set {
|
||||
indentation = (indentation / indent_value) * value;
|
||||
indent_value = value;
|
||||
}
|
||||
}
|
||||
|
||||
public bool PrettyPrint {
|
||||
get { return pretty_print; }
|
||||
set { pretty_print = value; }
|
||||
}
|
||||
|
||||
public TextWriter TextWriter {
|
||||
get { return writer; }
|
||||
}
|
||||
|
||||
public bool Validate {
|
||||
get { return validate; }
|
||||
set { validate = value; }
|
||||
}
|
||||
|
||||
public bool LowerCaseProperties {
|
||||
get { return lower_case_properties; }
|
||||
set { lower_case_properties = value; }
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
#region Constructors
|
||||
static JsonWriter ()
|
||||
{
|
||||
number_format = NumberFormatInfo.InvariantInfo;
|
||||
}
|
||||
|
||||
public JsonWriter ()
|
||||
{
|
||||
inst_string_builder = new StringBuilder ();
|
||||
writer = new StringWriter (inst_string_builder);
|
||||
|
||||
Init ();
|
||||
}
|
||||
|
||||
public JsonWriter (StringBuilder sb) :
|
||||
this (new StringWriter (sb))
|
||||
{
|
||||
}
|
||||
|
||||
public JsonWriter (TextWriter writer)
|
||||
{
|
||||
if (writer == null)
|
||||
throw new ArgumentNullException ("writer");
|
||||
|
||||
this.writer = writer;
|
||||
|
||||
Init ();
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
#region Private Methods
|
||||
private void DoValidation (Condition cond)
|
||||
{
|
||||
if (! context.ExpectingValue)
|
||||
context.Count++;
|
||||
|
||||
if (! validate)
|
||||
return;
|
||||
|
||||
if (has_reached_end)
|
||||
throw new JsonException (
|
||||
"A complete JSON symbol has already been written");
|
||||
|
||||
switch (cond) {
|
||||
case Condition.InArray:
|
||||
if (! context.InArray)
|
||||
throw new JsonException (
|
||||
"Can't close an array here");
|
||||
break;
|
||||
|
||||
case Condition.InObject:
|
||||
if (! context.InObject || context.ExpectingValue)
|
||||
throw new JsonException (
|
||||
"Can't close an object here");
|
||||
break;
|
||||
|
||||
case Condition.NotAProperty:
|
||||
if (context.InObject && ! context.ExpectingValue)
|
||||
throw new JsonException (
|
||||
"Expected a property");
|
||||
break;
|
||||
|
||||
case Condition.Property:
|
||||
if (! context.InObject || context.ExpectingValue)
|
||||
throw new JsonException (
|
||||
"Can't add a property here");
|
||||
break;
|
||||
|
||||
case Condition.Value:
|
||||
if (! context.InArray &&
|
||||
(! context.InObject || ! context.ExpectingValue))
|
||||
throw new JsonException (
|
||||
"Can't add a value here");
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void Init ()
|
||||
{
|
||||
has_reached_end = false;
|
||||
hex_seq = new char[4];
|
||||
indentation = 0;
|
||||
indent_value = 4;
|
||||
pretty_print = false;
|
||||
validate = true;
|
||||
lower_case_properties = false;
|
||||
|
||||
ctx_stack = new Stack<WriterContext> ();
|
||||
context = new WriterContext ();
|
||||
ctx_stack.Push (context);
|
||||
}
|
||||
|
||||
private static void IntToHex (int n, char[] hex)
|
||||
{
|
||||
int num;
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
num = n % 16;
|
||||
|
||||
if (num < 10)
|
||||
hex[3 - i] = (char) ('0' + num);
|
||||
else
|
||||
hex[3 - i] = (char) ('A' + (num - 10));
|
||||
|
||||
n >>= 4;
|
||||
}
|
||||
}
|
||||
|
||||
private void Indent ()
|
||||
{
|
||||
if (pretty_print)
|
||||
indentation += indent_value;
|
||||
}
|
||||
|
||||
|
||||
private void Put (string str)
|
||||
{
|
||||
if (pretty_print && ! context.ExpectingValue)
|
||||
for (int i = 0; i < indentation; i++)
|
||||
writer.Write (' ');
|
||||
|
||||
writer.Write (str);
|
||||
}
|
||||
|
||||
private void PutNewline ()
|
||||
{
|
||||
PutNewline (true);
|
||||
}
|
||||
|
||||
private void PutNewline (bool add_comma)
|
||||
{
|
||||
if (add_comma && ! context.ExpectingValue &&
|
||||
context.Count > 1)
|
||||
writer.Write (',');
|
||||
|
||||
if (pretty_print && ! context.ExpectingValue)
|
||||
writer.Write (Environment.NewLine);
|
||||
}
|
||||
|
||||
private void PutString (string str)
|
||||
{
|
||||
Put (String.Empty);
|
||||
|
||||
writer.Write ('"');
|
||||
|
||||
int n = str.Length;
|
||||
for (int i = 0; i < n; i++) {
|
||||
switch (str[i]) {
|
||||
case '\n':
|
||||
writer.Write ("\\n");
|
||||
continue;
|
||||
|
||||
case '\r':
|
||||
writer.Write ("\\r");
|
||||
continue;
|
||||
|
||||
case '\t':
|
||||
writer.Write ("\\t");
|
||||
continue;
|
||||
|
||||
case '"':
|
||||
case '\\':
|
||||
writer.Write ('\\');
|
||||
writer.Write (str[i]);
|
||||
continue;
|
||||
|
||||
case '\f':
|
||||
writer.Write ("\\f");
|
||||
continue;
|
||||
|
||||
case '\b':
|
||||
writer.Write ("\\b");
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((int) str[i] >= 32 && (int) str[i] <= 126) {
|
||||
writer.Write (str[i]);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Default, turn into a \uXXXX sequence
|
||||
IntToHex ((int) str[i], hex_seq);
|
||||
writer.Write ("\\u");
|
||||
writer.Write (hex_seq);
|
||||
}
|
||||
|
||||
writer.Write ('"');
|
||||
}
|
||||
|
||||
private void Unindent ()
|
||||
{
|
||||
if (pretty_print)
|
||||
indentation -= indent_value;
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
public override string ToString ()
|
||||
{
|
||||
if (inst_string_builder == null)
|
||||
return String.Empty;
|
||||
|
||||
return inst_string_builder.ToString ();
|
||||
}
|
||||
|
||||
public void Reset ()
|
||||
{
|
||||
has_reached_end = false;
|
||||
|
||||
ctx_stack.Clear ();
|
||||
context = new WriterContext ();
|
||||
ctx_stack.Push (context);
|
||||
|
||||
if (inst_string_builder != null)
|
||||
inst_string_builder.Remove (0, inst_string_builder.Length);
|
||||
}
|
||||
|
||||
public void Write (bool boolean)
|
||||
{
|
||||
DoValidation (Condition.Value);
|
||||
PutNewline ();
|
||||
|
||||
Put (boolean ? "true" : "false");
|
||||
|
||||
context.ExpectingValue = false;
|
||||
}
|
||||
|
||||
public void Write (decimal number)
|
||||
{
|
||||
DoValidation (Condition.Value);
|
||||
PutNewline ();
|
||||
|
||||
Put (Convert.ToString (number, number_format));
|
||||
|
||||
context.ExpectingValue = false;
|
||||
}
|
||||
|
||||
public void Write (double number)
|
||||
{
|
||||
DoValidation (Condition.Value);
|
||||
PutNewline ();
|
||||
|
||||
string str = Convert.ToString (number, number_format);
|
||||
Put (str);
|
||||
|
||||
if (str.IndexOf ('.') == -1 &&
|
||||
str.IndexOf ('E') == -1)
|
||||
writer.Write (".0");
|
||||
|
||||
context.ExpectingValue = false;
|
||||
}
|
||||
|
||||
public void Write(float number)
|
||||
{
|
||||
DoValidation(Condition.Value);
|
||||
PutNewline();
|
||||
|
||||
string str = Convert.ToString(number, number_format);
|
||||
Put(str);
|
||||
|
||||
context.ExpectingValue = false;
|
||||
}
|
||||
|
||||
public void Write (int number)
|
||||
{
|
||||
DoValidation (Condition.Value);
|
||||
PutNewline ();
|
||||
|
||||
Put (Convert.ToString (number, number_format));
|
||||
|
||||
context.ExpectingValue = false;
|
||||
}
|
||||
|
||||
public void Write (long number)
|
||||
{
|
||||
DoValidation (Condition.Value);
|
||||
PutNewline ();
|
||||
|
||||
Put (Convert.ToString (number, number_format));
|
||||
|
||||
context.ExpectingValue = false;
|
||||
}
|
||||
|
||||
public void Write (string str)
|
||||
{
|
||||
DoValidation (Condition.Value);
|
||||
PutNewline ();
|
||||
|
||||
if (str == null)
|
||||
Put ("null");
|
||||
else
|
||||
PutString (str);
|
||||
|
||||
context.ExpectingValue = false;
|
||||
}
|
||||
|
||||
[CLSCompliant(false)]
|
||||
#pragma warning disable CS3021 // <20><><EFBFBD>ڳ<EFBFBD><DAB3><EFBFBD><EFBFBD><EFBFBD>û<EFBFBD><C3BB> CLSCompliant <20><><EFBFBD>ԣ<EFBFBD><D4A3><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ͻ<EFBFBD><CDBB><EFBFBD>Ա<EFBFBD><D4B1><EFBFBD><EFBFBD>Ҫ CLSCompliant <20><><EFBFBD><EFBFBD>
|
||||
public void Write (ulong number)
|
||||
#pragma warning restore CS3021 // <20><><EFBFBD>ڳ<EFBFBD><DAB3><EFBFBD><EFBFBD><EFBFBD>û<EFBFBD><C3BB> CLSCompliant <20><><EFBFBD>ԣ<EFBFBD><D4A3><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ͻ<EFBFBD><CDBB><EFBFBD>Ա<EFBFBD><D4B1><EFBFBD><EFBFBD>Ҫ CLSCompliant <20><><EFBFBD><EFBFBD>
|
||||
{
|
||||
DoValidation (Condition.Value);
|
||||
PutNewline ();
|
||||
|
||||
Put (Convert.ToString (number, number_format));
|
||||
|
||||
context.ExpectingValue = false;
|
||||
}
|
||||
|
||||
public void WriteArrayEnd ()
|
||||
{
|
||||
DoValidation (Condition.InArray);
|
||||
PutNewline (false);
|
||||
|
||||
ctx_stack.Pop ();
|
||||
if (ctx_stack.Count == 1)
|
||||
has_reached_end = true;
|
||||
else {
|
||||
context = ctx_stack.Peek ();
|
||||
context.ExpectingValue = false;
|
||||
}
|
||||
|
||||
Unindent ();
|
||||
Put ("]");
|
||||
}
|
||||
|
||||
public void WriteArrayStart ()
|
||||
{
|
||||
DoValidation (Condition.NotAProperty);
|
||||
PutNewline ();
|
||||
|
||||
Put ("[");
|
||||
|
||||
context = new WriterContext ();
|
||||
context.InArray = true;
|
||||
ctx_stack.Push (context);
|
||||
|
||||
Indent ();
|
||||
}
|
||||
|
||||
public void WriteObjectEnd ()
|
||||
{
|
||||
DoValidation (Condition.InObject);
|
||||
PutNewline (false);
|
||||
|
||||
ctx_stack.Pop ();
|
||||
if (ctx_stack.Count == 1)
|
||||
has_reached_end = true;
|
||||
else {
|
||||
context = ctx_stack.Peek ();
|
||||
context.ExpectingValue = false;
|
||||
}
|
||||
|
||||
Unindent ();
|
||||
Put ("}");
|
||||
}
|
||||
|
||||
public void WriteObjectStart ()
|
||||
{
|
||||
DoValidation (Condition.NotAProperty);
|
||||
PutNewline ();
|
||||
|
||||
Put ("{");
|
||||
|
||||
context = new WriterContext ();
|
||||
context.InObject = true;
|
||||
ctx_stack.Push (context);
|
||||
|
||||
Indent ();
|
||||
}
|
||||
|
||||
public void WritePropertyName (string property_name)
|
||||
{
|
||||
DoValidation (Condition.Property);
|
||||
PutNewline ();
|
||||
string propertyName = (property_name == null || !lower_case_properties)
|
||||
? property_name
|
||||
: property_name.ToLowerInvariant();
|
||||
|
||||
PutString (propertyName);
|
||||
|
||||
if (pretty_print) {
|
||||
if (propertyName.Length > context.Padding)
|
||||
context.Padding = propertyName.Length;
|
||||
|
||||
for (int i = context.Padding - propertyName.Length;
|
||||
i >= 0; i--)
|
||||
writer.Write (' ');
|
||||
|
||||
writer.Write (": ");
|
||||
} else
|
||||
writer.Write (':');
|
||||
|
||||
context.ExpectingValue = true;
|
||||
}
|
||||
}
|
||||
}
|
912
TEngineHotUpdate/src/TEngineCore/Json/LitJson/Lexer.cs
Normal file
912
TEngineHotUpdate/src/TEngineCore/Json/LitJson/Lexer.cs
Normal file
@@ -0,0 +1,912 @@
|
||||
#region Header
|
||||
/**
|
||||
* Lexer.cs
|
||||
* JSON lexer implementation based on a finite state machine.
|
||||
*
|
||||
* The authors disclaim copyright to this source code. For more details, see
|
||||
* the COPYING file included with this distribution.
|
||||
**/
|
||||
#endregion
|
||||
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
|
||||
namespace TEngineCore.ListJson
|
||||
{
|
||||
internal class FsmContext
|
||||
{
|
||||
public bool Return;
|
||||
public int NextState;
|
||||
public Lexer L;
|
||||
public int StateStack;
|
||||
}
|
||||
|
||||
|
||||
internal class Lexer
|
||||
{
|
||||
#region Fields
|
||||
private delegate bool StateHandler (FsmContext ctx);
|
||||
|
||||
private static readonly int[] fsm_return_table;
|
||||
private static readonly StateHandler[] fsm_handler_table;
|
||||
|
||||
private bool allow_comments;
|
||||
private bool allow_single_quoted_strings;
|
||||
private bool end_of_input;
|
||||
private FsmContext fsm_context;
|
||||
private int input_buffer;
|
||||
private int input_char;
|
||||
private TextReader reader;
|
||||
private int state;
|
||||
private StringBuilder string_buffer;
|
||||
private string string_value;
|
||||
private int token;
|
||||
private int unichar;
|
||||
#endregion
|
||||
|
||||
|
||||
#region Properties
|
||||
public bool AllowComments {
|
||||
get { return allow_comments; }
|
||||
set { allow_comments = value; }
|
||||
}
|
||||
|
||||
public bool AllowSingleQuotedStrings {
|
||||
get { return allow_single_quoted_strings; }
|
||||
set { allow_single_quoted_strings = value; }
|
||||
}
|
||||
|
||||
public bool EndOfInput {
|
||||
get { return end_of_input; }
|
||||
}
|
||||
|
||||
public int Token {
|
||||
get { return token; }
|
||||
}
|
||||
|
||||
public string StringValue {
|
||||
get { return string_value; }
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
#region Constructors
|
||||
static Lexer ()
|
||||
{
|
||||
PopulateFsmTables (out fsm_handler_table, out fsm_return_table);
|
||||
}
|
||||
|
||||
public Lexer (TextReader reader)
|
||||
{
|
||||
allow_comments = true;
|
||||
allow_single_quoted_strings = true;
|
||||
|
||||
input_buffer = 0;
|
||||
string_buffer = new StringBuilder (128);
|
||||
state = 1;
|
||||
end_of_input = false;
|
||||
this.reader = reader;
|
||||
|
||||
fsm_context = new FsmContext ();
|
||||
fsm_context.L = this;
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
#region Static Methods
|
||||
private static int HexValue (int digit)
|
||||
{
|
||||
switch (digit) {
|
||||
case 'a':
|
||||
case 'A':
|
||||
return 10;
|
||||
|
||||
case 'b':
|
||||
case 'B':
|
||||
return 11;
|
||||
|
||||
case 'c':
|
||||
case 'C':
|
||||
return 12;
|
||||
|
||||
case 'd':
|
||||
case 'D':
|
||||
return 13;
|
||||
|
||||
case 'e':
|
||||
case 'E':
|
||||
return 14;
|
||||
|
||||
case 'f':
|
||||
case 'F':
|
||||
return 15;
|
||||
|
||||
default:
|
||||
return digit - '0';
|
||||
}
|
||||
}
|
||||
|
||||
private static void PopulateFsmTables (out StateHandler[] fsm_handler_table, out int[] fsm_return_table)
|
||||
{
|
||||
// See section A.1. of the manual for details of the finite
|
||||
// state machine.
|
||||
fsm_handler_table = new StateHandler[28] {
|
||||
State1,
|
||||
State2,
|
||||
State3,
|
||||
State4,
|
||||
State5,
|
||||
State6,
|
||||
State7,
|
||||
State8,
|
||||
State9,
|
||||
State10,
|
||||
State11,
|
||||
State12,
|
||||
State13,
|
||||
State14,
|
||||
State15,
|
||||
State16,
|
||||
State17,
|
||||
State18,
|
||||
State19,
|
||||
State20,
|
||||
State21,
|
||||
State22,
|
||||
State23,
|
||||
State24,
|
||||
State25,
|
||||
State26,
|
||||
State27,
|
||||
State28
|
||||
};
|
||||
|
||||
fsm_return_table = new int[28] {
|
||||
(int) ParserToken.Char,
|
||||
0,
|
||||
(int) ParserToken.Number,
|
||||
(int) ParserToken.Number,
|
||||
0,
|
||||
(int) ParserToken.Number,
|
||||
0,
|
||||
(int) ParserToken.Number,
|
||||
0,
|
||||
0,
|
||||
(int) ParserToken.True,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
(int) ParserToken.False,
|
||||
0,
|
||||
0,
|
||||
(int) ParserToken.Null,
|
||||
(int) ParserToken.CharSeq,
|
||||
(int) ParserToken.Char,
|
||||
0,
|
||||
0,
|
||||
(int) ParserToken.CharSeq,
|
||||
(int) ParserToken.Char,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0
|
||||
};
|
||||
}
|
||||
|
||||
private static char ProcessEscChar (int esc_char)
|
||||
{
|
||||
switch (esc_char) {
|
||||
case '"':
|
||||
case '\'':
|
||||
case '\\':
|
||||
case '/':
|
||||
return Convert.ToChar (esc_char);
|
||||
|
||||
case 'n':
|
||||
return '\n';
|
||||
|
||||
case 't':
|
||||
return '\t';
|
||||
|
||||
case 'r':
|
||||
return '\r';
|
||||
|
||||
case 'b':
|
||||
return '\b';
|
||||
|
||||
case 'f':
|
||||
return '\f';
|
||||
|
||||
default:
|
||||
// Unreachable
|
||||
return '?';
|
||||
}
|
||||
}
|
||||
|
||||
private static bool State1 (FsmContext ctx)
|
||||
{
|
||||
while (ctx.L.GetChar ()) {
|
||||
if (ctx.L.input_char == ' ' ||
|
||||
ctx.L.input_char >= '\t' && ctx.L.input_char <= '\r')
|
||||
continue;
|
||||
|
||||
if (ctx.L.input_char >= '1' && ctx.L.input_char <= '9') {
|
||||
ctx.L.string_buffer.Append ((char) ctx.L.input_char);
|
||||
ctx.NextState = 3;
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (ctx.L.input_char) {
|
||||
case '"':
|
||||
ctx.NextState = 19;
|
||||
ctx.Return = true;
|
||||
return true;
|
||||
|
||||
case ',':
|
||||
case ':':
|
||||
case '[':
|
||||
case ']':
|
||||
case '{':
|
||||
case '}':
|
||||
ctx.NextState = 1;
|
||||
ctx.Return = true;
|
||||
return true;
|
||||
|
||||
case '-':
|
||||
ctx.L.string_buffer.Append ((char) ctx.L.input_char);
|
||||
ctx.NextState = 2;
|
||||
return true;
|
||||
|
||||
case '0':
|
||||
ctx.L.string_buffer.Append ((char) ctx.L.input_char);
|
||||
ctx.NextState = 4;
|
||||
return true;
|
||||
|
||||
case 'f':
|
||||
ctx.NextState = 12;
|
||||
return true;
|
||||
|
||||
case 'n':
|
||||
ctx.NextState = 16;
|
||||
return true;
|
||||
|
||||
case 't':
|
||||
ctx.NextState = 9;
|
||||
return true;
|
||||
|
||||
case '\'':
|
||||
if (! ctx.L.allow_single_quoted_strings)
|
||||
return false;
|
||||
|
||||
ctx.L.input_char = '"';
|
||||
ctx.NextState = 23;
|
||||
ctx.Return = true;
|
||||
return true;
|
||||
|
||||
case '/':
|
||||
if (! ctx.L.allow_comments)
|
||||
return false;
|
||||
|
||||
ctx.NextState = 25;
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool State2 (FsmContext ctx)
|
||||
{
|
||||
ctx.L.GetChar ();
|
||||
|
||||
if (ctx.L.input_char >= '1' && ctx.L.input_char<= '9') {
|
||||
ctx.L.string_buffer.Append ((char) ctx.L.input_char);
|
||||
ctx.NextState = 3;
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (ctx.L.input_char) {
|
||||
case '0':
|
||||
ctx.L.string_buffer.Append ((char) ctx.L.input_char);
|
||||
ctx.NextState = 4;
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool State3 (FsmContext ctx)
|
||||
{
|
||||
while (ctx.L.GetChar ()) {
|
||||
if (ctx.L.input_char >= '0' && ctx.L.input_char <= '9') {
|
||||
ctx.L.string_buffer.Append ((char) ctx.L.input_char);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ctx.L.input_char == ' ' ||
|
||||
ctx.L.input_char >= '\t' && ctx.L.input_char <= '\r') {
|
||||
ctx.Return = true;
|
||||
ctx.NextState = 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (ctx.L.input_char) {
|
||||
case ',':
|
||||
case ']':
|
||||
case '}':
|
||||
ctx.L.UngetChar ();
|
||||
ctx.Return = true;
|
||||
ctx.NextState = 1;
|
||||
return true;
|
||||
|
||||
case '.':
|
||||
ctx.L.string_buffer.Append ((char) ctx.L.input_char);
|
||||
ctx.NextState = 5;
|
||||
return true;
|
||||
|
||||
case 'e':
|
||||
case 'E':
|
||||
ctx.L.string_buffer.Append ((char) ctx.L.input_char);
|
||||
ctx.NextState = 7;
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool State4 (FsmContext ctx)
|
||||
{
|
||||
ctx.L.GetChar ();
|
||||
|
||||
if (ctx.L.input_char == ' ' ||
|
||||
ctx.L.input_char >= '\t' && ctx.L.input_char <= '\r') {
|
||||
ctx.Return = true;
|
||||
ctx.NextState = 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (ctx.L.input_char) {
|
||||
case ',':
|
||||
case ']':
|
||||
case '}':
|
||||
ctx.L.UngetChar ();
|
||||
ctx.Return = true;
|
||||
ctx.NextState = 1;
|
||||
return true;
|
||||
|
||||
case '.':
|
||||
ctx.L.string_buffer.Append ((char) ctx.L.input_char);
|
||||
ctx.NextState = 5;
|
||||
return true;
|
||||
|
||||
case 'e':
|
||||
case 'E':
|
||||
ctx.L.string_buffer.Append ((char) ctx.L.input_char);
|
||||
ctx.NextState = 7;
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool State5 (FsmContext ctx)
|
||||
{
|
||||
ctx.L.GetChar ();
|
||||
|
||||
if (ctx.L.input_char >= '0' && ctx.L.input_char <= '9') {
|
||||
ctx.L.string_buffer.Append ((char) ctx.L.input_char);
|
||||
ctx.NextState = 6;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool State6 (FsmContext ctx)
|
||||
{
|
||||
while (ctx.L.GetChar ()) {
|
||||
if (ctx.L.input_char >= '0' && ctx.L.input_char <= '9') {
|
||||
ctx.L.string_buffer.Append ((char) ctx.L.input_char);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ctx.L.input_char == ' ' ||
|
||||
ctx.L.input_char >= '\t' && ctx.L.input_char <= '\r') {
|
||||
ctx.Return = true;
|
||||
ctx.NextState = 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (ctx.L.input_char) {
|
||||
case ',':
|
||||
case ']':
|
||||
case '}':
|
||||
ctx.L.UngetChar ();
|
||||
ctx.Return = true;
|
||||
ctx.NextState = 1;
|
||||
return true;
|
||||
|
||||
case 'e':
|
||||
case 'E':
|
||||
ctx.L.string_buffer.Append ((char) ctx.L.input_char);
|
||||
ctx.NextState = 7;
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool State7 (FsmContext ctx)
|
||||
{
|
||||
ctx.L.GetChar ();
|
||||
|
||||
if (ctx.L.input_char >= '0' && ctx.L.input_char<= '9') {
|
||||
ctx.L.string_buffer.Append ((char) ctx.L.input_char);
|
||||
ctx.NextState = 8;
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (ctx.L.input_char) {
|
||||
case '+':
|
||||
case '-':
|
||||
ctx.L.string_buffer.Append ((char) ctx.L.input_char);
|
||||
ctx.NextState = 8;
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool State8 (FsmContext ctx)
|
||||
{
|
||||
while (ctx.L.GetChar ()) {
|
||||
if (ctx.L.input_char >= '0' && ctx.L.input_char<= '9') {
|
||||
ctx.L.string_buffer.Append ((char) ctx.L.input_char);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ctx.L.input_char == ' ' ||
|
||||
ctx.L.input_char >= '\t' && ctx.L.input_char<= '\r') {
|
||||
ctx.Return = true;
|
||||
ctx.NextState = 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (ctx.L.input_char) {
|
||||
case ',':
|
||||
case ']':
|
||||
case '}':
|
||||
ctx.L.UngetChar ();
|
||||
ctx.Return = true;
|
||||
ctx.NextState = 1;
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool State9 (FsmContext ctx)
|
||||
{
|
||||
ctx.L.GetChar ();
|
||||
|
||||
switch (ctx.L.input_char) {
|
||||
case 'r':
|
||||
ctx.NextState = 10;
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool State10 (FsmContext ctx)
|
||||
{
|
||||
ctx.L.GetChar ();
|
||||
|
||||
switch (ctx.L.input_char) {
|
||||
case 'u':
|
||||
ctx.NextState = 11;
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool State11 (FsmContext ctx)
|
||||
{
|
||||
ctx.L.GetChar ();
|
||||
|
||||
switch (ctx.L.input_char) {
|
||||
case 'e':
|
||||
ctx.Return = true;
|
||||
ctx.NextState = 1;
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool State12 (FsmContext ctx)
|
||||
{
|
||||
ctx.L.GetChar ();
|
||||
|
||||
switch (ctx.L.input_char) {
|
||||
case 'a':
|
||||
ctx.NextState = 13;
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool State13 (FsmContext ctx)
|
||||
{
|
||||
ctx.L.GetChar ();
|
||||
|
||||
switch (ctx.L.input_char) {
|
||||
case 'l':
|
||||
ctx.NextState = 14;
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool State14 (FsmContext ctx)
|
||||
{
|
||||
ctx.L.GetChar ();
|
||||
|
||||
switch (ctx.L.input_char) {
|
||||
case 's':
|
||||
ctx.NextState = 15;
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool State15 (FsmContext ctx)
|
||||
{
|
||||
ctx.L.GetChar ();
|
||||
|
||||
switch (ctx.L.input_char) {
|
||||
case 'e':
|
||||
ctx.Return = true;
|
||||
ctx.NextState = 1;
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool State16 (FsmContext ctx)
|
||||
{
|
||||
ctx.L.GetChar ();
|
||||
|
||||
switch (ctx.L.input_char) {
|
||||
case 'u':
|
||||
ctx.NextState = 17;
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool State17 (FsmContext ctx)
|
||||
{
|
||||
ctx.L.GetChar ();
|
||||
|
||||
switch (ctx.L.input_char) {
|
||||
case 'l':
|
||||
ctx.NextState = 18;
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool State18 (FsmContext ctx)
|
||||
{
|
||||
ctx.L.GetChar ();
|
||||
|
||||
switch (ctx.L.input_char) {
|
||||
case 'l':
|
||||
ctx.Return = true;
|
||||
ctx.NextState = 1;
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool State19 (FsmContext ctx)
|
||||
{
|
||||
while (ctx.L.GetChar ()) {
|
||||
switch (ctx.L.input_char) {
|
||||
case '"':
|
||||
ctx.L.UngetChar ();
|
||||
ctx.Return = true;
|
||||
ctx.NextState = 20;
|
||||
return true;
|
||||
|
||||
case '\\':
|
||||
ctx.StateStack = 19;
|
||||
ctx.NextState = 21;
|
||||
return true;
|
||||
|
||||
default:
|
||||
ctx.L.string_buffer.Append ((char) ctx.L.input_char);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool State20 (FsmContext ctx)
|
||||
{
|
||||
ctx.L.GetChar ();
|
||||
|
||||
switch (ctx.L.input_char) {
|
||||
case '"':
|
||||
ctx.Return = true;
|
||||
ctx.NextState = 1;
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool State21 (FsmContext ctx)
|
||||
{
|
||||
ctx.L.GetChar ();
|
||||
|
||||
switch (ctx.L.input_char) {
|
||||
case 'u':
|
||||
ctx.NextState = 22;
|
||||
return true;
|
||||
|
||||
case '"':
|
||||
case '\'':
|
||||
case '/':
|
||||
case '\\':
|
||||
case 'b':
|
||||
case 'f':
|
||||
case 'n':
|
||||
case 'r':
|
||||
case 't':
|
||||
ctx.L.string_buffer.Append (
|
||||
ProcessEscChar (ctx.L.input_char));
|
||||
ctx.NextState = ctx.StateStack;
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool State22 (FsmContext ctx)
|
||||
{
|
||||
int counter = 0;
|
||||
int mult = 4096;
|
||||
|
||||
ctx.L.unichar = 0;
|
||||
|
||||
while (ctx.L.GetChar ()) {
|
||||
|
||||
if (ctx.L.input_char >= '0' && ctx.L.input_char <= '9' ||
|
||||
ctx.L.input_char >= 'A' && ctx.L.input_char <= 'F' ||
|
||||
ctx.L.input_char >= 'a' && ctx.L.input_char <= 'f') {
|
||||
|
||||
ctx.L.unichar += HexValue (ctx.L.input_char) * mult;
|
||||
|
||||
counter++;
|
||||
mult /= 16;
|
||||
|
||||
if (counter == 4) {
|
||||
ctx.L.string_buffer.Append (
|
||||
Convert.ToChar (ctx.L.unichar));
|
||||
ctx.NextState = ctx.StateStack;
|
||||
return true;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool State23 (FsmContext ctx)
|
||||
{
|
||||
while (ctx.L.GetChar ()) {
|
||||
switch (ctx.L.input_char) {
|
||||
case '\'':
|
||||
ctx.L.UngetChar ();
|
||||
ctx.Return = true;
|
||||
ctx.NextState = 24;
|
||||
return true;
|
||||
|
||||
case '\\':
|
||||
ctx.StateStack = 23;
|
||||
ctx.NextState = 21;
|
||||
return true;
|
||||
|
||||
default:
|
||||
ctx.L.string_buffer.Append ((char) ctx.L.input_char);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool State24 (FsmContext ctx)
|
||||
{
|
||||
ctx.L.GetChar ();
|
||||
|
||||
switch (ctx.L.input_char) {
|
||||
case '\'':
|
||||
ctx.L.input_char = '"';
|
||||
ctx.Return = true;
|
||||
ctx.NextState = 1;
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool State25 (FsmContext ctx)
|
||||
{
|
||||
ctx.L.GetChar ();
|
||||
|
||||
switch (ctx.L.input_char) {
|
||||
case '*':
|
||||
ctx.NextState = 27;
|
||||
return true;
|
||||
|
||||
case '/':
|
||||
ctx.NextState = 26;
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool State26 (FsmContext ctx)
|
||||
{
|
||||
while (ctx.L.GetChar ()) {
|
||||
if (ctx.L.input_char == '\n') {
|
||||
ctx.NextState = 1;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool State27 (FsmContext ctx)
|
||||
{
|
||||
while (ctx.L.GetChar ()) {
|
||||
if (ctx.L.input_char == '*') {
|
||||
ctx.NextState = 28;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool State28 (FsmContext ctx)
|
||||
{
|
||||
while (ctx.L.GetChar ()) {
|
||||
if (ctx.L.input_char == '*')
|
||||
continue;
|
||||
|
||||
if (ctx.L.input_char == '/') {
|
||||
ctx.NextState = 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
ctx.NextState = 27;
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
private bool GetChar ()
|
||||
{
|
||||
if ((input_char = NextChar ()) != -1)
|
||||
return true;
|
||||
|
||||
end_of_input = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
private int NextChar ()
|
||||
{
|
||||
if (input_buffer != 0) {
|
||||
int tmp = input_buffer;
|
||||
input_buffer = 0;
|
||||
|
||||
return tmp;
|
||||
}
|
||||
|
||||
return reader.Read ();
|
||||
}
|
||||
|
||||
public bool NextToken ()
|
||||
{
|
||||
StateHandler handler;
|
||||
fsm_context.Return = false;
|
||||
|
||||
while (true) {
|
||||
handler = fsm_handler_table[state - 1];
|
||||
|
||||
if (! handler (fsm_context))
|
||||
throw new JsonException (input_char);
|
||||
|
||||
if (end_of_input)
|
||||
return false;
|
||||
|
||||
if (fsm_context.Return) {
|
||||
string_value = string_buffer.ToString ();
|
||||
string_buffer.Remove (0, string_buffer.Length);
|
||||
token = fsm_return_table[state - 1];
|
||||
|
||||
if (token == (int) ParserToken.Char)
|
||||
token = input_char;
|
||||
|
||||
state = fsm_context.NextState;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
state = fsm_context.NextState;
|
||||
}
|
||||
}
|
||||
|
||||
private void UngetChar ()
|
||||
{
|
||||
input_buffer = input_char;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,24 @@
|
||||
#if NETSTANDARD1_5
|
||||
using System;
|
||||
using System.Reflection;
|
||||
namespace TEngineCore.ListJson
|
||||
{
|
||||
internal static class Netstandard15Polyfill
|
||||
{
|
||||
internal static Type GetInterface(this Type type, string name)
|
||||
{
|
||||
return type.GetTypeInfo().GetInterface(name);
|
||||
}
|
||||
|
||||
internal static bool IsClass(this Type type)
|
||||
{
|
||||
return type.GetTypeInfo().IsClass;
|
||||
}
|
||||
|
||||
internal static bool IsEnum(this Type type)
|
||||
{
|
||||
return type.GetTypeInfo().IsEnum;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
44
TEngineHotUpdate/src/TEngineCore/Json/LitJson/ParserToken.cs
Normal file
44
TEngineHotUpdate/src/TEngineCore/Json/LitJson/ParserToken.cs
Normal file
@@ -0,0 +1,44 @@
|
||||
#region Header
|
||||
/**
|
||||
* ParserToken.cs
|
||||
* Internal representation of the tokens used by the lexer and the parser.
|
||||
*
|
||||
* The authors disclaim copyright to this source code. For more details, see
|
||||
* the COPYING file included with this distribution.
|
||||
**/
|
||||
#endregion
|
||||
|
||||
|
||||
namespace TEngineCore.ListJson
|
||||
{
|
||||
internal enum ParserToken
|
||||
{
|
||||
// Lexer tokens (see section A.1.1. of the manual)
|
||||
None = System.Char.MaxValue + 1,
|
||||
Number,
|
||||
True,
|
||||
False,
|
||||
Null,
|
||||
CharSeq,
|
||||
// Single char
|
||||
Char,
|
||||
|
||||
// Parser Rules (see section A.2.1 of the manual)
|
||||
Text,
|
||||
Object,
|
||||
ObjectPrime,
|
||||
Pair,
|
||||
PairRest,
|
||||
Array,
|
||||
ArrayPrime,
|
||||
Value,
|
||||
ValueRest,
|
||||
String,
|
||||
|
||||
// End of input
|
||||
End,
|
||||
|
||||
// The empty rule
|
||||
Epsilon
|
||||
}
|
||||
}
|
441
TEngineHotUpdate/src/TEngineCore/Json/MiniJSON/Json.cs
Normal file
441
TEngineHotUpdate/src/TEngineCore/Json/MiniJSON/Json.cs
Normal file
@@ -0,0 +1,441 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
namespace MiniJSON
|
||||
{
|
||||
public static class Json
|
||||
{
|
||||
public static object Deserialize(string json)
|
||||
{
|
||||
if (json == null)
|
||||
return (object)null;
|
||||
return Json.Parser.Parse(json);
|
||||
}
|
||||
|
||||
public static string Serialize(object obj)
|
||||
{
|
||||
return Json.Serializer.Serialize(obj);
|
||||
}
|
||||
|
||||
private sealed class Parser : IDisposable
|
||||
{
|
||||
private const string WORD_BREAK = "{}[],:\"";
|
||||
private StringReader json;
|
||||
|
||||
public static bool IsWordBreak(char c)
|
||||
{
|
||||
return char.IsWhiteSpace(c) || "{}[],:\"".IndexOf(c) != -1;
|
||||
}
|
||||
|
||||
private Parser(string jsonString)
|
||||
{
|
||||
this.json = new StringReader(jsonString);
|
||||
}
|
||||
|
||||
public static object Parse(string jsonString)
|
||||
{
|
||||
using (Json.Parser parser = new Json.Parser(jsonString))
|
||||
return parser.ParseValue();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
this.json.Dispose();
|
||||
this.json = (StringReader)null;
|
||||
}
|
||||
|
||||
private Dictionary<string, object> ParseObject()
|
||||
{
|
||||
Dictionary<string, object> dictionary = new Dictionary<string, object>();
|
||||
this.json.Read();
|
||||
while (true)
|
||||
{
|
||||
Json.Parser.TOKEN nextToken;
|
||||
do
|
||||
{
|
||||
nextToken = this.NextToken;
|
||||
if (nextToken != Json.Parser.TOKEN.NONE)
|
||||
{
|
||||
if (nextToken == Json.Parser.TOKEN.CURLY_CLOSE)
|
||||
goto label_4;
|
||||
}
|
||||
else
|
||||
goto label_3;
|
||||
}
|
||||
while (nextToken == Json.Parser.TOKEN.COMMA);
|
||||
string index = this.ParseString();
|
||||
if (index != null && this.NextToken == Json.Parser.TOKEN.COLON)
|
||||
{
|
||||
this.json.Read();
|
||||
dictionary[index] = this.ParseValue();
|
||||
}
|
||||
else
|
||||
goto label_6;
|
||||
}
|
||||
label_3:
|
||||
return (Dictionary<string, object>)null;
|
||||
label_4:
|
||||
return dictionary;
|
||||
label_6:
|
||||
return (Dictionary<string, object>)null;
|
||||
}
|
||||
|
||||
private List<object> ParseArray()
|
||||
{
|
||||
List<object> objectList = new List<object>();
|
||||
this.json.Read();
|
||||
bool flag = true;
|
||||
while (flag)
|
||||
{
|
||||
Json.Parser.TOKEN nextToken = this.NextToken;
|
||||
switch (nextToken)
|
||||
{
|
||||
case Json.Parser.TOKEN.NONE:
|
||||
return (List<object>)null;
|
||||
case Json.Parser.TOKEN.SQUARED_CLOSE:
|
||||
flag = false;
|
||||
break;
|
||||
case Json.Parser.TOKEN.COMMA:
|
||||
continue;
|
||||
default:
|
||||
object byToken = this.ParseByToken(nextToken);
|
||||
objectList.Add(byToken);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return objectList;
|
||||
}
|
||||
|
||||
private object ParseValue()
|
||||
{
|
||||
return this.ParseByToken(this.NextToken);
|
||||
}
|
||||
|
||||
private object ParseByToken(Json.Parser.TOKEN token)
|
||||
{
|
||||
switch (token)
|
||||
{
|
||||
case Json.Parser.TOKEN.CURLY_OPEN:
|
||||
return (object)this.ParseObject();
|
||||
case Json.Parser.TOKEN.SQUARED_OPEN:
|
||||
return (object)this.ParseArray();
|
||||
case Json.Parser.TOKEN.STRING:
|
||||
return (object)this.ParseString();
|
||||
case Json.Parser.TOKEN.NUMBER:
|
||||
return this.ParseNumber();
|
||||
case Json.Parser.TOKEN.TRUE:
|
||||
return (object)true;
|
||||
case Json.Parser.TOKEN.FALSE:
|
||||
return (object)false;
|
||||
case Json.Parser.TOKEN.NULL:
|
||||
return (object)null;
|
||||
default:
|
||||
return (object)null;
|
||||
}
|
||||
}
|
||||
|
||||
private string ParseString()
|
||||
{
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
this.json.Read();
|
||||
bool flag = true;
|
||||
while (flag)
|
||||
{
|
||||
if (this.json.Peek() == -1)
|
||||
break;
|
||||
char nextChar1 = this.NextChar;
|
||||
switch (nextChar1)
|
||||
{
|
||||
case '"':
|
||||
flag = false;
|
||||
break;
|
||||
case '\\':
|
||||
if (this.json.Peek() == -1)
|
||||
{
|
||||
flag = false;
|
||||
break;
|
||||
}
|
||||
char nextChar2 = this.NextChar;
|
||||
switch (nextChar2)
|
||||
{
|
||||
case '"':
|
||||
case '/':
|
||||
case '\\':
|
||||
stringBuilder.Append(nextChar2);
|
||||
break;
|
||||
case 'b':
|
||||
stringBuilder.Append('\b');
|
||||
break;
|
||||
case 'f':
|
||||
stringBuilder.Append('\f');
|
||||
break;
|
||||
case 'n':
|
||||
stringBuilder.Append('\n');
|
||||
break;
|
||||
case 'r':
|
||||
stringBuilder.Append('\r');
|
||||
break;
|
||||
case 't':
|
||||
stringBuilder.Append('\t');
|
||||
break;
|
||||
case 'u':
|
||||
char[] chArray = new char[4];
|
||||
for (int index = 0; index < 4; ++index)
|
||||
chArray[index] = this.NextChar;
|
||||
stringBuilder.Append((char)Convert.ToInt32(new string(chArray), 16));
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
stringBuilder.Append(nextChar1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return stringBuilder.ToString();
|
||||
}
|
||||
|
||||
private object ParseNumber()
|
||||
{
|
||||
string nextWord = this.NextWord;
|
||||
if (nextWord.IndexOf('.') == -1)
|
||||
{
|
||||
long result;
|
||||
long.TryParse(nextWord, out result);
|
||||
return (object)result;
|
||||
}
|
||||
double result1;
|
||||
double.TryParse(nextWord, out result1);
|
||||
return (object)result1;
|
||||
}
|
||||
|
||||
private void EatWhitespace()
|
||||
{
|
||||
while (char.IsWhiteSpace(this.PeekChar))
|
||||
{
|
||||
this.json.Read();
|
||||
if (this.json.Peek() == -1)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private char PeekChar
|
||||
{
|
||||
get
|
||||
{
|
||||
return Convert.ToChar(this.json.Peek());
|
||||
}
|
||||
}
|
||||
|
||||
private char NextChar
|
||||
{
|
||||
get
|
||||
{
|
||||
return Convert.ToChar(this.json.Read());
|
||||
}
|
||||
}
|
||||
|
||||
private string NextWord
|
||||
{
|
||||
get
|
||||
{
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
while (!Json.Parser.IsWordBreak(this.PeekChar))
|
||||
{
|
||||
stringBuilder.Append(this.NextChar);
|
||||
if (this.json.Peek() == -1)
|
||||
break;
|
||||
}
|
||||
return stringBuilder.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
private Json.Parser.TOKEN NextToken
|
||||
{
|
||||
get
|
||||
{
|
||||
this.EatWhitespace();
|
||||
if (this.json.Peek() == -1)
|
||||
return Json.Parser.TOKEN.NONE;
|
||||
switch (this.PeekChar)
|
||||
{
|
||||
case '"':
|
||||
return Json.Parser.TOKEN.STRING;
|
||||
case ',':
|
||||
this.json.Read();
|
||||
return Json.Parser.TOKEN.COMMA;
|
||||
case '-':
|
||||
case '0':
|
||||
case '1':
|
||||
case '2':
|
||||
case '3':
|
||||
case '4':
|
||||
case '5':
|
||||
case '6':
|
||||
case '7':
|
||||
case '8':
|
||||
case '9':
|
||||
return Json.Parser.TOKEN.NUMBER;
|
||||
case ':':
|
||||
return Json.Parser.TOKEN.COLON;
|
||||
case '[':
|
||||
return Json.Parser.TOKEN.SQUARED_OPEN;
|
||||
case ']':
|
||||
this.json.Read();
|
||||
return Json.Parser.TOKEN.SQUARED_CLOSE;
|
||||
case '{':
|
||||
return Json.Parser.TOKEN.CURLY_OPEN;
|
||||
case '}':
|
||||
this.json.Read();
|
||||
return Json.Parser.TOKEN.CURLY_CLOSE;
|
||||
default:
|
||||
string nextWord = this.NextWord;
|
||||
if (nextWord == "false")
|
||||
return Json.Parser.TOKEN.FALSE;
|
||||
if (nextWord == "true")
|
||||
return Json.Parser.TOKEN.TRUE;
|
||||
return nextWord == "null" ? Json.Parser.TOKEN.NULL : Json.Parser.TOKEN.NONE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private enum TOKEN
|
||||
{
|
||||
NONE,
|
||||
CURLY_OPEN,
|
||||
CURLY_CLOSE,
|
||||
SQUARED_OPEN,
|
||||
SQUARED_CLOSE,
|
||||
COLON,
|
||||
COMMA,
|
||||
STRING,
|
||||
NUMBER,
|
||||
TRUE,
|
||||
FALSE,
|
||||
NULL,
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class Serializer
|
||||
{
|
||||
private StringBuilder builder;
|
||||
|
||||
private Serializer()
|
||||
{
|
||||
this.builder = new StringBuilder();
|
||||
}
|
||||
|
||||
public static string Serialize(object obj)
|
||||
{
|
||||
Json.Serializer serializer = new Json.Serializer();
|
||||
serializer.SerializeValue(obj);
|
||||
return serializer.builder.ToString();
|
||||
}
|
||||
|
||||
private void SerializeValue(object value)
|
||||
{
|
||||
if (value == null)
|
||||
this.builder.Append("null");
|
||||
else if (value is string str)
|
||||
this.SerializeString(str);
|
||||
else if (value is bool)
|
||||
this.builder.Append((bool)value ? "true" : "false");
|
||||
else if (value is IList anArray)
|
||||
this.SerializeArray(anArray);
|
||||
else if (value is IDictionary dictionary)
|
||||
this.SerializeObject(dictionary);
|
||||
else if (value is char)
|
||||
this.SerializeString(new string((char)value, 1));
|
||||
else
|
||||
this.SerializeOther(value);
|
||||
}
|
||||
|
||||
private void SerializeObject(IDictionary obj)
|
||||
{
|
||||
bool flag = true;
|
||||
this.builder.Append('{');
|
||||
foreach (object key in (IEnumerable)obj.Keys)
|
||||
{
|
||||
if (!flag)
|
||||
this.builder.Append(',');
|
||||
this.SerializeString(key.ToString());
|
||||
this.builder.Append(':');
|
||||
this.SerializeValue(obj[key]);
|
||||
flag = false;
|
||||
}
|
||||
this.builder.Append('}');
|
||||
}
|
||||
|
||||
private void SerializeArray(IList anArray)
|
||||
{
|
||||
this.builder.Append('[');
|
||||
bool flag = true;
|
||||
foreach (object an in (IEnumerable)anArray)
|
||||
{
|
||||
if (!flag)
|
||||
this.builder.Append(',');
|
||||
this.SerializeValue(an);
|
||||
flag = false;
|
||||
}
|
||||
this.builder.Append(']');
|
||||
}
|
||||
|
||||
private void SerializeString(string str)
|
||||
{
|
||||
this.builder.Append('"');
|
||||
foreach (char ch in str.ToCharArray())
|
||||
{
|
||||
switch (ch)
|
||||
{
|
||||
case '\b':
|
||||
this.builder.Append("\\b");
|
||||
break;
|
||||
case '\t':
|
||||
this.builder.Append("\\t");
|
||||
break;
|
||||
case '\n':
|
||||
this.builder.Append("\\n");
|
||||
break;
|
||||
case '\f':
|
||||
this.builder.Append("\\f");
|
||||
break;
|
||||
case '\r':
|
||||
this.builder.Append("\\r");
|
||||
break;
|
||||
case '"':
|
||||
this.builder.Append("\\\"");
|
||||
break;
|
||||
case '\\':
|
||||
this.builder.Append("\\\\");
|
||||
break;
|
||||
default:
|
||||
int int32 = Convert.ToInt32(ch);
|
||||
if (int32 >= 32 && int32 <= 126)
|
||||
{
|
||||
this.builder.Append(ch);
|
||||
break;
|
||||
}
|
||||
this.builder.Append("\\u");
|
||||
this.builder.Append(int32.ToString("x4"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
this.builder.Append('"');
|
||||
}
|
||||
|
||||
private void SerializeOther(object value)
|
||||
{
|
||||
if (value is float)
|
||||
this.builder.Append(((float)value).ToString("R"));
|
||||
else if (value is int || value is uint || (value is long || value is sbyte) || (value is byte || value is short || value is ushort) || value is ulong)
|
||||
this.builder.Append(value);
|
||||
else if (value is double || value is Decimal)
|
||||
this.builder.Append(Convert.ToDouble(value).ToString("R"));
|
||||
else
|
||||
this.SerializeString(value.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
64
TEngineHotUpdate/src/TEngineCore/Mono/MonoController.cs
Normal file
64
TEngineHotUpdate/src/TEngineCore/Mono/MonoController.cs
Normal file
@@ -0,0 +1,64 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.Events;
|
||||
|
||||
namespace TEngineCore
|
||||
{
|
||||
/// <summary>
|
||||
/// Mono管理者
|
||||
/// </summary>
|
||||
public class MonoController : MonoBehaviour
|
||||
{
|
||||
private event UnityAction updateEvent;
|
||||
private event UnityAction fixedUpdateEvent;
|
||||
|
||||
void Update()
|
||||
{
|
||||
if (updateEvent != null)
|
||||
{
|
||||
updateEvent();
|
||||
}
|
||||
}
|
||||
|
||||
void FixedUpdate()
|
||||
{
|
||||
if (fixedUpdateEvent != null)
|
||||
{
|
||||
fixedUpdateEvent();
|
||||
}
|
||||
}
|
||||
|
||||
public void AddFixedUpdateListener(UnityAction fun)
|
||||
{
|
||||
fixedUpdateEvent += fun;
|
||||
}
|
||||
|
||||
public void RemoveFixedUpdateListener(UnityAction fun)
|
||||
{
|
||||
fixedUpdateEvent -= fun;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 为给外部提供的 添加帧更新事件
|
||||
/// </summary>
|
||||
/// <param name="fun"></param>
|
||||
public void AddUpdateListener(UnityAction fun)
|
||||
{
|
||||
updateEvent += fun;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 移除帧更新事件
|
||||
/// </summary>
|
||||
/// <param name="fun"></param>
|
||||
public void RemoveUpdateListener(UnityAction fun)
|
||||
{
|
||||
updateEvent -= fun;
|
||||
}
|
||||
|
||||
public void Release()
|
||||
{
|
||||
updateEvent = null;
|
||||
fixedUpdateEvent = null;
|
||||
}
|
||||
}
|
||||
}
|
144
TEngineHotUpdate/src/TEngineCore/Mono/MonoManager.cs
Normal file
144
TEngineHotUpdate/src/TEngineCore/Mono/MonoManager.cs
Normal file
@@ -0,0 +1,144 @@
|
||||
using System.Collections;
|
||||
using System.ComponentModel;
|
||||
using TEngineCore;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Events;
|
||||
|
||||
namespace TEngineCore
|
||||
{
|
||||
public class MonoManager : TSingleton<MonoManager>
|
||||
{
|
||||
private MonoController controller;
|
||||
|
||||
public override void Release()
|
||||
{
|
||||
StopAllCoroutine();
|
||||
controller.Release();
|
||||
controller = null;
|
||||
base.Release();
|
||||
}
|
||||
|
||||
public MonoManager()
|
||||
{
|
||||
GameObject obj = new GameObject("MonoManager");
|
||||
|
||||
controller = obj.AddComponent<MonoController>();
|
||||
|
||||
#if UNITY_EDITOR
|
||||
GameObject tEngine = SingletonMgr.Root;
|
||||
if (tEngine != null)
|
||||
{
|
||||
obj.transform.SetParent(tEngine.transform);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
~MonoManager()
|
||||
{
|
||||
StopAllCoroutine();
|
||||
controller.Release();
|
||||
controller = null;
|
||||
}
|
||||
|
||||
#region 注入UnityUpdate/FixedUpdate
|
||||
/// <summary>
|
||||
/// 为给外部提供的 添加帧更新事件
|
||||
/// </summary>
|
||||
/// <param name="fun"></param>
|
||||
public void AddUpdateListener(UnityAction fun)
|
||||
{
|
||||
controller.AddUpdateListener(fun);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 为给外部提供的 添加物理帧更新事件
|
||||
/// </summary>
|
||||
/// <param name="fun"></param>
|
||||
public void AddFixedUpdateListener(UnityAction fun)
|
||||
{
|
||||
controller.AddFixedUpdateListener(fun);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 移除帧更新事件
|
||||
/// </summary>
|
||||
/// <param name="fun"></param>
|
||||
public void RemoveUpdateListener(UnityAction fun)
|
||||
{
|
||||
controller.RemoveUpdateListener(fun);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 控制协程Coroutine
|
||||
public Coroutine StartCoroutine(string methodName)
|
||||
{
|
||||
if (controller == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return controller.StartCoroutine(methodName);
|
||||
}
|
||||
|
||||
public Coroutine StartCoroutine(IEnumerator routine)
|
||||
{
|
||||
if (controller == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return controller.StartCoroutine(routine);
|
||||
}
|
||||
|
||||
public Coroutine StartCoroutine(string methodName, [DefaultValue("null")] object value)
|
||||
{
|
||||
if (controller == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return controller.StartCoroutine(methodName, value);
|
||||
}
|
||||
|
||||
public void StopCoroutine(string methodName)
|
||||
{
|
||||
if (controller == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
controller.StopCoroutine(methodName);
|
||||
}
|
||||
|
||||
public void StopCoroutine(IEnumerator routine)
|
||||
{
|
||||
if (controller == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
controller.StopCoroutine(routine);
|
||||
}
|
||||
|
||||
public void StopCoroutine(Coroutine routine)
|
||||
{
|
||||
if (controller == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
controller.StopCoroutine(routine);
|
||||
}
|
||||
|
||||
public void StopAllCoroutine()
|
||||
{
|
||||
if (controller != null)
|
||||
{
|
||||
controller.StopAllCoroutines();
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region GC
|
||||
public void GC()
|
||||
{
|
||||
System.GC.Collect();
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
}
|
408
TEngineHotUpdate/src/TEngineCore/Res/AssetBundleData.cs
Normal file
408
TEngineHotUpdate/src/TEngineCore/Res/AssetBundleData.cs
Normal file
@@ -0,0 +1,408 @@
|
||||
using UnityEngine;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace TEngineCore
|
||||
{
|
||||
public class AssetBundleData
|
||||
{
|
||||
public AssetBundle Bundle;
|
||||
public AssetBundleData[] Dependencies;
|
||||
|
||||
private AssetData[] _assets = null;
|
||||
private Dictionary<string, int> _path2Index = null;
|
||||
|
||||
internal static ulong Offset;
|
||||
|
||||
string _name;
|
||||
float _unloadTimer;
|
||||
int _refCount;
|
||||
int _depCount;
|
||||
bool _isInvokeLoad;
|
||||
bool _bLoaded;
|
||||
|
||||
public int RefCount => _refCount;
|
||||
public int DepCount => _depCount;
|
||||
|
||||
class AsyncLoadRequest
|
||||
{
|
||||
public AssetBundleCreateRequest Request;
|
||||
public int DependedRefCount;
|
||||
}
|
||||
AsyncLoadRequest _asyncLoadRequest;
|
||||
private event System.Action<AssetBundleData> _onAsyncLoadComplete;
|
||||
|
||||
public string Name
|
||||
{
|
||||
get
|
||||
{
|
||||
return _name;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 是否能被卸载
|
||||
/// </summary>
|
||||
public bool Unloadable
|
||||
{
|
||||
get
|
||||
{
|
||||
return Bundle != null && _refCount <= 0 && _depCount <= 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 是否正在加载
|
||||
/// </summary>
|
||||
public bool IsAsyncLoading => _asyncLoadRequest != null;
|
||||
|
||||
public bool IsLoadComplete => _bLoaded;
|
||||
|
||||
public event System.Action<AssetBundleData> OnAsyncLoadComplete
|
||||
{
|
||||
add
|
||||
{
|
||||
_onAsyncLoadComplete += value;
|
||||
}
|
||||
remove
|
||||
{
|
||||
_onAsyncLoadComplete -= value;
|
||||
}
|
||||
}
|
||||
|
||||
public AssetBundleData(string name)
|
||||
{
|
||||
Bundle = null;
|
||||
_name = name;
|
||||
_unloadTimer = 0f;
|
||||
_refCount = 0;
|
||||
_depCount = 0;
|
||||
_asyncLoadRequest = null;
|
||||
_isInvokeLoad = false;
|
||||
_bLoaded = false;
|
||||
}
|
||||
|
||||
public void InitAssets(string[] assets)
|
||||
{
|
||||
if (assets.Length > 0)
|
||||
{
|
||||
_assets = new AssetData[assets.Length];
|
||||
_path2Index = new Dictionary<string, int>();
|
||||
|
||||
for (int i = 0; i < assets.Length; ++i)
|
||||
{
|
||||
_path2Index.Add(assets[i], i);
|
||||
_assets[i] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public AssetData GetAsset(string assetPath)
|
||||
{
|
||||
if (_assets != null)
|
||||
{
|
||||
if (_path2Index.ContainsKey(assetPath))
|
||||
return _assets[_path2Index[assetPath]];
|
||||
else
|
||||
{
|
||||
TLogger.LogError($"Try to get asset data '{assetPath}' which not in the bundle '{_name}'");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
TLogger.LogError($"Try to get asset data in the bundle '{_name}' which not include any asset");
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public void SetAsset(string assetPath, AssetData assetData)
|
||||
{
|
||||
if (_assets != null)
|
||||
{
|
||||
if (_path2Index.ContainsKey(assetPath))
|
||||
_assets[_path2Index[assetPath]] = assetData;
|
||||
else
|
||||
TLogger.LogError($"Try to set asset data '{assetPath}' which not in the bundle '{_name}'");
|
||||
}
|
||||
else
|
||||
TLogger.LogError($"Try to set asset data in the bundle '{_name}' which not include any asset");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 同步加载AssetBundle接口
|
||||
/// </summary>
|
||||
/// <param name="bDepended">该AssetBundle是否被其他AssetBundle依赖</param>
|
||||
public void Load(bool bDepended = false)
|
||||
{
|
||||
if (Bundle == null)
|
||||
{
|
||||
// 置标志,用于循环依赖检测
|
||||
_isInvokeLoad = true;
|
||||
|
||||
for (int i = 0; i < Dependencies.Length; ++i)
|
||||
{
|
||||
if (!Dependencies[i]._isInvokeLoad)
|
||||
{
|
||||
Dependencies[i].Load(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
TLogger.LogWarning($"Dependency cycle detected for '{Dependencies[i]._name}'");
|
||||
}
|
||||
}
|
||||
Bundle = AssetBundle.LoadFromFile(FileSystem.GetAssetBundlePathInVersion(_name), 0, Offset);
|
||||
|
||||
_isInvokeLoad = false;
|
||||
}
|
||||
|
||||
if (Bundle != null)
|
||||
{
|
||||
if (bDepended)
|
||||
{
|
||||
AddDepRef();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
TLogger.LogError($"Can not load AssetBundle '{_name}'!");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 异步加载AssetBundle接口
|
||||
/// </summary>
|
||||
/// <param name="onComplete">加载回调</param>
|
||||
/// <param name="bDepended">该AssetBundle是否被其他AssetBundle依赖</param>
|
||||
public void LoadAsync(System.Action<AssetBundleData> onComplete, bool bDepended = false)
|
||||
{
|
||||
_onAsyncLoadComplete -= onComplete;
|
||||
_onAsyncLoadComplete += onComplete;
|
||||
|
||||
if (Bundle == null)
|
||||
{
|
||||
if (_asyncLoadRequest == null)
|
||||
{
|
||||
_isInvokeLoad = true;
|
||||
|
||||
for (int i = 0; i < Dependencies.Length; ++i)
|
||||
{
|
||||
if (!Dependencies[i]._isInvokeLoad)
|
||||
Dependencies[i].LoadAsync(OnDependenciesAsyncLoadComplete, true);
|
||||
else
|
||||
TLogger.LogWarning($"Dependency cycle detected for '{Dependencies[i]._name}'");
|
||||
}
|
||||
_asyncLoadRequest = new AsyncLoadRequest
|
||||
{
|
||||
Request = AssetBundle.LoadFromFileAsync(FileSystem.GetAssetBundlePathInVersion(_name), 0, Offset)
|
||||
,
|
||||
DependedRefCount = bDepended ? 1 : 0
|
||||
};
|
||||
_asyncLoadRequest.Request.completed += OnAssetBundleLoadComplete;
|
||||
|
||||
_isInvokeLoad = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (bDepended)
|
||||
{
|
||||
++_asyncLoadRequest.DependedRefCount;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (bDepended)
|
||||
{
|
||||
AddDepRef();
|
||||
}
|
||||
|
||||
OnDependenciesAsyncLoadComplete(this);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 卸载AssetBundle及其中包含的Asset
|
||||
/// </summary>
|
||||
/// <param name="bForce">强制卸载</param>
|
||||
public void Unload(bool bForce = false)
|
||||
{
|
||||
// 当仅作为依赖时_assets为空
|
||||
if (_assets != null)
|
||||
{
|
||||
for (int i = 0; i < _assets.Length; ++i)
|
||||
{
|
||||
if (_assets[i] != null)
|
||||
{
|
||||
_assets[i].Unload();
|
||||
_assets[i] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (Bundle != null)
|
||||
{
|
||||
Bundle.Unload(true);
|
||||
Bundle = null;
|
||||
TLogger.LogInfo($"Unload Bundle {_name}");
|
||||
}
|
||||
_onAsyncLoadComplete = null;
|
||||
_asyncLoadRequest = null;
|
||||
_isInvokeLoad = false;
|
||||
_bLoaded = false;
|
||||
|
||||
if (!bForce)
|
||||
{
|
||||
for (int i = 0; i < Dependencies.Length; ++i)
|
||||
{
|
||||
Dependencies[i].DecDepRef();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//不卸载bundle相关的资源,防止出错
|
||||
public void UnloadBundleFalse()
|
||||
{
|
||||
if (_assets != null)
|
||||
{
|
||||
for (int i = 0; i < _assets.Length; ++i)
|
||||
{
|
||||
if (_assets[i] != null)
|
||||
{
|
||||
_assets[i].Unload();
|
||||
_assets[i] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (Bundle != null)
|
||||
{
|
||||
Bundle.Unload(false);
|
||||
Bundle = null;
|
||||
TLogger.LogInfo($"Unload Bundle {_name}");
|
||||
}
|
||||
_onAsyncLoadComplete = null;
|
||||
_asyncLoadRequest = null;
|
||||
_isInvokeLoad = false;
|
||||
_bLoaded = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 增加依赖计数
|
||||
/// </summary>
|
||||
/// <param name="count">引用计数增量</param>
|
||||
/// <remark>异步加载未完成时,存在另外的加载请求,此时加载请求里记录依赖次数</remark>
|
||||
public void AddDepRef(int count = 1)
|
||||
{
|
||||
_depCount += count;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 减依赖计数
|
||||
/// </summary>
|
||||
public void DecDepRef()
|
||||
{
|
||||
--_depCount;
|
||||
if (_depCount == 0)
|
||||
{
|
||||
_unloadTimer = 0f;
|
||||
}
|
||||
else if (_depCount < 0)
|
||||
{
|
||||
_depCount = 0;
|
||||
TLogger.LogWarning($"{_name} _depCount < 0");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 增加引用计数
|
||||
/// </summary>
|
||||
public void AddRef()
|
||||
{
|
||||
++_refCount;
|
||||
//TLogger.LogInfo($"Add AssetBundle {_name} refCount = {_refCount}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 减引用计数
|
||||
/// </summary>
|
||||
public void DecRef(bool bNoDelay)
|
||||
{
|
||||
--_refCount;
|
||||
//TLogger.LogInfo($"Dec AssetBundle {_name} refCount = {_refCount}");
|
||||
if (_refCount == 0)
|
||||
{
|
||||
_unloadTimer = bNoDelay ? System.Single.MaxValue : 0f;
|
||||
}
|
||||
else if (_refCount < 0)
|
||||
{
|
||||
_refCount = 0;
|
||||
TLogger.LogWarning($"{_name} _refCount < 0");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 卸载时间更新
|
||||
/// </summary>
|
||||
/// <param name="delta">游戏时间帧间隔</param>
|
||||
/// <returns>从引用计数为0开始到现在的时长</returns>
|
||||
public float Update(float delta)
|
||||
{
|
||||
if (Unloadable)
|
||||
{
|
||||
_unloadTimer += delta;
|
||||
}
|
||||
|
||||
return _unloadTimer;
|
||||
}
|
||||
|
||||
void OnAssetBundleLoadComplete(AsyncOperation asyncOperation)
|
||||
{
|
||||
if (asyncOperation.isDone)
|
||||
{
|
||||
if (_asyncLoadRequest != null && _asyncLoadRequest.Request == asyncOperation)
|
||||
{
|
||||
AssetBundleCreateRequest assetBundleCreateRequest = asyncOperation as AssetBundleCreateRequest;
|
||||
if (assetBundleCreateRequest != null)
|
||||
{
|
||||
Bundle = assetBundleCreateRequest.assetBundle;
|
||||
if (Bundle != null)
|
||||
{
|
||||
AddDepRef(_asyncLoadRequest.DependedRefCount);
|
||||
OnDependenciesAsyncLoadComplete(this);
|
||||
}
|
||||
else
|
||||
{
|
||||
TLogger.LogError($"Can not load AssetBundle '{_name}' asynchronously!");
|
||||
}
|
||||
}
|
||||
|
||||
_asyncLoadRequest = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
TLogger.LogError("Return a mismatch asyncOperation for AssetBundleCreateRequest");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
TLogger.LogError("Return a not done AsyncOperation for AssetBundleCreateRequest");
|
||||
}
|
||||
}
|
||||
void OnDependenciesAsyncLoadComplete(AssetBundleData depBundle)
|
||||
{
|
||||
bool bDepAllLoaded = true;
|
||||
for (int i = 0; i < Dependencies.Length; ++i)
|
||||
{
|
||||
if (Dependencies[i].Bundle == null)
|
||||
{
|
||||
bDepAllLoaded = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (Bundle != null && bDepAllLoaded && _onAsyncLoadComplete != null)
|
||||
{
|
||||
_onAsyncLoadComplete(this);
|
||||
_bLoaded = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
356
TEngineHotUpdate/src/TEngineCore/Res/AssetConfig.cs
Normal file
356
TEngineHotUpdate/src/TEngineCore/Res/AssetConfig.cs
Normal file
@@ -0,0 +1,356 @@
|
||||
using System.IO;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine.SceneManagement;
|
||||
|
||||
namespace TEngineCore
|
||||
{
|
||||
/// <summary>
|
||||
/// 资源配置器 负责资源路径 AssetBundle映射,管理AssetBundle、Asset数据
|
||||
/// </summary>
|
||||
public class AssetConfig
|
||||
{
|
||||
public const string AssetRootPath = "Assets/TResources";
|
||||
public const string AssetBundleMeta = "AssetBundleMeta.bin";
|
||||
|
||||
private float _assetUnloadDelay = 10f;
|
||||
private int _maxUnloadNumPerFrame = 5;
|
||||
|
||||
public float AssetUnloadDelay
|
||||
{
|
||||
set => _assetUnloadDelay = value;
|
||||
get { return _assetUnloadDelay; }
|
||||
}
|
||||
public int MaxUnloadNumPerFrame
|
||||
{
|
||||
set => _maxUnloadNumPerFrame = value;
|
||||
get { return _maxUnloadNumPerFrame; }
|
||||
}
|
||||
|
||||
readonly Dictionary<string, AssetBundleData> _bundleDatas = new Dictionary<string, AssetBundleData>();
|
||||
readonly Dictionary<string, string> _assetPath2BundleDatas = new Dictionary<string, string>();
|
||||
|
||||
/// <summary>
|
||||
/// 加载资源依赖数据,产生AssetBundle依赖拓扑
|
||||
/// </summary>
|
||||
public void Load()
|
||||
{
|
||||
#if ASSETBUNDLE_ENABLE
|
||||
Stream stream = FileSystem.OpenRead(FileSystem.GetAssetBundlePathInVersion(AssetBundleMeta));
|
||||
BinaryReader reader = new BinaryReader(stream);
|
||||
uint resVersion = reader.ReadUInt32();
|
||||
int count = reader.ReadInt32();
|
||||
ulong offset = reader.ReadUInt32();
|
||||
AssetBundleData.Offset = offset;
|
||||
string bundleName;
|
||||
AssetBundleData assetBundleData;
|
||||
AssetBundleData depAssetBundleData;
|
||||
int assetCount;
|
||||
int depCount;
|
||||
string[] assetPaths;
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
bundleName = reader.ReadString();
|
||||
assetCount = reader.ReadInt32();
|
||||
assetPaths = new string[assetCount];
|
||||
for (int ii = 0; ii < assetCount; ++ii)
|
||||
{
|
||||
assetPaths[ii] = reader.ReadString();
|
||||
_assetPath2BundleDatas.Add(assetPaths[ii], bundleName);
|
||||
}
|
||||
depCount = reader.ReadInt32();
|
||||
if (!_bundleDatas.TryGetValue(bundleName, out assetBundleData))
|
||||
{
|
||||
assetBundleData = new AssetBundleData(bundleName);
|
||||
_bundleDatas.Add(assetBundleData.Name, assetBundleData);
|
||||
assetBundleData.Dependencies = new AssetBundleData[depCount];
|
||||
}
|
||||
else
|
||||
{
|
||||
if (assetBundleData.Dependencies == null)
|
||||
assetBundleData.Dependencies = new AssetBundleData[depCount];
|
||||
}
|
||||
|
||||
assetBundleData.InitAssets(assetPaths);
|
||||
|
||||
for (int ii = 0; ii < depCount; ++ii)
|
||||
{
|
||||
bundleName = reader.ReadString();
|
||||
if (!_bundleDatas.TryGetValue(bundleName, out depAssetBundleData))
|
||||
{
|
||||
depAssetBundleData = new AssetBundleData(bundleName);
|
||||
_bundleDatas.Add(bundleName, depAssetBundleData);
|
||||
}
|
||||
assetBundleData.Dependencies[ii] = depAssetBundleData;
|
||||
}
|
||||
}
|
||||
stream.Close();
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 卸载AssetBundle数据
|
||||
/// </summary>
|
||||
public void Unload()
|
||||
{
|
||||
#if ASSETBUNDLE_ENABLE
|
||||
foreach(var bundleData in _bundleDatas)
|
||||
bundleData.Value.Unload(true);
|
||||
_bundleDatas.Clear();
|
||||
_assetPath2BundleDatas.Clear();
|
||||
#endif
|
||||
}
|
||||
|
||||
public void UnloadFalse()
|
||||
{
|
||||
#if ASSETBUNDLE_ENABLE
|
||||
foreach(var bundleData in _bundleDatas)
|
||||
bundleData.Value.UnloadBundleFalse();
|
||||
_bundleDatas.Clear();
|
||||
_assetPath2BundleDatas.Clear();
|
||||
#endif
|
||||
}
|
||||
|
||||
public void TryReload()
|
||||
{
|
||||
if (_bundleDatas.Count > 0)
|
||||
{
|
||||
UnloadFalse();
|
||||
Load();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 延迟卸载更新
|
||||
/// </summary>
|
||||
/// <param name="delta">游戏帧间隔</param>
|
||||
public void Update(float delta)
|
||||
{
|
||||
#if ASSETBUNDLE_ENABLE
|
||||
var iter = _bundleDatas.GetEnumerator();
|
||||
AssetBundleData bundleData;
|
||||
int count = 0;
|
||||
while (iter.MoveNext())
|
||||
{
|
||||
bundleData = iter.Current.Value;
|
||||
if (bundleData.Unloadable)
|
||||
{
|
||||
if (bundleData.Update(delta) >= AssetUnloadDelay && count < MaxUnloadNumPerFrame)
|
||||
{
|
||||
bundleData.Unload();
|
||||
++count;
|
||||
}
|
||||
}
|
||||
}
|
||||
iter.Dispose();
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 立即卸载没有引用的AssetBundle
|
||||
/// </summary>
|
||||
public void UnloadUnusedAssetBundle()
|
||||
{
|
||||
var iter = _bundleDatas.GetEnumerator();
|
||||
AssetBundleData bundleData;
|
||||
while (iter.MoveNext())
|
||||
{
|
||||
bundleData = iter.Current.Value;
|
||||
if (bundleData.Unloadable)
|
||||
bundleData.Unload();
|
||||
}
|
||||
iter.Dispose();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 同步获取Asset
|
||||
/// </summary>
|
||||
/// <param name="path">是否加载子Asset,针对Sprite图集</param>
|
||||
/// <param name="withSubAssets">是否加载子Asset,针对Sprite图集</param>
|
||||
/// <returns>Asset数据</returns>
|
||||
public AssetData GetAssetAtPath(string path, bool withSubAssets = false)
|
||||
{
|
||||
AssetData assetData = null;
|
||||
#if ASSETBUNDLE_ENABLE
|
||||
AssetBundleData bundleData = FindAssetBundle(path);
|
||||
if (bundleData != null)
|
||||
{
|
||||
assetData = bundleData.GetAsset(path);
|
||||
if(assetData == null)
|
||||
{
|
||||
assetData = new AssetData(path, bundleData);
|
||||
assetData.LoadAsset(withSubAssets);
|
||||
bundleData.SetAsset(path, assetData);
|
||||
}
|
||||
}
|
||||
#elif UNITY_EDITOR
|
||||
if (withSubAssets)
|
||||
{
|
||||
UnityEngine.Object[] subAssets = UnityEditor.AssetDatabase.LoadAllAssetsAtPath($"{AssetRootPath}/{path}");
|
||||
if (subAssets != null && subAssets.Length > 0)
|
||||
{
|
||||
assetData = new AssetData(path);
|
||||
assetData.ProcessSubAssets(subAssets);
|
||||
}
|
||||
else
|
||||
{
|
||||
TLogger.LogError($"Can not load the asset '{path}'");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
UnityEngine.Object obj = UnityEditor.AssetDatabase.LoadAssetAtPath<UnityEngine.Object>($"{AssetRootPath}/{path}");
|
||||
if (obj != null)
|
||||
{
|
||||
assetData = new AssetData(path, null, obj);
|
||||
}
|
||||
else
|
||||
{
|
||||
TLogger.LogError($"Can not load the asset '{path}'");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return assetData;
|
||||
}
|
||||
|
||||
public AssetData GetPackageAssetAtPath(string path, bool withSubAssets = false)
|
||||
{
|
||||
AssetData assetData = null;
|
||||
#if ASSETBUNDLE_ENABLE
|
||||
AssetBundleData bundleData = FindAssetBundle(path);
|
||||
if (bundleData != null)
|
||||
{
|
||||
assetData = bundleData.GetAsset(path);
|
||||
if(assetData == null)
|
||||
{
|
||||
assetData = new AssetData(path, bundleData);
|
||||
assetData.SetAsPackageAsset(path);
|
||||
assetData.LoadAsset(withSubAssets);
|
||||
bundleData.SetAsset(path, assetData);
|
||||
}
|
||||
}
|
||||
#elif UNITY_EDITOR
|
||||
if (withSubAssets)
|
||||
{
|
||||
UnityEngine.Object[] subAssets = UnityEditor.AssetDatabase.LoadAllAssetsAtPath(path);
|
||||
if (subAssets != null && subAssets.Length > 0)
|
||||
{
|
||||
assetData = new AssetData(path);
|
||||
assetData.SetAsPackageAsset(path);
|
||||
assetData.ProcessSubAssets(subAssets);
|
||||
}
|
||||
else
|
||||
{
|
||||
TLogger.LogError($"Can not load the asset '{path}'");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
UnityEngine.Object obj = UnityEditor.AssetDatabase.LoadAssetAtPath<UnityEngine.Object>(path);
|
||||
if (obj != null)
|
||||
{
|
||||
assetData = new AssetData(path, null, obj);
|
||||
assetData.SetAsPackageAsset(path);
|
||||
}
|
||||
else
|
||||
{
|
||||
TLogger.LogError($"Can not load the asset '{path}'");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return assetData;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 资源是否存在
|
||||
/// </summary>
|
||||
/// <param name="path">通过右键菜单Get Asset Path获取的路径</param>
|
||||
/// <returns>true:存在,false:不存在</returns>
|
||||
public bool Exists(string path)
|
||||
{
|
||||
#if ASSETBUNDLE_ENABLE
|
||||
return _assetPath2BundleDatas.ContainsKey(path);
|
||||
#elif UNITY_EDITOR
|
||||
return File.Exists($"{AssetRootPath}/{path}");
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 异步获取Asset
|
||||
/// </summary>
|
||||
/// <param name="path">通过右键菜单Get Asset Path获取的路径</param>
|
||||
/// <param name="withSubAssets">是否加载子Asset,针对Sprite图集</param>
|
||||
/// <param name="onComplete">加载回调</param>
|
||||
/// <returns>Asset数据</returns>
|
||||
public void GetAssetAtPathAsync(string path, bool withSubAssets, System.Action<AssetData> onComplete)
|
||||
{
|
||||
#if ASSETBUNDLE_ENABLE
|
||||
AssetData assetData = null;
|
||||
AssetBundleData bundleData = FindAssetBundle(path);
|
||||
if (bundleData != null)
|
||||
{
|
||||
assetData = bundleData.GetAsset(path);
|
||||
if (assetData != null)
|
||||
{
|
||||
assetData.LoadAsync(onComplete, withSubAssets);
|
||||
}
|
||||
else
|
||||
{
|
||||
assetData = new AssetData(path, bundleData);
|
||||
bundleData.SetAsset(path, assetData);
|
||||
assetData.LoadAsync(onComplete, withSubAssets);
|
||||
}
|
||||
}
|
||||
else
|
||||
onComplete(null);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取场景Asset
|
||||
/// </summary>
|
||||
/// <param name="sceneName">场景名</param>
|
||||
/// <returns>Asset数据</returns>
|
||||
public AssetData GetSceneAsset(string sceneName, LoadSceneMode mode)
|
||||
{
|
||||
AssetData assetData = null;
|
||||
|
||||
#if ASSETBUNDLE_ENABLE
|
||||
AssetBundleData bundleData = FindAssetBundle(sceneName);
|
||||
if (bundleData != null)
|
||||
{
|
||||
assetData = new AssetData(sceneName, bundleData);
|
||||
assetData.LoadScene(mode);
|
||||
}
|
||||
else
|
||||
{
|
||||
TLogger.LogError($"Can not load scene asset '{sceneName}'");
|
||||
}
|
||||
#else
|
||||
assetData = new AssetData(sceneName);
|
||||
assetData.LoadScene(mode);
|
||||
#endif
|
||||
|
||||
return assetData;
|
||||
}
|
||||
|
||||
AssetBundleData FindAssetBundle(string path)
|
||||
{
|
||||
string bundleName;
|
||||
AssetBundleData assetBundleData = null;
|
||||
if (_assetPath2BundleDatas.TryGetValue(path, out bundleName))
|
||||
{
|
||||
if (!_bundleDatas.TryGetValue(bundleName, out assetBundleData))
|
||||
TLogger.LogError($"Can not get AssetBundleData with AssetBundle '{bundleName}'!");
|
||||
}
|
||||
else
|
||||
TLogger.LogError($"Can not find '{path}' in any AssetBundle!");
|
||||
|
||||
return assetBundleData;
|
||||
}
|
||||
}
|
||||
}
|
358
TEngineHotUpdate/src/TEngineCore/Res/AssetData.cs
Normal file
358
TEngineHotUpdate/src/TEngineCore/Res/AssetData.cs
Normal file
@@ -0,0 +1,358 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.SceneManagement;
|
||||
using UnityEngine.U2D;
|
||||
|
||||
namespace TEngineCore
|
||||
{
|
||||
/// <summary>
|
||||
/// 封装的AssetBundle
|
||||
/// </summary>
|
||||
public class AssetData
|
||||
{
|
||||
private AssetBundleData _refBundle;
|
||||
private Object _assetObject;
|
||||
private Dictionary<string, UnityEngine.Object> _subAssetObjects;
|
||||
private string _kPath;
|
||||
private string _fPath;
|
||||
private string _name;
|
||||
private int _refCount;
|
||||
private bool _useSubAsset;
|
||||
private AsyncOperation _asyncLoadRequest;
|
||||
private event System.Action<AssetData> _onAsyncLoadComplete;
|
||||
|
||||
public UnityEngine.Object AssetObject => _assetObject;
|
||||
|
||||
public UnityEngine.Object this[string key]
|
||||
{
|
||||
get
|
||||
{
|
||||
UnityEngine.Object assetObject = null;
|
||||
|
||||
if (_subAssetObjects != null)
|
||||
{
|
||||
if (!_subAssetObjects.TryGetValue(key, out assetObject))
|
||||
{
|
||||
if (_assetObject != null && _assetObject is SpriteAtlas atlas)
|
||||
{
|
||||
assetObject = atlas.GetSprite(key);
|
||||
if (assetObject != null)
|
||||
{
|
||||
_subAssetObjects.Add(key, assetObject);
|
||||
}
|
||||
else
|
||||
{
|
||||
TLogger.LogError($"Can not get sub asset({key}) from {_fPath}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return assetObject;
|
||||
}
|
||||
}
|
||||
|
||||
public string Path => _kPath;
|
||||
|
||||
public string FullPath => _fPath;
|
||||
|
||||
/// <summary>
|
||||
/// Asset名
|
||||
/// </summary>
|
||||
public string Name => _name;
|
||||
|
||||
/// <summary>
|
||||
/// 异步操作对象
|
||||
/// </summary>
|
||||
public AsyncOperation AsyncOp
|
||||
{
|
||||
get
|
||||
{
|
||||
return _asyncLoadRequest;
|
||||
}
|
||||
}
|
||||
|
||||
public event System.Action<AssetData> OnAsyncLoadComplete
|
||||
{
|
||||
add
|
||||
{
|
||||
_onAsyncLoadComplete += value;
|
||||
}
|
||||
remove
|
||||
{
|
||||
_onAsyncLoadComplete -= value;
|
||||
}
|
||||
}
|
||||
|
||||
public AssetData(string path = "", AssetBundleData refBundle = null, UnityEngine.Object assetObject = null)
|
||||
{
|
||||
_kPath = path;
|
||||
if (!string.IsNullOrEmpty(_kPath))
|
||||
{
|
||||
int ei = _kPath.LastIndexOf('/');
|
||||
_name = ei >= 0 ? _kPath.Substring(ei + 1) : _kPath;
|
||||
_fPath = $"{AssetConfig.AssetRootPath}/{_kPath}";
|
||||
}
|
||||
else
|
||||
{
|
||||
_fPath = System.String.Empty;
|
||||
}
|
||||
|
||||
_assetObject = assetObject;
|
||||
_refBundle = refBundle;
|
||||
if (refBundle != null)
|
||||
{
|
||||
_refBundle.AddRef();
|
||||
}
|
||||
}
|
||||
|
||||
internal void SetAsPackageAsset(string path)
|
||||
{
|
||||
_fPath = path;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 增引用计数
|
||||
/// </summary>
|
||||
public void AddRef()
|
||||
{
|
||||
++_refCount;
|
||||
#if UNITY_EDITOR
|
||||
//TLogger.LogInfo($"Add AssetData {_fPath} _refCount = {_refCount}");
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 减引用计数,当引用计数为0时减其AssetBundle的引用计数
|
||||
/// </summary>
|
||||
public void DecRef(bool bNoDelay = false)
|
||||
{
|
||||
--_refCount;
|
||||
#if UNITY_EDITOR
|
||||
//TLogger.LogInfo($"Dec AssetData {_fPath} _refCount = {_refCount}");
|
||||
#endif
|
||||
if (_refCount <= 0)
|
||||
{
|
||||
if (_refBundle != null)
|
||||
{
|
||||
_refBundle.DecRef(bNoDelay);
|
||||
_refBundle.SetAsset(_kPath, null);
|
||||
Unload();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 同步加载Asset
|
||||
/// </summary>
|
||||
/// <param name="withSubAssets">是否加载子Asset,针对Sprite图集</param>
|
||||
public void LoadAsset(bool withSubAssets = false)
|
||||
{
|
||||
_useSubAsset = withSubAssets;
|
||||
|
||||
if (_refBundle.Bundle == null)
|
||||
_refBundle.Load();
|
||||
|
||||
if (_refBundle.Bundle != null)
|
||||
{
|
||||
if (_useSubAsset)
|
||||
{
|
||||
UnityEngine.Object[] subAssets = _refBundle.Bundle.LoadAssetWithSubAssets(_fPath);
|
||||
if (subAssets != null && subAssets.Length > 0)
|
||||
{
|
||||
ProcessSubAssets(subAssets);
|
||||
}
|
||||
else
|
||||
{
|
||||
TLogger.LogError($"Can not load the asset '{_fPath}'");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
UnityEngine.Object assetObject = _refBundle.Bundle.LoadAsset(_fPath);
|
||||
if (assetObject != null)
|
||||
{
|
||||
_assetObject = assetObject;
|
||||
}
|
||||
else
|
||||
{
|
||||
TLogger.LogError($"Can not load the asset '{_fPath}'");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 异步加载Asset
|
||||
/// </summary>
|
||||
/// <param name="onComplete">加载回调</param>
|
||||
/// <param name="withSubAssets">是否加载子Asset,针对Sprite图集</param>
|
||||
public void LoadAsync(System.Action<AssetData> onComplete, bool withSubAssets = false)
|
||||
{
|
||||
_onAsyncLoadComplete -= onComplete;
|
||||
_onAsyncLoadComplete += onComplete;
|
||||
_useSubAsset = withSubAssets;
|
||||
|
||||
if (!_refBundle.IsLoadComplete)
|
||||
{
|
||||
_refBundle.LoadAsync(OnAssetBundleDataLoadComplete);
|
||||
}
|
||||
else
|
||||
{
|
||||
OnAssetBundleDataLoadComplete(_refBundle);
|
||||
}
|
||||
}
|
||||
|
||||
void OnAssetBundleDataLoadComplete(AssetBundleData bundleData)
|
||||
{
|
||||
if (_refBundle == bundleData)
|
||||
{
|
||||
_refBundle.OnAsyncLoadComplete -= OnAssetBundleDataLoadComplete;
|
||||
|
||||
if (_useSubAsset)
|
||||
{
|
||||
if (_subAssetObjects == null)
|
||||
{
|
||||
if (_asyncLoadRequest == null)
|
||||
{
|
||||
_asyncLoadRequest = bundleData.Bundle.LoadAssetWithSubAssetsAsync(_fPath);
|
||||
_asyncLoadRequest.completed += OnAssetLoadComplete;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_onAsyncLoadComplete(this);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_assetObject == null)
|
||||
{
|
||||
if (_asyncLoadRequest == null)
|
||||
{
|
||||
_asyncLoadRequest = bundleData.Bundle.LoadAssetAsync(_fPath);
|
||||
_asyncLoadRequest.completed += OnAssetLoadComplete;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_onAsyncLoadComplete(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
TLogger.LogError("Return a mismatch AssetBundleData for OnAssetBundleDataLoadComplete");
|
||||
}
|
||||
|
||||
void OnAssetLoadComplete(AsyncOperation asyncOperation)
|
||||
{
|
||||
if (asyncOperation.isDone)
|
||||
{
|
||||
if (_asyncLoadRequest != null && _asyncLoadRequest == asyncOperation)
|
||||
{
|
||||
AssetBundleRequest assetBundleRequest = asyncOperation as AssetBundleRequest;
|
||||
if (assetBundleRequest != null)
|
||||
{
|
||||
if (_useSubAsset)
|
||||
{
|
||||
if (assetBundleRequest.allAssets != null && assetBundleRequest.allAssets.Length > 0)
|
||||
{
|
||||
ProcessSubAssets(assetBundleRequest.allAssets);
|
||||
}
|
||||
else
|
||||
{
|
||||
TLogger.LogError($"Can not load the asset '{_fPath}'");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_assetObject = assetBundleRequest.asset;
|
||||
if (_assetObject == null)
|
||||
{
|
||||
TLogger.LogError($"Can not load the asset '{_fPath}'");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_asyncLoadRequest = null;
|
||||
_onAsyncLoadComplete(this);
|
||||
}
|
||||
else
|
||||
{
|
||||
TLogger.LogError("Return a mismatch asyncOperation for AssetBundleCreateRequest");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
TLogger.LogError("Return a not done AsyncOperation for AssetBundleCreateRequest");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
internal void ProcessSubAssets(UnityEngine.Object[] subAssets)
|
||||
{
|
||||
_subAssetObjects = new Dictionary<string, UnityEngine.Object>();
|
||||
|
||||
if (subAssets[0] is SpriteAtlas) // SpriteAtlas
|
||||
{
|
||||
_assetObject = subAssets[0];
|
||||
// load all 懒加载//TODO
|
||||
SpriteAtlas spriteAtlas = subAssets[0] as SpriteAtlas;
|
||||
Sprite[] sprites = new Sprite[spriteAtlas.spriteCount];
|
||||
int count = spriteAtlas.GetSprites(sprites);
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
string name = sprites[i].name;
|
||||
int ci = name.LastIndexOf("(Clone)", System.StringComparison.Ordinal);
|
||||
if (ci >= 0)
|
||||
name = name.Substring(0, ci);
|
||||
_subAssetObjects.Add(name, sprites[i]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < subAssets.Length; ++i)
|
||||
{
|
||||
if (subAssets[i].GetType() != typeof(UnityEngine.Texture2D))
|
||||
{
|
||||
_subAssetObjects.Add(subAssets[i].name, subAssets[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 加载场景
|
||||
/// </summary>
|
||||
/// <remarks>先加载场景AssetBundle,才能使用SceneManager中的加载接口</remarks>
|
||||
public void LoadScene(LoadSceneMode mode)
|
||||
{
|
||||
if (_refBundle != null && _refBundle.Bundle == null)
|
||||
_refBundle.Load();
|
||||
|
||||
_asyncLoadRequest = SceneManager.LoadSceneAsync(_name, mode);
|
||||
if (_asyncLoadRequest != null)
|
||||
_asyncLoadRequest.allowSceneActivation = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 卸载Asset
|
||||
/// </summary>
|
||||
/// <param name="bForce">是否强制卸载</param>
|
||||
public void Unload(bool bForce = false)
|
||||
{
|
||||
if (bForce || _refCount <= 0)
|
||||
{
|
||||
_assetObject = null;
|
||||
_subAssetObjects?.Clear();
|
||||
_refBundle = null;
|
||||
_onAsyncLoadComplete = null;
|
||||
_asyncLoadRequest = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
TLogger.LogWarning($"Try to unload refcount > 0 asset({_fPath})!");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
49
TEngineHotUpdate/src/TEngineCore/Res/AssetTag.cs
Normal file
49
TEngineHotUpdate/src/TEngineCore/Res/AssetTag.cs
Normal file
@@ -0,0 +1,49 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace TEngineCore
|
||||
{
|
||||
/// <summary>
|
||||
/// GameObject与AssetData的绑定对象,用于管理对AssetData的引用计数
|
||||
/// </summary>
|
||||
[DisallowMultipleComponent, AddComponentMenu("")]
|
||||
public sealed class AssetTag : MonoBehaviour
|
||||
{
|
||||
AssetData _assetData;
|
||||
[SerializeField, HideInInspector] private string _path;
|
||||
/// <summary>
|
||||
/// 对应的资源路径
|
||||
/// </summary>
|
||||
public string Path => _path;
|
||||
|
||||
/// <summary>
|
||||
/// 缓存池中的归还时间戳
|
||||
/// </summary>
|
||||
public float PoolReturnTimestamp { get; set; }
|
||||
|
||||
private int _instanceID = 0;
|
||||
private void Awake()
|
||||
{
|
||||
// 处理Cloned GameObject上引用计数不正确的问题
|
||||
if (_path != null && (_instanceID == -1 || _instanceID != gameObject.GetInstanceID()))
|
||||
Bind(ResMgr.Instance.GetAsset(_path, false));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// GameObject绑定AssetData
|
||||
/// </summary>
|
||||
/// <param name="assetData">Asset数据</param>
|
||||
public void Bind(AssetData assetData)
|
||||
{
|
||||
_assetData = assetData;
|
||||
_assetData.AddRef();
|
||||
_path = _assetData.Path;
|
||||
_instanceID = gameObject.GetInstanceID();
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
_assetData.DecRef();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
259
TEngineHotUpdate/src/TEngineCore/Res/ResMgr.cs
Normal file
259
TEngineHotUpdate/src/TEngineCore/Res/ResMgr.cs
Normal file
@@ -0,0 +1,259 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace TEngineCore
|
||||
{
|
||||
internal class ResMgr : TSingleton<ResMgr>
|
||||
{
|
||||
AssetConfig _assetConfig = new AssetConfig();
|
||||
private Dictionary<ScriptableObject, AssetData> _scriptableObjects = new Dictionary<ScriptableObject, AssetData>();
|
||||
public ResMgr()
|
||||
{
|
||||
_assetConfig.Load();
|
||||
}
|
||||
|
||||
~ResMgr()
|
||||
{
|
||||
_assetConfig.Unload();
|
||||
}
|
||||
public void AssetUnloadDelay(float value)
|
||||
{
|
||||
_assetConfig.AssetUnloadDelay = value;
|
||||
}
|
||||
|
||||
public void MaxUnloadNumPerFrame(int value)
|
||||
{
|
||||
_assetConfig.MaxUnloadNumPerFrame = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 卸载无用资源
|
||||
/// </summary>
|
||||
public void UnloadUnusedAssetBundle()
|
||||
{
|
||||
_assetConfig.UnloadUnusedAssetBundle();
|
||||
}
|
||||
|
||||
#region 获取资源
|
||||
/// <summary>
|
||||
/// 从文件获取字符串
|
||||
/// </summary>
|
||||
/// <param name="path">Asset下路径</param>
|
||||
/// <returns></returns>
|
||||
public string GetStringFromAsset(string path)
|
||||
{
|
||||
if (string.IsNullOrEmpty(path))
|
||||
return null;
|
||||
|
||||
string result = null;
|
||||
AssetData assetData = _assetConfig.GetAssetAtPath(path);
|
||||
|
||||
if (assetData != null)
|
||||
{
|
||||
assetData.AddRef();
|
||||
TextAsset textAsset = assetData.AssetObject as TextAsset;
|
||||
result = textAsset.text;
|
||||
assetData.DecRef();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public T GetScriptableObject<T>(string path) where T : ScriptableObject
|
||||
{
|
||||
T result = null;
|
||||
|
||||
if (!string.IsNullOrEmpty(path))
|
||||
{
|
||||
AssetData assetData = _assetConfig.GetAssetAtPath(path);
|
||||
|
||||
if (assetData != null)
|
||||
{
|
||||
assetData.AddRef();
|
||||
result = assetData.AssetObject as T;
|
||||
if (result != null && !_scriptableObjects.ContainsKey(result))
|
||||
_scriptableObjects.Add(result, assetData);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public T Load<T>(string path) where T:UnityEngine.Object
|
||||
{
|
||||
if (string.IsNullOrEmpty(path))
|
||||
return null;
|
||||
|
||||
T result = null;
|
||||
AssetData assetData = _assetConfig.GetAssetAtPath(path);
|
||||
|
||||
if (assetData != null)
|
||||
{
|
||||
result = assetData.AssetObject as T;
|
||||
if (result is GameObject)
|
||||
{
|
||||
var go = Object.Instantiate(assetData.AssetObject) as GameObject;
|
||||
BindAssetData(go, assetData);
|
||||
}
|
||||
else
|
||||
{
|
||||
assetData.AddRef();
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public GameObject Load(string path)
|
||||
{
|
||||
return GetGameObject(path);
|
||||
}
|
||||
|
||||
public GameObject GetGameObject(string path)
|
||||
{
|
||||
if (string.IsNullOrEmpty(path))
|
||||
return null;
|
||||
|
||||
GameObject go = null;
|
||||
AssetData assetData = _assetConfig.GetAssetAtPath(path);
|
||||
|
||||
if (assetData != null)
|
||||
{
|
||||
go = Object.Instantiate(assetData.AssetObject) as GameObject;
|
||||
BindAssetData(go, assetData);
|
||||
}
|
||||
|
||||
return go;
|
||||
}
|
||||
|
||||
public AssetData GetAsset(string path, bool withSubAssets)
|
||||
{
|
||||
if (string.IsNullOrEmpty(path))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return _assetConfig.GetAssetAtPath(path, withSubAssets);
|
||||
}
|
||||
|
||||
public byte[] GetBytesFromAsset(string path)
|
||||
{
|
||||
if (string.IsNullOrEmpty(path))
|
||||
return null;
|
||||
|
||||
byte[] result = null;
|
||||
AssetData assetData = _assetConfig.GetAssetAtPath(path);
|
||||
|
||||
if (assetData != null)
|
||||
{
|
||||
assetData.AddRef();
|
||||
TextAsset textAsset = assetData.AssetObject as TextAsset;
|
||||
result = textAsset.bytes;
|
||||
assetData.DecRef();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public void DestroyScriptableObject(ScriptableObject scriptableObject)
|
||||
{
|
||||
if (_scriptableObjects.TryGetValue(scriptableObject, out var assetData))
|
||||
{
|
||||
assetData.DecRef();
|
||||
_scriptableObjects.Remove(scriptableObject);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
#region 异步获取资源
|
||||
public void GetGameObjectAsync(string path, System.Action<GameObject> onComplete)
|
||||
{
|
||||
if (string.IsNullOrEmpty(path))
|
||||
onComplete(null);
|
||||
|
||||
#if ASSETBUNDLE_ENABLE
|
||||
void CallBack(AssetData assetData)
|
||||
{
|
||||
GameObject go = null;
|
||||
if (assetData != null)
|
||||
{
|
||||
go = Object.Instantiate(assetData.AssetObject) as GameObject;
|
||||
BindAssetData(go, assetData);
|
||||
assetData.OnAsyncLoadComplete -= CallBack;
|
||||
}
|
||||
|
||||
onComplete(go);
|
||||
}
|
||||
|
||||
_assetConfig.GetAssetAtPathAsync(path, false, CallBack);
|
||||
#else
|
||||
onComplete(GetGameObject(path));
|
||||
#endif
|
||||
}
|
||||
|
||||
public void GetAssetAtPathAsync(string path, bool withSubAssets, System.Action<AssetData> onComplete)
|
||||
{
|
||||
if (string.IsNullOrEmpty(path))
|
||||
{
|
||||
onComplete(null);
|
||||
}
|
||||
#if ASSETBUNDLE_ENABLE
|
||||
void CallBack(AssetData assetData)
|
||||
{
|
||||
if (assetData != null)
|
||||
{
|
||||
if (assetData.AssetObject is GameObject)
|
||||
{
|
||||
GameObject go = Object.Instantiate(assetData.AssetObject) as GameObject;
|
||||
BindAssetData(go, assetData);
|
||||
}
|
||||
else
|
||||
{
|
||||
assetData.AddRef();
|
||||
}
|
||||
assetData.OnAsyncLoadComplete -= CallBack;
|
||||
|
||||
onComplete(assetData);
|
||||
}
|
||||
_assetConfig.GetAssetAtPathAsync(path, withSubAssets, CallBack);
|
||||
}
|
||||
#else
|
||||
onComplete(GetAsset(path,withSubAssets));
|
||||
#endif
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public bool Exists(string path)
|
||||
{
|
||||
return _assetConfig.Exists(path);
|
||||
}
|
||||
|
||||
public void Release(GameObject go)
|
||||
{
|
||||
if (go == null) return;
|
||||
|
||||
Object.Destroy(go);
|
||||
}
|
||||
|
||||
private void BindAssetData(GameObject go, AssetData assetData)
|
||||
{
|
||||
bool isActive = go.activeSelf;
|
||||
GameObject prefab = (GameObject)assetData.AssetObject;
|
||||
if (!go.activeSelf && prefab.activeSelf)
|
||||
{
|
||||
go.SetActive(true);
|
||||
}
|
||||
else if (!prefab.activeSelf)
|
||||
{
|
||||
TLogger.LogWarning($"Try to get gameObject by an inactive Prefab ({assetData.Path})!");
|
||||
}
|
||||
go.AddComponent<AssetTag>().Bind(assetData);
|
||||
if (isActive != go.activeSelf)
|
||||
{
|
||||
go.SetActive(isActive);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
41
TEngineHotUpdate/src/TEngineCore/Res/TResource.cs
Normal file
41
TEngineHotUpdate/src/TEngineCore/Res/TResource.cs
Normal file
@@ -0,0 +1,41 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace TEngineCore
|
||||
{
|
||||
public static class TResources
|
||||
{
|
||||
#region 同步加载
|
||||
public static GameObject Load(string path)
|
||||
{
|
||||
return ResMgr.Instance.Load(path);
|
||||
}
|
||||
public static GameObject Load(string path, Transform parent)
|
||||
{
|
||||
var obj = Load(path);
|
||||
|
||||
if (obj != null && parent != null)
|
||||
{
|
||||
obj.transform.SetParent(parent);
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
public static T Load<T>(string path) where T : UnityEngine.Object
|
||||
{
|
||||
return ResMgr.Instance.Load<T>(path);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 异步加载
|
||||
public static void LoadAsync(string path, Action<GameObject> callBack)
|
||||
{
|
||||
ResMgr.Instance.GetGameObjectAsync(path, callBack);
|
||||
}
|
||||
public static void LoadAsync(string path, Action<AssetData> callBack, bool withSubAsset = false)
|
||||
{
|
||||
ResMgr.Instance.GetAssetAtPathAsync(path, withSubAsset, callBack);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
193
TEngineHotUpdate/src/TEngineCore/Thread/Loom.cs
Normal file
193
TEngineHotUpdate/src/TEngineCore/Thread/Loom.cs
Normal file
@@ -0,0 +1,193 @@
|
||||
using UnityEngine;
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Linq;
|
||||
|
||||
/*******************************************************************************
|
||||
* 开启一个Loom进程
|
||||
Loom.RunAsync(() =>
|
||||
{
|
||||
aucThread = new Thread(ReceiveMsg);
|
||||
aucThread.Start();
|
||||
}
|
||||
进程调用主线程方法
|
||||
MainPack pack = (MainPack)MainPack.Descriptor.Parser.ParseFrom(buffer, 0, len);
|
||||
Loom.QueueOnMainThread((param) =>
|
||||
{
|
||||
UdpHandleResponse(pack);
|
||||
}, null);
|
||||
|
||||
*******************************************************************************/
|
||||
namespace TEngineCore
|
||||
{
|
||||
// <summary>
|
||||
// 从子线程调用Unity的方法
|
||||
// </summary>
|
||||
public class Loom : MonoBehaviour
|
||||
{
|
||||
public Dictionary<string, CancellationTokenSource> TokenSourcesDictionary = new Dictionary<string, CancellationTokenSource>();
|
||||
public static int MaxThreads = 8;
|
||||
private static int _numThreads;
|
||||
private static Loom _current;
|
||||
public static Loom Current
|
||||
{
|
||||
get
|
||||
{
|
||||
Initialize();
|
||||
return _current;
|
||||
}
|
||||
}
|
||||
|
||||
public void Awake()
|
||||
{
|
||||
_current = this;
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
protected void OnDestroy()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
private static bool initialized;
|
||||
|
||||
public static void Initialize()
|
||||
{
|
||||
if (!initialized)
|
||||
{
|
||||
if (!Application.isPlaying)
|
||||
{
|
||||
return;
|
||||
}
|
||||
initialized = true;
|
||||
|
||||
var obj = new GameObject("Loom");
|
||||
|
||||
_current = obj.AddComponent<Loom>();
|
||||
|
||||
GameObject tEngine = SingletonMgr.Root;
|
||||
|
||||
if (tEngine != null)
|
||||
{
|
||||
obj.transform.SetParent(tEngine.transform);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public struct NoDelayedQueueItem
|
||||
{
|
||||
public Action<object> action;
|
||||
public object param;
|
||||
}
|
||||
|
||||
private List<NoDelayedQueueItem> _actions = new List<NoDelayedQueueItem>();
|
||||
|
||||
public struct DelayedQueueItem
|
||||
{
|
||||
public float time;
|
||||
public Action<object> action;
|
||||
public object param;
|
||||
}
|
||||
|
||||
private List<DelayedQueueItem> _delayed = new List<DelayedQueueItem>();
|
||||
|
||||
List<DelayedQueueItem> _currentDelayed = new List<DelayedQueueItem>();
|
||||
|
||||
public static void QueueOnMainThread(Action<object> taction, object tparam)
|
||||
{
|
||||
QueueOnMainThread(taction, tparam, 0f);
|
||||
}
|
||||
public static void QueueOnMainThread(Action<object> taction, object tparam, float time)
|
||||
{
|
||||
if (time != 0f)
|
||||
{
|
||||
lock (Current._delayed)
|
||||
{
|
||||
Current._delayed.Add(new DelayedQueueItem { time = Time.time + time, action = taction, param = tparam });
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
lock (Current._actions)
|
||||
{
|
||||
Current._actions.Add(new NoDelayedQueueItem { action = taction, param = tparam });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static Thread RunAsync(string actionName,Action action)
|
||||
{
|
||||
Initialize();
|
||||
while (_numThreads >= MaxThreads)
|
||||
{
|
||||
Thread.Sleep(100);
|
||||
}
|
||||
Interlocked.Increment(ref _numThreads);
|
||||
ThreadPool.QueueUserWorkItem(RunAction, action);
|
||||
return null;
|
||||
}
|
||||
|
||||
private static void RunAction(object action)
|
||||
{
|
||||
try
|
||||
{
|
||||
((Action)action)();
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
||||
}
|
||||
finally
|
||||
{
|
||||
Interlocked.Decrement(ref _numThreads);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void OnDisable()
|
||||
{
|
||||
if (_current == this)
|
||||
{
|
||||
_current = null;
|
||||
}
|
||||
}
|
||||
|
||||
List<NoDelayedQueueItem> _currentActions = new List<NoDelayedQueueItem>();
|
||||
|
||||
void Update()
|
||||
{
|
||||
if (_actions.Count > 0)
|
||||
{
|
||||
lock (_actions)
|
||||
{
|
||||
_currentActions.Clear();
|
||||
_currentActions.AddRange(_actions);
|
||||
_actions.Clear();
|
||||
}
|
||||
for (int i = 0; i < _currentActions.Count; i++)
|
||||
{
|
||||
_currentActions[i].action(_currentActions[i].param);
|
||||
}
|
||||
}
|
||||
|
||||
if (_delayed.Count > 0)
|
||||
{
|
||||
lock (_delayed)
|
||||
{
|
||||
_currentDelayed.Clear();
|
||||
_currentDelayed.AddRange(_delayed.Where(d => d.time <= Time.time));
|
||||
for (int i = 0; i < _currentDelayed.Count; i++)
|
||||
{
|
||||
_delayed.Remove(_currentDelayed[i]);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < _currentDelayed.Count; i++)
|
||||
{
|
||||
_currentDelayed[i].action(_currentDelayed[i].param);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
27
TEngineHotUpdate/src/TEngineCore/Thread/ThreadMgr.cs
Normal file
27
TEngineHotUpdate/src/TEngineCore/Thread/ThreadMgr.cs
Normal file
@@ -0,0 +1,27 @@
|
||||
namespace TEngineCore
|
||||
{
|
||||
public class ThreadMgr : UnitySingleton<ThreadMgr>
|
||||
{
|
||||
protected override void OnLoad()
|
||||
{
|
||||
base.OnLoad();
|
||||
StartThread();
|
||||
}
|
||||
|
||||
protected override void OnDestroy()
|
||||
{
|
||||
base.OnDestroy();
|
||||
ShutDownThread();
|
||||
}
|
||||
|
||||
private void StartThread()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
private void ShutDownThread()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
119
TEngineHotUpdate/src/TEngineCore/UI/Extend/DragItem.cs
Normal file
119
TEngineHotUpdate/src/TEngineCore/UI/Extend/DragItem.cs
Normal file
@@ -0,0 +1,119 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.EventSystems;
|
||||
|
||||
|
||||
namespace TEngineCore
|
||||
{
|
||||
public enum UIDragType
|
||||
{
|
||||
Draging,
|
||||
Drop
|
||||
}
|
||||
|
||||
public class DragItem : UIEventItem<DragItem>
|
||||
{
|
||||
private UIDragType m_DragState = UIDragType.Drop;
|
||||
private Vector3 m_ItemOldPos;
|
||||
private Vector3 m_ItemCachePos;
|
||||
private bool m_CanDrag = false;
|
||||
|
||||
/// <summary>
|
||||
/// 是否可以拖拽
|
||||
/// </summary>
|
||||
public bool CanDrag
|
||||
{
|
||||
get
|
||||
{
|
||||
return m_CanDrag;
|
||||
}
|
||||
set
|
||||
{
|
||||
m_CanDrag = value;
|
||||
if (m_CanDrag)
|
||||
{
|
||||
BindDrag();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnCreate()
|
||||
{
|
||||
base.OnCreate();
|
||||
BindDrag();
|
||||
}
|
||||
|
||||
private void BindDrag()
|
||||
{
|
||||
if (m_CanDrag)
|
||||
{
|
||||
BindBeginDragEvent(delegate (DragItem item, PointerEventData data)
|
||||
{
|
||||
if (!m_CanDrag)
|
||||
{
|
||||
return;
|
||||
}
|
||||
StartDragItem(UIDragType.Draging);
|
||||
});
|
||||
|
||||
BindEndDragEvent(delegate (DragItem item, PointerEventData data)
|
||||
{
|
||||
if (!m_CanDrag)
|
||||
{
|
||||
return;
|
||||
}
|
||||
EndDrag();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnUpdate()
|
||||
{
|
||||
if (!m_CanDrag)
|
||||
{
|
||||
return;
|
||||
}
|
||||
UpdateDragPos();
|
||||
}
|
||||
|
||||
private void StartDragItem(UIDragType type)
|
||||
{
|
||||
if (type != UIDragType.Drop)
|
||||
{
|
||||
m_ItemOldPos = transform.position;
|
||||
Vector3 pos;
|
||||
UISys.Mgr.GetMouseDownUiPos(out pos);
|
||||
m_ItemCachePos = pos;
|
||||
UpdateDragPos();
|
||||
m_DragState = type;
|
||||
}
|
||||
}
|
||||
|
||||
private void EndDrag()
|
||||
{
|
||||
m_DragState = UIDragType.Drop;
|
||||
transform.position = m_ItemOldPos;
|
||||
#if UNITY_EDITOR
|
||||
//Debug.LogError("m_ItemCachePos.y - m_ItemOldPos.y " + (m_ItemCachePos.y - m_ItemOldPos.y));
|
||||
#endif
|
||||
if (m_ItemCachePos.y - m_ItemOldPos.y > 3)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateDragPos()
|
||||
{
|
||||
if (m_DragState == UIDragType.Drop)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Vector3 pos;
|
||||
UISys.Mgr.GetMouseDownUiPos(out pos);
|
||||
transform.position += (pos - m_ItemCachePos);
|
||||
m_ItemCachePos = pos;
|
||||
}
|
||||
}
|
||||
}
|
18
TEngineHotUpdate/src/TEngineCore/UI/Extend/EmptyGraph.cs
Normal file
18
TEngineHotUpdate/src/TEngineCore/UI/Extend/EmptyGraph.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
using UnityEngine.UI;
|
||||
|
||||
public class EmptyGraph : Graphic
|
||||
{
|
||||
public bool m_debug = false;
|
||||
|
||||
protected override void OnPopulateMesh(VertexHelper vbo)
|
||||
{
|
||||
vbo.Clear();
|
||||
|
||||
#if UNITY_EDITOR
|
||||
if (m_debug)
|
||||
{
|
||||
base.OnPopulateMesh(vbo);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
253
TEngineHotUpdate/src/TEngineCore/UI/Extend/TipsUI.cs
Normal file
253
TEngineHotUpdate/src/TEngineCore/UI/Extend/TipsUI.cs
Normal file
@@ -0,0 +1,253 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using TEngineCore;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace TEngineCore
|
||||
{
|
||||
public class MsgUI : UIWindow
|
||||
{
|
||||
protected override void RegisterEvent()
|
||||
{
|
||||
base.RegisterEvent();
|
||||
GameEventMgr.Instance.AddEventListener<string>(TipsEvent.Log, TipsUI.Instance.Show);
|
||||
}
|
||||
}
|
||||
|
||||
public class TipsEvent
|
||||
{
|
||||
public static int Log = StringId.StringToHash("TipsEvent.Log");
|
||||
}
|
||||
|
||||
public class TipsUI : MonoBehaviour
|
||||
{
|
||||
//单例
|
||||
public static TipsUI Instance;
|
||||
private void Awake()
|
||||
{
|
||||
Instance = this;
|
||||
}
|
||||
//Prefab
|
||||
public GameObject Prefab;
|
||||
public GameObject Mask;
|
||||
//对象池
|
||||
public List<GameObject> AvailablePool;
|
||||
public List<GameObject> UsedPool;
|
||||
//自定义内容
|
||||
[SerializeField] private float Speed;
|
||||
//定时器
|
||||
private float timer1;
|
||||
private float timer2;
|
||||
void Start()
|
||||
{
|
||||
AvailablePool = new List<GameObject>();
|
||||
UsedPool = new List<GameObject>();
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
AvailablePool.Add(Instantiate(Prefab, Mask.transform));
|
||||
AvailablePool[i].SetActive(false);
|
||||
}
|
||||
//GameEventMgr.Instance.AddEventListener<string>(TipsEvent.Log, Show);
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
//如果一定时间内没有新消息,则挨个消失
|
||||
timer1 += Time.deltaTime;
|
||||
if (timer1 >= 2)
|
||||
{
|
||||
timer2 += Time.deltaTime;
|
||||
if (timer2 > 1)
|
||||
{
|
||||
timer2 = 0;
|
||||
if (UsedPool.Count > 0)
|
||||
{
|
||||
Text Tname = UsedPool[0].transform.Find("Name").GetComponent<Text>();
|
||||
Text Tmessage = UsedPool[0].transform.Find("Message").GetComponent<Text>();
|
||||
Image BG = UsedPool[0].GetComponent<Image>();
|
||||
StartCoroutine(AlphaDown(Tname));
|
||||
StartCoroutine(AlphaDown(Tmessage));
|
||||
StartCoroutine(ImageAlphaDown(BG));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
public void Show(string message)
|
||||
{
|
||||
GameObject go;
|
||||
//使用对象池
|
||||
if (AvailablePool.Count > 0)
|
||||
{
|
||||
go = AvailablePool[0];
|
||||
AvailablePool.Remove(go);
|
||||
UsedPool.Add(go);
|
||||
}
|
||||
else
|
||||
{
|
||||
go = UsedPool[0];
|
||||
UsedPool.Remove(go);
|
||||
UsedPool.Add(go);
|
||||
}
|
||||
//进行一些初始化设定
|
||||
go.transform.localPosition = new Vector3(0, -75, 0);
|
||||
go.SetActive(true);
|
||||
go.GetComponent<Image>().color = new Color(0, 0, 0, 150f / 255f);
|
||||
timer1 = timer2 = 0;
|
||||
Text Tname = go.transform.Find("Name").GetComponent<Text>();
|
||||
Text Tmessage = go.transform.Find("Message").GetComponent<Text>();
|
||||
Tname.text = " " + "SYSTEM";
|
||||
Tmessage.text = " " + message;
|
||||
float TnameWidth = Tname.preferredWidth;
|
||||
float TmessageWidth = Tmessage.preferredWidth;
|
||||
float goSizey = go.transform.GetComponent<RectTransform>().sizeDelta.y;
|
||||
go.transform.GetComponent<RectTransform>().sizeDelta = new Vector2(TnameWidth + TmessageWidth, goSizey);
|
||||
Tname.rectTransform.sizeDelta = new Vector2(TnameWidth, Tname.rectTransform.sizeDelta.y);
|
||||
Tname.rectTransform.anchoredPosition = new Vector2(TnameWidth / 2, Tname.rectTransform.anchoredPosition.y);
|
||||
Tmessage.rectTransform.sizeDelta = new Vector2(TmessageWidth, Tmessage.rectTransform.sizeDelta.y);
|
||||
Tmessage.rectTransform.anchoredPosition = new Vector2(-TmessageWidth / 2, Tmessage.rectTransform.anchoredPosition.y);
|
||||
StartCoroutine(AlphaUP(Tname));
|
||||
StartCoroutine(AlphaUP(Tmessage));
|
||||
foreach (GameObject go1 in UsedPool)
|
||||
{
|
||||
StartCoroutine(MoveUP(go1));
|
||||
}
|
||||
if (UsedPool.Count >= 4)
|
||||
{
|
||||
Text Tname2 = UsedPool[UsedPool.Count - 4].transform.Find("Name").GetComponent<Text>();
|
||||
Text Tmessage2 = UsedPool[UsedPool.Count - 4].transform.Find("Message").GetComponent<Text>();
|
||||
Image BG = UsedPool[UsedPool.Count - 4].GetComponent<Image>();
|
||||
StartCoroutine(AlphaDown(Tname2));
|
||||
StartCoroutine(AlphaDown(Tmessage2));
|
||||
StartCoroutine(ImageAlphaDown(BG));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void Show(string name, string message)
|
||||
{
|
||||
GameObject go;
|
||||
//使用对象池
|
||||
if (AvailablePool.Count > 0)
|
||||
{
|
||||
go = AvailablePool[0];
|
||||
AvailablePool.Remove(go);
|
||||
UsedPool.Add(go);
|
||||
}
|
||||
else
|
||||
{
|
||||
go = UsedPool[0];
|
||||
UsedPool.Remove(go);
|
||||
UsedPool.Add(go);
|
||||
}
|
||||
//进行一些初始化设定
|
||||
go.transform.localPosition = new Vector3(0, -75, 0);
|
||||
go.SetActive(true);
|
||||
go.GetComponent<Image>().color = new Color(0, 0, 0, 150f / 255f);
|
||||
timer1 = timer2 = 0;
|
||||
Text Tname = go.transform.Find("Name").GetComponent<Text>();
|
||||
Text Tmessage = go.transform.Find("Message").GetComponent<Text>();
|
||||
Tname.text = " " + name;
|
||||
Tmessage.text = " " + message;
|
||||
float TnameWidth = Tname.preferredWidth;
|
||||
float TmessageWidth = Tmessage.preferredWidth;
|
||||
float goSizey = go.transform.GetComponent<RectTransform>().sizeDelta.y;
|
||||
go.transform.GetComponent<RectTransform>().sizeDelta = new Vector2(TnameWidth + TmessageWidth, goSizey);
|
||||
Tname.rectTransform.sizeDelta = new Vector2(TnameWidth, Tname.rectTransform.sizeDelta.y);
|
||||
Tname.rectTransform.anchoredPosition = new Vector2(TnameWidth / 2, Tname.rectTransform.anchoredPosition.y);
|
||||
Tmessage.rectTransform.sizeDelta = new Vector2(TmessageWidth, Tmessage.rectTransform.sizeDelta.y);
|
||||
Tmessage.rectTransform.anchoredPosition = new Vector2(-TmessageWidth / 2, Tmessage.rectTransform.anchoredPosition.y);
|
||||
StartCoroutine(AlphaUP(Tname));
|
||||
StartCoroutine(AlphaUP(Tmessage));
|
||||
foreach (GameObject go1 in UsedPool)
|
||||
{
|
||||
StartCoroutine(MoveUP(go1));
|
||||
}
|
||||
if (UsedPool.Count >= 4)
|
||||
{
|
||||
Text Tname2 = UsedPool[UsedPool.Count - 4].transform.Find("Name").GetComponent<Text>();
|
||||
Text Tmessage2 = UsedPool[UsedPool.Count - 4].transform.Find("Message").GetComponent<Text>();
|
||||
Image BG = UsedPool[UsedPool.Count - 4].GetComponent<Image>();
|
||||
StartCoroutine(AlphaDown(Tname2));
|
||||
StartCoroutine(AlphaDown(Tmessage2));
|
||||
StartCoroutine(ImageAlphaDown(BG));
|
||||
}
|
||||
}
|
||||
//文字透明度提高
|
||||
public IEnumerator AlphaUP(Text text)
|
||||
{
|
||||
text.color += new Color(0, 0, 0, -1);
|
||||
while (true)
|
||||
{
|
||||
yield return new WaitForSeconds(0.01f);
|
||||
text.color += new Color(0, 0, 0, 0.08f);
|
||||
if (text.color.a >= 1)
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
}
|
||||
}
|
||||
//向上移动
|
||||
public IEnumerator MoveUP(GameObject go)
|
||||
{
|
||||
float i = 0;
|
||||
while (true)
|
||||
{
|
||||
yield return new WaitForSeconds(0.01f);
|
||||
if (i + Speed >= 35)
|
||||
{
|
||||
go.transform.localPosition += new Vector3(0, 35 - i, 0);
|
||||
yield break;
|
||||
}
|
||||
else
|
||||
{
|
||||
go.transform.localPosition += new Vector3(0, Speed, 0);
|
||||
i += Speed;
|
||||
}
|
||||
}
|
||||
}
|
||||
//文字透明度下降
|
||||
public IEnumerator AlphaDown(Text text)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
yield return new WaitForSeconds(0.01f);
|
||||
text.color -= new Color(0, 0, 0, 0.08f);
|
||||
if (text.color.a <= 0)
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
}
|
||||
}
|
||||
//背景透明度下降
|
||||
public IEnumerator ImageAlphaDown(Image image)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
yield return new WaitForSeconds(0.01f);
|
||||
image.color -= new Color(0, 0, 0, 0.08f);
|
||||
if (image.color.a <= 0)
|
||||
{
|
||||
image.gameObject.SetActive(false);
|
||||
UsedPool.Remove(image.gameObject);
|
||||
AvailablePool.Add(image.gameObject);
|
||||
yield break;
|
||||
}
|
||||
}
|
||||
}
|
||||
//三个按钮的测试函数
|
||||
public void test()
|
||||
{
|
||||
Show("[没关系丶是爱情啊(安娜)]", "该睡觉咯。");
|
||||
}
|
||||
public void test2()
|
||||
{
|
||||
Show("[没关系丶是爱情啊(安娜)]", "妈妈永远是对的。");
|
||||
}
|
||||
public void test3()
|
||||
{
|
||||
Show("[没关系丶是爱情啊(安娜)]", "天降正义。");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
112
TEngineHotUpdate/src/TEngineCore/UI/Extend/TweenUtil.cs
Normal file
112
TEngineHotUpdate/src/TEngineCore/UI/Extend/TweenUtil.cs
Normal file
@@ -0,0 +1,112 @@
|
||||
using TEngineCore;
|
||||
using UnityEngine;
|
||||
|
||||
namespace TEngineCore
|
||||
{
|
||||
public enum TweenType
|
||||
{
|
||||
Position,
|
||||
Rotation,
|
||||
Scale,
|
||||
Alpha,
|
||||
}
|
||||
|
||||
public class TweenUtil : MonoBehaviour
|
||||
{
|
||||
public bool isLocal;
|
||||
public TweenType type;
|
||||
public Vector3 from;
|
||||
public Vector3 to;
|
||||
public AnimationCurve curve = AnimationCurve.Linear(0, 0, 1, 1);
|
||||
public float duration = 1f;
|
||||
public bool isLoop;
|
||||
public bool isPingPong;
|
||||
private float timer = 0f;
|
||||
private CanvasGroup canvasGroup;
|
||||
|
||||
void Awake()
|
||||
{
|
||||
canvasGroup = gameObject.GetComponent<CanvasGroup>();
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
if (duration > 0)
|
||||
{
|
||||
timer += Time.deltaTime;
|
||||
float curveValue;
|
||||
if (isLoop)
|
||||
{
|
||||
float remainTime = timer % duration;
|
||||
int loopCount = (int)(timer / duration);
|
||||
float evaluateTime = remainTime / duration;
|
||||
if (isPingPong)
|
||||
{
|
||||
evaluateTime = loopCount % 2 == 0 ? evaluateTime : 1 - evaluateTime;
|
||||
}
|
||||
curveValue = curve.Evaluate(evaluateTime);
|
||||
}
|
||||
else
|
||||
{
|
||||
curveValue = curve.Evaluate(timer);
|
||||
}
|
||||
var lerpValue = Vector3.Lerp(from, to, curveValue);
|
||||
//if (lerpValue == lastValue)
|
||||
//{
|
||||
// return;
|
||||
//}
|
||||
//lastValue = lerpValue;
|
||||
switch (type)
|
||||
{
|
||||
case TweenType.Position:
|
||||
if (isLocal)
|
||||
{
|
||||
transform.localPosition = lerpValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
transform.position = lerpValue;
|
||||
}
|
||||
break;
|
||||
case TweenType.Rotation:
|
||||
if (isLocal)
|
||||
{
|
||||
transform.localEulerAngles = lerpValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
transform.eulerAngles = lerpValue;
|
||||
}
|
||||
break;
|
||||
case TweenType.Scale:
|
||||
if (isLocal)
|
||||
{
|
||||
transform.localScale = lerpValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
var value1 = VectorWiseDivision(transform.lossyScale, transform.localScale);
|
||||
var value2 = VectorWiseDivision(lerpValue, value1);
|
||||
transform.localScale = value2;
|
||||
}
|
||||
break;
|
||||
case TweenType.Alpha:
|
||||
if (canvasGroup != null)
|
||||
{
|
||||
canvasGroup.alpha = lerpValue.x;
|
||||
}
|
||||
else
|
||||
{
|
||||
TLogger.LogError("Change Alpha need Component: [CanvasGroup]");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Vector3 VectorWiseDivision(Vector3 a, Vector3 b)
|
||||
{
|
||||
return new Vector3(a.x / b.x, a.y / b.y, a.z / b.z);
|
||||
}
|
||||
}
|
||||
}
|
200
TEngineHotUpdate/src/TEngineCore/UI/Extend/UIEventItem.cs
Normal file
200
TEngineHotUpdate/src/TEngineCore/UI/Extend/UIEventItem.cs
Normal file
@@ -0,0 +1,200 @@
|
||||
using System;
|
||||
using TEngineCore;
|
||||
using UnityEngine.EventSystems;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace TEngineCore
|
||||
{
|
||||
public class UIEventItem<T> : UIWindowWidget where T : UIEventItem<T>
|
||||
{
|
||||
protected Button m_buttonClick;
|
||||
private object[] m_eventParam;
|
||||
public object EventParam1
|
||||
{
|
||||
get
|
||||
{
|
||||
return m_eventParam != null && m_eventParam.Length > 0 ? m_eventParam[0] : null;
|
||||
}
|
||||
}
|
||||
|
||||
public object EventParam2
|
||||
{
|
||||
get
|
||||
{
|
||||
return m_eventParam != null && m_eventParam.Length > 1 ? m_eventParam[1] : null;
|
||||
}
|
||||
}
|
||||
|
||||
public object EventParam3
|
||||
{
|
||||
get { return m_eventParam != null && m_eventParam.Length > 2 ? m_eventParam[2] : null; }
|
||||
}
|
||||
|
||||
public object this[int index]
|
||||
{
|
||||
get { return m_eventParam != null && index < m_eventParam.Length ? m_eventParam[index] : null; }
|
||||
}
|
||||
|
||||
private Action<T> m_clickAction;
|
||||
private Action<T, bool, PointerEventData> m_pressAction;
|
||||
private Action<T, PointerEventData> m_beginDragAction;
|
||||
private Action<T, PointerEventData> m_dragAction;
|
||||
private Action<T, PointerEventData> m_endDragAction;
|
||||
|
||||
public void BindClickEvent(Action<T> clickAction, params object[] arg)
|
||||
{
|
||||
if (m_clickAction != null)
|
||||
{
|
||||
m_clickAction = clickAction;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_clickAction = clickAction;
|
||||
m_buttonClick = UnityUtil.AddMonoBehaviour<Button>(gameObject);
|
||||
m_buttonClick.transition = Selectable.Transition.None;
|
||||
m_buttonClick.onClick.AddListener(OnButtonClick);
|
||||
}
|
||||
|
||||
SetEventParam(arg);
|
||||
}
|
||||
|
||||
private void OnButtonClick()
|
||||
{
|
||||
if (m_clickAction != null)
|
||||
{
|
||||
//AudioSys.GameMgr.PlayUISoundEffect(UiSoundType.UI_SOUND_CLICK);
|
||||
m_clickAction(this as T);
|
||||
}
|
||||
}
|
||||
|
||||
public void BindBeginDragEvent(Action<T, PointerEventData> dragAction, params object[] arg)
|
||||
{
|
||||
if (m_beginDragAction != null)
|
||||
{
|
||||
m_beginDragAction = dragAction;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_beginDragAction = dragAction;
|
||||
var trigger = UnityUtil.AddMonoBehaviour<EventTrigger>(gameObject);
|
||||
EventTrigger.Entry entry = new EventTrigger.Entry();
|
||||
entry.eventID = EventTriggerType.BeginDrag;
|
||||
entry.callback = new EventTrigger.TriggerEvent();
|
||||
entry.callback.AddListener(OnTriggerBeginDrag);
|
||||
trigger.triggers.Add(entry);
|
||||
}
|
||||
SetEventParam(arg);
|
||||
}
|
||||
|
||||
private void OnTriggerBeginDrag(BaseEventData data)
|
||||
{
|
||||
var pointerEventData = (PointerEventData)data;
|
||||
if (m_beginDragAction != null)
|
||||
{
|
||||
m_beginDragAction(this as T, pointerEventData);
|
||||
}
|
||||
}
|
||||
|
||||
public void BindDragEvent(Action<T, PointerEventData> dragAction, params object[] arg)
|
||||
{
|
||||
if (m_dragAction != null)
|
||||
{
|
||||
m_dragAction = dragAction;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_dragAction = dragAction;
|
||||
var trigger = UnityUtil.AddMonoBehaviour<EventTrigger>(gameObject);
|
||||
EventTrigger.Entry entry = new EventTrigger.Entry();
|
||||
entry.eventID = EventTriggerType.Drag;
|
||||
entry.callback = new EventTrigger.TriggerEvent();
|
||||
entry.callback.AddListener(OnTriggerDrag);
|
||||
trigger.triggers.Add(entry);
|
||||
}
|
||||
SetEventParam(arg);
|
||||
}
|
||||
|
||||
private void OnTriggerDrag(BaseEventData data)
|
||||
{
|
||||
var pointerEventData = (PointerEventData)data;
|
||||
if (m_dragAction != null)
|
||||
{
|
||||
m_dragAction(this as T, pointerEventData);
|
||||
}
|
||||
}
|
||||
|
||||
public void BindEndDragEvent(Action<T, PointerEventData> dragendAction, params object[] arg)
|
||||
{
|
||||
if (m_endDragAction != null)
|
||||
{
|
||||
m_endDragAction = dragendAction;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_endDragAction = dragendAction;
|
||||
var trigger = UnityUtil.AddMonoBehaviour<EventTrigger>(gameObject);
|
||||
EventTrigger.Entry entry = new EventTrigger.Entry();
|
||||
entry.eventID = EventTriggerType.EndDrag;
|
||||
entry.callback = new EventTrigger.TriggerEvent();
|
||||
entry.callback.AddListener(OnTriggerEndDrag);
|
||||
trigger.triggers.Add(entry);
|
||||
}
|
||||
SetEventParam(arg);
|
||||
}
|
||||
|
||||
private void OnTriggerEndDrag(BaseEventData data)
|
||||
{
|
||||
if (m_endDragAction != null)
|
||||
{
|
||||
m_endDragAction(this as T, (PointerEventData)data);
|
||||
}
|
||||
}
|
||||
|
||||
public void BindPressEvent(Action<T, bool, PointerEventData> pressAction, params object[] arg)
|
||||
{
|
||||
if (m_pressAction != null)
|
||||
{
|
||||
m_pressAction = pressAction;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_pressAction = pressAction;
|
||||
var trigger = UnityUtil.AddMonoBehaviour<EventTrigger>(gameObject);
|
||||
EventTrigger.Entry entry = new EventTrigger.Entry();
|
||||
entry.eventID = EventTriggerType.PointerDown;
|
||||
entry.callback = new EventTrigger.TriggerEvent();
|
||||
entry.callback.AddListener(OnTriggerPointerDown);
|
||||
trigger.triggers.Add(entry);
|
||||
entry = new EventTrigger.Entry();
|
||||
entry.eventID = EventTriggerType.PointerUp;
|
||||
entry.callback = new EventTrigger.TriggerEvent();
|
||||
entry.callback.AddListener(OnTriggerPointerUp);
|
||||
trigger.triggers.Add(entry);
|
||||
}
|
||||
|
||||
SetEventParam(arg);
|
||||
}
|
||||
|
||||
private void OnTriggerPointerUp(BaseEventData data)
|
||||
{
|
||||
if (m_pressAction != null)
|
||||
{
|
||||
m_pressAction(this as T, false, data as PointerEventData);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnTriggerPointerDown(BaseEventData data)
|
||||
{
|
||||
if (m_pressAction != null)
|
||||
{
|
||||
m_pressAction(this as T, true, data as PointerEventData);
|
||||
}
|
||||
}
|
||||
|
||||
public void SetEventParam(params object[] arg)
|
||||
{
|
||||
m_eventParam = arg;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
178
TEngineHotUpdate/src/TEngineCore/UI/Extend/UIWindowWidget.cs
Normal file
178
TEngineHotUpdate/src/TEngineCore/UI/Extend/UIWindowWidget.cs
Normal file
@@ -0,0 +1,178 @@
|
||||
using TEngineCore;
|
||||
using UnityEngine;
|
||||
|
||||
namespace TEngineCore
|
||||
{
|
||||
/// <summary>
|
||||
/// 用来封装各个界面里子模块用
|
||||
/// </summary>
|
||||
public class UIWindowWidget : UIWindowBase
|
||||
{
|
||||
public int SortingOrder
|
||||
{
|
||||
get
|
||||
{
|
||||
if (m_canvas != null)
|
||||
{
|
||||
return m_canvas.sortingOrder;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
if (m_canvas != null)
|
||||
{
|
||||
int oldOrder = m_canvas.sortingOrder;
|
||||
if (oldOrder != value)
|
||||
{
|
||||
var listCanvas = gameObject.GetComponentsInChildren<Canvas>(true);
|
||||
for (int i = 0; i < listCanvas.Length; i++)
|
||||
{
|
||||
var childCanvas = listCanvas[i];
|
||||
childCanvas.sortingOrder = value + (childCanvas.sortingOrder - oldOrder);
|
||||
}
|
||||
m_canvas.sortingOrder = value;
|
||||
_OnSortingOrderChg();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// 所属的窗口
|
||||
/// </summary>
|
||||
public UIWindow OwnerWindow
|
||||
{
|
||||
get
|
||||
{
|
||||
var parent = m_parent;
|
||||
while (parent != null)
|
||||
{
|
||||
if (parent.BaseType == UIWindowBaseType.Window)
|
||||
{
|
||||
return parent as UIWindow;
|
||||
}
|
||||
|
||||
parent = parent.Parent;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public override UIWindowBaseType BaseType
|
||||
{
|
||||
get { return UIWindowBaseType.Widget; }
|
||||
}
|
||||
|
||||
/// <summary> 根据类型创建 </summary>
|
||||
public bool CreateByType<T>(UIWindowBase parent, Transform parentTrans = null) where T : UIWindowWidget
|
||||
{
|
||||
string resPath = string.Format("UI/{0}.prefab", typeof(T).Name);
|
||||
return CreateByPath(resPath, parent, parentTrans);
|
||||
}
|
||||
|
||||
/// <summary> 根据资源名创建 </summary>
|
||||
public bool CreateByPath(string resPath, UIWindowBase parent, Transform parentTrans = null, bool visible = true)
|
||||
{
|
||||
GameObject goInst = TResources.Load(resPath, parentTrans);
|
||||
if (goInst == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (!Create(parent, goInst, visible))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
goInst.transform.localScale = Vector3.one;
|
||||
goInst.transform.localPosition = Vector3.zero;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据prefab或者模版来创建新的 widget
|
||||
*/
|
||||
public bool CreateByPrefab(UIWindowBase parent, GameObject goPrefab, Transform parentTrans, bool visible = true)
|
||||
{
|
||||
if (parentTrans == null)
|
||||
{
|
||||
parentTrans = parent.transform;
|
||||
}
|
||||
|
||||
var widgetRoot = GameObject.Instantiate(goPrefab, parentTrans);
|
||||
return CreateImp(parent, widgetRoot, true, visible);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建窗口内嵌的界面
|
||||
*/
|
||||
public bool Create(UIWindowBase parent, GameObject widgetRoot, bool visible = true)
|
||||
{
|
||||
return CreateImp(parent, widgetRoot, false, visible);
|
||||
}
|
||||
|
||||
#region 私有的函数
|
||||
|
||||
private bool CreateImp(UIWindowBase parent, GameObject widgetRoot, bool bindGo, bool visible = true)
|
||||
{
|
||||
if (!CreateBase(widgetRoot, bindGo))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
RestChildCanvas(parent);
|
||||
m_parent = parent;
|
||||
if (m_parent != null)
|
||||
{
|
||||
m_parent.AddChild(this);
|
||||
}
|
||||
|
||||
if (m_canvas != null)
|
||||
{
|
||||
m_canvas.overrideSorting = true;
|
||||
}
|
||||
ScriptGenerator();
|
||||
BindMemberProperty();
|
||||
RegisterEvent();
|
||||
|
||||
OnCreate();
|
||||
|
||||
if (visible)
|
||||
{
|
||||
Show(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
widgetRoot.SetActive(false);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void RestChildCanvas(UIWindowBase parent)
|
||||
{
|
||||
if (gameObject == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (parent == null || parent.gameObject == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
Canvas parentCanvas = parent.gameObject.GetComponentInParent<Canvas>();
|
||||
if (parentCanvas == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
var listCanvas = gameObject.GetComponentsInChildren<Canvas>(true);
|
||||
for (var index = 0; index < listCanvas.Length; index++)
|
||||
{
|
||||
var childCanvas = listCanvas[index];
|
||||
childCanvas.sortingOrder = parentCanvas.sortingOrder + childCanvas.sortingOrder % UIWindow.MaxCanvasSortingOrder;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
}
|
7
TEngineHotUpdate/src/TEngineCore/UI/IUIController.cs
Normal file
7
TEngineHotUpdate/src/TEngineCore/UI/IUIController.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
namespace TEngineCore
|
||||
{
|
||||
interface IUIController
|
||||
{
|
||||
void ResigterUIEvent();
|
||||
}
|
||||
}
|
102
TEngineHotUpdate/src/TEngineCore/UI/UIBase.cs
Normal file
102
TEngineHotUpdate/src/TEngineCore/UI/UIBase.cs
Normal file
@@ -0,0 +1,102 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using TEngineCore;
|
||||
using UnityEngine;
|
||||
|
||||
namespace TEngineCore
|
||||
{
|
||||
public class UIBase
|
||||
{
|
||||
protected GameObject m_go;
|
||||
protected RectTransform m_transform;
|
||||
protected string m_name;
|
||||
protected bool m_destroyed = true;
|
||||
|
||||
private GameEventMgr m_eventMgr;
|
||||
|
||||
protected GameEventMgr EventMgr
|
||||
{
|
||||
get
|
||||
{
|
||||
if (m_eventMgr == null)
|
||||
{
|
||||
m_eventMgr = GameEventMgr.Instance;
|
||||
}
|
||||
|
||||
return m_eventMgr;
|
||||
}
|
||||
}
|
||||
public bool IsDestroyed
|
||||
{
|
||||
get { return m_destroyed; }
|
||||
}
|
||||
|
||||
public bool IsCreated
|
||||
{
|
||||
get { return !IsDestroyed; }
|
||||
}
|
||||
|
||||
public RectTransform transform
|
||||
{
|
||||
get { return m_transform; }
|
||||
}
|
||||
|
||||
public GameObject gameObject
|
||||
{
|
||||
get { return m_go; }
|
||||
}
|
||||
|
||||
public string name
|
||||
{
|
||||
get
|
||||
{
|
||||
if (string.IsNullOrEmpty(m_name))
|
||||
{
|
||||
m_name = GetType().Name;
|
||||
}
|
||||
|
||||
return m_name;
|
||||
}
|
||||
}
|
||||
|
||||
#region Event
|
||||
private Dictionary<int, Delegate> m_eventTable = new Dictionary<int, Delegate>();
|
||||
|
||||
protected void ClearAllRegisterEvent()
|
||||
{
|
||||
var element = m_eventTable.GetEnumerator();
|
||||
while (element.MoveNext())
|
||||
{
|
||||
var m_event = element.Current.Value;
|
||||
//GameEventMgr.Instance.RemoveEventListener(element.Current.Key, m_event);
|
||||
}
|
||||
m_eventTable.Clear();
|
||||
}
|
||||
|
||||
protected void AddUIEvent(int eventType, Action handler)
|
||||
{
|
||||
m_eventTable.Add(eventType, handler);
|
||||
EventMgr.AddEventListener(eventType, handler);
|
||||
}
|
||||
|
||||
protected void AddUIEvent<T>(int eventType, Action<T> handler)
|
||||
{
|
||||
m_eventTable.Add(eventType, handler);
|
||||
EventMgr.AddEventListener(eventType, handler);
|
||||
}
|
||||
|
||||
protected void AddUIEvent<T, U>(int eventType, Action<T, U> handler)
|
||||
{
|
||||
m_eventTable.Add(eventType, handler);
|
||||
EventMgr.AddEventListener(eventType, handler);
|
||||
}
|
||||
|
||||
protected void AddUIEvent<T, U, V>(int eventType, Action<T, U, V> handler)
|
||||
{
|
||||
m_eventTable.Add(eventType, handler);
|
||||
EventMgr.AddEventListener(eventType, handler);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
}
|
546
TEngineHotUpdate/src/TEngineCore/UI/UIManager.cs
Normal file
546
TEngineHotUpdate/src/TEngineCore/UI/UIManager.cs
Normal file
@@ -0,0 +1,546 @@
|
||||
using System.Collections.Generic;
|
||||
using TEngineCore;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Events;
|
||||
using UnityEngine.EventSystems;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace TEngineCore
|
||||
{
|
||||
public enum WindowStackIndex
|
||||
{
|
||||
StackNormal = 0,
|
||||
StackTop = 1,
|
||||
StackMax
|
||||
};
|
||||
|
||||
public class UIWindowStack
|
||||
{
|
||||
public WindowStackIndex m_stackIndex;
|
||||
public int m_baseOrder = 0;
|
||||
public List<uint> m_windowList = new List<uint>();
|
||||
public Transform m_parentTrans;
|
||||
|
||||
public int FindIndex(uint windowId)
|
||||
{
|
||||
for (int i = 0; i < m_windowList.Count; i++)
|
||||
{
|
||||
if (m_windowList[i] == windowId)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
public sealed partial class UIManager : TSingleton<UIManager>
|
||||
{
|
||||
private Transform m_canvasTrans;
|
||||
public Canvas m_canvas;
|
||||
private Dictionary<uint, UIWindow> m_allWindow = new Dictionary<uint, UIWindow>();
|
||||
|
||||
/// <summary>
|
||||
/// 不同显示顺序的的窗口列表
|
||||
/// </summary>
|
||||
private UIWindowStack[] m_listWindowStack = new UIWindowStack[(int)WindowStackIndex.StackMax];
|
||||
|
||||
/// <summary>
|
||||
/// 类型到实例的索引
|
||||
/// </summary>
|
||||
private static Dictionary<string, UIWindow> m_typeToInst = new Dictionary<string, UIWindow>();
|
||||
|
||||
/// <summary>
|
||||
/// 类型和资源的绑定关系
|
||||
/// </summary>
|
||||
private Dictionary<string, string> m_uiType2PrefabPath = new Dictionary<string, string>();
|
||||
|
||||
private GameObject m_uiManagerGo;
|
||||
private Transform m_uiManagerTransform;
|
||||
private Camera m_uiCamera;
|
||||
public Camera Camera => m_uiCamera;
|
||||
|
||||
public UIManager()
|
||||
{
|
||||
m_uiManagerGo = TResources.Load("UI/UIRoot.prefab");
|
||||
Object.DontDestroyOnLoad(m_uiManagerGo);
|
||||
m_uiManagerTransform = m_uiManagerGo.transform;
|
||||
m_uiCamera = UnityUtil.FindChildComponent<Camera>(m_uiManagerTransform, "Camera");
|
||||
Canvas canvas = m_uiManagerGo.GetComponentInChildren<Canvas>();
|
||||
if (canvas != null)
|
||||
{
|
||||
m_canvas = canvas;
|
||||
m_canvasTrans = canvas.transform;
|
||||
}
|
||||
|
||||
var windowRoot = m_canvasTrans;
|
||||
|
||||
int baseOrder = 1000;
|
||||
for (int i = 0; i < (int)WindowStackIndex.StackMax; i++)
|
||||
{
|
||||
m_listWindowStack[i] = new UIWindowStack();
|
||||
m_listWindowStack[i].m_stackIndex = (WindowStackIndex)i;
|
||||
m_listWindowStack[i].m_baseOrder = baseOrder;
|
||||
m_listWindowStack[i].m_parentTrans = windowRoot;
|
||||
baseOrder += 1000;
|
||||
}
|
||||
CalcCameraRect();
|
||||
}
|
||||
|
||||
void CalcCameraRect()
|
||||
{
|
||||
CanvasScaler canvasScale = m_canvas.GetComponent<CanvasScaler>();
|
||||
if (canvasScale != null)
|
||||
{
|
||||
canvasScale.referenceResolution = new Vector2(UISys.DesginWidth, UISys.DesginHeight);
|
||||
float sceneScale = Screen.width / (float)Screen.height;
|
||||
float designScale = canvasScale.referenceResolution.x / canvasScale.referenceResolution.y;
|
||||
canvasScale.matchWidthOrHeight = sceneScale > designScale ? 1 : 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void Update()
|
||||
{
|
||||
var allList = GetAllWindowList();
|
||||
for (int i = 0; i < allList.Count; i++)
|
||||
{
|
||||
UIWindow window = allList[i];
|
||||
if (!window.IsDestroyed)
|
||||
{
|
||||
window.Update();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private List<UIWindow> m_tmpWindowList = new List<UIWindow>();
|
||||
private bool m_tmpWindowListDirty = true;
|
||||
private List<UIWindow> GetAllWindowList()
|
||||
{
|
||||
if (m_tmpWindowListDirty)
|
||||
{
|
||||
m_tmpWindowList.Clear();
|
||||
var itr = m_allWindow.GetEnumerator();
|
||||
while (itr.MoveNext())
|
||||
{
|
||||
var kv = itr.Current;
|
||||
m_tmpWindowList.Add(kv.Value);
|
||||
}
|
||||
m_tmpWindowListDirty = false;
|
||||
}
|
||||
return m_tmpWindowList;
|
||||
}
|
||||
|
||||
#region Methods
|
||||
public T ShowWindow<T>(bool isAsync = false) where T : UIWindow, new()
|
||||
{
|
||||
string typeName = GetWindowTypeName<T>();
|
||||
|
||||
T window = GetUIWindowByType(typeName) as T;
|
||||
if (window == null)
|
||||
{
|
||||
window = new T();
|
||||
if (!CreateWindowByType(window, typeName, isAsync))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
ShowWindow(window, -1);
|
||||
return window;
|
||||
}
|
||||
|
||||
public string GetWindowTypeName<T>()
|
||||
{
|
||||
string typeName = typeof(T).Name;
|
||||
return typeName;
|
||||
}
|
||||
|
||||
public string GetWindowTypeName(UIWindow window)
|
||||
{
|
||||
string typeName = window.GetType().Name;
|
||||
return typeName;
|
||||
}
|
||||
public void CloseWindow<T>() where T : UIWindow
|
||||
{
|
||||
string typeName = GetWindowTypeName<T>();
|
||||
CloseWindow(typeName);
|
||||
}
|
||||
public T GetWindow<T>() where T : UIWindow
|
||||
{
|
||||
string typeName = GetWindowTypeName<T>();
|
||||
UIWindow window = GetUIWindowByType(typeName);
|
||||
if (window != null)
|
||||
{
|
||||
return window as T;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public UIWindow GetUIWindowByType(string typeName)
|
||||
{
|
||||
UIWindow window;
|
||||
if (m_typeToInst.TryGetValue(typeName, out window))
|
||||
{
|
||||
return window;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
#endregion
|
||||
|
||||
private bool CreateWindowByType(UIWindow window, string typeName,bool async = false)
|
||||
{
|
||||
//先判断是否有缓存
|
||||
GameObject uiGo = null;
|
||||
|
||||
string resPath = string.Format("{0}.prefab", GetUIResourcePath(typeName));
|
||||
if (string.IsNullOrEmpty(resPath))
|
||||
{
|
||||
Debug.LogErrorFormat("CreateWindowByType failed, typeName:{0}, cant find respath", typeName);
|
||||
return false;
|
||||
}
|
||||
|
||||
UIWindowStack windowStack = GetUIWindowStack(window);
|
||||
|
||||
if (async)
|
||||
{
|
||||
TResources.LoadAsync(resPath, (obj) =>
|
||||
{
|
||||
if (obj == null)
|
||||
{
|
||||
Debug.LogErrorFormat("CreateWindowByType failed, typeName:{0}, load prefab failed: {1}", typeName, resPath);
|
||||
}
|
||||
|
||||
if (obj != null && windowStack.m_parentTrans != null)
|
||||
{
|
||||
obj.transform.SetParent(windowStack.m_parentTrans);
|
||||
}
|
||||
|
||||
obj.name = typeName;
|
||||
|
||||
window.AllocWindowId();
|
||||
|
||||
var rectTrans_ = obj.transform as RectTransform;
|
||||
if (window.NeedCenterUI())
|
||||
{
|
||||
rectTrans_.SetMax(); //localPosition = new Vector3(0, 0, 0);
|
||||
}
|
||||
|
||||
rectTrans_.localRotation = Quaternion.identity;
|
||||
rectTrans_.localScale = Vector3.one;
|
||||
|
||||
if (!window.Create(this, obj))
|
||||
{
|
||||
Debug.LogErrorFormat("window create failed, typeName:{0}", typeName);
|
||||
if (obj != null)
|
||||
{
|
||||
Object.Destroy(obj);
|
||||
obj = null;
|
||||
}
|
||||
}
|
||||
|
||||
m_typeToInst[typeName] = window;
|
||||
m_allWindow[window.WindowId] = window;
|
||||
m_tmpWindowListDirty = true;
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
uiGo = TResources.Load(resPath, windowStack.m_parentTrans);
|
||||
if (uiGo == null)
|
||||
{
|
||||
Debug.LogErrorFormat("CreateWindowByType failed, typeName:{0}, load prefab failed: {1}", typeName, resPath);
|
||||
//UISys.Mgr.ShowTipMsg(TextDefine.DOWNLOAD_TIP_UI);
|
||||
//GameEvent.Get<IHomePageUI>().ShowDownloadUI();
|
||||
return false;
|
||||
}
|
||||
|
||||
uiGo.name = typeName;
|
||||
|
||||
window.AllocWindowId();
|
||||
|
||||
RectTransform rectTrans = uiGo.transform as RectTransform;
|
||||
if (window.NeedCenterUI())
|
||||
{
|
||||
rectTrans.SetMax(); //localPosition = new Vector3(0, 0, 0);
|
||||
}
|
||||
|
||||
rectTrans.localRotation = Quaternion.identity;
|
||||
rectTrans.localScale = Vector3.one;
|
||||
|
||||
if (!window.Create(this, uiGo))
|
||||
{
|
||||
Debug.LogErrorFormat("window create failed, typeName:{0}", typeName);
|
||||
if (uiGo != null)
|
||||
{
|
||||
Object.Destroy(uiGo);
|
||||
uiGo = null;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
m_typeToInst[typeName] = window;
|
||||
m_allWindow[window.WindowId] = window;
|
||||
m_tmpWindowListDirty = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
#region MyRegion
|
||||
private string GetUIResourcePath(string typeName)
|
||||
{
|
||||
string resPath;
|
||||
if (m_uiType2PrefabPath.TryGetValue(typeName, out resPath))
|
||||
{
|
||||
return resPath;
|
||||
}
|
||||
|
||||
string path = string.Format("UI/{0}", typeName);
|
||||
m_uiType2PrefabPath.Add(typeName, path);
|
||||
return path;
|
||||
}
|
||||
|
||||
private void ShowWindow(UIWindow window, int showIndex)
|
||||
{
|
||||
UIWindowStack windowStack = GetUIWindowStack(window);
|
||||
List<uint> windowList = windowStack.m_windowList;
|
||||
int resortIndex = -1;
|
||||
int findIndex = windowList.IndexOf(window.WindowId);
|
||||
if (findIndex >= 0)
|
||||
{
|
||||
windowList.RemoveAt(findIndex);
|
||||
resortIndex = findIndex;
|
||||
}
|
||||
|
||||
windowList.Add(window.WindowId);
|
||||
|
||||
ResortStackUI(windowStack, resortIndex);
|
||||
ShowTopUI(windowStack);
|
||||
}
|
||||
|
||||
private void ResortStackUI(UIWindowStack stack, int startIdx)
|
||||
{
|
||||
if (stack.m_windowList.Count > 0)
|
||||
{
|
||||
startIdx = startIdx < 0 ? (stack.m_windowList.Count - 1) : startIdx;
|
||||
for (int i = startIdx; i < stack.m_windowList.Count; i++)
|
||||
{
|
||||
uint windowId = stack.m_windowList[i];
|
||||
UIWindow window = FindWindow(windowId);
|
||||
if (window != null)
|
||||
{
|
||||
int order;
|
||||
if (window.IsFixedSortingOrder)
|
||||
{
|
||||
order = stack.m_baseOrder + window.FixedAdditionalOrder;
|
||||
}
|
||||
else
|
||||
{
|
||||
order = stack.m_baseOrder + i * UIWindow.MaxCanvasSortingOrder;
|
||||
}
|
||||
|
||||
window.SortingOrder = order;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ShowTopUI(UIWindowStack stack)
|
||||
{
|
||||
if (stack.m_windowList.Count > 0)
|
||||
{
|
||||
bool hasTop = false;
|
||||
for (int i = stack.m_windowList.Count - 1; i >= 0; i--)
|
||||
{
|
||||
uint windowId = stack.m_windowList[i];
|
||||
UIWindow window = FindWindow(windowId);
|
||||
if (window != null)
|
||||
{
|
||||
if (!hasTop)
|
||||
{
|
||||
hasTop = window.IsFullScreen;
|
||||
|
||||
window.Show(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
window.Show(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
OnWindowVisibleChanged();
|
||||
}
|
||||
|
||||
private void OnWindowVisibleChanged()
|
||||
{
|
||||
bool isFullScreenMaskscene = false;
|
||||
for (int i = 0; i < m_listWindowStack.Length && !isFullScreenMaskscene; i++)
|
||||
{
|
||||
var stack = m_listWindowStack[i];
|
||||
if (stack == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var listWindow = stack.m_windowList;
|
||||
for (int k = 0; k < listWindow.Count; k++)
|
||||
{
|
||||
var winId = listWindow[k];
|
||||
var win = FindWindow(winId);
|
||||
if (win == null || !win.Visible)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (win.IsFullScreenMaskScene)
|
||||
{
|
||||
isFullScreenMaskscene = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
//SceneSys.Instance.CameraMgr.SetSceneCameraEnableByUI(true);
|
||||
}
|
||||
|
||||
public UIWindow FindWindow(uint windowId)
|
||||
{
|
||||
UIWindow window;
|
||||
if (m_allWindow.TryGetValue(windowId, out window))
|
||||
{
|
||||
return window;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public void CloseWindow(string typeName)
|
||||
{
|
||||
UIWindow window = GetUIWindowByType(typeName);
|
||||
if (window != null)
|
||||
{
|
||||
CloseWindow(window);
|
||||
}
|
||||
}
|
||||
|
||||
public void CloseWindow(UIWindow window)
|
||||
{
|
||||
if (window.IsDestroyed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
//刷新窗口order,保证新创建的窗口不会出现重叠
|
||||
UIWindowStack windowStack = GetUIWindowStack(window);
|
||||
|
||||
int findIndex = windowStack.FindIndex(window.WindowId);
|
||||
|
||||
//window.Destroy();
|
||||
DestroyWindowObject(window);
|
||||
|
||||
ResortStackUI(windowStack, findIndex);
|
||||
ShowTopUI(windowStack);
|
||||
}
|
||||
|
||||
private void DestroyWindowObject(UIWindow window)
|
||||
{
|
||||
string typeName = window.GetType().Name;
|
||||
UIWindow typeWindow = null;
|
||||
if (m_typeToInst.TryGetValue(typeName, out typeWindow) && typeWindow == window)
|
||||
{
|
||||
m_typeToInst.Remove(typeName);
|
||||
}
|
||||
uint windowId = window.WindowId;
|
||||
m_allWindow.Remove(windowId);
|
||||
UIWindowStack windowStack = GetUIWindowStack(window);
|
||||
windowStack.m_windowList.Remove(windowId);
|
||||
window.Destroy();
|
||||
m_tmpWindowListDirty = true;
|
||||
}
|
||||
|
||||
|
||||
private int GetIndexByWindowType(UIWindowType windowType)
|
||||
{
|
||||
if (windowType == UIWindowType.WindowTop)
|
||||
{
|
||||
return (int)WindowStackIndex.StackTop;
|
||||
}
|
||||
|
||||
return (int)WindowStackIndex.StackNormal;
|
||||
}
|
||||
|
||||
public UIWindowStack GetUIWindowStack(UIWindow window)
|
||||
{
|
||||
int index = GetIndexByWindowType(window.GetWindowType());
|
||||
return m_listWindowStack[index];
|
||||
}
|
||||
|
||||
public UIWindow GetWindowById(uint windowId)
|
||||
{
|
||||
return FindWindow(windowId);
|
||||
}
|
||||
|
||||
public UIWindowStack GetUIWindowStack(UIWindowType windowType)
|
||||
{
|
||||
int index = GetIndexByWindowType(windowType);
|
||||
return m_listWindowStack[index];
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 拓展
|
||||
/// <summary>
|
||||
/// 给控件添加自定义事件监听
|
||||
/// </summary>
|
||||
/// <param name="control">控件对象</param>
|
||||
/// <param name="type">事件类型</param>
|
||||
/// <param name="callback">事件的响应函数</param>
|
||||
public static void AddCustomEventListener(UIBehaviour control, EventTriggerType type, UnityAction<BaseEventData> callback)
|
||||
{
|
||||
EventTrigger trigger = control.GetComponent<EventTrigger>();
|
||||
|
||||
if (trigger == null)
|
||||
{
|
||||
trigger = control.gameObject.AddComponent<EventTrigger>();
|
||||
}
|
||||
|
||||
EventTrigger.Entry entry = new EventTrigger.Entry();
|
||||
entry.eventID = type;
|
||||
entry.callback.AddListener(callback);
|
||||
|
||||
trigger.triggers.Add(entry);
|
||||
}
|
||||
|
||||
public bool GetMouseDownUiPos(out Vector3 screenPos)
|
||||
{
|
||||
bool hadMouseDown = false;
|
||||
Vector3 mousePos = Vector3.zero;
|
||||
|
||||
#if UNITY_STANDALONE_WIN || UNITY_EDITOR
|
||||
mousePos = Input.mousePosition;
|
||||
hadMouseDown = Input.GetMouseButton(0);
|
||||
#else
|
||||
if (Input.touchCount > 0)
|
||||
{
|
||||
mousePos = Input.GetTouch(0).position;
|
||||
hadMouseDown = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
hadMouseDown = false;
|
||||
}
|
||||
#endif
|
||||
Vector2 pos;
|
||||
RectTransformUtility.ScreenPointToLocalPointInRectangle(m_canvasTrans as RectTransform, Input.mousePosition,
|
||||
m_uiCamera, out pos);
|
||||
screenPos = m_canvasTrans.TransformPoint(pos);
|
||||
|
||||
return hadMouseDown;
|
||||
}
|
||||
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
166
TEngineHotUpdate/src/TEngineCore/UI/UISys.cs
Normal file
166
TEngineHotUpdate/src/TEngineCore/UI/UISys.cs
Normal file
@@ -0,0 +1,166 @@
|
||||
using System.Collections.Generic;
|
||||
using TEngineCore;
|
||||
using UnityEngine;
|
||||
|
||||
namespace TEngineCore
|
||||
{
|
||||
public class UISys : BaseLogicSys<UISys>
|
||||
{
|
||||
public static int DesginWidth
|
||||
{
|
||||
get
|
||||
{
|
||||
return 750;
|
||||
}
|
||||
}
|
||||
|
||||
public static int DesginHeight
|
||||
{
|
||||
get
|
||||
{
|
||||
return 1624;
|
||||
}
|
||||
}
|
||||
|
||||
public static int ScreenWidth;
|
||||
public static int ScreenHeight;
|
||||
public bool IsLandScape { private set; get; }
|
||||
private List<IUIController> m_listController = new List<IUIController>();
|
||||
|
||||
public static UIManager Mgr
|
||||
{
|
||||
get { return UIManager.Instance; }
|
||||
}
|
||||
|
||||
public override void OnUpdate()
|
||||
{
|
||||
UIManager.Instance.Update();
|
||||
}
|
||||
|
||||
public override bool OnInit()
|
||||
{
|
||||
base.OnInit();
|
||||
|
||||
ScreenWidth = Screen.width;
|
||||
|
||||
ScreenHeight = Screen.height;
|
||||
|
||||
IsLandScape = ScreenWidth > ScreenHeight;
|
||||
|
||||
RegistAllController();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void RegistAllController()
|
||||
{
|
||||
//AddController<LoadingUIController>();
|
||||
}
|
||||
|
||||
private void AddController<T>() where T : IUIController, new()
|
||||
{
|
||||
for (int i = 0; i < m_listController.Count; i++)
|
||||
{
|
||||
var type = m_listController[i].GetType();
|
||||
|
||||
if (type == typeof(T))
|
||||
{
|
||||
Debug.LogError(string.Format("repeat controller type: {0}", typeof(T).Name));
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
var controller = new T();
|
||||
|
||||
m_listController.Add(controller);
|
||||
|
||||
controller.ResigterUIEvent();
|
||||
}
|
||||
|
||||
public static void ShowTipMsg(string str)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public static class CanvasUtils
|
||||
{
|
||||
public static void SetMax(this RectTransform rectTransform)
|
||||
{
|
||||
if (rectTransform == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
rectTransform.localPosition = new Vector3(0, 0, 0);
|
||||
rectTransform.localRotation = Quaternion.identity;
|
||||
rectTransform.localScale = Vector3.one;
|
||||
|
||||
rectTransform.SetInsetAndSizeFromParentEdge(RectTransform.Edge.Left, 0, 0);
|
||||
rectTransform.SetInsetAndSizeFromParentEdge(RectTransform.Edge.Top, 0, 0);
|
||||
rectTransform.anchorMin = Vector2.zero;
|
||||
rectTransform.anchorMax = Vector2.one;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 调整 RectTransform 组件中的 Left、Bottom 属性
|
||||
/// </summary>
|
||||
/// <param name="rt">引用目标 RectTransform 对象</param>
|
||||
/// <param name="left">Left值</param>
|
||||
/// <param name="bottom">Bottom值</param>
|
||||
public static void LeftBottom(RectTransform rectTransform, float left, float bottom)
|
||||
{
|
||||
Vector2 size = rectTransform.rect.size;
|
||||
rectTransform.SetInsetAndSizeFromParentEdge(RectTransform.Edge.Left, left, size.x);
|
||||
rectTransform.SetInsetAndSizeFromParentEdge(RectTransform.Edge.Bottom, bottom, size.y);
|
||||
}
|
||||
/// <summary>
|
||||
/// 调整 RectTransform 组件中的 Left、Top 属性
|
||||
/// </summary>
|
||||
/// <param name="rt"></param>
|
||||
/// <param name="left">Left值</param>
|
||||
/// <param name="top">Top值</param>
|
||||
public static void LeftTop(RectTransform rectTransform, float left, float top)
|
||||
{
|
||||
Vector2 size = rectTransform.rect.size;
|
||||
rectTransform.SetInsetAndSizeFromParentEdge(RectTransform.Edge.Left, left, size.x);
|
||||
rectTransform.SetInsetAndSizeFromParentEdge(RectTransform.Edge.Top, top, size.y);
|
||||
}
|
||||
/// <summary>
|
||||
/// 调整 RectTransform 组件中的 Right、Bottom 属性
|
||||
/// </summary>
|
||||
/// <param name="rt"></param>
|
||||
/// <param name="right">Right值</param>
|
||||
/// <param name="bottom">Bottom值</param>
|
||||
public static void RightBottom(RectTransform rectTransform, float right, float bottom)
|
||||
{
|
||||
Vector2 size = rectTransform.rect.size;
|
||||
rectTransform.SetInsetAndSizeFromParentEdge(RectTransform.Edge.Right, right, size.x);
|
||||
rectTransform.SetInsetAndSizeFromParentEdge(RectTransform.Edge.Bottom, bottom, size.y);
|
||||
}
|
||||
/// <summary>
|
||||
/// 调整 RectTransform 组件中的 Right、Top 属性
|
||||
/// </summary>
|
||||
/// <param name="rt"></param>
|
||||
/// <param name="right">Right值</param>
|
||||
/// <param name="top">Top值</param>
|
||||
public static void RightTop(RectTransform rectTransform, float right, float top)
|
||||
{
|
||||
Vector2 size = rectTransform.rect.size;
|
||||
rectTransform.SetInsetAndSizeFromParentEdge(RectTransform.Edge.Right, right, size.x);
|
||||
rectTransform.SetInsetAndSizeFromParentEdge(RectTransform.Edge.Top, top, size.y);
|
||||
}
|
||||
public static void SetCenter(this RectTransform rectTransform, float x = 0, float y = 0)
|
||||
{
|
||||
rectTransform.localPosition = new Vector3(0, 0, 0);
|
||||
rectTransform.localRotation = Quaternion.identity;
|
||||
rectTransform.localScale = Vector3.one;
|
||||
|
||||
Vector2 size = rectTransform.rect.size;
|
||||
rectTransform.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, size.x);
|
||||
rectTransform.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, size.y);
|
||||
rectTransform.localPosition = new Vector2(x, y);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
280
TEngineHotUpdate/src/TEngineCore/UI/UIWindow.cs
Normal file
280
TEngineHotUpdate/src/TEngineCore/UI/UIWindow.cs
Normal file
@@ -0,0 +1,280 @@
|
||||
using TEngineCore;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace TEngineCore
|
||||
{
|
||||
public enum UIWindowType
|
||||
{
|
||||
/// <summary> 普通窗口 </summary>
|
||||
WindowNormal,
|
||||
|
||||
/// <summary> 置顶窗口 </summary>
|
||||
WindowTop,
|
||||
|
||||
/// <summary> 模态窗口 </summary>
|
||||
WindowModel
|
||||
}
|
||||
|
||||
|
||||
public class UIWindow : UIWindowBase
|
||||
{
|
||||
#region 属性
|
||||
private bool m_isClosed = false;
|
||||
private bool m_isCreating = false;
|
||||
private Image m_modalImage;
|
||||
private float m_modalAlpha = 0.86f;
|
||||
|
||||
/// <summary>
|
||||
/// 是否固定SortingOrder
|
||||
/// </summary>
|
||||
public virtual bool IsFixedSortingOrder
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
public virtual bool NeedCenterUI()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 窗口Id
|
||||
/// </summary>
|
||||
private uint m_windowId = 0;
|
||||
|
||||
/// <summary>
|
||||
/// 窗口Id
|
||||
/// </summary>
|
||||
public uint WindowId
|
||||
{
|
||||
get { return m_windowId; }
|
||||
}
|
||||
|
||||
private static uint m_nextWindowId = 0;
|
||||
|
||||
public virtual bool IsFullScreen
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
public virtual bool IsFullScreenMaskScene
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 一个界面中最大sortOrder值
|
||||
/// </summary>
|
||||
public const int MaxCanvasSortingOrder = 50;
|
||||
|
||||
/// <summary>
|
||||
/// SortingOrder = stack.m_baseOrder + FixedAdditionalOrder
|
||||
/// </summary>
|
||||
public virtual int FixedAdditionalOrder
|
||||
{
|
||||
get { return UIWindow.MaxCanvasSortingOrder; }
|
||||
}
|
||||
|
||||
public int SortingOrder
|
||||
{
|
||||
get
|
||||
{
|
||||
if (m_canvas != null)
|
||||
{
|
||||
return m_canvas.sortingOrder;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
if (m_canvas != null)
|
||||
{
|
||||
int oldOrder = m_canvas.sortingOrder;
|
||||
if (oldOrder != value)
|
||||
{
|
||||
var listCanvas = gameObject.GetComponentsInChildren<Canvas>(true);
|
||||
for (int i = 0; i < listCanvas.Length; i++)
|
||||
{
|
||||
var childCanvas = listCanvas[i];
|
||||
childCanvas.sortingOrder = value + (childCanvas.sortingOrder - oldOrder);
|
||||
}
|
||||
m_canvas.sortingOrder = value;
|
||||
_OnSortingOrderChg();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
public void AllocWindowId()
|
||||
{
|
||||
if (m_nextWindowId == 0)
|
||||
{
|
||||
m_nextWindowId++;
|
||||
}
|
||||
|
||||
m_windowId = m_nextWindowId++;
|
||||
}
|
||||
|
||||
#region virtual function
|
||||
|
||||
public virtual UIWindowType GetWindowType()
|
||||
{
|
||||
return UIWindowType.WindowNormal;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Call From UIManager
|
||||
public bool Create(UIManager uiMgr, GameObject uiGo)
|
||||
{
|
||||
if (IsCreated)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
m_isClosed = false;
|
||||
if (!CreateBase(uiGo, true))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_canvas == null)
|
||||
{
|
||||
Debug.LogErrorFormat("{0} have not a canvas!!", this.ToString());
|
||||
Destroy();
|
||||
return false;
|
||||
}
|
||||
|
||||
m_ownUIManager = uiMgr;
|
||||
m_firstVisible = false;
|
||||
|
||||
if (m_canvas != null)
|
||||
{
|
||||
m_canvas.overrideSorting = true;
|
||||
}
|
||||
|
||||
ScriptGenerator();
|
||||
BindMemberProperty();
|
||||
RegisterEvent();
|
||||
|
||||
m_isCreating = true;
|
||||
OnCreate();
|
||||
m_isCreating = false;
|
||||
|
||||
if (m_isClosed)
|
||||
{
|
||||
Destroy();
|
||||
return false;
|
||||
}
|
||||
|
||||
SetModalState(GetModalType());
|
||||
return true;
|
||||
}
|
||||
|
||||
public virtual ModalType GetModalType()
|
||||
{
|
||||
if (IsFullScreen || GetWindowType() == UIWindowType.WindowTop)
|
||||
{
|
||||
return ModalType.TransparentType;
|
||||
}
|
||||
return ModalType.NormalType;
|
||||
}
|
||||
|
||||
private void SetModalState(ModalType type)
|
||||
{
|
||||
var canClose = false;
|
||||
switch (type)
|
||||
{
|
||||
case ModalType.NormalType:
|
||||
{
|
||||
m_modalAlpha = 0f;
|
||||
break;
|
||||
}
|
||||
case ModalType.NormalHaveClose:
|
||||
{
|
||||
canClose = true;
|
||||
break;
|
||||
}
|
||||
case ModalType.TransparentType:
|
||||
{
|
||||
m_modalAlpha = 0.01f;
|
||||
break;
|
||||
}
|
||||
case ModalType.TransparentHaveClose:
|
||||
{
|
||||
m_modalAlpha = 0.01f;
|
||||
canClose = true;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
m_modalAlpha = 0f;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (m_modalAlpha > 0)
|
||||
{
|
||||
string path = "UI/ModalSprite.prefab";
|
||||
GameObject modal = TResources.Load(path);
|
||||
modal.transform.SetParent(transform);
|
||||
modal.transform.SetAsFirstSibling();
|
||||
modal.transform.localScale = Vector3.one;
|
||||
modal.transform.localPosition = Vector3.zero;
|
||||
if (canClose)
|
||||
{
|
||||
var button = UnityUtil.AddMonoBehaviour<Button>(modal);
|
||||
button.onClick.AddListener(Close);
|
||||
}
|
||||
m_modalImage = UnityUtil.AddMonoBehaviour<Image>(modal);
|
||||
m_modalImage.color = new Color(0, 0, 0, m_modalAlpha);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void Close()
|
||||
{
|
||||
if (m_isCreating)
|
||||
{
|
||||
m_isClosed = true;
|
||||
return;
|
||||
}
|
||||
|
||||
var mgr = UIMgr;
|
||||
if (mgr != null)
|
||||
{
|
||||
mgr.CloseWindow(this);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
public enum ModalType
|
||||
{
|
||||
/// <summary> 普通模态 </summary>
|
||||
NormalType,
|
||||
|
||||
/// <summary> 透明模态 </summary>
|
||||
TransparentType,
|
||||
|
||||
/// <summary> 普通状态且有关闭功能 </summary>
|
||||
NormalHaveClose,
|
||||
|
||||
/// <summary> 透明状态且有关闭功能 </summary>
|
||||
TransparentHaveClose,
|
||||
|
||||
/// <summary> 非模态 </summary>
|
||||
NoneType,
|
||||
}
|
||||
|
||||
public enum UIWindowBaseType
|
||||
{
|
||||
None,
|
||||
Window,
|
||||
Widget,
|
||||
}
|
||||
|
||||
}
|
445
TEngineHotUpdate/src/TEngineCore/UI/UIWindowBase.cs
Normal file
445
TEngineHotUpdate/src/TEngineCore/UI/UIWindowBase.cs
Normal file
@@ -0,0 +1,445 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using TEngineCore;
|
||||
using UnityEngine;
|
||||
|
||||
namespace TEngineCore
|
||||
{
|
||||
public class UIWindowBase : UIBase
|
||||
{
|
||||
/// <summary>
|
||||
/// 所属的window
|
||||
/// </summary>
|
||||
protected UIWindowBase m_parent = null;
|
||||
protected Canvas m_canvas;
|
||||
|
||||
private List<UIWindowBase> m_listChild = null;
|
||||
private List<UIWindowBase> m_listUpdateChild = null;
|
||||
private bool m_updateListValid = false;
|
||||
|
||||
/// <summary>
|
||||
/// 是否首次显示过了
|
||||
/// </summary>
|
||||
protected bool m_firstVisible = false;
|
||||
/// <summary>
|
||||
/// 当前是否显示出来了
|
||||
/// </summary>
|
||||
private bool m_visible = false;
|
||||
public bool Visible
|
||||
{
|
||||
get { return m_visible; }
|
||||
}
|
||||
|
||||
public UIWindowBase Parent
|
||||
{
|
||||
get { return m_parent; }
|
||||
}
|
||||
|
||||
protected UIManager m_ownUIManager = null;
|
||||
|
||||
protected UIManager UIMgr
|
||||
{
|
||||
get
|
||||
{
|
||||
if (m_ownUIManager == null && m_parent != null)
|
||||
{
|
||||
return m_parent.UIMgr;
|
||||
}
|
||||
|
||||
return m_ownUIManager;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual UIWindowBaseType BaseType
|
||||
{
|
||||
get { return UIWindowBaseType.None; }
|
||||
}
|
||||
|
||||
|
||||
#region 最基础的接口
|
||||
|
||||
/**
|
||||
* 创建对象
|
||||
*
|
||||
* bindGO 是否把GameObject和Window绑定在一起
|
||||
*/
|
||||
protected bool CreateBase(GameObject go, bool bindGo)
|
||||
{
|
||||
///has created
|
||||
if (!m_destroyed)
|
||||
{
|
||||
Debug.LogErrorFormat("UIBase has created: {0}", go.name);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (go == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
m_destroyed = false;
|
||||
m_go = go;
|
||||
|
||||
m_transform = go.GetComponent<RectTransform>();
|
||||
m_canvas = gameObject.GetComponent<Canvas>();
|
||||
|
||||
var canvas = gameObject.GetComponentsInChildren<Canvas>(true);
|
||||
for (var i = 0; i < canvas.Length; i++)
|
||||
{
|
||||
var canva = canvas[i];
|
||||
canva.additionalShaderChannels = AdditionalCanvasShaderChannels.TexCoord1;
|
||||
}
|
||||
|
||||
if (m_transform == null)
|
||||
{
|
||||
Debug.LogErrorFormat("{0} ui base element need to be RectTransform", go.name);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protected void DestroyAllChild()
|
||||
{
|
||||
//销毁子对象
|
||||
if (m_listChild != null)
|
||||
{
|
||||
for (int i = 0; i < m_listChild.Count; i++)
|
||||
{
|
||||
var child = m_listChild[i];
|
||||
child.Destroy();
|
||||
}
|
||||
|
||||
m_listChild.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
public void Destroy()
|
||||
{
|
||||
if (IsDestroyed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_destroyed = true;
|
||||
|
||||
OnDestroy();
|
||||
DestroyAllChild();
|
||||
|
||||
if (m_parent != null)
|
||||
{
|
||||
m_parent.RmvChild(this);
|
||||
m_parent = null;
|
||||
}
|
||||
|
||||
if (m_go != null)
|
||||
{
|
||||
Object.Destroy(m_go);
|
||||
m_go = null;
|
||||
}
|
||||
|
||||
m_transform = null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 子类扩展的接口
|
||||
|
||||
/// <summary> 脚本生成的代码 </summary>
|
||||
protected virtual void ScriptGenerator()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 绑定代码和prefab之间元素的关系
|
||||
/// </summary>
|
||||
protected virtual void BindMemberProperty()
|
||||
{
|
||||
}
|
||||
|
||||
protected virtual void RegisterEvent()
|
||||
{
|
||||
}
|
||||
|
||||
private bool m_hasOverrideUpdate = true;
|
||||
protected virtual void OnUpdate()
|
||||
{
|
||||
m_hasOverrideUpdate = false;
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 界面创建出来的时候调用,被覆盖不可见不会重复触发
|
||||
/// </summary>
|
||||
protected virtual void OnCreate()
|
||||
{
|
||||
}
|
||||
|
||||
protected virtual void OnDestroy()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建出来首次visible
|
||||
/// 用来播放一些显示动画之类的
|
||||
/// </summary>
|
||||
protected virtual void OnFirstVisible()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 当显示出来的时候调用
|
||||
/// 包括首次初始化后显示和上面的界面消失后重新恢复显示
|
||||
/// </summary>
|
||||
protected virtual void OnVisible()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 界面不可见的时候调用
|
||||
/// 当被上层全屏界面覆盖后,也会触发一次隐藏
|
||||
/// </summary>
|
||||
protected virtual void OnHidden()
|
||||
{
|
||||
}
|
||||
|
||||
protected void _OnSortingOrderChg()
|
||||
{
|
||||
if (m_listChild != null)
|
||||
{
|
||||
for (int i = 0; i < m_listChild.Count; i++)
|
||||
{
|
||||
if (m_listChild[i].m_visible)
|
||||
{
|
||||
m_listChild[i]._OnSortingOrderChg();
|
||||
}
|
||||
}
|
||||
}
|
||||
OnSortingOrderChg();
|
||||
}
|
||||
|
||||
protected virtual void OnSortingOrderChg()
|
||||
{
|
||||
}
|
||||
#endregion
|
||||
|
||||
public void AddChild(UIWindowBase child)
|
||||
{
|
||||
if (m_listChild == null)
|
||||
{
|
||||
m_listChild = new List<UIWindowBase>();
|
||||
}
|
||||
|
||||
m_listChild.Add(child);
|
||||
MarkListChanged();
|
||||
}
|
||||
|
||||
public void RmvChild(UIWindowBase child)
|
||||
{
|
||||
//如果已经销毁了或者销毁过程中,那么不掉用删除
|
||||
if (m_destroyed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_listChild != null)
|
||||
{
|
||||
if (m_listChild.Remove(child))
|
||||
{
|
||||
MarkListChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 重新整理update和lateupdate的调用缓存
|
||||
/// </summary>
|
||||
private void MarkListChanged()
|
||||
{
|
||||
m_updateListValid = false;
|
||||
if (m_parent != null)
|
||||
{
|
||||
m_parent.MarkListChanged();
|
||||
}
|
||||
}
|
||||
|
||||
#region 子类调用的接口
|
||||
|
||||
protected Coroutine StartCoroutine(IEnumerator routine)
|
||||
{
|
||||
return MonoManager.Instance.StartCoroutine(routine);
|
||||
}
|
||||
|
||||
protected void StopCoroutine(Coroutine cort)
|
||||
{
|
||||
MonoManager.Instance.StopCoroutine(cort);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 通用的操作接口
|
||||
|
||||
public Transform FindChild(string path)
|
||||
{
|
||||
return UnityUtil.FindChild(transform, path);
|
||||
}
|
||||
|
||||
public Transform FindChild(Transform _transform, string path)
|
||||
{
|
||||
return UnityUtil.FindChild(_transform, path);
|
||||
}
|
||||
|
||||
public T FindChildComponent<T>(string path) where T : Component
|
||||
{
|
||||
return UnityUtil.FindChildComponent<T>(transform, path);
|
||||
}
|
||||
|
||||
public T FindChildComponent<T>(Transform _transform, string path) where T : Component
|
||||
{
|
||||
return UnityUtil.FindChildComponent<T>(_transform, path);
|
||||
}
|
||||
|
||||
|
||||
public void Show(bool visible)
|
||||
{
|
||||
// 加个保护
|
||||
if (m_destroyed || gameObject == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (m_visible != visible)
|
||||
{
|
||||
m_visible = visible;
|
||||
if (visible)
|
||||
{
|
||||
gameObject.SetActive(true);
|
||||
_OnVisible();
|
||||
}
|
||||
else
|
||||
{
|
||||
_OnHidden();
|
||||
|
||||
if (gameObject == null)
|
||||
{
|
||||
Debug.LogErrorFormat("ui bug, hiden destory gameobject: {0}", name);
|
||||
}
|
||||
else
|
||||
{
|
||||
gameObject.SetActive(false);
|
||||
}
|
||||
}
|
||||
|
||||
MarkListChanged();
|
||||
}
|
||||
}
|
||||
|
||||
protected void _OnVisible()
|
||||
{
|
||||
if (m_listChild != null)
|
||||
{
|
||||
for (int i = 0; i < m_listChild.Count; i++)
|
||||
{
|
||||
var child = m_listChild[i];
|
||||
if (child.gameObject.activeInHierarchy)
|
||||
{
|
||||
child._OnVisible();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!m_firstVisible)
|
||||
{
|
||||
m_firstVisible = true;
|
||||
OnFirstVisible();
|
||||
}
|
||||
OnVisible();
|
||||
}
|
||||
|
||||
protected void _OnHidden()
|
||||
{
|
||||
if (m_listChild != null)
|
||||
{
|
||||
for (int i = 0; i < m_listChild.Count; i++)
|
||||
{
|
||||
var child = m_listChild[i];
|
||||
if (child.gameObject.activeInHierarchy)
|
||||
{
|
||||
child._OnHidden();
|
||||
}
|
||||
}
|
||||
}
|
||||
OnHidden();
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 返回是否有必要下一帧继续执行
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public bool Update()
|
||||
{
|
||||
if (!m_visible || m_destroyed)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
List<UIWindowBase> listNextUpdateChild = null;
|
||||
if (m_listChild != null && m_listChild.Count > 0)
|
||||
{
|
||||
listNextUpdateChild = m_listUpdateChild;
|
||||
var updateListValid = m_updateListValid;
|
||||
List<UIWindowBase> listChild = null;
|
||||
if (!updateListValid)
|
||||
{
|
||||
if (listNextUpdateChild == null)
|
||||
{
|
||||
listNextUpdateChild = new List<UIWindowBase>();
|
||||
m_listUpdateChild = listNextUpdateChild;
|
||||
}
|
||||
else
|
||||
{
|
||||
listNextUpdateChild.Clear();
|
||||
}
|
||||
|
||||
listChild = m_listChild;
|
||||
}
|
||||
else
|
||||
{
|
||||
listChild = listNextUpdateChild;
|
||||
}
|
||||
|
||||
for (int i = 0; i < listChild.Count; i++)
|
||||
{
|
||||
var window = listChild[i];
|
||||
|
||||
var needValid = window.Update();
|
||||
|
||||
if (!updateListValid && needValid)
|
||||
{
|
||||
listNextUpdateChild.Add(window);
|
||||
}
|
||||
}
|
||||
|
||||
if (!updateListValid)
|
||||
{
|
||||
m_updateListValid = true;
|
||||
}
|
||||
}
|
||||
|
||||
bool needUpdate = false;
|
||||
if (listNextUpdateChild == null || listNextUpdateChild.Count <= 0)
|
||||
{
|
||||
m_hasOverrideUpdate = true;
|
||||
OnUpdate();
|
||||
needUpdate = m_hasOverrideUpdate;
|
||||
}
|
||||
else
|
||||
{
|
||||
OnUpdate();
|
||||
needUpdate = true;
|
||||
}
|
||||
return needUpdate;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,175 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace TEngineCore
|
||||
{
|
||||
/// <summary>
|
||||
/// 硬件设备性能适配工具
|
||||
/// </summary>
|
||||
public static class DevicePerformanceUtil
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取设备性能评级
|
||||
/// </summary>
|
||||
/// <returns>性能评级</returns>
|
||||
public static DevicePerformanceLevel GetDevicePerformanceLevel()
|
||||
{
|
||||
if (SystemInfo.graphicsDeviceVendorID == 32902)
|
||||
{
|
||||
//集显
|
||||
return DevicePerformanceLevel.Low;
|
||||
}
|
||||
else //NVIDIA 系列显卡(N卡)和AMD系列显卡
|
||||
{
|
||||
//根据目前硬件配置三个平台设置了不一样的评判标准(仅个人意见)
|
||||
//CPU核心数
|
||||
#if UNITY_EDITOR || UNITY_STANDALONE_WIN
|
||||
if (SystemInfo.processorCount <= 2)
|
||||
#elif UNITY_STANDALONE_OSX || UNITY_IPHONE
|
||||
if (SystemInfo.processorCount < 2)
|
||||
#elif UNITY_ANDROID
|
||||
if (SystemInfo.processorCount <= 4)
|
||||
#else
|
||||
if(SystemInfo.processorCount <= 2)
|
||||
#endif
|
||||
{
|
||||
//CPU核心数<=2判定为低端
|
||||
return DevicePerformanceLevel.Low;
|
||||
}
|
||||
else
|
||||
{
|
||||
//显存
|
||||
int graphicsMemorySize = SystemInfo.graphicsMemorySize;
|
||||
//内存
|
||||
int systemMemorySize = SystemInfo.systemMemorySize;
|
||||
#if UNITY_EDITOR || UNITY_STANDALONE_WIN
|
||||
if (graphicsMemorySize >= 4000 && systemMemorySize >= 8000)
|
||||
return DevicePerformanceLevel.High;
|
||||
else if (graphicsMemorySize >= 2000 && systemMemorySize >= 4000)
|
||||
return DevicePerformanceLevel.Mid;
|
||||
else
|
||||
return DevicePerformanceLevel.Low;
|
||||
#elif UNITY_STANDALONE_OSX || UNITY_IPHONE
|
||||
if (graphicsMemorySize >= 4000 && systemMemorySize >= 8000)
|
||||
return DevicePerformanceLevel.High;
|
||||
else if (graphicsMemorySize >= 2000 && systemMemorySize >= 4000)
|
||||
return DevicePerformanceLevel.Mid;
|
||||
else
|
||||
return DevicePerformanceLevel.Low;
|
||||
#elif UNITY_ANDROID
|
||||
if (graphicsMemorySize >= 6000 && systemMemorySize >= 8000)
|
||||
return DevicePerformanceLevel.High;
|
||||
else if (graphicsMemorySize >= 2000 && systemMemorySize >= 4000)
|
||||
return DevicePerformanceLevel.Mid;
|
||||
else
|
||||
return DevicePerformanceLevel.Low;
|
||||
#else
|
||||
return DevicePerformanceLevel.Low;
|
||||
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据手机性能修改项目设置
|
||||
/// </summary>
|
||||
/// <param name="lowQuality">QualitySettings中对应Low的等级</param>
|
||||
/// <param name="midQuality">QualitySettings中对应Mid的等级</param>
|
||||
/// <param name="highQuality">QualitySettings中对应High的等级</param>
|
||||
public static void ModifySettingsBasedOnPerformance(int lowQuality, int midQuality, int highQuality)
|
||||
{
|
||||
DevicePerformanceLevel level = GetDevicePerformanceLevel();
|
||||
switch (level)
|
||||
{
|
||||
case DevicePerformanceLevel.Low:
|
||||
QualitySettings.SetQualityLevel(lowQuality, true);
|
||||
break;
|
||||
case DevicePerformanceLevel.Mid:
|
||||
QualitySettings.SetQualityLevel(midQuality, true);
|
||||
break;
|
||||
case DevicePerformanceLevel.High:
|
||||
QualitySettings.SetQualityLevel(highQuality, true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据机型配置自动设置质量
|
||||
/// </summary>
|
||||
public static void ModifySettingsBasedOnPerformance()
|
||||
{
|
||||
DevicePerformanceLevel level = GetDevicePerformanceLevel();
|
||||
switch (level)
|
||||
{
|
||||
case DevicePerformanceLevel.Low:
|
||||
SetQualitySettings(QualityLevel.Low);
|
||||
break;
|
||||
case DevicePerformanceLevel.Mid:
|
||||
SetQualitySettings(QualityLevel.Mid);
|
||||
break;
|
||||
case DevicePerformanceLevel.High:
|
||||
SetQualitySettings(QualityLevel.High);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据自身需要调整各级别需要修改的设置,可根据需求修改低中高三种方案某一项具体设置
|
||||
/// </summary>
|
||||
/// <param name="qualityLevel">质量等级</param>
|
||||
public static void SetQualitySettings(QualityLevel qualityLevel)
|
||||
{
|
||||
switch (qualityLevel)
|
||||
{
|
||||
case QualityLevel.Low:
|
||||
//前向渲染使用的像素灯的最大数量,建议最少为1
|
||||
QualitySettings.pixelLightCount = 2;
|
||||
//你可以设置使用最大分辨率的纹理或者部分纹理(低分辨率纹理的处理开销低)。选项有 0_完整分辨率,1_1/2分辨率,2_1/4分辨率,3_1/8分辨率
|
||||
QualitySettings.masterTextureLimit = 1;
|
||||
//设置抗锯齿级别。选项有 0_不开启抗锯齿,2_2倍,4_4倍和8_8倍采样。
|
||||
QualitySettings.antiAliasing = 0;
|
||||
//是否使用粒子软融合
|
||||
QualitySettings.softParticles = false;
|
||||
//启用实时反射探针,此设置需要用的时候再打开
|
||||
QualitySettings.realtimeReflectionProbes = false;
|
||||
//如果启用,公告牌将面向摄像机位置而不是摄像机方向。似乎与地形系统有关,此处没啥必要打开
|
||||
QualitySettings.billboardsFaceCameraPosition = false;
|
||||
//设置软硬阴影是否打开
|
||||
QualitySettings.shadows = ShadowQuality.Disable;
|
||||
//设置垂直同步方案,VSyncs数值需要在每帧之间传递,使用0为不等待垂直同步。值必须是0,1或2。
|
||||
QualitySettings.vSyncCount = 0;
|
||||
break;
|
||||
case QualityLevel.Mid:
|
||||
QualitySettings.pixelLightCount = 4;
|
||||
QualitySettings.antiAliasing = 2;
|
||||
QualitySettings.softParticles = false;
|
||||
QualitySettings.realtimeReflectionProbes = true;
|
||||
QualitySettings.billboardsFaceCameraPosition = true;
|
||||
QualitySettings.shadows = ShadowQuality.HardOnly;
|
||||
QualitySettings.vSyncCount = 2;
|
||||
break;
|
||||
case QualityLevel.High:
|
||||
QualitySettings.pixelLightCount = 4;
|
||||
QualitySettings.antiAliasing = 8;
|
||||
QualitySettings.softParticles = true;
|
||||
QualitySettings.realtimeReflectionProbes = true;
|
||||
QualitySettings.billboardsFaceCameraPosition = true;
|
||||
QualitySettings.shadows = ShadowQuality.All;
|
||||
QualitySettings.vSyncCount = 2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
public enum DevicePerformanceLevel
|
||||
{
|
||||
Low,
|
||||
Mid,
|
||||
High
|
||||
}
|
||||
public enum QualityLevel
|
||||
{
|
||||
Low,
|
||||
Mid,
|
||||
High
|
||||
}
|
||||
}
|
298
TEngineHotUpdate/src/TEngineCore/Unitity/UnityUtil.cs
Normal file
298
TEngineHotUpdate/src/TEngineCore/Unitity/UnityUtil.cs
Normal file
@@ -0,0 +1,298 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.RegularExpressions;
|
||||
using UnityEngine;
|
||||
using UnityEngine.EventSystems;
|
||||
using UnityEngineInternal;
|
||||
using Object = UnityEngine.Object;
|
||||
|
||||
namespace TEngineCore
|
||||
{
|
||||
/// <summary>
|
||||
/// 封装Unity相关的一些通用接口
|
||||
/// </summary>
|
||||
public class UnityUtil
|
||||
{
|
||||
[TypeInferenceRule(TypeInferenceRules.TypeReferencedByFirstArgument)]
|
||||
public static Component AddMonoBehaviour(Type type, GameObject go)
|
||||
{
|
||||
var comp = go.GetComponent(type);
|
||||
if (comp == null)
|
||||
{
|
||||
comp = go.AddComponent(type);
|
||||
}
|
||||
|
||||
return comp;
|
||||
}
|
||||
|
||||
public static T AddMonoBehaviour<T>(Component comp) where T : Component
|
||||
{
|
||||
|
||||
var ret = comp.GetComponent<T>();
|
||||
if (ret == null)
|
||||
{
|
||||
ret = comp.gameObject.AddComponent<T>();
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static T AddMonoBehaviour<T>(GameObject go) where T : Component
|
||||
{
|
||||
var comp = go.GetComponent<T>();
|
||||
if (comp == null)
|
||||
{
|
||||
comp = go.AddComponent<T>();
|
||||
}
|
||||
|
||||
return comp;
|
||||
}
|
||||
|
||||
[TypeInferenceRule(TypeInferenceRules.TypeReferencedByFirstArgument)]
|
||||
public static void RmvMonoBehaviour(Type type, GameObject go)
|
||||
{
|
||||
var comp = go.GetComponent(type);
|
||||
if (comp != null)
|
||||
{
|
||||
UnityEngine.Object.Destroy(comp);
|
||||
}
|
||||
}
|
||||
|
||||
public static void RmvMonoBehaviour<T>(GameObject go) where T : Component
|
||||
{
|
||||
var comp = go.GetComponent<T>();
|
||||
if (comp != null)
|
||||
{
|
||||
UnityEngine.Object.Destroy(comp);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static Transform FindChild(Transform transform, string path)
|
||||
{
|
||||
var findTrans = transform.Find(path);
|
||||
if (findTrans != null)
|
||||
{
|
||||
return findTrans;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static Transform FindChildByName(Transform transform, string name)
|
||||
{
|
||||
if (transform == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
for (int i = 0; i < transform.childCount; i++)
|
||||
{
|
||||
var childTrans = transform.GetChild(i);
|
||||
if (childTrans.name == name)
|
||||
{
|
||||
return childTrans;
|
||||
}
|
||||
|
||||
var find = FindChildByName(childTrans, name);
|
||||
if (find != null)
|
||||
{
|
||||
return find;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
[TypeInferenceRule(TypeInferenceRules.TypeReferencedByFirstArgument)]
|
||||
public static Component FindChildComponent(Type type, Transform transform, string path)
|
||||
{
|
||||
var findTrans = transform.Find(path);
|
||||
if (findTrans != null)
|
||||
{
|
||||
return findTrans.gameObject.GetComponent(type);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static T FindChildComponent<T>(Transform transform, string path) where T : Component
|
||||
{
|
||||
var findTrans = transform.Find(path);
|
||||
if (findTrans != null)
|
||||
{
|
||||
return findTrans.gameObject.GetComponent<T>();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static void SetLayer(GameObject go, int layer)
|
||||
{
|
||||
if (go == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
SetLayer(go.transform, layer);
|
||||
}
|
||||
|
||||
public static void SetLayer(Transform trans, int layer)
|
||||
{
|
||||
if (trans == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
trans.gameObject.layer = layer;
|
||||
for (int i = 0, imax = trans.childCount; i < imax; ++i)
|
||||
{
|
||||
Transform child = trans.GetChild(i);
|
||||
SetLayer(child, layer);
|
||||
}
|
||||
}
|
||||
|
||||
public static int RandomRangeInt(int min, int max)
|
||||
{
|
||||
return UnityEngine.Random.Range(min, max);
|
||||
}
|
||||
|
||||
public static float RandomRangeFloat(float min, float max)
|
||||
{
|
||||
return UnityEngine.Random.Range(min, max);
|
||||
}
|
||||
|
||||
public static Vector2 RandomInsideCircle(float radius)
|
||||
{
|
||||
return UnityEngine.Random.insideUnitCircle * radius;
|
||||
}
|
||||
|
||||
[TypeInferenceRule(TypeInferenceRules.TypeReferencedByFirstArgument)]
|
||||
public static Array CreateUnityArray(Type type, int length)
|
||||
{
|
||||
return Array.CreateInstance(type, length);
|
||||
}
|
||||
|
||||
public static T[] CreateUnityArray<T>(int length)
|
||||
{
|
||||
return new T[length];
|
||||
}
|
||||
|
||||
|
||||
public static GameObject Instantiate(GameObject go)
|
||||
{
|
||||
if (go != null)
|
||||
{
|
||||
return UnityEngine.GameObject.Instantiate(go);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static int GetHashCodeByString(string str)
|
||||
{
|
||||
return str.GetHashCode();
|
||||
}
|
||||
|
||||
public static bool Raycast(Ray ray, out RaycastHit hitInfo, float maxDistance, int layerMask)
|
||||
{
|
||||
return Physics.Raycast(ray, out hitInfo, maxDistance, layerMask);
|
||||
}
|
||||
|
||||
public static List<string> GetRegexMatchGroups(string pattern, string input)
|
||||
{
|
||||
List<string> list = new List<string>();
|
||||
var regexLink = new Regex(pattern);
|
||||
var links = regexLink.Match(input);
|
||||
for (var i = 0; i < links.Groups.Count; ++i)
|
||||
{
|
||||
list.Add(links.Groups[i].Value);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
public static void SetMaterialVector3(Material mat, int nameId, Vector3 val)
|
||||
{
|
||||
mat.SetVector(nameId, val);
|
||||
}
|
||||
|
||||
public static void GetVectorData(Vector3 val, out float x, out float y, out float z)
|
||||
{
|
||||
x = val.x;
|
||||
y = val.y;
|
||||
z = val.z;
|
||||
}
|
||||
public static void GetVector2Data(Vector2 val, out float x, out float y)
|
||||
{
|
||||
x = val.x;
|
||||
y = val.y;
|
||||
}
|
||||
|
||||
public static bool GetTouchByFingerId(int fingerId, out Touch findTouch)
|
||||
{
|
||||
var finded = false;
|
||||
var touchCnt = Input.touchCount;
|
||||
|
||||
findTouch = new Touch();
|
||||
for (int i = 0; i < touchCnt; i++)
|
||||
{
|
||||
var touch = Input.GetTouch(i);
|
||||
if (touch.fingerId == fingerId)
|
||||
{
|
||||
findTouch = touch;
|
||||
finded = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return finded;
|
||||
}
|
||||
|
||||
public static bool SetAnimatorController(GameObject go, string resPath)
|
||||
{
|
||||
//RuntimeAnimatorController rac = (RuntimeAnimatorController)ResourcesManager.Instance.Load<UnityEngine.Object>(resPath);
|
||||
//if (rac == null)
|
||||
//{
|
||||
// Debug.Log("GetAnimator failed path: " + resPath);
|
||||
// return false;
|
||||
//}
|
||||
|
||||
//var ani = go.GetComponentInChildren<Animator>(true);
|
||||
|
||||
//if (ani != null)
|
||||
//{
|
||||
// ani.runtimeAnimatorController = rac;
|
||||
// return true;
|
||||
//}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void SetGameObjectActive(GameObject go, bool active)
|
||||
{
|
||||
if (go != null && go.activeSelf != active)
|
||||
{
|
||||
go.SetActive(active);
|
||||
}
|
||||
}
|
||||
|
||||
public static T[] GetComponentsInChildren<T>(GameObject go) where T : UIBehaviour
|
||||
{
|
||||
if (go != null)
|
||||
{
|
||||
return go.GetComponentsInChildren<T>();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static T GetComponent<T>(GameObject go)
|
||||
{
|
||||
if (go != null)
|
||||
{
|
||||
return go.GetComponent<T>();
|
||||
}
|
||||
|
||||
return default(T);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user