diff --git a/TEngineHotUpdate/TEngineHotUpdate.csproj b/TEngineHotUpdate/TEngineHotUpdate.csproj index 58a320ae..c6ead9b4 100644 --- a/TEngineHotUpdate/TEngineHotUpdate.csproj +++ b/TEngineHotUpdate/TEngineHotUpdate.csproj @@ -1,5 +1,6 @@  - + Debug @@ -38,9 +39,24 @@ UnityLib\UnityEngine.dll + + UnityLib\UnityEngine.CoreModule.dll + + + UnityLib\UnityEngine.AssetBundleModule.dll + UnityLib\UnityEngine.UI.dll + + UnityLib\UnityEngine.UIModule.dll + + + UnityLib\UnityEngine.PhysicsModule.dll + + + UnityLib\UnityEngine.InputLegacyModule.dll + @@ -49,7 +65,76 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/TEngineHotUpdate/UnityLib/2019.4.12/UnityEngine.AssetBundleModule.dll b/TEngineHotUpdate/UnityLib/2019.4.12/UnityEngine.AssetBundleModule.dll new file mode 100644 index 00000000..6701e2b8 Binary files /dev/null and b/TEngineHotUpdate/UnityLib/2019.4.12/UnityEngine.AssetBundleModule.dll differ diff --git a/TEngineHotUpdate/UnityLib/2019.4.12/UnityEngine.CoreModule.dll b/TEngineHotUpdate/UnityLib/2019.4.12/UnityEngine.CoreModule.dll new file mode 100644 index 00000000..cdb35c00 Binary files /dev/null and b/TEngineHotUpdate/UnityLib/2019.4.12/UnityEngine.CoreModule.dll differ diff --git a/TEngineHotUpdate/UnityLib/2019.4.12/UnityEngine.InputLegacyModule.dll b/TEngineHotUpdate/UnityLib/2019.4.12/UnityEngine.InputLegacyModule.dll new file mode 100644 index 00000000..d0e56457 Binary files /dev/null and b/TEngineHotUpdate/UnityLib/2019.4.12/UnityEngine.InputLegacyModule.dll differ diff --git a/TEngineHotUpdate/UnityLib/2019.4.12/UnityEngine.Physics2DModule.dll b/TEngineHotUpdate/UnityLib/2019.4.12/UnityEngine.Physics2DModule.dll new file mode 100644 index 00000000..cb4a1b02 Binary files /dev/null and b/TEngineHotUpdate/UnityLib/2019.4.12/UnityEngine.Physics2DModule.dll differ diff --git a/TEngineHotUpdate/UnityLib/2019.4.12/UnityEngine.PhysicsModule.dll b/TEngineHotUpdate/UnityLib/2019.4.12/UnityEngine.PhysicsModule.dll new file mode 100644 index 00000000..884b544f Binary files /dev/null and b/TEngineHotUpdate/UnityLib/2019.4.12/UnityEngine.PhysicsModule.dll differ diff --git a/TEngineHotUpdate/UnityLib/2019.4.12/UnityEngine.UIModule.dll b/TEngineHotUpdate/UnityLib/2019.4.12/UnityEngine.UIModule.dll new file mode 100644 index 00000000..8339f790 Binary files /dev/null and b/TEngineHotUpdate/UnityLib/2019.4.12/UnityEngine.UIModule.dll differ diff --git a/TEngineHotUpdate/UnityLib/2019.4.12/UnityEngine.dll b/TEngineHotUpdate/UnityLib/2019.4.12/UnityEngine.dll index dd1853bf..f27b0928 100644 Binary files a/TEngineHotUpdate/UnityLib/2019.4.12/UnityEngine.dll and b/TEngineHotUpdate/UnityLib/2019.4.12/UnityEngine.dll differ diff --git a/TEngineHotUpdate/UnityLib/UnityEngine.AssetBundleModule.dll b/TEngineHotUpdate/UnityLib/UnityEngine.AssetBundleModule.dll new file mode 100644 index 00000000..6701e2b8 Binary files /dev/null and b/TEngineHotUpdate/UnityLib/UnityEngine.AssetBundleModule.dll differ diff --git a/TEngineHotUpdate/UnityLib/UnityEngine.CoreModule.dll b/TEngineHotUpdate/UnityLib/UnityEngine.CoreModule.dll new file mode 100644 index 00000000..cdb35c00 Binary files /dev/null and b/TEngineHotUpdate/UnityLib/UnityEngine.CoreModule.dll differ diff --git a/TEngineHotUpdate/UnityLib/UnityEngine.InputLegacyModule.dll b/TEngineHotUpdate/UnityLib/UnityEngine.InputLegacyModule.dll new file mode 100644 index 00000000..d0e56457 Binary files /dev/null and b/TEngineHotUpdate/UnityLib/UnityEngine.InputLegacyModule.dll differ diff --git a/TEngineHotUpdate/UnityLib/UnityEngine.Physics2DModule.dll b/TEngineHotUpdate/UnityLib/UnityEngine.Physics2DModule.dll new file mode 100644 index 00000000..cb4a1b02 Binary files /dev/null and b/TEngineHotUpdate/UnityLib/UnityEngine.Physics2DModule.dll differ diff --git a/TEngineHotUpdate/UnityLib/UnityEngine.PhysicsModule.dll b/TEngineHotUpdate/UnityLib/UnityEngine.PhysicsModule.dll new file mode 100644 index 00000000..884b544f Binary files /dev/null and b/TEngineHotUpdate/UnityLib/UnityEngine.PhysicsModule.dll differ diff --git a/TEngineHotUpdate/UnityLib/UnityEngine.UIModule.dll b/TEngineHotUpdate/UnityLib/UnityEngine.UIModule.dll new file mode 100644 index 00000000..8339f790 Binary files /dev/null and b/TEngineHotUpdate/UnityLib/UnityEngine.UIModule.dll differ diff --git a/TEngineHotUpdate/UnityLib/UnityEngine.dll b/TEngineHotUpdate/UnityLib/UnityEngine.dll index dd1853bf..f27b0928 100644 Binary files a/TEngineHotUpdate/UnityLib/UnityEngine.dll and b/TEngineHotUpdate/UnityLib/UnityEngine.dll differ diff --git a/TEngineHotUpdate/src/TEngineCore/ClientSaveData/ClientSaveData.cs b/TEngineHotUpdate/src/TEngineCore/ClientSaveData/ClientSaveData.cs new file mode 100644 index 00000000..f4721c27 --- /dev/null +++ b/TEngineHotUpdate/src/TEngineCore/ClientSaveData/ClientSaveData.cs @@ -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 + { + private Dictionary m_dictSaveData = new Dictionary(); + + public T GetSaveData() 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(); + } + } + } +} \ No newline at end of file diff --git a/TEngineHotUpdate/src/TEngineCore/Config/ResConfigUtil.cs b/TEngineHotUpdate/src/TEngineCore/Config/ResConfigUtil.cs new file mode 100644 index 00000000..0695507a --- /dev/null +++ b/TEngineHotUpdate/src/TEngineCore/Config/ResConfigUtil.cs @@ -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 ReadConfigListRes(string fileName) + { + string resPath = string.Format("Config/{0}.json",fileName); + TextAsset jsonStr = TResources.Load(resPath); + if (jsonStr == null) + { + TLogger.LogError("读取Json配置数据失败:{0}", fileName); + return null; + } + + List list = new List(); + var jsonData = JsonHelper.Instance.Deserialize>(jsonStr.text); + list = jsonData; + return list; + } + + public static Dictionary ReadConfigRes(string fileName) + { + string resPath = string.Format("Config/{0}.json", fileName); + TextAsset jsonStr = TResources.Load(resPath); + if (jsonStr == null) + { + TLogger.LogError("读取Json配置数据失败:{0}", fileName); + return null; + } + + Dictionary dic = new Dictionary(); + var jsonData = JsonHelper.Instance.Deserialize>(jsonStr.text); + dic = jsonData; + return dic; + } + + public static Dictionary ReadConfigResIntKey(string fileName) + { + string resPath = string.Format("Config/{0}.json", fileName); + TextAsset jsonStr = TResources.Load(resPath); + if (jsonStr == null) + { + TLogger.LogError("读取Json配置数据失败:{0}", fileName); + return null; + } + + Dictionary dic = new Dictionary(); + var jsonData = JsonHelper.Instance.Deserialize>(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 +{ + private Dictionary m_dictBaseConfig = new Dictionary(); + + public BufferMgr() + { + m_dictBaseConfig = ResConfigUtil.ReadConfigRes("BuffConfig"); + } + + public Dictionary GetBuffConfig() + { + return m_dictBaseConfig; + } +} + * + */ \ No newline at end of file diff --git a/TEngineHotUpdate/src/TEngineCore/Core/BaseLogicSys.cs b/TEngineHotUpdate/src/TEngineCore/Core/BaseLogicSys.cs new file mode 100644 index 00000000..7961c4f6 --- /dev/null +++ b/TEngineHotUpdate/src/TEngineCore/Core/BaseLogicSys.cs @@ -0,0 +1,74 @@ +namespace TEngineCore +{ + /// + /// 基础LogicSys,生命周期由TEngine实现,推荐给系统实现, + /// 减少多余的Mono,保持系统层面只有一个Update + /// 用TEngine的主Mono来驱动LogicSys的生命周期 + /// + /// + public class BaseLogicSys : 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 + } + +} \ No newline at end of file diff --git a/TEngineHotUpdate/src/TEngineCore/Core/ILogicSys.cs b/TEngineHotUpdate/src/TEngineCore/Core/ILogicSys.cs new file mode 100644 index 00000000..9e06411f --- /dev/null +++ b/TEngineHotUpdate/src/TEngineCore/Core/ILogicSys.cs @@ -0,0 +1,20 @@ +namespace TEngineCore +{ + public interface ILogicSys + { + bool OnInit(); + + void OnDestroy(); + + void OnStart(); + + void OnUpdate(); + + void OnLateUpdate(); + + void OnPause(); + + void OnResume(); + } + +} \ No newline at end of file diff --git a/TEngineHotUpdate/src/TEngineCore/Core/ListPool.cs b/TEngineHotUpdate/src/TEngineCore/Core/ListPool.cs new file mode 100644 index 00000000..7ec22313 --- /dev/null +++ b/TEngineHotUpdate/src/TEngineCore/Core/ListPool.cs @@ -0,0 +1,90 @@ +using System.Collections.Generic; +using UnityEngine.Events; + +namespace TEngineCore +{ + public static class ListPool + { + private static readonly ObjectPool> m_ListPool = new ObjectPool>(null, Clear); + + static void Clear(List list) + { + list.Clear(); + } + + public static List Get() + { + return m_ListPool.Get(); + } + + public static void Release(List toRelease) + { + m_ListPool.Release(toRelease); + } + } + + public class ObjectPool where T : new() + { + private readonly Stack m_Stack = new Stack(); + private readonly UnityAction m_ActionGet; + private readonly UnityAction 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 actionGet, UnityAction 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); + } + } +} \ No newline at end of file diff --git a/TEngineHotUpdate/src/TEngineCore/Core/MemPoolMgr.cs b/TEngineHotUpdate/src/TEngineCore/Core/MemPoolMgr.cs new file mode 100644 index 00000000..13960992 --- /dev/null +++ b/TEngineHotUpdate/src/TEngineCore/Core/MemPoolMgr.cs @@ -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 + { + List m_listPool = new List(); + + [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 : TSingleton>, MemPoolBase where T : IMemPoolObject, new() + { + private List m_objPool = new List(); + + public static T Alloc() + { + return GameMemPool.Instance.DoAlloc(); + } + + public static void Free(T obj) + { + GameMemPool.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; + } + } + +} \ No newline at end of file diff --git a/TEngineHotUpdate/src/TEngineCore/Core/Singleton.cs b/TEngineHotUpdate/src/TEngineCore/Core/Singleton.cs new file mode 100644 index 00000000..4e64e0a3 --- /dev/null +++ b/TEngineHotUpdate/src/TEngineCore/Core/Singleton.cs @@ -0,0 +1,28 @@ +namespace TEngineCore +{ + /// + /// 通用单例,无需释放和销毁 + /// + /// + public class Singleton 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; + } + } +} \ No newline at end of file diff --git a/TEngineHotUpdate/src/TEngineCore/Core/TEngine.cs b/TEngineHotUpdate/src/TEngineCore/Core/TEngine.cs new file mode 100644 index 00000000..454c1615 --- /dev/null +++ b/TEngineHotUpdate/src/TEngineCore/Core/TEngine.cs @@ -0,0 +1,149 @@ +using System.Collections.Generic; +using UnityEngine; + +namespace TEngineCore +{ + /// + /// 游戏入口,派生TEngine,实现进入游戏虚函数(override StartGame) + /// + public class TEngine : UnitySingleton + { + public override void Awake() + { + base.Awake(); + + TLogger.LogInfo($"DevicePerformanceLevel 设备性能评级:{DevicePerformanceUtil.GetDevicePerformanceLevel()}"); + + InitLibImp(); + + RegisterAllSystem(); + + AfterAwake(); + } + + /// + /// 注册实现库 + /// + protected virtual void InitLibImp() + { + + } + + /// + /// 注册系统(例如BaseLogic/TEngineObject/MonoManger) + /// + protected virtual void RegisterAllSystem() + { + + } + + protected void SetTargetFrameRate(int targetFrameRate) + { + Application.targetFrameRate = targetFrameRate; + } + + //-------------------------------------------------------系统注册--------------------------------------------------------// + private List m_LogicMgrList = new List(); + + /// + /// 系统注册 + /// + /// + /// + 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 + } +} \ No newline at end of file diff --git a/TEngineHotUpdate/src/TEngineCore/Core/TEngineRedux.cs b/TEngineHotUpdate/src/TEngineCore/Core/TEngineRedux.cs new file mode 100644 index 00000000..d1106fa9 --- /dev/null +++ b/TEngineHotUpdate/src/TEngineCore/Core/TEngineRedux.cs @@ -0,0 +1,880 @@ +using System; +using System.Collections.Generic; +using UnityEngine; + +namespace TEngineCore +{ + #region Architecture + + public interface IArchitecture + { + void RegisterSystem(T system) where T : ISystem; + + void RegisterModel(T model) where T : IModel; + + void RegisterUtility(T utility) where T : IUtility; + + T GetSystem() where T : class, ISystem; + + T GetModel() where T : class, IModel; + + T GetUtility() where T : class, IUtility; + + void SendCommand() where T : ICommand, new(); + + void SendCommand(T command) where T : ICommand; + + TResult SendQuery(IQuery query); + + void SendEvent() where T : new(); + + void SendEvent(T e); + + IUnRegister RegisterEvent(Action onEvent); + + void UnRegisterEvent(Action onEvent); + } + + public abstract class Architecture : IArchitecture where T : Architecture, new() + { + private bool _Inited = false; + + private List _Systems = new List(); + + private List _Models = new List(); + + public static Action 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 system) where TSystem : ISystem + { + system.SetArchitecture(this); + mContainer.Register(system); + + if (!_Inited) + { + _Systems.Add(system); + } + else + { + system.Init(); + } + } + + public void RegisterModel(TModel model) where TModel : IModel + { + model.SetArchitecture(this); + mContainer.Register(model); + + if (!_Inited) + { + _Models.Add(model); + } + else + { + model.Init(); + } + } + + public void RegisterUtility(TUtility utility) where TUtility : IUtility + { + mContainer.Register(utility); + } + + public TSystem GetSystem() where TSystem : class, ISystem + { + return mContainer.Get(); + } + + public TModel GetModel() where TModel : class, IModel + { + return mContainer.Get(); + } + + public TUtility GetUtility() where TUtility : class, IUtility + { + return mContainer.Get(); + } + + public void SendCommand() where TCommand : ICommand, new() + { + var command = new TCommand(); + command.SetArchitecture(this); + command.Execute(); + } + + public void SendCommand(TCommand command) where TCommand : ICommand + { + command.SetArchitecture(this); + command.Execute(); + } + + public TResult SendQuery(IQuery query) + { + query.SetArchitecture(this); + return query.Do(); + } + + private TypeEventSystem mTypeEventSystem = new TypeEventSystem(); + + public void SendEvent() where TEvent : new() + { + mTypeEventSystem.Send(); + } + + public void SendEvent(TEvent e) + { + mTypeEventSystem.Send(e); + } + + public IUnRegister RegisterEvent(Action onEvent) + { + return mTypeEventSystem.Register(onEvent); + } + + public void UnRegisterEvent(Action onEvent) + { + mTypeEventSystem.UnRegister(onEvent); + } + } + + public interface IOnEvent + { + void OnEvent(T e); + } + + public static class OnGlobalEventExtension + { + public static IUnRegister RegisterEvent(this IOnEvent self) where T : struct + { + return TypeEventSystem.Global.Register(self.OnEvent); + } + + public static void UnRegisterEvent(this IOnEvent self) where T : struct + { + TypeEventSystem.Global.UnRegister(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 : IBelongToArchitecture, ICanSetArchitecture, ICanGetModel, ICanGetSystem, + ICanSendQuery + { + TResult Do(); + } + + public abstract class AbstractQuery : IQuery + { + 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(this ICanGetModel self) where T : class, IModel + { + return self.GetArchitecture().GetModel(); + } + } + + public interface ICanGetSystem : IBelongToArchitecture + { + } + + public static class CanGetSystemExtension + { + public static T GetSystem(this ICanGetSystem self) where T : class, ISystem + { + return self.GetArchitecture().GetSystem(); + } + } + + public interface ICanGetUtility : IBelongToArchitecture + { + } + + public static class CanGetUtilityExtension + { + public static T GetUtility(this ICanGetUtility self) where T : class, IUtility + { + return self.GetArchitecture().GetUtility(); + } + } + + public interface ICanRegisterEvent : IBelongToArchitecture + { + } + + public static class CanRegisterEventExtension + { + public static IUnRegister RegisterEvent(this ICanRegisterEvent self, Action onEvent) + { + return self.GetArchitecture().RegisterEvent(onEvent); + } + + public static void UnRegisterEvent(this ICanRegisterEvent self, Action onEvent) + { + self.GetArchitecture().UnRegisterEvent(onEvent); + } + } + + public interface ICanSendCommand : IBelongToArchitecture + { + } + + public static class CanSendCommandExtension + { + public static void SendCommand(this ICanSendCommand self) where T : ICommand, new() + { + self.GetArchitecture().SendCommand(); + } + + public static void SendCommand(this ICanSendCommand self, T command) where T : ICommand + { + self.GetArchitecture().SendCommand(command); + } + } + + public interface ICanSendEvent : IBelongToArchitecture + { + } + + public static class CanSendEventExtension + { + public static void SendEvent(this ICanSendEvent self) where T : new() + { + self.GetArchitecture().SendEvent(); + } + + public static void SendEvent(this ICanSendEvent self, T e) + { + self.GetArchitecture().SendEvent(e); + } + } + + public interface ICanSendQuery : IBelongToArchitecture + { + } + + public static class CanSendQueryExtension + { + public static TResult SendQuery(this ICanSendQuery self, IQuery query) + { + return self.GetArchitecture().SendQuery(query); + } + } + + #endregion + + #region TypeEventSystem + + public interface IUnRegister + { + void UnRegister(); + } + + public interface IUnRegisterList + { + List 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(); + } + } + + /// + /// 自定义可注销的类 + /// + public struct CustomUnRegister : IUnRegister + { + /// + /// 委托对象 + /// + private Action mOnUnRegister { get; set; } + + /// + /// 带参构造函数 + /// + /// + public CustomUnRegister(Action onUnRegsiter) + { + mOnUnRegister = onUnRegsiter; + } + + /// + /// 资源释放 + /// + public void UnRegister() + { + mOnUnRegister.Invoke(); + mOnUnRegister = null; + } + } + + public class UnRegisterOnDestroyTrigger : MonoBehaviour + { + private readonly HashSet mUnRegisters = new HashSet(); + + 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(); + + if (!trigger) + { + trigger = gameObject.AddComponent(); + } + + trigger.AddUnRegister(unRegister); + + return unRegister; + } + } + + public class TypeEventSystem + { + private readonly EasyEvents mEvents = new EasyEvents(); + + + public static readonly TypeEventSystem Global = new TypeEventSystem(); + + public void Send() where T : new() + { + mEvents.GetEvent>()?.Trigger(new T()); + } + + public void Send(T e) + { + mEvents.GetEvent>()?.Trigger(e); + } + + public IUnRegister Register(Action onEvent) + { + var e = mEvents.GetOrAddEvent>(); + return e.Register(onEvent); + } + + public void UnRegister(Action onEvent) + { + var e = mEvents.GetEvent>(); + if (e != null) + { + e.UnRegister(onEvent); + } + } + } + + #endregion + + #region TEngine + + public class TEngineContainer + { + private Dictionary _Instances = new Dictionary(); + + public void Register(T instance) + { + var key = typeof(T); + + if (_Instances.ContainsKey(key)) + { + _Instances[key] = instance; + } + else + { + _Instances.Add(key, instance); + } + } + + public T Get() 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 : IReadonlyBindableProperty + { + new T Value { get; set; } + void SetValueWithoutEvent(T newValue); + } + + public interface IReadonlyBindableProperty + { + T Value { get; } + + IUnRegister RegisterWithInitValue(Action action); + void UnRegister(Action onValueChanged); + IUnRegister Register(Action onValueChanged); + } + + public class BindableProperty : IBindableProperty + { + 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 mOnValueChanged = (v) => { }; + + public IUnRegister Register(Action onValueChanged) + { + mOnValueChanged += onValueChanged; + return new BindablePropertyUnRegister() + { + BindableProperty = this, + OnValueChanged = onValueChanged + }; + } + + public IUnRegister RegisterWithInitValue(Action onValueChanged) + { + onValueChanged(mValue); + return Register(onValueChanged); + } + + public static implicit operator T(BindableProperty property) + { + return property.Value; + } + + public override string ToString() + { + return Value.ToString(); + } + + public void UnRegister(Action onValueChanged) + { + mOnValueChanged -= onValueChanged; + } + } + + public class BindablePropertyUnRegister : IUnRegister + { + public BindableProperty BindableProperty { get; set; } + + public Action 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 : IEasyEvent + { + private Action mOnEvent = e => { }; + + public IUnRegister Register(Action onEvent) + { + mOnEvent += onEvent; + return new CustomUnRegister(() => { UnRegister(onEvent); }); + } + + public void UnRegister(Action onEvent) + { + mOnEvent -= onEvent; + } + + public void Trigger(T t) + { + mOnEvent?.Invoke(t); + } + } + + public class EasyEvent : IEasyEvent + { + private Action mOnEvent = (t, k) => { }; + + public IUnRegister Register(Action onEvent) + { + mOnEvent += onEvent; + return new CustomUnRegister(() => { UnRegister(onEvent); }); + } + + public void UnRegister(Action onEvent) + { + mOnEvent -= onEvent; + } + + public void Trigger(T t, K k) + { + mOnEvent?.Invoke(t, k); + } + } + + public class EasyEvent : IEasyEvent + { + private Action mOnEvent = (t, k, s) => { }; + + public IUnRegister Register(Action onEvent) + { + mOnEvent += onEvent; + return new CustomUnRegister(() => { UnRegister(onEvent); }); + } + + public void UnRegister(Action 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() where T : IEasyEvent + { + return mGlobalEvents.GetEvent(); + } + + + public static void Register() where T : IEasyEvent, new() + { + mGlobalEvents.AddEvent(); + } + + private Dictionary mTypeEvents = new Dictionary(); + + public void AddEvent() where T : IEasyEvent, new() + { + mTypeEvents.Add(typeof(T), new T()); + } + + public T GetEvent() where T : IEasyEvent + { + IEasyEvent e; + + if (mTypeEvents.TryGetValue(typeof(T), out e)) + { + return (T)e; + } + + return default; + } + + public T GetOrAddEvent() 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 +} \ No newline at end of file diff --git a/TEngineHotUpdate/src/TEngineCore/Core/TLogger.cs b/TEngineHotUpdate/src/TEngineCore/Core/TLogger.cs new file mode 100644 index 00000000..94d01a1e --- /dev/null +++ b/TEngineHotUpdate/src/TEngineCore/Core/TLogger.cs @@ -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("{1}", color, str); + } + } + + public class TLogger : TSingleton + { + 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}] - {1}", + System.DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss fff"), logString, ColorUtils.Green); + } + else + { + _stringBuilder.AppendFormat( + bColor ? "[TLogger][INFO][{0}] - {1}" : "[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}] - {1}" , + System.DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss fff"), logString,ColorUtils.Black); + } + else + { + _stringBuilder.AppendFormat( + bColor ? "[TLogger][INFO][{0}] - {1}" : "[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}] - {1}", + System.DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss fff"), logString,ColorUtils.Exception); + } + else + { + _stringBuilder.AppendFormat( + bColor ? "[TLogger][ASSERT][{0}] - {1}" : "[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}] - {1}", + System.DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss fff"), logString, ColorUtils.Oringe); + } + else + { + _stringBuilder.AppendFormat( + bColor + ? "[TLogger][WARNING][{0}] - {1}" + : "[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}] - {1}", + System.DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss fff"), logString, ColorUtils.Red); + } + else + { + _stringBuilder.AppendFormat( + bColor ? "[TLogger][ERROR][{0}] - {1}" : "[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}] - {1}", + System.DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss fff"), logString, ColorUtils.Exception); + } + else + { + _stringBuilder.AppendFormat( + bColor + ? "[TLogger][EXCEPTION][{0}] - {1}" + : "[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 + } +} \ No newline at end of file diff --git a/TEngineHotUpdate/src/TEngineCore/Core/TSingleton.cs b/TEngineHotUpdate/src/TEngineCore/Core/TSingleton.cs new file mode 100644 index 00000000..761d24f3 --- /dev/null +++ b/TEngineHotUpdate/src/TEngineCore/Core/TSingleton.cs @@ -0,0 +1,198 @@ +using System.Collections.Generic; +using UnityEngine; + +namespace TEngineCore +{ + /// + /// 单例接口 + /// + public interface ISingleton + { + void Active(); + + void Release(); + } + + /// + /// 单例管理器(统一化持久和释放) + /// + public static class SingletonMgr + { + private static List _iSingletonList; + private static Dictionary _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(); + } + _iSingletonList.Add(go); + } + + public static void Retain(GameObject go) + { + if (_gameObjects == null) + { + _gameObjects = new Dictionary(); + } + + 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; + } + } + + /// + /// 全局单例对象(非线程安全) + /// + /// + public abstract class TSingleton : ISingleton where T : TSingleton, 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; + } + } + } +} \ No newline at end of file diff --git a/TEngineHotUpdate/src/TEngineCore/Core/UnitySingleton.cs b/TEngineHotUpdate/src/TEngineCore/Core/UnitySingleton.cs new file mode 100644 index 00000000..205cabe8 --- /dev/null +++ b/TEngineHotUpdate/src/TEngineCore/Core/UnitySingleton.cs @@ -0,0 +1,110 @@ +using UnityEngine; + +namespace TEngineCore +{ + /// + /// 具备Unity完整生命周期的单例 + /// + /// + public class UnitySingleton : 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(); + if (_instance == null) + { + _instance = go.AddComponent(); + } + } + + 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; + } + } + } +} \ No newline at end of file diff --git a/TEngineHotUpdate/src/TEngineCore/ECS/ArrayPool.cs b/TEngineHotUpdate/src/TEngineCore/ECS/ArrayPool.cs new file mode 100644 index 00000000..96e1550d --- /dev/null +++ b/TEngineHotUpdate/src/TEngineCore/ECS/ArrayPool.cs @@ -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 where T : IIndex + { + private readonly ArrayPool m_Set; + + public HashSetDebugView(ArrayPool 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 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 elements = new List(); + 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; + } + } + } +} \ No newline at end of file diff --git a/TEngineHotUpdate/src/TEngineCore/ECS/Demo/ECSDemoApp.cs b/TEngineHotUpdate/src/TEngineCore/ECS/Demo/ECSDemoApp.cs new file mode 100644 index 00000000..1fd8ca76 --- /dev/null +++ b/TEngineHotUpdate/src/TEngineCore/ECS/Demo/ECSDemoApp.cs @@ -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(); + ECSActor actor = entity.AddComponent(); + actor.Name = typeof(ECSActor).ToString(); + actor.gameObject = Instantiate(@object); + actor.transform = actor.gameObject.GetComponent(); + entity.AddComponent(); + + Debug.Log(entity.ToString()); + } + + void Update() + { + EcsGameSystem.OnUpdate(); + } +} diff --git a/TEngineHotUpdate/src/TEngineCore/ECS/Demo/ECSGameSystem.cs b/TEngineHotUpdate/src/TEngineCore/ECS/Demo/ECSGameSystem.cs new file mode 100644 index 00000000..bb8af66e --- /dev/null +++ b/TEngineHotUpdate/src/TEngineCore/ECS/Demo/ECSGameSystem.cs @@ -0,0 +1,9 @@ +using TEngineCore; + +public class ECSGameSystem : ECSSystem +{ + public void OnUpdate() + { + Update(); + } +} diff --git a/TEngineHotUpdate/src/TEngineCore/ECS/ECSActor.cs b/TEngineHotUpdate/src/TEngineCore/ECS/ECSActor.cs new file mode 100644 index 00000000..860215b6 --- /dev/null +++ b/TEngineHotUpdate/src/TEngineCore/ECS/ECSActor.cs @@ -0,0 +1,13 @@ +namespace TEngineCore +{ + /// + /// ECS Actor + /// + public class ECSActor : ECSComponent + { + public string Name; + public UnityEngine.GameObject gameObject; + public UnityEngine.Transform transform; + public uint ActorID; + } +} \ No newline at end of file diff --git a/TEngineHotUpdate/src/TEngineCore/ECS/ECSComponent.cs b/TEngineHotUpdate/src/TEngineCore/ECS/ECSComponent.cs new file mode 100644 index 00000000..557e2202 --- /dev/null +++ b/TEngineHotUpdate/src/TEngineCore/ECS/ECSComponent.cs @@ -0,0 +1,14 @@ +namespace TEngineCore +{ + /// + /// ECS构架可以将此组件从Entity上移除这个组件并丢入对象池,给其他此刻需要此组件的Entity使用, + /// 因此可以节省大量的内存反复创建和释放, 这也是ECS的特性可以大量重复使用组件 + /// + public class ECSComponent:ECSObject + { +#pragma warning disable IDE1006 + public Entity Entity { get; set; } +#pragma warning restore IDE1006 + } +} + diff --git a/TEngineHotUpdate/src/TEngineCore/ECS/ECSDebugBehaviour.cs b/TEngineHotUpdate/src/TEngineCore/ECS/ECSDebugBehaviour.cs new file mode 100644 index 00000000..a23d52ea --- /dev/null +++ b/TEngineHotUpdate/src/TEngineCore/ECS/ECSDebugBehaviour.cs @@ -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 m_info = new List(); + } + + public class ECSDebugBehaviour : UnityEngine.MonoBehaviour + { + public List m_ECSInfo = new List(); + 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(); + } + } + } +} \ No newline at end of file diff --git a/TEngineHotUpdate/src/TEngineCore/ECS/ECSEventCmpt.cs b/TEngineHotUpdate/src/TEngineCore/ECS/ECSEventCmpt.cs new file mode 100644 index 00000000..924e0683 --- /dev/null +++ b/TEngineHotUpdate/src/TEngineCore/ECS/ECSEventCmpt.cs @@ -0,0 +1,236 @@ +using System; +using System.Collections.Generic; + +namespace TEngineCore +{ + public class ECSEventCmpt : ECSComponent + { + private Dictionary m_eventDic = new Dictionary(); + + #region AddEventListener + 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)); + } + } + + 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)); + } + } + + 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)); + } + } + + 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(int eventid, Action action) + { + if (action == null) + { + return; + } + + if (m_eventDic.ContainsKey(eventid)) + { + (m_eventDic[eventid] as EcsEventInfo).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; + } + } + + public void RemoveEventListener(int eventid, Action action) + { + if (action == null) + { + return; + } + + if (m_eventDic.ContainsKey(eventid)) + { + (m_eventDic[eventid] as EcsEventInfo).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(int eventid, T info) + { + if (m_eventDic.ContainsKey(eventid)) + { + var EcsEventInfo = (m_eventDic[eventid] as EcsEventInfo); + if (EcsEventInfo != null) + { + EcsEventInfo.actions.Invoke(info); + } + } + } + + public void Send(int eventid, T info, U info2) + { + if (m_eventDic.ContainsKey(eventid)) + { + var EcsEventInfo = (m_eventDic[eventid] as EcsEventInfo); + if (EcsEventInfo != null) + { + EcsEventInfo.actions.Invoke(info, info2); + } + } + } + + public void Send(int eventid, T info, U info2, W info3) + { + if (m_eventDic.ContainsKey(eventid)) + { + var EcsEventInfo = (m_eventDic[eventid] as EcsEventInfo); + if (EcsEventInfo != null) + { + EcsEventInfo.actions.Invoke(info, info2, info3); + } + } + } + + /// + /// 事件触发 无参 + /// + /// + 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 : IEcsEcsEventInfo + { + public Action actions; + + public EcsEventInfo(Action action) + { + actions += action; + } + } + + public class EcsEventInfo : IEcsEcsEventInfo + { + public Action actions; + + public EcsEventInfo(Action action) + { + actions += action; + } + } + + public class EcsEventInfo : IEcsEcsEventInfo + { + public Action actions; + + public EcsEventInfo(Action action) + { + actions += action; + } + } + #endregion + +} \ No newline at end of file diff --git a/TEngineHotUpdate/src/TEngineCore/ECS/ECSObject.cs b/TEngineHotUpdate/src/TEngineCore/ECS/ECSObject.cs new file mode 100644 index 00000000..b7266b71 --- /dev/null +++ b/TEngineHotUpdate/src/TEngineCore/ECS/ECSObject.cs @@ -0,0 +1,112 @@ +using System; +using System.Collections.Generic; + +namespace TEngineCore +{ + /// + /// ECS架构基类Object + /// + public class ECSObject + { + internal int HashCode; + + internal ECSSystem System; + + public ECSObject() + { + HashCode = GetType().GetHashCode(); + } + + public virtual void Awake() { } + + public virtual void OnDestroy() { } + + /// + /// Remove The ECSEntity or Component And Throw the ECSObject to ArrayPool When AddComponent Or Create Can Use Again + /// + /// + /// 此对象是否可以复用,复用会将对象丢入System对象池中 等待再次使用,如果是Entity对象,并且不复用的话,则把Entity所使用的组件也不复用 + 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() 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() where T : ECSObject + { + Type type = typeof(T); + var items = System.Entities.ToArray(); + List elements = new List(); + 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(); + } + } +} + diff --git a/TEngineHotUpdate/src/TEngineCore/ECS/ECSSystem.cs b/TEngineHotUpdate/src/TEngineCore/ECS/ECSSystem.cs new file mode 100644 index 00000000..dce93b61 --- /dev/null +++ b/TEngineHotUpdate/src/TEngineCore/ECS/ECSSystem.cs @@ -0,0 +1,233 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace TEngineCore +{ + /// + /// ECS系统 管理Entity、ECSComponent复用对象池 + /// + [Serializable] + public class ECSSystem : IDisposable + { + private static ECSSystem instance = new ECSSystem(); + public static ECSSystem Instance => instance; + private readonly Dictionary> m_ObjectPool = new Dictionary>(); + internal readonly ArrayPool Entities = new ArrayPool(); + 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); + } + + /// + /// Get Object From ECSSystem + /// + /// + /// + public T Get() where T : ECSObject, new() + { + int type = typeof(T).GetHashCode(); + if (m_ObjectPool.TryGetValue(type,out Stack stack)) + { + if (stack.Count > 0) + { + return (T) stack.Pop(); + } + goto Instantiate; + } + stack = new Stack(); + m_ObjectPool.Add(type,stack); + Instantiate: T ecsObject = new T(); + return ecsObject; + } + + /// + /// Push Object To ECSSystem + /// + public void Push(ECSObject ecsObject) + { + int type = ecsObject.HashCode; + + if (m_ObjectPool.TryGetValue(type,out Stack stack)) + { + stack.Push(ecsObject); + return; + } + stack = new Stack(); + m_ObjectPool.Add(type,stack); + stack.Push(ecsObject); + } + + public T Create() where T : Entity, new() + { + T entity = Get(); + AddEntity(entity); + return entity; + } + + public T Create(T entity) where T : Entity, new() + { + AddEntity(entity); + return entity; + } + + /// + /// 更新ECS系统 + /// + /// 线程池是否并行 + public void Update(bool worker = false) + { + Run(worker); + } + + /// + /// 更新ECS物理系统 + /// + /// 线程池是否并行 + public void FixedUpdate(bool worker = false) + { + RunFixed(worker); + } + + /// + /// 运行ECS系统 + /// + /// 线程池是否并行 + 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() 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() where T : ECSObject + { + Type type = typeof(T); + var entities = Entities.ToArray(); + List elements = new List(); + 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(); + } + } +} + diff --git a/TEngineHotUpdate/src/TEngineCore/ECS/Entity.cs b/TEngineHotUpdate/src/TEngineCore/ECS/Entity.cs new file mode 100644 index 00000000..91e12b37 --- /dev/null +++ b/TEngineHotUpdate/src/TEngineCore/ECS/Entity.cs @@ -0,0 +1,273 @@ +using System; +using System.Collections.Generic; +using UnityEngine; + +namespace TEngineCore +{ + public class Entity : ECSObject, IIndex + { + [SerializeField] + internal List Components = new List(); + internal List Updates = new List(); + internal List FixedUpdates = new List(); + 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() 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() where T : ECSComponent, new() + { +#if UNITY_EDITOR + CheckDebugInfo(); +#endif + T component = System.Get(); + 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() 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() where T : ECSComponent + { + List elements = new List(); + for (int i = 0; i < Components.Count; i++) + { + if (Components[i] is T type) + { + elements.Add(type); + } + } + return elements.ToArray(); + } + + public List GetComponentsList() where T : ECSComponent + { + List elements = new List(); + 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 elements = new List(); + 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(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(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 + } + } +} diff --git a/TEngineHotUpdate/src/TEngineCore/ECS/HotfixComponent.cs b/TEngineHotUpdate/src/TEngineCore/ECS/HotfixComponent.cs new file mode 100644 index 00000000..acd37722 --- /dev/null +++ b/TEngineHotUpdate/src/TEngineCore/ECS/HotfixComponent.cs @@ -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; + } + } +} + diff --git a/TEngineHotUpdate/src/TEngineCore/ECS/IFixedUpdate.cs b/TEngineHotUpdate/src/TEngineCore/ECS/IFixedUpdate.cs new file mode 100644 index 00000000..5e87e6de --- /dev/null +++ b/TEngineHotUpdate/src/TEngineCore/ECS/IFixedUpdate.cs @@ -0,0 +1,10 @@ +namespace TEngineCore +{ + /// + /// ECS组件更新接口(减少组件for循环开销) + /// + public interface IFixedUpdate + { + void FixedUpdate(); + } +} \ No newline at end of file diff --git a/TEngineHotUpdate/src/TEngineCore/ECS/IUpdate.cs b/TEngineHotUpdate/src/TEngineCore/ECS/IUpdate.cs new file mode 100644 index 00000000..2ca37f71 --- /dev/null +++ b/TEngineHotUpdate/src/TEngineCore/ECS/IUpdate.cs @@ -0,0 +1,10 @@ +namespace TEngineCore +{ + /// + /// ECS组件更新接口(减少组件for循环开销) + /// + public interface IUpdate + { + void Update(); + } +} \ No newline at end of file diff --git a/TEngineHotUpdate/src/TEngineCore/ECS/UpdateComponent.cs b/TEngineHotUpdate/src/TEngineCore/ECS/UpdateComponent.cs new file mode 100644 index 00000000..7076ee88 --- /dev/null +++ b/TEngineHotUpdate/src/TEngineCore/ECS/UpdateComponent.cs @@ -0,0 +1,13 @@ +namespace TEngineCore +{ + /// + /// 热更层由此组件进行更新(ILRuntime不支持多继承, 接口继承) + /// + public class UpdateComponent :ECSComponent, IUpdate + { + public virtual void Update() + { + + } + } +} \ No newline at end of file diff --git a/TEngineHotUpdate/src/TEngineCore/Event/GameEventMgr.cs b/TEngineHotUpdate/src/TEngineCore/Event/GameEventMgr.cs new file mode 100644 index 00000000..ccc6b0a2 --- /dev/null +++ b/TEngineHotUpdate/src/TEngineCore/Event/GameEventMgr.cs @@ -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 : IEventInfo + { + public Action actions; + + public EventInfo(Action action) + { + actions += action; + } + } + + public class EventInfo : IEventInfo + { + public Action actions; + + public EventInfo(Action action) + { + actions += action; + } + } + + public class EventInfo : IEventInfo + { + public Action actions; + + public EventInfo(Action action) + { + actions += action; + } + } + #endregion + /// + /// 总观察者 - 总事件中心系统 + /// + public class GameEventMgr : TSingleton + { + /// + /// Dictionary Key->Int.32 Value->EventInfo,调用频率高建议使用int事件,减少字典内String的哈希碰撞 + /// + private Dictionary m_eventDic = new Dictionary(); + + /// + /// Dictionary Key->string Value->EventInfo,调用频率不高的时候可以使用 + /// + private Dictionary m_eventStrDic = new Dictionary(); + + #region AddEventListener + 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)); + } + } + + 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)); + } + } + + 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)); + } + } + + 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(int eventid, Action action) + { + if (action == null) + { + return; + } + + if (m_eventDic.ContainsKey(eventid)) + { + (m_eventDic[eventid] as EventInfo).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; + } + } + + public void RemoveEventListener(int eventid, Action action) + { + if (action == null) + { + return; + } + + if (m_eventDic.ContainsKey(eventid)) + { + (m_eventDic[eventid] as EventInfo).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(int eventid, T info) + { + if (m_eventDic.ContainsKey(eventid)) + { + var eventInfo = (m_eventDic[eventid] as EventInfo); + if (eventInfo != null) + { + eventInfo.actions.Invoke(info); + } + } + } + + public void Send(int eventid, T info, U info2) + { + if (m_eventDic.ContainsKey(eventid)) + { + var eventInfo = (m_eventDic[eventid] as EventInfo); + if (eventInfo != null) + { + eventInfo.actions.Invoke(info, info2); + } + } + } + + public void Send(int eventid, T info, U info2, W info3) + { + if (m_eventDic.ContainsKey(eventid)) + { + var eventInfo = (m_eventDic[eventid] as EventInfo); + if (eventInfo != null) + { + eventInfo.actions.Invoke(info, info2, info3); + } + } + } + + /// + /// 事件触发 无参 + /// + /// + 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(string eventid, Action action) + { + if (m_eventStrDic.ContainsKey(eventid)) + { + (m_eventStrDic[eventid] as EventInfo).actions += action; + } + else + { + m_eventStrDic.Add(eventid, new EventInfo(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)); + } + } + + 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)); + } + } + + 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(string eventid, Action action) + { + if (action == null) + { + return; + } + + if (m_eventStrDic.ContainsKey(eventid)) + { + (m_eventStrDic[eventid] as EventInfo).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; + } + } + + public void RemoveEventListener(string eventid, Action action) + { + if (action == null) + { + return; + } + + if (m_eventStrDic.ContainsKey(eventid)) + { + (m_eventStrDic[eventid] as EventInfo).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(string eventid, T info) + { + if (m_eventStrDic.ContainsKey(eventid)) + { + var eventInfo = (m_eventStrDic[eventid] as EventInfo); + if (eventInfo != null) + { + eventInfo.actions.Invoke(info); + } + } + } + + public void Send(string eventid, T info, U info2) + { + if (m_eventStrDic.ContainsKey(eventid)) + { + var eventInfo = (m_eventStrDic[eventid] as EventInfo); + if (eventInfo != null) + { + eventInfo.actions.Invoke(info, info2); + } + } + } + + public void Send(string eventid, T info, U info2, W info3) + { + if (m_eventStrDic.ContainsKey(eventid)) + { + var eventInfo = (m_eventStrDic[eventid] as EventInfo); + 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 + } + +} \ No newline at end of file diff --git a/TEngineHotUpdate/src/TEngineCore/Event/StringId.cs b/TEngineHotUpdate/src/TEngineCore/Event/StringId.cs new file mode 100644 index 00000000..80c9e1df --- /dev/null +++ b/TEngineHotUpdate/src/TEngineCore/Event/StringId.cs @@ -0,0 +1,37 @@ +using System.Collections.Generic; + +namespace TEngineCore +{ + public class StringId + { + private static Dictionary m_eventTypeHashMap = new Dictionary(); + private static Dictionary m_eventHashToStringMap = new Dictionary(); + 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; + } + } + +} \ No newline at end of file diff --git a/TEngineHotUpdate/src/TEngineCore/FileSystem/FileSystem.cs b/TEngineHotUpdate/src/TEngineCore/FileSystem/FileSystem.cs new file mode 100644 index 00000000..75893358 --- /dev/null +++ b/TEngineHotUpdate/src/TEngineCore/FileSystem/FileSystem.cs @@ -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 _fileFixList = new Dictionary(); + 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"; + /// + /// 资源更新读取根目录 + /// + /// + public static string ResourceRoot + { + get + { + if (string.IsNullOrEmpty(_resRootPath)) + { + _resRootPath = Path.Combine(PersistentDataPath, "TEngine"); + } + + if (!Directory.Exists(_resRootPath)) + { + Directory.CreateDirectory(_resRootPath); + } + + return _resRootPath.FixPath(); + } + } + + /// + /// 持久化数据存储路径 + /// + 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(); + } + } + + /// + /// 资源更新读取StreamAsset根目录 + /// + /// + 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 + } + } +} \ No newline at end of file diff --git a/TEngineHotUpdate/src/TEngineCore/FileSystem/FileWriter.cs b/TEngineHotUpdate/src/TEngineCore/FileSystem/FileWriter.cs new file mode 100644 index 00000000..4991b574 --- /dev/null +++ b/TEngineHotUpdate/src/TEngineCore/FileSystem/FileWriter.cs @@ -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()); + } + } + } +} \ No newline at end of file diff --git a/TEngineHotUpdate/src/TEngineCore/FileSystem/LogToFile.cs b/TEngineHotUpdate/src/TEngineCore/FileSystem/LogToFile.cs new file mode 100644 index 00000000..78dfaedf --- /dev/null +++ b/TEngineHotUpdate/src/TEngineCore/FileSystem/LogToFile.cs @@ -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 foldersToKeep = new HashSet(); + 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()); + } + } + } + } +} \ No newline at end of file diff --git a/TEngineHotUpdate/src/TEngineCore/Json/JsonHelper.cs b/TEngineHotUpdate/src/TEngineCore/Json/JsonHelper.cs new file mode 100644 index 00000000..81e18107 --- /dev/null +++ b/TEngineHotUpdate/src/TEngineCore/Json/JsonHelper.cs @@ -0,0 +1,30 @@ +using UnityEngine; +using TEngineCore; +using TEngineCore.ListJson; + +public class JsonHelper : Singleton +{ + public JsonHelper() + { + } + + public T Deserialize(string json) + { + return JsonMapper.ToObject(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); + } +} \ No newline at end of file diff --git a/TEngineHotUpdate/src/TEngineCore/Json/LitJson/IJsonWrapper.cs b/TEngineHotUpdate/src/TEngineCore/Json/LitJson/IJsonWrapper.cs new file mode 100644 index 00000000..7276a4df --- /dev/null +++ b/TEngineHotUpdate/src/TEngineCore/Json/LitJson/IJsonWrapper.cs @@ -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); + } +} diff --git a/TEngineHotUpdate/src/TEngineCore/Json/LitJson/JsonData.cs b/TEngineHotUpdate/src/TEngineCore/Json/LitJson/JsonData.cs new file mode 100644 index 00000000..bb439a3a --- /dev/null +++ b/TEngineHotUpdate/src/TEngineCore/Json/LitJson/JsonData.cs @@ -0,0 +1,1059 @@ +#region Header +/** + * JsonData.cs + * Generic type to hold JSON data (objects, arrays, and so on). This is + * the default type returned by JsonMapper.ToObject(). + * + * 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.Collections.Specialized; +using System.IO; + + +namespace TEngineCore.ListJson +{ + public class JsonData : IJsonWrapper, IEquatable + { + #region Fields + private IList inst_array; + private bool inst_boolean; + private double inst_double; + private int inst_int; + private long inst_long; + private IDictionary inst_object; + private string inst_string; + private string json; + private JsonType type; + + // Used to implement the IOrderedDictionary interface + private IList> object_list; + #endregion + + + #region Properties + public int Count { + get { return EnsureCollection ().Count; } + } + + public bool IsArray { + get { return type == JsonType.Array; } + } + + public bool IsBoolean { + get { return type == JsonType.Boolean; } + } + + public bool IsDouble { + get { return type == JsonType.Double; } + } + + public bool IsInt { + get { return type == JsonType.Int; } + } + + public bool IsLong { + get { return type == JsonType.Long; } + } + + public bool IsObject { + get { return type == JsonType.Object; } + } + + public bool IsString { + get { return type == JsonType.String; } + } + + public ICollection Keys { + get { EnsureDictionary (); return inst_object.Keys; } + } + + /// + /// Determines whether the json contains an element that has the specified key. + /// + /// The key to locate in the json. + /// true if the json contains an element that has the specified key; otherwise, false. + public Boolean ContainsKey(String key) { + EnsureDictionary(); + return this.inst_object.Keys.Contains(key); + } + #endregion + + + #region ICollection Properties + int ICollection.Count { + get { + return Count; + } + } + + bool ICollection.IsSynchronized { + get { + return EnsureCollection ().IsSynchronized; + } + } + + object ICollection.SyncRoot { + get { + return EnsureCollection ().SyncRoot; + } + } + #endregion + + + #region IDictionary Properties + bool IDictionary.IsFixedSize { + get { + return EnsureDictionary ().IsFixedSize; + } + } + + bool IDictionary.IsReadOnly { + get { + return EnsureDictionary ().IsReadOnly; + } + } + + ICollection IDictionary.Keys { + get { + EnsureDictionary (); + IList keys = new List (); + + foreach (KeyValuePair entry in + object_list) { + keys.Add (entry.Key); + } + + return (ICollection) keys; + } + } + + ICollection IDictionary.Values { + get { + EnsureDictionary (); + IList values = new List (); + + foreach (KeyValuePair entry in + object_list) { + values.Add (entry.Value); + } + + return (ICollection) values; + } + } + #endregion + + + + #region IJsonWrapper Properties + bool IJsonWrapper.IsArray { + get { return IsArray; } + } + + bool IJsonWrapper.IsBoolean { + get { return IsBoolean; } + } + + bool IJsonWrapper.IsDouble { + get { return IsDouble; } + } + + bool IJsonWrapper.IsInt { + get { return IsInt; } + } + + bool IJsonWrapper.IsLong { + get { return IsLong; } + } + + bool IJsonWrapper.IsObject { + get { return IsObject; } + } + + bool IJsonWrapper.IsString { + get { return IsString; } + } + #endregion + + + #region IList Properties + bool IList.IsFixedSize { + get { + return EnsureList ().IsFixedSize; + } + } + + bool IList.IsReadOnly { + get { + return EnsureList ().IsReadOnly; + } + } + #endregion + + + #region IDictionary Indexer + object IDictionary.this[object key] { + get { + return EnsureDictionary ()[key]; + } + + set { + if (! (key is String)) + throw new ArgumentException ( + "The key has to be a string"); + + JsonData data = ToJsonData (value); + + this[(string) key] = data; + } + } + #endregion + + + #region IOrderedDictionary Indexer + object IOrderedDictionary.this[int idx] { + get { + EnsureDictionary (); + return object_list[idx].Value; + } + + set { + EnsureDictionary (); + JsonData data = ToJsonData (value); + + KeyValuePair old_entry = object_list[idx]; + + inst_object[old_entry.Key] = data; + + KeyValuePair entry = + new KeyValuePair (old_entry.Key, data); + + object_list[idx] = entry; + } + } + #endregion + + + #region IList Indexer + object IList.this[int index] { + get { + return EnsureList ()[index]; + } + + set { + EnsureList (); + JsonData data = ToJsonData (value); + + this[index] = data; + } + } + #endregion + + + #region Public Indexers + public JsonData this[string prop_name] { + get { + EnsureDictionary (); + return inst_object[prop_name]; + } + + set { + EnsureDictionary (); + + KeyValuePair entry = + new KeyValuePair (prop_name, value); + + if (inst_object.ContainsKey (prop_name)) { + for (int i = 0; i < object_list.Count; i++) { + if (object_list[i].Key == prop_name) { + object_list[i] = entry; + break; + } + } + } else + object_list.Add (entry); + + inst_object[prop_name] = value; + + json = null; + } + } + + public JsonData this[int index] { + get { + EnsureCollection (); + + if (type == JsonType.Array) + return inst_array[index]; + + return object_list[index].Value; + } + + set { + EnsureCollection (); + + if (type == JsonType.Array) + inst_array[index] = value; + else { + KeyValuePair entry = object_list[index]; + KeyValuePair new_entry = + new KeyValuePair (entry.Key, value); + + object_list[index] = new_entry; + inst_object[entry.Key] = value; + } + + json = null; + } + } + #endregion + + + #region Constructors + public JsonData () + { + } + + public JsonData (bool boolean) + { + type = JsonType.Boolean; + inst_boolean = boolean; + } + + public JsonData (double number) + { + type = JsonType.Double; + inst_double = number; + } + + public JsonData (int number) + { + type = JsonType.Int; + inst_int = number; + } + + public JsonData (long number) + { + type = JsonType.Long; + inst_long = number; + } + + public JsonData (object obj) + { + if (obj is Boolean) { + type = JsonType.Boolean; + inst_boolean = (bool) obj; + return; + } + + if (obj is Double) { + type = JsonType.Double; + inst_double = (double) obj; + return; + } + + if (obj is Int32) { + type = JsonType.Int; + inst_int = (int) obj; + return; + } + + if (obj is Int64) { + type = JsonType.Long; + inst_long = (long) obj; + return; + } + + if (obj is String) { + type = JsonType.String; + inst_string = (string) obj; + return; + } + + throw new ArgumentException ( + "Unable to wrap the given object with JsonData"); + } + + public JsonData (string str) + { + type = JsonType.String; + inst_string = str; + } + #endregion + + + #region Implicit Conversions + public static implicit operator JsonData (Boolean data) + { + return new JsonData (data); + } + + public static implicit operator JsonData (Double data) + { + return new JsonData (data); + } + + public static implicit operator JsonData (Int32 data) + { + return new JsonData (data); + } + + public static implicit operator JsonData (Int64 data) + { + return new JsonData (data); + } + + public static implicit operator JsonData (String data) + { + return new JsonData (data); + } + #endregion + + + #region Explicit Conversions + public static explicit operator Boolean (JsonData data) + { + if (data.type != JsonType.Boolean) + throw new InvalidCastException ( + "Instance of JsonData doesn't hold a double"); + + return data.inst_boolean; + } + + public static explicit operator Double (JsonData data) + { + if (data.type != JsonType.Double) + throw new InvalidCastException ( + "Instance of JsonData doesn't hold a double"); + + return data.inst_double; + } + + public static explicit operator Int32(JsonData data) + { + if (data.type != JsonType.Int && data.type != JsonType.Long) + { + throw new InvalidCastException( + "Instance of JsonData doesn't hold an int"); + } + + // cast may truncate data... but that's up to the user to consider + return data.type == JsonType.Int ? data.inst_int : (int)data.inst_long; + } + + public static explicit operator Int64(JsonData data) + { + if (data.type != JsonType.Long && data.type != JsonType.Int) + { + throw new InvalidCastException( + "Instance of JsonData doesn't hold a long"); + } + + return data.type == JsonType.Long ? data.inst_long : data.inst_int; + } + + public static explicit operator String (JsonData data) + { + if (data.type != JsonType.String) + throw new InvalidCastException ( + "Instance of JsonData doesn't hold a string"); + + return data.inst_string; + } + #endregion + + + #region ICollection Methods + void ICollection.CopyTo (Array array, int index) + { + EnsureCollection ().CopyTo (array, index); + } + #endregion + + + #region IDictionary Methods + void IDictionary.Add (object key, object value) + { + JsonData data = ToJsonData (value); + + EnsureDictionary ().Add (key, data); + + KeyValuePair entry = + new KeyValuePair ((string) key, data); + object_list.Add (entry); + + json = null; + } + + void IDictionary.Clear () + { + EnsureDictionary ().Clear (); + object_list.Clear (); + json = null; + } + + bool IDictionary.Contains (object key) + { + return EnsureDictionary ().Contains (key); + } + + IDictionaryEnumerator IDictionary.GetEnumerator () + { + return ((IOrderedDictionary) this).GetEnumerator (); + } + + void IDictionary.Remove (object key) + { + EnsureDictionary ().Remove (key); + + for (int i = 0; i < object_list.Count; i++) { + if (object_list[i].Key == (string) key) { + object_list.RemoveAt (i); + break; + } + } + + json = null; + } + #endregion + + + #region IEnumerable Methods + IEnumerator IEnumerable.GetEnumerator () + { + return EnsureCollection ().GetEnumerator (); + } + #endregion + + + #region IJsonWrapper Methods + bool IJsonWrapper.GetBoolean () + { + if (type != JsonType.Boolean) + throw new InvalidOperationException ( + "JsonData instance doesn't hold a boolean"); + + return inst_boolean; + } + + double IJsonWrapper.GetDouble () + { + if (type != JsonType.Double) + throw new InvalidOperationException ( + "JsonData instance doesn't hold a double"); + + return inst_double; + } + + int IJsonWrapper.GetInt () + { + if (type != JsonType.Int) + throw new InvalidOperationException ( + "JsonData instance doesn't hold an int"); + + return inst_int; + } + + long IJsonWrapper.GetLong () + { + if (type != JsonType.Long) + throw new InvalidOperationException ( + "JsonData instance doesn't hold a long"); + + return inst_long; + } + + string IJsonWrapper.GetString () + { + if (type != JsonType.String) + throw new InvalidOperationException ( + "JsonData instance doesn't hold a string"); + + return inst_string; + } + + void IJsonWrapper.SetBoolean (bool val) + { + type = JsonType.Boolean; + inst_boolean = val; + json = null; + } + + void IJsonWrapper.SetDouble (double val) + { + type = JsonType.Double; + inst_double = val; + json = null; + } + + void IJsonWrapper.SetInt (int val) + { + type = JsonType.Int; + inst_int = val; + json = null; + } + + void IJsonWrapper.SetLong (long val) + { + type = JsonType.Long; + inst_long = val; + json = null; + } + + void IJsonWrapper.SetString (string val) + { + type = JsonType.String; + inst_string = val; + json = null; + } + + string IJsonWrapper.ToJson () + { + return ToJson (); + } + + void IJsonWrapper.ToJson (JsonWriter writer) + { + ToJson (writer); + } + #endregion + + + #region IList Methods + int IList.Add (object value) + { + return Add (value); + } + + void IList.Clear () + { + EnsureList ().Clear (); + json = null; + } + + bool IList.Contains (object value) + { + return EnsureList ().Contains (value); + } + + int IList.IndexOf (object value) + { + return EnsureList ().IndexOf (value); + } + + void IList.Insert (int index, object value) + { + EnsureList ().Insert (index, value); + json = null; + } + + void IList.Remove (object value) + { + EnsureList ().Remove (value); + json = null; + } + + void IList.RemoveAt (int index) + { + EnsureList ().RemoveAt (index); + json = null; + } + #endregion + + + #region IOrderedDictionary Methods + IDictionaryEnumerator IOrderedDictionary.GetEnumerator () + { + EnsureDictionary (); + + return new OrderedDictionaryEnumerator ( + object_list.GetEnumerator ()); + } + + void IOrderedDictionary.Insert (int idx, object key, object value) + { + string property = (string) key; + JsonData data = ToJsonData (value); + + this[property] = data; + + KeyValuePair entry = + new KeyValuePair (property, data); + + object_list.Insert (idx, entry); + } + + void IOrderedDictionary.RemoveAt (int idx) + { + EnsureDictionary (); + + inst_object.Remove (object_list[idx].Key); + object_list.RemoveAt (idx); + } + #endregion + + + #region Private Methods + private ICollection EnsureCollection () + { + if (type == JsonType.Array) + return (ICollection) inst_array; + + if (type == JsonType.Object) + return (ICollection) inst_object; + + throw new InvalidOperationException ( + "The JsonData instance has to be initialized first"); + } + + private IDictionary EnsureDictionary () + { + if (type == JsonType.Object) + return (IDictionary) inst_object; + + if (type != JsonType.None) + throw new InvalidOperationException ( + "Instance of JsonData is not a dictionary"); + + type = JsonType.Object; + inst_object = new Dictionary (); + object_list = new List> (); + + return (IDictionary) inst_object; + } + + private IList EnsureList () + { + if (type == JsonType.Array) + return (IList) inst_array; + + if (type != JsonType.None) + throw new InvalidOperationException ( + "Instance of JsonData is not a list"); + + type = JsonType.Array; + inst_array = new List (); + + return (IList) inst_array; + } + + private JsonData ToJsonData (object obj) + { + if (obj == null) + return null; + + if (obj is JsonData) + return (JsonData) obj; + + return new JsonData (obj); + } + + private static void WriteJson (IJsonWrapper obj, JsonWriter writer) + { + if (obj == null) { + writer.Write (null); + return; + } + + if (obj.IsString) { + writer.Write (obj.GetString ()); + return; + } + + if (obj.IsBoolean) { + writer.Write (obj.GetBoolean ()); + return; + } + + if (obj.IsDouble) { + writer.Write (obj.GetDouble ()); + return; + } + + if (obj.IsInt) { + writer.Write (obj.GetInt ()); + return; + } + + if (obj.IsLong) { + writer.Write (obj.GetLong ()); + return; + } + + if (obj.IsArray) { + writer.WriteArrayStart (); + foreach (object elem in (IList) obj) + WriteJson ((JsonData) elem, writer); + writer.WriteArrayEnd (); + + return; + } + + if (obj.IsObject) { + writer.WriteObjectStart (); + + foreach (DictionaryEntry entry in ((IDictionary) obj)) { + writer.WritePropertyName ((string) entry.Key); + WriteJson ((JsonData) entry.Value, writer); + } + writer.WriteObjectEnd (); + + return; + } + } + #endregion + + + public int Add (object value) + { + JsonData data = ToJsonData (value); + + json = null; + + return EnsureList ().Add (data); + } + + public bool Remove(object obj) + { + json = null; + if(IsObject) + { + JsonData value = null; + if (inst_object.TryGetValue((string)obj, out value)) + return inst_object.Remove((string)obj) && object_list.Remove(new KeyValuePair((string)obj, value)); + else + throw new KeyNotFoundException("The specified key was not found in the JsonData object."); + } + if(IsArray) + { + return inst_array.Remove(ToJsonData(obj)); + } + throw new InvalidOperationException ( + "Instance of JsonData is not an object or a list."); + } + + public void Clear () + { + if (IsObject) { + ((IDictionary) this).Clear (); + return; + } + + if (IsArray) { + ((IList) this).Clear (); + return; + } + } + + public bool Equals (JsonData x) + { + if (x == null) + return false; + + if (x.type != this.type) + { + // further check to see if this is a long to int comparison + if ((x.type != JsonType.Int && x.type != JsonType.Long) + || (this.type != JsonType.Int && this.type != JsonType.Long)) + { + return false; + } + } + + switch (this.type) { + case JsonType.None: + return true; + + case JsonType.Object: + return this.inst_object.Equals (x.inst_object); + + case JsonType.Array: + return this.inst_array.Equals (x.inst_array); + + case JsonType.String: + return this.inst_string.Equals (x.inst_string); + + case JsonType.Int: + { + if (x.IsLong) + { + if (x.inst_long < Int32.MinValue || x.inst_long > Int32.MaxValue) + return false; + return this.inst_int.Equals((int)x.inst_long); + } + return this.inst_int.Equals(x.inst_int); + } + + case JsonType.Long: + { + if (x.IsInt) + { + if (this.inst_long < Int32.MinValue || this.inst_long > Int32.MaxValue) + return false; + return x.inst_int.Equals((int)this.inst_long); + } + return this.inst_long.Equals(x.inst_long); + } + + case JsonType.Double: + return this.inst_double.Equals (x.inst_double); + + case JsonType.Boolean: + return this.inst_boolean.Equals (x.inst_boolean); + } + + return false; + } + + public JsonType GetJsonType () + { + return type; + } + + public void SetJsonType (JsonType type) + { + if (this.type == type) + return; + + switch (type) { + case JsonType.None: + break; + + case JsonType.Object: + inst_object = new Dictionary (); + object_list = new List> (); + break; + + case JsonType.Array: + inst_array = new List (); + break; + + case JsonType.String: + inst_string = default (String); + break; + + case JsonType.Int: + inst_int = default (Int32); + break; + + case JsonType.Long: + inst_long = default (Int64); + break; + + case JsonType.Double: + inst_double = default (Double); + break; + + case JsonType.Boolean: + inst_boolean = default (Boolean); + break; + } + + this.type = type; + } + + public string ToJson () + { + if (json != null) + return json; + + StringWriter sw = new StringWriter (); + JsonWriter writer = new JsonWriter (sw); + writer.Validate = false; + + WriteJson (this, writer); + json = sw.ToString (); + + return json; + } + + public void ToJson (JsonWriter writer) + { + bool old_validate = writer.Validate; + + writer.Validate = false; + + WriteJson (this, writer); + + writer.Validate = old_validate; + } + + public override string ToString () + { + switch (type) { + case JsonType.Array: + return "JsonData array"; + + case JsonType.Boolean: + return inst_boolean.ToString (); + + case JsonType.Double: + return inst_double.ToString (); + + case JsonType.Int: + return inst_int.ToString (); + + case JsonType.Long: + return inst_long.ToString (); + + case JsonType.Object: + return "JsonData object"; + + case JsonType.String: + return inst_string; + } + + return "Uninitialized JsonData"; + } + } + + + internal class OrderedDictionaryEnumerator : IDictionaryEnumerator + { + IEnumerator> list_enumerator; + + + public object Current { + get { return Entry; } + } + + public DictionaryEntry Entry { + get { + KeyValuePair curr = list_enumerator.Current; + return new DictionaryEntry (curr.Key, curr.Value); + } + } + + public object Key { + get { return list_enumerator.Current.Key; } + } + + public object Value { + get { return list_enumerator.Current.Value; } + } + + + public OrderedDictionaryEnumerator ( + IEnumerator> enumerator) + { + list_enumerator = enumerator; + } + + + public bool MoveNext () + { + return list_enumerator.MoveNext (); + } + + public void Reset () + { + list_enumerator.Reset (); + } + } +} diff --git a/TEngineHotUpdate/src/TEngineCore/Json/LitJson/JsonException.cs b/TEngineHotUpdate/src/TEngineCore/Json/LitJson/JsonException.cs new file mode 100644 index 00000000..8125d926 --- /dev/null +++ b/TEngineHotUpdate/src/TEngineCore/Json/LitJson/JsonException.cs @@ -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) + { + } + } +} diff --git a/TEngineHotUpdate/src/TEngineCore/Json/LitJson/JsonMapper.cs b/TEngineHotUpdate/src/TEngineCore/Json/LitJson/JsonMapper.cs new file mode 100644 index 00000000..f64ace69 --- /dev/null +++ b/TEngineHotUpdate/src/TEngineCore/Json/LitJson/JsonMapper.cs @@ -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 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 Properties { + get { return properties; } + set { properties = value; } + } + } + + + internal delegate void ExporterFunc (object obj, JsonWriter writer); + public delegate void ExporterFunc (T obj, JsonWriter writer); + + internal delegate object ImporterFunc (object input); + public delegate TValue ImporterFunc (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 base_exporters_table; + private static readonly IDictionary custom_exporters_table; + + private static readonly IDictionary> base_importers_table; + private static readonly IDictionary> custom_importers_table; + + private static readonly IDictionary array_metadata; + private static readonly object array_metadata_lock = new Object (); + + private static readonly IDictionary> conv_ops; + private static readonly object conv_ops_lock = new Object (); + + private static readonly IDictionary object_metadata; + private static readonly object object_metadata_lock = new Object (); + + private static readonly IDictionary> 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 (); + conv_ops = new Dictionary> (); + object_metadata = new Dictionary (); + type_properties = new Dictionary> (); + + static_writer = new JsonWriter (); + + datetime_format = DateTimeFormatInfo.InvariantInfo; + + base_exporters_table = new Dictionary (); + custom_exporters_table = new Dictionary (); + + base_importers_table = new Dictionary> (); + custom_importers_table = new Dictionary> (); + + 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 (); + + 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 props = new List (); + + 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 ()); + } + + 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> table, + Type json_type, Type value_type, ImporterFunc importer) + { + if (! table.ContainsKey (json_type)) + table.Add (json_type, new Dictionary ()); + + 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 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 (JsonReader reader) + { + return (T) ReadValue (typeof (T), reader); + } + + public static T ToObject (TextReader reader) + { + JsonReader json_reader = new JsonReader (reader); + + return (T) ReadValue (typeof (T), json_reader); + } + + public static T ToObject (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 (ExporterFunc exporter) + { + ExporterFunc exporter_wrapper = + delegate (object obj, JsonWriter writer) { + exporter ((T) obj, writer); + }; + + custom_exporters_table[typeof (T)] = exporter_wrapper; + } + + public static void RegisterImporter ( + ImporterFunc 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 (); + } + } +} diff --git a/TEngineHotUpdate/src/TEngineCore/Json/LitJson/JsonMockWrapper.cs b/TEngineHotUpdate/src/TEngineCore/Json/LitJson/JsonMockWrapper.cs new file mode 100644 index 00000000..cb98faa3 --- /dev/null +++ b/TEngineHotUpdate/src/TEngineCore/Json/LitJson/JsonMockWrapper.cs @@ -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) {} + } +} diff --git a/TEngineHotUpdate/src/TEngineCore/Json/LitJson/JsonReader.cs b/TEngineHotUpdate/src/TEngineCore/Json/LitJson/JsonReader.cs new file mode 100644 index 00000000..3bc6b556 --- /dev/null +++ b/TEngineHotUpdate/src/TEngineCore/Json/LitJson/JsonReader.cs @@ -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> parse_table; + + private Stack 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 (); + 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> PopulateParseTable () + { + // See section A.2. of the manual for details + IDictionary> parse_table = new Dictionary> (); + + 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> parse_table, ParserToken row, int col, + params int[] symbols) + { + parse_table[(int) row].Add (col, symbols); + } + + private static void TableAddRow (IDictionary> parse_table, ParserToken rule) + { + parse_table.Add ((int) rule, new Dictionary ()); + } + #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]); + } + } + + } +} diff --git a/TEngineHotUpdate/src/TEngineCore/Json/LitJson/JsonWriter.cs b/TEngineHotUpdate/src/TEngineCore/Json/LitJson/JsonWriter.cs new file mode 100644 index 00000000..4dc796b0 --- /dev/null +++ b/TEngineHotUpdate/src/TEngineCore/Json/LitJson/JsonWriter.cs @@ -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 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 (); + 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 // ڳû CLSCompliant ԣͻԱҪ CLSCompliant + public void Write (ulong number) +#pragma warning restore CS3021 // ڳû CLSCompliant ԣͻԱҪ CLSCompliant + { + 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; + } + } +} diff --git a/TEngineHotUpdate/src/TEngineCore/Json/LitJson/Lexer.cs b/TEngineHotUpdate/src/TEngineCore/Json/LitJson/Lexer.cs new file mode 100644 index 00000000..b8f7f6de --- /dev/null +++ b/TEngineHotUpdate/src/TEngineCore/Json/LitJson/Lexer.cs @@ -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; + } + } +} diff --git a/TEngineHotUpdate/src/TEngineCore/Json/LitJson/Netstandard15Polyfill.cs b/TEngineHotUpdate/src/TEngineCore/Json/LitJson/Netstandard15Polyfill.cs new file mode 100644 index 00000000..2b5857f2 --- /dev/null +++ b/TEngineHotUpdate/src/TEngineCore/Json/LitJson/Netstandard15Polyfill.cs @@ -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 \ No newline at end of file diff --git a/TEngineHotUpdate/src/TEngineCore/Json/LitJson/ParserToken.cs b/TEngineHotUpdate/src/TEngineCore/Json/LitJson/ParserToken.cs new file mode 100644 index 00000000..43784018 --- /dev/null +++ b/TEngineHotUpdate/src/TEngineCore/Json/LitJson/ParserToken.cs @@ -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 + } +} diff --git a/TEngineHotUpdate/src/TEngineCore/Json/MiniJSON/Json.cs b/TEngineHotUpdate/src/TEngineCore/Json/MiniJSON/Json.cs new file mode 100644 index 00000000..4fcb2705 --- /dev/null +++ b/TEngineHotUpdate/src/TEngineCore/Json/MiniJSON/Json.cs @@ -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 ParseObject() + { + Dictionary dictionary = new Dictionary(); + 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)null; + label_4: + return dictionary; + label_6: + return (Dictionary)null; + } + + private List ParseArray() + { + List objectList = new List(); + this.json.Read(); + bool flag = true; + while (flag) + { + Json.Parser.TOKEN nextToken = this.NextToken; + switch (nextToken) + { + case Json.Parser.TOKEN.NONE: + return (List)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()); + } + } + } +} diff --git a/TEngineHotUpdate/src/TEngineCore/Mono/MonoController.cs b/TEngineHotUpdate/src/TEngineCore/Mono/MonoController.cs new file mode 100644 index 00000000..9c32f71e --- /dev/null +++ b/TEngineHotUpdate/src/TEngineCore/Mono/MonoController.cs @@ -0,0 +1,64 @@ +using UnityEngine; +using UnityEngine.Events; + +namespace TEngineCore +{ + /// + /// Mono管理者 + /// + 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; + } + + /// + /// 为给外部提供的 添加帧更新事件 + /// + /// + public void AddUpdateListener(UnityAction fun) + { + updateEvent += fun; + } + + /// + /// 移除帧更新事件 + /// + /// + public void RemoveUpdateListener(UnityAction fun) + { + updateEvent -= fun; + } + + public void Release() + { + updateEvent = null; + fixedUpdateEvent = null; + } + } +} \ No newline at end of file diff --git a/TEngineHotUpdate/src/TEngineCore/Mono/MonoManager.cs b/TEngineHotUpdate/src/TEngineCore/Mono/MonoManager.cs new file mode 100644 index 00000000..47be3d55 --- /dev/null +++ b/TEngineHotUpdate/src/TEngineCore/Mono/MonoManager.cs @@ -0,0 +1,144 @@ +using System.Collections; +using System.ComponentModel; +using TEngineCore; +using UnityEngine; +using UnityEngine.Events; + +namespace TEngineCore +{ + public class MonoManager : TSingleton + { + private MonoController controller; + + public override void Release() + { + StopAllCoroutine(); + controller.Release(); + controller = null; + base.Release(); + } + + public MonoManager() + { + GameObject obj = new GameObject("MonoManager"); + + controller = obj.AddComponent(); + +#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 + /// + /// 为给外部提供的 添加帧更新事件 + /// + /// + public void AddUpdateListener(UnityAction fun) + { + controller.AddUpdateListener(fun); + } + + /// + /// 为给外部提供的 添加物理帧更新事件 + /// + /// + public void AddFixedUpdateListener(UnityAction fun) + { + controller.AddFixedUpdateListener(fun); + } + + /// + /// 移除帧更新事件 + /// + /// + 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 + } + +} \ No newline at end of file diff --git a/TEngineHotUpdate/src/TEngineCore/Res/AssetBundleData.cs b/TEngineHotUpdate/src/TEngineCore/Res/AssetBundleData.cs new file mode 100644 index 00000000..f9d287da --- /dev/null +++ b/TEngineHotUpdate/src/TEngineCore/Res/AssetBundleData.cs @@ -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 _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 _onAsyncLoadComplete; + + public string Name + { + get + { + return _name; + } + } + + /// + /// 是否能被卸载 + /// + public bool Unloadable + { + get + { + return Bundle != null && _refCount <= 0 && _depCount <= 0; + } + } + + /// + /// 是否正在加载 + /// + public bool IsAsyncLoading => _asyncLoadRequest != null; + + public bool IsLoadComplete => _bLoaded; + + public event System.Action 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(); + + 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"); + } + + /// + /// 同步加载AssetBundle接口 + /// + /// 该AssetBundle是否被其他AssetBundle依赖 + 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}'!"); + } + } + + /// + /// 异步加载AssetBundle接口 + /// + /// 加载回调 + /// 该AssetBundle是否被其他AssetBundle依赖 + public void LoadAsync(System.Action 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); + } + } + + /// + /// 卸载AssetBundle及其中包含的Asset + /// + /// 强制卸载 + 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; + } + + /// + /// 增加依赖计数 + /// + /// 引用计数增量 + /// 异步加载未完成时,存在另外的加载请求,此时加载请求里记录依赖次数 + public void AddDepRef(int count = 1) + { + _depCount += count; + } + + /// + /// 减依赖计数 + /// + public void DecDepRef() + { + --_depCount; + if (_depCount == 0) + { + _unloadTimer = 0f; + } + else if (_depCount < 0) + { + _depCount = 0; + TLogger.LogWarning($"{_name} _depCount < 0"); + } + } + + /// + /// 增加引用计数 + /// + public void AddRef() + { + ++_refCount; + //TLogger.LogInfo($"Add AssetBundle {_name} refCount = {_refCount}"); + } + + /// + /// 减引用计数 + /// + 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"); + } + } + + /// + /// 卸载时间更新 + /// + /// 游戏时间帧间隔 + /// 从引用计数为0开始到现在的时长 + 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; + } + } + } +} diff --git a/TEngineHotUpdate/src/TEngineCore/Res/AssetConfig.cs b/TEngineHotUpdate/src/TEngineCore/Res/AssetConfig.cs new file mode 100644 index 00000000..65a0bad1 --- /dev/null +++ b/TEngineHotUpdate/src/TEngineCore/Res/AssetConfig.cs @@ -0,0 +1,356 @@ +using System.IO; +using System.Collections.Generic; +using UnityEngine.SceneManagement; + +namespace TEngineCore +{ + /// + /// 资源配置器 负责资源路径 AssetBundle映射,管理AssetBundle、Asset数据 + /// + 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 _bundleDatas = new Dictionary(); + readonly Dictionary _assetPath2BundleDatas = new Dictionary(); + + /// + /// 加载资源依赖数据,产生AssetBundle依赖拓扑 + /// + 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 + } + + /// + /// 卸载AssetBundle数据 + /// + 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(); + } + + } + + /// + /// 延迟卸载更新 + /// + /// 游戏帧间隔 + 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 + } + + /// + /// 立即卸载没有引用的AssetBundle + /// + public void UnloadUnusedAssetBundle() + { + var iter = _bundleDatas.GetEnumerator(); + AssetBundleData bundleData; + while (iter.MoveNext()) + { + bundleData = iter.Current.Value; + if (bundleData.Unloadable) + bundleData.Unload(); + } + iter.Dispose(); + } + + /// + /// 同步获取Asset + /// + /// 是否加载子Asset,针对Sprite图集 + /// 是否加载子Asset,针对Sprite图集 + /// Asset数据 + 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($"{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(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; + } + + /// + /// 资源是否存在 + /// + /// 通过右键菜单Get Asset Path获取的路径 + /// true:存在,false:不存在 + public bool Exists(string path) + { +#if ASSETBUNDLE_ENABLE + return _assetPath2BundleDatas.ContainsKey(path); +#elif UNITY_EDITOR + return File.Exists($"{AssetRootPath}/{path}"); +#else + return false; +#endif + } + + /// + /// 异步获取Asset + /// + /// 通过右键菜单Get Asset Path获取的路径 + /// 是否加载子Asset,针对Sprite图集 + /// 加载回调 + /// Asset数据 + public void GetAssetAtPathAsync(string path, bool withSubAssets, System.Action 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 + } + + /// + /// 获取场景Asset + /// + /// 场景名 + /// Asset数据 + 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; + } + } +} diff --git a/TEngineHotUpdate/src/TEngineCore/Res/AssetData.cs b/TEngineHotUpdate/src/TEngineCore/Res/AssetData.cs new file mode 100644 index 00000000..d8d2152d --- /dev/null +++ b/TEngineHotUpdate/src/TEngineCore/Res/AssetData.cs @@ -0,0 +1,358 @@ +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.SceneManagement; +using UnityEngine.U2D; + +namespace TEngineCore +{ + /// + /// 封装的AssetBundle + /// + public class AssetData + { + private AssetBundleData _refBundle; + private Object _assetObject; + private Dictionary _subAssetObjects; + private string _kPath; + private string _fPath; + private string _name; + private int _refCount; + private bool _useSubAsset; + private AsyncOperation _asyncLoadRequest; + private event System.Action _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; + + /// + /// Asset名 + /// + public string Name => _name; + + /// + /// 异步操作对象 + /// + public AsyncOperation AsyncOp + { + get + { + return _asyncLoadRequest; + } + } + + public event System.Action 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; + } + + /// + /// 增引用计数 + /// + public void AddRef() + { + ++_refCount; +#if UNITY_EDITOR + //TLogger.LogInfo($"Add AssetData {_fPath} _refCount = {_refCount}"); +#endif + } + + /// + /// 减引用计数,当引用计数为0时减其AssetBundle的引用计数 + /// + 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(); + } + } + } + + /// + /// 同步加载Asset + /// + /// 是否加载子Asset,针对Sprite图集 + 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}'"); + } + } + } + } + + /// + /// 异步加载Asset + /// + /// 加载回调 + /// 是否加载子Asset,针对Sprite图集 + public void LoadAsync(System.Action 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(); + + 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]); + } + } + } + } + + /// + /// 加载场景 + /// + /// 先加载场景AssetBundle,才能使用SceneManager中的加载接口 + public void LoadScene(LoadSceneMode mode) + { + if (_refBundle != null && _refBundle.Bundle == null) + _refBundle.Load(); + + _asyncLoadRequest = SceneManager.LoadSceneAsync(_name, mode); + if (_asyncLoadRequest != null) + _asyncLoadRequest.allowSceneActivation = false; + } + + /// + /// 卸载Asset + /// + /// 是否强制卸载 + 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})!"); + } + } + } +} diff --git a/TEngineHotUpdate/src/TEngineCore/Res/AssetTag.cs b/TEngineHotUpdate/src/TEngineCore/Res/AssetTag.cs new file mode 100644 index 00000000..7e5468c9 --- /dev/null +++ b/TEngineHotUpdate/src/TEngineCore/Res/AssetTag.cs @@ -0,0 +1,49 @@ +using UnityEngine; + +namespace TEngineCore +{ + /// + /// GameObject与AssetData的绑定对象,用于管理对AssetData的引用计数 + /// + [DisallowMultipleComponent, AddComponentMenu("")] + public sealed class AssetTag : MonoBehaviour + { + AssetData _assetData; + [SerializeField, HideInInspector] private string _path; + /// + /// 对应的资源路径 + /// + public string Path => _path; + + /// + /// 缓存池中的归还时间戳 + /// + 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)); + } + + /// + /// GameObject绑定AssetData + /// + /// Asset数据 + public void Bind(AssetData assetData) + { + _assetData = assetData; + _assetData.AddRef(); + _path = _assetData.Path; + _instanceID = gameObject.GetInstanceID(); + } + + private void OnDestroy() + { + _assetData.DecRef(); + } + } + +} \ No newline at end of file diff --git a/TEngineHotUpdate/src/TEngineCore/Res/ResMgr.cs b/TEngineHotUpdate/src/TEngineCore/Res/ResMgr.cs new file mode 100644 index 00000000..2f1721da --- /dev/null +++ b/TEngineHotUpdate/src/TEngineCore/Res/ResMgr.cs @@ -0,0 +1,259 @@ +using System.Collections.Generic; +using UnityEngine; + +namespace TEngineCore +{ + internal class ResMgr : TSingleton + { + AssetConfig _assetConfig = new AssetConfig(); + private Dictionary _scriptableObjects = new Dictionary(); + public ResMgr() + { + _assetConfig.Load(); + } + + ~ResMgr() + { + _assetConfig.Unload(); + } + public void AssetUnloadDelay(float value) + { + _assetConfig.AssetUnloadDelay = value; + } + + public void MaxUnloadNumPerFrame(int value) + { + _assetConfig.MaxUnloadNumPerFrame = value; + } + + /// + /// 卸载无用资源 + /// + public void UnloadUnusedAssetBundle() + { + _assetConfig.UnloadUnusedAssetBundle(); + } + + #region 获取资源 + /// + /// 从文件获取字符串 + /// + /// Asset下路径 + /// + 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(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(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 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 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().Bind(assetData); + if (isActive != go.activeSelf) + { + go.SetActive(isActive); + } + } + } +} \ No newline at end of file diff --git a/TEngineHotUpdate/src/TEngineCore/Res/TResource.cs b/TEngineHotUpdate/src/TEngineCore/Res/TResource.cs new file mode 100644 index 00000000..58e775ef --- /dev/null +++ b/TEngineHotUpdate/src/TEngineCore/Res/TResource.cs @@ -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(string path) where T : UnityEngine.Object + { + return ResMgr.Instance.Load(path); + } + #endregion + + #region 异步加载 + public static void LoadAsync(string path, Action callBack) + { + ResMgr.Instance.GetGameObjectAsync(path, callBack); + } + public static void LoadAsync(string path, Action callBack, bool withSubAsset = false) + { + ResMgr.Instance.GetAssetAtPathAsync(path, withSubAsset, callBack); + } + #endregion + } +} \ No newline at end of file diff --git a/TEngineHotUpdate/src/TEngineCore/Thread/Loom.cs b/TEngineHotUpdate/src/TEngineCore/Thread/Loom.cs new file mode 100644 index 00000000..5d0ad62c --- /dev/null +++ b/TEngineHotUpdate/src/TEngineCore/Thread/Loom.cs @@ -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 +{ + // + // 从子线程调用Unity的方法 + // + public class Loom : MonoBehaviour + { + public Dictionary TokenSourcesDictionary = new Dictionary(); + 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(); + + GameObject tEngine = SingletonMgr.Root; + + if (tEngine != null) + { + obj.transform.SetParent(tEngine.transform); + } + } + } + + public struct NoDelayedQueueItem + { + public Action action; + public object param; + } + + private List _actions = new List(); + + public struct DelayedQueueItem + { + public float time; + public Action action; + public object param; + } + + private List _delayed = new List(); + + List _currentDelayed = new List(); + + public static void QueueOnMainThread(Action taction, object tparam) + { + QueueOnMainThread(taction, tparam, 0f); + } + public static void QueueOnMainThread(Action 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 _currentActions = new List(); + + 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); + } + } + } + } +} \ No newline at end of file diff --git a/TEngineHotUpdate/src/TEngineCore/Thread/ThreadMgr.cs b/TEngineHotUpdate/src/TEngineCore/Thread/ThreadMgr.cs new file mode 100644 index 00000000..fa1d13b8 --- /dev/null +++ b/TEngineHotUpdate/src/TEngineCore/Thread/ThreadMgr.cs @@ -0,0 +1,27 @@ +namespace TEngineCore +{ + public class ThreadMgr : UnitySingleton + { + protected override void OnLoad() + { + base.OnLoad(); + StartThread(); + } + + protected override void OnDestroy() + { + base.OnDestroy(); + ShutDownThread(); + } + + private void StartThread() + { + + } + + private void ShutDownThread() + { + + } + } +} \ No newline at end of file diff --git a/TEngineHotUpdate/src/TEngineCore/UI/Extend/DragItem.cs b/TEngineHotUpdate/src/TEngineCore/UI/Extend/DragItem.cs new file mode 100644 index 00000000..98bd7024 --- /dev/null +++ b/TEngineHotUpdate/src/TEngineCore/UI/Extend/DragItem.cs @@ -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 + { + private UIDragType m_DragState = UIDragType.Drop; + private Vector3 m_ItemOldPos; + private Vector3 m_ItemCachePos; + private bool m_CanDrag = false; + + /// + /// 是否可以拖拽 + /// + 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; + } + } +} \ No newline at end of file diff --git a/TEngineHotUpdate/src/TEngineCore/UI/Extend/EmptyGraph.cs b/TEngineHotUpdate/src/TEngineCore/UI/Extend/EmptyGraph.cs new file mode 100644 index 00000000..32ff9e2f --- /dev/null +++ b/TEngineHotUpdate/src/TEngineCore/UI/Extend/EmptyGraph.cs @@ -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 + } +} \ No newline at end of file diff --git a/TEngineHotUpdate/src/TEngineCore/UI/Extend/TipsUI.cs b/TEngineHotUpdate/src/TEngineCore/UI/Extend/TipsUI.cs new file mode 100644 index 00000000..d6c632da --- /dev/null +++ b/TEngineHotUpdate/src/TEngineCore/UI/Extend/TipsUI.cs @@ -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(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 AvailablePool; + public List UsedPool; + //自定义内容 + [SerializeField] private float Speed; + //定时器 + private float timer1; + private float timer2; + void Start() + { + AvailablePool = new List(); + UsedPool = new List(); + for (int i = 0; i < 5; i++) + { + AvailablePool.Add(Instantiate(Prefab, Mask.transform)); + AvailablePool[i].SetActive(false); + } + //GameEventMgr.Instance.AddEventListener(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 Tmessage = UsedPool[0].transform.Find("Message").GetComponent(); + Image BG = UsedPool[0].GetComponent(); + 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().color = new Color(0, 0, 0, 150f / 255f); + timer1 = timer2 = 0; + Text Tname = go.transform.Find("Name").GetComponent(); + Text Tmessage = go.transform.Find("Message").GetComponent(); + Tname.text = " " + "SYSTEM"; + Tmessage.text = " " + message; + float TnameWidth = Tname.preferredWidth; + float TmessageWidth = Tmessage.preferredWidth; + float goSizey = go.transform.GetComponent().sizeDelta.y; + go.transform.GetComponent().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 Tmessage2 = UsedPool[UsedPool.Count - 4].transform.Find("Message").GetComponent(); + Image BG = UsedPool[UsedPool.Count - 4].GetComponent(); + 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().color = new Color(0, 0, 0, 150f / 255f); + timer1 = timer2 = 0; + Text Tname = go.transform.Find("Name").GetComponent(); + Text Tmessage = go.transform.Find("Message").GetComponent(); + Tname.text = " " + name; + Tmessage.text = " " + message; + float TnameWidth = Tname.preferredWidth; + float TmessageWidth = Tmessage.preferredWidth; + float goSizey = go.transform.GetComponent().sizeDelta.y; + go.transform.GetComponent().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 Tmessage2 = UsedPool[UsedPool.Count - 4].transform.Find("Message").GetComponent(); + Image BG = UsedPool[UsedPool.Count - 4].GetComponent(); + 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("[没关系丶是爱情啊(安娜)]", "天降正义。"); + } + } + +} \ No newline at end of file diff --git a/TEngineHotUpdate/src/TEngineCore/UI/Extend/TweenUtil.cs b/TEngineHotUpdate/src/TEngineCore/UI/Extend/TweenUtil.cs new file mode 100644 index 00000000..593a915f --- /dev/null +++ b/TEngineHotUpdate/src/TEngineCore/UI/Extend/TweenUtil.cs @@ -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(); + } + + 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); + } + } +} \ No newline at end of file diff --git a/TEngineHotUpdate/src/TEngineCore/UI/Extend/UIEventItem.cs b/TEngineHotUpdate/src/TEngineCore/UI/Extend/UIEventItem.cs new file mode 100644 index 00000000..af8fbfe5 --- /dev/null +++ b/TEngineHotUpdate/src/TEngineCore/UI/Extend/UIEventItem.cs @@ -0,0 +1,200 @@ +using System; +using TEngineCore; +using UnityEngine.EventSystems; +using UnityEngine.UI; + +namespace TEngineCore +{ + public class UIEventItem : UIWindowWidget where T : UIEventItem + { + 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 m_clickAction; + private Action m_pressAction; + private Action m_beginDragAction; + private Action m_dragAction; + private Action m_endDragAction; + + public void BindClickEvent(Action clickAction, params object[] arg) + { + if (m_clickAction != null) + { + m_clickAction = clickAction; + } + else + { + m_clickAction = clickAction; + m_buttonClick = UnityUtil.AddMonoBehaviour